@henols/vice-mcp 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/THIRD-PARTY-NOTICES.md +423 -25
- package/{r2000-acme-ident.ts → anno-acme-ident.ts} +13 -13
- package/anno-bank.ts +171 -0
- package/anno-cli.ts +3040 -0
- package/{r2000-confidence.ts → anno-confidence.ts} +22 -22
- package/anno-coverage.ts +2465 -0
- package/anno-derive.ts +590 -0
- package/anno-details.ts +169 -0
- package/anno-enum-gen.ts +919 -0
- package/anno-export-asm.ts +2396 -0
- package/anno-graphics.ts +338 -0
- package/anno-hazard-report.ts +1367 -0
- package/anno-import.ts +495 -0
- package/anno-index.ts +150 -0
- package/anno-join.ts +480 -0
- package/{r2000-memmap-render.ts → anno-memmap-render.ts} +236 -95
- package/anno-provenance-ledger.ts +472 -0
- package/{r2000-regbits-gen.ts → anno-regbits-gen.ts} +20 -15
- package/{r2000-regbits.json → anno-regbits.json} +2 -2
- package/anno-register.ts +399 -0
- package/anno-store-export.ts +661 -0
- package/anno-store.ts +4002 -0
- package/anno-symbols.ts +266 -0
- package/anno-tools.ts +3264 -0
- package/anno-types.ts +1909 -0
- package/backend-detect.mts +124 -312
- package/block-class.ts +201 -0
- package/build.ts +4 -2
- package/capture-predicate.ts +597 -0
- package/channel-lock.ts +349 -0
- package/disasm-decoder.ts +14 -14
- package/disasm-opcodes.ts +4 -4
- package/disasm-renderer.ts +2 -2
- package/evid-ingest.ts +217 -0
- package/evid-reconcile.ts +316 -0
- package/host-tool-client.ts +430 -0
- package/hostpath.ts +1 -1
- package/incident-record.ts +23 -12
- package/install-resources.ts +30 -14
- package/memmap-lookup.ts +285 -0
- package/package.json +48 -23
- package/prg-image.ts +118 -0
- package/repo-root.ts +107 -8
- package/resources/backend-detect.mjs +98 -236
- package/resources/broker-control.mjs +189 -16
- package/resources/broker-epoch.mjs +1 -1
- package/resources/broker-kill.mjs +8 -2
- package/resources/broker-launch.mjs +373 -214
- package/resources/broker-state.mjs +64 -18
- package/resources/container-guard.mjs +1 -1
- package/resources/ghidra-project.mjs +790 -0
- package/resources/host-tool.mjs +2561 -0
- package/resources/vice-broker.mjs +330 -184
- package/resources/vice-launcher.sh +130 -12
- package/stock-address.ts +6 -6
- package/stock-cia.ts +2 -2
- package/stock-condition.ts +8 -8
- package/stock-connect.ts +10 -6
- package/stock-derived.ts +29 -37
- package/stock-diagnose.ts +200 -36
- package/stock-dispatch.ts +200 -68
- package/stock-execution.ts +5 -3
- package/stock-handler.ts +1 -1
- package/stock-input.ts +9 -9
- package/stock-machine.ts +17 -6
- package/stock-paths.ts +18 -14
- package/stock-petscii.ts +1 -1
- package/stock-protocol.ts +17 -12
- package/stock-recycle.ts +83 -2
- package/stock-registers.ts +54 -29
- package/stock-reproducible-run.ts +811 -0
- package/stock-run-until.ts +100 -1
- package/stock-sprites.ts +3 -3
- package/stock-symbols.ts +13 -13
- package/stock-timing.ts +2 -2
- package/stock-vicii.ts +1 -1
- package/stop-oracle.ts +167 -0
- package/text-capability-probe.ts +660 -0
- package/text-connect.ts +157 -0
- package/text-protocol.ts +810 -0
- package/text-tools.ts +778 -0
- package/textmon-backtrace.ts +385 -0
- package/textmon-cpuhistory.ts +335 -0
- package/textmon-memmap.ts +494 -0
- package/textmon-profile.ts +458 -0
- package/textmon-registers.ts +748 -0
- package/tools-manifest.stock.json +864 -3
- package/version.ts +1 -1
- package/vice-broker-client.ts +189 -42
- package/vice-errors.ts +268 -0
- package/vice-proxy.ts +392 -2175
- package/vsf-slice.ts +640 -0
- package/capability-registry.ts +0 -388
- package/r2000-cli.ts +0 -1103
- package/r2000-d64.ts +0 -310
- package/r2000-enum-gen.ts +0 -574
- package/r2000-launch.ts +0 -357
- package/r2000-mcp-client.ts +0 -596
- package/r2000-project.ts +0 -190
- package/r2000-symbols.ts +0 -388
- package/r2000-tools.ts +0 -914
- package/r2000-verify.ts +0 -184
- package/refresh-manifest.ts +0 -124
- package/tools-manifest.json +0 -1223
- package/vice-probe.ts +0 -278
- package/vice-sync.ts +0 -336
- package/vice.ts +0 -772
package/r2000-verify.ts
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// r2000-verify.ts -- the ONE place that interprets regenerator2000's
|
|
3
|
-
// `--verify` output.
|
|
4
|
-
//
|
|
5
|
-
// WHY PARSING IS REQUIRED AT ALL (D-10, the concrete incident): with ACME
|
|
6
|
-
// absent from PATH and ca65 present, a real `regenerator2000 0.9.20 --verify`
|
|
7
|
-
// run on this host printed
|
|
8
|
-
//
|
|
9
|
-
// ✗ ACME — ACME not found in PATH (skipped)
|
|
10
|
-
// ✓ All roundtrip verifications passed.
|
|
11
|
-
// EXIT=0
|
|
12
|
-
//
|
|
13
|
-
// -- exit 0, and a summary line that reads as a full pass, while the one
|
|
14
|
-
// assembler this project actually cares about (`!cpu 6510`, ACME 0.97) never
|
|
15
|
-
// ran at all. Trusting the exit code here would let ACME be silently
|
|
16
|
-
// skipped and still report success. This is the WHAT-NOT-TO-DO for any
|
|
17
|
-
// future edit to this file: never derive `ok` from `status`, ever, no
|
|
18
|
-
// matter how tempting a bare zero-exit-status check looks. The verdict this
|
|
19
|
-
// module produces comes ONLY from parsing the per-assembler result lines
|
|
20
|
-
// and reading ACME's own line -- never from the process exit code, and
|
|
21
|
-
// never from the aggregate "All roundtrip verifications passed." summary
|
|
22
|
-
// line, which is itself the thing that lied in the transcript above.
|
|
23
|
-
//
|
|
24
|
-
// Both captured transcripts (the honest pass and this exact false-pass trap)
|
|
25
|
-
// are pinned verbatim as fixtures in r2000-verify.test.ts, so a future
|
|
26
|
-
// "simplification" back to an exit-code check fails a unit test immediately.
|
|
27
|
-
//
|
|
28
|
-
// WR-04 (10-REVIEW.md, fixed in plan 11-01): the verdict must also never
|
|
29
|
-
// trust just the FIRST ACME result line. `acmeVerdict()` used to select
|
|
30
|
-
// ACME's line with a bare array .find() over the first matching entry, so
|
|
31
|
-
// a transcript containing both a
|
|
32
|
-
// passing and a failing ACME line (a shape --verify has never printed as of
|
|
33
|
-
// 0.9.20, but one this defensive parser is explicitly meant to survive)
|
|
34
|
-
// reported `ok: true` from the first (passing) line while discarding the
|
|
35
|
-
// later failure -- exactly the "misleading success" this module exists to
|
|
36
|
-
// refuse. The fix requires UNANIMITY: every parsed ACME line must be `ok`,
|
|
37
|
-
// the first non-ok line (if any) drives the verdict, and if more than one
|
|
38
|
-
// ACME line is present after passing that check, the module refuses to
|
|
39
|
-
// guess which one is authoritative rather than picking one arbitrarily.
|
|
40
|
-
// Both the mixed-transcript case and the too-many-ACME-lines case are pinned
|
|
41
|
-
// verbatim as fixtures in r2000-verify.test.ts.
|
|
42
|
-
//
|
|
43
|
-
// Import nothing from `hostpath.ts`/`containerpath.ts` -- plan 10-01's
|
|
44
|
-
// absence assertion in `hostpath-consumers.test.ts` already names this file.
|
|
45
|
-
|
|
46
|
-
import { buildVerifyArgs, runR2000 } from "./r2000-launch.ts";
|
|
47
|
-
|
|
48
|
-
/** The three possible outcomes for a single per-assembler `--verify` result
|
|
49
|
-
* line. `"skipped"` and `"ok"` are DIFFERENT outcomes and must never be
|
|
50
|
-
* conflated -- a skipped assembler did not run, so it proves nothing, while
|
|
51
|
-
* an `ok` assembler was actually invoked and its output byte-diffed. */
|
|
52
|
-
export type AssemblerOutcome = "ok" | "skipped" | "failed";
|
|
53
|
-
|
|
54
|
-
export interface VerifyLine {
|
|
55
|
-
assembler: string;
|
|
56
|
-
outcome: AssemblerOutcome;
|
|
57
|
-
detail: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Matches exactly a per-assembler result line, e.g.:
|
|
61
|
-
// ✓ ACME — byte-identical (44 bytes)
|
|
62
|
-
// ✗ 64tass — 64tass not found in PATH (skipped)
|
|
63
|
-
// Tolerates an em-dash (U+2014), en-dash (U+2013) or plain hyphen as the
|
|
64
|
-
// separator, since only the ACME verdict is load-bearing here and the exact
|
|
65
|
-
// glyph regenerator2000 prints is an upstream formatting detail, not
|
|
66
|
-
// something this parser should be brittle against. Deliberately does NOT
|
|
67
|
-
// match the aggregate "✓ All roundtrip verifications passed." summary line
|
|
68
|
-
// (no separator token present there) or the "EXIT=N" line some transcripts
|
|
69
|
-
// carry -- both are excluded from the returned array by construction, not
|
|
70
|
-
// by a special-cased skip: they simply never match this shape.
|
|
71
|
-
const VERIFY_LINE_PATTERN = /^[✓✗]\s+(.+?)\s+[—–-]\s+(.+)$/;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Parses `regenerator2000 --verify`'s stdout into one `VerifyLine` per
|
|
75
|
-
* per-assembler result line. The aggregate `✓ All roundtrip verifications
|
|
76
|
-
* passed.` line is a summary, not an assembler line -- it never matches
|
|
77
|
-
* `VERIFY_LINE_PATTERN` (no `—`/`-` separator), so it is excluded from the
|
|
78
|
-
* result by construction rather than filtered out after the fact.
|
|
79
|
-
*/
|
|
80
|
-
export function parseVerifyOutput(stdout: string): VerifyLine[] {
|
|
81
|
-
const lines: VerifyLine[] = [];
|
|
82
|
-
for (const rawLine of stdout.split(/\r?\n/)) {
|
|
83
|
-
const trimmed = rawLine.trim();
|
|
84
|
-
if (!trimmed) continue;
|
|
85
|
-
const match = trimmed.match(VERIFY_LINE_PATTERN);
|
|
86
|
-
if (!match) continue;
|
|
87
|
-
const [, assemblerRaw, detailRaw] = match;
|
|
88
|
-
const assembler = assemblerRaw!.trim();
|
|
89
|
-
const detail = detailRaw!.trim();
|
|
90
|
-
// A "(skipped)" suffix always means skipped, regardless of the leading
|
|
91
|
-
// glyph. Anything else takes its outcome from the leading glyph: a
|
|
92
|
-
// leading ✓ is "ok", any other non-skipped result line is "failed" --
|
|
93
|
-
// this project has never observed a real ACME/ca65 failure transcript,
|
|
94
|
-
// but a future one must not silently parse as "ok".
|
|
95
|
-
const outcome: AssemblerOutcome = /\(skipped\)\s*$/i.test(detail)
|
|
96
|
-
? "skipped"
|
|
97
|
-
: trimmed.startsWith("✓")
|
|
98
|
-
? "ok"
|
|
99
|
-
: "failed";
|
|
100
|
-
lines.push({ assembler, outcome, detail });
|
|
101
|
-
}
|
|
102
|
-
return lines;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Derives the ACME-specific verdict from a parsed line set. `ok` only when
|
|
107
|
-
* at least one ACME line exists, EVERY parsed ACME line has outcome `"ok"`
|
|
108
|
-
* (unanimity -- WR-04), AND exactly one ACME line is present. A missing
|
|
109
|
-
* ACME line, any ACME line with outcome `"skipped"` or `"failed"`, and more
|
|
110
|
-
* than one `"ok"` ACME line each return `ok: false` with a distinct,
|
|
111
|
-
* quotable reason -- never conflate "skipped" with "passed", never let a
|
|
112
|
-
* passing line hide a later failing one, and never fall back to the summary
|
|
113
|
-
* line (which this module never even parses as a VerifyLine, see
|
|
114
|
-
* `parseVerifyOutput`).
|
|
115
|
-
*/
|
|
116
|
-
export function acmeVerdict(lines: VerifyLine[]): { ok: boolean; reason: string } {
|
|
117
|
-
const acmeLines = lines.filter((l) => l.assembler.toLowerCase() === "acme");
|
|
118
|
-
|
|
119
|
-
if (acmeLines.length === 0) {
|
|
120
|
-
return {
|
|
121
|
-
ok: false,
|
|
122
|
-
reason:
|
|
123
|
-
"no ACME line found in --verify output -- ACME was never invoked at all, which is a failure, " +
|
|
124
|
-
"not an absence of evidence",
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// First non-ok line wins -- so a passing ACME line earlier in the
|
|
129
|
-
// transcript can never hide a failing one later in it (WR-04).
|
|
130
|
-
const bad = acmeLines.find((l) => l.outcome !== "ok");
|
|
131
|
-
|
|
132
|
-
if (bad?.outcome === "skipped") {
|
|
133
|
-
return {
|
|
134
|
-
ok: false,
|
|
135
|
-
reason:
|
|
136
|
-
`ACME was skipped, not run -- "${bad.detail}". A skipped ACME is a failure, never a pass ` +
|
|
137
|
-
`(D-10): --verify can print "✓ All roundtrip verifications passed." and exit 0 even when ` +
|
|
138
|
-
`ACME never ran at all -- exactly the false pass observed live on this host with ACME absent ` +
|
|
139
|
-
`and ca65 present.`,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (bad?.outcome === "failed") {
|
|
144
|
-
return { ok: false, reason: `ACME reported a failure: "${bad.detail}"` };
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Every ACME line is ok at this point. Still refuse to guess which one is
|
|
148
|
-
// authoritative if more than one was printed (WR-04) -- unanimous is not
|
|
149
|
-
// the same as unambiguous.
|
|
150
|
-
if (acmeLines.length > 1) {
|
|
151
|
-
return {
|
|
152
|
-
ok: false,
|
|
153
|
-
reason: `--verify printed ${acmeLines.length} ACME result lines -- refusing to guess which one is the verdict`,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return { ok: true, reason: `ACME reported: ${acmeLines[0]!.detail}` };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export interface VerifyProjectResult {
|
|
161
|
-
ok: boolean;
|
|
162
|
-
reason: string;
|
|
163
|
-
lines: VerifyLine[];
|
|
164
|
-
status: number | null;
|
|
165
|
-
stdout: string;
|
|
166
|
-
stderr: string;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Runs `regenerator2000 --verify` against `projectPath` (via
|
|
171
|
-
* `buildVerifyArgs()`/`runR2000()`, so the `--vice` scan applies here too),
|
|
172
|
-
* parses stdout, and derives `ok` from `acmeVerdict()` -- deliberately NOT
|
|
173
|
-
* from `status`. Returns the raw status and streams so a caller can print
|
|
174
|
-
* them for diagnostics, but no code path in this function ever lets a zero
|
|
175
|
-
* exit status alone make `ok` true. A warning on stderr is never treated as
|
|
176
|
-
* a failure -- only the parsed ACME result line decides the verdict.
|
|
177
|
-
*/
|
|
178
|
-
export function verifyProject(projectPath: string): VerifyProjectResult {
|
|
179
|
-
const argv = buildVerifyArgs({ projectPath });
|
|
180
|
-
const { status, stdout, stderr } = runR2000(argv);
|
|
181
|
-
const lines = parseVerifyOutput(stdout);
|
|
182
|
-
const verdict = acmeVerdict(lines);
|
|
183
|
-
return { ok: verdict.ok, reason: verdict.reason, lines, status, stdout, stderr };
|
|
184
|
-
}
|
package/refresh-manifest.ts
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// The ONLY writer of tools-manifest.json (see vice-proxy.mjs's tools/list
|
|
3
|
-
// handler doc comment -- that handler is a pure, offline READ of the file
|
|
4
|
-
// this CLI produces). An operator runs this by hand against a running host
|
|
5
|
-
// to refresh the snapshot; vice-proxy.mjs never imports this file, so this
|
|
6
|
-
// file's stdout is a normal CLI stream, not the MCP channel.
|
|
7
|
-
//
|
|
8
|
-
// Sibling import: the transport module lives in this skill's own scripts/
|
|
9
|
-
// directory (plan 01.1-04 relocated it from the now-retired `vice-session`
|
|
10
|
-
// skill).
|
|
11
|
-
import { serverInfo, activeInstance, type ServerInfoPayload, type ToolInfo } from "./vice.ts";
|
|
12
|
-
import { writeFileSync, chmodSync, renameSync } from "node:fs";
|
|
13
|
-
import { fileURLToPath } from "node:url";
|
|
14
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
15
|
-
|
|
16
|
-
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
17
|
-
const DEFAULT_MANIFEST_PATH = join(HERE, "tools-manifest.json");
|
|
18
|
-
|
|
19
|
-
// The hand-authored stock surface's own filename (stock-dispatch.ts's
|
|
20
|
-
// tools-manifest.stock.json, plan 02-09) -- named here ONLY so
|
|
21
|
-
// writeManifestAtomic() below can refuse to ever target it, never as
|
|
22
|
-
// something this file writes.
|
|
23
|
-
const STOCK_MANIFEST_BASENAME = "tools-manifest.stock.json";
|
|
24
|
-
|
|
25
|
-
function manifestPath(): string {
|
|
26
|
-
return process.env.VICE_TOOLS_MANIFEST
|
|
27
|
-
? resolve(process.env.VICE_TOOLS_MANIFEST)
|
|
28
|
-
: DEFAULT_MANIFEST_PATH;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface ToolsManifest {
|
|
32
|
-
generated_at: string;
|
|
33
|
-
endpoint: string;
|
|
34
|
-
tools: ToolInfo[];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Write `manifest` to `path` via the tmp sibling -> restrict-mode -> content
|
|
38
|
-
* -> rename sequence (01.6.1-06 Decision 1), copied verbatim in shape from
|
|
39
|
-
* vice-broker.mts's writeBrokerRecord(). The tmp file is created EMPTY and
|
|
40
|
-
* mode-tightened to 0o600 BEFORE any content reaches it, then renamed into
|
|
41
|
-
* place -- so a crash or a full disk mid-write can only ever leave a stray
|
|
42
|
-
* tmp sibling behind, never a partial or empty file at the real path. This
|
|
43
|
-
* is what makes this module's own long-standing promise ("a partial or
|
|
44
|
-
* empty manifest is never written over a good one on failure") true against
|
|
45
|
-
* a crash, not only against a rejected handshake -- the rejected-handshake
|
|
46
|
-
* half of that guarantee already held (the early return below, unchanged
|
|
47
|
-
* from the original), but a crash between "open the file" and "write the
|
|
48
|
-
* content" could previously still truncate a good manifest to nothing.
|
|
49
|
-
*
|
|
50
|
-
* T-02-31 (02-09): this function regenerates the manifest from a LIVE fork
|
|
51
|
-
* host's tools/list, so its output path must NEVER be
|
|
52
|
-
* tools-manifest.stock.json -- that file is the hand-authored, separately
|
|
53
|
-
* committed stock surface (D-07/D-09), and this refresh path overwriting it
|
|
54
|
-
* with the fork's full tool list would silently destroy the trimming that
|
|
55
|
-
* surface exists to enforce. This assertion makes that impossible by
|
|
56
|
-
* accident; a future `--stock` flag pointing this generator at the stock
|
|
57
|
-
* file has to edit this line deliberately, not merely change a default. */
|
|
58
|
-
function writeManifestAtomic(path: string, manifest: ToolsManifest): void {
|
|
59
|
-
if (basename(path) === STOCK_MANIFEST_BASENAME) {
|
|
60
|
-
throw new Error(
|
|
61
|
-
`refresh-manifest: refusing to write ${STOCK_MANIFEST_BASENAME} -- this generator regenerates the fork's ` +
|
|
62
|
-
`manifest from a live host and must never overwrite the hand-authored stock surface (D-07/D-09)`
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
const tmpPath = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
66
|
-
writeFileSync(tmpPath, "");
|
|
67
|
-
chmodSync(tmpPath, 0o600);
|
|
68
|
-
writeFileSync(tmpPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
69
|
-
renameSync(tmpPath, path);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Parses no arguments (there are none), performs the host handshake, and
|
|
73
|
-
* writes the manifest -- or leaves the existing one untouched and sets a
|
|
74
|
-
* non-zero exit code if the handshake fails. Exported (01.6.1-06 Decision 2)
|
|
75
|
-
* so this can be driven directly in a test without spawning a process; the
|
|
76
|
-
* bottom-of-module entry-point guard below is what makes that safe -- import
|
|
77
|
-
* alone must never perform a handshake or write anything. */
|
|
78
|
-
export async function main(): Promise<void> {
|
|
79
|
-
const path = manifestPath();
|
|
80
|
-
|
|
81
|
-
// serverInfo() performs the host handshake (initialize + tools/list) and
|
|
82
|
-
// already strips DENY_LIST names before returning (vice.ts's own
|
|
83
|
-
// documented choke point) -- vice-proxy.mjs's tools/list handler applies
|
|
84
|
-
// the SAME filter again at read time, so a snapshot generated by any other
|
|
85
|
-
// means is still safe, but this refresh path inherits the omission for
|
|
86
|
-
// free.
|
|
87
|
-
let info: ServerInfoPayload;
|
|
88
|
-
try {
|
|
89
|
-
info = (await serverInfo()) as ServerInfoPayload;
|
|
90
|
-
} catch (e) {
|
|
91
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
92
|
-
console.error(
|
|
93
|
-
`refresh-manifest: could not reach the host VICE MCP server (${message}) -- ` +
|
|
94
|
-
`manifest at ${path} left UNCHANGED. A partial or empty manifest is never written over a good one on ` +
|
|
95
|
-
`failure -- whether the failure is a rejected handshake (this path) or a crash mid-write, the latter now ` +
|
|
96
|
-
`also covered by the tmp-sibling -> restrict-mode -> content -> rename sequence below.`
|
|
97
|
-
);
|
|
98
|
-
process.exitCode = 1;
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const tools = Array.isArray(info?.tools) ? info.tools : [];
|
|
103
|
-
const manifest: ToolsManifest = {
|
|
104
|
-
generated_at: new Date().toISOString(),
|
|
105
|
-
endpoint: activeInstance().url,
|
|
106
|
-
tools,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
writeManifestAtomic(path, manifest);
|
|
110
|
-
console.log(`refresh-manifest: wrote ${tools.length} tool${tools.length === 1 ? "" : "s"} to ${path}`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// -------------------------------------------------------------------- CLI
|
|
114
|
-
// Guarded exactly like vice-broker.mts's own entry point: importing this
|
|
115
|
-
// module (e.g. from a test) must never itself perform a handshake or write
|
|
116
|
-
// anything. process.exitCode, never process.exit(), so any pending I/O
|
|
117
|
-
// flushes first.
|
|
118
|
-
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
119
|
-
main().catch((e: unknown) => {
|
|
120
|
-
const detail = e instanceof Error && e.stack ? e.stack : String(e);
|
|
121
|
-
console.error(`refresh-manifest: unexpected error -- ${detail}`);
|
|
122
|
-
process.exitCode = 1;
|
|
123
|
-
});
|
|
124
|
-
}
|