@agent-surface/cli 0.10.0 → 0.11.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.
Files changed (41) hide show
  1. package/README.md +64 -23
  2. package/dist/bin.js +63 -41
  3. package/dist/bin.js.map +1 -1
  4. package/dist/check-XU3FYNGJ.js +122 -0
  5. package/dist/check-XU3FYNGJ.js.map +1 -0
  6. package/dist/chunk-2FG527AM.js +740 -0
  7. package/dist/chunk-2FG527AM.js.map +1 -0
  8. package/dist/chunk-AFLVTBI6.js +316 -0
  9. package/dist/chunk-AFLVTBI6.js.map +1 -0
  10. package/dist/chunk-DYDSJM7R.js +170 -0
  11. package/dist/chunk-DYDSJM7R.js.map +1 -0
  12. package/dist/{chunk-FYEXHWGG.js → chunk-QIVOZAWX.js} +52 -2
  13. package/dist/chunk-QIVOZAWX.js.map +1 -0
  14. package/dist/index.d.ts +36 -2
  15. package/dist/init-ODFEGU3P.js +141 -0
  16. package/dist/init-ODFEGU3P.js.map +1 -0
  17. package/dist/{ink-HBPOQTRS.js → ink-P23VKP4H.js} +102 -33
  18. package/dist/ink-P23VKP4H.js.map +1 -0
  19. package/dist/inspect-6XKULUH2.js +108 -0
  20. package/dist/inspect-6XKULUH2.js.map +1 -0
  21. package/dist/snapshot-YGEDJVTG.js +59 -0
  22. package/dist/snapshot-YGEDJVTG.js.map +1 -0
  23. package/package.json +4 -4
  24. package/dist/capabilities-OLFYMHCL.js +0 -37
  25. package/dist/capabilities-OLFYMHCL.js.map +0 -1
  26. package/dist/check-IN4XKAND.js +0 -84
  27. package/dist/check-IN4XKAND.js.map +0 -1
  28. package/dist/chunk-4AEQKM2X.js +0 -498
  29. package/dist/chunk-4AEQKM2X.js.map +0 -1
  30. package/dist/chunk-A27Y7ALQ.js +0 -51
  31. package/dist/chunk-A27Y7ALQ.js.map +0 -1
  32. package/dist/chunk-FYEXHWGG.js.map +0 -1
  33. package/dist/chunk-ODUIFFPM.js +0 -104
  34. package/dist/chunk-ODUIFFPM.js.map +0 -1
  35. package/dist/coverage-HCHLJTDD.js +0 -133
  36. package/dist/coverage-HCHLJTDD.js.map +0 -1
  37. package/dist/ink-HBPOQTRS.js.map +0 -1
  38. package/dist/inspect-NJNB6CAS.js +0 -213
  39. package/dist/inspect-NJNB6CAS.js.map +0 -1
  40. package/dist/snapshot-JQAB73OV.js +0 -38
  41. package/dist/snapshot-JQAB73OV.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/load.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createServer, type ViteDevServer } from \"vite\";\nimport { ViteNodeServer } from \"vite-node/server\";\nimport { ViteNodeRunner } from \"vite-node/client\";\nimport { installSourcemapsSupport } from \"vite-node/source-map\";\nimport type { CollectOptions, CollectResult } from \"./collect.js\";\nimport type { SurfaceConfig } from \"./config.js\";\n\nconst CONFIG_NAMES = [\n \"agent-surface.config.tsx\",\n \"agent-surface.config.ts\",\n \"agent-surface.config.mjs\",\n \"agent-surface.config.js\",\n];\n\n/** Walks up from `from` looking for an `agent-surface.config.*`. */\nexport function findConfig(from: string = process.cwd()): string | undefined {\n let dir = resolve(from);\n for (;;) {\n for (const name of CONFIG_NAMES) {\n const candidate = join(dir, name);\n if (existsSync(candidate)) return candidate;\n }\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\n/** `dist/collect.js` when installed; `src/collect.ts` when run from source. */\nfunction collectorPath(): string {\n for (const ext of [\"js\", \"ts\"]) {\n const candidate = fileURLToPath(new URL(`./collect.${ext}`, import.meta.url));\n if (existsSync(candidate)) return candidate;\n }\n throw new Error(\"could not locate the agent-surface collector module\");\n}\n\nexport interface SurfaceRunner {\n config: SurfaceConfig;\n scenarioNames: string[];\n collect(options: CollectOptions): Promise<CollectResult>;\n close(): Promise<void>;\n}\n\n/**\n * Boots a Vite dev server on the app's own config, so the config file and the\n * app modules it imports are transformed and resolved exactly as the app\n * resolves them — its aliases, its plugins, its TSX.\n */\nexport async function createSurfaceRunner(configPath: string): Promise<SurfaceRunner> {\n const absoluteConfig = isAbsolute(configPath) ? configPath : resolve(configPath);\n if (!existsSync(absoluteConfig)) {\n throw new Error(`config not found: ${absoluteConfig}`);\n }\n const root = dirname(absoluteConfig);\n\n let server: ViteDevServer;\n try {\n server = await createServer({\n root,\n logLevel: \"error\",\n // `serve` so plugins behave as they do in dev; nothing is ever served.\n server: { middlewareMode: true, watch: null, fs: { strict: false } },\n optimizeDeps: { noDiscovery: true, include: [] },\n resolve: {\n // Both halves of the graph must agree on these. React because two\n // copies break hooks; core because `explainSurface` finds the registry\n // through a Symbol, which is per-module-instance (see collect.ts).\n dedupe: [\n \"react\",\n \"react-dom\",\n \"@agent-surface/core\",\n \"@agent-surface/react\",\n \"@agent-surface/testing\",\n ],\n },\n });\n } catch (error) {\n throw new Error(\n `could not start Vite for ${root}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n try {\n await server.pluginContainer.buildStart({});\n } catch {\n // Vite keeps moving this; a plugin that needs buildStart will say so itself.\n }\n\n const nodeServer = new ViteNodeServer(server);\n installSourcemapsSupport({ getSourceMap: (source) => nodeServer.getSourceMap(source) });\n\n const runner = new ViteNodeRunner({\n root: server.config.root,\n base: server.config.base,\n fetchModule: (id) => nodeServer.fetchModule(id),\n resolveId: (id, importer) => nodeServer.resolveId(id, importer),\n });\n\n const close = async (): Promise<void> => {\n await server.close();\n };\n\n try {\n const configModule = (await runner.executeFile(absoluteConfig)) as {\n default?: SurfaceConfig;\n };\n const config = configModule.default;\n if (!config || typeof config.mount !== \"function\") {\n throw new Error(\n `${absoluteConfig} must \\`export default defineSurface({ mount, scenarios })\\``,\n );\n }\n const scenarioNames = Object.keys(config.scenarios ?? {});\n if (scenarioNames.length === 0) {\n throw new Error(`${absoluteConfig} defines no scenarios`);\n }\n\n // Same runner ⇒ same module graph ⇒ the collector shares React and core\n // with the app tree it is about to mount.\n const collector = (await runner.executeFile(collectorPath())) as {\n collect(config: SurfaceConfig, options: CollectOptions): Promise<CollectResult>;\n };\n\n return {\n config,\n scenarioNames,\n collect: async (options) => {\n // Scoped to the mount, never process-wide: `act()` needs it, and Ink\n // renders its own React tree afterwards — with the flag still set,\n // every frame of the CLI's own UI prints React's \"not wrapped in\n // act(...)\" warning at the user.\n const globals = globalThis as Record<string, unknown>;\n const previous = globals[\"IS_REACT_ACT_ENVIRONMENT\"];\n globals[\"IS_REACT_ACT_ENVIRONMENT\"] = true;\n try {\n return await collector.collect(config, options);\n } finally {\n globals[\"IS_REACT_ACT_ENVIRONMENT\"] = previous;\n }\n },\n close,\n };\n } catch (error) {\n await close();\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,104 +0,0 @@
