@desplega.ai/qa-use 2.8.6 → 2.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/package.json +1 -1
- package/dist/src/cli/commands/api/index.d.ts +3 -0
- package/dist/src/cli/commands/api/index.d.ts.map +1 -0
- package/dist/src/cli/commands/api/index.js +6 -0
- package/dist/src/cli/commands/api/index.js.map +1 -0
- package/dist/src/cli/commands/api/lib/http.d.ts +18 -0
- package/dist/src/cli/commands/api/lib/http.d.ts.map +1 -0
- package/dist/src/cli/commands/api/lib/http.js +51 -0
- package/dist/src/cli/commands/api/lib/http.js.map +1 -0
- package/dist/src/cli/commands/api/lib/openapi-cache.d.ts +10 -0
- package/dist/src/cli/commands/api/lib/openapi-cache.d.ts.map +1 -0
- package/dist/src/cli/commands/api/lib/openapi-cache.js +41 -0
- package/dist/src/cli/commands/api/lib/openapi-cache.js.map +1 -0
- package/dist/src/cli/commands/api/lib/openapi-errors.d.ts +8 -0
- package/dist/src/cli/commands/api/lib/openapi-errors.d.ts.map +1 -0
- package/dist/src/cli/commands/api/lib/openapi-errors.js +20 -0
- package/dist/src/cli/commands/api/lib/openapi-errors.js.map +1 -0
- package/dist/src/cli/commands/api/lib/openapi-spec.d.ts +62 -0
- package/dist/src/cli/commands/api/lib/openapi-spec.d.ts.map +1 -0
- package/dist/src/cli/commands/api/lib/openapi-spec.js +166 -0
- package/dist/src/cli/commands/api/lib/openapi-spec.js.map +1 -0
- package/dist/src/cli/commands/api/lib/output.d.ts +8 -0
- package/dist/src/cli/commands/api/lib/output.d.ts.map +1 -0
- package/dist/src/cli/commands/api/lib/output.js +31 -0
- package/dist/src/cli/commands/api/lib/output.js.map +1 -0
- package/dist/src/cli/commands/api/ls.d.ts +3 -0
- package/dist/src/cli/commands/api/ls.d.ts.map +1 -0
- package/dist/src/cli/commands/api/ls.js +76 -0
- package/dist/src/cli/commands/api/ls.js.map +1 -0
- package/dist/src/cli/commands/api/request.d.ts +11 -0
- package/dist/src/cli/commands/api/request.d.ts.map +1 -0
- package/dist/src/cli/commands/api/request.js +225 -0
- package/dist/src/cli/commands/api/request.js.map +1 -0
- package/dist/src/cli/index.js +2 -0
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,6 +96,27 @@ qa-use browser run # Interactive REPL mode
|
|
|
96
96
|
|
|
97
97
|
Run `qa-use browser --help` for the full list of 29 browser commands.
|
|
98
98
|
|
|
99
|
+
### API Commands
|
|
100
|
+
|
|
101
|
+
Dynamic API access powered by live OpenAPI (`/api/v1/openapi.json`) with local cache fallback.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
qa-use api ls # List endpoints from live/cached OpenAPI
|
|
105
|
+
qa-use api /api/v1/tests # Call endpoint (method inferred)
|
|
106
|
+
qa-use api -X POST /api/v1/tests-actions/run --input body.json
|
|
107
|
+
qa-use api ls --refresh # Force refresh OpenAPI cache
|
|
108
|
+
qa-use api ls --offline # Use cached OpenAPI only
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
| Command | Description |
|
|
112
|
+
|---------|-------------|
|
|
113
|
+
| `qa-use api ls` | List `/api/v1/*` endpoints from OpenAPI |
|
|
114
|
+
| `qa-use api <path>` | Send API request to endpoint |
|
|
115
|
+
| `qa-use api ... --refresh` | Force OpenAPI spec refresh |
|
|
116
|
+
| `qa-use api ... --offline` | Use cached spec without network |
|
|
117
|
+
|
|
118
|
+
If live spec fetch fails, qa-use falls back to the last cached spec and prints a stale-cache warning.
|
|
119
|
+
|
|
99
120
|
### Setup Commands
|
|
100
121
|
|
|
101
122
|
| Command | Description |
|
package/dist/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,UAAU,SAGtB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { lsCommand } from './ls.js';
|
|
3
|
+
import { registerApiRequestAction } from './request.js';
|
|
4
|
+
export const apiCommand = registerApiRequestAction(new Command('api').description('Call desplega.ai API endpoints using OpenAPI metadata'), { endpointRequired: false });
|
|
5
|
+
apiCommand.addCommand(lsCommand);
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/cli/commands/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,CAAC,MAAM,UAAU,GAAG,wBAAwB,CAChD,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,uDAAuD,CAAC,EACvF,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAC5B,CAAC;AAEF,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ExecuteApiRequestOptions {
|
|
2
|
+
apiUrl: string;
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
method: string;
|
|
5
|
+
path: string;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
query?: Record<string, string>;
|
|
8
|
+
body?: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface ExecuteApiRequestResult {
|
|
11
|
+
status: number;
|
|
12
|
+
statusText: string;
|
|
13
|
+
headers: Record<string, string>;
|
|
14
|
+
data: unknown;
|
|
15
|
+
}
|
|
16
|
+
export declare function formatApiResponseError(status: number, statusText: string, data: unknown): string;
|
|
17
|
+
export declare function executeApiRequest(options: ExecuteApiRequestOptions): Promise<ExecuteApiRequestResult>;
|
|
18
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/commands/api/lib/http.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,OAAO,CAAC;CACf;AAMD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAYhG;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,uBAAuB,CAAC,CAkClC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
function normalizeApiUrl(apiUrl) {
|
|
3
|
+
return apiUrl.replace(/\/$/, '');
|
|
4
|
+
}
|
|
5
|
+
export function formatApiResponseError(status, statusText, data) {
|
|
6
|
+
if (data && typeof data === 'object') {
|
|
7
|
+
const payload = data;
|
|
8
|
+
if (payload.message && typeof payload.message === 'string') {
|
|
9
|
+
return `HTTP ${status} ${statusText}: ${payload.message}`;
|
|
10
|
+
}
|
|
11
|
+
if (payload.detail && typeof payload.detail === 'string') {
|
|
12
|
+
return `HTTP ${status} ${statusText}: ${payload.detail}`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return `HTTP ${status} ${statusText}`;
|
|
16
|
+
}
|
|
17
|
+
export async function executeApiRequest(options) {
|
|
18
|
+
const requestConfig = {
|
|
19
|
+
method: options.method,
|
|
20
|
+
baseURL: normalizeApiUrl(options.apiUrl),
|
|
21
|
+
url: options.path,
|
|
22
|
+
headers: {
|
|
23
|
+
...(options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {}),
|
|
24
|
+
...options.headers,
|
|
25
|
+
},
|
|
26
|
+
params: options.query,
|
|
27
|
+
data: options.body,
|
|
28
|
+
timeout: 25000,
|
|
29
|
+
validateStatus: () => true,
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
const response = await axios.request(requestConfig);
|
|
33
|
+
const headers = {};
|
|
34
|
+
for (const [key, value] of Object.entries(response.headers || {})) {
|
|
35
|
+
headers[key] = Array.isArray(value) ? value.join(', ') : String(value);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
status: response.status,
|
|
39
|
+
statusText: response.statusText,
|
|
40
|
+
headers,
|
|
41
|
+
data: response.data,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (axios.isAxiosError(error)) {
|
|
46
|
+
throw new Error(error.message);
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../../../../src/cli/commands/api/lib/http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAmB1B,SAAS,eAAe,CAAC,MAAc;IACrC,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc,EAAE,UAAkB,EAAE,IAAa;IACtF,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,IAA6C,CAAC;QAC9D,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC3D,OAAO,QAAQ,MAAM,IAAI,UAAU,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;QAC5D,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzD,OAAO,QAAQ,MAAM,IAAI,UAAU,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,MAAM,IAAI,UAAU,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAiC;IAEjC,MAAM,aAAa,GAAuB;QACxC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC;QACxC,GAAG,EAAE,OAAO,CAAC,IAAI;QACjB,OAAO,EAAE;YACP,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxE,GAAG,OAAO,CAAC,OAAO;SACnB;QACD,MAAM,EAAE,OAAO,CAAC,KAAK;QACrB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,KAAK;QACd,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;KAC3B,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACpD,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;YAClE,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzE,CAAC;QAED,OAAO;YACL,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,OAAO;YACP,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface CachedOpenApiSpec {
|
|
2
|
+
apiUrl: string;
|
|
3
|
+
fetchedAt: string;
|
|
4
|
+
etag?: string;
|
|
5
|
+
specHash: string;
|
|
6
|
+
spec: unknown;
|
|
7
|
+
}
|
|
8
|
+
export declare function readOpenApiCache(apiUrl: string): CachedOpenApiSpec | undefined;
|
|
9
|
+
export declare function writeOpenApiCache(entry: CachedOpenApiSpec): void;
|
|
10
|
+
//# sourceMappingURL=openapi-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-cache.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/commands/api/lib/openapi-cache.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;CACf;AA+BD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAG9E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAShE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const CONFIG_PATH = join(homedir(), '.qa-use.json');
|
|
5
|
+
function normalizeApiUrl(apiUrl) {
|
|
6
|
+
return apiUrl.replace(/\/$/, '');
|
|
7
|
+
}
|
|
8
|
+
function readConfig() {
|
|
9
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function writeConfig(config) {
|
|
20
|
+
try {
|
|
21
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Ignore cache write failures (permissions, disk full, etc.)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function readOpenApiCache(apiUrl) {
|
|
28
|
+
const key = normalizeApiUrl(apiUrl);
|
|
29
|
+
return readConfig().openapi_cache?.[key];
|
|
30
|
+
}
|
|
31
|
+
export function writeOpenApiCache(entry) {
|
|
32
|
+
const key = normalizeApiUrl(entry.apiUrl);
|
|
33
|
+
const config = readConfig();
|
|
34
|
+
config.openapi_cache = config.openapi_cache || {};
|
|
35
|
+
config.openapi_cache[key] = {
|
|
36
|
+
...entry,
|
|
37
|
+
apiUrl: key,
|
|
38
|
+
};
|
|
39
|
+
writeConfig(config);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=openapi-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-cache.js","sourceRoot":"","sources":["../../../../../../src/cli/commands/api/lib/openapi-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;AAepD,SAAS,eAAe,CAAC,MAAc;IACrC,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,UAAU;IACjB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAgB,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,MAAmB;IACtC,IAAI,CAAC;QACH,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;IAC/D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,UAAU,EAAE,CAAC,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAwB;IACxD,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC;IAClD,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG;QAC1B,GAAG,KAAK;QACR,MAAM,EAAE,GAAG;KACZ,CAAC;IACF,WAAW,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class OpenApiError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare function formatMissingSpecError(apiUrl: string, detail?: string): string;
|
|
5
|
+
export declare function formatInvalidSpecError(detail: string): string;
|
|
6
|
+
export declare function formatStaleCacheWarning(apiUrl: string, fetchedAt: string, reason: string): string;
|
|
7
|
+
export declare function formatOfflineCacheMissError(apiUrl: string): string;
|
|
8
|
+
//# sourceMappingURL=openapi-errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-errors.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/commands/api/lib/openapi-errors.ts"],"names":[],"mappings":"AAAA,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAG9E;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjG;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class OpenApiError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'OpenApiError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export function formatMissingSpecError(apiUrl, detail) {
|
|
8
|
+
const suffix = detail ? `\nReason: ${detail}` : '';
|
|
9
|
+
return `Unable to load OpenAPI spec from ${apiUrl}/api/v1/openapi.json and no cached spec is available.${suffix}\nRun the command again when online or provide credentials with QA_USE_API_KEY.`;
|
|
10
|
+
}
|
|
11
|
+
export function formatInvalidSpecError(detail) {
|
|
12
|
+
return `OpenAPI spec is invalid: ${detail}`;
|
|
13
|
+
}
|
|
14
|
+
export function formatStaleCacheWarning(apiUrl, fetchedAt, reason) {
|
|
15
|
+
return `Warning: using stale cached OpenAPI spec for ${apiUrl} (cached at ${fetchedAt}). Refresh failed: ${reason}`;
|
|
16
|
+
}
|
|
17
|
+
export function formatOfflineCacheMissError(apiUrl) {
|
|
18
|
+
return `Offline mode requested, but no cached OpenAPI spec exists for ${apiUrl}. Run with --refresh while online first.`;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=openapi-errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-errors.js","sourceRoot":"","sources":["../../../../../../src/cli/commands/api/lib/openapi-errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc,EAAE,MAAe;IACpE,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,aAAa,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,OAAO,oCAAoC,MAAM,wDAAwD,MAAM,iFAAiF,CAAC;AACnM,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,OAAO,4BAA4B,MAAM,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,MAAc,EAAE,SAAiB,EAAE,MAAc;IACvF,OAAO,gDAAgD,MAAM,eAAe,SAAS,sBAAsB,MAAM,EAAE,CAAC;AACtH,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,MAAc;IACxD,OAAO,iEAAiE,MAAM,0CAA0C,CAAC;AAC3H,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type OpenApiRefreshMode = 'default' | 'refresh' | 'offline';
|
|
2
|
+
interface OpenApiPathOperation {
|
|
3
|
+
summary?: string;
|
|
4
|
+
operationId?: string;
|
|
5
|
+
tags?: string[];
|
|
6
|
+
parameters?: Array<{
|
|
7
|
+
name?: string;
|
|
8
|
+
in?: string;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
schema?: {
|
|
11
|
+
type?: string;
|
|
12
|
+
};
|
|
13
|
+
}>;
|
|
14
|
+
requestBody?: {
|
|
15
|
+
required?: boolean;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
interface OpenApiSpecDocument {
|
|
19
|
+
openapi: string;
|
|
20
|
+
paths: Record<string, Record<string, OpenApiPathOperation>>;
|
|
21
|
+
components?: {
|
|
22
|
+
securitySchemes?: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface NormalizedOperation {
|
|
26
|
+
key: string;
|
|
27
|
+
method: string;
|
|
28
|
+
path: string;
|
|
29
|
+
summary?: string;
|
|
30
|
+
operationId?: string;
|
|
31
|
+
tags: string[];
|
|
32
|
+
parameters: Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
in: 'path' | 'query' | 'header' | 'cookie' | 'unknown';
|
|
35
|
+
required: boolean;
|
|
36
|
+
schemaType?: string;
|
|
37
|
+
}>;
|
|
38
|
+
parameterCount: number;
|
|
39
|
+
requestBodyRequired: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface OpenApiSpecIndex {
|
|
42
|
+
raw: OpenApiSpecDocument;
|
|
43
|
+
operations: Record<string, NormalizedOperation>;
|
|
44
|
+
}
|
|
45
|
+
export interface LoadedOpenApiSpec {
|
|
46
|
+
source: 'live' | 'cache';
|
|
47
|
+
stale: boolean;
|
|
48
|
+
apiUrl: string;
|
|
49
|
+
fetchedAt: string;
|
|
50
|
+
etag?: string;
|
|
51
|
+
specHash: string;
|
|
52
|
+
index: OpenApiSpecIndex;
|
|
53
|
+
warnings: string[];
|
|
54
|
+
}
|
|
55
|
+
export interface LoadOpenApiSpecOptions {
|
|
56
|
+
apiUrl: string;
|
|
57
|
+
apiKey?: string;
|
|
58
|
+
refreshMode?: OpenApiRefreshMode;
|
|
59
|
+
}
|
|
60
|
+
export declare function loadOpenApiSpec(options: LoadOpenApiSpecOptions): Promise<LoadedOpenApiSpec>;
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=openapi-spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-spec.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/commands/api/lib/openapi-spec.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEnE,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,MAAM,CAAC,EAAE;YACP,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,CAAC;KACH,CAAC,CAAC;IACH,WAAW,CAAC,EAAE;QACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;CACH;AAED,UAAU,mBAAmB;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;IAC5D,UAAU,CAAC,EAAE;QACX,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC3C,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;QACvD,QAAQ,EAAE,OAAO,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,mBAAmB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAkHD,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0FjG"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readOpenApiCache, writeOpenApiCache } from './openapi-cache.js';
|
|
3
|
+
import { formatInvalidSpecError, formatMissingSpecError, formatOfflineCacheMissError, formatStaleCacheWarning, OpenApiError, } from './openapi-errors.js';
|
|
4
|
+
const HTTP_METHODS = new Set(['get', 'post', 'put', 'patch', 'delete', 'options', 'head']);
|
|
5
|
+
function normalizeApiUrl(apiUrl) {
|
|
6
|
+
return apiUrl.replace(/\/$/, '');
|
|
7
|
+
}
|
|
8
|
+
function buildOpenApiUrl(apiUrl) {
|
|
9
|
+
return `${normalizeApiUrl(apiUrl)}/api/v1/openapi.json`;
|
|
10
|
+
}
|
|
11
|
+
function computeSpecHash(spec) {
|
|
12
|
+
return createHash('sha256').update(JSON.stringify(spec)).digest('hex');
|
|
13
|
+
}
|
|
14
|
+
function validateOpenApiDocument(spec) {
|
|
15
|
+
if (!spec || typeof spec !== 'object') {
|
|
16
|
+
throw new OpenApiError(formatInvalidSpecError('expected an object payload'));
|
|
17
|
+
}
|
|
18
|
+
const candidate = spec;
|
|
19
|
+
if (!candidate.openapi || typeof candidate.openapi !== 'string') {
|
|
20
|
+
throw new OpenApiError(formatInvalidSpecError('missing `openapi` version string'));
|
|
21
|
+
}
|
|
22
|
+
if (!candidate.paths || typeof candidate.paths !== 'object' || Array.isArray(candidate.paths)) {
|
|
23
|
+
throw new OpenApiError(formatInvalidSpecError('missing `paths` object'));
|
|
24
|
+
}
|
|
25
|
+
if (candidate.components &&
|
|
26
|
+
candidate.components.securitySchemes !== undefined &&
|
|
27
|
+
(typeof candidate.components.securitySchemes !== 'object' ||
|
|
28
|
+
Array.isArray(candidate.components.securitySchemes))) {
|
|
29
|
+
throw new OpenApiError(formatInvalidSpecError('`components.securitySchemes` must be an object'));
|
|
30
|
+
}
|
|
31
|
+
return candidate;
|
|
32
|
+
}
|
|
33
|
+
function normalizeOperations(spec) {
|
|
34
|
+
const operations = {};
|
|
35
|
+
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
36
|
+
if (!pathItem || typeof pathItem !== 'object') {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
for (const [method, operation] of Object.entries(pathItem)) {
|
|
40
|
+
const normalizedMethod = method.toLowerCase();
|
|
41
|
+
if (!HTTP_METHODS.has(normalizedMethod)) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const op = operation || {};
|
|
45
|
+
const key = `${normalizedMethod.toUpperCase()} ${path}`;
|
|
46
|
+
operations[key] = {
|
|
47
|
+
key,
|
|
48
|
+
method: normalizedMethod.toUpperCase(),
|
|
49
|
+
path,
|
|
50
|
+
summary: op.summary,
|
|
51
|
+
operationId: op.operationId,
|
|
52
|
+
tags: Array.isArray(op.tags) ? op.tags.filter((tag) => typeof tag === 'string') : [],
|
|
53
|
+
parameters: Array.isArray(op.parameters)
|
|
54
|
+
? op.parameters
|
|
55
|
+
.filter((parameter) => Boolean(parameter?.name))
|
|
56
|
+
.map((parameter) => ({
|
|
57
|
+
name: String(parameter.name),
|
|
58
|
+
in: parameter.in === 'path' ||
|
|
59
|
+
parameter.in === 'query' ||
|
|
60
|
+
parameter.in === 'header' ||
|
|
61
|
+
parameter.in === 'cookie'
|
|
62
|
+
? parameter.in
|
|
63
|
+
: 'unknown',
|
|
64
|
+
required: Boolean(parameter.required),
|
|
65
|
+
schemaType: parameter.schema?.type,
|
|
66
|
+
}))
|
|
67
|
+
: [],
|
|
68
|
+
parameterCount: Array.isArray(op.parameters) ? op.parameters.length : 0,
|
|
69
|
+
requestBodyRequired: Boolean(op.requestBody?.required),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return operations;
|
|
74
|
+
}
|
|
75
|
+
function createIndex(spec) {
|
|
76
|
+
return {
|
|
77
|
+
raw: spec,
|
|
78
|
+
operations: normalizeOperations(spec),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function fromCache(cache, stale, warning) {
|
|
82
|
+
const spec = validateOpenApiDocument(cache.spec);
|
|
83
|
+
return {
|
|
84
|
+
source: 'cache',
|
|
85
|
+
stale,
|
|
86
|
+
apiUrl: cache.apiUrl,
|
|
87
|
+
fetchedAt: cache.fetchedAt,
|
|
88
|
+
etag: cache.etag,
|
|
89
|
+
specHash: cache.specHash,
|
|
90
|
+
index: createIndex(spec),
|
|
91
|
+
warnings: warning ? [warning] : [],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export async function loadOpenApiSpec(options) {
|
|
95
|
+
const apiUrl = normalizeApiUrl(options.apiUrl);
|
|
96
|
+
const refreshMode = options.refreshMode || 'default';
|
|
97
|
+
const cache = readOpenApiCache(apiUrl);
|
|
98
|
+
if (refreshMode === 'offline') {
|
|
99
|
+
if (!cache) {
|
|
100
|
+
throw new OpenApiError(formatOfflineCacheMissError(apiUrl));
|
|
101
|
+
}
|
|
102
|
+
return fromCache(cache, false);
|
|
103
|
+
}
|
|
104
|
+
const headers = {};
|
|
105
|
+
if (options.apiKey) {
|
|
106
|
+
headers.Authorization = `Bearer ${options.apiKey}`;
|
|
107
|
+
}
|
|
108
|
+
if (refreshMode !== 'refresh' && cache?.etag) {
|
|
109
|
+
headers['If-None-Match'] = cache.etag;
|
|
110
|
+
}
|
|
111
|
+
let response;
|
|
112
|
+
try {
|
|
113
|
+
response = await fetch(buildOpenApiUrl(apiUrl), {
|
|
114
|
+
method: 'GET',
|
|
115
|
+
headers,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (!cache) {
|
|
120
|
+
throw new OpenApiError(formatMissingSpecError(apiUrl, error instanceof Error ? error.message : 'network failure'));
|
|
121
|
+
}
|
|
122
|
+
return fromCache(cache, true, formatStaleCacheWarning(apiUrl, cache.fetchedAt, error instanceof Error ? error.message : 'network failure'));
|
|
123
|
+
}
|
|
124
|
+
if (response.status === 304) {
|
|
125
|
+
if (!cache) {
|
|
126
|
+
throw new OpenApiError(formatMissingSpecError(apiUrl, 'received 304 without cached spec'));
|
|
127
|
+
}
|
|
128
|
+
return fromCache(cache, false);
|
|
129
|
+
}
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
const reason = `HTTP ${response.status}`;
|
|
132
|
+
if (!cache) {
|
|
133
|
+
throw new OpenApiError(formatMissingSpecError(apiUrl, reason));
|
|
134
|
+
}
|
|
135
|
+
return fromCache(cache, true, formatStaleCacheWarning(apiUrl, cache.fetchedAt, reason));
|
|
136
|
+
}
|
|
137
|
+
let jsonPayload;
|
|
138
|
+
try {
|
|
139
|
+
jsonPayload = await response.json();
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
throw new OpenApiError(formatInvalidSpecError('response body is not valid JSON'));
|
|
143
|
+
}
|
|
144
|
+
const spec = validateOpenApiDocument(jsonPayload);
|
|
145
|
+
const specHash = computeSpecHash(spec);
|
|
146
|
+
const fetchedAt = new Date().toISOString();
|
|
147
|
+
const etag = response.headers.get('etag') ?? undefined;
|
|
148
|
+
writeOpenApiCache({
|
|
149
|
+
apiUrl,
|
|
150
|
+
fetchedAt,
|
|
151
|
+
etag,
|
|
152
|
+
specHash,
|
|
153
|
+
spec,
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
source: 'live',
|
|
157
|
+
stale: false,
|
|
158
|
+
apiUrl,
|
|
159
|
+
fetchedAt,
|
|
160
|
+
etag,
|
|
161
|
+
specHash,
|
|
162
|
+
index: createIndex(spec),
|
|
163
|
+
warnings: [],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=openapi-spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-spec.js","sourceRoot":"","sources":["../../../../../../src/cli/commands/api/lib/openapi-spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NormalizedOperation } from './openapi-spec.js';
|
|
2
|
+
export interface RenderListOptions {
|
|
3
|
+
source: 'live' | 'cache';
|
|
4
|
+
stale: boolean;
|
|
5
|
+
json?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function renderOperationList(operations: NormalizedOperation[], options: RenderListOptions): string;
|
|
8
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/commands/api/lib/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AASD,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,EAAE,iBAAiB,GACzB,MAAM,CAkCR"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function pad(value, width) {
|
|
2
|
+
if (value.length >= width) {
|
|
3
|
+
return value;
|
|
4
|
+
}
|
|
5
|
+
return `${value}${' '.repeat(width - value.length)}`;
|
|
6
|
+
}
|
|
7
|
+
export function renderOperationList(operations, options) {
|
|
8
|
+
if (options.json) {
|
|
9
|
+
return JSON.stringify({
|
|
10
|
+
source: options.source,
|
|
11
|
+
stale: options.stale,
|
|
12
|
+
count: operations.length,
|
|
13
|
+
operations,
|
|
14
|
+
}, null, 2);
|
|
15
|
+
}
|
|
16
|
+
if (operations.length === 0) {
|
|
17
|
+
return `No API operations matched your filters.\nSource: ${options.source}${options.stale ? ' (stale cache)' : ''}`;
|
|
18
|
+
}
|
|
19
|
+
const methodWidth = Math.max(6, ...operations.map((operation) => operation.method.length));
|
|
20
|
+
const pathWidth = Math.max(20, ...operations.map((operation) => operation.path.length));
|
|
21
|
+
const lines = [
|
|
22
|
+
`Source: ${options.source}${options.stale ? ' (stale cache)' : ''}`,
|
|
23
|
+
`${pad('METHOD', methodWidth)} ${pad('PATH', pathWidth)} SUMMARY`,
|
|
24
|
+
`${'-'.repeat(methodWidth)} ${'-'.repeat(pathWidth)} ${'-'.repeat(32)}`,
|
|
25
|
+
];
|
|
26
|
+
for (const operation of operations) {
|
|
27
|
+
lines.push(`${pad(operation.method, methodWidth)} ${pad(operation.path, pathWidth)} ${operation.summary || ''}`);
|
|
28
|
+
}
|
|
29
|
+
return lines.join('\n');
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../../../../../src/cli/commands/api/lib/output.ts"],"names":[],"mappings":"AAQA,SAAS,GAAG,CAAC,KAAa,EAAE,KAAa;IACvC,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,GAAG,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,UAAiC,EACjC,OAA0B;IAE1B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC,SAAS,CACnB;YACE,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,KAAK,EAAE,UAAU,CAAC,MAAM;YACxB,UAAU;SACX,EACD,IAAI,EACJ,CAAC,CACF,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,oDAAoD,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACtH,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3F,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAExF,MAAM,KAAK,GAAG;QACZ,WAAW,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE;QACnE,GAAG,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,KAAK,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,WAAW;QACnE,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE;KAC1E,CAAC;IAEF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CACR,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,KAAK,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,KAAK,SAAS,CAAC,OAAO,IAAI,EAAE,EAAE,CACvG,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ls.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/api/ls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,eAAO,MAAM,SAAS,SAwElB,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { loadConfig } from '../../lib/config.js';
|
|
3
|
+
import { error, formatError, warning } from '../../lib/output.js';
|
|
4
|
+
import { loadOpenApiSpec } from './lib/openapi-spec.js';
|
|
5
|
+
import { renderOperationList } from './lib/output.js';
|
|
6
|
+
function getRefreshMode(options) {
|
|
7
|
+
if (options.refresh && options.offline) {
|
|
8
|
+
throw new Error('Cannot use --refresh and --offline together');
|
|
9
|
+
}
|
|
10
|
+
if (options.refresh)
|
|
11
|
+
return 'refresh';
|
|
12
|
+
if (options.offline)
|
|
13
|
+
return 'offline';
|
|
14
|
+
return 'default';
|
|
15
|
+
}
|
|
16
|
+
export const lsCommand = new Command('ls')
|
|
17
|
+
.description('List available /api/v1 endpoints from OpenAPI')
|
|
18
|
+
.option('-X, --method <method>', 'Filter by HTTP method (GET, POST, PUT, PATCH, DELETE)')
|
|
19
|
+
.option('-q, --query <text>', 'Filter by text in path, summary, or operation id')
|
|
20
|
+
.option('--tag <tag>', 'Filter by OpenAPI tag')
|
|
21
|
+
.option('--json', 'Render JSON output')
|
|
22
|
+
.option('--refresh', 'Force refresh OpenAPI spec from server')
|
|
23
|
+
.option('--offline', 'Use cached OpenAPI spec only')
|
|
24
|
+
.action(async (options, command) => {
|
|
25
|
+
try {
|
|
26
|
+
const parentOptions = (command.parent?.opts() || {});
|
|
27
|
+
const effectiveOptions = {
|
|
28
|
+
...parentOptions,
|
|
29
|
+
...options,
|
|
30
|
+
};
|
|
31
|
+
const config = await loadConfig();
|
|
32
|
+
const apiUrl = config.api_url || process.env.QA_USE_API_URL || 'https://api.desplega.ai';
|
|
33
|
+
const apiKey = config.api_key || process.env.QA_USE_API_KEY;
|
|
34
|
+
const refreshMode = getRefreshMode(effectiveOptions);
|
|
35
|
+
const loadedSpec = await loadOpenApiSpec({ apiUrl, apiKey, refreshMode });
|
|
36
|
+
if (loadedSpec.stale) {
|
|
37
|
+
for (const message of loadedSpec.warnings) {
|
|
38
|
+
console.error(warning(message));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
let operations = Object.values(loadedSpec.index.operations);
|
|
42
|
+
if (effectiveOptions.method) {
|
|
43
|
+
const expectedMethod = effectiveOptions.method.toUpperCase();
|
|
44
|
+
operations = operations.filter((operation) => operation.method === expectedMethod);
|
|
45
|
+
}
|
|
46
|
+
if (effectiveOptions.query) {
|
|
47
|
+
const query = effectiveOptions.query.toLowerCase();
|
|
48
|
+
operations = operations.filter((operation) => {
|
|
49
|
+
return (operation.path.toLowerCase().includes(query) ||
|
|
50
|
+
(operation.summary || '').toLowerCase().includes(query) ||
|
|
51
|
+
(operation.operationId || '').toLowerCase().includes(query));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (effectiveOptions.tag) {
|
|
55
|
+
const tag = effectiveOptions.tag.toLowerCase();
|
|
56
|
+
operations = operations.filter((operation) => operation.tags.some((operationTag) => operationTag.toLowerCase() === tag));
|
|
57
|
+
}
|
|
58
|
+
operations.sort((a, b) => {
|
|
59
|
+
const pathCompare = a.path.localeCompare(b.path);
|
|
60
|
+
if (pathCompare !== 0) {
|
|
61
|
+
return pathCompare;
|
|
62
|
+
}
|
|
63
|
+
return a.method.localeCompare(b.method);
|
|
64
|
+
});
|
|
65
|
+
console.log(renderOperationList(operations, {
|
|
66
|
+
source: loadedSpec.source,
|
|
67
|
+
stale: loadedSpec.stale,
|
|
68
|
+
json: effectiveOptions.json,
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
console.error(error(`Failed to list API operations: ${formatError(err)}`));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=ls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ls.js","sourceRoot":"","sources":["../../../../../src/cli/commands/api/ls.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { type NormalizedOperation } from './lib/openapi-spec.js';
|
|
3
|
+
interface RequestRegistrationOptions {
|
|
4
|
+
endpointRequired?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function inferMethod(explicitMethod: string | undefined, path: string, operations: Record<string, NormalizedOperation>): string;
|
|
7
|
+
export declare function resolveOperation(path: string, method: string, operations: Record<string, NormalizedOperation>): NormalizedOperation | undefined;
|
|
8
|
+
export declare function registerApiRequestAction(command: Command, registrationOptions?: RequestRegistrationOptions): Command;
|
|
9
|
+
export declare const requestCommand: Command;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/api/request.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,uBAAuB,CAAC;AAa/B,UAAU,0BAA0B;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAoDD,wBAAgB,WAAW,CACzB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAC9C,MAAM,CAwBR;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAC9C,mBAAmB,GAAG,SAAS,CAIjC;AA0CD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,mBAAmB,GAAE,0BAA+B,GACnD,OAAO,CA0IT;AAED,eAAO,MAAM,cAAc,SAG1B,CAAC"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { loadConfig } from '../../lib/config.js';
|
|
4
|
+
import { error, formatError, warning } from '../../lib/output.js';
|
|
5
|
+
import { executeApiRequest, formatApiResponseError } from './lib/http.js';
|
|
6
|
+
import { loadOpenApiSpec, } from './lib/openapi-spec.js';
|
|
7
|
+
function collectValues(value, previous = []) {
|
|
8
|
+
previous.push(value);
|
|
9
|
+
return previous;
|
|
10
|
+
}
|
|
11
|
+
function parseKeyValuePairs(entries, separator) {
|
|
12
|
+
const output = {};
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
const idx = entry.indexOf(separator);
|
|
15
|
+
if (idx <= 0) {
|
|
16
|
+
throw new Error(`Invalid entry \`${entry}\`. Expected format key${separator}value`);
|
|
17
|
+
}
|
|
18
|
+
const key = entry.slice(0, idx).trim();
|
|
19
|
+
const value = entry.slice(idx + 1).trim();
|
|
20
|
+
if (!key) {
|
|
21
|
+
throw new Error(`Invalid entry \`${entry}\`: key cannot be empty`);
|
|
22
|
+
}
|
|
23
|
+
output[key] = value;
|
|
24
|
+
}
|
|
25
|
+
return output;
|
|
26
|
+
}
|
|
27
|
+
function parseEndpoint(endpoint) {
|
|
28
|
+
const parsed = new URL(endpoint, 'https://qa-use.local');
|
|
29
|
+
const query = {};
|
|
30
|
+
for (const [key, value] of parsed.searchParams.entries()) {
|
|
31
|
+
query[key] = value;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
path: parsed.pathname,
|
|
35
|
+
query,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function pathTemplateToRegExp(pathTemplate) {
|
|
39
|
+
const escaped = pathTemplate.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
40
|
+
const pattern = escaped.replace(/\\\{[^/]+\\\}/g, '[^/]+');
|
|
41
|
+
return new RegExp(`^${pattern}$`);
|
|
42
|
+
}
|
|
43
|
+
function resolveOperationCandidates(path, operations) {
|
|
44
|
+
return Object.values(operations).filter((operation) => pathTemplateToRegExp(operation.path).test(path));
|
|
45
|
+
}
|
|
46
|
+
export function inferMethod(explicitMethod, path, operations) {
|
|
47
|
+
if (explicitMethod) {
|
|
48
|
+
return explicitMethod.toUpperCase();
|
|
49
|
+
}
|
|
50
|
+
const candidates = resolveOperationCandidates(path, operations);
|
|
51
|
+
if (candidates.length === 1) {
|
|
52
|
+
return candidates[0].method;
|
|
53
|
+
}
|
|
54
|
+
const hasGet = candidates.find((candidate) => candidate.method === 'GET');
|
|
55
|
+
if (hasGet) {
|
|
56
|
+
return 'GET';
|
|
57
|
+
}
|
|
58
|
+
if (candidates.length > 1) {
|
|
59
|
+
const methods = candidates
|
|
60
|
+
.map((candidate) => candidate.method)
|
|
61
|
+
.sort()
|
|
62
|
+
.join(', ');
|
|
63
|
+
throw new Error(`Multiple operations match ${path}. Specify --method one of: ${methods}`);
|
|
64
|
+
}
|
|
65
|
+
return 'GET';
|
|
66
|
+
}
|
|
67
|
+
export function resolveOperation(path, method, operations) {
|
|
68
|
+
return resolveOperationCandidates(path, operations).find((operation) => operation.method === method);
|
|
69
|
+
}
|
|
70
|
+
function validateFieldType(name, value, schemaType) {
|
|
71
|
+
if (!schemaType) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (schemaType === 'integer' && !Number.isInteger(Number(value))) {
|
|
75
|
+
throw new Error(`Field \`${name}\` must be an integer`);
|
|
76
|
+
}
|
|
77
|
+
if (schemaType === 'number' && Number.isNaN(Number(value))) {
|
|
78
|
+
throw new Error(`Field \`${name}\` must be a number`);
|
|
79
|
+
}
|
|
80
|
+
if (schemaType === 'boolean' && value !== 'true' && value !== 'false') {
|
|
81
|
+
throw new Error(`Field \`${name}\` must be true or false`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function loadInputBody(path) {
|
|
85
|
+
const raw = await fs.readFile(path, 'utf-8');
|
|
86
|
+
try {
|
|
87
|
+
return JSON.parse(raw);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
throw new Error(`Failed to parse JSON input file: ${path}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function getRefreshMode(options) {
|
|
94
|
+
if (options.refresh && options.offline) {
|
|
95
|
+
throw new Error('Cannot use --refresh and --offline together');
|
|
96
|
+
}
|
|
97
|
+
if (options.refresh)
|
|
98
|
+
return 'refresh';
|
|
99
|
+
if (options.offline)
|
|
100
|
+
return 'offline';
|
|
101
|
+
return 'default';
|
|
102
|
+
}
|
|
103
|
+
function shouldUseQuery(method) {
|
|
104
|
+
return method === 'GET' || method === 'HEAD' || method === 'DELETE' || method === 'OPTIONS';
|
|
105
|
+
}
|
|
106
|
+
export function registerApiRequestAction(command, registrationOptions = {}) {
|
|
107
|
+
const endpointArgument = registrationOptions.endpointRequired === false ? '[endpoint]' : '<endpoint>';
|
|
108
|
+
return command
|
|
109
|
+
.argument(endpointArgument, 'API endpoint path, e.g. /api/v1/tests')
|
|
110
|
+
.option('-X, --method <method>', 'HTTP method (GET, POST, PUT, PATCH, DELETE)')
|
|
111
|
+
.option('-f, --field <key=value>', 'Add request field (query for GET, body for others)', collectValues, [])
|
|
112
|
+
.option('-H, --header <name:value>', 'Add request header', collectValues, [])
|
|
113
|
+
.option('--input <file>', 'JSON body file path')
|
|
114
|
+
.option('--include', 'Include response status and headers in output')
|
|
115
|
+
.option('--raw', 'Print raw response body')
|
|
116
|
+
.option('--refresh', 'Force refresh OpenAPI spec from server')
|
|
117
|
+
.option('--offline', 'Use cached OpenAPI spec only')
|
|
118
|
+
.action(async (endpoint, options) => {
|
|
119
|
+
try {
|
|
120
|
+
if (!endpoint) {
|
|
121
|
+
throw new Error('Endpoint path is required. Use `qa-use api ls` to list available endpoints.');
|
|
122
|
+
}
|
|
123
|
+
const config = await loadConfig();
|
|
124
|
+
const apiUrl = config.api_url || process.env.QA_USE_API_URL || 'https://api.desplega.ai';
|
|
125
|
+
const apiKey = config.api_key || process.env.QA_USE_API_KEY;
|
|
126
|
+
const refreshMode = getRefreshMode(options);
|
|
127
|
+
const { path, query: urlQuery } = parseEndpoint(endpoint);
|
|
128
|
+
const fieldMap = parseKeyValuePairs(options.field || [], '=');
|
|
129
|
+
const headerMap = parseKeyValuePairs(options.header || [], ':');
|
|
130
|
+
const openApi = await loadOpenApiSpec({
|
|
131
|
+
apiUrl,
|
|
132
|
+
apiKey,
|
|
133
|
+
refreshMode,
|
|
134
|
+
});
|
|
135
|
+
if (openApi.stale) {
|
|
136
|
+
for (const message of openApi.warnings) {
|
|
137
|
+
console.error(warning(message));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const method = inferMethod(options.method, path, openApi.index.operations);
|
|
141
|
+
const operation = resolveOperation(path, method, openApi.index.operations);
|
|
142
|
+
if (!operation && !options.method) {
|
|
143
|
+
throw new Error(`No OpenAPI operation found for ${path}. Use --method to call unknown endpoints explicitly.`);
|
|
144
|
+
}
|
|
145
|
+
const query = { ...urlQuery };
|
|
146
|
+
let body;
|
|
147
|
+
if (options.input) {
|
|
148
|
+
body = await loadInputBody(options.input);
|
|
149
|
+
}
|
|
150
|
+
if (shouldUseQuery(method)) {
|
|
151
|
+
Object.assign(query, fieldMap);
|
|
152
|
+
}
|
|
153
|
+
else if (Object.keys(fieldMap).length > 0) {
|
|
154
|
+
if (options.input) {
|
|
155
|
+
throw new Error('Cannot combine --input with --field for non-GET methods');
|
|
156
|
+
}
|
|
157
|
+
body = fieldMap;
|
|
158
|
+
}
|
|
159
|
+
if (operation) {
|
|
160
|
+
for (const parameter of operation.parameters) {
|
|
161
|
+
if (parameter.in === 'query') {
|
|
162
|
+
const value = query[parameter.name];
|
|
163
|
+
if (parameter.required && (value === undefined || value === '')) {
|
|
164
|
+
throw new Error(`Missing required query parameter: ${parameter.name}`);
|
|
165
|
+
}
|
|
166
|
+
if (value !== undefined) {
|
|
167
|
+
validateFieldType(parameter.name, value, parameter.schemaType);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (operation.requestBodyRequired && body === undefined) {
|
|
172
|
+
throw new Error(`Operation ${method} ${operation.path} requires a request body`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const response = await executeApiRequest({
|
|
176
|
+
apiUrl,
|
|
177
|
+
apiKey,
|
|
178
|
+
method,
|
|
179
|
+
path,
|
|
180
|
+
headers: headerMap,
|
|
181
|
+
query,
|
|
182
|
+
body,
|
|
183
|
+
});
|
|
184
|
+
if (options.include) {
|
|
185
|
+
console.log(`HTTP ${response.status} ${response.statusText}`);
|
|
186
|
+
for (const [name, value] of Object.entries(response.headers)) {
|
|
187
|
+
console.log(`${name}: ${value}`);
|
|
188
|
+
}
|
|
189
|
+
console.log('');
|
|
190
|
+
}
|
|
191
|
+
if (response.status >= 400) {
|
|
192
|
+
console.error(error(formatApiResponseError(response.status, response.statusText, response.data)));
|
|
193
|
+
if (response.data !== undefined) {
|
|
194
|
+
if (typeof response.data === 'string') {
|
|
195
|
+
console.error(response.data);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
console.error(JSON.stringify(response.data, null, 2));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
if (options.raw) {
|
|
204
|
+
if (typeof response.data === 'string') {
|
|
205
|
+
console.log(response.data);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
console.log(JSON.stringify(response.data));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else if (typeof response.data === 'string') {
|
|
212
|
+
console.log(response.data);
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
console.log(JSON.stringify(response.data, null, 2));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
console.error(error(`API request failed: ${formatError(err)}`));
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
export const requestCommand = registerApiRequestAction(new Command('request').description('Send API requests using OpenAPI metadata'), { endpointRequired: true });
|
|
225
|
+
//# sourceMappingURL=request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../../../../src/cli/commands/api/request.ts"],"names":[],"mappings":"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"}
|
package/dist/src/cli/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
6
|
import { Command } from 'commander';
|
|
7
|
+
import { apiCommand } from './commands/api/index.js';
|
|
7
8
|
import { browserCommand } from './commands/browser/index.js';
|
|
8
9
|
import { infoCommand } from './commands/info.js';
|
|
9
10
|
import { installDepsCommand } from './commands/install-deps.js';
|
|
@@ -25,6 +26,7 @@ program.addCommand(mcpCommand);
|
|
|
25
26
|
program.addCommand(browserCommand);
|
|
26
27
|
program.addCommand(installDepsCommand);
|
|
27
28
|
program.addCommand(updateCommand);
|
|
29
|
+
program.addCommand(apiCommand);
|
|
28
30
|
// Auto-update hint (reads from cache, fires async fetch — never blocks)
|
|
29
31
|
if (!shouldSkipCheck(process.argv)) {
|
|
30
32
|
showUpdateHintIfAvailable(version);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AAEA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,yBAAyB,GAC1B,MAAM,uBAAuB,CAAC;AAE/B,gCAAgC;AAChC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,oCAAoC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAE1F,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;AACvC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AAEA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,yBAAyB,GAC1B,MAAM,uBAAuB,CAAC;AAE/B,gCAAgC;AAChC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAElD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,oCAAoC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAE1F,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;AACvC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAE/B,wEAAwE;AACxE,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;IACnC,yBAAyB,CAAC,OAAO,CAAC,CAAC;IACnC,mBAAmB,EAAE,CAAC;AACxB,CAAC;AAED,sCAAsC;AACtC,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;AAElE,+BAA+B;AAC/B,OAAO,CAAC,KAAK,EAAE,CAAC"}
|