@skhema/cli 0.3.1
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 +163 -0
- package/bin/skhema.js +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +92 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +154 -0
- package/dist/commands/api.d.ts +19 -0
- package/dist/commands/api.d.ts.map +1 -0
- package/dist/commands/api.js +97 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +403 -0
- package/dist/commands/component.d.ts +3 -0
- package/dist/commands/component.d.ts.map +1 -0
- package/dist/commands/component.js +221 -0
- package/dist/commands/contribute.d.ts +3 -0
- package/dist/commands/contribute.d.ts.map +1 -0
- package/dist/commands/contribute.js +318 -0
- package/dist/commands/element.d.ts +7 -0
- package/dist/commands/element.d.ts.map +1 -0
- package/dist/commands/element.js +167 -0
- package/dist/commands/export.d.ts +9 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +42 -0
- package/dist/commands/init.d.ts +49 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +275 -0
- package/dist/commands/link.d.ts +8 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +70 -0
- package/dist/commands/resource.d.ts +3 -0
- package/dist/commands/resource.d.ts.map +1 -0
- package/dist/commands/resource.js +88 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +113 -0
- package/dist/commands/strategy.d.ts +3 -0
- package/dist/commands/strategy.d.ts.map +1 -0
- package/dist/commands/strategy.js +143 -0
- package/dist/commands/webhook.d.ts +3 -0
- package/dist/commands/webhook.d.ts.map +1 -0
- package/dist/commands/webhook.js +77 -0
- package/dist/commands/workspace.d.ts +8 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +219 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/api/client.d.ts +27 -0
- package/dist/lib/api/client.d.ts.map +1 -0
- package/dist/lib/api/client.js +71 -0
- package/dist/lib/api/command-helpers.d.ts +27 -0
- package/dist/lib/api/command-helpers.d.ts.map +1 -0
- package/dist/lib/api/command-helpers.js +77 -0
- package/dist/lib/api/credential-context.d.ts +3 -0
- package/dist/lib/api/credential-context.d.ts.map +1 -0
- package/dist/lib/api/credential-context.js +13 -0
- package/dist/lib/api/export-cli.d.ts +14 -0
- package/dist/lib/api/export-cli.d.ts.map +1 -0
- package/dist/lib/api/export-cli.js +26 -0
- package/dist/lib/api/formats.d.ts +12 -0
- package/dist/lib/api/formats.d.ts.map +1 -0
- package/dist/lib/api/formats.js +18 -0
- package/dist/lib/api/passthrough.d.ts +34 -0
- package/dist/lib/api/passthrough.d.ts.map +1 -0
- package/dist/lib/api/passthrough.js +106 -0
- package/dist/lib/api/payload.d.ts +16 -0
- package/dist/lib/api/payload.d.ts.map +1 -0
- package/dist/lib/api/payload.js +53 -0
- package/dist/lib/api/run-command.d.ts +69 -0
- package/dist/lib/api/run-command.d.ts.map +1 -0
- package/dist/lib/api/run-command.js +127 -0
- package/dist/lib/api/scope.d.ts +8 -0
- package/dist/lib/api/scope.d.ts.map +1 -0
- package/dist/lib/api/scope.js +16 -0
- package/dist/lib/api/transfer.d.ts +57 -0
- package/dist/lib/api/transfer.d.ts.map +1 -0
- package/dist/lib/api/transfer.js +153 -0
- package/dist/lib/auth/api-key-admin.d.ts +39 -0
- package/dist/lib/auth/api-key-admin.d.ts.map +1 -0
- package/dist/lib/auth/api-key-admin.js +66 -0
- package/dist/lib/auth/api-key-store.d.ts +8 -0
- package/dist/lib/auth/api-key-store.d.ts.map +1 -0
- package/dist/lib/auth/api-key-store.js +109 -0
- package/dist/lib/auth/device-flow.d.ts +4 -0
- package/dist/lib/auth/device-flow.d.ts.map +1 -0
- package/dist/lib/auth/device-flow.js +109 -0
- package/dist/lib/auth/entitlements.d.ts +16 -0
- package/dist/lib/auth/entitlements.d.ts.map +1 -0
- package/dist/lib/auth/entitlements.js +89 -0
- package/dist/lib/auth/localhost-flow.d.ts +4 -0
- package/dist/lib/auth/localhost-flow.d.ts.map +1 -0
- package/dist/lib/auth/localhost-flow.js +134 -0
- package/dist/lib/auth/oauth-client.d.ts +33 -0
- package/dist/lib/auth/oauth-client.d.ts.map +1 -0
- package/dist/lib/auth/oauth-client.js +96 -0
- package/dist/lib/auth/token-store.d.ts +6 -0
- package/dist/lib/auth/token-store.d.ts.map +1 -0
- package/dist/lib/auth/token-store.js +136 -0
- package/dist/lib/auth/types.d.ts +49 -0
- package/dist/lib/auth/types.d.ts.map +1 -0
- package/dist/lib/auth/types.js +1 -0
- package/dist/lib/banner.d.ts +2 -0
- package/dist/lib/banner.d.ts.map +1 -0
- package/dist/lib/banner.js +82 -0
- package/dist/lib/config-store.d.ts +23 -0
- package/dist/lib/config-store.d.ts.map +1 -0
- package/dist/lib/config-store.js +53 -0
- package/dist/lib/config.d.ts +20 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +33 -0
- package/dist/lib/contribute/author-resolver.d.ts +13 -0
- package/dist/lib/contribute/author-resolver.d.ts.map +1 -0
- package/dist/lib/contribute/author-resolver.js +63 -0
- package/dist/lib/contribute/embed-generator.d.ts +4 -0
- package/dist/lib/contribute/embed-generator.d.ts.map +1 -0
- package/dist/lib/contribute/embed-generator.js +108 -0
- package/dist/lib/contribute/link-generator.d.ts +11 -0
- package/dist/lib/contribute/link-generator.d.ts.map +1 -0
- package/dist/lib/contribute/link-generator.js +119 -0
- package/dist/lib/contribute/method-schema.d.ts +239 -0
- package/dist/lib/contribute/method-schema.d.ts.map +1 -0
- package/dist/lib/contribute/method-schema.js +172 -0
- package/dist/lib/contribute/profile-resolver.d.ts +2 -0
- package/dist/lib/contribute/profile-resolver.d.ts.map +1 -0
- package/dist/lib/contribute/profile-resolver.js +25 -0
- package/dist/lib/contribute/types.d.ts +91 -0
- package/dist/lib/contribute/types.d.ts.map +1 -0
- package/dist/lib/contribute/types.js +1 -0
- package/dist/lib/mcp/registrar.d.ts +26 -0
- package/dist/lib/mcp/registrar.d.ts.map +1 -0
- package/dist/lib/mcp/registrar.js +69 -0
- package/dist/lib/mcp/registry.d.ts +54 -0
- package/dist/lib/mcp/registry.d.ts.map +1 -0
- package/dist/lib/mcp/registry.js +105 -0
- package/dist/lib/open.d.ts +2 -0
- package/dist/lib/open.d.ts.map +1 -0
- package/dist/lib/open.js +24 -0
- package/dist/lib/output.d.ts +25 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +74 -0
- package/dist/lib/skills/installer.d.ts +4 -0
- package/dist/lib/skills/installer.d.ts.map +1 -0
- package/dist/lib/skills/installer.js +100 -0
- package/dist/lib/skills/lock.d.ts +4 -0
- package/dist/lib/skills/lock.d.ts.map +1 -0
- package/dist/lib/skills/lock.js +28 -0
- package/dist/lib/skills/platform-detect.d.ts +4 -0
- package/dist/lib/skills/platform-detect.d.ts.map +1 -0
- package/dist/lib/skills/platform-detect.js +89 -0
- package/dist/lib/skills/types.d.ts +29 -0
- package/dist/lib/skills/types.d.ts.map +1 -0
- package/dist/lib/skills/types.js +1 -0
- package/dist/postinstall.d.ts +2 -0
- package/dist/postinstall.d.ts.map +1 -0
- package/dist/postinstall.js +11 -0
- package/package.json +67 -0
- package/skills/.manifest.json +15 -0
- package/skills/skhema-challenge-framing/SKILL.md +28 -0
- package/skills/skhema-coherence-check/SKILL.md +28 -0
- package/skills/skhema-element-decomposition/SKILL.md +28 -0
- package/skills/skhema-element-writer/SKILL.md +20 -0
- package/skills/skhema-judgment-audit/SKILL.md +28 -0
- package/skills/skhema-semantic-sharpening/SKILL.md +28 -0
- package/skills/skhema-strategy-advisor/SKILL.md +20 -0
- package/skills/skhema-workspace-navigator/SKILL.md +20 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { getDefaultWorkspaceId } from '../config-store.js';
|
|
3
|
+
import { UsageError } from './run-command.js';
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the target workspace for a workspace-scoped command: the explicit
|
|
6
|
+
* `--workspace` flag wins, else the stored default (`skhema workspace use`).
|
|
7
|
+
* Throws a {@link UsageError} (exit 2) when neither is set.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveWorkspaceId(flag) {
|
|
10
|
+
const workspaceId = flag ?? getDefaultWorkspaceId();
|
|
11
|
+
if (!workspaceId) {
|
|
12
|
+
throw new UsageError('No workspace. Pass --workspace <id> or set a default with `skhema workspace use <id>`.');
|
|
13
|
+
}
|
|
14
|
+
return workspaceId;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Read a full JSON request payload from `--file <path>` (or `-` for stdin).
|
|
18
|
+
* Returns `undefined` when no path is given. A malformed file is a usage error.
|
|
19
|
+
*/
|
|
20
|
+
export function readPayloadFile(filePath) {
|
|
21
|
+
if (!filePath)
|
|
22
|
+
return undefined;
|
|
23
|
+
let raw;
|
|
24
|
+
try {
|
|
25
|
+
// fd 0 = stdin, so `--file -` reads a piped payload.
|
|
26
|
+
raw = fs.readFileSync(filePath === '-' ? 0 : filePath, 'utf-8');
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
throw new UsageError(`Could not read payload file "${filePath}": ${err instanceof Error ? err.message : String(err)}`);
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const parsed = JSON.parse(raw);
|
|
33
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
34
|
+
throw new Error('expected a JSON object');
|
|
35
|
+
}
|
|
36
|
+
return parsed;
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
throw new UsageError(`Invalid JSON in payload file "${filePath}": ${err instanceof Error ? err.message : String(err)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build a request body from an optional `--file` base merged with field flags.
|
|
44
|
+
* Flag values that are `undefined` are dropped, so explicit flags override the
|
|
45
|
+
* file and unset flags leave the file's value intact.
|
|
46
|
+
*/
|
|
47
|
+
export function buildBody(base, fields) {
|
|
48
|
+
const body = { ...(base ?? {}) };
|
|
49
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
50
|
+
if (value !== undefined)
|
|
51
|
+
body[key] = value;
|
|
52
|
+
}
|
|
53
|
+
return body;
|
|
54
|
+
}
|
|
55
|
+
/** Narrow an unknown value to a plain record for defensive field access. */
|
|
56
|
+
export function asRecord(value) {
|
|
57
|
+
return value && typeof value === 'object'
|
|
58
|
+
? value
|
|
59
|
+
: {};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Coerce a list-shaped API response to an array. The gateway wraps collections
|
|
63
|
+
* under a resource key (`{ workspaces: [...] }`); fall back to the first array
|
|
64
|
+
* property, or the value itself when it is already an array.
|
|
65
|
+
*/
|
|
66
|
+
export function coerceList(value, key) {
|
|
67
|
+
if (Array.isArray(value))
|
|
68
|
+
return value;
|
|
69
|
+
const record = asRecord(value);
|
|
70
|
+
if (key && Array.isArray(record[key]))
|
|
71
|
+
return record[key];
|
|
72
|
+
for (const v of Object.values(record)) {
|
|
73
|
+
if (Array.isArray(v))
|
|
74
|
+
return v;
|
|
75
|
+
}
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-context.d.ts","sourceRoot":"","sources":["../../../src/lib/api/credential-context.ts"],"names":[],"mappings":"AAQA,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAE7D;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Holds the value of the global `--api-key` flag for the lifetime of one CLI
|
|
3
|
+
* invocation. Set once from the root command's `preAction` hook (mirroring
|
|
4
|
+
* `setGlobalOptions`), then read by `resolveCredential()` as the highest-priority
|
|
5
|
+
* credential source.
|
|
6
|
+
*/
|
|
7
|
+
let apiKeyFlag;
|
|
8
|
+
export function setApiKeyFlag(value) {
|
|
9
|
+
apiKeyFlag = value;
|
|
10
|
+
}
|
|
11
|
+
export function getApiKeyFlag() {
|
|
12
|
+
return apiKeyFlag;
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ExportResult } from './transfer.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validate export output flags before any API call:
|
|
4
|
+
* - `--out -` streams raw binary to stdout, which would corrupt the `--json`
|
|
5
|
+
* envelope, so the two are mutually exclusive.
|
|
6
|
+
* - `--out` and `--email` are two different deliveries; pick one.
|
|
7
|
+
*/
|
|
8
|
+
export declare function assertExportOutput(opts: {
|
|
9
|
+
out?: string;
|
|
10
|
+
email?: string;
|
|
11
|
+
}): void;
|
|
12
|
+
/** Human summary line for a completed export. */
|
|
13
|
+
export declare function reportExport(result: ExportResult): void;
|
|
14
|
+
//# sourceMappingURL=export-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-cli.d.ts","sourceRoot":"","sources":["../../../src/lib/api/export-cli.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,GAAG,IAAI,CASP;AAED,iDAAiD;AACjD,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAMvD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getGlobalOptions, log } from '../output.js';
|
|
3
|
+
import { UsageError } from './run-command.js';
|
|
4
|
+
/**
|
|
5
|
+
* Validate export output flags before any API call:
|
|
6
|
+
* - `--out -` streams raw binary to stdout, which would corrupt the `--json`
|
|
7
|
+
* envelope, so the two are mutually exclusive.
|
|
8
|
+
* - `--out` and `--email` are two different deliveries; pick one.
|
|
9
|
+
*/
|
|
10
|
+
export function assertExportOutput(opts) {
|
|
11
|
+
if (opts.out === '-' && getGlobalOptions().json === true) {
|
|
12
|
+
throw new UsageError('Cannot combine --json with --out - (raw binary to stdout).');
|
|
13
|
+
}
|
|
14
|
+
if (opts.email && opts.out) {
|
|
15
|
+
throw new UsageError('Use either --out or --email, not both.');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Human summary line for a completed export. */
|
|
19
|
+
export function reportExport(result) {
|
|
20
|
+
if (result.delivered === 'email') {
|
|
21
|
+
log(chalk.green(`Emailed ${result.format} export to ${result.recipient}`));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
log(chalk.green(`Wrote ${result.format} export to ${result.path}`));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Single-document export formats (component / strategy). */
|
|
2
|
+
export declare const DOCUMENT_FORMATS: readonly ["pdf", "docx", "md", "json"];
|
|
3
|
+
/** Full-portability workspace export formats. */
|
|
4
|
+
export declare const WORKSPACE_FORMATS: readonly ["xlsx", "csv", "json"];
|
|
5
|
+
export type DocumentFormat = (typeof DOCUMENT_FORMATS)[number];
|
|
6
|
+
export type WorkspaceFormat = (typeof WORKSPACE_FORMATS)[number];
|
|
7
|
+
/**
|
|
8
|
+
* Validate a `--format` value client-side against the accepted set, so a typo
|
|
9
|
+
* fails fast with exit 2 rather than a round-trip. Returns the narrowed value.
|
|
10
|
+
*/
|
|
11
|
+
export declare function assertFormat<T extends string>(value: string | undefined, allowed: readonly T[]): T;
|
|
12
|
+
//# sourceMappingURL=formats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formats.d.ts","sourceRoot":"","sources":["../../../src/lib/api/formats.ts"],"names":[],"mappings":"AAEA,6DAA6D;AAC7D,eAAO,MAAM,gBAAgB,wCAAyC,CAAA;AACtE,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,kCAAmC,CAAA;AAEjE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAA;AAC9D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,OAAO,EAAE,SAAS,CAAC,EAAE,GACpB,CAAC,CAUH"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { UsageError } from './run-command.js';
|
|
2
|
+
/** Single-document export formats (component / strategy). */
|
|
3
|
+
export const DOCUMENT_FORMATS = ['pdf', 'docx', 'md', 'json'];
|
|
4
|
+
/** Full-portability workspace export formats. */
|
|
5
|
+
export const WORKSPACE_FORMATS = ['xlsx', 'csv', 'json'];
|
|
6
|
+
/**
|
|
7
|
+
* Validate a `--format` value client-side against the accepted set, so a typo
|
|
8
|
+
* fails fast with exit 2 rather than a round-trip. Returns the narrowed value.
|
|
9
|
+
*/
|
|
10
|
+
export function assertFormat(value, allowed) {
|
|
11
|
+
if (!value) {
|
|
12
|
+
throw new UsageError(`--format is required (one of ${allowed.join(', ')}).`);
|
|
13
|
+
}
|
|
14
|
+
if (!allowed.includes(value)) {
|
|
15
|
+
throw new UsageError(`Invalid --format "${value}". Expected one of: ${allowed.join(', ')}.`);
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { HttpMethod } from '@skhema/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Validate and normalize a method token (case-insensitive) to an
|
|
4
|
+
* {@link HttpMethod}. Throws {@link UsageError} (exit 2) on anything else.
|
|
5
|
+
*/
|
|
6
|
+
export declare function normalizeMethod(raw: string): HttpMethod;
|
|
7
|
+
/**
|
|
8
|
+
* Normalize a caller-supplied path to an absolute, `/v1`-prefixed gateway path.
|
|
9
|
+
*
|
|
10
|
+
* Accepts `workspaces`, `/workspaces`, `/v1/workspaces`, or a full
|
|
11
|
+
* `https://api.skhema.com/v1/...` URL whose host matches `apiBaseUrl`. Any other
|
|
12
|
+
* host is refused (the passthrough only reaches the Skhema gateway). Preserves a
|
|
13
|
+
* query string already present in the path.
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalizePath(raw: string, apiBaseUrl: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the `--data` argument into a JSON body. Forms:
|
|
18
|
+
* - `@path` — read JSON from a file (via `readFile`).
|
|
19
|
+
* - `-` — read JSON from stdin (via `readStdin`).
|
|
20
|
+
* - anything else — parse the literal as JSON.
|
|
21
|
+
*
|
|
22
|
+
* Returns `undefined` when `raw` is undefined. Throws {@link UsageError} on
|
|
23
|
+
* invalid JSON or a non-object top-level value.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveDataBody(raw: string | undefined, io: {
|
|
26
|
+
readFile: (path: string) => Promise<string>;
|
|
27
|
+
readStdin: () => Promise<string>;
|
|
28
|
+
}): Promise<Record<string, unknown> | undefined>;
|
|
29
|
+
/**
|
|
30
|
+
* Parse repeated `--query k=v` tokens into an entry list (preserving repeats and
|
|
31
|
+
* order). A token missing `=` is a usage error.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseQueryPairs(raw: string[] | undefined): Array<[string, string]>;
|
|
34
|
+
//# sourceMappingURL=passthrough.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passthrough.d.ts","sourceRoot":"","sources":["../../../src/lib/api/passthrough.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAW7C;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAQvD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAyBrE;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,EAAE,EAAE;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3C,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CACjC,GACA,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CA+B9C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EAAE,GAAG,SAAS,GACxB,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CASzB"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { UsageError } from './run-command.js';
|
|
2
|
+
/** HTTP verbs the raw `skhema api` passthrough accepts. */
|
|
3
|
+
const SUPPORTED_METHODS = [
|
|
4
|
+
'GET',
|
|
5
|
+
'POST',
|
|
6
|
+
'PUT',
|
|
7
|
+
'DELETE',
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* Validate and normalize a method token (case-insensitive) to an
|
|
11
|
+
* {@link HttpMethod}. Throws {@link UsageError} (exit 2) on anything else.
|
|
12
|
+
*/
|
|
13
|
+
export function normalizeMethod(raw) {
|
|
14
|
+
const upper = raw.toUpperCase();
|
|
15
|
+
if (!SUPPORTED_METHODS.includes(upper)) {
|
|
16
|
+
throw new UsageError(`Unsupported method "${raw}". Use one of: ${SUPPORTED_METHODS.join(', ')}.`);
|
|
17
|
+
}
|
|
18
|
+
return upper;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Normalize a caller-supplied path to an absolute, `/v1`-prefixed gateway path.
|
|
22
|
+
*
|
|
23
|
+
* Accepts `workspaces`, `/workspaces`, `/v1/workspaces`, or a full
|
|
24
|
+
* `https://api.skhema.com/v1/...` URL whose host matches `apiBaseUrl`. Any other
|
|
25
|
+
* host is refused (the passthrough only reaches the Skhema gateway). Preserves a
|
|
26
|
+
* query string already present in the path.
|
|
27
|
+
*/
|
|
28
|
+
export function normalizePath(raw, apiBaseUrl) {
|
|
29
|
+
let pathPart = raw.trim();
|
|
30
|
+
if (/^https?:\/\//i.test(pathPart)) {
|
|
31
|
+
let url;
|
|
32
|
+
try {
|
|
33
|
+
url = new URL(pathPart);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
throw new UsageError(`Invalid URL: ${raw}`);
|
|
37
|
+
}
|
|
38
|
+
const expectedHost = new URL(apiBaseUrl).host;
|
|
39
|
+
if (url.host !== expectedHost) {
|
|
40
|
+
throw new UsageError(`Refusing to call host "${url.host}". \`skhema api\` only reaches the Skhema gateway (${expectedHost}).`);
|
|
41
|
+
}
|
|
42
|
+
pathPart = `${url.pathname}${url.search}`;
|
|
43
|
+
}
|
|
44
|
+
if (!pathPart.startsWith('/'))
|
|
45
|
+
pathPart = `/${pathPart}`;
|
|
46
|
+
// Leading /v1 is optional in the caller's path; add it when missing.
|
|
47
|
+
if (!/^\/v1(\/|$|\?)/.test(pathPart)) {
|
|
48
|
+
pathPart = `/v1${pathPart}`;
|
|
49
|
+
}
|
|
50
|
+
return pathPart;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the `--data` argument into a JSON body. Forms:
|
|
54
|
+
* - `@path` — read JSON from a file (via `readFile`).
|
|
55
|
+
* - `-` — read JSON from stdin (via `readStdin`).
|
|
56
|
+
* - anything else — parse the literal as JSON.
|
|
57
|
+
*
|
|
58
|
+
* Returns `undefined` when `raw` is undefined. Throws {@link UsageError} on
|
|
59
|
+
* invalid JSON or a non-object top-level value.
|
|
60
|
+
*/
|
|
61
|
+
export async function resolveDataBody(raw, io) {
|
|
62
|
+
if (raw === undefined)
|
|
63
|
+
return undefined;
|
|
64
|
+
let text;
|
|
65
|
+
if (raw === '-') {
|
|
66
|
+
text = await io.readStdin();
|
|
67
|
+
}
|
|
68
|
+
else if (raw.startsWith('@')) {
|
|
69
|
+
const path = raw.slice(1);
|
|
70
|
+
try {
|
|
71
|
+
text = await io.readFile(path);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
throw new UsageError(`Could not read --data file "${path}": ${err instanceof Error ? err.message : String(err)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
text = raw;
|
|
79
|
+
}
|
|
80
|
+
let parsed;
|
|
81
|
+
try {
|
|
82
|
+
parsed = JSON.parse(text);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
throw new UsageError('--data is not valid JSON.');
|
|
86
|
+
}
|
|
87
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
88
|
+
throw new UsageError('--data must be a JSON object.');
|
|
89
|
+
}
|
|
90
|
+
return parsed;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Parse repeated `--query k=v` tokens into an entry list (preserving repeats and
|
|
94
|
+
* order). A token missing `=` is a usage error.
|
|
95
|
+
*/
|
|
96
|
+
export function parseQueryPairs(raw) {
|
|
97
|
+
if (!raw || raw.length === 0)
|
|
98
|
+
return [];
|
|
99
|
+
return raw.map((pair) => {
|
|
100
|
+
const eq = pair.indexOf('=');
|
|
101
|
+
if (eq <= 0) {
|
|
102
|
+
throw new UsageError(`Invalid --query "${pair}". Expected key=value.`);
|
|
103
|
+
}
|
|
104
|
+
return [pair.slice(0, eq), pair.slice(eq + 1)];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RequestBody } from '@skhema/sdk';
|
|
2
|
+
/** Read a file path, or all of stdin when `source` is `-`. */
|
|
3
|
+
export declare function readFileOrStdin(source: string): string;
|
|
4
|
+
/** Parse a JSON object from a `--file` argument (path or `-` for stdin). */
|
|
5
|
+
export declare function parseJsonFile(source: string): RequestBody;
|
|
6
|
+
/**
|
|
7
|
+
* Build a request body from an optional `--file` base payload overlaid with
|
|
8
|
+
* explicit field flags (flags win). Used by the create/update verbs so an agent
|
|
9
|
+
* can hand over a full JSON payload while a human can pass a couple of flags.
|
|
10
|
+
* Throws when the result is empty (nothing to send).
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveBody(options: {
|
|
13
|
+
file?: string;
|
|
14
|
+
fields?: RequestBody;
|
|
15
|
+
}): RequestBody;
|
|
16
|
+
//# sourceMappingURL=payload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../../src/lib/api/payload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAI9C,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAatD;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAczD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IACnC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,WAAW,CAad"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { UsageError } from './run-command.js';
|
|
3
|
+
/** Read a file path, or all of stdin when `source` is `-`. */
|
|
4
|
+
export function readFileOrStdin(source) {
|
|
5
|
+
if (source === '-') {
|
|
6
|
+
try {
|
|
7
|
+
return fs.readFileSync(0, 'utf-8');
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
throw new UsageError('Failed to read payload from stdin.');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
return fs.readFileSync(source, 'utf-8');
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new UsageError(`Cannot read payload file: ${source}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Parse a JSON object from a `--file` argument (path or `-` for stdin). */
|
|
21
|
+
export function parseJsonFile(source) {
|
|
22
|
+
const raw = readFileOrStdin(source);
|
|
23
|
+
let parsed;
|
|
24
|
+
try {
|
|
25
|
+
parsed = JSON.parse(raw);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new UsageError(`Payload in ${source === '-' ? 'stdin' : source} is not valid JSON.`);
|
|
29
|
+
}
|
|
30
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
31
|
+
throw new UsageError('Payload must be a JSON object.');
|
|
32
|
+
}
|
|
33
|
+
return parsed;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build a request body from an optional `--file` base payload overlaid with
|
|
37
|
+
* explicit field flags (flags win). Used by the create/update verbs so an agent
|
|
38
|
+
* can hand over a full JSON payload while a human can pass a couple of flags.
|
|
39
|
+
* Throws when the result is empty (nothing to send).
|
|
40
|
+
*/
|
|
41
|
+
export function resolveBody(options) {
|
|
42
|
+
const base = options.file ? parseJsonFile(options.file) : {};
|
|
43
|
+
const fields = options.fields ?? {};
|
|
44
|
+
const merged = { ...base };
|
|
45
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
46
|
+
if (value !== undefined)
|
|
47
|
+
merged[key] = value;
|
|
48
|
+
}
|
|
49
|
+
if (Object.keys(merged).length === 0) {
|
|
50
|
+
throw new UsageError('Nothing to send. Provide field flags or --file <path|-> with a JSON payload.');
|
|
51
|
+
}
|
|
52
|
+
return merged;
|
|
53
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic process exit codes. Agents branch on these, so they are a stable
|
|
3
|
+
* contract (spec §3.3).
|
|
4
|
+
*/
|
|
5
|
+
export declare const ExitCode: {
|
|
6
|
+
/** Success. */
|
|
7
|
+
readonly Ok: 0;
|
|
8
|
+
/** Generic or API error (non-specific 4xx/5xx, unexpected failure). */
|
|
9
|
+
readonly Error: 1;
|
|
10
|
+
/** Usage error — bad/missing arguments, resolved before any API call. */
|
|
11
|
+
readonly Usage: 2;
|
|
12
|
+
/** No usable credential — the caller must authenticate. */
|
|
13
|
+
readonly AuthRequired: 3;
|
|
14
|
+
/** Permission or plan denied (HTTP 403 or 402). */
|
|
15
|
+
readonly Denied: 4;
|
|
16
|
+
/** Rate limited (HTTP 429). */
|
|
17
|
+
readonly RateLimited: 5;
|
|
18
|
+
};
|
|
19
|
+
export type ExitCodeValue = (typeof ExitCode)[keyof typeof ExitCode];
|
|
20
|
+
/** A caller-input error (missing flag, bad value). Maps to {@link ExitCode.Usage}. */
|
|
21
|
+
export declare class UsageError extends Error {
|
|
22
|
+
readonly code = "usage_error";
|
|
23
|
+
constructor(message: string);
|
|
24
|
+
}
|
|
25
|
+
/** No credential available. Maps to {@link ExitCode.AuthRequired}. */
|
|
26
|
+
export declare class AuthRequiredError extends Error {
|
|
27
|
+
readonly code = "auth_required";
|
|
28
|
+
constructor(message: string);
|
|
29
|
+
}
|
|
30
|
+
/** Stable machine envelope emitted under `--json` (spec §3.3). */
|
|
31
|
+
export interface JsonEnvelope<T = unknown> {
|
|
32
|
+
ok: boolean;
|
|
33
|
+
command: string;
|
|
34
|
+
data?: T;
|
|
35
|
+
error?: {
|
|
36
|
+
code: string;
|
|
37
|
+
message: string;
|
|
38
|
+
requestId?: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Emit `{ ok: true, command, data }` when `--json` is active. */
|
|
42
|
+
export declare function emitSuccess<T>(command: string, data: T): void;
|
|
43
|
+
/** Emit `{ ok: false, command, error }` when `--json` is active. */
|
|
44
|
+
export declare function emitErrorEnvelope(command: string, error: {
|
|
45
|
+
code: string;
|
|
46
|
+
message: string;
|
|
47
|
+
requestId?: string;
|
|
48
|
+
}): void;
|
|
49
|
+
export interface MappedError {
|
|
50
|
+
exitCode: ExitCodeValue;
|
|
51
|
+
error: {
|
|
52
|
+
code: string;
|
|
53
|
+
message: string;
|
|
54
|
+
requestId?: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Map any thrown value to an exit code + a stable error envelope. Pure and
|
|
59
|
+
* exported so the mapping is unit-testable without spawning a process.
|
|
60
|
+
*/
|
|
61
|
+
export declare function mapError(err: unknown): MappedError;
|
|
62
|
+
/**
|
|
63
|
+
* Run a command body with unified error handling. On success, emits the JSON
|
|
64
|
+
* success envelope (when `--json`) with whatever `fn` returns; human output is
|
|
65
|
+
* `fn`'s own responsibility. On failure, maps the error to an exit code, emits
|
|
66
|
+
* the JSON error envelope (or a red line), and exits.
|
|
67
|
+
*/
|
|
68
|
+
export declare function runCommand<T>(command: string, fn: () => Promise<T>): Promise<void>;
|
|
69
|
+
//# sourceMappingURL=run-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-command.d.ts","sourceRoot":"","sources":["../../../src/lib/api/run-command.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,eAAO,MAAM,QAAQ;IACnB,eAAe;;IAEf,uEAAuE;;IAEvE,yEAAyE;;IAEzE,2DAA2D;;IAE3D,mDAAmD;;IAEnD,+BAA+B;;CAEvB,CAAA;AAEV,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAA;AAEpE,sFAAsF;AACtF,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,iBAAgB;gBACjB,OAAO,EAAE,MAAM;CAI5B;AAED,sEAAsE;AACtE,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,mBAAkB;gBACnB,OAAO,EAAE,MAAM;CAI5B;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACvC,EAAE,EAAE,OAAO,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9D;AAMD,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAK7D;AAED,oEAAoE;AACpE,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3D,IAAI,CAKN;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,aAAa,CAAA;IACvB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC7D;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CA8ClD;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,IAAI,CAAC,CAaf"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { AuthError, RateLimitedError, SkhemaApiError, WriteLockedError, } from '@skhema/sdk';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { getGlobalOptions } from '../output.js';
|
|
4
|
+
/**
|
|
5
|
+
* Deterministic process exit codes. Agents branch on these, so they are a stable
|
|
6
|
+
* contract (spec §3.3).
|
|
7
|
+
*/
|
|
8
|
+
export const ExitCode = {
|
|
9
|
+
/** Success. */
|
|
10
|
+
Ok: 0,
|
|
11
|
+
/** Generic or API error (non-specific 4xx/5xx, unexpected failure). */
|
|
12
|
+
Error: 1,
|
|
13
|
+
/** Usage error — bad/missing arguments, resolved before any API call. */
|
|
14
|
+
Usage: 2,
|
|
15
|
+
/** No usable credential — the caller must authenticate. */
|
|
16
|
+
AuthRequired: 3,
|
|
17
|
+
/** Permission or plan denied (HTTP 403 or 402). */
|
|
18
|
+
Denied: 4,
|
|
19
|
+
/** Rate limited (HTTP 429). */
|
|
20
|
+
RateLimited: 5,
|
|
21
|
+
};
|
|
22
|
+
/** A caller-input error (missing flag, bad value). Maps to {@link ExitCode.Usage}. */
|
|
23
|
+
export class UsageError extends Error {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.code = 'usage_error';
|
|
27
|
+
this.name = 'UsageError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** No credential available. Maps to {@link ExitCode.AuthRequired}. */
|
|
31
|
+
export class AuthRequiredError extends Error {
|
|
32
|
+
constructor(message) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.code = 'auth_required';
|
|
35
|
+
this.name = 'AuthRequiredError';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function jsonEnabled() {
|
|
39
|
+
return getGlobalOptions().json === true;
|
|
40
|
+
}
|
|
41
|
+
/** Emit `{ ok: true, command, data }` when `--json` is active. */
|
|
42
|
+
export function emitSuccess(command, data) {
|
|
43
|
+
if (jsonEnabled()) {
|
|
44
|
+
const envelope = { ok: true, command, data };
|
|
45
|
+
console.log(JSON.stringify(envelope, null, 2));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Emit `{ ok: false, command, error }` when `--json` is active. */
|
|
49
|
+
export function emitErrorEnvelope(command, error) {
|
|
50
|
+
if (jsonEnabled()) {
|
|
51
|
+
const envelope = { ok: false, command, error };
|
|
52
|
+
console.log(JSON.stringify(envelope, null, 2));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Map any thrown value to an exit code + a stable error envelope. Pure and
|
|
57
|
+
* exported so the mapping is unit-testable without spawning a process.
|
|
58
|
+
*/
|
|
59
|
+
export function mapError(err) {
|
|
60
|
+
// Order matters: subclasses (AuthError/WriteLockedError/RateLimitedError)
|
|
61
|
+
// are all `instanceof SkhemaApiError`, so check them first.
|
|
62
|
+
if (err instanceof AuthError) {
|
|
63
|
+
return {
|
|
64
|
+
exitCode: ExitCode.AuthRequired,
|
|
65
|
+
error: { code: err.code, message: err.message, requestId: err.requestId },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (err instanceof WriteLockedError) {
|
|
69
|
+
return {
|
|
70
|
+
exitCode: ExitCode.Denied,
|
|
71
|
+
error: { code: err.code, message: err.message, requestId: err.requestId },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (err instanceof RateLimitedError) {
|
|
75
|
+
return {
|
|
76
|
+
exitCode: ExitCode.RateLimited,
|
|
77
|
+
error: { code: err.code, message: err.message, requestId: err.requestId },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (err instanceof SkhemaApiError) {
|
|
81
|
+
return {
|
|
82
|
+
exitCode: err.status === 403 ? ExitCode.Denied : ExitCode.Error,
|
|
83
|
+
error: { code: err.code, message: err.message, requestId: err.requestId },
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (err instanceof AuthRequiredError) {
|
|
87
|
+
return {
|
|
88
|
+
exitCode: ExitCode.AuthRequired,
|
|
89
|
+
error: { code: err.code, message: err.message },
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (err instanceof UsageError) {
|
|
93
|
+
return {
|
|
94
|
+
exitCode: ExitCode.Usage,
|
|
95
|
+
error: { code: err.code, message: err.message },
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
exitCode: ExitCode.Error,
|
|
100
|
+
error: {
|
|
101
|
+
code: 'error',
|
|
102
|
+
message: err instanceof Error ? err.message : String(err),
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Run a command body with unified error handling. On success, emits the JSON
|
|
108
|
+
* success envelope (when `--json`) with whatever `fn` returns; human output is
|
|
109
|
+
* `fn`'s own responsibility. On failure, maps the error to an exit code, emits
|
|
110
|
+
* the JSON error envelope (or a red line), and exits.
|
|
111
|
+
*/
|
|
112
|
+
export async function runCommand(command, fn) {
|
|
113
|
+
try {
|
|
114
|
+
const data = await fn();
|
|
115
|
+
emitSuccess(command, data);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const mapped = mapError(err);
|
|
119
|
+
if (jsonEnabled()) {
|
|
120
|
+
emitErrorEnvelope(command, mapped.error);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.error(chalk.red(`Error: ${mapped.error.message}`));
|
|
124
|
+
}
|
|
125
|
+
process.exit(mapped.exitCode);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the target workspace for a workspace-scoped command: the explicit
|
|
3
|
+
* `--workspace <id>` flag wins, otherwise the stored default (`skhema workspace
|
|
4
|
+
* use <id>`). Throws {@link UsageError} (exit 2) when neither is present so the
|
|
5
|
+
* caller never silently hits the wrong workspace.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveWorkspaceId(flag: string | undefined): string;
|
|
8
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../../src/lib/api/scope.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CASnE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getDefaultWorkspaceId } from '../config-store.js';
|
|
2
|
+
import { UsageError } from './run-command.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the target workspace for a workspace-scoped command: the explicit
|
|
5
|
+
* `--workspace <id>` flag wins, otherwise the stored default (`skhema workspace
|
|
6
|
+
* use <id>`). Throws {@link UsageError} (exit 2) when neither is present so the
|
|
7
|
+
* caller never silently hits the wrong workspace.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveWorkspaceId(flag) {
|
|
10
|
+
const id = flag ?? getDefaultWorkspaceId();
|
|
11
|
+
if (!id) {
|
|
12
|
+
throw new UsageError('No workspace specified. Pass --workspace <id> or set a default with ' +
|
|
13
|
+
'`skhema workspace use <id>`.');
|
|
14
|
+
}
|
|
15
|
+
return id;
|
|
16
|
+
}
|