@opensip-cli/contracts 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.
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +31 -0
- package/dist/__tests__/exit-codes.test.d.ts +2 -0
- package/dist/__tests__/exit-codes.test.d.ts.map +1 -0
- package/dist/__tests__/exit-codes.test.js +185 -0
- package/dist/__tests__/exit-codes.test.js.map +1 -0
- package/dist/__tests__/manifest-reexport.test.d.ts +6 -0
- package/dist/__tests__/manifest-reexport.test.d.ts.map +1 -0
- package/dist/__tests__/manifest-reexport.test.js +38 -0
- package/dist/__tests__/manifest-reexport.test.js.map +1 -0
- package/dist/__tests__/types-only.test.d.ts +10 -0
- package/dist/__tests__/types-only.test.d.ts.map +1 -0
- package/dist/__tests__/types-only.test.js +49 -0
- package/dist/__tests__/types-only.test.js.map +1 -0
- package/dist/cli-flags.d.ts +56 -0
- package/dist/cli-flags.d.ts.map +1 -0
- package/dist/cli-flags.js +85 -0
- package/dist/cli-flags.js.map +1 -0
- package/dist/cli-flags.test.d.ts +2 -0
- package/dist/cli-flags.test.d.ts.map +1 -0
- package/dist/cli-flags.test.js +51 -0
- package/dist/cli-flags.test.js.map +1 -0
- package/dist/command-outcome.d.ts +87 -0
- package/dist/command-outcome.d.ts.map +1 -0
- package/dist/command-outcome.js +32 -0
- package/dist/command-outcome.js.map +1 -0
- package/dist/command-outcome.test.d.ts +10 -0
- package/dist/command-outcome.test.d.ts.map +1 -0
- package/dist/command-outcome.test.js +68 -0
- package/dist/command-outcome.test.js.map +1 -0
- package/dist/command-results.d.ts +501 -0
- package/dist/command-results.d.ts.map +1 -0
- package/dist/command-results.js +14 -0
- package/dist/command-results.js.map +1 -0
- package/dist/exit-codes.d.ts +44 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +186 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/graph-catalog.d.ts +143 -0
- package/dist/graph-catalog.d.ts.map +1 -0
- package/dist/graph-catalog.js +13 -0
- package/dist/graph-catalog.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/recipe-default.d.ts +53 -0
- package/dist/recipe-default.d.ts.map +1 -0
- package/dist/recipe-default.js +55 -0
- package/dist/recipe-default.js.map +1 -0
- package/dist/recipe-default.test.d.ts +2 -0
- package/dist/recipe-default.test.d.ts.map +1 -0
- package/dist/recipe-default.test.js +32 -0
- package/dist/recipe-default.test.js.map +1 -0
- package/dist/score.d.ts +26 -0
- package/dist/score.d.ts.map +1 -0
- package/dist/score.js +25 -0
- package/dist/score.js.map +1 -0
- package/dist/score.test.d.ts +2 -0
- package/dist/score.test.d.ts.map +1 -0
- package/dist/score.test.js +22 -0
- package/dist/score.test.js.map +1 -0
- package/dist/session-types.d.ts +132 -0
- package/dist/session-types.d.ts.map +1 -0
- package/dist/session-types.js +11 -0
- package/dist/session-types.js.map +1 -0
- package/dist/signal-envelope.d.ts +118 -0
- package/dist/signal-envelope.d.ts.map +1 -0
- package/dist/signal-envelope.js +84 -0
- package/dist/signal-envelope.js.map +1 -0
- package/dist/signal-envelope.test.d.ts +2 -0
- package/dist/signal-envelope.test.d.ts.map +1 -0
- package/dist/signal-envelope.test.js +168 -0
- package/dist/signal-envelope.test.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/verbose-detail.d.ts +26 -0
- package/dist/verbose-detail.d.ts.map +1 -0
- package/dist/verbose-detail.js +75 -0
- package/dist/verbose-detail.js.map +1 -0
- package/dist/verbose-detail.test.d.ts +2 -0
- package/dist/verbose-detail.test.d.ts.map +1 -0
- package/dist/verbose-detail.test.js +53 -0
- package/dist/verbose-detail.test.js.map +1 -0
- package/dist/verdict-envelope.test.d.ts +8 -0
- package/dist/verdict-envelope.test.d.ts.map +1 -0
- package/dist/verdict-envelope.test.js +67 -0
- package/dist/verdict-envelope.test.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-tool CLI flag currency (ADR-0021).
|
|
3
|
+
*
|
|
4
|
+
* The single source of truth for the common flags every tool's run command
|
|
5
|
+
* shares (`--json`, `--cwd`, `--quiet`, `--verbose`, `--debug`,
|
|
6
|
+
* `--report-to`/`--api-key`, `--open`). Each tool builds its command with
|
|
7
|
+
* `applyCommonFlags(...)` for the shared flags and adds only its
|
|
8
|
+
* genuinely tool-specific options by hand — so a flag's text/short-alias/default
|
|
9
|
+
* is declared once and cannot drift (it already had: `--report-to` read three
|
|
10
|
+
* different ways across the three tools before this registry).
|
|
11
|
+
*
|
|
12
|
+
* `commander` is referenced ONLY as a type (`import type`), matching the rest of
|
|
13
|
+
* contracts: `applyCommonFlags` calls `.option(...)` on a `Command` instance the
|
|
14
|
+
* caller passes in, so no runtime `commander` require lands in `dist`. The
|
|
15
|
+
* package keeps `commander` as an optional peer dependency (see index.ts).
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The canonical common-flag registry. Adding a tool means calling
|
|
19
|
+
* {@link applyCommonFlags} with the relevant keys — never re-declaring a flag.
|
|
20
|
+
*/
|
|
21
|
+
export const commonFlags = {
|
|
22
|
+
json: { flags: '--json', description: 'Output structured JSON', defaultValue: false },
|
|
23
|
+
cwd: { flags: '--cwd <path>', description: 'Target directory' },
|
|
24
|
+
quiet: {
|
|
25
|
+
flags: '-q, --quiet',
|
|
26
|
+
description: 'Suppress banner / boxes; print only the pass-fail summary',
|
|
27
|
+
defaultValue: false,
|
|
28
|
+
},
|
|
29
|
+
verbose: {
|
|
30
|
+
flags: '-v, --verbose',
|
|
31
|
+
description: 'Show the detailed report body inline',
|
|
32
|
+
defaultValue: false,
|
|
33
|
+
},
|
|
34
|
+
debug: {
|
|
35
|
+
flags: '--debug',
|
|
36
|
+
description: 'Enable debug mode for structured log output',
|
|
37
|
+
defaultValue: false,
|
|
38
|
+
},
|
|
39
|
+
reportTo: {
|
|
40
|
+
flags: '--report-to <url>',
|
|
41
|
+
description: 'POST findings to OpenSIP Cloud or a compatible endpoint',
|
|
42
|
+
},
|
|
43
|
+
apiKey: { flags: '--api-key <key>', description: 'API key for --report-to authentication' },
|
|
44
|
+
open: {
|
|
45
|
+
flags: '--open',
|
|
46
|
+
description: 'Launch the HTML report in your browser after the run completes',
|
|
47
|
+
defaultValue: false,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Apply the given common flags to a Commander command in registry order.
|
|
52
|
+
*
|
|
53
|
+
* `overrides` supplies per-invocation defaults for flags whose default is not a
|
|
54
|
+
* literal — notably `cwd`, where callers pass `{ cwd: process.cwd() }`. An
|
|
55
|
+
* override also wins over a spec's `defaultValue` when both are present.
|
|
56
|
+
*
|
|
57
|
+
* Returns the same `command` for chaining. No runtime `commander` dependency is
|
|
58
|
+
* introduced — only methods on the passed-in instance are called.
|
|
59
|
+
*/
|
|
60
|
+
export function applyCommonFlags(command, keys, overrides) {
|
|
61
|
+
for (const key of keys) {
|
|
62
|
+
const spec = commonFlags[key];
|
|
63
|
+
const def = overrides?.[key] ?? spec.defaultValue;
|
|
64
|
+
if (def === undefined)
|
|
65
|
+
command.option(spec.flags, spec.description);
|
|
66
|
+
else
|
|
67
|
+
command.option(spec.flags, spec.description, def);
|
|
68
|
+
}
|
|
69
|
+
return command;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The flags every tool's run command MUST declare (the parity set enforced by
|
|
73
|
+
* the `cross-tool-flag-parity` fitness check, ADR-0021). `open` is intentionally
|
|
74
|
+
* NOT mandatory — only report-producing tools expose it.
|
|
75
|
+
*/
|
|
76
|
+
export const MANDATORY_COMMON_FLAGS = [
|
|
77
|
+
'json',
|
|
78
|
+
'cwd',
|
|
79
|
+
'quiet',
|
|
80
|
+
'verbose',
|
|
81
|
+
'debug',
|
|
82
|
+
'reportTo',
|
|
83
|
+
'apiKey',
|
|
84
|
+
];
|
|
85
|
+
//# sourceMappingURL=cli-flags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-flags.js","sourceRoot":"","sources":["../src/cli-flags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA4BH;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAoD;IAC1E,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,wBAAwB,EAAE,YAAY,EAAE,KAAK,EAAE;IACrF,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,kBAAkB,EAAE;IAC/D,KAAK,EAAE;QACL,KAAK,EAAE,aAAa;QACpB,WAAW,EAAE,2DAA2D;QACxE,YAAY,EAAE,KAAK;KACpB;IACD,OAAO,EAAE;QACP,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,sCAAsC;QACnD,YAAY,EAAE,KAAK;KACpB;IACD,KAAK,EAAE;QACL,KAAK,EAAE,SAAS;QAChB,WAAW,EAAE,6CAA6C;QAC1D,YAAY,EAAE,KAAK;KACpB;IACD,QAAQ,EAAE;QACR,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,yDAAyD;KACvE;IACD,MAAM,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,wCAAwC,EAAE;IAC3F,IAAI,EAAE;QACJ,KAAK,EAAE,QAAQ;QACf,WAAW,EAAE,gEAAgE;QAC7E,YAAY,EAAE,KAAK;KACpB;CACO,CAAC;AAEX;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAgB,EAChB,IAA8B,EAC9B,SAA4D;IAE5D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC;QAClD,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;;YAC/D,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA6B;IAC9D,MAAM;IACN,KAAK;IACL,OAAO;IACP,SAAS;IACT,OAAO;IACP,UAAU;IACV,QAAQ;CACA,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-flags.test.d.ts","sourceRoot":"","sources":["../src/cli-flags.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { applyCommonFlags, commonFlags, MANDATORY_COMMON_FLAGS, } from './cli-flags.js';
|
|
4
|
+
describe('commonFlags registry', () => {
|
|
5
|
+
it('pins the canonical flag strings + descriptions (drift fails here)', () => {
|
|
6
|
+
expect(commonFlags.json).toEqual({
|
|
7
|
+
flags: '--json',
|
|
8
|
+
description: 'Output structured JSON',
|
|
9
|
+
defaultValue: false,
|
|
10
|
+
});
|
|
11
|
+
expect(commonFlags.cwd).toEqual({ flags: '--cwd <path>', description: 'Target directory' });
|
|
12
|
+
expect(commonFlags.verbose.flags).toBe('-v, --verbose');
|
|
13
|
+
expect(commonFlags.quiet.flags).toBe('-q, --quiet');
|
|
14
|
+
// The drift this registry exists to prevent: one canonical --report-to string.
|
|
15
|
+
expect(commonFlags.reportTo.description).toBe('POST findings to OpenSIP Cloud or a compatible endpoint');
|
|
16
|
+
expect(commonFlags.apiKey.description).toBe('API key for --report-to authentication');
|
|
17
|
+
});
|
|
18
|
+
it('mandatory set is the parity-enforced flags (open is optional)', () => {
|
|
19
|
+
expect([...MANDATORY_COMMON_FLAGS].sort()).toEqual(['apiKey', 'cwd', 'debug', 'json', 'quiet', 'reportTo', 'verbose'].sort());
|
|
20
|
+
expect(MANDATORY_COMMON_FLAGS).not.toContain('open');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('applyCommonFlags', () => {
|
|
24
|
+
it('registers exactly the requested flags with their registry specs', () => {
|
|
25
|
+
const cmd = new Command('demo');
|
|
26
|
+
applyCommonFlags(cmd, ['json', 'verbose', 'reportTo']);
|
|
27
|
+
const longNames = cmd.options.map((o) => o.long);
|
|
28
|
+
expect(longNames).toEqual(['--json', '--verbose', '--report-to']);
|
|
29
|
+
const verbose = cmd.options.find((o) => o.long === '--verbose');
|
|
30
|
+
expect(verbose?.short).toBe('-v');
|
|
31
|
+
expect(verbose?.description).toBe('Show the detailed report body inline');
|
|
32
|
+
});
|
|
33
|
+
it('applies literal defaults and honors the cwd override', () => {
|
|
34
|
+
const cmd = new Command('demo');
|
|
35
|
+
applyCommonFlags(cmd, ['json', 'cwd'], { cwd: '/work/proj' });
|
|
36
|
+
const parsed = cmd.opts();
|
|
37
|
+
expect(parsed.json).toBe(false); // literal default from the registry
|
|
38
|
+
expect(parsed.cwd).toBe('/work/proj'); // per-invocation override
|
|
39
|
+
});
|
|
40
|
+
it('returns the same command for chaining', () => {
|
|
41
|
+
const cmd = new Command('demo');
|
|
42
|
+
expect(applyCommonFlags(cmd, ['debug'])).toBe(cmd);
|
|
43
|
+
});
|
|
44
|
+
it('covers every registry key without throwing', () => {
|
|
45
|
+
const cmd = new Command('demo');
|
|
46
|
+
const allKeys = Object.keys(commonFlags);
|
|
47
|
+
applyCommonFlags(cmd, allKeys, { cwd: process.cwd() });
|
|
48
|
+
expect(cmd.options.length).toBe(allKeys.length);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=cli-flags.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-flags.test.js","sourceRoot":"","sources":["../src/cli-flags.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandOutcome — the standard OUTER currency wrapping every command result and
|
|
3
|
+
* error (north-star §5.5, launch).
|
|
4
|
+
*
|
|
5
|
+
* `SignalEnvelope` (ADR-0011) is the strong INNER currency, but the outer shape
|
|
6
|
+
* drifted: run commands emitted a bare envelope, list/report commands a bare
|
|
7
|
+
* `CommandResult`, errors a bare `ErrorResult`, and the bootstrap bypassed all of
|
|
8
|
+
* it (`process.exit` + raw stream writes). So a machine consumer could not rely on
|
|
9
|
+
* one schema for every outcome — and `--json` produced nothing structured for the
|
|
10
|
+
* highest-friction failures (no project, bad schema), the ones that happen before
|
|
11
|
+
* a handler ever runs.
|
|
12
|
+
*
|
|
13
|
+
* `CommandOutcome<T>` is that one schema. It wraps the **unchanged** inner
|
|
14
|
+
* envelope under `.envelope` (run commands) or the domain `CommandResult` under
|
|
15
|
+
* `.data` (list/report/…); an error or bootstrap outcome carries `errors` +
|
|
16
|
+
* `diagnostics` with neither payload. The host ASSEMBLES it — stamping
|
|
17
|
+
* `kind`/`status`/`exitCode`/`diagnostics` from the handler's pure-domain return —
|
|
18
|
+
* so no tool, first-party or external, chooses its own error JSON or success
|
|
19
|
+
* carrier. The handler contract does not change (the command-plane spec's
|
|
20
|
+
* "no handler contract change"); all the outer-shape change lands at the host
|
|
21
|
+
* dispatch seam.
|
|
22
|
+
*
|
|
23
|
+
* This is the one user-visible breaking change before GA: `--json` now nests the
|
|
24
|
+
* envelope one level down (consumers read `.envelope`/`.data`). The inner envelope
|
|
25
|
+
* is byte-identical to 2.7.0+. Shipped as a 2.x minor with a migration note, like
|
|
26
|
+
* the 2.7.0 `--json` change (ADR-0024).
|
|
27
|
+
*
|
|
28
|
+
* Types-only (the contracts charter): every field is a primitive, a sibling
|
|
29
|
+
* contract type, or a readonly array thereof.
|
|
30
|
+
*/
|
|
31
|
+
import type { SignalEnvelope } from './signal-envelope.js';
|
|
32
|
+
import type { RunDiagnostics } from '@opensip-cli/core';
|
|
33
|
+
/** Outer status of a command outcome. `partial` = ran but with non-fatal gaps. */
|
|
34
|
+
export type CommandOutcomeStatus = 'ok' | 'error' | 'partial';
|
|
35
|
+
/**
|
|
36
|
+
* One error attached to a `status:'error'` outcome. The structured successor to
|
|
37
|
+
* the bare `ErrorResult` shape — `message` plus an optional actionable
|
|
38
|
+
* `suggestion` (the field `--json` consumers most need on a bootstrap failure)
|
|
39
|
+
* and an optional machine `code` (e.g. a `ToolError` code).
|
|
40
|
+
*/
|
|
41
|
+
export interface ErrorDetail {
|
|
42
|
+
readonly message: string;
|
|
43
|
+
readonly suggestion?: string;
|
|
44
|
+
readonly code?: string;
|
|
45
|
+
}
|
|
46
|
+
/** One non-fatal warning attached to an outcome. */
|
|
47
|
+
export interface WarningDetail {
|
|
48
|
+
readonly message: string;
|
|
49
|
+
readonly code?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Hints the renderer consumes when materializing the outcome. RESERVED for
|
|
53
|
+
* launch — populated only with what the existing renderer already branches on
|
|
54
|
+
* (`quiet`, `noColor`, `preferredFormat`); the field exists so later releases can
|
|
55
|
+
* extend rendering policy without another outer-shape break. Do not invent new
|
|
56
|
+
* hints here.
|
|
57
|
+
*/
|
|
58
|
+
export interface RenderHints {
|
|
59
|
+
readonly quiet?: boolean;
|
|
60
|
+
readonly noColor?: boolean;
|
|
61
|
+
readonly preferredFormat?: 'json' | 'human';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The one outer outcome shape every command — and the bootstrap — emits.
|
|
65
|
+
*
|
|
66
|
+
* - `kind` identifies the command/source (`'<name>.run'` for envelope output,
|
|
67
|
+
* `'<name>'` for a `CommandResult`, `'bootstrap.error'` for a pre-handler
|
|
68
|
+
* failure). Derived by the host assembler from the `CommandSpec`, not chosen by
|
|
69
|
+
* the tool.
|
|
70
|
+
* - `data` (a `CommandResult`) and `envelope` (a `SignalEnvelope`) are the two
|
|
71
|
+
* mutually-informative payload slots — BOTH optional, because an error or
|
|
72
|
+
* bootstrap outcome has neither, only `errors` + `diagnostics`.
|
|
73
|
+
* - `diagnostics` is attached by the host from the scope-owned diagnostics bus
|
|
74
|
+
* (north-star §5.10).
|
|
75
|
+
*/
|
|
76
|
+
export interface CommandOutcome<T = unknown> {
|
|
77
|
+
readonly kind: string;
|
|
78
|
+
readonly status: CommandOutcomeStatus;
|
|
79
|
+
readonly exitCode: number;
|
|
80
|
+
readonly data?: T;
|
|
81
|
+
readonly envelope?: SignalEnvelope;
|
|
82
|
+
readonly errors?: readonly ErrorDetail[];
|
|
83
|
+
readonly warnings?: readonly WarningDetail[];
|
|
84
|
+
readonly diagnostics?: RunDiagnostics;
|
|
85
|
+
readonly renderHints?: RenderHints;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=command-outcome.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-outcome.d.ts","sourceRoot":"","sources":["../src/command-outcome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,kFAAkF;AAClF,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,oDAAoD;AACpD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7C;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,cAAc,CAAC;IACtC,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;CACpC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandOutcome — the standard OUTER currency wrapping every command result and
|
|
3
|
+
* error (north-star §5.5, launch).
|
|
4
|
+
*
|
|
5
|
+
* `SignalEnvelope` (ADR-0011) is the strong INNER currency, but the outer shape
|
|
6
|
+
* drifted: run commands emitted a bare envelope, list/report commands a bare
|
|
7
|
+
* `CommandResult`, errors a bare `ErrorResult`, and the bootstrap bypassed all of
|
|
8
|
+
* it (`process.exit` + raw stream writes). So a machine consumer could not rely on
|
|
9
|
+
* one schema for every outcome — and `--json` produced nothing structured for the
|
|
10
|
+
* highest-friction failures (no project, bad schema), the ones that happen before
|
|
11
|
+
* a handler ever runs.
|
|
12
|
+
*
|
|
13
|
+
* `CommandOutcome<T>` is that one schema. It wraps the **unchanged** inner
|
|
14
|
+
* envelope under `.envelope` (run commands) or the domain `CommandResult` under
|
|
15
|
+
* `.data` (list/report/…); an error or bootstrap outcome carries `errors` +
|
|
16
|
+
* `diagnostics` with neither payload. The host ASSEMBLES it — stamping
|
|
17
|
+
* `kind`/`status`/`exitCode`/`diagnostics` from the handler's pure-domain return —
|
|
18
|
+
* so no tool, first-party or external, chooses its own error JSON or success
|
|
19
|
+
* carrier. The handler contract does not change (the command-plane spec's
|
|
20
|
+
* "no handler contract change"); all the outer-shape change lands at the host
|
|
21
|
+
* dispatch seam.
|
|
22
|
+
*
|
|
23
|
+
* This is the one user-visible breaking change before GA: `--json` now nests the
|
|
24
|
+
* envelope one level down (consumers read `.envelope`/`.data`). The inner envelope
|
|
25
|
+
* is byte-identical to 2.7.0+. Shipped as a 2.x minor with a migration note, like
|
|
26
|
+
* the 2.7.0 `--json` change (ADR-0024).
|
|
27
|
+
*
|
|
28
|
+
* Types-only (the contracts charter): every field is a primitive, a sibling
|
|
29
|
+
* contract type, or a readonly array thereof.
|
|
30
|
+
*/
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=command-outcome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-outcome.js","sourceRoot":"","sources":["../src/command-outcome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandOutcome shape — the three outcome flavours construct and serialize.
|
|
3
|
+
*
|
|
4
|
+
* The contract is "one outer schema for every result and error". The load-bearing
|
|
5
|
+
* checks: a run outcome nests the UNCHANGED envelope under `.envelope`; a
|
|
6
|
+
* command-result outcome uses `.data`; a bootstrap/error outcome carries neither
|
|
7
|
+
* payload, only `errors` (+ diagnostics). All three JSON round-trip.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=command-outcome.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-outcome.test.d.ts","sourceRoot":"","sources":["../src/command-outcome.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandOutcome shape — the three outcome flavours construct and serialize.
|
|
3
|
+
*
|
|
4
|
+
* The contract is "one outer schema for every result and error". The load-bearing
|
|
5
|
+
* checks: a run outcome nests the UNCHANGED envelope under `.envelope`; a
|
|
6
|
+
* command-result outcome uses `.data`; a bootstrap/error outcome carries neither
|
|
7
|
+
* payload, only `errors` (+ diagnostics). All three JSON round-trip.
|
|
8
|
+
*/
|
|
9
|
+
import { HOST_VERDICT_POLICY_FALLBACK } from '@opensip-cli/core';
|
|
10
|
+
import { describe, it, expect } from 'vitest';
|
|
11
|
+
import { buildSignalEnvelope } from './signal-envelope.js';
|
|
12
|
+
const DIAGNOSTICS = { runId: 'run_1', events: [] };
|
|
13
|
+
describe('CommandOutcome', () => {
|
|
14
|
+
it('wraps a run as .envelope without altering the inner envelope', () => {
|
|
15
|
+
const envelope = buildSignalEnvelope({
|
|
16
|
+
tool: 'fit',
|
|
17
|
+
runId: 'run_1',
|
|
18
|
+
createdAt: '2026-06-07T00:00:00.000Z',
|
|
19
|
+
units: [{ slug: 'a', passed: true, durationMs: 1 }],
|
|
20
|
+
signals: [],
|
|
21
|
+
policy: HOST_VERDICT_POLICY_FALLBACK,
|
|
22
|
+
runFaulted: false,
|
|
23
|
+
});
|
|
24
|
+
const outcome = {
|
|
25
|
+
kind: 'fit.run',
|
|
26
|
+
status: 'ok',
|
|
27
|
+
exitCode: 0,
|
|
28
|
+
envelope,
|
|
29
|
+
diagnostics: DIAGNOSTICS,
|
|
30
|
+
};
|
|
31
|
+
// The inner envelope is byte-identical — the break is purely the new wrapper.
|
|
32
|
+
expect(outcome.envelope).toBe(envelope);
|
|
33
|
+
expect(outcome.data).toBeUndefined();
|
|
34
|
+
const wire = JSON.stringify(outcome);
|
|
35
|
+
expect(JSON.parse(wire).envelope).toEqual(envelope);
|
|
36
|
+
});
|
|
37
|
+
it('wraps a command result as .data', () => {
|
|
38
|
+
const outcome = {
|
|
39
|
+
kind: 'sessions.list',
|
|
40
|
+
status: 'ok',
|
|
41
|
+
exitCode: 0,
|
|
42
|
+
data: { type: 'history', count: 4 },
|
|
43
|
+
};
|
|
44
|
+
expect(outcome.data).toEqual({ type: 'history', count: 4 });
|
|
45
|
+
expect(outcome.envelope).toBeUndefined();
|
|
46
|
+
});
|
|
47
|
+
it('carries a bootstrap error with neither payload, only errors + diagnostics', () => {
|
|
48
|
+
const outcome = {
|
|
49
|
+
kind: 'bootstrap.error',
|
|
50
|
+
status: 'error',
|
|
51
|
+
exitCode: 2,
|
|
52
|
+
errors: [
|
|
53
|
+
{
|
|
54
|
+
message: 'No OpenSIP CLI project found.',
|
|
55
|
+
suggestion: 'Run opensip init.',
|
|
56
|
+
code: 'CONFIGURATION_ERROR',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
diagnostics: DIAGNOSTICS,
|
|
60
|
+
};
|
|
61
|
+
expect(outcome.data).toBeUndefined();
|
|
62
|
+
expect(outcome.envelope).toBeUndefined();
|
|
63
|
+
expect(outcome.errors?.[0]?.suggestion).toBe('Run opensip init.');
|
|
64
|
+
const wire = JSON.stringify(outcome);
|
|
65
|
+
expect(JSON.parse(wire)).toEqual(outcome);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=command-outcome.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-outcome.test.js","sourceRoot":"","sources":["../src/command-outcome.test.ts"],"names":[],"mappings":"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"}
|