diff --git a/README-template.md b/README-template.md index 570e0b7..a48e7ac 100644 --- a/README-template.md +++ b/README-template.md @@ -6,11 +6,23 @@ A command-line interface for interacting with the Octomind API. This CLI allows you to execute tests, retrieve test reports, and manage private locations as well as environments. See [API documentation](https://octomind.dev/docs/api-reference/) -## Usage +## Usage / Installation -1. Install the package - `npm i -g @octomind/octomind` and use it directly e.g. `octomind -h` -2. Use the cli through npx e.g. `npx @octomind/octomind -h` +1. To install the package globally do **NOT** just a `npm i -g @octomind/octomind` but instead +```bash +mkdir -p ~/.local/packages +cd ~/.local/packages +npm install @octomind/octomind@latest +# either create an alias +alias octomind="node ~/.local/packages/node_modules/@octomind/octomind/dist/index.js" +# or create a symlink +sudo ln -s ~/.local/packages/node_modules/@octomind/octomind/dist/index.js /usr/local/bin/octomind +``` +this will install the package to `~/.local/packages` and create symlinks in `/usr/local/bin` or creates an alias. +This is necessary for the cli to work and avoid dependency conflicts, when installing the package globally. + +2. Use the cli through npx e.g. `npx @octomind/octomind -h` ${commands} diff --git a/README.md b/README.md index 17472c2..2480eb1 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,27 @@ A command-line interface for interacting with the Octomind API. This CLI allows you to execute tests, retrieve test reports, and manage private locations as well as environments. See [API documentation](https://octomind.dev/docs/api-reference/) -## Usage +## Usage / Installation -1. Install the package - `npm i -g @octomind/octomind` and use it directly e.g. `octomind -h` -2. Use the cli through npx e.g. `npx @octomind/octomind -h` +1. To install the package globally do **NOT** just a `npm i -g @octomind/octomind` but instead +```bash +mkdir -p ~/.local/packages +cd ~/.local/packages +npm install @octomind/octomind@latest +# either create an alias +alias octomind="node ~/.local/packages/node_modules/@octomind/octomind/dist/index.js" +# or create a symlink +sudo ln -s ~/.local/packages/node_modules/@octomind/octomind/dist/index.js /usr/local/bin/octomind +``` +this will install the package to `~/.local/packages` and create symlinks in `/usr/local/bin` or creates an alias. +This is necessary for the cli to work and avoid dependency conflicts, when installing the package globally. + +2. Use the cli through npx e.g. `npx @octomind/octomind -h` # octomind -Octomind cli tool. Version: 1.1.1. Additional documentation see https://octomind.dev/docs/api-reference/ +Octomind cli tool. Version: 1.1.2. Additional documentation see https://octomind.dev/docs/api-reference/ **Usage:** `octomind [options] [command]` @@ -26,7 +38,7 @@ Octomind cli tool. Version: 1.1.1. Additional documentation see https://octomind # octomind CLI Documentation -Octomind cli tool. Version: 1.1.1. Additional documentation see https://octomind.dev/docs/api-reference/ +Octomind cli tool. Version: 1.1.2. Additional documentation see https://octomind.dev/docs/api-reference/ ## Commands diff --git a/package.json b/package.json index 67dacfd..a1ff7ab 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,16 @@ { "name": "@octomind/octomind", - "version": "1.1.1", + "version": "1.1.2", "description": "a command line client for octomind apis", "main": "./dist/index.js", "packageManager": "pnpm@10.13.1", + "files": [ + "dist", + "src", + "README.md", + "LICENSE", + "package.json" + ], "engines": { "node": ">=20.0.0" }, @@ -26,7 +33,7 @@ "@playwright/test": "^1.54.1", "commander": "^14.0.0", "openapi-fetch": "^0.14.0", - "tsx": "^4.19.3" + "otplib": "^12.0.1" }, "devDependencies": { "@types/jest": "^30.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89bff17..147e8d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,9 +17,9 @@ importers: openapi-fetch: specifier: ^0.14.0 version: 0.14.0 - tsx: - specifier: ^4.19.3 - version: 4.20.3 + otplib: + specifier: ^12.0.1 + version: 12.0.1 devDependencies: '@types/jest': specifier: ^30.0.0 @@ -41,7 +41,7 @@ importers: version: 4.4.4(eslint-plugin-import@2.32.0)(eslint@8.57.1) eslint-plugin-github: specifier: ^5.0.0 - version: 5.1.8(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1)(typescript@5.7.3) + version: 5.1.8(eslint-import-resolver-typescript@4.4.4(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1)(typescript@5.7.3) eslint-plugin-import: specifier: ^2.27.5 version: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1) @@ -66,6 +66,9 @@ importers: ts-node: specifier: ^10.9.1 version: 10.9.2(@types/node@24.1.0)(typescript@5.7.3) + tsx: + specifier: ^4.19.3 + version: 4.20.3 typescript: specifier: ^5.0.4 version: 5.7.3 @@ -625,6 +628,21 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@otplib/core@12.0.1': + resolution: {integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==} + + '@otplib/plugin-crypto@12.0.1': + resolution: {integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==} + + '@otplib/plugin-thirty-two@12.0.1': + resolution: {integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==} + + '@otplib/preset-default@12.0.1': + resolution: {integrity: sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==} + + '@otplib/preset-v11@12.0.1': + resolution: {integrity: sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -2410,6 +2428,9 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + otplib@12.0.1: + resolution: {integrity: sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==} + own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} @@ -2763,6 +2784,10 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + thirty-two@1.0.2: + resolution: {integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==} + engines: {node: '>=0.2.6'} + tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} @@ -3634,6 +3659,29 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.0 + '@otplib/core@12.0.1': {} + + '@otplib/plugin-crypto@12.0.1': + dependencies: + '@otplib/core': 12.0.1 + + '@otplib/plugin-thirty-two@12.0.1': + dependencies: + '@otplib/core': 12.0.1 + thirty-two: 1.0.2 + + '@otplib/preset-default@12.0.1': + dependencies: + '@otplib/core': 12.0.1 + '@otplib/plugin-crypto': 12.0.1 + '@otplib/plugin-thirty-two': 12.0.1 + + '@otplib/preset-v11@12.0.1': + dependencies: + '@otplib/core': 12.0.1 + '@otplib/plugin-crypto': 12.0.1 + '@otplib/plugin-thirty-two': 12.0.1 + '@pkgjs/parseargs@0.11.0': optional: true @@ -4592,7 +4640,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -4622,7 +4670,7 @@ snapshots: lodash.snakecase: 4.1.1 lodash.upperfirst: 4.3.1 - eslint-plugin-github@5.1.8(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1)(typescript@5.7.3): + eslint-plugin-github@5.1.8(eslint-import-resolver-typescript@4.4.4(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1)(typescript@5.7.3): dependencies: '@eslint/compat': 1.2.7(eslint@8.57.1) '@eslint/eslintrc': 3.3.0 @@ -4669,7 +4717,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4(eslint-plugin-import@2.32.0)(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -5801,6 +5849,12 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + otplib@12.0.1: + dependencies: + '@otplib/core': 12.0.1 + '@otplib/preset-default': 12.0.1 + '@otplib/preset-v11': 12.0.1 + own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 @@ -6154,6 +6208,8 @@ snapshots: text-table@0.2.0: {} + thirty-two@1.0.2: {} + tinyglobby@0.2.14: dependencies: fdir: 6.4.6(picomatch@4.0.2) diff --git a/src/config.ts b/src/config.ts index 9a8da42..3311e93 100644 --- a/src/config.ts +++ b/src/config.ts @@ -23,11 +23,24 @@ export interface Config { testTargetId?: string; } +let configLoaded = false; +let config: Config = {}; + +export function resetConfig() { + configLoaded = false; + config = {}; +} + export async function loadConfig(force?: boolean): Promise { + if(configLoaded && !force) { + return config; + } try { const configPath = await getConfigPath(); const data = await fs.readFile(configPath, "utf8"); - return JSON.parse(data); + config = JSON.parse(data); + configLoaded = true; + return config; } catch (error) { // only exit on overwrite attempt if (force) { diff --git a/src/tools/notifications.ts b/src/tools/notifications.ts index 193a2cc..a6b496b 100644 --- a/src/tools/notifications.ts +++ b/src/tools/notifications.ts @@ -1,5 +1,6 @@ import type { paths, components } from "../api"; // generated by openapi-typescript import { client, handleError, ListOptions, logJson } from "./client"; +import { getUrl } from "../url"; export type GetNotificationsParams = paths["/apiKey/v2/test-targets/{testTargetId}/notifications"]["get"]["parameters"]["path"]; @@ -27,15 +28,21 @@ export const listNotifications = async ( } console.log("Notifications:"); - response.forEach((notification) => { + for (const notification of response) { console.log(`\nID: ${notification.id}`); console.log(`Type: ${notification.type}`); console.log(`Created At: ${notification.createdAt}`); if (notification.payload?.testReportId) { console.log(`Test Report ID: ${notification.payload.testReportId}`); + console.log( + `URL: ${await getUrl({ testReportId: notification.payload.testReportId, entityType: "test-report" })}`, + ); } if (notification.payload?.testCaseId) { console.log(`Test Case ID: ${notification.payload.testCaseId}`); + console.log( + `URL: ${await getUrl({ testCaseId: notification.payload.testCaseId, entityType: "test-case" })}`, + ); } if (notification.payload?.failed !== undefined) { console.log(`Failed: ${notification.payload.failed}`); @@ -43,5 +50,5 @@ export const listNotifications = async ( if (notification.ack) { console.log(`Acknowledged: ${notification.ack}`); } - }); + } }; diff --git a/tests/config.spec.ts b/tests/config.spec.ts index 29e738e..3a1e531 100644 --- a/tests/config.spec.ts +++ b/tests/config.spec.ts @@ -1,6 +1,6 @@ import fs from "fs/promises"; import path from "path"; -import { loadConfig, Config } from "../src/config"; +import { loadConfig, Config, resetConfig } from "../src/config"; import { homedir } from "os"; jest.mock("fs/promises"); const mockedFs = fs as jest.Mocked; @@ -10,6 +10,7 @@ const originalConsoleError = console.error; describe("Config", () => { beforeEach(() => { console.error = jest.fn(); + resetConfig(); }); afterEach(() => {