@henols/vice-mcp 0.2.2 → 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 +2 -2
- package/THIRD-PARTY-NOTICES.md +422 -1
- package/anno-bank.ts +171 -0
- package/anno-cli.ts +1674 -99
- package/anno-enum-gen.ts +416 -30
- package/anno-export-asm.ts +1175 -89
- package/anno-graphics.ts +338 -0
- package/anno-hazard-report.ts +1367 -0
- package/anno-import.ts +495 -0
- package/anno-join.ts +480 -0
- package/anno-provenance-ledger.ts +472 -0
- package/anno-register.ts +159 -0
- package/anno-store-export.ts +661 -0
- package/anno-store.ts +518 -2
- package/anno-tools.ts +1169 -16
- package/anno-types.ts +275 -2
- 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 +189 -16
- package/resources/broker-epoch.mjs +1 -1
- package/resources/broker-kill.mjs +8 -2
- package/resources/broker-launch.mjs +365 -210
- 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 +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 +189 -42
- 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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Compiled by `tsc` from broker-control.mts. Edit the TypeScript source and rebuild;
|
|
3
3
|
// changes made directly to this file are silently overwritten by the next build, and are never
|
|
4
4
|
// deployed to the host on their own -- install-resources.mjs copies THIS file's on-disk contents
|
|
5
|
-
// verbatim to tools/, so an edit made only here reaches the host but is lost on the very next
|
|
5
|
+
// verbatim to .c64-re-tools/bin/, so an edit made only here reaches the host but is lost on the very next
|
|
6
6
|
// rebuild.
|
|
7
7
|
// broker-control.mts
|
|
8
8
|
//
|
|
@@ -48,6 +48,91 @@ const MAX_LINE_BYTES = 65536;
|
|
|
48
48
|
* and T-02-18's prohibition on wedge/hang vocabulary in this file's
|
|
49
49
|
* monitor-op refusals. */
|
|
50
50
|
const MONITOR_OWNERSHIP_DENIAL = "monitor_claim/monitor_release may only target the grant this connection itself holds";
|
|
51
|
+
/** Resolves the `channel` field on a `monitor_claim`/`monitor_release`
|
|
52
|
+
* request line (plan 41-03, D-14): an ABSENT field means `binary`
|
|
53
|
+
* deliberately -- a broker restarted mid-phase against a client that
|
|
54
|
+
* predates this field keeps working (backward compatibility, this plan's
|
|
55
|
+
* own must-have). An unrecognised NON-EMPTY value is `bad_request`, never a
|
|
56
|
+
* silent fallback and never cast -- the caller below names both accepted
|
|
57
|
+
* values in the refusal message. */
|
|
58
|
+
function resolveMonitorChannel(raw) {
|
|
59
|
+
if (raw === undefined)
|
|
60
|
+
return "binary";
|
|
61
|
+
if (raw === "binary" || raw === "text")
|
|
62
|
+
return raw;
|
|
63
|
+
return "bad_request";
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Phase 33, plan 33-06 (REPRO-05, D-15, T-33-03/T-33-04): the launch-profile
|
|
67
|
+
// narrowing site.
|
|
68
|
+
//
|
|
69
|
+
// THIS IS THE ONE PLACE `profile` IS NARROWED. Do not re-derive this check
|
|
70
|
+
// anywhere else -- not in vice-broker.mts, not in broker-launch.mts, not in
|
|
71
|
+
// the container-side client. A second copy is how one of them ends up
|
|
72
|
+
// accepting a shape the other refuses.
|
|
73
|
+
//
|
|
74
|
+
// WHY IT HAS TO EXIST AT ALL: `ControlRequest` above carries an index
|
|
75
|
+
// signature, so *anything* a container writes on the wire parses into it. The
|
|
76
|
+
// profile then feeds buildViceArgs(), i.e. an `execve(x64sc, argv)` on the
|
|
77
|
+
// HOST. An unvalidated `profile` is therefore an argv-construction surface
|
|
78
|
+
// across a trust boundary, not merely a typing inconvenience.
|
|
79
|
+
//
|
|
80
|
+
// WHY UNKNOWN KEYS ARE REFUSED BY NAME rather than dropped: a silently
|
|
81
|
+
// accepted typo means a caller asked for warp, got an unwarped instance, and
|
|
82
|
+
// received a confident success. That is the same undetectable-lie failure
|
|
83
|
+
// D-16 exists to prevent one layer down, and it is why the message below
|
|
84
|
+
// names the offending key -- the by-name unexpected-argument discipline the
|
|
85
|
+
// tool handlers already use (RUN_UNTIL_KEYS' own convention).
|
|
86
|
+
//
|
|
87
|
+
// WHAT MUST NEVER BE ADDED HERE: a passthrough string, an `extraArgs`, or any
|
|
88
|
+
// key whose VALUE reaches argv. `profile` maps to exactly two literal flag
|
|
89
|
+
// tokens (`-console`, `-warp`) and to nothing else (T-33-04). `VICE_ARGS`
|
|
90
|
+
// stays the single, deliberate operator-only whole-argv override.
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
/** The complete accepted key set -- the ONE binding list this narrowing
|
|
93
|
+
* checks against, so adding a knob to LaunchProfile without adding it here
|
|
94
|
+
* refuses the knob rather than silently widening the boundary. */
|
|
95
|
+
const LAUNCH_PROFILE_KEYS = Object.freeze(["warp", "headless"]);
|
|
96
|
+
const LAUNCH_PROFILE_SHAPE = `an object with optional boolean keys ${LAUNCH_PROFILE_KEYS.join("/")}, or absent`;
|
|
97
|
+
/** Narrows an untrusted `profile` field off the wire. Never throws; answers a
|
|
98
|
+
* discriminated result so the caller writes the existing `bad_request` error
|
|
99
|
+
* shape rather than needing a try/catch at the protocol boundary.
|
|
100
|
+
*
|
|
101
|
+
* Rules, in the order they are applied:
|
|
102
|
+
* - `undefined` (key absent) and `null` -> `ok` with `undefined`. Both mean
|
|
103
|
+
* profile-less, which is byte-identically today's behaviour.
|
|
104
|
+
* - a PLAIN object (arrays and every other non-plain value refused) whose
|
|
105
|
+
* keys are a subset of LAUNCH_PROFILE_KEYS and whose PRESENT values are
|
|
106
|
+
* booleans -> `ok` with that object.
|
|
107
|
+
* - anything else -> `ok: false`, with a message naming the offending value
|
|
108
|
+
* (or key) and the accepted shape. Never coerced, never silently dropped:
|
|
109
|
+
* `"yes"`, `1` and `"warp"` are refusals, not truthy warp requests. */
|
|
110
|
+
export function normaliseLaunchProfile(raw) {
|
|
111
|
+
if (raw === undefined || raw === null)
|
|
112
|
+
return { ok: true, profile: undefined };
|
|
113
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
114
|
+
// Arrays are specifically excluded: `typeof [] === "object"` in JS, so
|
|
115
|
+
// without the Array.isArray() arm a JSON array would reach the key walk
|
|
116
|
+
// below and pass it vacuously (an empty array has no own keys).
|
|
117
|
+
return { ok: false, message: `profile must be ${LAUNCH_PROFILE_SHAPE}; got ${JSON.stringify(raw) ?? String(raw)}` };
|
|
118
|
+
}
|
|
119
|
+
const entries = Object.entries(raw);
|
|
120
|
+
const unknownKeys = entries.filter(([key]) => !LAUNCH_PROFILE_KEYS.includes(key)).map(([key]) => key);
|
|
121
|
+
if (unknownKeys.length > 0) {
|
|
122
|
+
return { ok: false, message: `profile has unknown key(s) ${unknownKeys.join(", ")}; accepted shape is ${LAUNCH_PROFILE_SHAPE}` };
|
|
123
|
+
}
|
|
124
|
+
const profile = {};
|
|
125
|
+
for (const [key, value] of entries) {
|
|
126
|
+
if (typeof value !== "boolean") {
|
|
127
|
+
return { ok: false, message: `profile.${key} must be a boolean; got ${JSON.stringify(value) ?? String(value)}` };
|
|
128
|
+
}
|
|
129
|
+
if (key === "warp")
|
|
130
|
+
profile.warp = value;
|
|
131
|
+
if (key === "headless")
|
|
132
|
+
profile.headless = value;
|
|
133
|
+
}
|
|
134
|
+
return { ok: true, profile };
|
|
135
|
+
}
|
|
51
136
|
export function resolveControlPort(override) {
|
|
52
137
|
if (typeof override === "number")
|
|
53
138
|
return override;
|
|
@@ -74,6 +159,21 @@ function writeLine(socket, obj) {
|
|
|
74
159
|
socket.write(`${JSON.stringify(obj)}\n`);
|
|
75
160
|
}
|
|
76
161
|
}
|
|
162
|
+
/** Phase 34, plan 34-01: writes a `host_tool` SUCCESS response line -- the
|
|
163
|
+
* object host-tool.mts's runHostTool() produced, whatever shape that is
|
|
164
|
+
* (`{ ok: true, ... }` or its own `{ ok: false, message }` refusal). This is
|
|
165
|
+
* deliberately NOT `writeLine()`/`ControlResponse`: the host-tool response
|
|
166
|
+
* shape is host-tool.mts's own contract, not one more `ControlResponse`
|
|
167
|
+
* variant this module would otherwise have to keep in sync with a sibling
|
|
168
|
+
* module's allowlist. A REJECTED onHostTool() promise never reaches this
|
|
169
|
+
* function -- it is answered through the ordinary `writeLine()`/`error`
|
|
170
|
+
* path instead, so every protocol-level failure still goes through one
|
|
171
|
+
* shape. */
|
|
172
|
+
function writeHostToolLine(socket, obj) {
|
|
173
|
+
if (socket.writable) {
|
|
174
|
+
socket.write(`${JSON.stringify(obj)}\n`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
77
177
|
function defaultRequestId(prefix) {
|
|
78
178
|
return `${prefix}-${process.pid}-${Date.now()}`;
|
|
79
179
|
}
|
|
@@ -217,7 +317,7 @@ function attachControlProtocol(server, opts, pendingAcquires) {
|
|
|
217
317
|
* callback with the same request id instead of silently dropping the
|
|
218
318
|
* grant it produced.
|
|
219
319
|
*/
|
|
220
|
-
function attemptAcquire(requestId) {
|
|
320
|
+
function attemptAcquire(requestId, profile) {
|
|
221
321
|
// Half one: a queued entry whose owning socket is already gone is
|
|
222
322
|
// settled immediately, WITHOUT ever calling onAcquire() -- this is
|
|
223
323
|
// what keeps a retried drain pass from performing a real, ownerless
|
|
@@ -225,7 +325,12 @@ function attachControlProtocol(server, opts, pendingAcquires) {
|
|
|
225
325
|
if (socket.destroyed)
|
|
226
326
|
return Promise.resolve(true);
|
|
227
327
|
return opts
|
|
228
|
-
|
|
328
|
+
// Phase 33, plan 33-06: the profile is threaded through THIS shared
|
|
329
|
+
// helper, which both the immediate first attempt and every later
|
|
330
|
+
// drainPendingAcquires() retry go through -- so a request that
|
|
331
|
+
// queued behind an in-flight launch is retried later with the
|
|
332
|
+
// profile it was MADE with, never with a profile-less one.
|
|
333
|
+
.onAcquire(requestId, profile)
|
|
229
334
|
.then((outcome) => {
|
|
230
335
|
if (outcome.ok) {
|
|
231
336
|
// Half two: the pre-check above ran before this call; a
|
|
@@ -245,6 +350,14 @@ function attachControlProtocol(server, opts, pendingAcquires) {
|
|
|
245
350
|
url: outcome.grant.url,
|
|
246
351
|
epoch_file: outcome.grant.epochFile,
|
|
247
352
|
supervisor_dir: outcome.grant.supervisorDir,
|
|
353
|
+
// D-15; tightened by plan 41-05 (D-16): key omitted entirely
|
|
354
|
+
// when absent -- the fork case only now. A stock grant whose
|
|
355
|
+
// second (text-monitor) port allocation failed never reaches
|
|
356
|
+
// this line at all: acquirePortAndLaunch() fails the WHOLE
|
|
357
|
+
// acquire (`no_free_text_port`) before any grant is produced,
|
|
358
|
+
// so "absent" no longer needs to cover that case. Never a
|
|
359
|
+
// fabricated 0 or null standing in for "no port".
|
|
360
|
+
...(outcome.grant.remoteMonitorPort === undefined ? {} : { remote_monitor_port: outcome.grant.remoteMonitorPort }),
|
|
248
361
|
});
|
|
249
362
|
return true;
|
|
250
363
|
}
|
|
@@ -289,11 +402,52 @@ function attachControlProtocol(server, opts, pendingAcquires) {
|
|
|
289
402
|
socket.destroy();
|
|
290
403
|
return;
|
|
291
404
|
}
|
|
292
|
-
|
|
405
|
+
// Phase 34, plan 34-01 (SEAM-01): dispatched FIRST in the chain, before
|
|
406
|
+
// "acquire" -- so the ordering reads as the requirement does. Dispatch
|
|
407
|
+
// here is on EXACT STRING EQUALITY, never fallthrough, so branch order
|
|
408
|
+
// does not itself change which requests reach attemptAcquire() -- what
|
|
409
|
+
// actually makes this branch unable to touch lease state is that
|
|
410
|
+
// opts.onHostTool is its OWN callback (see StartControlListenerOptions'
|
|
411
|
+
// own comment), never composed from onAcquire/onRelease/onRecycle/
|
|
412
|
+
// onStatus/onHostState/onMonitorClaim/onMonitorRelease.
|
|
413
|
+
if (req.op === "host_tool") {
|
|
414
|
+
opts
|
|
415
|
+
.onHostTool(req)
|
|
416
|
+
.then((result) => {
|
|
417
|
+
if (!socket.destroyed)
|
|
418
|
+
writeHostToolLine(socket, result);
|
|
419
|
+
})
|
|
420
|
+
.catch(() => {
|
|
421
|
+
if (!socket.destroyed) {
|
|
422
|
+
writeLine(socket, { kind: "error", code: "internal", message: "host_tool threw" });
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
else if (req.op === "acquire") {
|
|
293
427
|
const requestId = typeof req.id === "string" && req.id !== "" ? req.id : defaultRequestId("req");
|
|
294
|
-
|
|
428
|
+
// Phase 33, plan 33-06 (T-33-03): narrow BEFORE attemptAcquire, so a
|
|
429
|
+
// malformed profile never reaches onAcquire and therefore never
|
|
430
|
+
// reaches the port allocator, a spawn, or argv construction. A
|
|
431
|
+
// refusal also does NOT enqueue -- the request is answered and
|
|
432
|
+
// dropped, never retried on a later drain pass with the same bad
|
|
433
|
+
// shape.
|
|
434
|
+
const normalised = normaliseLaunchProfile(req.profile);
|
|
435
|
+
if (!normalised.ok) {
|
|
436
|
+
writeLine(socket, { kind: "error", code: "bad_request", message: normalised.message });
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
const profile = normalised.profile;
|
|
440
|
+
// 33 review WR-03's profile-is-stock-only refusal lived here: it
|
|
441
|
+
// refused `profile.warp`/`profile.headless` when this broker's
|
|
442
|
+
// resolved backend had no `-warp`/`-console` route at all, so a
|
|
443
|
+
// caller learned a knob would be silently ignored rather than
|
|
444
|
+
// getting a confident grant with no effect. FORKRM-01 (plan 52-06):
|
|
445
|
+
// there is one backend now and it always has that route, so the
|
|
446
|
+
// condition this refused can no longer occur -- deleted rather than
|
|
447
|
+
// left as a check against a value that can never disagree.
|
|
448
|
+
void attemptAcquire(requestId, profile).then((settled) => {
|
|
295
449
|
if (!settled) {
|
|
296
|
-
enqueueAcquire(pendingAcquires, { requestId, attempt: () => attemptAcquire(requestId) });
|
|
450
|
+
enqueueAcquire(pendingAcquires, { requestId, attempt: () => attemptAcquire(requestId, profile) });
|
|
297
451
|
}
|
|
298
452
|
});
|
|
299
453
|
}
|
|
@@ -354,7 +508,6 @@ function attachControlProtocol(server, opts, pendingAcquires) {
|
|
|
354
508
|
started_at: hs.startedAt,
|
|
355
509
|
node_version: hs.nodeVersion,
|
|
356
510
|
vice_bin: hs.viceBin,
|
|
357
|
-
warm_floor: hs.warmFloor,
|
|
358
511
|
max_instances: hs.maxInstances,
|
|
359
512
|
base_port: hs.basePort,
|
|
360
513
|
backend: hs.backend,
|
|
@@ -370,15 +523,25 @@ function attachControlProtocol(server, opts, pendingAcquires) {
|
|
|
370
523
|
writeLine(socket, { kind: "error", code: "denied", message: MONITOR_OWNERSHIP_DENIAL });
|
|
371
524
|
return;
|
|
372
525
|
}
|
|
526
|
+
const channel = resolveMonitorChannel(req.channel);
|
|
527
|
+
if (channel === "bad_request") {
|
|
528
|
+
writeLine(socket, {
|
|
529
|
+
kind: "error",
|
|
530
|
+
code: "bad_request",
|
|
531
|
+
message: `monitor_claim: unrecognised channel ${JSON.stringify(req.channel)} -- accepted values are "binary" and "text"`,
|
|
532
|
+
});
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
373
535
|
const requestId = typeof req.id === "string" && req.id !== "" ? req.id : defaultRequestId("claim");
|
|
374
|
-
const outcome = opts.onMonitorClaim(requestId, targetId);
|
|
536
|
+
const outcome = opts.onMonitorClaim(requestId, targetId, channel);
|
|
375
537
|
if (outcome.ok) {
|
|
376
538
|
writeLine(socket, { kind: "monitor_claimed" });
|
|
377
539
|
}
|
|
378
540
|
else if (outcome.code === "monitor_owned") {
|
|
379
|
-
// Ownership conflict, named by holder
|
|
380
|
-
// never suggest the emulator
|
|
381
|
-
// (T-02-18; the plan's own grep gate
|
|
541
|
+
// Ownership conflict, named by holder AND channel (plan 41-03,
|
|
542
|
+
// D-14) -- deliberately worded to never suggest the emulator
|
|
543
|
+
// itself has stopped answering (T-02-18; the plan's own grep gate
|
|
544
|
+
// polices this).
|
|
382
545
|
//
|
|
383
546
|
// WR-08 (broker side): `holder` is REQUIRED by MonitorClaimOutcome for
|
|
384
547
|
// this code, but this handler runs inside socket.on("data") with no
|
|
@@ -386,13 +549,14 @@ function attachControlProtocol(server, opts, pendingAcquires) {
|
|
|
386
549
|
// TypeError out of the control listener and take the broker process
|
|
387
550
|
// with it -- a type contract is not a runtime guarantee at a wire
|
|
388
551
|
// boundary. The fallback names the holder as unknown rather than
|
|
389
|
-
// fabricating one
|
|
390
|
-
// with a malformed holder payload
|
|
391
|
-
|
|
552
|
+
// fabricating one (matching what the container-side client now does
|
|
553
|
+
// with a malformed holder payload), and defaults `channel` to the
|
|
554
|
+
// channel THIS request asked for -- never a fabricated third value.
|
|
555
|
+
const holder = outcome.holder ?? { grantId: "unknown", claimedAt: 0, pid: null, channel };
|
|
392
556
|
writeLine(socket, {
|
|
393
557
|
kind: "error",
|
|
394
558
|
code: "monitor_owned",
|
|
395
|
-
message: `instance already has a monitor client (grant ${holder.grantId}, claimed at ${holder.claimedAt}) -- this is an ownership conflict, not an emulator failure`,
|
|
559
|
+
message: `instance already has a monitor client on the ${holder.channel} channel (grant ${holder.grantId}, claimed at ${holder.claimedAt}) -- this is an ownership conflict, not an emulator failure`,
|
|
396
560
|
holder,
|
|
397
561
|
});
|
|
398
562
|
}
|
|
@@ -410,8 +574,17 @@ function attachControlProtocol(server, opts, pendingAcquires) {
|
|
|
410
574
|
writeLine(socket, { kind: "error", code: "denied", message: MONITOR_OWNERSHIP_DENIAL });
|
|
411
575
|
return;
|
|
412
576
|
}
|
|
577
|
+
const channel = resolveMonitorChannel(req.channel);
|
|
578
|
+
if (channel === "bad_request") {
|
|
579
|
+
writeLine(socket, {
|
|
580
|
+
kind: "error",
|
|
581
|
+
code: "bad_request",
|
|
582
|
+
message: `monitor_release: unrecognised channel ${JSON.stringify(req.channel)} -- accepted values are "binary" and "text"`,
|
|
583
|
+
});
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
413
586
|
const requestId = typeof req.id === "string" && req.id !== "" ? req.id : defaultRequestId("release-monitor");
|
|
414
|
-
const outcome = opts.onMonitorRelease(requestId, targetId);
|
|
587
|
+
const outcome = opts.onMonitorRelease(requestId, targetId, channel);
|
|
415
588
|
if (outcome.ok) {
|
|
416
589
|
writeLine(socket, { kind: "monitor_released" });
|
|
417
590
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Compiled by `tsc` from broker-epoch.mts. Edit the TypeScript source and rebuild;
|
|
3
3
|
// changes made directly to this file are silently overwritten by the next build, and are never
|
|
4
4
|
// deployed to the host on their own -- install-resources.mjs copies THIS file's on-disk contents
|
|
5
|
-
// verbatim to tools/, so an edit made only here reaches the host but is lost on the very next
|
|
5
|
+
// verbatim to .c64-re-tools/bin/, so an edit made only here reaches the host but is lost on the very next
|
|
6
6
|
// rebuild.
|
|
7
7
|
// broker-epoch.mts
|
|
8
8
|
//
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Compiled by `tsc` from broker-kill.mts. Edit the TypeScript source and rebuild;
|
|
3
3
|
// changes made directly to this file are silently overwritten by the next build, and are never
|
|
4
4
|
// deployed to the host on their own -- install-resources.mjs copies THIS file's on-disk contents
|
|
5
|
-
// verbatim to tools/, so an edit made only here reaches the host but is lost on the very next
|
|
5
|
+
// verbatim to .c64-re-tools/bin/, so an edit made only here reaches the host but is lost on the very next
|
|
6
6
|
// rebuild.
|
|
7
7
|
// broker-kill.mts
|
|
8
8
|
//
|
|
@@ -325,7 +325,13 @@ export function startupBanner() {
|
|
|
325
325
|
"vice-broker: nohup/setsid/systemd -- this launcher does not offer a --detach flag.",
|
|
326
326
|
];
|
|
327
327
|
if (process.env.VICE_BROKER_SPARES !== undefined) { // banner-only presence check (D-25/P-13) -- never reads the value
|
|
328
|
-
lines.push(
|
|
328
|
+
lines.push(
|
|
329
|
+
// Plan 41-05 (folded todo): this note's own former "use the warm-floor
|
|
330
|
+
// knob instead" replacement is ITSELF retired along with the warm
|
|
331
|
+
// floor -- pointing an operator at a second dead knob would be worse
|
|
332
|
+
// than pointing at none. VICE now launches strictly on demand, on the
|
|
333
|
+
// first request; there is no warming knob left to name.
|
|
334
|
+
"vice-broker: NOTE -- the VICE_BROKER_SPARES environment variable is set and is IGNORED; it was retired with no alias or fallback. There is no replacement -- VICE now launches strictly on demand, on the first request.");
|
|
329
335
|
}
|
|
330
336
|
return lines.join("\n");
|
|
331
337
|
}
|