@jini-ai/cli 0.1.0

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.
Files changed (62) hide show
  1. package/LICENSE +201 -0
  2. package/dist/command-registry.d.ts +82 -0
  3. package/dist/command-registry.d.ts.map +1 -0
  4. package/dist/command-registry.js +88 -0
  5. package/dist/command-registry.js.map +1 -0
  6. package/dist/daemon-command.d.ts +42 -0
  7. package/dist/daemon-command.d.ts.map +1 -0
  8. package/dist/daemon-command.js +103 -0
  9. package/dist/daemon-command.js.map +1 -0
  10. package/dist/daemon-url.d.ts +68 -0
  11. package/dist/daemon-url.d.ts.map +1 -0
  12. package/dist/daemon-url.js +99 -0
  13. package/dist/daemon-url.js.map +1 -0
  14. package/dist/errors.d.ts +82 -0
  15. package/dist/errors.d.ts.map +1 -0
  16. package/dist/errors.js +103 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/flags.d.ts +61 -0
  19. package/dist/flags.d.ts.map +1 -0
  20. package/dist/flags.js +111 -0
  21. package/dist/flags.js.map +1 -0
  22. package/dist/http.d.ts +63 -0
  23. package/dist/http.d.ts.map +1 -0
  24. package/dist/http.js +226 -0
  25. package/dist/http.js.map +1 -0
  26. package/dist/index.d.ts +24 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +30 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/local-daemon-discovery.d.ts +31 -0
  31. package/dist/local-daemon-discovery.d.ts.map +1 -0
  32. package/dist/local-daemon-discovery.js +53 -0
  33. package/dist/local-daemon-discovery.js.map +1 -0
  34. package/dist/main.d.ts +21 -0
  35. package/dist/main.d.ts.map +1 -0
  36. package/dist/main.js +231 -0
  37. package/dist/main.js.map +1 -0
  38. package/dist/prompt.d.ts +83 -0
  39. package/dist/prompt.d.ts.map +1 -0
  40. package/dist/prompt.js +198 -0
  41. package/dist/prompt.js.map +1 -0
  42. package/dist/redact.d.ts +48 -0
  43. package/dist/redact.d.ts.map +1 -0
  44. package/dist/redact.js +134 -0
  45. package/dist/redact.js.map +1 -0
  46. package/dist/run-command.d.ts +39 -0
  47. package/dist/run-command.d.ts.map +1 -0
  48. package/dist/run-command.js +291 -0
  49. package/dist/run-command.js.map +1 -0
  50. package/dist/tokens.d.ts +3 -0
  51. package/dist/tokens.d.ts.map +1 -0
  52. package/dist/tokens.js +12 -0
  53. package/dist/tokens.js.map +1 -0
  54. package/dist/usage.d.ts +24 -0
  55. package/dist/usage.d.ts.map +1 -0
  56. package/dist/usage.js +25 -0
  57. package/dist/usage.js.map +1 -0
  58. package/dist/version-command.d.ts +38 -0
  59. package/dist/version-command.d.ts.map +1 -0
  60. package/dist/version-command.js +78 -0
  61. package/dist/version-command.js.map +1 -0
  62. package/package.json +48 -0
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @module daemon-url
3
+ *
4
+ * Generic daemon-URL resolution, generalized from OD's
5
+ * `apps/daemon/src/daemon-url.ts` (see `source-map.md`). The *order* —
6
+ * explicit flag, then an env var, then an injected discovery probe, then a
7
+ * caller-supplied default — is the reusable part. OD's own implementation
8
+ * hardcodes its sidecar IPC protocol and a `pnpm exec tools-dev status`
9
+ * fallback; neither belongs in a product-neutral package, so both are
10
+ * replaced with an injected `discover` callback the caller (e.g. a future
11
+ * `@jini-ai/sidecar`-backed adapter) supplies.
12
+ */
13
+ /**
14
+ * Resolve a daemon HTTP base URL for a CLI client command.
15
+ *
16
+ * Resolution order: `flagUrl`, then `env[envVarName]` (when `envVarName` is
17
+ * given), then `discover()` (when given), then `defaultUrl`. Throws when
18
+ * every step is exhausted and no `defaultUrl` was supplied — a silent
19
+ * `http://127.0.0.1:0`-style fallback would be worse than a clear error,
20
+ * and this package has no locked default port to fall back to on its own.
21
+ */
22
+ export async function resolveDaemonUrl(options = {}) {
23
+ const env = options.env ?? process.env;
24
+ const warn = options.warn ?? (() => { });
25
+ const report = (url) => {
26
+ const warning = daemonUrlPolicyWarning(url);
27
+ if (warning !== null)
28
+ warn(warning);
29
+ return url;
30
+ };
31
+ const flagUrl = options.flagUrl ?? null;
32
+ if (flagUrl !== null && flagUrl.length > 0)
33
+ return report(flagUrl);
34
+ if (options.envVarName !== undefined) {
35
+ const envUrl = env[options.envVarName];
36
+ if (envUrl !== undefined && envUrl.length > 0)
37
+ return report(envUrl);
38
+ }
39
+ if (options.discover !== undefined) {
40
+ const discovered = await options.discover(env, options.timeoutMs ?? 800);
41
+ if (discovered !== null && discovered.length > 0)
42
+ return report(discovered);
43
+ }
44
+ if (options.defaultUrl !== undefined)
45
+ return report(options.defaultUrl);
46
+ throw new Error('no daemon URL resolved: pass flagUrl, set envVarName on a populated env var, ' +
47
+ 'supply a discover() probe, or provide defaultUrl.');
48
+ }
49
+ // Matches a scheme + userinfo prefix (`scheme://user:pass@`) on a URL string that failed to
50
+ // parse via the `URL` constructor, so userinfo can still be stripped from a malformed value
51
+ // before it's ever printed.
52
+ const USERINFO_PREFIX_RE = /^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)[^/@]*@/;
53
+ /**
54
+ * Return `url` with any embedded userinfo (`user:pass@host`) stripped, safe to include in a
55
+ * printed error message, log line, or structured-error envelope. Per SEC-RB-009
56
+ * (`ADS-memory/reports/security/SEC-remaining-backend-audit-2026-07-21.md`), a daemon URL a
57
+ * caller configures themselves can legitimately carry basic-auth credentials; those must never
58
+ * reach stderr/stdout verbatim. Returns `url` unchanged when it has no userinfo, so normal URLs
59
+ * are never reformatted/normalized as a side effect of display-sanitizing them.
60
+ */
61
+ export function sanitizeDaemonUrlForDisplay(url) {
62
+ let parsed;
63
+ try {
64
+ parsed = new URL(url);
65
+ }
66
+ catch {
67
+ return url.replace(USERINFO_PREFIX_RE, '$1');
68
+ }
69
+ if (parsed.username.length === 0 && parsed.password.length === 0)
70
+ return url;
71
+ parsed.username = '';
72
+ parsed.password = '';
73
+ return parsed.toString();
74
+ }
75
+ const LOOPBACK_HOSTNAMES = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);
76
+ function isLoopbackHostname(hostname) {
77
+ return LOOPBACK_HOSTNAMES.has(hostname.toLowerCase());
78
+ }
79
+ /**
80
+ * Non-fatal policy check for a resolved daemon URL: warn (never throw or reject) when it is
81
+ * neither loopback nor HTTPS, since that combination sends CLI traffic — including whatever
82
+ * headers/body a command sends — over an unencrypted connection to a non-local host. Returns
83
+ * `null` when the URL looks safe or doesn't parse (a parse failure is reported elsewhere, by
84
+ * whatever code actually tries to use the URL).
85
+ */
86
+ export function daemonUrlPolicyWarning(url) {
87
+ let parsed;
88
+ try {
89
+ parsed = new URL(url);
90
+ }
91
+ catch {
92
+ return null;
93
+ }
94
+ if (parsed.protocol === 'https:' || isLoopbackHostname(parsed.hostname))
95
+ return null;
96
+ return (`daemon URL ${sanitizeDaemonUrlForDisplay(url)} is neither loopback nor HTTPS; ` +
97
+ 'traffic to a remote, non-HTTPS daemon is not encrypted.');
98
+ }
99
+ //# sourceMappingURL=daemon-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-url.js","sourceRoot":"","sources":["../src/daemon-url.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @module errors
3
+ *
4
+ * Structured error / exit-code handling, generalized from OD's
5
+ * `apps/daemon/src/cli.ts` `exitWithStructuredError`/`structuredHttpFailure`
6
+ * (see `source-map.md`). The mechanism — map a stable error `code` to a
7
+ * process exit code, write a `{ error: { code, message, data } }` envelope
8
+ * to stderr — is generic CLI transport plumbing; OD's own exit-code table
9
+ * (`plugin-not-found`, `snapshot-stale`, …) is product-specific and was not
10
+ * ported. Callers extend {@link DEFAULT_CLI_EXIT_CODES} with their own pack's
11
+ * codes rather than this package hosting one global mutable map.
12
+ */
13
+ export type ExitCodeTable = Readonly<Record<string, number>>;
14
+ /** A minimal, product-neutral starting table. Packs extend this with their own codes. */
15
+ export declare const DEFAULT_CLI_EXIT_CODES: ExitCodeTable;
16
+ export interface StructuredErrorEnvelope {
17
+ error: {
18
+ code: string;
19
+ message: string;
20
+ data: Record<string, unknown>;
21
+ };
22
+ }
23
+ export interface ExitWithStructuredErrorInput {
24
+ code: string;
25
+ message: string;
26
+ data?: Record<string, unknown>;
27
+ }
28
+ export interface StructuredErrorOptions {
29
+ /** Extra/overriding `code -> exitCode` entries layered on {@link DEFAULT_CLI_EXIT_CODES}. */
30
+ exitCodes?: ExitCodeTable;
31
+ /** Defaults to `process.stderr.write`; inject for tests. */
32
+ write?: (text: string) => void;
33
+ /** Defaults to `process.exit`; inject for tests (must not return). */
34
+ exit?: (code: number) => never;
35
+ }
36
+ /**
37
+ * Write a `{ error }` envelope to stderr and exit with the code mapped from
38
+ * `input.code` (falling back to `1` for an unrecognized code).
39
+ */
40
+ export declare function exitWithStructuredError(input: ExitWithStructuredErrorInput, options?: StructuredErrorOptions): never;
41
+ interface DaemonErrorBody {
42
+ code?: unknown;
43
+ message?: unknown;
44
+ data?: unknown;
45
+ details?: unknown;
46
+ retryable?: unknown;
47
+ }
48
+ /** The minimal shape of a `fetch()` `Response` this module needs. */
49
+ export interface HttpFailureLike {
50
+ status: number;
51
+ text(): Promise<string>;
52
+ }
53
+ /**
54
+ * Extract `{ data }` worth surfacing to the structured envelope from a
55
+ * daemon error body: any `data` fields, plus `details`/`retryable` when
56
+ * present. Returns `undefined` when there's nothing to attach.
57
+ *
58
+ * `data`/`details` are daemon-supplied and untrusted, so both are run
59
+ * through {@link sanitizeUnknownDeep} — every string leaf gets bounded,
60
+ * control-sequence-stripped, and secret-redacted before it can reach a
61
+ * printed envelope (CR-004/SEC-RB-009).
62
+ */
63
+ export declare function structuredErrorData(error: DaemonErrorBody | undefined): Record<string, unknown> | undefined;
64
+ /**
65
+ * Map a daemon HTTP error response into the structured exit-code envelope.
66
+ * Daemon error bodies come in two shapes in practice: `{ error: { code,
67
+ * message, ... } }` (structured routes) and `{ error: '<message>' }` (flat
68
+ * legacy routes) — both normalize so the message always reaches the
69
+ * envelope instead of collapsing to a bare `HTTP <status>`.
70
+ *
71
+ * Per CR-004/SEC-RB-009, when there is no structured `message` this no
72
+ * longer embeds the complete raw response body verbatim into `message`
73
+ * (a malicious/broken daemon's body could be enormous, contain terminal
74
+ * control sequences, or echo back a secret). The envelope's `message` stays
75
+ * a stable `HTTP <status>` in that case; a bounded, redacted excerpt of the
76
+ * raw body is attached separately as `data.rawExcerpt` instead. A
77
+ * structured `message` (when present) is still sanitized — it originates
78
+ * from the same untrusted response.
79
+ */
80
+ export declare function structuredHttpFailure(resp: HttpFailureLike, fallbackCode?: string, options?: StructuredErrorOptions): Promise<never>;
81
+ export {};
82
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAYH,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE7D,yFAAyF;AACzF,eAAO,MAAM,sBAAsB,EAAE,aAIpC,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B,CAAC;CACH;AAED,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACrC,6FAA6F;IAC7F,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,4DAA4D;IAC5D,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,sEAAsE;IACtE,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,4BAA4B,EACnC,OAAO,GAAE,sBAA2B,GACnC,KAAK,CAUP;AAED,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAS3G;AAUD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,eAAe,EACrB,YAAY,SAAuB,EACnC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,KAAK,CAAC,CAiBhB"}
package/dist/errors.js ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @module errors
3
+ *
4
+ * Structured error / exit-code handling, generalized from OD's
5
+ * `apps/daemon/src/cli.ts` `exitWithStructuredError`/`structuredHttpFailure`
6
+ * (see `source-map.md`). The mechanism — map a stable error `code` to a
7
+ * process exit code, write a `{ error: { code, message, data } }` envelope
8
+ * to stderr — is generic CLI transport plumbing; OD's own exit-code table
9
+ * (`plugin-not-found`, `snapshot-stale`, …) is product-specific and was not
10
+ * ported. Callers extend {@link DEFAULT_CLI_EXIT_CODES} with their own pack's
11
+ * codes rather than this package hosting one global mutable map.
12
+ */
13
+ import { sanitizeUnknownDeep, sanitizeUntrustedText, stripControlSequences } from './redact.js';
14
+ /** A minimal, product-neutral starting table. Packs extend this with their own codes. */
15
+ export const DEFAULT_CLI_EXIT_CODES = {
16
+ 'daemon-not-running': 64,
17
+ 'missing-input': 67,
18
+ 'invalid-flag': 2,
19
+ };
20
+ /**
21
+ * Write a `{ error }` envelope to stderr and exit with the code mapped from
22
+ * `input.code` (falling back to `1` for an unrecognized code).
23
+ */
24
+ export function exitWithStructuredError(input, options = {}) {
25
+ const exitCodes = { ...DEFAULT_CLI_EXIT_CODES, ...options.exitCodes };
26
+ const exitCode = exitCodes[input.code] ?? 1;
27
+ const envelope = {
28
+ error: { code: input.code, message: input.message, data: input.data ?? {} },
29
+ };
30
+ const write = options.write ?? ((text) => { process.stderr.write(text); });
31
+ const exit = options.exit ?? ((code) => process.exit(code));
32
+ write(`${JSON.stringify(envelope)}\n`);
33
+ return exit(exitCode);
34
+ }
35
+ /**
36
+ * Extract `{ data }` worth surfacing to the structured envelope from a
37
+ * daemon error body: any `data` fields, plus `details`/`retryable` when
38
+ * present. Returns `undefined` when there's nothing to attach.
39
+ *
40
+ * `data`/`details` are daemon-supplied and untrusted, so both are run
41
+ * through {@link sanitizeUnknownDeep} — every string leaf gets bounded,
42
+ * control-sequence-stripped, and secret-redacted before it can reach a
43
+ * printed envelope (CR-004/SEC-RB-009).
44
+ */
45
+ export function structuredErrorData(error) {
46
+ if (error === undefined)
47
+ return undefined;
48
+ const data = {};
49
+ if (error.data !== undefined && typeof error.data === 'object' && error.data !== null) {
50
+ Object.assign(data, sanitizeUnknownDeep(error.data));
51
+ }
52
+ if (error.details !== undefined)
53
+ data.details = sanitizeUnknownDeep(error.details);
54
+ if (typeof error.retryable === 'boolean')
55
+ data.retryable = error.retryable;
56
+ return Object.keys(data).length > 0 ? data : undefined;
57
+ }
58
+ function extractErrorObject(parsed) {
59
+ if (typeof parsed !== 'object' || parsed === null || !('error' in parsed))
60
+ return undefined;
61
+ const error = parsed.error;
62
+ if (typeof error === 'string')
63
+ return { message: error };
64
+ if (typeof error === 'object' && error !== null)
65
+ return error;
66
+ return undefined;
67
+ }
68
+ /**
69
+ * Map a daemon HTTP error response into the structured exit-code envelope.
70
+ * Daemon error bodies come in two shapes in practice: `{ error: { code,
71
+ * message, ... } }` (structured routes) and `{ error: '<message>' }` (flat
72
+ * legacy routes) — both normalize so the message always reaches the
73
+ * envelope instead of collapsing to a bare `HTTP <status>`.
74
+ *
75
+ * Per CR-004/SEC-RB-009, when there is no structured `message` this no
76
+ * longer embeds the complete raw response body verbatim into `message`
77
+ * (a malicious/broken daemon's body could be enormous, contain terminal
78
+ * control sequences, or echo back a secret). The envelope's `message` stays
79
+ * a stable `HTTP <status>` in that case; a bounded, redacted excerpt of the
80
+ * raw body is attached separately as `data.rawExcerpt` instead. A
81
+ * structured `message` (when present) is still sanitized — it originates
82
+ * from the same untrusted response.
83
+ */
84
+ export async function structuredHttpFailure(resp, fallbackCode = 'daemon-not-running', options = {}) {
85
+ let raw = '';
86
+ let parsed = {};
87
+ try {
88
+ raw = await resp.text();
89
+ parsed = raw.length > 0 ? JSON.parse(raw) : {};
90
+ }
91
+ catch {
92
+ parsed = {};
93
+ }
94
+ const errorObj = extractErrorObject(parsed);
95
+ const data = structuredErrorData(errorObj);
96
+ const code = typeof errorObj?.code === 'string' ? stripControlSequences(errorObj.code) : fallbackCode;
97
+ const structuredMessage = typeof errorObj?.message === 'string' ? sanitizeUntrustedText(errorObj.message) : undefined;
98
+ const message = structuredMessage ?? `HTTP ${resp.status}`;
99
+ const rawExcerpt = structuredMessage === undefined && raw.length > 0 ? sanitizeUntrustedText(raw) : undefined;
100
+ const mergedData = rawExcerpt !== undefined ? { ...(data ?? {}), rawExcerpt } : data;
101
+ return exitWithStructuredError({ code, message, ...(mergedData !== undefined ? { data: mergedData } : {}) }, options);
102
+ }
103
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @module flags
3
+ *
4
+ * Generic `argv` → flags parsing, ported from OD's `apps/daemon/src/cli.ts`
5
+ * `parseFlags`/`positionalArgs`/`collectCliPositionals` (see `source-map.md`).
6
+ * Zero product nouns: callers declare which `--flag` names take a string
7
+ * value vs. are booleans, and get back a flat map plus the leftover
8
+ * positional tokens.
9
+ */
10
+ /** A flag's value: `true` for a bare boolean flag, otherwise its string value. */
11
+ export type FlagValue = string | boolean;
12
+ /** The parsed `--flag`/`--flag=value` map produced by {@link parseFlags}. */
13
+ export type ParsedFlags = Record<string, FlagValue>;
14
+ export interface ParseFlagsOptions {
15
+ /** Flag names (without leading `--`) that always consume the next token as a string value. */
16
+ string?: ReadonlySet<string>;
17
+ /** Flag names (without leading `--`) that never consume a value — presence alone means `true`. */
18
+ boolean?: ReadonlySet<string>;
19
+ }
20
+ /**
21
+ * Parse `--flag value`, `--flag=value`, and bare boolean `--flag` tokens out
22
+ * of `argv`. Non-`--`-prefixed tokens (positionals) are silently skipped —
23
+ * use {@link positionalArgs} to collect those.
24
+ *
25
+ * When either `string` or `boolean` is non-empty, an unrecognized `--flag`
26
+ * throws (fail-fast: a hallucinated flag from an LLM-driven caller should
27
+ * error immediately rather than silently no-op). When both are empty, every
28
+ * `--flag` is accepted and a heuristic decides string-vs-boolean: if the
29
+ * next token doesn't itself look like a flag, it's consumed as the value.
30
+ */
31
+ export declare function parseFlags(argv: readonly string[], opts?: ParseFlagsOptions): ParsedFlags;
32
+ export interface PositionalArgsOptions {
33
+ /** String-valued flag names whose value token must be skipped, not collected as a positional. */
34
+ string?: ReadonlySet<string>;
35
+ /**
36
+ * When `true`, a bare `--` token stops flag-skipping and every remaining
37
+ * token (including ones that look like `--flags`) is collected verbatim.
38
+ * Mirrors the shell `--` convention (`collectCliPositionals` in the OD
39
+ * source). Defaults to `false`.
40
+ */
41
+ stopAtDoubleDash?: boolean;
42
+ }
43
+ /**
44
+ * Collect every non-flag token from `argv`, skipping the value token that
45
+ * follows a known string flag (so `od run start --project p1 foo` yields
46
+ * `['foo']`, not `['p1', 'foo']`).
47
+ */
48
+ export declare function positionalArgs(argv: readonly string[], opts?: PositionalArgsOptions): string[];
49
+ /** The native type {@link coerceCliValue} can produce from a raw string flag value. */
50
+ export type CoercedCliValue = boolean | number | string;
51
+ /**
52
+ * Coerce a raw `--flag <value>` string into its likely native type:
53
+ * `'true'`/`'false'` become booleans, a numeric-looking string becomes a
54
+ * `number`, and anything else passes through unchanged. Ported from OD's
55
+ * `cli.ts` `coerceCliValue` (see `source-map.md`) — used wherever a CLI
56
+ * accepts an untyped string flag value that application code should receive
57
+ * as its natural type (e.g. a config `set` command, or a plugin's dynamic
58
+ * `--input` flags forwarded as a JSON-typed payload).
59
+ */
60
+ export declare function coerceCliValue(raw: string): CoercedCliValue;
61
+ //# sourceMappingURL=flags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC,6EAA6E;AAC7E,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IAChC,8FAA8F;IAC9F,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,kGAAkG;IAClG,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,GAAE,iBAAsB,GAAG,WAAW,CA4C7F;AAED,MAAM,WAAW,qBAAqB;IACpC,iGAAiG;IACjG,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,GAAE,qBAA0B,GAAG,MAAM,EAAE,CAqBlG;AAED,uFAAuF;AACvF,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAIxD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAK3D"}
package/dist/flags.js ADDED
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @module flags
3
+ *
4
+ * Generic `argv` → flags parsing, ported from OD's `apps/daemon/src/cli.ts`
5
+ * `parseFlags`/`positionalArgs`/`collectCliPositionals` (see `source-map.md`).
6
+ * Zero product nouns: callers declare which `--flag` names take a string
7
+ * value vs. are booleans, and get back a flat map plus the leftover
8
+ * positional tokens.
9
+ */
10
+ /**
11
+ * Parse `--flag value`, `--flag=value`, and bare boolean `--flag` tokens out
12
+ * of `argv`. Non-`--`-prefixed tokens (positionals) are silently skipped —
13
+ * use {@link positionalArgs} to collect those.
14
+ *
15
+ * When either `string` or `boolean` is non-empty, an unrecognized `--flag`
16
+ * throws (fail-fast: a hallucinated flag from an LLM-driven caller should
17
+ * error immediately rather than silently no-op). When both are empty, every
18
+ * `--flag` is accepted and a heuristic decides string-vs-boolean: if the
19
+ * next token doesn't itself look like a flag, it's consumed as the value.
20
+ */
21
+ export function parseFlags(argv, opts = {}) {
22
+ const stringFlags = opts.string ?? new Set();
23
+ const booleanFlags = opts.boolean ?? new Set();
24
+ const knownFlags = new Set([...stringFlags, ...booleanFlags]);
25
+ const out = {};
26
+ for (let i = 0; i < argv.length; i++) {
27
+ // Loop is bounded by argv.length, so this index is always in range —
28
+ // noUncheckedIndexedAccess types it as possibly-undefined regardless.
29
+ const a = argv[i];
30
+ if (!a.startsWith('--'))
31
+ continue;
32
+ const eq = a.indexOf('=');
33
+ const key = eq >= 0 ? a.slice(2, eq) : a.slice(2);
34
+ if (knownFlags.size > 0 && !knownFlags.has(key)) {
35
+ throw new Error(`unknown flag: --${key}. Run with --help for the list of accepted flags.`);
36
+ }
37
+ if (eq >= 0) {
38
+ out[key] = a.slice(eq + 1);
39
+ continue;
40
+ }
41
+ if (booleanFlags.has(key)) {
42
+ out[key] = true;
43
+ continue;
44
+ }
45
+ if (stringFlags.has(key)) {
46
+ const next = argv[i + 1];
47
+ if (next === undefined) {
48
+ throw new Error(`flag --${key} requires a value`);
49
+ }
50
+ out[key] = next;
51
+ i++;
52
+ continue;
53
+ }
54
+ const next = argv[i + 1];
55
+ if (next !== undefined && !next.startsWith('--')) {
56
+ out[key] = next;
57
+ i++;
58
+ }
59
+ else {
60
+ out[key] = true;
61
+ }
62
+ }
63
+ return out;
64
+ }
65
+ /**
66
+ * Collect every non-flag token from `argv`, skipping the value token that
67
+ * follows a known string flag (so `od run start --project p1 foo` yields
68
+ * `['foo']`, not `['p1', 'foo']`).
69
+ */
70
+ export function positionalArgs(argv, opts = {}) {
71
+ const stringFlags = opts.string ?? new Set();
72
+ const out = [];
73
+ for (let i = 0; i < argv.length; i++) {
74
+ // Loop is bounded by argv.length, so this index is always in range —
75
+ // noUncheckedIndexedAccess types it as possibly-undefined regardless.
76
+ const a = argv[i];
77
+ if (opts.stopAtDoubleDash === true && a === '--') {
78
+ out.push(...argv.slice(i + 1));
79
+ break;
80
+ }
81
+ if (!a.startsWith('--')) {
82
+ out.push(a);
83
+ continue;
84
+ }
85
+ const eq = a.indexOf('=');
86
+ const key = eq >= 0 ? a.slice(2, eq) : a.slice(2);
87
+ if (eq < 0 && stringFlags.has(key))
88
+ i++;
89
+ }
90
+ return out;
91
+ }
92
+ const NUMERIC_LITERAL_RE = /^-?\d+(\.\d+)?$/;
93
+ /**
94
+ * Coerce a raw `--flag <value>` string into its likely native type:
95
+ * `'true'`/`'false'` become booleans, a numeric-looking string becomes a
96
+ * `number`, and anything else passes through unchanged. Ported from OD's
97
+ * `cli.ts` `coerceCliValue` (see `source-map.md`) — used wherever a CLI
98
+ * accepts an untyped string flag value that application code should receive
99
+ * as its natural type (e.g. a config `set` command, or a plugin's dynamic
100
+ * `--input` flags forwarded as a JSON-typed payload).
101
+ */
102
+ export function coerceCliValue(raw) {
103
+ if (raw === 'true')
104
+ return true;
105
+ if (raw === 'false')
106
+ return false;
107
+ if (NUMERIC_LITERAL_RE.test(raw))
108
+ return Number(raw);
109
+ return raw;
110
+ }
111
+ //# sourceMappingURL=flags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flags.js","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @module http
3
+ *
4
+ * HTTP-client-mode transport plumbing, ported from OD's
5
+ * `apps/daemon/src/cli.ts` `surfaceFetchError`/`postJsonToDaemon` (see
6
+ * `source-map.md`). This is the "HTTP-client mode default" half of
7
+ * extraction-plan §3's `@jini-ai/cli` spec: fetch a daemon route, translate a
8
+ * network failure or a daemon error envelope into the structured exit-code
9
+ * contract from `errors.ts`.
10
+ */
11
+ import { type ExitCodeTable } from './errors.js';
12
+ export interface SurfaceFetchErrorOptions {
13
+ /** Defaults to `process.stderr.write`; inject for tests. */
14
+ write?: (text: string) => void;
15
+ }
16
+ /**
17
+ * Format a `fetch()` rejection (network-unreachable, sandbox-denied) into a
18
+ * human-readable stderr line, with a hint when the underlying cause looks
19
+ * like a sandboxed-environment outbound-connect denial. Per CR-004/SEC-RB-009,
20
+ * `daemonUrl` and the failure detail are both sanitized before they reach
21
+ * stderr — a `daemonUrl` with embedded userinfo, or a cause message that
22
+ * embeds terminal control sequences or a leaked credential, must not be
23
+ * printed verbatim.
24
+ */
25
+ export declare function surfaceFetchError(err: unknown, daemonUrl: string, options?: SurfaceFetchErrorOptions): void;
26
+ export interface PostJsonToDaemonOptions {
27
+ headers?: Record<string, string>;
28
+ exitCodes?: ExitCodeTable;
29
+ /** Defaults to the global `fetch`; inject for tests. */
30
+ fetchImpl?: typeof fetch;
31
+ /** Defaults to `process.stderr.write`; inject for tests. Used only for the unrecognized-error-code fallback path. */
32
+ write?: (text: string) => void;
33
+ /** Defaults to `process.exit`; inject for tests (must not return). Used only for the unrecognized-error-code fallback path. */
34
+ exit?: (code: number) => never;
35
+ /** Caller-supplied cancellation signal; combined with the internal timeout signal, so either one aborts the request. */
36
+ signal?: AbortSignal;
37
+ /** Request deadline in ms. Defaults to {@link DEFAULT_TIMEOUT_MS}. */
38
+ timeoutMs?: number;
39
+ /** Maximum daemon response size in bytes before the read is aborted. Defaults to {@link DEFAULT_MAX_RESPONSE_BYTES}. */
40
+ maxResponseBytes?: number;
41
+ }
42
+ /** `GET`'s options are identical to {@link PostJsonToDaemonOptions} minus the (POST-only) request body. */
43
+ export type GetJsonFromDaemonOptions = PostJsonToDaemonOptions;
44
+ /**
45
+ * `POST` a JSON body to `<base><route>` and return the parsed JSON response.
46
+ * A network failure or a non-2xx daemon response both exit via the
47
+ * structured error contract (`errors.ts`) instead of returning — this
48
+ * function either resolves with the parsed body or never returns.
49
+ *
50
+ * Per CR-004/SEC-RB-009, this now enforces a request deadline (combined with
51
+ * an optional caller `signal`) and a maximum response size, and redacts
52
+ * daemon-supplied error detail before it reaches stderr instead of dumping
53
+ * it verbatim.
54
+ */
55
+ export declare function postJsonToDaemon(base: string, route: string, body: unknown, options?: PostJsonToDaemonOptions): Promise<unknown>;
56
+ /**
57
+ * `GET` `<base><route>` and return the parsed JSON response. Same transport contract as
58
+ * {@link postJsonToDaemon} (timeout, response-size cap, structured-error mapping, redaction) with
59
+ * no request body — for read-only daemon routes such as `@jini-ai/http`'s `GET /api/runs` and
60
+ * `GET /api/runs/:runId`.
61
+ */
62
+ export declare function getJsonFromDaemon(base: string, route: string, options?: GetJsonFromDaemonOptions): Promise<unknown>;
63
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAwE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAIvH,MAAM,WAAW,wBAAwB;IACvC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,wBAA6B,GAAG,IAAI,CAsB/G;AA0ED,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,qHAAqH;IACrH,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,+HAA+H;IAC/H,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;IAC/B,wHAAwH;IACxH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wHAAwH;IACxH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,2GAA2G;AAC3G,MAAM,MAAM,wBAAwB,GAAG,uBAAuB,CAAC;AA+F/D;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CAElB"}