deadbyte-mcp 0.15.45 → 0.15.47
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/MANIFEST.SHA256 +52 -27
- package/PROVENANCE.json +260 -257
- package/README.txt +22 -21
- package/RELEASE.json +366 -346
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +501 -503
- package/bin/deadbyte-kernel-lock-r74.exe +0 -0
- package/bridge/package.json +1 -1
- package/controller/deadbyte-controller.ps1 +20 -6
- package/controller/deadbyte-process-policy.json +7 -7
- package/docs/R73-CAPABILITY-GAP-MATRIX.json +17 -0
- package/docs/R73-EXECUTION-CONTRACT.md +11 -0
- package/docs/R73-IMPLEMENTATION-CHANGELOG.md +12 -0
- package/docs/R73-REMAINING-DEFECTS.md +9 -0
- package/docs/R74-KERNEL-LOCK-BINARY.json +11 -0
- package/native/deadbyte-kernel-lock-r74.cpp +65 -0
- package/package.json +2 -2
- package/scripts/build-kernel-lock-r74.ps1 +28 -0
- package/scripts/gate-windows-r72.ps1 +76 -0
- package/scripts/npm-prepublish-contract-r74.mjs +113 -0
- package/scripts/npm-release-r48.mjs +4 -0
- package/scripts/npm-release-readiness-r72.mjs +76 -0
- package/scripts/npm-release-readiness-r74.mjs +46 -0
- package/scripts/r73-atomic-benchmark.mjs +39 -0
- package/src/atomic-mutation.mjs +99 -9
- package/src/deadbyte-cli.mjs +722 -721
- package/src/kernel-lock-r74.mjs +125 -0
- package/src/launcher-isolation.mjs +58 -0
- package/src/remote-authority.mjs +75 -75
- package/src/remote-mcp-machine-smoke-client.mjs +84 -84
- package/src/runtime-update.mjs +2056 -2008
- package/src/staging-input-isolation.mjs +79 -0
- package/src/version.mjs +1 -1
- package/test/atomic-mutation.test.mjs +68 -2
- package/test/commander-schema-differential-r48.test.mjs +1 -1
- package/test/deferred-slot-operation-behavior.test.mjs +2 -2
- package/test/r58-cleanup-retention.test.mjs +3 -3
- package/test/r58-concurrent-recovery.test.mjs +3 -3
- package/test/r58-stale-owner.test.mjs +1 -1
- package/test/r59-tool-surface-compat.test.mjs +2 -2
- package/test/r60-authority-coherence.test.mjs +10 -2
- package/test/r71-isolated-slot-rollback-restore.test.mjs +11 -11
- package/test/r71-release-identity.test.mjs +12 -10
- package/test/r72-launcher-isolation.test.mjs +99 -0
- package/test/r72-publication-fence.test.mjs +56 -0
- package/test/r72-publication-output-integrity.test.mjs +110 -0
- package/test/r72-remote-safe-contract.test.mjs +101 -0
- package/test/r72-staging-input-isolation.test.mjs +87 -0
- package/test/r72-staging-workspace-isolation.test.mjs +114 -0
- package/test/r73-package-boundary.test.mjs +22 -0
- package/test/r74-kernel-lock.test.mjs +150 -0
- package/test/r74-prepublish-contract.test.mjs +89 -0
- package/test/remote-session-cli.test.mjs +110 -64
package/src/deadbyte-cli.mjs
CHANGED
|
@@ -1,721 +1,722 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { closeSync, openSync } from 'node:fs';
|
|
3
|
-
import { lstat, mkdir, open, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { spawn, spawnSync } from 'node:child_process';
|
|
5
|
-
import { createHash, randomBytes } from 'node:crypto';
|
|
6
|
-
import os from 'node:os';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import { setTimeout as sleep } from 'node:timers/promises';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
10
|
-
import { defaultPublicKeyPath } from './trust.mjs';
|
|
11
|
-
import { verifyAuditChain } from './audit-verify.mjs';
|
|
12
|
-
import { openRemoteResultJournal, parseRemoteResultCursor } from './remote-result-journal.mjs';
|
|
13
|
-
import { DEADBYTE_VERSION } from './version.mjs';
|
|
14
|
-
import { runNativeInspect } from './native-inspector-r67.mjs';
|
|
15
|
-
import { exportOfflineDiagnostics } from './r67-diagnostic-export.mjs';
|
|
16
|
-
import { verifyOfflineDiagnosticsZip } from './r67-diagnostics-verify.mjs';
|
|
17
|
-
import { parseActivePointer } from './release-slot.mjs';
|
|
18
|
-
import { printDoctor } from './doctor-r45.mjs';
|
|
19
|
-
import { classifyRemoteAuditOutcome, createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine, selectLiveLogNames } from './remote-live-log.mjs';
|
|
20
|
-
import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
|
|
21
|
-
import { ensureRuntimeCoherence } from './runtime-coherence.mjs';
|
|
22
|
-
import { windowsPowerShellEnvironment } from './runtime-update.mjs';
|
|
23
|
-
import { resolveStateRoot } from './state-root.mjs';
|
|
24
|
-
import {foregroundIdentityMatches,queryForegroundIdentity,validForegroundIdentity} from './foreground-process-identity.mjs';
|
|
25
|
-
import {acquireForegroundSessionLock} from './foreground-session-lock.mjs';
|
|
26
|
-
import { AUTHORITY_PLANES, authorityVector, authorityRestorePlan, parseAuthorityObservation, assertAuthorityVector, assertRuntimeReadyObservation, authorityBanner } from './remote-authority.mjs';
|
|
27
|
-
import { decodeRemoteLogBytes, boundedRemoteDiagnostic, safeRemoteLogPrefixLength } from './remote-log-decode-r60.mjs';
|
|
28
|
-
|
|
29
|
-
const REMOTE_COMMANDS=new Set([
|
|
30
|
-
'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
|
|
31
|
-
'armprocess','disarmprocess','processstatus','armhost','disarmhost','hoststatus','hosttest',
|
|
32
|
-
'armdesktop','disarmdesktop','desktopstatus',
|
|
33
|
-
'armcoding','disarmcoding','codingstatus','codingtest','armautonomous','disarmautonomous','autonomousstatus','autonomoustest'
|
|
34
|
-
]);
|
|
35
|
-
const REMOTE_STATUS_TIMEOUT_MS=30000;
|
|
36
|
-
const REMOTE_START_TIMEOUT_MS=150000;
|
|
37
|
-
const SESSION_CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost'];
|
|
38
|
-
const SUPERVISOR_COMMANDS=Object.freeze({
|
|
39
|
-
start:'supervisorstart',stop:'supervisorstop',status:'supervisorstatus',recover:'supervisorrecover'
|
|
40
|
-
});
|
|
41
|
-
const CURRENT_PACKAGE_ROOT=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
42
|
-
const CURRENT_DEPLOY_SCRIPT=path.join(CURRENT_PACKAGE_ROOT,'scripts','deploy-r48.mjs');
|
|
43
|
-
const REMOTE_UPDATE_TIMEOUT_MS=900000;
|
|
44
|
-
const LIVE_LOG_DISCOVERY_INTERVAL_MS=1000;
|
|
45
|
-
|
|
46
|
-
function stateRoot(){return resolveStateRoot();}
|
|
47
|
-
async function requireRegular(file,label){const info=await lstat(file);if(!info.isFile()||info.isSymbolicLink()) throw new Error(`${label} must be a regular non-symlink file`);return file;}
|
|
48
|
-
function printHelp(){
|
|
49
|
-
const exact=`deadbyte-mcp@${DEADBYTE_VERSION}`;
|
|
50
|
-
process.stdout.write([
|
|
51
|
-
'DEADBYTE MCP CLI','',
|
|
52
|
-
` npx -y ${exact} doctor # verify current package/release truth`,
|
|
53
|
-
` npx -y ${exact} mcp # stdio MCP server for local clients`,
|
|
54
|
-
` npx -y ${exact} remote #
|
|
55
|
-
` npx -y ${exact} remote --
|
|
56
|
-
` npx -y ${exact} remote --
|
|
57
|
-
` npx -y ${exact} remote --
|
|
58
|
-
` npx -y ${exact} remote --
|
|
59
|
-
` npx -y ${exact} remote --
|
|
60
|
-
` npx -y ${exact} remote --
|
|
61
|
-
` npx -y ${exact} remote status`,
|
|
62
|
-
` npx -y ${exact} remote
|
|
63
|
-
` npx -y ${exact}
|
|
64
|
-
` npx -y ${exact} supervisor
|
|
65
|
-
` npx -y ${exact}
|
|
66
|
-
` npx -y ${exact} audit
|
|
67
|
-
` npx -y ${exact}
|
|
68
|
-
` npx -y ${exact} diagnostics
|
|
69
|
-
` npx -y ${exact}
|
|
70
|
-
` npx -y ${exact} inspect
|
|
71
|
-
` npx -y ${exact} inspect
|
|
72
|
-
` npx -y ${exact} inspect memory
|
|
73
|
-
` npx -y ${exact} inspect
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if(capture&&
|
|
193
|
-
if(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
if(
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
/^
|
|
231
|
-
/^
|
|
232
|
-
/^
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
try{
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
...selected.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if(
|
|
267
|
-
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
...(
|
|
280
|
-
...(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
let
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if(
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if(timeline
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
...(timeline.
|
|
350
|
-
...(timeline.
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
let
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
if(
|
|
374
|
-
|
|
375
|
-
partialLines.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
function
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
let
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if(
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
(value.
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if(prior.owner_identity
|
|
459
|
-
if(
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
if(prior.guardian_identity
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
if(
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
await
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const
|
|
484
|
-
const
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
marker.
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
guardian.
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
const
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
if(
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
await
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const
|
|
543
|
-
const
|
|
544
|
-
const
|
|
545
|
-
const
|
|
546
|
-
const
|
|
547
|
-
const
|
|
548
|
-
process.
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
let
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
process.stdout.write(
|
|
555
|
-
process.stdout.write(`${formatRemoteLifecycle(
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
process.stdout.write(`${formatRemoteLifecycle('
|
|
586
|
-
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
process.stdout
|
|
609
|
-
process.stdout.write(
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
aborter.
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
process.stdout.write(
|
|
621
|
-
process.
|
|
622
|
-
|
|
623
|
-
process.
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
await
|
|
627
|
-
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
if(arg==='--
|
|
636
|
-
if(
|
|
637
|
-
|
|
638
|
-
mode
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
if(
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
if(
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
if(
|
|
655
|
-
if(
|
|
656
|
-
|
|
657
|
-
const
|
|
658
|
-
await
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
if(command==='
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if(command==='
|
|
695
|
-
if(command==='
|
|
696
|
-
if(command==='
|
|
697
|
-
|
|
698
|
-
if(rest
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { closeSync, openSync } from 'node:fs';
|
|
3
|
+
import { lstat, mkdir, open, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
5
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { defaultPublicKeyPath } from './trust.mjs';
|
|
11
|
+
import { verifyAuditChain } from './audit-verify.mjs';
|
|
12
|
+
import { openRemoteResultJournal, parseRemoteResultCursor } from './remote-result-journal.mjs';
|
|
13
|
+
import { DEADBYTE_VERSION } from './version.mjs';
|
|
14
|
+
import { runNativeInspect } from './native-inspector-r67.mjs';
|
|
15
|
+
import { exportOfflineDiagnostics } from './r67-diagnostic-export.mjs';
|
|
16
|
+
import { verifyOfflineDiagnosticsZip } from './r67-diagnostics-verify.mjs';
|
|
17
|
+
import { parseActivePointer } from './release-slot.mjs';
|
|
18
|
+
import { printDoctor } from './doctor-r45.mjs';
|
|
19
|
+
import { classifyRemoteAuditOutcome, createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine, selectLiveLogNames } from './remote-live-log.mjs';
|
|
20
|
+
import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
|
|
21
|
+
import { ensureRuntimeCoherence } from './runtime-coherence.mjs';
|
|
22
|
+
import { windowsPowerShellEnvironment } from './runtime-update.mjs';
|
|
23
|
+
import { resolveStateRoot } from './state-root.mjs';
|
|
24
|
+
import {foregroundIdentityMatches,queryForegroundIdentity,validForegroundIdentity} from './foreground-process-identity.mjs';
|
|
25
|
+
import {acquireForegroundSessionLock} from './foreground-session-lock.mjs';
|
|
26
|
+
import { AUTHORITY_PLANES, authorityVector, authorityRestorePlan, parseAuthorityObservation, assertAuthorityVector, assertRuntimeReadyObservation, authorityBanner } from './remote-authority.mjs';
|
|
27
|
+
import { decodeRemoteLogBytes, boundedRemoteDiagnostic, safeRemoteLogPrefixLength } from './remote-log-decode-r60.mjs';
|
|
28
|
+
|
|
29
|
+
const REMOTE_COMMANDS=new Set([
|
|
30
|
+
'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
|
|
31
|
+
'armprocess','disarmprocess','processstatus','armhost','disarmhost','hoststatus','hosttest',
|
|
32
|
+
'armdesktop','disarmdesktop','desktopstatus',
|
|
33
|
+
'armcoding','disarmcoding','codingstatus','codingtest','armautonomous','disarmautonomous','autonomousstatus','autonomoustest'
|
|
34
|
+
]);
|
|
35
|
+
const REMOTE_STATUS_TIMEOUT_MS=30000;
|
|
36
|
+
const REMOTE_START_TIMEOUT_MS=150000;
|
|
37
|
+
const SESSION_CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost'];
|
|
38
|
+
const SUPERVISOR_COMMANDS=Object.freeze({
|
|
39
|
+
start:'supervisorstart',stop:'supervisorstop',status:'supervisorstatus',recover:'supervisorrecover'
|
|
40
|
+
});
|
|
41
|
+
const CURRENT_PACKAGE_ROOT=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
42
|
+
const CURRENT_DEPLOY_SCRIPT=path.join(CURRENT_PACKAGE_ROOT,'scripts','deploy-r48.mjs');
|
|
43
|
+
const REMOTE_UPDATE_TIMEOUT_MS=900000;
|
|
44
|
+
const LIVE_LOG_DISCOVERY_INTERVAL_MS=1000;
|
|
45
|
+
|
|
46
|
+
function stateRoot(){return resolveStateRoot();}
|
|
47
|
+
async function requireRegular(file,label){const info=await lstat(file);if(!info.isFile()||info.isSymbolicLink()) throw new Error(`${label} must be a regular non-symlink file`);return file;}
|
|
48
|
+
function printHelp(){
|
|
49
|
+
const exact=`deadbyte-mcp@${DEADBYTE_VERSION}`;
|
|
50
|
+
process.stdout.write([
|
|
51
|
+
'DEADBYTE MCP CLI','',
|
|
52
|
+
` npx -y ${exact} doctor # verify current package/release truth`,
|
|
53
|
+
` npx -y ${exact} mcp # stdio MCP server for local clients`,
|
|
54
|
+
` npx -y ${exact} remote # safe observation-only foreground session`,
|
|
55
|
+
` npx -y ${exact} remote --operator # process/coding/autonomous; explicit owner gates still apply`,
|
|
56
|
+
` npx -y ${exact} remote --desktop # session with Desktop authority; Host remains disarmed`,
|
|
57
|
+
` npx -y ${exact} remote --host # session with Host authority; Desktop remains disarmed`,
|
|
58
|
+
` npx -y ${exact} remote --full # explicit five-plane foreground session`,
|
|
59
|
+
` npx -y ${exact} remote --resume <cursor> # replay missed results, then reconnect`,
|
|
60
|
+
` npx -y ${exact} remote --trace # show trace/span identity without payload detail`,
|
|
61
|
+
` npx -y ${exact} remote --filter <expr> # presentation filter: tool/category/status/trace`,
|
|
62
|
+
` npx -y ${exact} remote status`,
|
|
63
|
+
` npx -y ${exact} remote restart`,
|
|
64
|
+
` npx -y ${exact} supervisor status`,
|
|
65
|
+
` npx -y ${exact} supervisor recover`,
|
|
66
|
+
` npx -y ${exact} audit verify`,
|
|
67
|
+
` npx -y ${exact} audit status`,
|
|
68
|
+
` npx -y ${exact} diagnostics export --output <local.zip> # offline, never uploads`,
|
|
69
|
+
` npx -y ${exact} diagnostics verify --input <local.zip> # local structural/CRC verify`,
|
|
70
|
+
` npx -y ${exact} inspect process [limit] # local native process snapshot`,
|
|
71
|
+
` npx -y ${exact} inspect identity PID # native process creation token`,
|
|
72
|
+
` npx -y ${exact} inspect memory self [limit] # local VirtualQueryEx metadata only`,
|
|
73
|
+
` npx -y ${exact} inspect memory PID <creation_filetime> [limit] # PID-reuse guarded`,
|
|
74
|
+
` npx -y ${exact} inspect pe <file> # local native PE32/PE32+ inspection`,''
|
|
75
|
+
].join('\n'));
|
|
76
|
+
}
|
|
77
|
+
async function remoteContext(){
|
|
78
|
+
if(process.platform!=='win32') throw new Error('remote controller is currently Windows-only');
|
|
79
|
+
const root=stateRoot();
|
|
80
|
+
const launcher=await requireRegular(path.join(root,'bin','DEADBYTE-MCP.ps1'),'stable DEADBYTE launcher');
|
|
81
|
+
const systemRoot=process.env.SystemRoot||process.env.WINDIR;if(!systemRoot) throw new Error('SystemRoot/WINDIR missing');
|
|
82
|
+
const powershell=await requireRegular(path.join(systemRoot,'System32','WindowsPowerShell','v1.0','powershell.exe'),'Windows PowerShell');
|
|
83
|
+
return {root,launcher,powershell};
|
|
84
|
+
}
|
|
85
|
+
function boundedSingleLine(value,maxChars=2048){
|
|
86
|
+
return boundedRemoteDiagnostic(value,maxChars);
|
|
87
|
+
}
|
|
88
|
+
async function readActiveRuntimeBinding(root){
|
|
89
|
+
const file=path.join(root,'active-release.json');
|
|
90
|
+
try{return parseActivePointer(await readFile(file,'utf8'));}
|
|
91
|
+
catch(error){if(error?.code==='ENOENT') return null;throw new Error(`active runtime pointer invalid: ${boundedSingleLine(error?.message||error,512)}`);}
|
|
92
|
+
}
|
|
93
|
+
async function readLiveRootPaths(root){
|
|
94
|
+
const active=await readActiveRuntimeBinding(root);
|
|
95
|
+
if(!active) return Object.freeze({});
|
|
96
|
+
const file=path.join(active.generation_root,'coding-policy.json');
|
|
97
|
+
let raw;
|
|
98
|
+
try{raw=JSON.parse(await readFile(file,'utf8'));}
|
|
99
|
+
catch(error){if(error?.code==='ENOENT') return Object.freeze({});throw new Error(`live root policy invalid: ${boundedSingleLine(error?.message||error,512)}`);}
|
|
100
|
+
if(raw?.schema!=='deadbyte.coding-policy.v1'||!raw.roots||typeof raw.roots!=='object'||Array.isArray(raw.roots)) throw new Error('live root policy schema invalid');
|
|
101
|
+
const roots={};
|
|
102
|
+
for(const [id,spec] of Object.entries(raw.roots)){
|
|
103
|
+
if(!/^[A-Za-z0-9._-]{1,64}$/.test(id)||!spec||typeof spec!=='object'||typeof spec.path!=='string'||!path.isAbsolute(spec.path)) continue;
|
|
104
|
+
roots[id]=path.resolve(spec.path);
|
|
105
|
+
}
|
|
106
|
+
return Object.freeze(roots);
|
|
107
|
+
}
|
|
108
|
+
async function reportVersionCoherence(root,{displayMode='ascii'}={}){
|
|
109
|
+
const active=await readActiveRuntimeBinding(root);
|
|
110
|
+
if(!active||active.version===DEADBYTE_VERSION) return active;
|
|
111
|
+
writeRemote(process.stderr,`${remoteTag('warn',displayMode)} VERSION SKEW: CLI package ${DEADBYTE_VERSION}; active runtime ${active.version} (${active.release_id})\n`,displayMode);
|
|
112
|
+
return active;
|
|
113
|
+
}
|
|
114
|
+
async function synchronizeForegroundRuntime(ctx,{displayMode='unicode'}={}){
|
|
115
|
+
const packageManifestSha256=createHash('sha256').update(await readFile(path.join(CURRENT_PACKAGE_ROOT,'MANIFEST.SHA256'))).digest('hex');
|
|
116
|
+
return ensureRuntimeCoherence({
|
|
117
|
+
cliVersion:DEADBYTE_VERSION,
|
|
118
|
+
packageManifestSha256,
|
|
119
|
+
readActive:()=>readActiveRuntimeBinding(ctx.root),
|
|
120
|
+
disarmPrivileged:async()=>{
|
|
121
|
+
for(const command of ['disarmhost','disarmdesktop']){
|
|
122
|
+
const result=invokeController(ctx,command,{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
123
|
+
if(result.status!==0) throw new Error(`runtime coherence '${command}' failed exit=${result.status}`);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
activateCurrentPackage:async()=>{
|
|
127
|
+
const result=spawnSync(process.execPath,[CURRENT_DEPLOY_SCRIPT],{
|
|
128
|
+
cwd:CURRENT_PACKAGE_ROOT,
|
|
129
|
+
env:{...process.env,DEADBYTE_STATE_ROOT:ctx.root},
|
|
130
|
+
encoding:'utf8',
|
|
131
|
+
windowsHide:true,
|
|
132
|
+
shell:false,
|
|
133
|
+
timeout:REMOTE_UPDATE_TIMEOUT_MS,
|
|
134
|
+
maxBuffer:16*1024*1024
|
|
135
|
+
});
|
|
136
|
+
if(result.error){
|
|
137
|
+
if(result.error?.code==='ETIMEDOUT') throw new Error(`runtime coherence activation timed out after ${REMOTE_UPDATE_TIMEOUT_MS}ms`);
|
|
138
|
+
throw result.error;
|
|
139
|
+
}
|
|
140
|
+
if(result.status!==0){
|
|
141
|
+
const detail=boundedSingleLine(result.stderr||result.stdout||'no deployment output',2048);
|
|
142
|
+
throw new Error(`runtime coherence activation failed exit=${String(result.status)}: ${detail}`);
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
onEvent:event=>{
|
|
146
|
+
if(event.phase==='upgrade_start') writeRemote(process.stdout,`[UPDATE] Runtime ${event.from_version} -> ${event.to_version}; activating current package...\n`,displayMode);
|
|
147
|
+
else if(event.phase==='upgrade_complete') writeRemote(process.stdout,`[UPDATE] Runtime synchronized: ${event.release_id||event.to_version}\n`,displayMode);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
async function synchronizeOneShotRuntime(ctx,{displayMode='ascii'}={}){
|
|
152
|
+
const active=await readActiveRuntimeBinding(ctx.root);
|
|
153
|
+
if(!active||active.version===DEADBYTE_VERSION) return synchronizeForegroundRuntime(ctx,{displayMode});
|
|
154
|
+
const before=observedAuthority(ctx);
|
|
155
|
+
const expected=Object.fromEntries(AUTHORITY_PLANES.map(plane=>[plane,before.authority[plane].armed]));
|
|
156
|
+
const result=await synchronizeForegroundRuntime(ctx,{displayMode});
|
|
157
|
+
if(result.status!=='upgraded') return result;
|
|
158
|
+
for(const command of authorityRestorePlan(before)){
|
|
159
|
+
const restored=invokeController(ctx,command,{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
160
|
+
if(restored.status!==0) throw new Error(`runtime coherence authority restore '${command}' failed exit=${restored.status}`);
|
|
161
|
+
}
|
|
162
|
+
assertAuthorityVector(observedAuthority(ctx),expected);
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
async function readControllerStartFailure(root,command,{maxChars=2048}={}){
|
|
166
|
+
const file=path.join(root,'runtime','controller-start-failure.json');
|
|
167
|
+
let raw;
|
|
168
|
+
try{raw=await readFile(file,'utf8');}catch(error){if(error?.code==='ENOENT') return null;throw error;}
|
|
169
|
+
let value;
|
|
170
|
+
try{value=JSON.parse(String(raw).replace(/^\uFEFF/,''));}catch{return null;}
|
|
171
|
+
if(value?.schema!=='deadbyte.controller-start-failure.v1'||String(value?.command||'')!==command||typeof value?.message!=='string') return null;
|
|
172
|
+
const message=boundedSingleLine(value.message,maxChars);
|
|
173
|
+
return message?{file,message}:null;
|
|
174
|
+
}
|
|
175
|
+
async function readTailBounded(file,maxBytes=4096){
|
|
176
|
+
try{
|
|
177
|
+
const info=await stat(file);
|
|
178
|
+
const length=Math.min(info.size,maxBytes);
|
|
179
|
+
if(length===0) return '';
|
|
180
|
+
const buffer=Buffer.alloc(length);
|
|
181
|
+
const handle=await open(file,'r');
|
|
182
|
+
let encoding=null;
|
|
183
|
+
try{
|
|
184
|
+
const header=Buffer.alloc(2);await handle.read(header,0,2,0);
|
|
185
|
+
if(header[0]===0xff&&header[1]===0xfe)encoding='utf16le';
|
|
186
|
+
await handle.read(buffer,0,length,info.size-length);
|
|
187
|
+
}finally{await handle.close();}
|
|
188
|
+
return boundedSingleLine(decodeRemoteLogBytes(file,buffer,{encoding}),maxBytes);
|
|
189
|
+
}catch(error){if(error?.code==='ENOENT') return '';throw error;}
|
|
190
|
+
}
|
|
191
|
+
function invokeController(ctx,command,{capture=false,allowFailure=false,echoCaptured=true,timeoutMs=null,discardOutput=false,redirectOutput=null,json=false,probeMode=null}={}){
|
|
192
|
+
if(capture&&discardOutput) throw new Error('invokeController cannot capture and discard output together');
|
|
193
|
+
if(capture&&redirectOutput) throw new Error('invokeController cannot capture and redirect output together');
|
|
194
|
+
if(discardOutput&&redirectOutput) throw new Error('invokeController cannot discard and redirect output together');
|
|
195
|
+
let stdoutFd=null,stderrFd=null,result;
|
|
196
|
+
try{
|
|
197
|
+
let stdio;
|
|
198
|
+
if(discardOutput) stdio=['ignore','ignore','ignore'];
|
|
199
|
+
else if(redirectOutput){
|
|
200
|
+
stdoutFd=openSync(redirectOutput.stdout_path,'w');
|
|
201
|
+
stderrFd=openSync(redirectOutput.stderr_path,'w');
|
|
202
|
+
stdio=['ignore',stdoutFd,stderrFd];
|
|
203
|
+
}else stdio=capture?['ignore','pipe','pipe']:'inherit';
|
|
204
|
+
const options={stdio,encoding:capture?'utf8':undefined,windowsHide:false,shell:false,
|
|
205
|
+
env:windowsPowerShellEnvironment(probeMode?{...process.env,DEADBYTE_REMOTE_PROBE_MODE:probeMode}:process.env)};
|
|
206
|
+
if(Number.isInteger(timeoutMs)&&timeoutMs>0) options.timeout=timeoutMs;
|
|
207
|
+
result=spawnSync(ctx.powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.launcher,command,...(json?['-Json']:[])],options);
|
|
208
|
+
}finally{
|
|
209
|
+
if(stdoutFd!==null) closeSync(stdoutFd);
|
|
210
|
+
if(stderrFd!==null) closeSync(stderrFd);
|
|
211
|
+
}
|
|
212
|
+
if(result.error){
|
|
213
|
+
if(result.error?.code==='ETIMEDOUT') throw new Error(`remote controller '${command}' timed out after ${timeoutMs}ms`);
|
|
214
|
+
throw result.error;
|
|
215
|
+
}
|
|
216
|
+
const status=Number.isInteger(result.status)?result.status:1;
|
|
217
|
+
const stdout=capture?String(result.stdout||''):'';
|
|
218
|
+
const stderr=capture?String(result.stderr||''):'';
|
|
219
|
+
if(capture&&echoCaptured){if(stdout) process.stdout.write(stdout);if(stderr) process.stderr.write(stderr);}
|
|
220
|
+
if(status!==0&&!allowFailure) throw new Error(`remote controller '${command}' failed exit=${status}`);
|
|
221
|
+
return {status,stdout,stderr};
|
|
222
|
+
}
|
|
223
|
+
function observedAuthority(ctx){
|
|
224
|
+
const result=invokeController(ctx,'status',{capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS,json:true});
|
|
225
|
+
return parseAuthorityObservation(result.stdout);
|
|
226
|
+
}
|
|
227
|
+
function controllerRuntimeReady(text){
|
|
228
|
+
const value=String(text||'');
|
|
229
|
+
return /^DEADBYTE MCP\s*=\s*ONLINE\s*$/m.test(value)&&
|
|
230
|
+
/^Bridge PID\s*:\s*\d+\s+\[verified\]\s*$/m.test(value)&&
|
|
231
|
+
/^Cloud PID\s*:\s*\d+\s+\[verified\]\s*$/m.test(value)&&
|
|
232
|
+
/^Local ready\s*:\s*True\s*$/m.test(value)&&
|
|
233
|
+
/^Loop supervisor:\s*RUNNING\s+\[verified\]\s*$/m.test(value);
|
|
234
|
+
}
|
|
235
|
+
function controllerRuntimeDegraded(text){
|
|
236
|
+
return /^DEADBYTE MCP\s*=\s*(?:DEGRADED|FAILED)\s*$/m.test(String(text||''));
|
|
237
|
+
}
|
|
238
|
+
async function listLogFiles(root){
|
|
239
|
+
const auditDir=path.join(root,'audit');
|
|
240
|
+
const runtimeDir=path.join(root,'runtime','logs');
|
|
241
|
+
let auditNames=[],runtimeNames=[];
|
|
242
|
+
try{auditNames=await readdir(auditDir);}catch(error){if(error?.code!=='ENOENT') throw error;}
|
|
243
|
+
try{runtimeNames=await readdir(runtimeDir);}catch(error){if(error?.code!=='ENOENT') throw error;}
|
|
244
|
+
const selected=selectLiveLogNames({auditNames,runtimeNames});
|
|
245
|
+
return [
|
|
246
|
+
...selected.audit.map(name=>path.join(auditDir,name)),
|
|
247
|
+
...selected.runtime.map(name=>path.join(runtimeDir,name))
|
|
248
|
+
];
|
|
249
|
+
}
|
|
250
|
+
function parseAuditLogLine(file,line){
|
|
251
|
+
if(!/^segment-\d{8}\.jsonl$/.test(path.basename(file))||!line) return null;
|
|
252
|
+
try{return JSON.parse(line);}catch{return null;}
|
|
253
|
+
}
|
|
254
|
+
function formatLogLine(file,line,{auditEvent=null,activityTracker=null,sequence=null,sourceSequence=null,verbose=false,displayMode='unicode'}={}){
|
|
255
|
+
if(!line) return null;
|
|
256
|
+
if(/^segment-\d{8}\.jsonl$/.test(path.basename(file))){
|
|
257
|
+
const event=auditEvent??parseAuditLogLine(file,line);
|
|
258
|
+
if(!event) return null;
|
|
259
|
+
return activityTracker?activityTracker.observe(event,{sequence,sourceSequence}):formatCompactAuditEvent(event);
|
|
260
|
+
}
|
|
261
|
+
return formatRuntimeDiagnosticLine(file,line,{verbose,displayMode});
|
|
262
|
+
}
|
|
263
|
+
function remoteResultJournalPath(root,sessionId){return path.join(root,'runtime','remote-results',`${sessionId}.json`);}
|
|
264
|
+
function auditTimelineRecord(file,line,auditEvent=null){
|
|
265
|
+
const event=auditEvent??parseAuditLogLine(file,line);
|
|
266
|
+
if(!event) return null;
|
|
267
|
+
if(typeof event?.request_id!=='string'||!event.request_id||typeof event.tool!=='string'||!event.tool) return null;
|
|
268
|
+
const stamp=[event.ts,event.created_at_utc,event.observed_at_utc].find(value=>typeof value==='string'&&Number.isFinite(Date.parse(value)));
|
|
269
|
+
const observed=stamp?new Date(Date.parse(stamp)).toISOString():new Date().toISOString();
|
|
270
|
+
const payload_sha256=createHash('sha256').update(Buffer.from(line,'utf8')).digest('hex');
|
|
271
|
+
if(event.event_type==='tool.request') return {event_type:'tool.call',request_id:event.request_id,tool:event.tool,
|
|
272
|
+
observed_at_utc:observed,arguments:event.args??{},payload_sha256};
|
|
273
|
+
if(event.event_type==='tool.result'){
|
|
274
|
+
const classified=classifyRemoteAuditOutcome(event);
|
|
275
|
+
const unknown=classified==='unknown';
|
|
276
|
+
const recoveryReason=unknown&&typeof event.outcome?.recovery?.reason==='string'?event.outcome.recovery.reason:null;
|
|
277
|
+
return {event_type:'tool.result',request_id:event.request_id,tool:event.tool,
|
|
278
|
+
status:classified==='pending'?'pending':classified==='ok'?'ok':'error',observed_at_utc:observed,payload_sha256,
|
|
279
|
+
...(Number.isFinite(Number(event.outcome?.duration_ms))?{duration_ms:Number(event.outcome.duration_ms)}:{}),
|
|
280
|
+
...(unknown?{side_effect_state:'unknown'}:{}),
|
|
281
|
+
...(typeof event.outcome?.reason==='string'&&event.outcome.reason?{reason:event.outcome.reason}:(recoveryReason?{reason:recoveryReason}:
|
|
282
|
+
(classified==='pending'&&typeof event.outcome?.status==='string'?{reason:event.outcome.status}:{})))};
|
|
283
|
+
}
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
async function replayRemoteResults(root,cursor,{displayMode='unicode'}={}){
|
|
287
|
+
const parsed=parseRemoteResultCursor(cursor);
|
|
288
|
+
const file=remoteResultJournalPath(root,parsed.session_id);
|
|
289
|
+
await requireRegular(file,'remote result journal');
|
|
290
|
+
const journal=await openRemoteResultJournal({filePath:file,sessionId:parsed.session_id,maxEntries:2048});
|
|
291
|
+
let next=cursor;
|
|
292
|
+
for(;;){
|
|
293
|
+
const page=await journal.replayAfter(next,{limit:200});
|
|
294
|
+
for(const event of page.events){
|
|
295
|
+
if(event.event_type==='tool.call'){
|
|
296
|
+
const line=formatRemoteToolCall({event_type:'tool.request',tool:event.tool,request_id:event.request_id,
|
|
297
|
+
observed_at_utc:event.observed_at_utc,args:event.arguments},{displayMode});
|
|
298
|
+
process.stdout.write(remoteDisplayText(`${line} | REPLAY #${event.seq}\n`,displayMode));
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const unknown=event.side_effect_state==='unknown';
|
|
302
|
+
const marker=unknown?'OUTCOME_UNKNOWN':event.status==='pending'?'PENDING':event.status==='error'?(displayMode==='ascii'?'ERR':'✗'):(displayMode==='ascii'?'OK':'✓');
|
|
303
|
+
const duration=Number.isFinite(Number(event.duration_ms))?` ${event.duration_ms<1000?Math.round(event.duration_ms)+'ms':(event.duration_ms/1000).toFixed(2)+'s'}`:'';
|
|
304
|
+
const reason=(event.status==='error'||event.status==='pending'||unknown)&&typeof event.reason==='string'&&event.reason?` — ${event.reason}`:'';
|
|
305
|
+
process.stdout.write(remoteDisplayText(`[REPLAY] #${event.seq} ${event.tool}${duration} ${marker}${reason}\n`,displayMode));
|
|
306
|
+
}
|
|
307
|
+
next=page.next_cursor;
|
|
308
|
+
if(!page.has_more) return next;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
async function readAppend(file,offset){
|
|
312
|
+
let info;try{info=await stat(file);}catch(error){if(error?.code==='ENOENT') return {offset:0,text:''};throw error;}
|
|
313
|
+
let start=offset;
|
|
314
|
+
const reset=info.size<start;
|
|
315
|
+
if(reset) start=0;
|
|
316
|
+
if(info.size===start) return {offset:start,text:''};
|
|
317
|
+
const handle=await open(file,'r');
|
|
318
|
+
let encoding=null,buffer,bytesRead=0;
|
|
319
|
+
try{
|
|
320
|
+
const header=Buffer.alloc(2);await handle.read(header,0,2,0);
|
|
321
|
+
if(header[0]===0xff&&header[1]===0xfe&&/\.log$/i.test(file)){
|
|
322
|
+
encoding='utf16le';if(start%2!==0)start=Math.max(0,start-1);
|
|
323
|
+
}
|
|
324
|
+
const length=Math.min(1024*1024,info.size-start);
|
|
325
|
+
if(length===0)return {offset:start,text:''};
|
|
326
|
+
buffer=Buffer.alloc(length);
|
|
327
|
+
({bytesRead}=await handle.read(buffer,0,length,start));
|
|
328
|
+
}finally{await handle.close();}
|
|
329
|
+
const complete=safeRemoteLogPrefixLength(buffer.subarray(0,bytesRead),encoding??'utf8');
|
|
330
|
+
return {offset:start+complete,text:decodeRemoteLogBytes(file,buffer.subarray(0,complete),{encoding}),reset};
|
|
331
|
+
}
|
|
332
|
+
async function snapshotRemoteLogOffsets(root){
|
|
333
|
+
const offsets=new Map();
|
|
334
|
+
const files=await listLogFiles(root);
|
|
335
|
+
for(const file of files){
|
|
336
|
+
try{offsets.set(file,(await stat(file)).size);}catch{}
|
|
337
|
+
}
|
|
338
|
+
return offsets;
|
|
339
|
+
}
|
|
340
|
+
function timelineFilterEvent(timeline){
|
|
341
|
+
if(!timeline) return null;
|
|
342
|
+
if(timeline.event_type==='tool.call') return {
|
|
343
|
+
event_type:'tool.request',request_id:timeline.request_id,tool:timeline.tool,args:timeline.arguments??{}
|
|
344
|
+
};
|
|
345
|
+
return {
|
|
346
|
+
event_type:'tool.result',request_id:timeline.request_id,tool:timeline.tool,
|
|
347
|
+
outcome:{
|
|
348
|
+
is_error:timeline.status==='error',
|
|
349
|
+
...(Number.isFinite(Number(timeline.duration_ms))?{duration_ms:Number(timeline.duration_ms)}:{}),
|
|
350
|
+
...(timeline.side_effect_state?{side_effect_state:timeline.side_effect_state}:{}),
|
|
351
|
+
...(timeline.reason?{reason:timeline.reason}:{})
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTracker=null,timelineFilter=null,timelineWriter=null,verbose=false,displayMode='unicode'}={}){
|
|
356
|
+
process.stdout.write(verbose
|
|
357
|
+
? '[LOG] Verbose live timeline. Full sanitized request detail enabled; Ctrl+C disconnects and revokes authority.\n'
|
|
358
|
+
: '[LOG] Human live timeline. Transport IN/OUT hidden; full replay/audit detail remains on disk. START/DONE retained; heartbeat bursts folded; Ctrl+C disconnects and revokes authority.\n');
|
|
359
|
+
const activeOffsets=offsets??await snapshotRemoteLogOffsets(root);
|
|
360
|
+
const partialLines=new Map();
|
|
361
|
+
let liveFiles=[...activeOffsets.keys()];
|
|
362
|
+
let nextDiscoveryAt=0;
|
|
363
|
+
while(!signal.aborted){
|
|
364
|
+
const now=Date.now();
|
|
365
|
+
if(now>=nextDiscoveryAt){
|
|
366
|
+
liveFiles=await listLogFiles(root);
|
|
367
|
+
nextDiscoveryAt=now+LIVE_LOG_DISCOVERY_INTERVAL_MS;
|
|
368
|
+
}
|
|
369
|
+
for(const file of liveFiles){
|
|
370
|
+
const prior=activeOffsets.has(file)?activeOffsets.get(file):0;
|
|
371
|
+
const next=await readAppend(file,prior);
|
|
372
|
+
activeOffsets.set(file,next.offset);
|
|
373
|
+
if(next.reset)partialLines.delete(file);
|
|
374
|
+
if(!next.text)continue;
|
|
375
|
+
const lines=((partialLines.get(file)??'')+next.text).split(/\r?\n/);
|
|
376
|
+
partialLines.set(file,lines.pop()??'');
|
|
377
|
+
if(Buffer.byteLength(partialLines.get(file),'utf8')>1024*1024) throw new Error(`remote log line exceeds bounded decode limit: ${path.basename(file)}`);
|
|
378
|
+
for(const line of lines){
|
|
379
|
+
if(Buffer.byteLength(line,'utf8')>1024*1024) throw new Error(`remote log line exceeds bounded decode limit: ${path.basename(file)}`);
|
|
380
|
+
const auditEvent=parseAuditLogLine(file,line);
|
|
381
|
+
const timeline=auditTimelineRecord(file,line,auditEvent);
|
|
382
|
+
let recorded=null;
|
|
383
|
+
if(timeline&&journal) recorded=timeline.event_type==='tool.call'
|
|
384
|
+
? await journal.recordCall(timeline)
|
|
385
|
+
: await journal.recordTerminal(timeline);
|
|
386
|
+
if(recorded?.duplicate) continue;
|
|
387
|
+
const accepted=timelineFilter&&timeline?timelineFilter.accept(timelineFilterEvent(timeline)):true;
|
|
388
|
+
const formatted=formatLogLine(file,line,{auditEvent,activityTracker,sequence:recorded?.event?.seq??null,
|
|
389
|
+
sourceSequence:Number.isSafeInteger(auditEvent?.seq)?auditEvent.seq:null,verbose,displayMode});
|
|
390
|
+
if(!formatted||!accepted) continue;
|
|
391
|
+
const output=Array.isArray(formatted)?formatted:[formatted];
|
|
392
|
+
for(const item of output) if(item){
|
|
393
|
+
if(timelineWriter) timelineWriter.write(item,{event_type:timeline?.event_type??null,request_id:timeline?.request_id??null});
|
|
394
|
+
else process.stdout.write(`${item}\n`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
try{await sleep(250,null,{signal});}catch(error){if(error?.name!=='AbortError') throw error;}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function remoteTag(kind,displayMode){
|
|
402
|
+
const unicode={warn:'⚠️ [WARN]',recovery:'♻️ [RECOVERY]',auth:'🔐 [AUTH]'};
|
|
403
|
+
const ascii={warn:'[WARN]',recovery:'[RECOVERY]',auth:'[AUTH]'};
|
|
404
|
+
return (displayMode==='ascii'?ascii:unicode)[kind];
|
|
405
|
+
}
|
|
406
|
+
function writeRemote(stream,text,displayMode){stream.write(remoteDisplayText(text,displayMode));}
|
|
407
|
+
async function cleanupRemoteSession(ctx,{displayMode='unicode'}={}){
|
|
408
|
+
const failed=[];
|
|
409
|
+
for(const command of SESSION_CLEANUP){
|
|
410
|
+
try{
|
|
411
|
+
const result=invokeController(ctx,command,{allowFailure:true,capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
412
|
+
if(result.status!==0){failed.push(command);writeRemote(process.stderr,`${remoteTag('warn',displayMode)} cleanup ${command} exit=${result.status}\n`,displayMode);}
|
|
413
|
+
}
|
|
414
|
+
catch(error){failed.push(command);writeRemote(process.stderr,`${remoteTag('warn',displayMode)} cleanup ${command}: ${String(error?.message||error)}\n`,displayMode);}
|
|
415
|
+
}
|
|
416
|
+
if(failed.length) throw new Error(`remote session cleanup failed; ownership marker retained: ${failed.join(',')}`);
|
|
417
|
+
let snapshot;
|
|
418
|
+
try{snapshot=assertAuthorityVector(observedAuthority(ctx),Object.fromEntries(AUTHORITY_PLANES.map(plane=>[plane,false])));}
|
|
419
|
+
catch(error){throw new Error(`remote session cleanup verification failed; ownership marker retained: ${String(error?.message||error)}`);}
|
|
420
|
+
if(snapshot.state==='ONLINE'){
|
|
421
|
+
try{invokeController(ctx,'machinetest',{capture:true,echoCaptured:false,timeoutMs:REMOTE_START_TIMEOUT_MS,probeMode:'revoked'});}
|
|
422
|
+
catch(error){throw new Error(`remote session enforcement cleanup verification failed; ownership marker retained: ${String(error?.message||error)}`);}
|
|
423
|
+
}
|
|
424
|
+
const parked=invokeController(ctx,'park',{allowFailure:true,capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
425
|
+
if(parked.status!==0) throw new Error(`remote session park failed exit=${parked.status}; ownership marker retained`);
|
|
426
|
+
}
|
|
427
|
+
const GUARDIAN_SCRIPT=fileURLToPath(new URL('./remote-session-guardian.mjs',import.meta.url));
|
|
428
|
+
function remoteSessionMarker(root){return path.join(root,'runtime','remote-session.json');}
|
|
429
|
+
function pidAlive(pid){
|
|
430
|
+
if(!Number.isInteger(pid)||pid<1)return false;
|
|
431
|
+
try{process.kill(pid,0);return true;}
|
|
432
|
+
catch(error){return error?.code!=='ESRCH';}
|
|
433
|
+
}
|
|
434
|
+
function remoteOwnerPid(marker){return marker?.schema==='deadbyte.remote-session.v2'?marker.owner_pid:marker?.pid;}
|
|
435
|
+
async function readRemoteSessionMarker(root){
|
|
436
|
+
const file=remoteSessionMarker(root);
|
|
437
|
+
let text;try{text=await readFile(file,'utf8');}catch(error){if(error?.code==='ENOENT') return null;throw error;}
|
|
438
|
+
let value;try{value=JSON.parse(text);}catch{throw new Error('remote session marker is invalid JSON');}
|
|
439
|
+
if(value?.schema==='deadbyte.remote-session.v1'){
|
|
440
|
+
if(!Number.isInteger(value?.pid)||value.pid<1) throw new Error('remote session marker schema invalid');
|
|
441
|
+
return value;
|
|
442
|
+
}
|
|
443
|
+
if(value?.schema==='deadbyte.remote-session.v2'){
|
|
444
|
+
for(const key of ['owner_identity','guardian_identity'])if(value[key]!==undefined&&!validForegroundIdentity(value[key]))throw new Error('remote session ownership identity invalid');
|
|
445
|
+
if(!Number.isInteger(value?.owner_pid)||value.owner_pid<1||
|
|
446
|
+
!/^[0-9a-f]{64}$/.test(String(value?.session_nonce||''))||
|
|
447
|
+
!Number.isInteger(value?.guardian_pid)||value.guardian_pid<0||
|
|
448
|
+
(value.result_session_id!==undefined&&!/^[0-9a-f]{24}$/.test(String(value.result_session_id)))) throw new Error('remote session marker schema invalid');
|
|
449
|
+
return value;
|
|
450
|
+
}
|
|
451
|
+
throw new Error('remote session marker schema invalid');
|
|
452
|
+
}
|
|
453
|
+
async function recoverRemoteSession(ctx,{displayMode='unicode'}={}){
|
|
454
|
+
let prior=await readRemoteSessionMarker(ctx.root);
|
|
455
|
+
if(!prior) return;
|
|
456
|
+
const ownerPid=remoteOwnerPid(prior);
|
|
457
|
+
if(pidAlive(ownerPid)){
|
|
458
|
+
if(!prior.owner_identity)throw new Error(`remote session ownership unverified for legacy pid=${ownerPid}; marker retained`);
|
|
459
|
+
if(prior.owner_identity.pid!==ownerPid)throw new Error('remote session owner identity PID binding mismatch');
|
|
460
|
+
if(foregroundIdentityMatches(prior.owner_identity,queryForegroundIdentity(ownerPid)))throw new Error(`remote session already active pid=${ownerPid}`);
|
|
461
|
+
}
|
|
462
|
+
let guardianAlive=prior.schema==='deadbyte.remote-session.v2'&&pidAlive(prior.guardian_pid);
|
|
463
|
+
if(guardianAlive){
|
|
464
|
+
if(!prior.guardian_identity)throw new Error('remote guardian ownership unverified; marker retained');
|
|
465
|
+
if(prior.guardian_identity.pid!==prior.guardian_pid)throw new Error('remote guardian identity PID binding mismatch');
|
|
466
|
+
guardianAlive=foregroundIdentityMatches(prior.guardian_identity,queryForegroundIdentity(prior.guardian_pid));
|
|
467
|
+
}
|
|
468
|
+
if(guardianAlive){
|
|
469
|
+
const deadline=Date.now()+3000;
|
|
470
|
+
while(Date.now()<deadline){
|
|
471
|
+
await sleep(100);
|
|
472
|
+
prior=await readRemoteSessionMarker(ctx.root);
|
|
473
|
+
if(!prior) return;
|
|
474
|
+
if(remoteOwnerPid(prior)!==ownerPid) throw new Error('remote session ownership changed during guardian recovery');
|
|
475
|
+
}
|
|
476
|
+
throw new Error(`remote session guardian cleanup still active pid=${prior.guardian_pid}`);
|
|
477
|
+
}
|
|
478
|
+
writeRemote(process.stdout,`${remoteTag('recovery',displayMode)} Stale remote session pid=${ownerPid}; revoking authority before restart.\n`,displayMode);
|
|
479
|
+
await cleanupRemoteSession(ctx,{displayMode});
|
|
480
|
+
await rm(remoteSessionMarker(ctx.root),{force:true});
|
|
481
|
+
}
|
|
482
|
+
async function claimRemoteSession(ctx){
|
|
483
|
+
const dir=path.join(ctx.root,'runtime');await mkdir(dir,{recursive:true});
|
|
484
|
+
const nonce=randomBytes(32).toString('hex');
|
|
485
|
+
const resultSessionId=randomBytes(12).toString('hex');
|
|
486
|
+
const marker={schema:'deadbyte.remote-session.v2',owner_pid:process.pid,guardian_pid:0,session_nonce:nonce,
|
|
487
|
+
result_session_id:resultSessionId,started_at_utc:new Date().toISOString()};
|
|
488
|
+
marker.owner_identity=queryForegroundIdentity(process.pid);
|
|
489
|
+
if(!marker.owner_identity)throw new Error('remote foreground identity unavailable');
|
|
490
|
+
const markerPath=remoteSessionMarker(ctx.root);
|
|
491
|
+
try{await writeFile(markerPath,`${JSON.stringify(marker)}\n`,{encoding:'utf8',flag:'wx'});}
|
|
492
|
+
catch(error){if(error?.code==='EEXIST') throw new Error('remote session already claimed');throw error;}
|
|
493
|
+
let guardian=null;
|
|
494
|
+
try{
|
|
495
|
+
guardian=spawn(process.execPath,[GUARDIAN_SCRIPT,'--state-root',ctx.root,'--owner-pid',String(process.pid),'--session-nonce',nonce,'--owner-channel','ipc'],{
|
|
496
|
+
detached:true,stdio:['ignore','ignore','ignore','ipc'],windowsHide:true,shell:false,env:process.env
|
|
497
|
+
});
|
|
498
|
+
if(!Number.isInteger(guardian.pid)||guardian.pid<1) throw new Error('remote session guardian did not start');
|
|
499
|
+
marker.guardian_pid=guardian.pid;
|
|
500
|
+
marker.guardian_identity=queryForegroundIdentity(guardian.pid);
|
|
501
|
+
if(!marker.guardian_identity||marker.guardian_identity.parent_pid!==process.pid)throw new Error('remote guardian parent identity mismatch');
|
|
502
|
+
await writeFile(markerPath,`${JSON.stringify(marker)}\n`,'utf8');
|
|
503
|
+
guardian.unref();
|
|
504
|
+
guardian.channel?.unref();
|
|
505
|
+
return marker;
|
|
506
|
+
}catch(error){
|
|
507
|
+
try{guardian?.kill();}catch{}
|
|
508
|
+
await rm(markerPath,{force:true});
|
|
509
|
+
throw error;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
async function releaseRemoteSession(ctx,marker,{displayMode='unicode'}={}){
|
|
513
|
+
let current=null;try{current=await readRemoteSessionMarker(ctx.root);}catch(error){writeRemote(process.stderr,`${remoteTag('warn',displayMode)} remote session marker cleanup: ${String(error?.message||error)}\n`,displayMode);return;}
|
|
514
|
+
const same=current?.schema==='deadbyte.remote-session.v2'&¤t.owner_pid===marker.owner_pid&¤t.session_nonce===marker.session_nonce;
|
|
515
|
+
if(same) await rm(remoteSessionMarker(ctx.root),{force:true});
|
|
516
|
+
}
|
|
517
|
+
async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,filterExpression='',mode='default'}={}){
|
|
518
|
+
const display=prepareRemoteConsole();
|
|
519
|
+
const displayMode=display.mode;
|
|
520
|
+
const ctx=await remoteContext();
|
|
521
|
+
if(displayMode==='ascii') writeRemote(process.stdout,`[DISPLAY] ASCII fallback: ${display.reason}\n`,displayMode);
|
|
522
|
+
if(resumeCursor!==null) await replayRemoteResults(ctx.root,resumeCursor,{displayMode});
|
|
523
|
+
const startupLock=await acquireForegroundSessionLock({root:ctx.root,powershell:ctx.powershell});
|
|
524
|
+
let marker;
|
|
525
|
+
try{
|
|
526
|
+
startupLock.assertHeld();
|
|
527
|
+
await recoverRemoteSession(ctx,{displayMode});
|
|
528
|
+
startupLock.assertHeld();
|
|
529
|
+
marker=await claimRemoteSession(ctx);
|
|
530
|
+
startupLock.assertHeld();
|
|
531
|
+
}finally{await startupLock.release();}
|
|
532
|
+
try{
|
|
533
|
+
await synchronizeForegroundRuntime(ctx,{displayMode});
|
|
534
|
+
}catch(error){
|
|
535
|
+
await cleanupRemoteSession(ctx,{displayMode});
|
|
536
|
+
await releaseRemoteSession(ctx,marker,{displayMode});
|
|
537
|
+
throw error;
|
|
538
|
+
}
|
|
539
|
+
const resultJournal=await openRemoteResultJournal({
|
|
540
|
+
filePath:remoteResultJournalPath(ctx.root,marker.result_session_id),sessionId:marker.result_session_id,maxEntries:2048
|
|
541
|
+
});
|
|
542
|
+
const rootPaths=await readLiveRootPaths(ctx.root);
|
|
543
|
+
const activityTracker=createLiveTimelineTracker({verbose,displayMode,rootPaths,trace,foldHeartbeats:!verbose});
|
|
544
|
+
const timelineFilter=filterExpression?createLiveTimelineFilter(filterExpression):null;
|
|
545
|
+
const timelineWriter=createLiveTimelineWriter({stream:process.stdout,displayMode,rewrite:false});
|
|
546
|
+
const aborter=new AbortController();
|
|
547
|
+
const abort=()=>aborter.abort();
|
|
548
|
+
const watchStdin=!process.stdin.isTTY;
|
|
549
|
+
process.once('SIGINT',abort);process.once('SIGTERM',abort);
|
|
550
|
+
if(watchStdin){process.stdin.once('end',abort);process.stdin.once('close',abort);process.stdin.resume();}
|
|
551
|
+
let ready=false;
|
|
552
|
+
let logTask=null;
|
|
553
|
+
try{
|
|
554
|
+
process.stdout.write('DEADBYTE MCP\n');
|
|
555
|
+
process.stdout.write(`${formatRemoteLifecycle('device_start',{displayMode})}\n`);
|
|
556
|
+
process.stdout.write(`${formatRemoteLifecycle(resumeCursor!==null?'session_restored':'session_new',{displayMode})}\n`);
|
|
557
|
+
const logOffsets=await snapshotRemoteLogOffsets(ctx.root);
|
|
558
|
+
logTask=tailRemoteLogs(ctx.root,aborter.signal,{journal:resultJournal,offsets:logOffsets,activityTracker,timelineFilter,timelineWriter,verbose,displayMode});
|
|
559
|
+
process.stdout.write('[BOOT] Starting local + remote runtime...\n');
|
|
560
|
+
const preflight=invokeController(ctx,'status',{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
561
|
+
let startText=preflight.stdout||'';
|
|
562
|
+
if(preflight.status===0&&controllerRuntimeReady(preflight.stdout)){
|
|
563
|
+
process.stdout.write('[BOOT] Runtime already ONLINE; reusing verified bridge/cloud/supervisor.\n');
|
|
564
|
+
}else{
|
|
565
|
+
const recovery=preflight.status===0&&controllerRuntimeDegraded(preflight.stdout);
|
|
566
|
+
process.stdout.write(recovery?'[RECOVERY] Runtime failed readiness; restarting verified owned components...\n':'[BOOT] Starting bridge/runtime...\n');
|
|
567
|
+
const startCommand=recovery?'restart':'start';
|
|
568
|
+
const controllerLogDir=path.join(ctx.root,'runtime','logs');
|
|
569
|
+
await mkdir(controllerLogDir,{recursive:true});
|
|
570
|
+
const launchOutput={
|
|
571
|
+
stdout_path:path.join(controllerLogDir,`controller-${startCommand}.stdout.log`),
|
|
572
|
+
stderr_path:path.join(controllerLogDir,`controller-${startCommand}.stderr.log`)
|
|
573
|
+
};
|
|
574
|
+
const launch=invokeController(ctx,startCommand,{allowFailure:true,redirectOutput:launchOutput,timeoutMs:REMOTE_START_TIMEOUT_MS});
|
|
575
|
+
if(launch.status!==0){
|
|
576
|
+
const evidence=await readControllerStartFailure(ctx.root,startCommand);
|
|
577
|
+
const fallback=evidence?'':await readTailBounded(launchOutput.stderr_path,2048);
|
|
578
|
+
const detail=evidence?`; controller-start-failure: ${evidence.message}`:(fallback?`; controller-stderr: ${fallback}`:'');
|
|
579
|
+
throw new Error(`remote controller '${startCommand}' failed exit=${launch.status}${detail}`);
|
|
580
|
+
}
|
|
581
|
+
const started=invokeController(ctx,'status',{capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
582
|
+
if(started.status!==0||!controllerRuntimeReady(started.stdout)) throw new Error(`remote controller ${recovery?'restart':'start'} returned without a verified ONLINE runtime`);
|
|
583
|
+
startText=started.stdout||'';
|
|
584
|
+
}
|
|
585
|
+
process.stdout.write(`${formatRemoteLifecycle('bridge_connected',{displayMode})}\n`);
|
|
586
|
+
process.stdout.write(`${formatRemoteLifecycle('cloud_connected',{displayMode})}\n`);
|
|
587
|
+
const desired=authorityVector(mode);
|
|
588
|
+
const priorAuthority=observedAuthority(ctx);
|
|
589
|
+
for(const plane of ['host','desktop','autonomous','coding','process'].filter(name=>!desired[name]&&priorAuthority.authority[name].armed)){
|
|
590
|
+
const command=`disarm${plane}`;
|
|
591
|
+
writeRemote(process.stdout,`${remoteTag('auth',displayMode)} Revoking unwanted ${plane} authority...\n`,displayMode);
|
|
592
|
+
invokeController(ctx,command,{capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
593
|
+
}
|
|
594
|
+
for(const plane of AUTHORITY_PLANES.filter(name=>desired[name])){
|
|
595
|
+
const command=`arm${plane}`;
|
|
596
|
+
writeRemote(process.stdout,`${remoteTag('auth',displayMode)} Arming ${command.slice(3)}...\n`,displayMode);
|
|
597
|
+
invokeController(ctx,command,{capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
598
|
+
}
|
|
599
|
+
const url=startText.match(/^URL\s*:\s*(\S+)/m)?.[1]||'';
|
|
600
|
+
writeRemote(process.stdout,`${remoteTag('auth',displayMode)} Verifying exact ${mode} authority vector...\n`,displayMode);
|
|
601
|
+
const current=await readActiveRuntimeBinding(ctx.root);
|
|
602
|
+
const authority=assertAuthorityVector(assertRuntimeReadyObservation(observedAuthority(ctx),{
|
|
603
|
+
coreRoot:current?.release_root??null
|
|
604
|
+
}),desired);
|
|
605
|
+
process.stdout.write('[BOOT] Verifying public signed MCP execution path...\n');
|
|
606
|
+
invokeController(ctx,'machinetest',{capture:true,echoCaptured:false,timeoutMs:REMOTE_START_TIMEOUT_MS,probeMode:mode==='default'?'revoked':mode});
|
|
607
|
+
ready=true;
|
|
608
|
+
writeRemote(process.stdout,`${remoteTag('auth',displayMode)} ${authorityBanner(authority)}\n`,displayMode);
|
|
609
|
+
process.stdout.write('[OK] ONLINE\n');
|
|
610
|
+
process.stdout.write(`${formatRemoteLifecycle('ready',{displayMode})}\n`);
|
|
611
|
+
if(url) writeRemote(process.stdout,`URL: ${url}\n`,displayMode);
|
|
612
|
+
await logTask;
|
|
613
|
+
}finally{
|
|
614
|
+
const wasAborted=aborter.signal.aborted;
|
|
615
|
+
aborter.abort();
|
|
616
|
+
if(logTask) await logTask.catch(()=>{});
|
|
617
|
+
timelineWriter.flush();
|
|
618
|
+
for(const line of activityTracker.reconcileInterrupted('session_end')) process.stdout.write(`${line}\n`);
|
|
619
|
+
const filterRetention=timelineFilter?.retention();
|
|
620
|
+
if(filterRetention?.evicted>0) process.stdout.write(`[WARN] LIVELOG filter evicted ${filterRetention.evicted} presentation identities; full audit remains on disk.\n`);
|
|
621
|
+
process.stdout.write(`${activityTracker.summary()}\n`);
|
|
622
|
+
process.removeListener('SIGINT',abort);process.removeListener('SIGTERM',abort);
|
|
623
|
+
if(watchStdin){process.stdin.removeListener('end',abort);process.stdin.removeListener('close',abort);process.stdin.pause();}
|
|
624
|
+
process.stdout.write(`${formatRemoteLifecycle('cleanup',{displayMode})}\n`);
|
|
625
|
+
if(ready||!wasAborted) process.stdout.write('[INFO] Disconnecting; revoking authority...\n');
|
|
626
|
+
await cleanupRemoteSession(ctx,{displayMode});
|
|
627
|
+
await releaseRemoteSession(ctx,marker,{displayMode});
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
async function runRemote(args){
|
|
631
|
+
let verbose=false,trace=false,filterExpression='',resumeCursor=null,mode='default';
|
|
632
|
+
const positional=[];
|
|
633
|
+
for(let index=0;index<args.length;index++){
|
|
634
|
+
const arg=args[index];
|
|
635
|
+
if(arg==='--verbose'||arg==='-v'){verbose=true;continue;}
|
|
636
|
+
if(arg==='--trace'){trace=true;continue;}
|
|
637
|
+
if(['--operator','--desktop','--host','--full'].includes(arg)){
|
|
638
|
+
if(mode!=='default') throw new Error('remote authority modes are mutually exclusive');
|
|
639
|
+
mode=arg.slice(2);continue;
|
|
640
|
+
}
|
|
641
|
+
if(arg==='--filter'){
|
|
642
|
+
if(filterExpression) throw new Error('remote --filter may be specified once');
|
|
643
|
+
if(index+1>=args.length) throw new Error('remote --filter requires an expression');
|
|
644
|
+
filterExpression=args[++index];continue;
|
|
645
|
+
}
|
|
646
|
+
if(arg==='--resume'){
|
|
647
|
+
if(resumeCursor!==null) throw new Error('remote --resume may be specified once');
|
|
648
|
+
if(index+1>=args.length) throw new Error('remote --resume requires a cursor');
|
|
649
|
+
resumeCursor=args[++index];continue;
|
|
650
|
+
}
|
|
651
|
+
positional.push(arg);
|
|
652
|
+
}
|
|
653
|
+
const presentation=verbose||trace||Boolean(filterExpression)||resumeCursor!==null||mode!=='default';
|
|
654
|
+
if(positional.length===0){await runRemoteSession({resumeCursor,verbose,trace,filterExpression,mode});return;}
|
|
655
|
+
if(presentation) throw new Error('remote presentation flags are valid only for foreground session');
|
|
656
|
+
if(positional.length>1) throw new Error('remote accepts one controller command');
|
|
657
|
+
const command=positional[0];if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
|
|
658
|
+
const ctx=await remoteContext();
|
|
659
|
+
await synchronizeOneShotRuntime(ctx,{displayMode:'ascii'});
|
|
660
|
+
const result=invokeController(ctx,command,{allowFailure:true});
|
|
661
|
+
process.exitCode=result.status;
|
|
662
|
+
}
|
|
663
|
+
async function runSupervisor(args){
|
|
664
|
+
const action=args[0]||'status';
|
|
665
|
+
if(args.length>1||!Object.hasOwn(SUPERVISOR_COMMANDS,action)) throw new Error(`unsupported supervisor command '${action}'`);
|
|
666
|
+
const ctx=await remoteContext();
|
|
667
|
+
const result=invokeController(ctx,SUPERVISOR_COMMANDS[action],{allowFailure:true});
|
|
668
|
+
process.exitCode=result.status;
|
|
669
|
+
}
|
|
670
|
+
async function runAudit(args){
|
|
671
|
+
const command=args[0]||'status';if(args.length>1) throw new Error('audit accepts one command');
|
|
672
|
+
if(!['verify','status'].includes(command)) throw new Error(`unsupported audit command '${command}'`);
|
|
673
|
+
const result=await verifyAuditChain({stateRoot:stateRoot(),verifyKeyPath:process.env.DEADBYTE_VERIFY_KEY||defaultPublicKeyPath(),requireComplete:command==='verify'});
|
|
674
|
+
const output=command==='status'?{...result,mode:'status'}:result;
|
|
675
|
+
process.stdout.write(`${JSON.stringify(output,null,2)}\n`);
|
|
676
|
+
if(command==='verify'&&!result.ok) process.exitCode=1;
|
|
677
|
+
}
|
|
678
|
+
async function main(argv){
|
|
679
|
+
const [command,...rest]=argv;
|
|
680
|
+
if(command==='--version'||command==='-v'||command==='version'){
|
|
681
|
+
if(rest.length) throw new Error('version accepts no arguments');
|
|
682
|
+
process.stdout.write(`${DEADBYTE_VERSION}\n`);return;
|
|
683
|
+
}
|
|
684
|
+
if(!command||command==='help'||command==='--help'||command==='-h'){printHelp();return;}
|
|
685
|
+
if(command==='doctor'){
|
|
686
|
+
if(rest.length) throw new Error('doctor accepts no arguments');
|
|
687
|
+
await printDoctor();return;
|
|
688
|
+
}
|
|
689
|
+
if(command==='mcp'){
|
|
690
|
+
if(rest.length) throw new Error('mcp accepts no arguments');
|
|
691
|
+
await import('./mcp-server.mjs');
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
if(command==='remote'){await runRemote(rest);return;}
|
|
695
|
+
if(command==='supervisor'){await runSupervisor(rest);return;}
|
|
696
|
+
if(command==='audit'){await runAudit(rest);return;}
|
|
697
|
+
if(command==='diagnostics'){
|
|
698
|
+
if(rest.length!==3)throw new Error('diagnostics export --output or verify --input; no network upload supported');
|
|
699
|
+
if(rest[0]==='export'&&rest[1]==='--output'){
|
|
700
|
+
const report=await exportOfflineDiagnostics({stateRoot:stateRoot(),outputPath:rest[2]});
|
|
701
|
+
process.stdout.write(`${JSON.stringify(report)}\n`);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
if(rest[0]==='verify'&&rest[1]==='--input'){
|
|
705
|
+
const file=path.resolve(rest[2]);
|
|
706
|
+
await requireRegular(file,'diagnostic ZIP');
|
|
707
|
+
const report=verifyOfflineDiagnosticsZip(await readFile(file));
|
|
708
|
+
process.stdout.write(`${JSON.stringify(report)}\n`);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
throw new Error('unsupported diagnostics mode; network upload is forbidden');
|
|
712
|
+
}
|
|
713
|
+
if(command==='inspect'){
|
|
714
|
+
const result=await runNativeInspect(rest);
|
|
715
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
716
|
+
if(result.status!=='ok')process.exitCode=2;
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
throw new Error(`unknown command '${command}'`);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
main(process.argv.slice(2)).catch(error=>{process.stderr.write(`DEADBYTE CLI: ${String(error?.message||error)}\n`);process.exitCode=1;});
|