@agent-surface/cli 0.8.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 +21 -0
- package/README.md +99 -0
- package/dist/bin.js +174 -0
- package/dist/bin.js.map +1 -0
- package/dist/check-7Z2VNN5R.js +79 -0
- package/dist/check-7Z2VNN5R.js.map +1 -0
- package/dist/chunk-A2G4QLX5.js +29 -0
- package/dist/chunk-A2G4QLX5.js.map +1 -0
- package/dist/chunk-KZUR4CAU.js +84 -0
- package/dist/chunk-KZUR4CAU.js.map +1 -0
- package/dist/chunk-ODUIFFPM.js +104 -0
- package/dist/chunk-ODUIFFPM.js.map +1 -0
- package/dist/chunk-S2LM3N6D.js +167 -0
- package/dist/chunk-S2LM3N6D.js.map +1 -0
- package/dist/collect.js +35 -0
- package/dist/collect.js.map +1 -0
- package/dist/config-DqK8YqyQ.d.ts +52 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/ink-GCWDO4ML.js +122 -0
- package/dist/ink-GCWDO4ML.js.map +1 -0
- package/dist/inspect-3AXX3VVK.js +202 -0
- package/dist/inspect-3AXX3VVK.js.map +1 -0
- package/dist/snapshot-3D55FL63.js +36 -0
- package/dist/snapshot-3D55FL63.js.map +1 -0
- package/dist/vitest.d.ts +30 -0
- package/dist/vitest.js +9 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1 @@
|
|
|
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":[]}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// src/load.ts
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { dirname, isAbsolute, join, resolve } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { createServer } from "vite";
|
|
6
|
+
import { ViteNodeServer } from "vite-node/server";
|
|
7
|
+
import { ViteNodeRunner } from "vite-node/client";
|
|
8
|
+
import { installSourcemapsSupport } from "vite-node/source-map";
|
|
9
|
+
var CONFIG_NAMES = [
|
|
10
|
+
"agent-surface.config.tsx",
|
|
11
|
+
"agent-surface.config.ts",
|
|
12
|
+
"agent-surface.config.mjs",
|
|
13
|
+
"agent-surface.config.js"
|
|
14
|
+
];
|
|
15
|
+
function findConfig(from = process.cwd()) {
|
|
16
|
+
let dir = resolve(from);
|
|
17
|
+
for (; ; ) {
|
|
18
|
+
for (const name of CONFIG_NAMES) {
|
|
19
|
+
const candidate = join(dir, name);
|
|
20
|
+
if (existsSync(candidate)) return candidate;
|
|
21
|
+
}
|
|
22
|
+
const parent = dirname(dir);
|
|
23
|
+
if (parent === dir) return void 0;
|
|
24
|
+
dir = parent;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function collectorPath() {
|
|
28
|
+
for (const ext of ["js", "ts"]) {
|
|
29
|
+
const candidate = fileURLToPath(new URL(`./collect.${ext}`, import.meta.url));
|
|
30
|
+
if (existsSync(candidate)) return candidate;
|
|
31
|
+
}
|
|
32
|
+
throw new Error("could not locate the agent-surface collector module");
|
|
33
|
+
}
|
|
34
|
+
async function createSurfaceRunner(configPath) {
|
|
35
|
+
const absoluteConfig = isAbsolute(configPath) ? configPath : resolve(configPath);
|
|
36
|
+
if (!existsSync(absoluteConfig)) {
|
|
37
|
+
throw new Error(`config not found: ${absoluteConfig}`);
|
|
38
|
+
}
|
|
39
|
+
const root = dirname(absoluteConfig);
|
|
40
|
+
let server;
|
|
41
|
+
try {
|
|
42
|
+
server = await createServer({
|
|
43
|
+
root,
|
|
44
|
+
logLevel: "error",
|
|
45
|
+
// `serve` so plugins behave as they do in dev; nothing is ever served.
|
|
46
|
+
server: { middlewareMode: true, watch: null, fs: { strict: false } },
|
|
47
|
+
optimizeDeps: { noDiscovery: true, include: [] },
|
|
48
|
+
resolve: {
|
|
49
|
+
// Both halves of the graph must agree on these. React because two
|
|
50
|
+
// copies break hooks; core because `explainSurface` finds the registry
|
|
51
|
+
// through a Symbol, which is per-module-instance (see collect.ts).
|
|
52
|
+
dedupe: [
|
|
53
|
+
"react",
|
|
54
|
+
"react-dom",
|
|
55
|
+
"@agent-surface/core",
|
|
56
|
+
"@agent-surface/react",
|
|
57
|
+
"@agent-surface/testing"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
} catch (error) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`could not start Vite for ${root}: ${error instanceof Error ? error.message : String(error)}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
await server.pluginContainer.buildStart({});
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
const nodeServer = new ViteNodeServer(server);
|
|
71
|
+
installSourcemapsSupport({ getSourceMap: (source) => nodeServer.getSourceMap(source) });
|
|
72
|
+
const runner = new ViteNodeRunner({
|
|
73
|
+
root: server.config.root,
|
|
74
|
+
base: server.config.base,
|
|
75
|
+
fetchModule: (id) => nodeServer.fetchModule(id),
|
|
76
|
+
resolveId: (id, importer) => nodeServer.resolveId(id, importer)
|
|
77
|
+
});
|
|
78
|
+
const close = async () => {
|
|
79
|
+
await server.close();
|
|
80
|
+
};
|
|
81
|
+
try {
|
|
82
|
+
const configModule = await runner.executeFile(absoluteConfig);
|
|
83
|
+
const config = configModule.default;
|
|
84
|
+
if (!config || typeof config.mount !== "function") {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`${absoluteConfig} must \`export default defineSurface({ mount, scenarios })\``
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
const scenarioNames = Object.keys(config.scenarios ?? {});
|
|
90
|
+
if (scenarioNames.length === 0) {
|
|
91
|
+
throw new Error(`${absoluteConfig} defines no scenarios`);
|
|
92
|
+
}
|
|
93
|
+
const collector = await runner.executeFile(collectorPath());
|
|
94
|
+
return {
|
|
95
|
+
config,
|
|
96
|
+
scenarioNames,
|
|
97
|
+
collect: async (options) => {
|
|
98
|
+
const globals = globalThis;
|
|
99
|
+
const previous = globals["IS_REACT_ACT_ENVIRONMENT"];
|
|
100
|
+
globals["IS_REACT_ACT_ENVIRONMENT"] = true;
|
|
101
|
+
try {
|
|
102
|
+
return await collector.collect(config, options);
|
|
103
|
+
} finally {
|
|
104
|
+
globals["IS_REACT_ACT_ENVIRONMENT"] = previous;
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
close
|
|
108
|
+
};
|
|
109
|
+
} catch (error) {
|
|
110
|
+
await close();
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/output.ts
|
|
116
|
+
function isPlain(flags) {
|
|
117
|
+
if (flags.json) return true;
|
|
118
|
+
if (flags.plain) return true;
|
|
119
|
+
if (process.env["CI"]) return true;
|
|
120
|
+
if (process.env["NO_COLOR"]) return true;
|
|
121
|
+
if (process.stdout.isTTY !== true) return true;
|
|
122
|
+
return !process.stdout.columns;
|
|
123
|
+
}
|
|
124
|
+
function write(text) {
|
|
125
|
+
process.stdout.write(`${text}
|
|
126
|
+
`);
|
|
127
|
+
}
|
|
128
|
+
function writeError(text) {
|
|
129
|
+
process.stderr.write(`${text}
|
|
130
|
+
`);
|
|
131
|
+
}
|
|
132
|
+
var cached;
|
|
133
|
+
async function loadInk() {
|
|
134
|
+
if (cached !== void 0) return cached;
|
|
135
|
+
try {
|
|
136
|
+
cached = await import("./ink-GCWDO4ML.js");
|
|
137
|
+
} catch {
|
|
138
|
+
cached = null;
|
|
139
|
+
}
|
|
140
|
+
return cached;
|
|
141
|
+
}
|
|
142
|
+
async function paint(element) {
|
|
143
|
+
const { render } = await import("ink");
|
|
144
|
+
const instance = render(element);
|
|
145
|
+
instance.unmount();
|
|
146
|
+
await instance.waitUntilExit();
|
|
147
|
+
}
|
|
148
|
+
async function transient(element) {
|
|
149
|
+
const { render } = await import("ink");
|
|
150
|
+
const instance = render(element);
|
|
151
|
+
return () => {
|
|
152
|
+
instance.clear();
|
|
153
|
+
instance.unmount();
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export {
|
|
158
|
+
findConfig,
|
|
159
|
+
createSurfaceRunner,
|
|
160
|
+
isPlain,
|
|
161
|
+
write,
|
|
162
|
+
writeError,
|
|
163
|
+
loadInk,
|
|
164
|
+
paint,
|
|
165
|
+
transient
|
|
166
|
+
};
|
|
167
|
+
//# sourceMappingURL=chunk-S2LM3N6D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/load.ts","../src/output.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","import type { ReactElement } from \"react\";\n\nexport interface OutputFlags {\n plain?: boolean;\n json?: boolean;\n}\n\n/**\n * Terminal-aware only when there is a terminal. Piped output, `--plain`, `CI`\n * and `NO_COLOR` all fall back to plain text — a CLI whose output changes shape\n * when redirected is unusable in a build log.\n */\nexport function isPlain(flags: OutputFlags): boolean {\n if (flags.json) return true;\n if (flags.plain) return true;\n if (process.env[\"CI\"]) return true;\n if (process.env[\"NO_COLOR\"]) return true;\n if (process.stdout.isTTY !== true) return true;\n // A TTY that cannot report its width (some CI ptys, `script` on macOS) makes\n // Ink lay out at zero columns and emit one character per line. Plain text is\n // the only honest rendering for a terminal whose size is unknown.\n return !process.stdout.columns;\n}\n\nexport function write(text: string): void {\n process.stdout.write(`${text}\\n`);\n}\n\nexport function writeError(text: string): void {\n process.stderr.write(`${text}\\n`);\n}\n\ntype InkModule = typeof import(\"./render/ink.js\");\n\nlet cached: InkModule | null | undefined;\n\n/**\n * Loads the Ink renderer, or returns `null` when it cannot run here.\n *\n * Two reasons this is lazy rather than a top-level import. It keeps `--plain`\n * and `--json` from paying for a terminal UI they never draw — and Ink drives\n * React through `react-reconciler`, which reads React 19 internals, so a host\n * that pins React 18 globally cannot load it at all. Neither is a reason to\n * fail a command that was about to print text.\n */\nexport async function loadInk(): Promise<InkModule | null> {\n if (cached !== undefined) return cached;\n try {\n cached = await import(\"./render/ink.js\");\n } catch {\n cached = null;\n }\n return cached;\n}\n\n/** Paints an Ink element once and returns when the frame has been flushed. */\nexport async function paint(element: ReactElement): Promise<void> {\n const { render } = await import(\"ink\");\n const instance = render(element);\n instance.unmount();\n await instance.waitUntilExit();\n}\n\n/** A live Ink frame (spinner) that is cleared before the real output lands. */\nexport async function transient(element: ReactElement): Promise<() => void> {\n const { render } = await import(\"ink\");\n const instance = render(element);\n return () => {\n instance.clear();\n instance.unmount();\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/collect.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mountScenario
|
|
3
|
+
} from "./chunk-A2G4QLX5.js";
|
|
4
|
+
|
|
5
|
+
// src/collect.ts
|
|
6
|
+
import { explainSurface } from "@agent-surface/core/explain";
|
|
7
|
+
async function collect(config, options) {
|
|
8
|
+
const mount = await mountScenario(config, options.scenario, {
|
|
9
|
+
...options.consumer ? { consumer: options.consumer } : {}
|
|
10
|
+
});
|
|
11
|
+
const scope = options.scope ?? config.scope;
|
|
12
|
+
const ctx = {
|
|
13
|
+
consumer: mount.consumer,
|
|
14
|
+
includeUnavailable: true,
|
|
15
|
+
...scope ? { scope } : {}
|
|
16
|
+
};
|
|
17
|
+
try {
|
|
18
|
+
return {
|
|
19
|
+
scenario: options.scenario,
|
|
20
|
+
// Inert copies: the live objects are frozen and graph-local, and only
|
|
21
|
+
// plain JSON may cross back into the CLI process.
|
|
22
|
+
snapshot: jsonify(mount.mounted.registry.snapshot(ctx)),
|
|
23
|
+
explanation: jsonify(explainSurface(mount.mounted.registry, ctx))
|
|
24
|
+
};
|
|
25
|
+
} finally {
|
|
26
|
+
mount.surface.dispose();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function jsonify(value) {
|
|
30
|
+
return JSON.parse(JSON.stringify(value));
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
collect
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=collect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/collect.ts"],"sourcesContent":["/**\n * The collector — the *only* module the CLI executes inside the vite-node\n * graph, and the reason that boundary exists.\n *\n * Two things force it:\n *\n * 1. **One React.** The app's component tree resolves React through the app's\n * own Vite config. If the mount ran in the CLI's Node graph instead, a\n * second React copy would render it and every hook would throw.\n *\n * 2. **One `@agent-surface/core`.** `explainSurface()` reaches the registry\n * through a plain `Symbol` seam, and a symbol is only equal to itself within\n * one module instance. Load core twice and the seam silently misses. So the\n * explanation is computed *here*, beside the registry that owns it.\n *\n * Everything crosses back as plain JSON. Nothing live — no registry, no React\n * element, no policy function — escapes into the CLI process.\n */\nimport { explainSurface, type SurfaceExplanation } from \"@agent-surface/core/explain\";\nimport type { AgentConsumer, AgentSurfaceSnapshot, SnapshotContext } from \"@agent-surface/core\";\nimport type { SurfaceConfig } from \"./config.js\";\nimport { mountScenario } from \"./mount.js\";\n\nexport interface CollectOptions {\n scenario: string;\n consumer?: AgentConsumer;\n scope?: string[];\n}\n\nexport interface CollectResult {\n scenario: string;\n snapshot: AgentSurfaceSnapshot;\n explanation: SurfaceExplanation;\n}\n\nexport async function collect(\n config: SurfaceConfig,\n options: CollectOptions,\n): Promise<CollectResult> {\n const mount = await mountScenario(config, options.scenario, {\n ...(options.consumer ? { consumer: options.consumer } : {}),\n });\n const scope = options.scope ?? config.scope;\n const ctx: SnapshotContext = {\n consumer: mount.consumer,\n includeUnavailable: true,\n ...(scope ? { scope } : {}),\n };\n\n try {\n return {\n scenario: options.scenario,\n // Inert copies: the live objects are frozen and graph-local, and only\n // plain JSON may cross back into the CLI process.\n snapshot: jsonify(mount.mounted.registry.snapshot(ctx)),\n explanation: jsonify(explainSurface(mount.mounted.registry, ctx)),\n };\n } finally {\n mount.surface.dispose();\n }\n}\n\nfunction jsonify<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n"],"mappings":";;;;;AAkBA,SAAS,sBAA+C;AAiBxD,eAAsB,QACpB,QACA,SACwB;AACxB,QAAM,QAAQ,MAAM,cAAc,QAAQ,QAAQ,UAAU;AAAA,IAC1D,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,EAC3D,CAAC;AACD,QAAM,QAAQ,QAAQ,SAAS,OAAO;AACtC,QAAM,MAAuB;AAAA,IAC3B,UAAU,MAAM;AAAA,IAChB,oBAAoB;AAAA,IACpB,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,EAC3B;AAEA,MAAI;AACF,WAAO;AAAA,MACL,UAAU,QAAQ;AAAA;AAAA;AAAA,MAGlB,UAAU,QAAQ,MAAM,QAAQ,SAAS,SAAS,GAAG,CAAC;AAAA,MACtD,aAAa,QAAQ,eAAe,MAAM,QAAQ,UAAU,GAAG,CAAC;AAAA,IAClE;AAAA,EACF,UAAE;AACA,UAAM,QAAQ,QAAQ;AAAA,EACxB;AACF;AAEA,SAAS,QAAW,OAAa;AAC/B,SAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC;","names":[]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { AgentSurfaceRegistry, AgentConsumer } from '@agent-surface/core';
|
|
3
|
+
|
|
4
|
+
/** What `mount()` hands back: the app's own registry and its rendered tree. */
|
|
5
|
+
interface MountResult<TApp = unknown> {
|
|
6
|
+
registry: AgentSurfaceRegistry;
|
|
7
|
+
ui: ReactElement;
|
|
8
|
+
/**
|
|
9
|
+
* Anything else your tests need back — the app wiring, a backend double, a
|
|
10
|
+
* router handle. The CLI ignores it entirely; it exists so the same scenario
|
|
11
|
+
* can drive `agent-surface inspect` and a Vitest suite without the suite
|
|
12
|
+
* having to rebuild the app a second way.
|
|
13
|
+
*/
|
|
14
|
+
app?: TApp;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Scenario properties are whatever your `mount()` needs — a user, a route, a
|
|
18
|
+
* feature flag. The CLI never interprets them; it just hands them back, plus
|
|
19
|
+
* `scenario` (the key it was listed under).
|
|
20
|
+
*/
|
|
21
|
+
type ScenarioProps = Record<string, unknown>;
|
|
22
|
+
interface SurfaceConfig<TScenario extends ScenarioProps = ScenarioProps, TApp = unknown> {
|
|
23
|
+
/**
|
|
24
|
+
* Build the app the way the app builds itself. This should point at your
|
|
25
|
+
* existing composition root, not restate it — whatever `main.tsx` calls.
|
|
26
|
+
*/
|
|
27
|
+
mount(props: TScenario & {
|
|
28
|
+
scenario: string;
|
|
29
|
+
}): MountResult<TApp> | Promise<MountResult<TApp>>;
|
|
30
|
+
/**
|
|
31
|
+
* Optional extra settling after mount effects flush, for anything the first
|
|
32
|
+
* render kicks off asynchronously (an initial fetch, a router resolve).
|
|
33
|
+
* The CLI already flushes React effects and pending microtasks for you.
|
|
34
|
+
*/
|
|
35
|
+
settle?: (mounted: MountResult<TApp>) => void | Promise<void>;
|
|
36
|
+
/** Named surfaces to inspect and check. At least one is required. */
|
|
37
|
+
scenarios: Record<string, TScenario>;
|
|
38
|
+
/** Consumer identity snapshots are computed for. Default `{id:"cli",kind:"test"}`. */
|
|
39
|
+
consumer?: AgentConsumer;
|
|
40
|
+
/** Component-type prefixes to restrict to, same meaning as `SnapshotContext.scope`. */
|
|
41
|
+
scope?: string[];
|
|
42
|
+
/** Where `snapshot`/`check` keep baselines. Default `.agent-surface`, relative to the config. */
|
|
43
|
+
baselineDir?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Identity function that exists purely for type inference — the same shape as
|
|
47
|
+
* Vite's `defineConfig`. Your scenario props stay strongly typed inside
|
|
48
|
+
* `mount()` without you annotating them.
|
|
49
|
+
*/
|
|
50
|
+
declare function defineSurface<TScenario extends ScenarioProps, TApp = unknown>(config: SurfaceConfig<TScenario, TApp>): SurfaceConfig<TScenario, TApp>;
|
|
51
|
+
|
|
52
|
+
export { type MountResult as M, type ScenarioProps as S, type SurfaceConfig as a, defineSurface as d };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export { M as MountResult, S as ScenarioProps, a as SurfaceConfig, d as defineSurface } from './config-DqK8YqyQ.js';
|
|
2
|
+
import { SurfaceExplanation } from '@agent-surface/core/explain';
|
|
3
|
+
import { AgentSurfaceSnapshot } from '@agent-surface/core';
|
|
4
|
+
import 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The collector — the *only* module the CLI executes inside the vite-node
|
|
8
|
+
* graph, and the reason that boundary exists.
|
|
9
|
+
*
|
|
10
|
+
* Two things force it:
|
|
11
|
+
*
|
|
12
|
+
* 1. **One React.** The app's component tree resolves React through the app's
|
|
13
|
+
* own Vite config. If the mount ran in the CLI's Node graph instead, a
|
|
14
|
+
* second React copy would render it and every hook would throw.
|
|
15
|
+
*
|
|
16
|
+
* 2. **One `@agent-surface/core`.** `explainSurface()` reaches the registry
|
|
17
|
+
* through a plain `Symbol` seam, and a symbol is only equal to itself within
|
|
18
|
+
* one module instance. Load core twice and the seam silently misses. So the
|
|
19
|
+
* explanation is computed *here*, beside the registry that owns it.
|
|
20
|
+
*
|
|
21
|
+
* Everything crosses back as plain JSON. Nothing live — no registry, no React
|
|
22
|
+
* element, no policy function — escapes into the CLI process.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
interface CollectResult {
|
|
26
|
+
scenario: string;
|
|
27
|
+
snapshot: AgentSurfaceSnapshot;
|
|
28
|
+
explanation: SurfaceExplanation;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type { CollectResult };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import type { ReactElement } from \"react\";\nimport type { AgentConsumer, AgentSurfaceRegistry } from \"@agent-surface/core\";\n\n/** What `mount()` hands back: the app's own registry and its rendered tree. */\nexport interface MountResult<TApp = unknown> {\n registry: AgentSurfaceRegistry;\n ui: ReactElement;\n /**\n * Anything else your tests need back — the app wiring, a backend double, a\n * router handle. The CLI ignores it entirely; it exists so the same scenario\n * can drive `agent-surface inspect` and a Vitest suite without the suite\n * having to rebuild the app a second way.\n */\n app?: TApp;\n}\n\n/**\n * Scenario properties are whatever your `mount()` needs — a user, a route, a\n * feature flag. The CLI never interprets them; it just hands them back, plus\n * `scenario` (the key it was listed under).\n */\nexport type ScenarioProps = Record<string, unknown>;\n\nexport interface SurfaceConfig<TScenario extends ScenarioProps = ScenarioProps, TApp = unknown> {\n /**\n * Build the app the way the app builds itself. This should point at your\n * existing composition root, not restate it — whatever `main.tsx` calls.\n */\n mount(\n props: TScenario & { scenario: string },\n ): MountResult<TApp> | Promise<MountResult<TApp>>;\n\n /**\n * Optional extra settling after mount effects flush, for anything the first\n * render kicks off asynchronously (an initial fetch, a router resolve).\n * The CLI already flushes React effects and pending microtasks for you.\n */\n settle?: (mounted: MountResult<TApp>) => void | Promise<void>;\n\n /** Named surfaces to inspect and check. At least one is required. */\n scenarios: Record<string, TScenario>;\n\n /** Consumer identity snapshots are computed for. Default `{id:\"cli\",kind:\"test\"}`. */\n consumer?: AgentConsumer;\n\n /** Component-type prefixes to restrict to, same meaning as `SnapshotContext.scope`. */\n scope?: string[];\n\n /** Where `snapshot`/`check` keep baselines. Default `.agent-surface`, relative to the config. */\n baselineDir?: string;\n}\n\n/**\n * Identity function that exists purely for type inference — the same shape as\n * Vite's `defineConfig`. Your scenario props stay strongly typed inside\n * `mount()` without you annotating them.\n */\nexport function defineSurface<TScenario extends ScenarioProps, TApp = unknown>(\n config: SurfaceConfig<TScenario, TApp>,\n): SurfaceConfig<TScenario, TApp> {\n return config;\n}\n"],"mappings":";AAyDO,SAAS,cACd,QACgC;AAChC,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatValue
|
|
3
|
+
} from "./chunk-ODUIFFPM.js";
|
|
4
|
+
|
|
5
|
+
// src/render/ink.tsx
|
|
6
|
+
import { Box, Static, Text } from "ink";
|
|
7
|
+
import Spinner from "ink-spinner";
|
|
8
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
var OUTCOME = {
|
|
10
|
+
expose: { mark: "\u25CF", color: "green" },
|
|
11
|
+
disable: { mark: "\u25D0", color: "yellow" },
|
|
12
|
+
hide: { mark: "\u25CB", color: "red" }
|
|
13
|
+
};
|
|
14
|
+
function Loading({ label }) {
|
|
15
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
16
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) }),
|
|
17
|
+
` ${label}`
|
|
18
|
+
] });
|
|
19
|
+
}
|
|
20
|
+
function PolicyLine({
|
|
21
|
+
policy
|
|
22
|
+
}) {
|
|
23
|
+
const vote = policy.discovery?.decision;
|
|
24
|
+
const color = vote === "hide" ? "red" : vote === "disable" ? "yellow" : "green";
|
|
25
|
+
return /* @__PURE__ */ jsxs(Box, { paddingLeft: 6, children: [
|
|
26
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "policy " }),
|
|
27
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: policy.name }),
|
|
28
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` (${policy.scope}${policy.phases.length ? `, ${policy.phases.join("/")}` : ""}) ` }),
|
|
29
|
+
vote ? /* @__PURE__ */ jsxs(Text, { color, children: [
|
|
30
|
+
vote,
|
|
31
|
+
policy.discovery?.decision === "disable" ? ` \u2014 ${policy.discovery.reason}` : ""
|
|
32
|
+
] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "no discovery hook" }),
|
|
33
|
+
policy.threw ? /* @__PURE__ */ jsx(Text, { color: "red", bold: true, children: " THREW" }) : null,
|
|
34
|
+
policy.confirmationEscalation ? /* @__PURE__ */ jsx(Text, { color: "magenta", children: " escalates-confirmation" }) : null
|
|
35
|
+
] });
|
|
36
|
+
}
|
|
37
|
+
function Capability({ row }) {
|
|
38
|
+
const outcome = OUTCOME[row.outcome];
|
|
39
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
40
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
41
|
+
/* @__PURE__ */ jsx(Text, { color: outcome.color, children: ` ${outcome.mark} ` }),
|
|
42
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: row.name }),
|
|
43
|
+
row.tags.length > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${row.tags.join(" \xB7 ")}` }) : null
|
|
44
|
+
] }),
|
|
45
|
+
/* @__PURE__ */ jsx(Box, { paddingLeft: 4, children: /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: row.description }) }),
|
|
46
|
+
row.reason ? /* @__PURE__ */ jsx(Box, { paddingLeft: 4, children: /* @__PURE__ */ jsx(Text, { color: "yellow", wrap: "wrap", children: `\u2937 ${row.reason}` }) }) : null,
|
|
47
|
+
row.policies ? row.policies.length > 0 ? row.policies.map((policy, index) => /* @__PURE__ */ jsx(PolicyLine, { policy }, `${policy.name}-${index}`)) : [
|
|
48
|
+
/* @__PURE__ */ jsx(Box, { paddingLeft: 6, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "policies: none" }) }, "none")
|
|
49
|
+
] : null,
|
|
50
|
+
row.policies && row.availability && !row.availability.available ? /* @__PURE__ */ jsx(Box, { paddingLeft: 6, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: `availability: unavailable${row.availability.reason ? ` \u2014 ${row.availability.reason}` : ""}` }) }) : null,
|
|
51
|
+
row.schemas?.input !== void 0 ? /* @__PURE__ */ jsx(Box, { paddingLeft: 6, children: /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: `input: ${JSON.stringify(row.schemas.input)}` }) }) : null,
|
|
52
|
+
row.schemas?.output !== void 0 ? /* @__PURE__ */ jsx(Box, { paddingLeft: 6, children: /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: `output: ${JSON.stringify(row.schemas.output)}` }) }) : null
|
|
53
|
+
] });
|
|
54
|
+
}
|
|
55
|
+
function Group({ group }) {
|
|
56
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
57
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
58
|
+
/* @__PURE__ */ jsx(Text, { backgroundColor: "blueBright", color: "black", bold: true, children: ` ${group.heading} ` }),
|
|
59
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${group.rows.length}` })
|
|
60
|
+
] }),
|
|
61
|
+
group.rows.map((row) => /* @__PURE__ */ jsx(Capability, { row }, `${row.capabilityId}-${row.name}`))
|
|
62
|
+
] });
|
|
63
|
+
}
|
|
64
|
+
function Header({ view }) {
|
|
65
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
66
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: view.scenario }),
|
|
67
|
+
view.route ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${view.route}` }) : null,
|
|
68
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
|
|
69
|
+
/* @__PURE__ */ jsx(Text, { color: "green", children: `${view.counts.callable} callable` }),
|
|
70
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ", " }),
|
|
71
|
+
/* @__PURE__ */ jsx(Text, { color: "yellow", children: `${view.counts.disabled} visible-disabled` }),
|
|
72
|
+
view.explained ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
73
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ", " }),
|
|
74
|
+
/* @__PURE__ */ jsx(Text, { color: "red", children: `${view.counts.hidden} hidden` })
|
|
75
|
+
] }) : null
|
|
76
|
+
] });
|
|
77
|
+
}
|
|
78
|
+
function Empty({ view }) {
|
|
79
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
80
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: "Nothing is registered for this scenario \u2014 the agent has no surface here. That is the default: capabilities exist only where they were explicitly annotated." }),
|
|
81
|
+
view.explained ? null : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Re-run with --explain to see whether a policy hid it." })
|
|
82
|
+
] });
|
|
83
|
+
}
|
|
84
|
+
function Surface({ view }) {
|
|
85
|
+
const populated = view.groups.filter((group) => group.rows.length > 0);
|
|
86
|
+
const blocks = [
|
|
87
|
+
{ key: "__header" },
|
|
88
|
+
...populated.map((group) => ({ key: group.heading, group }))
|
|
89
|
+
];
|
|
90
|
+
return /* @__PURE__ */ jsx(Static, { items: blocks, children: (block) => block.group ? /* @__PURE__ */ jsx(Group, { group: block.group }, block.key) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
91
|
+
/* @__PURE__ */ jsx(Header, { view }),
|
|
92
|
+
populated.length === 0 ? /* @__PURE__ */ jsx(Empty, { view }) : null
|
|
93
|
+
] }, block.key) });
|
|
94
|
+
}
|
|
95
|
+
function Drift({
|
|
96
|
+
scenario,
|
|
97
|
+
entries
|
|
98
|
+
}) {
|
|
99
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
100
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
101
|
+
/* @__PURE__ */ jsx(Text, { backgroundColor: "yellow", color: "black", bold: true, children: ` ${scenario} ` }),
|
|
102
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${entries.length} change${entries.length === 1 ? "" : "s"}` })
|
|
103
|
+
] }),
|
|
104
|
+
entries.map((entry) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [
|
|
105
|
+
entry.subject ? /* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
106
|
+
entry.subject,
|
|
107
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${entry.path}` })
|
|
108
|
+
] }) : null,
|
|
109
|
+
entry.kind === "added" ? /* @__PURE__ */ jsx(Text, { color: "green", wrap: "wrap", children: `+ ${entry.path} ${formatValue(entry.after)}` }) : entry.kind === "removed" ? /* @__PURE__ */ jsx(Text, { color: "red", wrap: "wrap", children: `- ${entry.path} ${formatValue(entry.before)}` }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
110
|
+
/* @__PURE__ */ jsx(Text, { color: "yellow", children: `~ ${entry.path}` }),
|
|
111
|
+
/* @__PURE__ */ jsx(Text, { color: "red", wrap: "wrap", children: ` before: ${formatValue(entry.before)}` }),
|
|
112
|
+
/* @__PURE__ */ jsx(Text, { color: "green", wrap: "wrap", children: ` after: ${formatValue(entry.after)}` })
|
|
113
|
+
] })
|
|
114
|
+
] }, `${entry.kind}-${entry.path}`))
|
|
115
|
+
] });
|
|
116
|
+
}
|
|
117
|
+
export {
|
|
118
|
+
Drift,
|
|
119
|
+
Loading,
|
|
120
|
+
Surface
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=ink-GCWDO4ML.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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\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 <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 {view.explained ? (\n <>\n <Text dimColor>{\", \"}</Text>\n <Text color=\"red\">{`${view.counts.hidden} hidden`}</Text>\n </>\n ) : null}\n </Box>\n );\n}\n\nfunction Empty({ view }: { view: SurfaceView }): ReactElement {\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 {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":[]}
|