deadbyte-mcp 0.15.61 → 0.15.62

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.
Files changed (78) hide show
  1. package/MANIFEST.SHA256 +76 -31
  2. package/PROVENANCE.json +27 -8
  3. package/README.txt +18 -18
  4. package/RELEASE.json +44 -15
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +24 -2
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-controller.ps1 +78 -1
  9. package/controller/deadbyte-process-policy.json +4 -4
  10. package/docs/R156-LOOP-AND-EPOCH-ROUTING.md +86 -0
  11. package/docs/R157-ARCHITECTURE-DEPENDENCIES.md +58 -0
  12. package/docs/R157-ARCHITECTURE-GRAPH.json +2874 -0
  13. package/docs/R157-ENGINEERING-HANDOFF.md +71 -0
  14. package/docs/R157-FILE-DELTA.json +80 -0
  15. package/docs/R157-FOCUSED-VERIFICATION.log +0 -0
  16. package/docs/R157-REFERENCE-CAPABILITY-MATRIX.md +207 -0
  17. package/docs/R157-REFERENCE-DECISIONS.md +46 -0
  18. package/docs/R157-REFERENCE-INVENTORY.json +2849 -0
  19. package/docs/R158-COMBINED-REGRESSION.log +0 -0
  20. package/docs/R158-DELTA.json +80 -0
  21. package/docs/R158-EPOCH-BOOTSTRAP-GREEN.log +0 -0
  22. package/docs/R158-EPOCH-BOOTSTRAP-RED.log +0 -0
  23. package/docs/R158-EPOCH-IDENTITY-RED.log +0 -0
  24. package/docs/R158-FOCUSED-GREEN.log +0 -0
  25. package/docs/R158-PROTECTED-EPOCH-VERIFIED.md +43 -0
  26. package/docs/R158-PROTECTED-EXACT-SOURCE-LIVE.log +0 -0
  27. package/docs/R158-UPDATER-REGRESSION.log +0 -0
  28. package/docs/R159-COMPLETED-HISTORY-BACKUP/deadbyte-cli.mjs.before +965 -0
  29. package/docs/R159-COMPLETED-HISTORY-BACKUP/legacy-marker-evidence-r159.mjs.before +102 -0
  30. package/docs/R159-COMPLETED-HISTORY-BACKUP/r159-legacy-marker-evidence.test.mjs.before +79 -0
  31. package/docs/R159-COMPLETED-HISTORY-PATCH.json +26 -0
  32. package/docs/R159-MARKER-LOCATOR-CHECKPOINT.md +46 -0
  33. package/docs/R160-ENGINEERING-HANDOFF.md +32 -0
  34. package/docs/R160-REFERENCE-CAPABILITY-MATRIX.json +165 -0
  35. package/package.json +2 -2
  36. package/scripts/gate-windows-r160.ps1 +57 -0
  37. package/scripts/npm-release-r48.mjs +5 -0
  38. package/scripts/npm-release-readiness-r163.mjs +67 -0
  39. package/src/autonomous-supervisor.mjs +21 -4
  40. package/src/deadbyte-cli.mjs +70 -50
  41. package/src/legacy-marker-evidence-r159.mjs +128 -0
  42. package/src/package-identity-r160.mjs +61 -0
  43. package/src/release-generation.mjs +13 -0
  44. package/src/release-slot.mjs +2 -1
  45. package/src/remote-live-log.mjs +30 -2
  46. package/src/runtime-update.mjs +18 -6
  47. package/src/trusted-epoch-bootstrap-r158.mjs +42 -0
  48. package/src/version.mjs +1 -1
  49. package/src/windows-powershell-environment.mjs +5 -2
  50. package/test/r106-trusted-powershell-spawn.test.mjs +1 -0
  51. package/test/r130-release-security-contract.test.mjs +9 -9
  52. package/test/r132-release-identity.test.mjs +8 -10
  53. package/test/r153-prepublish-identity.test.mjs +25 -4
  54. package/test/r153-staging-leaf-acl.test.mjs +5 -2
  55. package/test/r155-commander-live-log.test.mjs +44 -0
  56. package/test/r155-default-live-log.test.mjs +89 -0
  57. package/test/r156-epoch-route-smoke.ps1 +48 -0
  58. package/test/r156-supervisor-pacing.test.mjs +59 -0
  59. package/test/r157-architecture-map.mjs +61 -0
  60. package/test/r157-reference-survey.mjs +91 -0
  61. package/test/r157-supervisor-singleflight.test.mjs +32 -0
  62. package/test/r158-epoch-bootstrap.test.mjs +34 -0
  63. package/test/r159-legacy-marker-evidence.test.mjs +100 -0
  64. package/test/r159-legacy-marker-evidence.test.mjs.before-assertion-fix-20261025-120246.bak +100 -0
  65. package/test/r160-marker-proof.test.mjs +87 -0
  66. package/test/r160-package-decision-manifest.test.mjs +67 -0
  67. package/test/r160-release-contract.test.mjs +30 -0
  68. package/test/r160-stable-launcher.test.mjs +10 -0
  69. package/test/r163-isolated-slot-rollback-restore.test.mjs +58 -0
  70. package/test/r163-publish-fence.test.mjs +13 -0
  71. package/test/r48-release-contract.test.mjs +7 -1
  72. package/test/r58-launcher-shell.test.mjs +4 -1
  73. package/test/r59-tool-surface-compat.test.mjs +1 -1
  74. package/test/release-generation.test.mjs +16 -0
  75. package/test/remote-live-log-r37.test.mjs +5 -3
  76. package/test/remote-session-cli.test.mjs +11 -11
  77. package/test/stabilization-state-root.test.mjs +2 -1
  78. package/node_modules/which/CHANGELOG.md +0 -166