1
- // src/baseline.ts
2
- import { mkdirSync, readFileSync, writeFileSync } from "fs";
3
- import { dirname, join, resolve } from "path";
4
- import { serializeSurfaceSnapshot } from "@agent-surface/testing";
5
- var DEFAULT_BASELINE_DIR = ".agent-surface";
6
- function baselineDirFor(configPath, configured) {
7
- return resolve(dirname(configPath), configured ?? DEFAULT_BASELINE_DIR);
8
- }
9
- function baselinePath(dir, scenario) {
10
- return join(dir, `${scenario}.json`);
11
- }
12
- function normalize(snapshot) {
13
- return serializeSurfaceSnapshot(snapshot);
14
- }
15
- function readBaseline(path) {
16
- try {
17
- return JSON.parse(readFileSync(path, "utf8"));
18
- } catch {
19
- return void 0;
20
- }
21
- }
22
- function writeBaseline(path, value) {
23
- mkdirSync(dirname(path), { recursive: true });
24
- writeFileSync(path, `${JSON.stringify(value, null, 2)}
25
- `, "utf8");
26
- }
27
- var PATH_SEGMENT = /([^.[\]]+)|\[(\d+)\]/g;
28
- function subjectFor(document, path) {
29
- let node = document;
30
- let subject;
31
- for (const match of path.matchAll(PATH_SEGMENT)) {
32
- if (typeof node !== "object" || node === null) return subject;
33
- const record = node;
34
- const candidate = record["capabilityId"] ?? record["procedureId"];
35
- if (typeof candidate === "string") subject = candidate;
36
- const key = match[1] ?? match[2];
37
- if (key === void 0) return subject;
38
- node = record[key];
39
- }
40
- if (typeof node === "object" && node !== null) {
41
- const record = node;
42
- const candidate = record["capabilityId"] ?? record["procedureId"];
43
- if (typeof candidate === "string") subject = candidate;
44
- }
45
- return subject;
46
- }
47
- function annotate(entries, after, before) {
48
- return entries.map((entry) => {
49
- const subject = subjectFor(after, entry.path) ?? subjectFor(before, entry.path);
50
- return subject ? { ...entry, subject } : entry;
51
- });
52
- }
53
- function diff(before, after, path = "") {
54
- if (Object.is(before, after)) return [];
55
- const bothArrays = Array.isArray(before) && Array.isArray(after);
56
- const bothObjects = !bothArrays && typeof before === "object" && typeof after === "object" && before !== null && after !== null;
57
- if (bothArrays) {
58
- const entries = [];
59
- const max = Math.max(before.length, after.length);
60
- for (let i = 0; i < max; i++) {
61
- const at = `${path}[${i}]`;
62
- if (i >= before.length) entries.push({ path: at, kind: "added", after: after[i] });
63
- else if (i >= after.length) entries.push({ path: at, kind: "removed", before: before[i] });
64
- else entries.push(...diff(before[i], after[i], at));
65
- }
66
- return entries;
67
- }
68
- if (bothObjects) {
69
- const entries = [];
70
- const beforeRecord = before;
71
- const afterRecord = after;
72
- const keys = /* @__PURE__ */ new Set([...Object.keys(beforeRecord), ...Object.keys(afterRecord)]);
73
- for (const key of [...keys].sort()) {
74
- const at = path ? `${path}.${key}` : key;
75
- if (!(key in beforeRecord)) {
76
- entries.push({ path: at, kind: "added", after: afterRecord[key] });
77
- } else if (!(key in afterRecord)) {
78
- entries.push({ path: at, kind: "removed", before: beforeRecord[key] });
79
- } else {
80
- entries.push(...diff(beforeRecord[key], afterRecord[key], at));
81
- }
82
- }
83
- return entries;
84
- }
85
- if (JSON.stringify(before) === JSON.stringify(after)) return [];
86
- return [{ path: path || "<root>", kind: "changed", before, after }];
87
- }
88
- function formatValue(value) {
89
- if (value === void 0) return "\u2014";
90
- const text = typeof value === "string" ? value : JSON.stringify(value);
91
- return text.length > 120 ? `${text.slice(0, 117)}\u2026` : text;
92
- }
93
-
94
- export {
95
- baselineDirFor,
96
- baselinePath,
97
- normalize,
98
- readBaseline,
99
- writeBaseline,
100
- annotate,
101
- diff,
102
- formatValue
103
- };
104
- //# sourceMappingURL=chunk-ODUIFFPM.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/baseline.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { serializeSurfaceSnapshot } from \"@agent-surface/testing\";\nimport type { AgentSurfaceSnapshot } from \"@agent-surface/core\";\n\nexport const DEFAULT_BASELINE_DIR = \".agent-surface\";\n\nexport function baselineDirFor(configPath: string, configured?: string): string {\n return resolve(dirname(configPath), configured ?? DEFAULT_BASELINE_DIR);\n}\n\nexport function baselinePath(dir: string, scenario: string): string {\n return join(dir, `${scenario}.json`);\n}\n\n/**\n * The committed form. `serializeSurfaceSnapshot` is the same normalizer the\n * Vitest matcher uses: registration ids become stable placeholders and the\n * volatile fields (surfaceId, capturedAt, version) drop out, so a baseline\n * diff is a diff of *what agents can see* and nothing else.\n */\nexport function normalize(snapshot: AgentSurfaceSnapshot): unknown {\n return serializeSurfaceSnapshot(snapshot);\n}\n\nexport function readBaseline(path: string): unknown | undefined {\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n } catch {\n return undefined;\n }\n}\n\nexport function writeBaseline(path: string, value: unknown): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n}\n\nexport interface DiffEntry {\n path: string;\n kind: \"added\" | \"removed\" | \"changed\";\n before?: unknown;\n after?: unknown;\n /**\n * The capability the change belongs to. A reviewer needs to know that\n * `view:devices.table.sort` changed — `components[3].actions[1]` is the same\n * fact in a form nobody can act on.\n */\n subject?: string;\n}\n\nconst PATH_SEGMENT = /([^.[\\]]+)|\\[(\\d+)\\]/g;\n\n/** Nearest enclosing capability id for a diff path, if the path sits inside one. */\nfunction subjectFor(document: unknown, path: string): string | undefined {\n let node: unknown = document;\n let subject: string | undefined;\n for (const match of path.matchAll(PATH_SEGMENT)) {\n if (typeof node !== \"object\" || node === null) return subject;\n const record = node as Record<string, unknown>;\n const candidate = record[\"capabilityId\"] ?? record[\"procedureId\"];\n if (typeof candidate === \"string\") subject = candidate;\n const key = match[1] ?? match[2];\n if (key === undefined) return subject;\n node = record[key];\n }\n if (typeof node === \"object\" && node !== null) {\n const record = node as Record<string, unknown>;\n const candidate = record[\"capabilityId\"] ?? record[\"procedureId\"];\n if (typeof candidate === \"string\") subject = candidate;\n }\n return subject;\n}\n\n/** Labels each entry with the capability it belongs to, when there is one. */\nexport function annotate(entries: DiffEntry[], after: unknown, before: unknown): DiffEntry[] {\n return entries.map((entry) => {\n const subject = subjectFor(after, entry.path) ?? subjectFor(before, entry.path);\n return subject ? { ...entry, subject } : entry;\n });\n}\n\n/**\n * Structural diff, deliberately total: every difference is drift, including a\n * changed description. Descriptions are the provider's cached prompt prefix\n * (D28) — a silent edit re-bills every conversation, so it is exactly the kind\n * of change a reviewer should see.\n */\nexport function diff(before: unknown, after: unknown, path = \"\"): DiffEntry[] {\n if (Object.is(before, after)) return [];\n\n const bothArrays = Array.isArray(before) && Array.isArray(after);\n const bothObjects =\n !bothArrays &&\n typeof before === \"object\" &&\n typeof after === \"object\" &&\n before !== null &&\n after !== null;\n\n if (bothArrays) {\n const entries: DiffEntry[] = [];\n const max = Math.max(before.length, after.length);\n for (let i = 0; i < max; i++) {\n const at = `${path}[${i}]`;\n if (i >= before.length) entries.push({ path: at, kind: \"added\", after: after[i] });\n else if (i >= after.length) entries.push({ path: at, kind: \"removed\", before: before[i] });\n else entries.push(...diff(before[i], after[i], at));\n }\n return entries;\n }\n\n if (bothObjects) {\n const entries: DiffEntry[] = [];\n const beforeRecord = before as Record<string, unknown>;\n const afterRecord = after as Record<string, unknown>;\n const keys = new Set([...Object.keys(beforeRecord), ...Object.keys(afterRecord)]);\n for (const key of [...keys].sort()) {\n const at = path ? `${path}.${key}` : key;\n if (!(key in beforeRecord)) {\n entries.push({ path: at, kind: \"added\", after: afterRecord[key] });\n } else if (!(key in afterRecord)) {\n entries.push({ path: at, kind: \"removed\", before: beforeRecord[key] });\n } else {\n entries.push(...diff(beforeRecord[key], afterRecord[key], at));\n }\n }\n return entries;\n }\n\n if (JSON.stringify(before) === JSON.stringify(after)) return [];\n return [{ path: path || \"<root>\", kind: \"changed\", before, after }];\n}\n\nexport function formatValue(value: unknown): string {\n if (value === undefined) return \"—\";\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n return text.length > 120 ? `${text.slice(0, 117)}…` : text;\n}\n"],"mappings":"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","names":[]}
@@ -1,133 +0,0 @@
1
- import {
2
- authoredIds,
3
- extractCapabilities,
4
- renderCoveragePlain,
5
- unresolved
6
- } from "./chunk-4AEQKM2X.js";
7
- import {
8
- createSurfaceRunner
9
- } from "./chunk-FYEXHWGG.js";
10
- import {
11
- write
12
- } from "./chunk-A27Y7ALQ.js";
13
- import {
14
- baselineDirFor
15
- } from "./chunk-ODUIFFPM.js";
16
-
17
- // src/commands/coverage.ts
18
- import { dirname } from "path";
19
-
20
- // src/coverage.ts
21
- import { existsSync, readFileSync } from "fs";
22
- import { join } from "path";
23
- var ALLOWLIST_FILE = "coverage-allow.json";
24
- function allowlistPathFor(baselineDir) {
25
- return join(baselineDir, ALLOWLIST_FILE);
26
- }
27
- function readAllowlist(path) {
28
- if (!existsSync(path)) return {};
29
- let parsed;
30
- try {
31
- parsed = JSON.parse(readFileSync(path, "utf8"));
32
- } catch (error) {
33
- throw new Error(
34
- `could not parse ${path}: ${error instanceof Error ? error.message : String(error)}`
35
- );
36
- }
37
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
38
- throw new Error(`${path} must be a JSON object of { "capabilityId": "reason" }`);
39
- }
40
- const allowlist = {};
41
- for (const [id, reason] of Object.entries(parsed)) {
42
- if (typeof reason !== "string" || reason.trim() === "") {
43
- throw new Error(`${path}: "${id}" needs a non-empty reason string`);
44
- }
45
- allowlist[id] = reason;
46
- }
47
- return allowlist;
48
- }
49
- function buildCoverageReport(input) {
50
- const unreached = [];
51
- const allowed = [];
52
- for (const id of [...input.authored].sort()) {
53
- if (input.reachedIds.has(id)) continue;
54
- if (id in input.allowlist) {
55
- allowed.push(id);
56
- continue;
57
- }
58
- unreached.push({ capabilityId: id, origin: input.origins.get(id) ?? { file: "?", line: 0 } });
59
- }
60
- const staleAllowlist = Object.keys(input.allowlist).filter((id) => input.reachedIds.has(id) || !input.authored.has(id)).sort();
61
- const unaccounted = [...input.reachedIds].filter((id) => !input.authored.has(id)).sort();
62
- const domainReached = unaccounted.filter((id) => id.startsWith("domain:"));
63
- const undeclared = unaccounted.filter((id) => !id.startsWith("domain:"));
64
- return {
65
- authored: input.authored.size,
66
- reached: [...input.authored].filter((id) => input.reachedIds.has(id)).length,
67
- scenarios: input.scenarios,
68
- unreached,
69
- undeclared,
70
- domainReached,
71
- unresolved: input.unresolved,
72
- allowed,
73
- staleAllowlist,
74
- allowlistPath: input.allowlistPath
75
- };
76
- }
77
- function coverageExitCode(report) {
78
- if (report.unreached.length > 0) return 1;
79
- if (report.unresolved.length > 0) return 1;
80
- if (report.staleAllowlist.length > 0) return 1;
81
- return 0;
82
- }
83
-
84
- // src/commands/coverage.ts
85
- async function runCoverage(options) {
86
- const root = dirname(options.configPath);
87
- const inventory = extractCapabilities({
88
- root,
89
- ...options.tsconfig ? { tsconfig: options.tsconfig } : {}
90
- });
91
- const authored = authoredIds(inventory);
92
- const origins = /* @__PURE__ */ new Map();
93
- for (const capability of inventory.capabilities) {
94
- if (!origins.has(capability.capabilityId)) origins.set(capability.capabilityId, capability.origin);
95
- }
96
- const runner = await createSurfaceRunner(options.configPath);
97
- try {
98
- const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;
99
- const reachedIds = /* @__PURE__ */ new Set();
100
- for (const scenario of scenarios) {
101
- const result = await runner.collect({
102
- scenario,
103
- ...options.scope ? { scope: options.scope } : {}
104
- });
105
- for (const capability of result.explanation.capabilities) {
106
- reachedIds.add(capability.capabilityId);
107
- }
108
- }
109
- const dir = baselineDirFor(
110
- options.configPath,
111
- options.baselineDir ?? runner.config.baselineDir
112
- );
113
- const allowlistPath = allowlistPathFor(dir);
114
- const report = buildCoverageReport({
115
- authored,
116
- origins,
117
- reachedIds,
118
- scenarios,
119
- unresolved: unresolved(inventory),
120
- allowlist: readAllowlist(allowlistPath),
121
- allowlistPath
122
- });
123
- if (options.json) write(JSON.stringify(report, null, 2));
124
- else write(renderCoveragePlain(report));
125
- return coverageExitCode(report);
126
- } finally {
127
- await runner.close();
128
- }
129
- }
130
- export {
131
- runCoverage
132
- };
133
- //# sourceMappingURL=coverage-HCHLJTDD.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/coverage.ts","../src/coverage.ts"],"sourcesContent":["import { dirname } from \"node:path\";\nimport { createSurfaceRunner } from \"../load.js\";\nimport { baselineDirFor } from \"../baseline.js\";\nimport { authoredIds, extractCapabilities, unresolved } from \"../extract.js\";\nimport {\n allowlistPathFor,\n buildCoverageReport,\n coverageExitCode,\n readAllowlist,\n} from \"../coverage.js\";\nimport { renderCoveragePlain } from \"../render/plain.js\";\nimport { write } from \"../output.js\";\n\nexport interface CoverageOptions {\n configPath: string;\n scenario?: string;\n scope?: string[];\n tsconfig?: string;\n baselineDir?: string;\n json?: boolean;\n plain?: boolean;\n}\n\n/**\n * Joins the two halves: the static inventory (what is authored) against the\n * union of every scenario's explanation (what is reached).\n *\n * The join key is `capabilityId`, which is instance-independent by\n * construction — `instanceId` is not part of it — so two mounted instances of\n * one component collapse onto the one authored entry, which is what a coverage\n * question means.\n */\nexport async function runCoverage(options: CoverageOptions): Promise<number> {\n const root = dirname(options.configPath);\n const inventory = extractCapabilities({\n root,\n ...(options.tsconfig ? { tsconfig: options.tsconfig } : {}),\n });\n\n const authored = authoredIds(inventory);\n const origins = new Map<string, { file: string; line: number }>();\n for (const capability of inventory.capabilities) {\n if (!origins.has(capability.capabilityId)) origins.set(capability.capabilityId, capability.origin);\n }\n\n const runner = await createSurfaceRunner(options.configPath);\n try {\n const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;\n const reachedIds = new Set<string>();\n\n for (const scenario of scenarios) {\n const result = await runner.collect({\n scenario,\n ...(options.scope ? { scope: options.scope } : {}),\n });\n // Reached means present in the *explanation*, not the snapshot — see\n // BuildCoverageInput.reachedIds for why hiding still counts as reaching.\n for (const capability of result.explanation.capabilities) {\n reachedIds.add(capability.capabilityId);\n }\n }\n\n const dir = baselineDirFor(\n options.configPath,\n options.baselineDir ?? runner.config.baselineDir,\n );\n const allowlistPath = allowlistPathFor(dir);\n const report = buildCoverageReport({\n authored,\n origins,\n reachedIds,\n scenarios,\n unresolved: unresolved(inventory),\n allowlist: readAllowlist(allowlistPath),\n allowlistPath,\n });\n\n if (options.json) write(JSON.stringify(report, null, 2));\n else write(renderCoveragePlain(report));\n\n // AS-CLI-002's contract: 0 clean, 1 a gap, 2 usage.\n return coverageExitCode(report);\n } finally {\n await runner.close();\n }\n}\n","/**\n * `coverage` — authored minus reached (`AS-COVER-004…005`, D36).\n *\n * The inventory says what the codebase authors; the scenarios say what a mount\n * surfaces. Neither half alone answers \"which authored capability does no\n * scenario reach\", because that is a set difference no command computed.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AuthoredCapability } from \"./extract.js\";\n\nexport const ALLOWLIST_FILE = \"coverage-allow.json\";\n\n/**\n * A committed list of unreached capabilities a repository has decided not to\n * fix yet, each with a reason. Adoption has to ratchet rather than gate: a\n * codebase turning this on with 200 unreached capabilities cannot fix them in\n * one pull request, and a check that can only be adopted big-bang is a check\n * that never gets adopted.\n */\nexport type CoverageAllowlist = Record<string, string>;\n\nexport function allowlistPathFor(baselineDir: string): string {\n return join(baselineDir, ALLOWLIST_FILE);\n}\n\nexport function readAllowlist(path: string): CoverageAllowlist {\n if (!existsSync(path)) return {};\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, \"utf8\"));\n } catch (error) {\n throw new Error(\n `could not parse ${path}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new Error(`${path} must be a JSON object of { \"capabilityId\": \"reason\" }`);\n }\n const allowlist: CoverageAllowlist = {};\n for (const [id, reason] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof reason !== \"string\" || reason.trim() === \"\") {\n throw new Error(`${path}: \"${id}\" needs a non-empty reason string`);\n }\n allowlist[id] = reason;\n }\n return allowlist;\n}\n\nexport interface UnreachedCapability {\n capabilityId: string;\n origin: { file: string; line: number };\n}\n\nexport interface CoverageReport {\n /** Distinct capability ids the inventory resolved. */\n authored: number;\n /** How many of them at least one scenario surfaced. */\n reached: number;\n scenarios: string[];\n /** Authored, surfaced by no scenario, and not allowlisted — the finding. */\n unreached: UnreachedCapability[];\n /**\n * Present at runtime with no static origin: a dynamic registration, or a gap\n * in the extractor. `view:` only — see `domainReached`.\n */\n undeclared: string[];\n /**\n * `domain:` capabilities a scenario surfaced. Held apart from `undeclared`\n * because the inventory never claimed to analyze that plane: filing them as\n * \"no static origin\" would report the design's own stated boundary as a\n * defect, which is the misleading check this whole command rejects.\n */\n domainReached: string[];\n /** Carried forward from the inventory. */\n unresolved: AuthoredCapability[];\n /** Unreached, but listed in the allowlist. */\n allowed: string[];\n /** Listed in the allowlist and reached anyway — the list has rotted. */\n staleAllowlist: string[];\n allowlistPath: string;\n}\n\nexport interface BuildCoverageInput {\n authored: Set<string>;\n /** First origin seen for each authored id, for the report. */\n origins: Map<string, { file: string; line: number }>;\n /**\n * Every capability id any scenario's *explanation* held.\n *\n * The explanation, not the snapshot. A capability a policy hid **was**\n * reached: a scenario mounted it and the policy made a deliberate decision\n * about it. Classifying those as unreached would flood the report with the\n * library's own correct behaviour — in the example app the `anonymous`\n * scenario alone would contribute eleven false gaps.\n */\n reachedIds: Set<string>;\n scenarios: string[];\n unresolved: AuthoredCapability[];\n allowlist: CoverageAllowlist;\n allowlistPath: string;\n}\n\nexport function buildCoverageReport(input: BuildCoverageInput): CoverageReport {\n const unreached: UnreachedCapability[] = [];\n const allowed: string[] = [];\n\n for (const id of [...input.authored].sort()) {\n if (input.reachedIds.has(id)) continue;\n if (id in input.allowlist) {\n allowed.push(id);\n continue;\n }\n unreached.push({ capabilityId: id, origin: input.origins.get(id) ?? { file: \"?\", line: 0 } });\n }\n\n // An allowlist entry that is no longer unreached fails the command, so the\n // list shrinks and cannot silently rot — the same idiom as the baselines\n // `check` already commits.\n const staleAllowlist = Object.keys(input.allowlist)\n .filter((id) => input.reachedIds.has(id) || !input.authored.has(id))\n .sort();\n\n const unaccounted = [...input.reachedIds].filter((id) => !input.authored.has(id)).sort();\n const domainReached = unaccounted.filter((id) => id.startsWith(\"domain:\"));\n const undeclared = unaccounted.filter((id) => !id.startsWith(\"domain:\"));\n\n return {\n authored: input.authored.size,\n reached: [...input.authored].filter((id) => input.reachedIds.has(id)).length,\n scenarios: input.scenarios,\n unreached,\n undeclared,\n domainReached,\n unresolved: input.unresolved,\n allowed,\n staleAllowlist,\n allowlistPath: input.allowlistPath,\n };\n}\n\n/**\n * `0` clean, `1` a gap.\n *\n * `undeclared` deliberately does not fail (OQ-4): a dynamically registered\n * capability is legitimate, and from the outside it is indistinguishable from\n * an extractor that missed something. Failing on it would punish the honest\n * case to catch the other one. It is reported, loudly, and revisited when a\n * codebase does it deliberately.\n */\nexport function coverageExitCode(report: CoverageReport): number {\n if (report.unreached.length > 0) return 1;\n if (report.unresolved.length > 0) return 1;\n if (report.staleAllowlist.length > 0) return 1;\n return 0;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/render/ink.tsx"],"sourcesContent":["import type { ReactElement } from \"react\";\nimport { Box, Static, Text } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport type { CapabilityRow, CapabilityGroup, SurfaceView } from \"./model.js\";\nimport type { DiffEntry } from \"../baseline.js\";\nimport { formatValue } from \"../baseline.js\";\n\nconst OUTCOME = {\n expose: { mark: \"●\", color: \"green\" as const },\n disable: { mark: \"◐\", color: \"yellow\" as const },\n hide: { mark: \"○\", color: \"red\" as const },\n};\n\nexport function Loading({ label }: { label: string }): ReactElement {\n return (\n <Text>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n {` ${label}`}\n </Text>\n );\n}\n\nfunction PolicyLine({\n policy,\n}: {\n policy: NonNullable<CapabilityRow[\"policies\"]>[number];\n}): ReactElement {\n const vote = policy.discovery?.decision;\n const color = vote === \"hide\" ? \"red\" : vote === \"disable\" ? \"yellow\" : \"green\";\n return (\n <Box paddingLeft={6}>\n <Text dimColor>policy </Text>\n <Text bold>{policy.name}</Text>\n <Text dimColor>{` (${policy.scope}${policy.phases.length ? `, ${policy.phases.join(\"/\")}` : \"\"}) `}</Text>\n {vote ? (\n <Text color={color}>\n {vote}\n {policy.discovery?.decision === \"disable\" ? ` — ${policy.discovery.reason}` : \"\"}\n </Text>\n ) : (\n <Text dimColor>no discovery hook</Text>\n )}\n {policy.threw ? <Text color=\"red\" bold>{\" THREW\"}</Text> : null}\n {policy.confirmationEscalation ? (\n <Text color=\"magenta\">{\" escalates-confirmation\"}</Text>\n ) : null}\n </Box>\n );\n}\n\nfunction Capability({ row }: { row: CapabilityRow }): ReactElement {\n const outcome = OUTCOME[row.outcome];\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text color={outcome.color}>{` ${outcome.mark} `}</Text>\n <Text bold>{row.name}</Text>\n {row.tags.length > 0 ? <Text dimColor>{` ${row.tags.join(\" · \")}`}</Text> : null}\n </Box>\n <Box paddingLeft={4}>\n <Text dimColor wrap=\"wrap\">\n {row.description}\n </Text>\n </Box>\n {row.reason ? (\n <Box paddingLeft={4}>\n <Text color=\"yellow\" wrap=\"wrap\">{`⤷ ${row.reason}`}</Text>\n </Box>\n ) : null}\n {row.policies\n ? row.policies.length > 0\n ? row.policies.map((policy, index) => (\n <PolicyLine key={`${policy.name}-${index}`} policy={policy} />\n ))\n : [\n <Box key=\"none\" paddingLeft={6}>\n <Text dimColor>policies: none</Text>\n </Box>,\n ]\n : null}\n {row.policies && row.availability && !row.availability.available ? (\n <Box paddingLeft={6}>\n <Text dimColor>{`availability: unavailable${\n row.availability.reason ? ` — ${row.availability.reason}` : \"\"\n }`}</Text>\n </Box>\n ) : null}\n {row.schemas?.input !== undefined ? (\n <Box paddingLeft={6}>\n <Text dimColor wrap=\"wrap\">{`input: ${JSON.stringify(row.schemas.input)}`}</Text>\n </Box>\n ) : null}\n {row.schemas?.output !== undefined ? (\n <Box paddingLeft={6}>\n <Text dimColor wrap=\"wrap\">{`output: ${JSON.stringify(row.schemas.output)}`}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction Group({ group }: { group: CapabilityGroup }): ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text backgroundColor=\"blueBright\" color=\"black\" bold>{` ${group.heading} `}</Text>\n <Text dimColor>{` ${group.rows.length}`}</Text>\n </Box>\n {group.rows.map((row) => (\n <Capability key={`${row.capabilityId}-${row.name}`} row={row} />\n ))}\n </Box>\n );\n}\n\n/**\n * The header states everything the counts are relative to (`AS-CLI-007`): the\n * scenario, the route, and the scope when one is active — a scope filters both\n * projections, so an unqualified count reads as a claim about the whole surface.\n * `hidden` is unconditional here for the same reason it is in plain text.\n */\nfunction Header({ view }: { view: SurfaceView }): ReactElement {\n return (\n <Box>\n <Text bold>{view.scenario}</Text>\n {view.route ? <Text dimColor>{` ${view.route}`}</Text> : null}\n {view.scope && view.scope.length > 0 ? (\n <Text color=\"cyan\">{` scope ${view.scope.join(\" \")}`}</Text>\n ) : null}\n <Text dimColor>{\" · \"}</Text>\n <Text color=\"green\">{`${view.counts.callable} callable`}</Text>\n <Text dimColor>{\", \"}</Text>\n <Text color=\"yellow\">{`${view.counts.disabled} visible-disabled`}</Text>\n <Text dimColor>{\", \"}</Text>\n <Text color=\"red\">{`${view.counts.hidden} hidden`}</Text>\n {view.rejections.length > 0 ? (\n <>\n <Text dimColor>{\", \"}</Text>\n <Text color=\"magenta\">\n {`${view.rejections.length} registration${\n view.rejections.length === 1 ? \"\" : \"s\"\n } rejected`}\n </Text>\n </>\n ) : null}\n </Box>\n );\n}\n\n/**\n * Rejected registrations (`AS-CLI-006`). A dead handle leaves no trace in either\n * projection, so without this block a copy-pasted component `type` removes a\n * capability and prints nothing anywhere.\n */\nfunction Rejections({ view }: { view: SurfaceView }): ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text backgroundColor=\"magenta\" color=\"black\" bold>\n {\" rejected during mount \"}\n </Text>\n <Text dimColor>{` ${view.rejections.length}`}</Text>\n </Box>\n {view.rejections.map((rejection) => (\n <Box key={`${rejection.componentType}@${rejection.instanceId}-${rejection.reason}`}>\n <Text color=\"magenta\">{\" ! \"}</Text>\n <Text bold>{`${rejection.componentType} (${rejection.instanceId})`}</Text>\n <Text dimColor>\n {rejection.reason === \"duplicate\"\n ? \" duplicate — an earlier registration holds this key\"\n : \" guard — onRegister rejected this registration\"}\n </Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction Empty({ view }: { view: SurfaceView }): ReactElement {\n if (view.counts.hidden > 0) {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor wrap=\"wrap\">\n {`Nothing is callable here — all ${view.counts.hidden} registered capabilities were hidden by policy. `}\n The surface is empty by decision, not because nothing was annotated.\n </Text>\n {view.explained ? null : (\n <Text dimColor>Re-run with --explain to see which policy hid them.</Text>\n )}\n </Box>\n );\n }\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor wrap=\"wrap\">\n Nothing is registered for this scenario — the agent has no surface here. That is the\n default: capabilities exist only where they were explicitly annotated.\n </Text>\n {view.explained ? null : (\n <Text dimColor>Re-run with --explain to see whether a policy hid it.</Text>\n )}\n </Box>\n );\n}\n\ntype Block = { key: string; group?: CapabilityGroup };\n\nexport function Surface({ view }: { view: SurfaceView }): ReactElement {\n const populated = view.groups.filter((group) => group.rows.length > 0);\n\n // Everything goes through <Static>, header included. Ink paints static output\n // once, permanently, above the live frame — and erases the live frame on\n // unmount. A one-shot render that leaves anything outside <Static> therefore\n // prints it and then wipes it, which is exactly what happened to this header.\n const blocks: Block[] = [\n { key: \"__header\" },\n ...populated.map((group) => ({ key: group.heading, group })),\n ];\n\n return (\n <Static items={blocks}>\n {(block) =>\n block.group ? (\n <Group key={block.key} group={block.group} />\n ) : (\n <Box key={block.key} flexDirection=\"column\">\n <Header view={view} />\n {view.rejections.length > 0 ? <Rejections view={view} /> : null}\n {populated.length === 0 ? <Empty view={view} /> : null}\n </Box>\n )\n }\n </Static>\n );\n}\n\nexport function Drift({\n scenario,\n entries,\n}: {\n scenario: string;\n entries: DiffEntry[];\n}): ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text backgroundColor=\"yellow\" color=\"black\" bold>{` ${scenario} `}</Text>\n <Text dimColor>{` ${entries.length} change${entries.length === 1 ? \"\" : \"s\"}`}</Text>\n </Box>\n {entries.map((entry) => (\n <Box key={`${entry.kind}-${entry.path}`} flexDirection=\"column\" paddingLeft={2}>\n {entry.subject ? (\n <Text bold>\n {entry.subject}\n <Text dimColor>{` ${entry.path}`}</Text>\n </Text>\n ) : null}\n {entry.kind === \"added\" ? (\n <Text color=\"green\" wrap=\"wrap\">{`+ ${entry.path} ${formatValue(entry.after)}`}</Text>\n ) : entry.kind === \"removed\" ? (\n <Text color=\"red\" wrap=\"wrap\">{`- ${entry.path} ${formatValue(entry.before)}`}</Text>\n ) : (\n <>\n <Text color=\"yellow\">{`~ ${entry.path}`}</Text>\n <Text color=\"red\" wrap=\"wrap\">{` before: ${formatValue(entry.before)}`}</Text>\n <Text color=\"green\" wrap=\"wrap\">{` after: ${formatValue(entry.after)}`}</Text>\n </>\n )}\n </Box>\n ))}\n </Box>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,213 +0,0 @@
1
- import {
2
- renderSurfacePlain
3
- } from "./chunk-4AEQKM2X.js";
4
- import {
5
- createSurfaceRunner
6
- } from "./chunk-FYEXHWGG.js";
7
- import {
8
- isPlain,
9
- loadInk,
10
- paint,
11
- transient,
12
- write
13
- } from "./chunk-A27Y7ALQ.js";
14
- import "./chunk-ODUIFFPM.js";
15
-
16
- // src/render/model.ts
17
- function leafOf(capabilityId) {
18
- const withoutPlane = capabilityId.replace(/^(view|domain):/, "");
19
- const dot = withoutPlane.lastIndexOf(".");
20
- return dot === -1 ? withoutPlane : withoutPlane.slice(dot + 1);
21
- }
22
- function observationTags() {
23
- return ["observation"];
24
- }
25
- function actionTags(action) {
26
- const tags = [action.effect];
27
- if (action.idempotent) tags.push("idempotent");
28
- if (action.reversible) tags.push("reversible");
29
- if (action.confirmation !== "never") tags.push(`confirmation:${action.confirmation}`);
30
- return tags;
31
- }
32
- function procedureTags(procedure) {
33
- const tags = [procedure.effect];
34
- if (procedure.confirmation !== "never") tags.push(`confirmation:${procedure.confirmation}`);
35
- for (const field of procedure.boundFields) {
36
- tags.push(`${field.path} bound${field.locked ? "+locked" : ""}`);
37
- }
38
- return tags;
39
- }
40
- function explanationIndex(explanation) {
41
- const index = /* @__PURE__ */ new Map();
42
- for (const capability of explanation.capabilities) {
43
- index.set(`${capability.capabilityId}\0${capability.registrationId}`, capability);
44
- }
45
- return index;
46
- }
47
- function buildView(result, options = {}) {
48
- const { snapshot, explanation } = result;
49
- const index = explanationIndex(explanation);
50
- const groups = [];
51
- const counts = { callable: 0, disabled: 0, hidden: 0 };
52
- const enrich = (row, capabilityId, registrationId) => {
53
- const explained = index.get(`${capabilityId}\0${registrationId}`);
54
- if (options.explain && explained) {
55
- row.policies = explained.policies;
56
- row.availability = explained.availability;
57
- }
58
- return row;
59
- };
60
- for (const component of snapshot.components) {
61
- const rows = [];
62
- for (const observation of component.observations) {
63
- rows.push(
64
- enrich(
65
- rowFor(observation, "observation", observationTags(), options, {
66
- input: void 0,
67
- output: observation.outputSchema
68
- }),
69
- observation.capabilityId,
70
- component.registrationId
71
- )
72
- );
73
- }
74
- for (const action of component.actions) {
75
- rows.push(
76
- enrich(
77
- rowFor(action, "action", actionTags(action), options, {
78
- input: action.inputSchema,
79
- output: action.outputSchema
80
- }),
81
- action.capabilityId,
82
- component.registrationId
83
- )
84
- );
85
- }
86
- groups.push({
87
- heading: component.instanceId === "default" ? component.type : `${component.type}@${component.instanceId}`,
88
- rows
89
- });
90
- }
91
- if (snapshot.procedures.length > 0) {
92
- groups.push({
93
- heading: "authoritative (domain)",
94
- rows: snapshot.procedures.map(
95
- (procedure) => enrich(
96
- {
97
- capabilityId: procedure.procedureId,
98
- name: procedure.procedureId.replace(/^domain:/, ""),
99
- kind: "procedure",
100
- plane: "domain",
101
- outcome: procedure.available ? "expose" : "disable",
102
- description: procedure.description,
103
- ...procedure.unavailableReason ? { reason: procedure.unavailableReason } : {},
104
- tags: procedureTags(procedure),
105
- ...options.schemas ? { schemas: { input: procedure.inputSchema, output: procedure.outputSchema } } : {}
106
- },
107
- procedure.procedureId,
108
- procedure.registrationId
109
- )
110
- )
111
- });
112
- }
113
- if (options.explain) {
114
- const hidden = explanation.capabilities.filter((c) => c.outcome === "hide");
115
- if (hidden.length > 0) {
116
- groups.push({
117
- heading: "hidden by policy (absent from the snapshot)",
118
- rows: hidden.map((capability) => ({
119
- capabilityId: capability.capabilityId,
120
- name: leafOf(capability.capabilityId),
121
- kind: capability.kind,
122
- plane: capability.plane,
123
- outcome: "hide",
124
- description: capability.description,
125
- tags: [`${capability.component.type}@${capability.component.instanceId}`],
126
- policies: capability.policies,
127
- availability: capability.availability
128
- }))
129
- });
130
- }
131
- }
132
- for (const capability of explanation.capabilities) {
133
- if (capability.outcome === "expose") counts.callable += 1;
134
- else if (capability.outcome === "disable") counts.disabled += 1;
135
- else counts.hidden += 1;
136
- }
137
- return {
138
- scenario: result.scenario,
139
- ...snapshot.route?.path ? { route: snapshot.route.path } : {},
140
- ...result.scope ? { scope: result.scope } : {},
141
- groups,
142
- counts,
143
- rejections: result.rejections ?? [],
144
- explained: options.explain === true
145
- };
146
- }
147
- function rowFor(descriptor, kind, tags, options, schemas) {
148
- return {
149
- capabilityId: descriptor.capabilityId,
150
- name: descriptor.name,
151
- kind,
152
- plane: "view",
153
- outcome: descriptor.available ? "expose" : "disable",
154
- description: descriptor.description,
155
- ...descriptor.unavailableReason ? { reason: descriptor.unavailableReason } : {},
156
- tags,
157
- ...options.schemas ? { schemas } : {}
158
- };
159
- }
160
-
161
- // src/commands/inspect.tsx
162
- import { jsx } from "react/jsx-runtime";
163
- function jsonFor(result, explain) {
164
- return {
165
- scenario: result.scenario,
166
- ...result.scope ? { scope: result.scope } : {},
167
- snapshot: result.snapshot,
168
- // Unconditional, and unconditionally present even when empty (`AS-CLI-006`):
169
- // a consumer that has to distinguish "no rejections" from "this CLI predates
170
- // the field" cannot rely on an absent key.
171
- rejections: result.rejections,
172
- ...explain ? { explanation: result.explanation } : {}
173
- };
174
- }
175
- async function runInspect(options) {
176
- const runner = await createSurfaceRunner(options.configPath);
177
- const ink = isPlain(options) ? null : await loadInk();
178
- try {
179
- const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;
180
- const collected = [];
181
- for (const [index, scenario] of scenarios.entries()) {
182
- const stop = ink ? await transient(/* @__PURE__ */ jsx(ink.Loading, { label: `mounting ${scenario}\u2026` })) : void 0;
183
- let result;
184
- try {
185
- result = await runner.collect({
186
- scenario,
187
- ...options.scope ? { scope: options.scope } : {}
188
- });
189
- } finally {
190
- stop?.();
191
- }
192
- if (options.json) {
193
- collected.push(jsonFor(result, options.explain === true));
194
- continue;
195
- }
196
- const view = buildView(result, {
197
- ...options.explain ? { explain: true } : {},
198
- ...options.schemas ? { schemas: true } : {}
199
- });
200
- if (ink) await paint(/* @__PURE__ */ jsx(ink.Surface, { view }));
201
- else write(index === 0 ? renderSurfacePlain(view) : `
202
- ${renderSurfacePlain(view)}`);
203
- }
204
- if (options.json) write(JSON.stringify({ scenarios: collected }, null, 2));
205
- return 0;
206
- } finally {
207
- await runner.close();
208
- }
209
- }
210
- export {
211
- runInspect
212
- };
213
- //# sourceMappingURL=inspect-NJNB6CAS.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/render/model.ts","../src/commands/inspect.tsx"],"sourcesContent":["import type {\n AgentActionDescriptor,\n AgentObservationDescriptor,\n AgentProcedureDescriptor,\n AgentSurfaceSnapshot,\n} from \"@agent-surface/core\";\nimport type { CapabilityExplanation, SurfaceExplanation } from \"@agent-surface/core/explain\";\nimport type { CollectResult, RegistrationRejection } from \"../collect.js\";\n\n/**\n * One view model, two renderers. The Ink UI and the plain-text fallback both\n * consume this, so `--plain` can never drift into showing something different\n * from what a TTY shows.\n */\nexport interface CapabilityRow {\n capabilityId: string;\n /** Leaf name — the group heading already carries the rest of the id. */\n name: string;\n kind: \"observation\" | \"action\" | \"procedure\";\n plane: \"view\" | \"domain\";\n outcome: \"expose\" | \"disable\" | \"hide\";\n description: string;\n reason?: string;\n tags: string[];\n policies?: CapabilityExplanation[\"policies\"];\n availability?: CapabilityExplanation[\"availability\"];\n schemas?: { input?: unknown; output?: unknown };\n}\n\nexport interface CapabilityGroup {\n heading: string;\n rows: CapabilityRow[];\n}\n\nexport interface SurfaceView {\n scenario: string;\n route?: string;\n /**\n * The scope the counts below were computed under (`AS-CLI-007`). A scope\n * filters the snapshot *and* the explanation, so without it on screen the\n * header reads as a statement about the whole surface when it is a statement\n * about one prefix of it.\n */\n scope?: string[];\n groups: CapabilityGroup[];\n counts: { callable: number; disabled: number; hidden: number };\n /** Refused during the mount — absent from both projections (`AS-CLI-006`). */\n rejections: RegistrationRejection[];\n explained: boolean;\n}\n\nexport interface ViewOptions {\n explain?: boolean;\n schemas?: boolean;\n}\n\nfunction leafOf(capabilityId: string): string {\n const withoutPlane = capabilityId.replace(/^(view|domain):/, \"\");\n const dot = withoutPlane.lastIndexOf(\".\");\n return dot === -1 ? withoutPlane : withoutPlane.slice(dot + 1);\n}\n\nfunction observationTags(): string[] {\n return [\"observation\"];\n}\n\nfunction actionTags(action: AgentActionDescriptor): string[] {\n const tags: string[] = [action.effect];\n if (action.idempotent) tags.push(\"idempotent\");\n if (action.reversible) tags.push(\"reversible\");\n if (action.confirmation !== \"never\") tags.push(`confirmation:${action.confirmation}`);\n return tags;\n}\n\nfunction procedureTags(procedure: AgentProcedureDescriptor): string[] {\n const tags: string[] = [procedure.effect];\n if (procedure.confirmation !== \"never\") tags.push(`confirmation:${procedure.confirmation}`);\n for (const field of procedure.boundFields) {\n tags.push(`${field.path} bound${field.locked ? \"+locked\" : \"\"}`);\n }\n return tags;\n}\n\nfunction explanationIndex(explanation: SurfaceExplanation): Map<string, CapabilityExplanation> {\n const index = new Map<string, CapabilityExplanation>();\n for (const capability of explanation.capabilities) {\n // Keyed by id + registration so two instances of one component stay apart.\n index.set(`${capability.capabilityId}\\u0000${capability.registrationId}`, capability);\n }\n return index;\n}\n\nexport function buildView(result: CollectResult, options: ViewOptions = {}): SurfaceView {\n const { snapshot, explanation } = result;\n const index = explanationIndex(explanation);\n const groups: CapabilityGroup[] = [];\n const counts = { callable: 0, disabled: 0, hidden: 0 };\n\n const enrich = (\n row: CapabilityRow,\n capabilityId: string,\n registrationId: string,\n ): CapabilityRow => {\n const explained = index.get(`${capabilityId}\\u0000${registrationId}`);\n if (options.explain && explained) {\n row.policies = explained.policies;\n row.availability = explained.availability;\n }\n return row;\n };\n\n for (const component of snapshot.components) {\n const rows: CapabilityRow[] = [];\n\n for (const observation of component.observations) {\n rows.push(\n enrich(\n rowFor(observation, \"observation\", observationTags(), options, {\n input: undefined,\n output: observation.outputSchema,\n }),\n observation.capabilityId,\n component.registrationId,\n ),\n );\n }\n for (const action of component.actions) {\n rows.push(\n enrich(\n rowFor(action, \"action\", actionTags(action), options, {\n input: action.inputSchema,\n output: action.outputSchema,\n }),\n action.capabilityId,\n component.registrationId,\n ),\n );\n }\n\n groups.push({\n heading:\n component.instanceId === \"default\"\n ? component.type\n : `${component.type}@${component.instanceId}`,\n rows,\n });\n }\n\n if (snapshot.procedures.length > 0) {\n groups.push({\n heading: \"authoritative (domain)\",\n rows: snapshot.procedures.map((procedure) =>\n enrich(\n {\n capabilityId: procedure.procedureId,\n name: procedure.procedureId.replace(/^domain:/, \"\"),\n kind: \"procedure\",\n plane: \"domain\",\n outcome: procedure.available ? \"expose\" : \"disable\",\n description: procedure.description,\n ...(procedure.unavailableReason ? { reason: procedure.unavailableReason } : {}),\n tags: procedureTags(procedure),\n ...(options.schemas\n ? { schemas: { input: procedure.inputSchema, output: procedure.outputSchema } }\n : {}),\n },\n procedure.procedureId,\n procedure.registrationId,\n ),\n ),\n });\n }\n\n // Hidden capabilities exist only in the explanation — that is the whole point\n // of it. They get their own group so nobody mistakes them for callable.\n if (options.explain) {\n const hidden = explanation.capabilities.filter((c) => c.outcome === \"hide\");\n if (hidden.length > 0) {\n groups.push({\n heading: \"hidden by policy (absent from the snapshot)\",\n rows: hidden.map((capability) => ({\n capabilityId: capability.capabilityId,\n name: leafOf(capability.capabilityId),\n kind: capability.kind,\n plane: capability.plane,\n outcome: \"hide\" as const,\n description: capability.description,\n tags: [`${capability.component.type}@${capability.component.instanceId}`],\n policies: capability.policies,\n availability: capability.availability,\n })),\n });\n }\n }\n\n for (const capability of explanation.capabilities) {\n if (capability.outcome === \"expose\") counts.callable += 1;\n else if (capability.outcome === \"disable\") counts.disabled += 1;\n else counts.hidden += 1;\n }\n\n return {\n scenario: result.scenario,\n ...(snapshot.route?.path ? { route: snapshot.route.path } : {}),\n ...(result.scope ? { scope: result.scope } : {}),\n groups,\n counts,\n rejections: result.rejections ?? [],\n explained: options.explain === true,\n };\n}\n\nfunction rowFor(\n descriptor: AgentObservationDescriptor | AgentActionDescriptor,\n kind: \"observation\" | \"action\",\n tags: string[],\n options: ViewOptions,\n schemas: { input?: unknown; output?: unknown },\n): CapabilityRow {\n return {\n capabilityId: descriptor.capabilityId,\n name: descriptor.name,\n kind,\n plane: \"view\",\n outcome: descriptor.available ? \"expose\" : \"disable\",\n description: descriptor.description,\n ...(descriptor.unavailableReason ? { reason: descriptor.unavailableReason } : {}),\n tags,\n ...(options.schemas ? { schemas } : {}),\n };\n}\n","import { createSurfaceRunner } from \"../load.js\";\nimport { buildView } from \"../render/model.js\";\nimport { renderSurfacePlain } from \"../render/plain.js\";\nimport { isPlain, loadInk, paint, transient, write } from \"../output.js\";\nimport type { CollectResult } from \"../collect.js\";\n\nexport interface InspectOptions {\n configPath: string;\n scenario?: string;\n scope?: string[];\n explain?: boolean;\n schemas?: boolean;\n json?: boolean;\n plain?: boolean;\n}\n\nfunction jsonFor(result: CollectResult, explain: boolean): Record<string, unknown> {\n return {\n scenario: result.scenario,\n ...(result.scope ? { scope: result.scope } : {}),\n snapshot: result.snapshot,\n // Unconditional, and unconditionally present even when empty (`AS-CLI-006`):\n // a consumer that has to distinguish \"no rejections\" from \"this CLI predates\n // the field\" cannot rely on an absent key.\n rejections: result.rejections,\n ...(explain ? { explanation: result.explanation } : {}),\n };\n}\n\n/**\n * Renders the live surface. A bare `inspect` covers every scenario the config\n * defines, the same way bare `snapshot` and `check` do — a config lists the\n * contexts worth looking at, and picking one of them by `Object.keys` order\n * made the default silently depend on the order they happened to be written in.\n */\nexport async function runInspect(options: InspectOptions): Promise<number> {\n const runner = await createSurfaceRunner(options.configPath);\n // `null` when Ink cannot run here (React 18 host), which is a fallback to\n // plain text rather than a failed command — see loadInk().\n const ink = isPlain(options) ? null : await loadInk();\n try {\n const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;\n const collected: Array<Record<string, unknown>> = [];\n\n for (const [index, scenario] of scenarios.entries()) {\n const stop = ink\n ? await transient(<ink.Loading label={`mounting ${scenario}…`} />)\n : undefined;\n\n let result;\n try {\n result = await runner.collect({\n scenario,\n ...(options.scope ? { scope: options.scope } : {}),\n });\n } finally {\n stop?.();\n }\n\n // Each scenario is mounted and rendered before the next one is mounted,\n // so a slow config prints as it goes instead of after the last mount.\n // `--json` is the exception: one document, so it has to be complete.\n if (options.json) {\n collected.push(jsonFor(result, options.explain === true));\n continue;\n }\n\n const view = buildView(result, {\n ...(options.explain ? { explain: true } : {}),\n ...(options.schemas ? { schemas: true } : {}),\n });\n\n if (ink) await paint(<ink.Surface view={view} />);\n else write(index === 0 ? renderSurfacePlain(view) : `\\n${renderSurfacePlain(view)}`);\n }\n\n if (options.json) write(JSON.stringify({ scenarios: collected }, null, 2));\n return 0;\n } finally {\n await runner.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAwDA,SAAS,OAAO,cAA8B;AAC5C,QAAM,eAAe,aAAa,QAAQ,mBAAmB,EAAE;AAC/D,QAAM,MAAM,aAAa,YAAY,GAAG;AACxC,SAAO,QAAQ,KAAK,eAAe,aAAa,MAAM,MAAM,CAAC;AAC/D;AAEA,SAAS,kBAA4B;AACnC,SAAO,CAAC,aAAa;AACvB;AAEA,SAAS,WAAW,QAAyC;AAC3D,QAAM,OAAiB,CAAC,OAAO,MAAM;AACrC,MAAI,OAAO,WAAY,MAAK,KAAK,YAAY;AAC7C,MAAI,OAAO,WAAY,MAAK,KAAK,YAAY;AAC7C,MAAI,OAAO,iBAAiB,QAAS,MAAK,KAAK,gBAAgB,OAAO,YAAY,EAAE;AACpF,SAAO;AACT;AAEA,SAAS,cAAc,WAA+C;AACpE,QAAM,OAAiB,CAAC,UAAU,MAAM;AACxC,MAAI,UAAU,iBAAiB,QAAS,MAAK,KAAK,gBAAgB,UAAU,YAAY,EAAE;AAC1F,aAAW,SAAS,UAAU,aAAa;AACzC,SAAK,KAAK,GAAG,MAAM,IAAI,SAAS,MAAM,SAAS,YAAY,EAAE,EAAE;AAAA,EACjE;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,aAAqE;AAC7F,QAAM,QAAQ,oBAAI,IAAmC;AACrD,aAAW,cAAc,YAAY,cAAc;AAEjD,UAAM,IAAI,GAAG,WAAW,YAAY,KAAS,WAAW,cAAc,IAAI,UAAU;AAAA,EACtF;AACA,SAAO;AACT;AAEO,SAAS,UAAU,QAAuB,UAAuB,CAAC,GAAgB;AACvF,QAAM,EAAE,UAAU,YAAY,IAAI;AAClC,QAAM,QAAQ,iBAAiB,WAAW;AAC1C,QAAM,SAA4B,CAAC;AACnC,QAAM,SAAS,EAAE,UAAU,GAAG,UAAU,GAAG,QAAQ,EAAE;AAErD,QAAM,SAAS,CACb,KACA,cACA,mBACkB;AAClB,UAAM,YAAY,MAAM,IAAI,GAAG,YAAY,KAAS,cAAc,EAAE;AACpE,QAAI,QAAQ,WAAW,WAAW;AAChC,UAAI,WAAW,UAAU;AACzB,UAAI,eAAe,UAAU;AAAA,IAC/B;AACA,WAAO;AAAA,EACT;AAEA,aAAW,aAAa,SAAS,YAAY;AAC3C,UAAM,OAAwB,CAAC;AAE/B,eAAW,eAAe,UAAU,cAAc;AAChD,WAAK;AAAA,QACH;AAAA,UACE,OAAO,aAAa,eAAe,gBAAgB,GAAG,SAAS;AAAA,YAC7D,OAAO;AAAA,YACP,QAAQ,YAAY;AAAA,UACtB,CAAC;AAAA,UACD,YAAY;AAAA,UACZ,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AACA,eAAW,UAAU,UAAU,SAAS;AACtC,WAAK;AAAA,QACH;AAAA,UACE,OAAO,QAAQ,UAAU,WAAW,MAAM,GAAG,SAAS;AAAA,YACpD,OAAO,OAAO;AAAA,YACd,QAAQ,OAAO;AAAA,UACjB,CAAC;AAAA,UACD,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,MACV,SACE,UAAU,eAAe,YACrB,UAAU,OACV,GAAG,UAAU,IAAI,IAAI,UAAU,UAAU;AAAA,MAC/C;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,WAAW,SAAS,GAAG;AAClC,WAAO,KAAK;AAAA,MACV,SAAS;AAAA,MACT,MAAM,SAAS,WAAW;AAAA,QAAI,CAAC,cAC7B;AAAA,UACE;AAAA,YACE,cAAc,UAAU;AAAA,YACxB,MAAM,UAAU,YAAY,QAAQ,YAAY,EAAE;AAAA,YAClD,MAAM;AAAA,YACN,OAAO;AAAA,YACP,SAAS,UAAU,YAAY,WAAW;AAAA,YAC1C,aAAa,UAAU;AAAA,YACvB,GAAI,UAAU,oBAAoB,EAAE,QAAQ,UAAU,kBAAkB,IAAI,CAAC;AAAA,YAC7E,MAAM,cAAc,SAAS;AAAA,YAC7B,GAAI,QAAQ,UACR,EAAE,SAAS,EAAE,OAAO,UAAU,aAAa,QAAQ,UAAU,aAAa,EAAE,IAC5E,CAAC;AAAA,UACP;AAAA,UACA,UAAU;AAAA,UACV,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAIA,MAAI,QAAQ,SAAS;AACnB,UAAM,SAAS,YAAY,aAAa,OAAO,CAAC,MAAM,EAAE,YAAY,MAAM;AAC1E,QAAI,OAAO,SAAS,GAAG;AACrB,aAAO,KAAK;AAAA,QACV,SAAS;AAAA,QACT,MAAM,OAAO,IAAI,CAAC,gBAAgB;AAAA,UAChC,cAAc,WAAW;AAAA,UACzB,MAAM,OAAO,WAAW,YAAY;AAAA,UACpC,MAAM,WAAW;AAAA,UACjB,OAAO,WAAW;AAAA,UAClB,SAAS;AAAA,UACT,aAAa,WAAW;AAAA,UACxB,MAAM,CAAC,GAAG,WAAW,UAAU,IAAI,IAAI,WAAW,UAAU,UAAU,EAAE;AAAA,UACxE,UAAU,WAAW;AAAA,UACrB,cAAc,WAAW;AAAA,QAC3B,EAAE;AAAA,MACJ,CAAC;AAAA,IACH;AAAA,EACF;AAEA,aAAW,cAAc,YAAY,cAAc;AACjD,QAAI,WAAW,YAAY,SAAU,QAAO,YAAY;AAAA,aAC/C,WAAW,YAAY,UAAW,QAAO,YAAY;AAAA,QACzD,QAAO,UAAU;AAAA,EACxB;AAEA,SAAO;AAAA,IACL,UAAU,OAAO;AAAA,IACjB,GAAI,SAAS,OAAO,OAAO,EAAE,OAAO,SAAS,MAAM,KAAK,IAAI,CAAC;AAAA,IAC7D,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,IAC9C;AAAA,IACA;AAAA,IACA,YAAY,OAAO,cAAc,CAAC;AAAA,IAClC,WAAW,QAAQ,YAAY;AAAA,EACjC;AACF;AAEA,SAAS,OACP,YACA,MACA,MACA,SACA,SACe;AACf,SAAO;AAAA,IACL,cAAc,WAAW;AAAA,IACzB,MAAM,WAAW;AAAA,IACjB;AAAA,IACA,OAAO;AAAA,IACP,SAAS,WAAW,YAAY,WAAW;AAAA,IAC3C,aAAa,WAAW;AAAA,IACxB,GAAI,WAAW,oBAAoB,EAAE,QAAQ,WAAW,kBAAkB,IAAI,CAAC;AAAA,IAC/E;AAAA,IACA,GAAI,QAAQ,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvC;AACF;;;ACxL0B;AA9B1B,SAAS,QAAQ,QAAuB,SAA2C;AACjF,SAAO;AAAA,IACL,UAAU,OAAO;AAAA,IACjB,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,IAC9C,UAAU,OAAO;AAAA;AAAA;AAAA;AAAA,IAIjB,YAAY,OAAO;AAAA,IACnB,GAAI,UAAU,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;AAAA,EACvD;AACF;AAQA,eAAsB,WAAW,SAA0C;AACzE,QAAM,SAAS,MAAM,oBAAoB,QAAQ,UAAU;AAG3D,QAAM,MAAM,QAAQ,OAAO,IAAI,OAAO,MAAM,QAAQ;AACpD,MAAI;AACF,UAAM,YAAY,QAAQ,WAAW,CAAC,QAAQ,QAAQ,IAAI,OAAO;AACjE,UAAM,YAA4C,CAAC;AAEnD,eAAW,CAAC,OAAO,QAAQ,KAAK,UAAU,QAAQ,GAAG;AACnD,YAAM,OAAO,MACT,MAAM,UAAU,oBAAC,IAAI,SAAJ,EAAY,OAAO,YAAY,QAAQ,UAAK,CAAE,IAC/D;AAEJ,UAAI;AACJ,UAAI;AACF,iBAAS,MAAM,OAAO,QAAQ;AAAA,UAC5B;AAAA,UACA,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,QAClD,CAAC;AAAA,MACH,UAAE;AACA,eAAO;AAAA,MACT;AAKA,UAAI,QAAQ,MAAM;AAChB,kBAAU,KAAK,QAAQ,QAAQ,QAAQ,YAAY,IAAI,CAAC;AACxD;AAAA,MACF;AAEA,YAAM,OAAO,UAAU,QAAQ;AAAA,QAC7B,GAAI,QAAQ,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,QAC3C,GAAI,QAAQ,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,MAC7C,CAAC;AAED,UAAI,IAAK,OAAM,MAAM,oBAAC,IAAI,SAAJ,EAAY,MAAY,CAAE;AAAA,UAC3C,OAAM,UAAU,IAAI,mBAAmB,IAAI,IAAI;AAAA,EAAK,mBAAmB,IAAI,CAAC,EAAE;AAAA,IACrF;AAEA,QAAI,QAAQ,KAAM,OAAM,KAAK,UAAU,EAAE,WAAW,UAAU,GAAG,MAAM,CAAC,CAAC;AACzE,WAAO;AAAA,EACT,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;","names":[]}
@@ -1,38 +0,0 @@
1
- import {
2
- createSurfaceRunner
3
- } from "./chunk-FYEXHWGG.js";
4
- import {
5
- write
6
- } from "./chunk-A27Y7ALQ.js";
7
- import {
8
- baselineDirFor,
9
- baselinePath,
10
- normalize,
11
- writeBaseline
12
- } from "./chunk-ODUIFFPM.js";
13
-
14
- // src/commands/snapshot.ts
15
- import { relative } from "path";
16
- async function runSnapshot(options) {
17
- const runner = await createSurfaceRunner(options.configPath);
18
- try {
19
- const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;
20
- const dir = baselineDirFor(options.configPath, options.baselineDir ?? runner.config.baselineDir);
21
- for (const scenario of scenarios) {
22
- const result = await runner.collect({
23
- scenario,
24
- ...options.scope ? { scope: options.scope } : {}
25
- });
26
- const path = baselinePath(dir, scenario);
27
- writeBaseline(path, normalize(result.snapshot));
28
- write(`wrote ${relative(process.cwd(), path)}`);
29
- }
30
- return 0;
31
- } finally {
32
- await runner.close();
33
- }
34
- }
35
- export {
36
- runSnapshot
37
- };
38
- //# sourceMappingURL=snapshot-JQAB73OV.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/snapshot.ts"],"sourcesContent":["import { relative } from \"node:path\";\nimport { createSurfaceRunner } from \"../load.js\";\nimport { baselineDirFor, baselinePath, normalize, writeBaseline } from \"../baseline.js\";\nimport { write } from \"../output.js\";\n\nexport interface SnapshotOptions {\n configPath: string;\n scenario?: string;\n scope?: string[];\n baselineDir?: string;\n}\n\n/** Writes (or refreshes) the committed baseline `check` compares against. */\nexport async function runSnapshot(options: SnapshotOptions): Promise<number> {\n const runner = await createSurfaceRunner(options.configPath);\n try {\n const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;\n const dir = baselineDirFor(options.configPath, options.baselineDir ?? runner.config.baselineDir);\n\n for (const scenario of scenarios) {\n const result = await runner.collect({\n scenario,\n ...(options.scope ? { scope: options.scope } : {}),\n });\n const path = baselinePath(dir, scenario);\n writeBaseline(path, normalize(result.snapshot));\n write(`wrote ${relative(process.cwd(), path)}`);\n }\n return 0;\n } finally {\n await runner.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AAazB,eAAsB,YAAY,SAA2C;AAC3E,QAAM,SAAS,MAAM,oBAAoB,QAAQ,UAAU;AAC3D,MAAI;AACF,UAAM,YAAY,QAAQ,WAAW,CAAC,QAAQ,QAAQ,IAAI,OAAO;AACjE,UAAM,MAAM,eAAe,QAAQ,YAAY,QAAQ,eAAe,OAAO,OAAO,WAAW;AAE/F,eAAW,YAAY,WAAW;AAChC,YAAM,SAAS,MAAM,OAAO,QAAQ;AAAA,QAClC;AAAA,QACA,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,MAClD,CAAC;AACD,YAAM,OAAO,aAAa,KAAK,QAAQ;AACvC,oBAAc,MAAM,UAAU,OAAO,QAAQ,CAAC;AAC9C,YAAM,SAAS,SAAS,QAAQ,IAAI,GAAG,IAAI,CAAC,EAAE;AAAA,IAChD;AACA,WAAO;AAAA,EACT,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;","names":[]}