@henols/vice-mcp 0.2.2 → 0.2.4
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 +2 -2
- package/THIRD-PARTY-NOTICES.md +422 -1
- package/anno-bank.ts +171 -0
- package/anno-cli.ts +1736 -163
- package/anno-confidence.ts +2 -2
- package/anno-derive.ts +6 -6
- package/anno-details.ts +4 -4
- package/anno-enum-gen.ts +416 -30
- package/anno-export-asm.ts +1211 -126
- package/anno-graphics.ts +338 -0
- package/anno-hazard-report.ts +1367 -0
- package/anno-import.ts +495 -0
- package/anno-index.ts +8 -8
- package/anno-join.ts +480 -0
- package/anno-memmap-render.ts +22 -21
- package/anno-provenance-ledger.ts +472 -0
- package/anno-regbits-gen.ts +13 -13
- package/anno-register.ts +159 -0
- package/anno-store-export.ts +661 -0
- package/anno-store.ts +635 -124
- package/anno-symbols.ts +7 -7
- package/anno-tools.ts +1169 -16
- package/anno-types.ts +313 -40
- package/backend-detect.mts +124 -312
- package/build.ts +3 -1
- package/capture-predicate.ts +597 -0
- package/channel-lock.ts +349 -0
- package/evid-ingest.ts +217 -0
- package/evid-reconcile.ts +316 -0
- package/host-tool-client.ts +430 -0
- package/incident-record.ts +23 -12
- package/install-resources.ts +29 -13
- package/memmap-lookup.ts +285 -0
- package/package.json +27 -8
- package/prg-image.ts +1 -2
- package/repo-root.ts +87 -3
- package/resources/backend-detect.mjs +98 -236
- package/resources/broker-control.mjs +220 -54
- package/resources/broker-epoch.mjs +7 -8
- package/resources/broker-kill.mjs +36 -31
- package/resources/broker-launch.mjs +511 -374
- package/resources/broker-state.mjs +69 -24
- package/resources/container-guard.mjs +1 -1
- package/resources/ghidra-project.mjs +790 -0
- package/resources/host-tool.mjs +2533 -0
- package/resources/vice-broker.mjs +434 -290
- package/resources/vice-launcher.sh +127 -9
- package/stock-address.ts +1 -1
- package/stock-condition.ts +1 -1
- package/stock-connect.ts +9 -5
- package/stock-derived.ts +29 -37
- package/stock-diagnose.ts +200 -36
- package/stock-dispatch.ts +179 -77
- package/stock-handler.ts +1 -1
- package/stock-paths.ts +18 -14
- package/stock-petscii.ts +1 -1
- package/stock-protocol.ts +1 -1
- package/stock-recycle.ts +83 -2
- package/stock-reproducible-run.ts +811 -0
- package/stock-run-until.ts +100 -1
- package/stock-symbols.ts +4 -4
- package/stock-timing.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/vice-broker-client.ts +253 -108
- package/vice-errors.ts +268 -0
- package/vice-proxy.ts +339 -2144
- package/vsf-slice.ts +640 -0
- package/anno-d64.ts +0 -310
- package/capability-registry.ts +0 -390
- 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/text-connect.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// text-connect.ts
|
|
3
|
+
//
|
|
4
|
+
// THE ONE place the text channel's session lifecycle lives: claim the text
|
|
5
|
+
// socket from the broker BEFORE any TCP dial (mirroring stock-connect.ts's
|
|
6
|
+
// stockConnect() discipline exactly), open a TextMonitorClient, and hand back
|
|
7
|
+
// a connected session. Never dials a raw host/port outside the claim/grant
|
|
8
|
+
// flow -- textConnect() always claims through the SAME broker control
|
|
9
|
+
// surface a caller already holds a HeldLease over.
|
|
10
|
+
//
|
|
11
|
+
// WHY THIS FILE EXISTS: text-protocol.ts's TextMonitorClient deliberately
|
|
12
|
+
// answers only "how are these bytes framed" (see its own header comment) --
|
|
13
|
+
// it has no notion of a broker, a grant, or a claim. Something has to sit
|
|
14
|
+
// between "a HeldLease carrying a validated remoteMonitorPort" and "a
|
|
15
|
+
// connected, claimed TextMonitorClient" -- this file is that seam, the exact
|
|
16
|
+
// role stock-connect.ts already plays for the binary monitor.
|
|
17
|
+
//
|
|
18
|
+
// WHAT NOT TO DO:
|
|
19
|
+
// - Never dial the text-monitor port before claimMonitor() has succeeded --
|
|
20
|
+
// the SAME PROTO-08/D-13 discipline stock-connect.ts's own header
|
|
21
|
+
// comment states: a refused claim must arrive as a JSON response on a
|
|
22
|
+
// working control-plane socket, never as a connect() that silently sits
|
|
23
|
+
// unserviced (stock's text monitor also services exactly one client).
|
|
24
|
+
// - Never build a textReconnect(). Per RESEARCH.md's Open Question 2, an
|
|
25
|
+
// unexpected text-socket close is treated as FATAL for the session, not
|
|
26
|
+
// something to silently reconnect -- this matches D-13's own "held for
|
|
27
|
+
// the session's lifetime" framing: a session whose text socket died
|
|
28
|
+
// underneath it has lost a fact (what happened on that channel while it
|
|
29
|
+
// was down) that a silent reconnect would paper over.
|
|
30
|
+
// - Never re-derive a parallel claim interface. StockConnectBrokerControl
|
|
31
|
+
// (stock-connect.ts) is reused here as-is -- the SAME narrow structural
|
|
32
|
+
// interface, extended (plan 41-03, D-14) with an optional `channel`
|
|
33
|
+
// field on the options object claimMonitor()/releaseMonitor() already
|
|
34
|
+
// take, never a second interface.
|
|
35
|
+
import { TextMonitorClient } from "./text-protocol.ts";
|
|
36
|
+
import { ViceError } from "./vice-errors.ts";
|
|
37
|
+
import type { StockConnectBrokerControl } from "./stock-connect.ts";
|
|
38
|
+
import { MonitorOwnershipError } from "./vice-broker-client.ts";
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Session shape.
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
export interface TextConnectSession {
|
|
45
|
+
client: TextMonitorClient;
|
|
46
|
+
host: string;
|
|
47
|
+
port: number;
|
|
48
|
+
targetId: string;
|
|
49
|
+
brokerControl: StockConnectBrokerControl;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface TextConnectOptions {
|
|
53
|
+
host: string;
|
|
54
|
+
/** THIS instance's own text-monitor port, read by the caller off
|
|
55
|
+
* `HeldLease.remoteMonitorPort` (D-15) -- never a guessed or re-derived
|
|
56
|
+
* value. Validated by this function: an absent or non-integer value is
|
|
57
|
+
* refused by name, naming `targetId`, rather than dialling a guessed port. */
|
|
58
|
+
remoteMonitorPort: number | null | undefined;
|
|
59
|
+
targetId: string;
|
|
60
|
+
brokerControl: StockConnectBrokerControl;
|
|
61
|
+
connectTimeoutMs?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isValidPort(port: unknown): port is number {
|
|
65
|
+
return typeof port === "number" && Number.isInteger(port) && port >= 1 && port <= 65535;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function safeDisconnect(client: TextMonitorClient): Promise<void> {
|
|
69
|
+
try {
|
|
70
|
+
await client.disconnect();
|
|
71
|
+
} catch {
|
|
72
|
+
// disconnect() itself never throws in text-protocol.ts's own
|
|
73
|
+
// implementation, but this handshake's own failure-cleanup path must
|
|
74
|
+
// never itself fail on the way out (mirrors stock-connect.ts's
|
|
75
|
+
// safeDisconnect() exactly).
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The one connect handshake for the text channel, in load-bearing order:
|
|
81
|
+
*
|
|
82
|
+
* 1. Validate remoteMonitorPort -- a missing or invalid value is refused
|
|
83
|
+
* by name, naming targetId, and says the instance has no text-monitor
|
|
84
|
+
* port recorded. Never a dial against a guessed port.
|
|
85
|
+
* 2. claimMonitor() -- BEFORE any socket is opened, exactly like
|
|
86
|
+
* stockConnect()'s own step 1, claiming `channel: "text"` explicitly
|
|
87
|
+
* (plan 41-03, D-14). A `monitor_owned` refusal rejects with
|
|
88
|
+
* MonitorOwnershipError naming the holder AND the text channel; a
|
|
89
|
+
* `timeout` refusal rejects distinctly (the broker did not answer,
|
|
90
|
+
* which is not "someone else owns it").
|
|
91
|
+
* 3. Open a TextMonitorClient against host:remoteMonitorPort.
|
|
92
|
+
*
|
|
93
|
+
* Every failure path releases the monitor claim before propagating -- a
|
|
94
|
+
* handshake that fails at any step must never leave the instance claimed.
|
|
95
|
+
*/
|
|
96
|
+
export async function textConnect({
|
|
97
|
+
host,
|
|
98
|
+
remoteMonitorPort,
|
|
99
|
+
targetId,
|
|
100
|
+
brokerControl,
|
|
101
|
+
connectTimeoutMs,
|
|
102
|
+
}: TextConnectOptions): Promise<TextConnectSession> {
|
|
103
|
+
if (!isValidPort(remoteMonitorPort)) {
|
|
104
|
+
throw new ViceError(
|
|
105
|
+
`textConnect: target ${targetId} has no valid text-monitor port recorded (remoteMonitorPort=${JSON.stringify(remoteMonitorPort)}) -- refusing to dial a guessed port`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Plan 41-03 (D-14): explicit "text" -- never relies on claimMonitor()'s
|
|
110
|
+
// own binary default.
|
|
111
|
+
const claimOutcome = await brokerControl.claimMonitor({ targetId, channel: "text" });
|
|
112
|
+
if (!claimOutcome.ok) {
|
|
113
|
+
if (claimOutcome.reason === "monitor_owned") {
|
|
114
|
+
throw new MonitorOwnershipError(
|
|
115
|
+
`textConnect: text monitor for target ${targetId} on port ${remoteMonitorPort} is already claimed by grant ${claimOutcome.holder.grantId} -- another client holds this instance's single text-monitor socket`,
|
|
116
|
+
{ holderGrantId: claimOutcome.holder.grantId, holderClaimedAt: claimOutcome.holder.claimedAt, port: remoteMonitorPort, channel: claimOutcome.holder.channel },
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
// "timeout" (the broker did not answer) is kept strictly distinct from
|
|
120
|
+
// "monitor_owned" (someone else holds it) -- never conflated, matching
|
|
121
|
+
// vice-broker-client.ts's own MonitorOwnershipError header comment and
|
|
122
|
+
// stock-connect.ts's identical posture.
|
|
123
|
+
throw new ViceError(`textConnect: monitor claim for target ${targetId} failed (${claimOutcome.reason})`, { code: claimOutcome.reason });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const client = new TextMonitorClient();
|
|
127
|
+
try {
|
|
128
|
+
await client.connect(host, remoteMonitorPort, connectTimeoutMs !== undefined ? { timeoutMs: connectTimeoutMs } : {});
|
|
129
|
+
return { client, host, port: remoteMonitorPort, targetId, brokerControl };
|
|
130
|
+
} catch (err) {
|
|
131
|
+
await safeDisconnect(client);
|
|
132
|
+
// WR-07 (stock-connect.ts's own precedent): the release must never
|
|
133
|
+
// REPLACE the original failure. Both outcomes are reported on stderr;
|
|
134
|
+
// neither can displace `err`.
|
|
135
|
+
try {
|
|
136
|
+
// Plan 41-03 (D-14): explicit "text" -- a failure here must release
|
|
137
|
+
// only the text claim this call itself took, never a binary claim.
|
|
138
|
+
const released = await brokerControl.releaseMonitor({ targetId, channel: "text" });
|
|
139
|
+
if (!released.ok) {
|
|
140
|
+
console.error(
|
|
141
|
+
`textConnect: text-monitor release for target ${targetId} after a failed handshake was refused (${released.reason}) -- the instance may still be claimed`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
} catch (releaseErr) {
|
|
145
|
+
console.error(`textConnect: text-monitor release for target ${targetId} after a failed handshake threw: ${String(releaseErr)}`);
|
|
146
|
+
}
|
|
147
|
+
throw err;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Normal counterpart to textConnect()'s claim: disconnects the socket and
|
|
152
|
+
* releases the monitor claim together, so a caller never ends up holding one
|
|
153
|
+
* without the other. Mirrors stockDisconnect() exactly. */
|
|
154
|
+
export async function textDisconnect(session: TextConnectSession): Promise<void> {
|
|
155
|
+
await safeDisconnect(session.client);
|
|
156
|
+
await session.brokerControl.releaseMonitor({ targetId: session.targetId, channel: "text" });
|
|
157
|
+
}
|