@@ -0,0 +1,965 @@
1
+ #!/usr/bin/env node
2
+ import { closeSync, openSync } from 'node:fs';
3
+ import { lstat, mkdir, open, readFile, readdir, 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, defaultForegroundTimelineOptions, formatRemoteLogResetNotice, 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,proveRestrictedPidReuse} from './foreground-process-identity.mjs';
25
+ import {readRemoteMarkerSnapshot,deletePinnedRemoteMarker} from './foreground-marker-cas-win32.mjs';
26
+ import {acquireForegroundSessionLock} from './foreground-session-lock.mjs';
27
+ import { AUTHORITY_PLANES, authorityVector, authorityRestorePlan, parseAuthorityObservation, assertAuthorityVector, assertRuntimeReadyObservation, authorityBanner } from './remote-authority.mjs';
28
+ import { decodeRemoteLogBytes, boundedRemoteDiagnostic, readRemoteLogAppend } from './remote-log-decode-r60.mjs';
29
+ import {reconcileStaleMarker,resumeMarkerReconciliation,recoverPublishedMarkerTerminal} from './clean-activation-recovery-r153.mjs';
30
+ import {selectLegacyMarkerEvidence} from './legacy-marker-evidence-r159.mjs';
31
+ import {normalizeHostCommandPins,encodeHostCommandPins} from './host-command-pins-r154.mjs';
32
+
33
+ const REMOTE_COMMANDS=new Set([
34
+ 'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
35
+ 'armprocess','disarmprocess','processstatus','armhost','disarmhost','hoststatus','hosttest',
36
+ 'armdesktop','disarmdesktop','desktopstatus',
37
+ 'armcoding','disarmcoding','codingstatus','codingtest','armautonomous','disarmautonomous','autonomousstatus','autonomoustest'
38
+ ]);
39
+ const REMOTE_STATUS_TIMEOUT_MS=30000;
40
+ const REMOTE_START_TIMEOUT_MS=150000;
41
+ const SESSION_CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost'];
42
+ const SUPERVISOR_COMMANDS=Object.freeze({
43
+ start:'supervisorstart',stop:'supervisorstop',status:'supervisorstatus',recover:'supervisorrecover'
44
+ });
45
+ const CURRENT_PACKAGE_ROOT=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
46
+ const CURRENT_DEPLOY_SCRIPT=path.join(CURRENT_PACKAGE_ROOT,'scripts','deploy-r48.mjs');
47
+ const UAC_BOOTSTRAP_SCRIPT=path.join(CURRENT_PACKAGE_ROOT,'scripts','bootstrap-uac-migration-r153.ps1');
48
+ const REMOTE_UPDATE_TIMEOUT_MS=900000;
49
+ const LIVE_LOG_DISCOVERY_INTERVAL_MS=1000;
50
+ const TRUSTED_WINDOWS_ROOT='C:\\Windows';
51
+
52
+ function stateRoot(){return resolveStateRoot();}
53
+ 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;}
54
+ function printHelp(){
55
+ const exact=`deadbyte-mcp@${DEADBYTE_VERSION}`;
56
+ process.stdout.write([
57
+ 'DEADBYTE MCP CLI','',
58
+ ` npx -y ${exact} doctor # verify current package/release truth`,
59
+ ` npx -y ${exact} mcp # stdio MCP server for local clients`,
60
+ ` npx -y ${exact} remote # safe observation-only foreground session`,
61
+ ` npx -y ${exact} remote --operator # process/coding/autonomous; explicit owner gates still apply`,
62
+ ` npx -y ${exact} remote --desktop # session with Desktop authority; Host remains disarmed`,
63
+ ` npx -y ${exact} remote --host # session with Host authority; Desktop remains disarmed`,
64
+ ` npx -y ${exact} remote --full # explicit five-plane foreground session`,
65
+ ` npx -y ${exact} remote --resume <cursor> # replay missed results, then reconnect`,
66
+ ` npx -y ${exact} remote --trace # show trace/span identity without payload detail`,
67
+
68
+ ` npx -y ${exact} remote --filter <expr> # presentation filter: tool/category/status/trace`,
69
+ ` npx -y ${exact} remote status`,
70
+ ` npx -y ${exact} remote restart`,
71
+ ` npx -y ${exact} supervisor status`,
72
+ ` npx -y ${exact} supervisor recover`,
73
+ ` npx -y ${exact} audit verify`,
74
+ ` npx -y ${exact} audit status`,
75
+ ` npx -y ${exact} diagnostics export --output <local.zip> # offline, never uploads`,
76
+ ` npx -y ${exact} diagnostics verify --input <local.zip> # local structural/CRC verify`,
77
+ ` npx -y ${exact} inspect process [limit] # local native process snapshot`,
78
+ ` npx -y ${exact} inspect identity PID # native process creation token`,
79
+ ` npx -y ${exact} inspect memory self [limit] # local VirtualQueryEx metadata only`,
80
+ ` npx -y ${exact} inspect memory PID <creation_filetime> [limit] # PID-reuse guarded`,
81
+ ` npx -y ${exact} inspect pe <file> # local native PE32/PE32+ inspection`,''
82
+ ].join('\n'));
83
+ }
84
+ async function remoteContext(){
85
+ if(process.platform!=='win32') throw new Error('remote controller is currently Windows-only');
86
+ const root=stateRoot();
87
+ const launcher=await requireRegular(path.join(root,'bin','DEADBYTE-MCP.ps1'),'stable DEADBYTE launcher');
88
+ const powershell=await requireRegular(path.join(TRUSTED_WINDOWS_ROOT,'System32','WindowsPowerShell','v1.0','powershell.exe'),'Windows PowerShell');
89
+ return {root,launcher,powershell};
90
+ }
91
+ function boundedSingleLine(value,maxChars=2048){
92
+ return boundedRemoteDiagnostic(value,maxChars);
93
+ }
94
+ async function readActiveRuntimeBinding(root){
95
+ const file=path.join(root,'active-release.json');
96
+ try{return parseActivePointer(await readFile(file,'utf8'));}
97
+ catch(error){if(error?.code==='ENOENT') return null;throw new Error(`active runtime pointer invalid: ${boundedSingleLine(error?.message||error,512)}`);}
98
+ }
99
+ async function readLiveRootPaths(root){
100
+ const active=await readActiveRuntimeBinding(root);
101
+ if(!active) return Object.freeze({});
102
+ const file=path.join(active.generation_root,'coding-policy.json');
103
+ let raw;
104
+ try{raw=JSON.parse(await readFile(file,'utf8'));}
105
+ catch(error){if(error?.code==='ENOENT') return Object.freeze({});throw new Error(`live root policy invalid: ${boundedSingleLine(error?.message||error,512)}`);}
106
+ 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');
107
+ const roots={};
108
+ for(const [id,spec] of Object.entries(raw.roots)){
109
+ if(!/^[A-Za-z0-9._-]{1,64}$/.test(id)||!spec||typeof spec!=='object'||typeof spec.path!=='string'||!path.isAbsolute(spec.path)) continue;
110
+ roots[id]=path.resolve(spec.path);
111
+ }
112
+ return Object.freeze(roots);
113
+ }
114
+ async function reportVersionCoherence(root,{displayMode='ascii'}={}){
115
+ const active=await readActiveRuntimeBinding(root);
116
+ if(!active||active.version===DEADBYTE_VERSION) return active;
117
+ writeRemote(process.stderr,`${remoteTag('warn',displayMode)} VERSION SKEW: CLI package ${DEADBYTE_VERSION}; active runtime ${active.version} (${active.release_id})\n`,displayMode);
118
+ return active;
119
+ }
120
+ async function synchronizeForegroundRuntime(ctx,{displayMode='unicode',migrationContext=null,hostCommandPins=[]}={}){
121
+ const packageManifestSha256=createHash('sha256').update(await readFile(path.join(CURRENT_PACKAGE_ROOT,'MANIFEST.SHA256'))).digest('hex');
122
+ return ensureRuntimeCoherence({
123
+ cliVersion:DEADBYTE_VERSION,
124
+ packageManifestSha256,
125
+ readActive:()=>readActiveRuntimeBinding(ctx.root),
126
+ disarmPrivileged:async()=>{
127
+ for(const command of ['disarmhost','disarmdesktop']){
128
+ const result=invokeController(ctx,command,{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
129
+ if(result.status!==0) throw new Error(`runtime coherence '${command}' failed exit=${result.status}`);
130
+ }
131
+ },
132
+ activateCurrentPackage:async()=>{
133
+ let result=spawnSync(process.execPath,[CURRENT_DEPLOY_SCRIPT],{
134
+ cwd:CURRENT_PACKAGE_ROOT,
135
+ env:{...process.env,DEADBYTE_STATE_ROOT:ctx.root},
136
+ encoding:'utf8',
137
+ windowsHide:true,
138
+ shell:false,
139
+ timeout:REMOTE_UPDATE_TIMEOUT_MS,
140
+ maxBuffer:16*1024*1024
141
+ });
142
+ if(result.error){
143
+ if(result.error?.code==='ETIMEDOUT') throw new Error(`runtime coherence activation timed out after ${REMOTE_UPDATE_TIMEOUT_MS}ms`);
144
+ throw result.error;
145
+ }
146
+ if(result.status!==0){
147
+ const detail=boundedSingleLine(result.stderr||result.stdout||'no deployment output',2048);
148
+ if(/activation preflight rejected:\s*AUTHORITY_UNVERIFIED\b/.test(detail)){
149
+ if(!migrationContext?.receipt_path||!/^[0-9a-f]{64}$/.test(String(migrationContext.receipt_sha256??'')))
150
+ throw new Error('runtime legacy migration marker reconciliation receipt unavailable');
151
+ const expectedArtifact=path.join(ctx.root,'evidence','npm-publication',DEADBYTE_VERSION,`deadbyte-mcp-${DEADBYTE_VERSION}.tgz`);
152
+ await requireRegular(expectedArtifact,'exact release tarball');
153
+ const publicationDir=path.dirname(expectedArtifact),claimPath=path.join(publicationDir,'claim.json');
154
+ await requireRegular(claimPath,'npm publication claim');
155
+ const claim=JSON.parse((await readFile(claimPath,'utf8')).replace(/^\uFEFF/,''));
156
+ const artifactBytes=await readFile(expectedArtifact),artifactSha256=createHash('sha256').update(artifactBytes).digest('hex');
157
+ if(claim?.schema!=='deadbyte.npm-publication-claim.r65.v1'||claim.version!==DEADBYTE_VERSION||
158
+ claim.tarball_sha256!==artifactSha256||!/^[0-9a-f]{64}$/.test(String(claim.manifest_sha256??'')))
159
+ throw new Error('exact release tarball differs from immutable npm publication claim');
160
+ const publicationEvents=(await readdir(publicationDir)).filter(name=>/^event-\d+-[0-9a-f]{16}\.json$/.test(name));
161
+ let registryVerified=false;
162
+ for(const name of publicationEvents){
163
+ const event=JSON.parse((await readFile(path.join(publicationDir,name),'utf8')).replace(/^\uFEFF/,''));
164
+ if(event?.schema==='deadbyte.npm-publication-event.r65.v1'&&event.state==='REGISTRY_IDENTITY_VERIFIED'&&
165
+ event.tarball_sha256===artifactSha256)registryVerified=true;
166
+ }
167
+ if(!registryVerified)throw new Error('exact release tarball lacks registry identity verification evidence');
168
+ const systemTar=await requireRegular(path.join(TRUSTED_WINDOWS_ROOT,'System32','tar.exe'),'Windows tar');
169
+ const manifestRead=spawnSync(systemTar,['-xOf',expectedArtifact,'package/MANIFEST.SHA256'],{
170
+ encoding:'utf8',windowsHide:true,shell:false,timeout:30000,maxBuffer:4*1024*1024});
171
+ if(manifestRead.error||manifestRead.status!==0)throw new Error('exact release tarball manifest extraction failed');
172
+ const packagedManifest=manifestRead.stdout,packagedManifestSha256=createHash('sha256').update(packagedManifest,'utf8').digest('hex');
173
+ if(packagedManifestSha256!==claim.manifest_sha256)throw new Error('exact release tarball manifest identity mismatch');
174
+ const bootstrapLine=packagedManifest.split(/\r?\n/).find(line=>line.endsWith(' scripts/bootstrap-uac-migration-r153.ps1'));
175
+ const bootstrapMatch=/^([0-9a-f]{64}) scripts\/bootstrap-uac-migration-r153\.ps1$/.exec(bootstrapLine??'');
176
+ if(!bootstrapMatch)throw new Error('exact release tarball bootstrap identity missing');
177
+ const evidenceDir=path.join(ctx.root,'evidence','legacy-migration');
178
+ await mkdir(evidenceDir,{recursive:true});
179
+ const id=`r153-${Date.now()}-${randomBytes(8).toString('hex')}`;
180
+ const stdoutPath=path.join(evidenceDir,`${id}.stdout.log`),stderrPath=path.join(evidenceDir,`${id}.stderr.log`);
181
+ writeRemote(process.stdout,`${remoteTag('auth',displayMode)} Legacy broad-root migration requires one-time Windows UAC confirmation.\n`,displayMode);
182
+ result=spawnSync(ctx.powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',UAC_BOOTSTRAP_SCRIPT,
183
+ '-ExpectedVersion',DEADBYTE_VERSION,'-StateRoot',ctx.root,'-TarballPath',expectedArtifact,
184
+ '-ExpectedTarballSha256',artifactSha256,'-ExpectedManifestSha256',packagedManifestSha256,
185
+ '-ExpectedBootstrapSha256',bootstrapMatch[1],
186
+ '-StdoutPath',stdoutPath,'-StderrPath',stderrPath,
187
+ '-MarkerReceiptPath',migrationContext.receipt_path,'-MarkerReceiptSha256',migrationContext.receipt_sha256,
188
+ ...(hostCommandPins.length?['-HostCommandPinsJson',encodeHostCommandPins(hostCommandPins)]:[])],{
189
+ cwd:CURRENT_PACKAGE_ROOT,env:windowsPowerShellEnvironment(process.env),encoding:'utf8',windowsHide:true,shell:false,
190
+ timeout:REMOTE_UPDATE_TIMEOUT_MS,maxBuffer:16*1024*1024
191
+ });
192
+ if(result.error){
193
+ if(result.error?.code==='ETIMEDOUT')throw new Error(`runtime migration UAC timed out after ${REMOTE_UPDATE_TIMEOUT_MS}ms`);
194
+ throw result.error;
195
+ }
196
+ if(result.status===0)return;
197
+ const uacDetail=boundedSingleLine(result.stderr||result.stdout||'UAC was cancelled or migration failed',2048);
198
+ throw new Error(`runtime coherence UAC migration failed exit=${String(result.status)}: ${uacDetail}`);
199
+ }
200
+ throw new Error(`runtime coherence activation failed exit=${String(result.status)}: ${detail}`);
201
+ }
202
+ },
203
+ onEvent:event=>{
204
+ if(event.phase==='upgrade_start') writeRemote(process.stdout,`[UPDATE] Runtime ${event.from_version} -> ${event.to_version}; activating current package...\n`,displayMode);
205
+ else if(event.phase==='upgrade_complete') writeRemote(process.stdout,`[UPDATE] Runtime synchronized: ${event.release_id||event.to_version}\n`,displayMode);
206
+ }
207
+ });
208
+ }
209
+ async function synchronizeOneShotRuntime(ctx,{displayMode='ascii'}={}){
210
+ const active=await readActiveRuntimeBinding(ctx.root);
211
+ if(!active||active.version===DEADBYTE_VERSION) return synchronizeForegroundRuntime(ctx,{displayMode});
212
+ const before=observedAuthority(ctx);
213
+ const expected=Object.fromEntries(AUTHORITY_PLANES.map(plane=>[plane,before.authority[plane].armed]));
214
+ const result=await synchronizeForegroundRuntime(ctx,{displayMode});
215
+ if(result.status!=='upgraded') return result;
216
+ for(const command of authorityRestorePlan(before)){
217
+ const restored=invokeController(ctx,command,{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
218
+ if(restored.status!==0) throw new Error(`runtime coherence authority restore '${command}' failed exit=${restored.status}`);
219
+ }
220
+ assertAuthorityVector(observedAuthority(ctx),expected);
221
+ return result;
222
+ }
223
+ async function readControllerStartFailure(root,command,{maxChars=2048}={}){
224
+ const file=path.join(root,'runtime','controller-start-failure.json');
225
+ let raw;
226
+ try{raw=await readFile(file,'utf8');}catch(error){if(error?.code==='ENOENT') return null;throw error;}
227
+ let value;
228
+ try{value=JSON.parse(String(raw).replace(/^\uFEFF/,''));}catch{return null;}
229
+ if(value?.schema!=='deadbyte.controller-start-failure.v1'||String(value?.command||'')!==command||typeof value?.message!=='string') return null;
230
+ const message=boundedSingleLine(value.message,maxChars);
231
+ return message?{file,message}:null;
232
+ }
233
+ async function readTailBounded(file,maxBytes=4096){
234
+ try{
235
+ const info=await stat(file);
236
+ const length=Math.min(info.size,maxBytes);
237
+ if(length===0) return '';
238
+ const buffer=Buffer.alloc(length);
239
+ const handle=await open(file,'r');
240
+ let encoding=null;
241
+ try{
242
+ const header=Buffer.alloc(2);await handle.read(header,0,2,0);
243
+ if(header[0]===0xff&&header[1]===0xfe)encoding='utf16le';
244
+ await handle.read(buffer,0,length,info.size-length);
245
+ }finally{await handle.close();}
246
+ return boundedSingleLine(decodeRemoteLogBytes(file,buffer,{encoding}),maxBytes);
247
+ }catch(error){if(error?.code==='ENOENT') return '';throw error;}
248
+ }
249
+ function invokeController(ctx,command,{capture=false,allowFailure=false,echoCaptured=true,timeoutMs=null,discardOutput=false,redirectOutput=null,json=false,probeMode=null}={}){
250
+ if(capture&&discardOutput) throw new Error('invokeController cannot capture and discard output together');
251
+ if(capture&&redirectOutput) throw new Error('invokeController cannot capture and redirect output together');
252
+ if(discardOutput&&redirectOutput) throw new Error('invokeController cannot discard and redirect output together');
253
+ let stdoutFd=null,stderrFd=null,result;
254
+ try{
255
+ let stdio;
256
+ if(discardOutput) stdio=['ignore','ignore','ignore'];
257
+ else if(redirectOutput){
258
+ stdoutFd=openSync(redirectOutput.stdout_path,'w');
259
+ stderrFd=openSync(redirectOutput.stderr_path,'w');
260
+ stdio=['ignore',stdoutFd,stderrFd];
261
+ }else stdio=capture?['ignore','pipe','pipe']:'inherit';
262
+ const options={stdio,encoding:capture?'utf8':undefined,windowsHide:false,shell:false,
263
+ env:windowsPowerShellEnvironment({...process.env,SystemRoot:TRUSTED_WINDOWS_ROOT,WINDIR:TRUSTED_WINDOWS_ROOT,
264
+ ...(probeMode?{DEADBYTE_REMOTE_PROBE_MODE:probeMode}:{})})};
265
+ if(Number.isInteger(timeoutMs)&&timeoutMs>0) options.timeout=timeoutMs;
266
+ result=spawnSync(ctx.powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.launcher,command,...(json?['-Json']:[])],options);
267
+ }finally{
268
+ if(stdoutFd!==null) closeSync(stdoutFd);
269
+ if(stderrFd!==null) closeSync(stderrFd);
270
+ }
271
+ if(result.error){
272
+ if(result.error?.code==='ETIMEDOUT') throw new Error(`remote controller '${command}' timed out after ${timeoutMs}ms`);
273
+ throw result.error;
274
+ }
275
+ const status=Number.isInteger(result.status)?result.status:1;
276
+ const stdout=capture?String(result.stdout||''):'';
277
+ const stderr=capture?String(result.stderr||''):'';
278
+ if(capture&&echoCaptured){if(stdout) process.stdout.write(stdout);if(stderr) process.stderr.write(stderr);}
279
+ if(status!==0&&!allowFailure) throw new Error(`remote controller '${command}' failed exit=${status}`);
280
+ return {status,stdout,stderr};
281
+ }
282
+ function observedAuthority(ctx){
283
+ const result=invokeController(ctx,'status',{capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS,json:true});
284
+ return parseAuthorityObservation(result.stdout);
285
+ }
286
+ function controllerRuntimeReady(text){
287
+ const value=String(text||'');
288
+ return /^DEADBYTE MCP\s*=\s*ONLINE\s*$/m.test(value)&&
289
+ /^Bridge PID\s*:\s*\d+\s+\[verified\]\s*$/m.test(value)&&
290
+ /^Cloud PID\s*:\s*\d+\s+\[verified\]\s*$/m.test(value)&&
291
+ /^Local ready\s*:\s*True\s*$/m.test(value)&&
292
+ /^Loop supervisor:\s*RUNNING\s+\[verified\]\s*$/m.test(value);
293
+ }
294
+ function controllerRuntimeDegraded(text){
295
+ return /^DEADBYTE MCP\s*=\s*(?:DEGRADED|FAILED)\s*$/m.test(String(text||''));
296
+ }
297
+ async function listLogFiles(root){
298
+ const auditDir=path.join(root,'audit');
299
+ const runtimeDir=path.join(root,'runtime','logs');
300
+ let auditNames=[],runtimeNames=[];
301
+ try{auditNames=await readdir(auditDir);}catch(error){if(error?.code!=='ENOENT') throw error;}
302
+ try{runtimeNames=await readdir(runtimeDir);}catch(error){if(error?.code!=='ENOENT') throw error;}
303
+ const selected=selectLiveLogNames({auditNames,runtimeNames});
304
+ return [
305
+ ...selected.audit.map(name=>path.join(auditDir,name)),
306
+ ...selected.runtime.map(name=>path.join(runtimeDir,name))
307
+ ];
308
+ }
309
+ function parseAuditLogLine(file,line){
310
+ if(!/^segment-\d{8}\.jsonl$/.test(path.basename(file))||!line) return null;
311
+ try{return JSON.parse(line);}catch{return null;}
312
+ }
313
+ function formatLogLine(file,line,{auditEvent=null,activityTracker=null,sequence=null,sourceSequence=null,verbose=false,displayMode='unicode'}={}){
314
+ if(!line) return null;
315
+ if(/^segment-\d{8}\.jsonl$/.test(path.basename(file))){
316
+ const event=auditEvent??parseAuditLogLine(file,line);
317
+ if(!event) return null;
318
+ return activityTracker?activityTracker.observe(event,{sequence,sourceSequence}):formatCompactAuditEvent(event);
319
+ }
320
+ return formatRuntimeDiagnosticLine(file,line,{verbose,displayMode});
321
+ }
322
+ function remoteResultJournalPath(root,sessionId){return path.join(root,'runtime','remote-results',`${sessionId}.json`);}
323
+ function auditTimelineRecord(file,line,auditEvent=null){
324
+ const event=auditEvent??parseAuditLogLine(file,line);
325
+ if(!event) return null;
326
+ if(typeof event?.request_id!=='string'||!event.request_id||typeof event.tool!=='string'||!event.tool) return null;
327
+ const stamp=[event.ts,event.created_at_utc,event.observed_at_utc].find(value=>typeof value==='string'&&Number.isFinite(Date.parse(value)));
328
+ const observed=stamp?new Date(Date.parse(stamp)).toISOString():new Date().toISOString();
329
+ const payload_sha256=createHash('sha256').update(Buffer.from(line,'utf8')).digest('hex');
330
+ if(event.event_type==='tool.request') return {event_type:'tool.call',request_id:event.request_id,tool:event.tool,
331
+ observed_at_utc:observed,arguments:event.args??{},payload_sha256};
332
+ if(event.event_type==='tool.result'){
333
+ const classified=classifyRemoteAuditOutcome(event);
334
+ const unknown=classified==='unknown';
335
+ const recoveryReason=unknown&&typeof event.outcome?.recovery?.reason==='string'?event.outcome.recovery.reason:null;
336
+ return {event_type:'tool.result',request_id:event.request_id,tool:event.tool,
337
+ status:classified==='pending'?'pending':classified==='ok'?'ok':'error',observed_at_utc:observed,payload_sha256,
338
+ ...(Number.isFinite(Number(event.outcome?.duration_ms))?{duration_ms:Number(event.outcome.duration_ms)}:{}),
339
+ ...(unknown?{side_effect_state:'unknown'}:{}),
340
+ ...(typeof event.outcome?.reason==='string'&&event.outcome.reason?{reason:event.outcome.reason}:(recoveryReason?{reason:recoveryReason}:
341
+ (classified==='pending'&&typeof event.outcome?.status==='string'?{reason:event.outcome.status}:{})))};
342
+ }
343
+ return null;
344
+ }
345
+ async function replayRemoteResults(root,cursor,{displayMode='unicode'}={}){
346
+ const parsed=parseRemoteResultCursor(cursor);
347
+ const file=remoteResultJournalPath(root,parsed.session_id);
348
+ await requireRegular(file,'remote result journal');
349
+ const journal=await openRemoteResultJournal({filePath:file,sessionId:parsed.session_id,maxEntries:2048});
350
+ let next=cursor;
351
+ for(;;){
352
+ const page=await journal.replayAfter(next,{limit:200});
353
+ for(const event of page.events){
354
+ if(event.event_type==='tool.call'){
355
+ const line=formatRemoteToolCall({event_type:'tool.request',tool:event.tool,request_id:event.request_id,
356
+ observed_at_utc:event.observed_at_utc,args:event.arguments},{displayMode});
357
+ process.stdout.write(remoteDisplayText(`${line} | REPLAY #${event.seq}\n`,displayMode));
358
+ continue;
359
+ }
360
+ const unknown=event.side_effect_state==='unknown';
361
+ const marker=unknown?'OUTCOME_UNKNOWN':event.status==='pending'?'PENDING':event.status==='error'?(displayMode==='ascii'?'ERR':'✗'):(displayMode==='ascii'?'OK':'✓');
362
+ 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'}`:'';
363
+ const reason=(event.status==='error'||event.status==='pending'||unknown)&&typeof event.reason==='string'&&event.reason?` — ${event.reason}`:'';
364
+ process.stdout.write(remoteDisplayText(`[REPLAY] #${event.seq} ${event.tool}${duration} ${marker}${reason}\n`,displayMode));
365
+ }
366
+ next=page.next_cursor;
367
+ if(!page.has_more) return next;
368
+ }
369
+ }
370
+ async function readAppend(file,cursor){
371
+ return readRemoteLogAppend(file,cursor);
372
+ }
373
+ async function snapshotRemoteLogOffsets(root){
374
+ const offsets=new Map();
375
+ const files=await listLogFiles(root);
376
+ for(const file of files){
377
+ const snapshot=await readRemoteLogAppend(file,null,{startAtEnd:true});
378
+ if(snapshot.cursor)offsets.set(file,snapshot.cursor);
379
+ }
380
+ return offsets;
381
+ }
382
+ function timelineFilterEvent(timeline){
383
+ if(!timeline) return null;
384
+ if(timeline.event_type==='tool.call') return {
385
+ event_type:'tool.request',request_id:timeline.request_id,tool:timeline.tool,args:timeline.arguments??{}
386
+ };
387
+ return {
388
+ event_type:'tool.result',request_id:timeline.request_id,tool:timeline.tool,
389
+ outcome:{
390
+ status:timeline.side_effect_state==='unknown'?'interrupted':timeline.status,
391
+ is_error:timeline.status==='error',
392
+ ...(Number.isFinite(Number(timeline.duration_ms))?{duration_ms:Number(timeline.duration_ms)}:{}),
393
+ ...(timeline.side_effect_state?{side_effect_state:timeline.side_effect_state}:{}),
394
+ ...(timeline.reason?{reason:timeline.reason}:{})
395
+ }
396
+ };
397
+ }
398
+ async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTracker=null,timelineFilter=null,timelineWriter=null,verbose=false,displayMode='unicode'}={}){
399
+ process.stdout.write(verbose
400
+ ? '[LOG] Verbose live timeline. Full sanitized request detail enabled; Ctrl+C disconnects and revokes authority.\n'
401
+ : '[LOG] Live tool calls with individual START/DONE, sanitized arguments and results. Transport hidden; signed audit remains on disk. Ctrl+C disconnects and revokes authority.\n');
402
+ const activeOffsets=offsets??await snapshotRemoteLogOffsets(root);
403
+ const partialLines=new Map();
404
+ let liveFiles=[...activeOffsets.keys()];
405
+ let nextDiscoveryAt=0;
406
+ while(!signal.aborted){
407
+ const now=Date.now();
408
+ if(now>=nextDiscoveryAt){
409
+ liveFiles=await listLogFiles(root);
410
+ nextDiscoveryAt=now+LIVE_LOG_DISCOVERY_INTERVAL_MS;
411
+ }
412
+ for(const file of liveFiles){
413
+ const prior=activeOffsets.has(file)?activeOffsets.get(file):null;
414
+ const next=await readAppend(file,prior);
415
+ activeOffsets.set(file,next.cursor);
416
+ if(next.reset){
417
+ partialLines.delete(file);
418
+ process.stderr.write(formatRemoteLogResetNotice(file,next.resetReason)+'\n');
419
+ }
420
+ if(!next.text)continue;
421
+ const lines=((partialLines.get(file)??'')+next.text).split(/\r?\n/);
422
+ partialLines.set(file,lines.pop()??'');
423
+ if(Buffer.byteLength(partialLines.get(file),'utf8')>1024*1024) throw new Error(`remote log line exceeds bounded decode limit: ${path.basename(file)}`);
424
+ for(const line of lines){
425
+ if(Buffer.byteLength(line,'utf8')>1024*1024) throw new Error(`remote log line exceeds bounded decode limit: ${path.basename(file)}`);
426
+ const auditEvent=parseAuditLogLine(file,line);
427
+ if(/^segment-\d{8}\.jsonl$/.test(path.basename(file))&&!auditEvent)
428
+ throw new Error('LIVELOG_AUDIT_RECORD_INVALID '+path.basename(file)+'; refusing to silently skip audit bytes');
429
+ const timeline=auditTimelineRecord(file,line,auditEvent);
430
+ let recorded=null;
431
+ if(timeline&&journal) recorded=timeline.event_type==='tool.call'
432
+ ? await journal.recordCall(timeline)
433
+ : await journal.recordTerminal(timeline);
434
+ if(recorded?.duplicate) continue;
435
+ const accepted=timelineFilter&&timeline?timelineFilter.accept(timelineFilterEvent(timeline)):true;
436
+ const formatted=formatLogLine(file,line,{auditEvent,activityTracker,sequence:recorded?.event?.seq??null,
437
+ sourceSequence:Number.isSafeInteger(auditEvent?.seq)?auditEvent.seq:null,verbose,displayMode});
438
+ if(!formatted||!accepted) continue;
439
+ const output=Array.isArray(formatted)?formatted:[formatted];
440
+ for(const item of output) if(item){
441
+ if(timelineWriter) timelineWriter.write(item,{event_type:timeline?.event_type??null,request_id:timeline?.request_id??null});
442
+ else process.stdout.write(`${item}\n`);
443
+ }
444
+ }
445
+ }
446
+ try{await sleep(250,null,{signal});}catch(error){if(error?.name!=='AbortError') throw error;}
447
+ }
448
+ }
449
+ function remoteTag(kind,displayMode){
450
+ const unicode={warn:'⚠️ [WARN]',recovery:'♻️ [RECOVERY]',auth:'🔐 [AUTH]'};
451
+ const ascii={warn:'[WARN]',recovery:'[RECOVERY]',auth:'[AUTH]'};
452
+ return (displayMode==='ascii'?ascii:unicode)[kind];
453
+ }
454
+ function writeRemote(stream,text,displayMode){stream.write(remoteDisplayText(text,displayMode));}
455
+ async function cleanupRemoteSession(ctx,{displayMode='unicode'}={}){
456
+ const failed=[];
457
+ for(const command of SESSION_CLEANUP){
458
+ try{
459
+ const result=invokeController(ctx,command,{allowFailure:true,capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
460
+ if(result.status!==0){
461
+ failed.push(command);
462
+ const detail=boundedSingleLine(result.stderr||result.stdout||'controller returned no diagnostic',512);
463
+ writeRemote(process.stderr,`${remoteTag('warn',displayMode)} cleanup ${command} exit=${result.status}: ${detail}\n`,displayMode);
464
+ }
465
+ }
466
+ catch(error){failed.push(command);writeRemote(process.stderr,`${remoteTag('warn',displayMode)} cleanup ${command}: ${String(error?.message||error)}\n`,displayMode);}
467
+ }
468
+ if(failed.length) throw new Error(`remote session cleanup failed; ownership marker retained: ${failed.join(',')}`);
469
+ let snapshot;
470
+ try{snapshot=assertAuthorityVector(observedAuthority(ctx),Object.fromEntries(AUTHORITY_PLANES.map(plane=>[plane,false])));}
471
+ catch(error){throw new Error(`remote session cleanup verification failed; ownership marker retained: ${String(error?.message||error)}`);}
472
+ if(snapshot.state==='ONLINE'){
473
+ try{invokeController(ctx,'machinetest',{capture:true,echoCaptured:false,timeoutMs:REMOTE_START_TIMEOUT_MS,probeMode:'revoked'});}
474
+ catch(error){throw new Error(`remote session enforcement cleanup verification failed; ownership marker retained: ${String(error?.message||error)}`);}
475
+ }
476
+ const parked=invokeController(ctx,'park',{allowFailure:true,capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
477
+ if(parked.status!==0) throw new Error(`remote session park failed exit=${parked.status}; ownership marker retained`);
478
+ }
479
+ const GUARDIAN_SCRIPT=fileURLToPath(new URL('./remote-session-guardian.mjs',import.meta.url));
480
+ function remoteSessionMarker(root){return path.join(root,'runtime','remote-session.json');}
481
+ function pidAlive(pid){
482
+ if(!Number.isInteger(pid)||pid<1)return false;
483
+ try{process.kill(pid,0);return true;}
484
+ catch(error){return error?.code!=='ESRCH';}
485
+ }
486
+ function remoteOwnerPid(marker){return marker?.schema==='deadbyte.remote-session.v2'?marker.owner_pid:marker?.pid;}
487
+ async function readRemoteSessionMarker(root){
488
+ const file=remoteSessionMarker(root);
489
+ let text;try{text=await readFile(file,'utf8');}catch(error){if(error?.code==='ENOENT') return null;throw error;}
490
+ let value;try{value=JSON.parse(text);}catch{throw new Error('remote session marker is invalid JSON');}
491
+ if(value?.schema==='deadbyte.remote-session.v1'){
492
+ if(!Number.isInteger(value?.pid)||value.pid<1) throw new Error('remote session marker schema invalid');
493
+ return value;
494
+ }
495
+ if(value?.schema==='deadbyte.remote-session.v2'){
496
+ for(const key of ['owner_identity','guardian_identity'])if(value[key]!==undefined&&!validForegroundIdentity(value[key]))throw new Error('remote session ownership identity invalid');
497
+ if(!Number.isInteger(value?.owner_pid)||value.owner_pid<1||
498
+ !/^[0-9a-f]{64}$/.test(String(value?.session_nonce||''))||
499
+ !Number.isInteger(value?.guardian_pid)||value.guardian_pid<0||
500
+ (value.result_session_id!==undefined&&!/^[0-9a-f]{24}$/.test(String(value.result_session_id)))) throw new Error('remote session marker schema invalid');
501
+ return value;
502
+ }
503
+ throw new Error('remote session marker schema invalid');
504
+ }
505
+ async function publishExclusiveJson(file,value){
506
+ const bytes=Buffer.from(`${JSON.stringify(value)}\n`,'utf8');
507
+ await mkdir(path.dirname(file),{recursive:true});
508
+ try{
509
+ const handle=await open(file,'wx',0o600);
510
+ try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
511
+ }catch(error){
512
+ if(error?.code!=='EEXIST')throw error;
513
+ const existing=await readFile(file);
514
+ if(!existing.equals(bytes))throw new Error(`exclusive evidence identity collision: ${path.basename(file)}`);
515
+ }
516
+ return {path:file,sha256:createHash('sha256').update(bytes).digest('hex')};
517
+ }
518
+ async function recoverRemoteSession(ctx,{displayMode='unicode'}={}){
519
+ let prior=await readRemoteSessionMarker(ctx.root);
520
+ const evidenceDir=path.join(ctx.root,'evidence','legacy-migration');
521
+ if(!prior){
522
+ const activePointer=await readActiveRuntimeBinding(ctx.root);
523
+ const selected=await selectLegacyMarkerEvidence({evidenceDir,activePointer});
524
+ if(selected.kind==='none')return null;
525
+ if(selected.kind==='located'){
526
+ const readback=await readActiveRuntimeBinding(ctx.root);
527
+ for(const key of ['release_id','version','manifest_sha256','generation_manifest_sha256'])
528
+ if(readback?.[key]!==activePointer?.[key])
529
+ throw new Error('R159 active release changed during located marker verification');
530
+ return {state:'MARKER_RECONCILED',receipt_path:selected.receiptPath,
531
+ receipt_sha256:selected.receiptSha256};
532
+ }
533
+ // Completed historical receipts are not a pending migration for an
534
+ // already-online current release. Keep bytes and their SHA untouched.
535
+ if(selected.kind==='completed'&&observedAuthority(ctx).state==='ONLINE')return null;
536
+ if(selected.kind==='pending'&&observedAuthority(ctx).state!=='OFFLINE')
537
+ throw new Error('R159 pending marker reconciliation requires an offline runtime');
538
+ // Crash recovery for the narrow window after marker CAS deletion and
539
+ // before terminal receipt publication. Never infer completion from the
540
+ // missing marker alone: require exactly one durable intent and re-observe
541
+ // the full predecessor/policy/grant/runtime subject.
542
+ const pending=selected.pending,intentInfo=await lstat(selected.pending.intent);
543
+ if(!intentInfo.isFile()||intentInfo.isSymbolicLink())throw new Error('marker reconciliation intent is not a regular file');
544
+ const intentBytes=await readFile(pending.intent),intent=JSON.parse(intentBytes.toString('utf8').replace(/^\uFEFF/,''));
545
+ if(intent.marker_sha256!==pending.markerSha256)throw new Error('marker reconciliation intent filename identity mismatch');
546
+ const active=await readActiveRuntimeBinding(ctx.root);
547
+ if(!active)throw new Error('marker reconciliation predecessor pointer missing');
548
+ for(const key of ['release_id','version','manifest_sha256','generation_manifest_sha256'])
549
+ if(active[key]!==activePointer?.[key])
550
+ throw new Error('R159 active release changed during marker recovery');
551
+ const observed=observedAuthority(ctx);
552
+ const grantField={process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'};
553
+ const policySha256={},grantPresence={};
554
+ for(const plane of AUTHORITY_PLANES){
555
+ const policyFile=path.join(active.generation_root,`${plane}-policy.json`),info=await lstat(policyFile);
556
+ if(!info.isFile()||info.isSymbolicLink()||path.resolve(path.dirname(policyFile)).toLowerCase()!==path.resolve(active.generation_root).toLowerCase())
557
+ throw new Error(`marker reconciliation ${plane} policy identity invalid`);
558
+ const bytes=await readFile(policyFile),policy=JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/,''));
559
+ policySha256[plane]=createHash('sha256').update(bytes).digest('hex');
560
+ const grant=policy?.[grantField[plane]];
561
+ if(typeof grant!=='string'||!path.isAbsolute(grant)||
562
+ path.resolve(path.dirname(grant)).toLowerCase()!==path.resolve(ctx.root,'runtime').toLowerCase())
563
+ throw new Error(`marker reconciliation ${plane} grant path invalid`);
564
+ try{await lstat(grant);grantPresence[plane]=true;}
565
+ catch(error){if(error?.code==='ENOENT')grantPresence[plane]=false;else throw error;}
566
+ }
567
+ if(await readRemoteMarkerSnapshot(remoteSessionMarker(ctx.root))!==null)
568
+ throw new Error('marker reconciliation marker reappeared');
569
+ const intentSha256=createHash('sha256').update(intentBytes).digest('hex');
570
+ let reconciled;
571
+ if(pending.terminalExists){
572
+ const terminalInfo=await lstat(pending.terminal);
573
+ if(!terminalInfo.isFile()||terminalInfo.isSymbolicLink())throw new Error('marker reconciliation terminal is not a regular file');
574
+ const terminalBytes=await readFile(pending.terminal),terminal=JSON.parse(terminalBytes.toString('utf8').replace(/^\uFEFF/,''));
575
+ reconciled=recoverPublishedMarkerTerminal({stateRoot:ctx.root,pointer:active,observation:observed,
576
+ policySha256,grantPresence,intent,intentSha256,terminal,
577
+ terminalSha256:createHash('sha256').update(terminalBytes).digest('hex'),markerPresent:false});
578
+ }else{
579
+ reconciled=await resumeMarkerReconciliation({stateRoot:ctx.root,pointer:active,observation:observed,
580
+ policySha256,grantPresence,intent,intentSha256,markerPresent:false,
581
+ publishTerminal:async terminal=>publishExclusiveJson(pending.terminal,terminal)});
582
+ }
583
+ const locator={schema:'deadbyte.r153-pending-migration.v1',receipt_path:pending.terminal,
584
+ receipt_sha256:reconciled.receipt_sha256,marker_sha256:reconciled.marker_sha256};
585
+ await publishExclusiveJson(path.join(evidenceDir,'marker-'+reconciled.marker_sha256+'.locator.json'),locator);
586
+ writeRemote(process.stdout,`${remoteTag('recovery',displayMode)} Interrupted marker reconciliation completed (${reconciled.marker_sha256.slice(0,12)}).\n`,displayMode);
587
+ return {...reconciled,receipt_path:pending.terminal};
588
+ }
589
+ const ownerPid=remoteOwnerPid(prior);
590
+ let restrictedReusedOwner=false;
591
+ if(pidAlive(ownerPid)){
592
+ if(!prior.owner_identity)throw new Error(`remote session ownership unverified for legacy pid=${ownerPid}; marker retained`);
593
+ if(prior.owner_identity.pid!==ownerPid)throw new Error('remote session owner identity PID binding mismatch; marker retained');
594
+ let actualOwner;
595
+ try{actualOwner=queryForegroundIdentity(ownerPid);}
596
+ catch(error){restrictedReusedOwner=proveRestrictedPidReuse(prior.owner_identity,error);}
597
+ if(!restrictedReusedOwner){
598
+ if(!foregroundIdentityMatches(prior.owner_identity,actualOwner))throw new Error('remote session owner identity mismatch; marker retained');
599
+ throw new Error(`remote session already active pid=${ownerPid}`);
600
+ }
601
+ }
602
+ if(prior.schema!=='deadbyte.remote-session.v2'||!prior.owner_identity)
603
+ throw new Error(`remote session ownership identity unverified for stale pid=${ownerPid}; marker retained`);
604
+ let guardianAlive=pidAlive(prior.guardian_pid);
605
+ if(guardianAlive){
606
+ if(!prior.guardian_identity)throw new Error('remote guardian ownership unverified; marker retained');
607
+ if(prior.guardian_identity.pid!==prior.guardian_pid)throw new Error('remote guardian identity PID binding mismatch');
608
+ const actualGuardian=queryForegroundIdentity(prior.guardian_pid);
609
+ if(!actualGuardian||!foregroundIdentityMatches(prior.guardian_identity,actualGuardian))
610
+ throw new Error('remote guardian identity mismatch or PID reuse; marker retained');
611
+ }
612
+ if(guardianAlive){
613
+ const deadline=Date.now()+3000;
614
+ while(Date.now()<deadline){
615
+ await sleep(100);
616
+ prior=await readRemoteSessionMarker(ctx.root);
617
+ if(!prior) return;
618
+ if(remoteOwnerPid(prior)!==ownerPid) throw new Error('remote session ownership changed during guardian recovery');
619
+ }
620
+ throw new Error(`remote session guardian cleanup still active pid=${prior.guardian_pid}`);
621
+ }
622
+ const markerPath=remoteSessionMarker(ctx.root);
623
+ const snapshot=await readRemoteMarkerSnapshot(markerPath);
624
+ if(!snapshot) return null;
625
+ const active=await readActiveRuntimeBinding(ctx.root);
626
+ if(!active)throw new Error(`remote session ownership unverified for stale pid=${ownerPid}; active pointer missing; marker retained`);
627
+ const observed=observedAuthority(ctx);
628
+ const grantField={process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'};
629
+ const policySha256={},grantPresence={};
630
+ for(const plane of AUTHORITY_PLANES){
631
+ const policyFile=path.join(active.generation_root,`${plane}-policy.json`);
632
+ const info=await lstat(policyFile);
633
+ if(!info.isFile()||info.isSymbolicLink()||path.resolve(path.dirname(policyFile)).toLowerCase()!==path.resolve(active.generation_root).toLowerCase())
634
+ throw new Error(`remote stale marker ${plane} policy identity invalid; marker retained`);
635
+ const bytes=await readFile(policyFile),policy=JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/,''));
636
+ policySha256[plane]=createHash('sha256').update(bytes).digest('hex');
637
+ const grant=policy?.[grantField[plane]];
638
+ if(typeof grant!=='string'||!path.isAbsolute(grant)||
639
+ path.resolve(path.dirname(grant)).toLowerCase()!==path.resolve(ctx.root,'runtime').toLowerCase())
640
+ throw new Error(`remote stale marker ${plane} grant path invalid; marker retained`);
641
+ try{await lstat(grant);grantPresence[plane]=true;}
642
+ catch(error){if(error?.code==='ENOENT')grantPresence[plane]=false;else throw error;}
643
+ }
644
+ const identityLive=identity=>{
645
+ if(!identity||!validForegroundIdentity(identity))throw new Error('remote stale marker identity unverified; marker retained');
646
+ // A later-created protected svchost is not the signed original Node owner.
647
+ // This negative proof is limited to the exact owner and never exempts the guardian.
648
+ if(restrictedReusedOwner&&identity.pid===ownerPid)return false;
649
+ const actual=queryForegroundIdentity(identity.pid);
650
+ if(actual===null)return false;
651
+ if(!foregroundIdentityMatches(identity,actual))throw new Error('remote stale marker PID reuse detected; marker retained');
652
+ return true;
653
+ };
654
+ const revalidate=async phase=>{
655
+ const current=await readActiveRuntimeBinding(ctx.root);
656
+ for(const key of ['release_id','manifest_sha256','generation_manifest_sha256','generation_root'])
657
+ if(current?.[key]!==active[key])throw new Error(`remote stale marker pointer drift during ${phase}`);
658
+ const fresh=observedAuthority(ctx);
659
+ if(fresh.state!=='OFFLINE'||fresh.local_ready!==false||fresh.bridge?.alive!==false||fresh.core?.alive!==false||
660
+ fresh.cloud?.alive!==false||fresh.supervisor?.running!==false)throw new Error(`remote stale marker runtime drift during ${phase}`);
661
+ for(const plane of AUTHORITY_PLANES){
662
+ if(fresh.authority?.[plane]?.configured!==true||fresh.authority?.[plane]?.armed!==false)
663
+ throw new Error(`remote stale marker ${plane} authority drift during ${phase}`);
664
+ const policyFile=path.join(active.generation_root,`${plane}-policy.json`),bytes=await readFile(policyFile);
665
+ if(createHash('sha256').update(bytes).digest('hex')!==policySha256[plane])
666
+ throw new Error(`remote stale marker ${plane} policy drift during ${phase}`);
667
+ const policy=JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/,'')),grant=policy?.[grantField[plane]];
668
+ try{await lstat(grant);throw new Error(`remote stale marker ${plane} grant appeared during ${phase}`);}
669
+ catch(error){if(error?.code!=='ENOENT')throw error;}
670
+ }
671
+ const currentMarker=await readRemoteMarkerSnapshot(markerPath);
672
+ if(phase==='before_delete'&&currentMarker?.sha256!==snapshot.sha256)
673
+ throw new Error('remote stale marker changed before deletion');
674
+ if(phase==='after_delete'&&currentMarker!==null)
675
+ throw new Error('remote stale marker remained or reappeared after deletion');
676
+ };
677
+ const intentPath=path.join(evidenceDir,`marker-${snapshot.sha256}.intent.json`);
678
+ const terminalPath=path.join(evidenceDir,`marker-${snapshot.sha256}.terminal.json`);
679
+ const reconciled=await reconcileStaleMarker({stateRoot:ctx.root,pointer:active,observation:observed,
680
+ policySha256,grantPresence,marker:{sha256:snapshot.sha256,
681
+ owner_live:identityLive(prior.owner_identity),
682
+ guardian_live:prior.guardian_pid===0?false:identityLive(prior.guardian_identity)},
683
+ publishIntent:async intent=>publishExclusiveJson(intentPath,intent),
684
+ publishTerminal:async terminal=>publishExclusiveJson(terminalPath,terminal),revalidate,
685
+ deleteIfHash:async expected=>deletePinnedRemoteMarker(markerPath,expected)});
686
+ const locator={schema:'deadbyte.r153-pending-migration.v1',receipt_path:terminalPath,
687
+ receipt_sha256:reconciled.receipt_sha256,marker_sha256:reconciled.marker_sha256};
688
+ await publishExclusiveJson(path.join(evidenceDir,'marker-'+reconciled.marker_sha256+'.locator.json'),locator);
689
+ writeRemote(process.stdout,`${remoteTag('recovery',displayMode)} Stale session evidence reconciled (${reconciled.marker_sha256.slice(0,12)}).\n`,displayMode);
690
+ return {...reconciled,receipt_path:terminalPath};
691
+ }
692
+ async function claimRemoteSession(ctx){
693
+ const dir=path.join(ctx.root,'runtime');await mkdir(dir,{recursive:true});
694
+ const nonce=randomBytes(32).toString('hex');
695
+ const resultSessionId=randomBytes(12).toString('hex');
696
+ const marker={schema:'deadbyte.remote-session.v2',owner_pid:process.pid,guardian_pid:0,session_nonce:nonce,
697
+ result_session_id:resultSessionId,started_at_utc:new Date().toISOString()};
698
+ marker.owner_identity=queryForegroundIdentity(process.pid);
699
+ if(!marker.owner_identity)throw new Error('remote foreground identity unavailable');
700
+ const markerPath=remoteSessionMarker(ctx.root);
701
+ try{await writeFile(markerPath,`${JSON.stringify(marker)}\n`,{encoding:'utf8',flag:'wx'});}
702
+ catch(error){if(error?.code==='EEXIST') throw new Error('remote session already claimed');throw error;}
703
+ let guardian=null;
704
+ try{
705
+ guardian=spawn(process.execPath,[GUARDIAN_SCRIPT,'--state-root',ctx.root,'--owner-pid',String(process.pid),'--session-nonce',nonce,'--owner-channel','ipc'],{
706
+ detached:true,stdio:['ignore','ignore','ignore','ipc'],windowsHide:true,shell:false,env:process.env
707
+ });
708
+ if(!Number.isInteger(guardian.pid)||guardian.pid<1) throw new Error('remote session guardian did not start');
709
+ marker.guardian_pid=guardian.pid;
710
+ marker.guardian_identity=queryForegroundIdentity(guardian.pid);
711
+ if(!marker.guardian_identity||marker.guardian_identity.parent_pid!==process.pid)throw new Error('remote guardian parent identity mismatch');
712
+ await writeFile(markerPath,`${JSON.stringify(marker)}\n`,'utf8');
713
+ guardian.unref();
714
+ guardian.channel?.unref();
715
+ return marker;
716
+ }catch(error){
717
+ try{guardian?.kill();}catch{}
718
+ try{
719
+ const snapshot=await readRemoteMarkerSnapshot(markerPath);
720
+ if(!snapshot||snapshot.marker?.owner_pid!==marker.owner_pid||snapshot.marker?.session_nonce!==marker.session_nonce)
721
+ throw new Error('claim marker ownership changed or missing');
722
+ if(deletePinnedRemoteMarker(markerPath,snapshot.sha256)!=='REMOVED')
723
+ throw new Error('claim marker changed before native deletion');
724
+ }catch(cleanupError){
725
+ throw new AggregateError([error,cleanupError],
726
+ 'remote session claim failed; marker cleanup unverified and marker retained');
727
+ }
728
+ throw error;
729
+ }
730
+ }
731
+ async function releaseRemoteSession(ctx,marker,{displayMode='unicode'}={}){
732
+ let snapshot;
733
+ try{snapshot=await readRemoteMarkerSnapshot(remoteSessionMarker(ctx.root));}
734
+ catch(error){writeRemote(process.stderr,`${remoteTag('warn',displayMode)} remote session marker cleanup: ${String(error?.message||error)}\n`,displayMode);throw new Error(`remote session ownership marker retained: ${String(error?.message||error)}`);}
735
+ const current=snapshot?.marker;
736
+ const same=current?.schema==='deadbyte.remote-session.v2'&&current.owner_pid===marker.owner_pid&&current.session_nonce===marker.session_nonce;
737
+ if(!same)throw new Error('remote session ownership marker changed or missing; marker retained');
738
+ if(deletePinnedRemoteMarker(remoteSessionMarker(ctx.root),snapshot.sha256)!=='REMOVED')
739
+ throw new Error('remote session ownership marker replaced before native deletion; marker retained');
740
+ }
741
+ async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,filterExpression='',mode='default',hostCommandPins=[]}={}){
742
+ const display=prepareRemoteConsole();
743
+ const displayMode=display.mode;
744
+ const ctx=await remoteContext();
745
+ if(displayMode==='ascii') writeRemote(process.stdout,`[DISPLAY] ASCII fallback: ${display.reason}\n`,displayMode);
746
+ if(resumeCursor!==null) await replayRemoteResults(ctx.root,resumeCursor,{displayMode});
747
+ const startupLock=await acquireForegroundSessionLock({root:ctx.root,powershell:ctx.powershell});
748
+ let marker;
749
+ try{
750
+ startupLock.assertHeld();
751
+ const migrationContext=await recoverRemoteSession(ctx,{displayMode});
752
+ startupLock.assertHeld();
753
+ await synchronizeForegroundRuntime(ctx,{displayMode,migrationContext,hostCommandPins});
754
+ startupLock.assertHeld();
755
+ marker=await claimRemoteSession(ctx);
756
+ startupLock.assertHeld();
757
+ }finally{await startupLock.release();}
758
+ const resultJournal=await openRemoteResultJournal({
759
+ filePath:remoteResultJournalPath(ctx.root,marker.result_session_id),sessionId:marker.result_session_id,maxEntries:2048
760
+ });
761
+ const rootPaths=await readLiveRootPaths(ctx.root);
762
+ const activityTracker=createLiveTimelineTracker({verbose,displayMode,rootPaths,trace,...defaultForegroundTimelineOptions({verbose})});
763
+ const timelineFilter=filterExpression?createLiveTimelineFilter(filterExpression):null;
764
+ const timelineWriter=createLiveTimelineWriter({stream:process.stdout,displayMode,rewrite:false});
765
+ const aborter=new AbortController();
766
+ const abort=()=>aborter.abort();
767
+ const watchStdin=!process.stdin.isTTY;
768
+ process.once('SIGINT',abort);process.once('SIGTERM',abort);
769
+ if(watchStdin){process.stdin.once('end',abort);process.stdin.once('close',abort);process.stdin.resume();}
770
+ let ready=false;
771
+ let logTask=null;
772
+ let sessionError=null;
773
+ try{
774
+ process.stdout.write('DEADBYTE MCP\n');
775
+ process.stdout.write(`${formatRemoteLifecycle('device_start',{displayMode})}\n`);
776
+ process.stdout.write(`${formatRemoteLifecycle(resumeCursor!==null?'session_restored':'session_new',{displayMode})}\n`);
777
+ const logOffsets=await snapshotRemoteLogOffsets(ctx.root);
778
+ logTask=tailRemoteLogs(ctx.root,aborter.signal,{journal:resultJournal,offsets:logOffsets,activityTracker,timelineFilter,timelineWriter,verbose,displayMode});
779
+ process.stdout.write('[BOOT] Starting local + remote runtime...\n');
780
+ const preflight=invokeController(ctx,'status',{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
781
+ let startText=preflight.stdout||'';
782
+ if(preflight.status===0&&controllerRuntimeReady(preflight.stdout)){
783
+ process.stdout.write('[BOOT] Runtime already ONLINE; reusing verified bridge/cloud/supervisor.\n');
784
+ }else{
785
+ const recovery=preflight.status===0&&controllerRuntimeDegraded(preflight.stdout);
786
+ process.stdout.write(recovery?'[RECOVERY] Runtime failed readiness; restarting verified owned components...\n':'[BOOT] Starting bridge/runtime...\n');
787
+ const startCommand=recovery?'restart':'start';
788
+ const controllerLogDir=path.join(ctx.root,'runtime','logs');
789
+ await mkdir(controllerLogDir,{recursive:true});
790
+ const launchOutput={
791
+ stdout_path:path.join(controllerLogDir,`controller-${startCommand}.stdout.log`),
792
+ stderr_path:path.join(controllerLogDir,`controller-${startCommand}.stderr.log`)
793
+ };
794
+ const launch=invokeController(ctx,startCommand,{allowFailure:true,redirectOutput:launchOutput,timeoutMs:REMOTE_START_TIMEOUT_MS});
795
+ if(launch.status!==0){
796
+ const evidence=await readControllerStartFailure(ctx.root,startCommand);
797
+ const fallback=evidence?'':await readTailBounded(launchOutput.stderr_path,2048);
798
+ const detail=evidence?`; controller-start-failure: ${evidence.message}`:(fallback?`; controller-stderr: ${fallback}`:'');
799
+ throw new Error(`remote controller '${startCommand}' failed exit=${launch.status}${detail}`);
800
+ }
801
+ const started=invokeController(ctx,'status',{capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
802
+ if(started.status!==0||!controllerRuntimeReady(started.stdout)) throw new Error(`remote controller ${recovery?'restart':'start'} returned without a verified ONLINE runtime`);
803
+ startText=started.stdout||'';
804
+ }
805
+ process.stdout.write(`${formatRemoteLifecycle('bridge_connected',{displayMode})}\n`);
806
+ process.stdout.write(`${formatRemoteLifecycle('cloud_connected',{displayMode})}\n`);
807
+ const desired=authorityVector(mode);
808
+ const priorAuthority=observedAuthority(ctx);
809
+ for(const plane of ['host','desktop','autonomous','coding','process'].filter(name=>!desired[name]&&priorAuthority.authority[name].armed)){
810
+ const command=`disarm${plane}`;
811
+ writeRemote(process.stdout,`${remoteTag('auth',displayMode)} Revoking unwanted ${plane} authority...\n`,displayMode);
812
+ invokeController(ctx,command,{capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
813
+ }
814
+ for(const plane of AUTHORITY_PLANES.filter(name=>desired[name])){
815
+ const command=`arm${plane}`;
816
+ writeRemote(process.stdout,`${remoteTag('auth',displayMode)} Arming ${command.slice(3)}...\n`,displayMode);
817
+ invokeController(ctx,command,{capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
818
+ }
819
+ const url=startText.match(/^URL\s*:\s*(\S+)/m)?.[1]||'';
820
+ writeRemote(process.stdout,`${remoteTag('auth',displayMode)} Verifying exact ${mode} authority vector...\n`,displayMode);
821
+ const current=await readActiveRuntimeBinding(ctx.root);
822
+ const authority=assertAuthorityVector(assertRuntimeReadyObservation(observedAuthority(ctx),{
823
+ coreRoot:current?.release_root??null
824
+ }),desired);
825
+ process.stdout.write('[BOOT] Verifying public signed MCP execution path...\n');
826
+ invokeController(ctx,'machinetest',{capture:true,echoCaptured:false,timeoutMs:REMOTE_START_TIMEOUT_MS,probeMode:mode==='default'?'revoked':mode});
827
+ ready=true;
828
+ writeRemote(process.stdout,`${remoteTag('auth',displayMode)} ${authorityBanner(authority)}\n`,displayMode);
829
+ process.stdout.write('[OK] ONLINE\n');
830
+ process.stdout.write(`${formatRemoteLifecycle('ready',{displayMode})}\n`);
831
+ if(url) writeRemote(process.stdout,`URL: ${url}\n`,displayMode);
832
+ await logTask;
833
+ }catch(error){sessionError=error;throw error;}
834
+ finally{
835
+ const wasAborted=aborter.signal.aborted;
836
+ aborter.abort();
837
+ if(logTask) await logTask.catch(()=>{});
838
+ timelineWriter.flush();
839
+ const liveLogDelivery=timelineWriter.retention();
840
+ if(liveLogDelivery.suppressed_since_drain>0){
841
+ process.stderr.write(`[WARN] LIVELOG ${liveLogDelivery.suppressed_since_drain} display lines still suppressed at disconnect due to stdout backpressure; inspect signed audit and runtime logs.\n`);
842
+ }
843
+ for(const line of activityTracker.reconcileInterrupted('session_end')) process.stdout.write(`${line}\n`);
844
+ const filterRetention=timelineFilter?.retention();
845
+ if(filterRetention?.evicted>0) process.stdout.write(`[WARN] LIVELOG filter evicted ${filterRetention.evicted} presentation identities; full audit remains on disk.\n`);
846
+ process.stdout.write(`${activityTracker.summary()}\n`);
847
+ process.removeListener('SIGINT',abort);process.removeListener('SIGTERM',abort);
848
+ if(watchStdin){process.stdin.removeListener('end',abort);process.stdin.removeListener('close',abort);process.stdin.pause();}
849
+ process.stdout.write(`${formatRemoteLifecycle('cleanup',{displayMode})}\n`);
850
+ if(ready||!wasAborted) process.stdout.write('[INFO] Disconnecting; revoking authority...\n');
851
+ try{await cleanupRemoteSession(ctx,{displayMode});}
852
+ catch(cleanupError){
853
+ if(sessionError) throw new AggregateError([sessionError,cleanupError],
854
+ `remote activation failed: ${String(sessionError?.message||sessionError)}; cleanup failed, ownership marker retained: ${String(cleanupError?.message||cleanupError)}`);
855
+ throw cleanupError;
856
+ }
857
+ try{await releaseRemoteSession(ctx,marker,{displayMode});}
858
+ catch(markerError){
859
+ if(sessionError)throw new AggregateError([sessionError,markerError],
860
+ `remote activation failed: ${String(sessionError?.message||sessionError)}; marker cleanup failed: ${String(markerError?.message||markerError)}`);
861
+ throw markerError;
862
+ }
863
+ }
864
+ }
865
+ async function runRemote(args){
866
+ let verbose=false,trace=false,filterExpression='',resumeCursor=null,mode='default';
867
+ const hostCommandPinValues=[];
868
+ const positional=[];
869
+ for(let index=0;index<args.length;index++){
870
+ const arg=args[index];
871
+ if(arg==='--verbose'||arg==='-v'){verbose=true;continue;}
872
+ if(arg==='--trace'){trace=true;continue;}
873
+ if(arg==='--host-command-pin'){
874
+ const value=String(args[++index]??'');
875
+ hostCommandPinValues.push(value);continue;
876
+ }
877
+ if(['--operator','--desktop','--host','--full'].includes(arg)){
878
+ if(mode!=='default') throw new Error('remote authority modes are mutually exclusive');
879
+ mode=arg.slice(2);continue;
880
+ }
881
+ if(arg==='--filter'){
882
+ if(filterExpression) throw new Error('remote --filter may be specified once');
883
+ if(index+1>=args.length) throw new Error('remote --filter requires an expression');
884
+ filterExpression=args[++index];continue;
885
+ }
886
+ if(arg==='--resume'){
887
+ if(resumeCursor!==null) throw new Error('remote --resume may be specified once');
888
+ if(index+1>=args.length) throw new Error('remote --resume requires a cursor');
889
+ resumeCursor=args[++index];continue;
890
+ }
891
+ positional.push(arg);
892
+ }
893
+ let hostCommandPins;
894
+ try{hostCommandPins=normalizeHostCommandPins(hostCommandPinValues);}
895
+ catch(error){throw new Error(`remote --host-command-pin ${error.message}`);}
896
+ const presentation=verbose||trace||Boolean(filterExpression)||resumeCursor!==null||mode!=='default'||hostCommandPins.length>0;
897
+ if(positional.length===0){await runRemoteSession({resumeCursor,verbose,trace,filterExpression,mode,hostCommandPins});return;}
898
+ if(presentation) throw new Error('remote presentation flags are valid only for foreground session');
899
+ if(positional.length>1) throw new Error('remote accepts one controller command');
900
+ const command=positional[0];if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
901
+ const ctx=await remoteContext();
902
+ await synchronizeOneShotRuntime(ctx,{displayMode:'ascii'});
903
+ const result=invokeController(ctx,command,{allowFailure:true});
904
+ process.exitCode=result.status;
905
+ }
906
+ async function runSupervisor(args){
907
+ const action=args[0]||'status';
908
+ if(args.length>1||!Object.hasOwn(SUPERVISOR_COMMANDS,action)) throw new Error(`unsupported supervisor command '${action}'`);
909
+ const ctx=await remoteContext();
910
+ const result=invokeController(ctx,SUPERVISOR_COMMANDS[action],{allowFailure:true});
911
+ process.exitCode=result.status;
912
+ }
913
+ async function runAudit(args){
914
+ const command=args[0]||'status';if(args.length>1) throw new Error('audit accepts one command');
915
+ if(!['verify','status'].includes(command)) throw new Error(`unsupported audit command '${command}'`);
916
+ const result=await verifyAuditChain({stateRoot:stateRoot(),verifyKeyPath:process.env.DEADBYTE_VERIFY_KEY||defaultPublicKeyPath(),requireComplete:command==='verify'});
917
+ const output=command==='status'?{...result,mode:'status'}:result;
918
+ process.stdout.write(`${JSON.stringify(output,null,2)}\n`);
919
+ if(command==='verify'&&!result.ok) process.exitCode=1;
920
+ }
921
+ async function main(argv){
922
+ const [command,...rest]=argv;
923
+ if(command==='--version'||command==='-v'||command==='version'){
924
+ if(rest.length) throw new Error('version accepts no arguments');
925
+ process.stdout.write(`${DEADBYTE_VERSION}\n`);return;
926
+ }
927
+ if(!command||command==='help'||command==='--help'||command==='-h'){printHelp();return;}
928
+ if(command==='doctor'){
929
+ if(rest.length) throw new Error('doctor accepts no arguments');
930
+ await printDoctor();return;
931
+ }
932
+ if(command==='mcp'){
933
+ if(rest.length) throw new Error('mcp accepts no arguments');
934
+ await import('./mcp-server.mjs');
935
+ return;
936
+ }
937
+ if(command==='remote'){await runRemote(rest);return;}
938
+ if(command==='supervisor'){await runSupervisor(rest);return;}
939
+ if(command==='audit'){await runAudit(rest);return;}
940
+ if(command==='diagnostics'){
941
+ if(rest.length!==3)throw new Error('diagnostics export --output or verify --input; no network upload supported');
942
+ if(rest[0]==='export'&&rest[1]==='--output'){
943
+ const report=await exportOfflineDiagnostics({stateRoot:stateRoot(),outputPath:rest[2]});
944
+ process.stdout.write(`${JSON.stringify(report)}\n`);
945
+ return;
946
+ }
947
+ if(rest[0]==='verify'&&rest[1]==='--input'){
948
+ const file=path.resolve(rest[2]);
949
+ await requireRegular(file,'diagnostic ZIP');
950
+ const report=verifyOfflineDiagnosticsZip(await readFile(file));
951
+ process.stdout.write(`${JSON.stringify(report)}\n`);
952
+ return;
953
+ }
954
+ throw new Error('unsupported diagnostics mode; network upload is forbidden');
955
+ }
956
+ if(command==='inspect'){
957
+ const result=await runNativeInspect(rest);
958
+ process.stdout.write(`${JSON.stringify(result)}\n`);
959
+ if(result.status!=='ok')process.exitCode=2;
960
+ return;
961
+ }
962
+ throw new Error(`unknown command '${command}'`);
963
+ }
964
+
965
+ main(process.argv.slice(2)).catch(error=>{process.stderr.write(`DEADBYTE CLI: ${String(error?.message||error)}\n`);process.exitCode=1;});