deadbyte-mcp 0.11.2 → 0.12.0
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 +101 -39
- package/README.txt +10 -9
- package/acceptance/r40/desktop-live-drift.mjs +106 -0
- package/acceptance/r40/desktop-live-ground.mjs +110 -0
- package/acceptance/r40/desktop-live-input.mjs +128 -0
- package/acceptance/r40/desktop-live-observe.mjs +100 -0
- package/acceptance/r40/desktop-live-uia.mjs +121 -0
- package/bin/deadbyte-desktop-input-r40.exe +0 -0
- package/bin/deadbyte-desktop-input-r40.obj +0 -0
- package/bin/deadbyte-desktop-observer-r40.exe +0 -0
- package/bin/deadbyte-desktop-observer-r40.obj +0 -0
- package/bin/deadbyte-desktop-uia-r40.exe +0 -0
- package/bin/deadbyte-desktop-uia-r40.obj +0 -0
- package/controller/README.TXT +7 -5
- package/controller/deadbyte-controller.config.psd1.example +3 -0
- package/controller/deadbyte-controller.ps1 +149 -4
- package/controller/deadbyte-desktop-policy.json +41 -0
- package/docs/ARCHITECTURE.md +6 -1
- package/docs/superpowers/specs/2026-09-19-r40-desktop-computer-use-design.md +214 -0
- package/native/deadbyte-desktop-input.cpp +455 -0
- package/native/deadbyte-desktop-observer.cpp +253 -0
- package/native/deadbyte-desktop-uia.cpp +307 -0
- package/package.json +10 -8
- package/scripts/build-desktop-r40.ps1 +106 -0
- package/scripts/deferred-slot-operation-r39.mjs +124 -0
- package/scripts/deferred-slot-operation-r40.mjs +126 -0
- package/scripts/final-closure-r39.mjs +113 -0
- package/scripts/final-closure-r40.mjs +114 -0
- package/scripts/final-closure-verify-r39.mjs +208 -0
- package/scripts/final-closure-verify-r40.mjs +212 -0
- package/scripts/gate-windows-r39.ps1 +42 -0
- package/scripts/gate-windows-r40.ps1 +43 -0
- package/scripts/init-desktop-policy-r40.ps1 +79 -0
- package/scripts/r40-compact-ledger-probe.mjs +4 -0
- package/scripts/release-parity-r39.mjs +200 -0
- package/scripts/release-parity-r40.mjs +212 -0
- package/scripts/release-parity-tests-r39.ps1 +9 -0
- package/scripts/release-parity-tests-r40.ps1 +9 -0
- package/scripts/windows-gate-evidence-r39.mjs +52 -0
- package/scripts/windows-gate-evidence-r40.mjs +52 -0
- package/src/autonomous-context-engine.mjs +2 -1
- package/src/autonomous-planner-contracts.mjs +19 -1
- package/src/autonomous-planner.mjs +22 -0
- package/src/autonomous-runtime.mjs +144 -24
- package/src/capability-ledger.mjs +6 -1
- package/src/compact-gateway-mcp-tools.mjs +14 -2
- package/src/deadbyte-cli.mjs +79 -47
- package/src/desktop-contract.mjs +263 -0
- package/src/desktop-input-r40.mjs +148 -0
- package/src/desktop-mcp-tools-r40.mjs +126 -0
- package/src/desktop-memory-r40.mjs +272 -0
- package/src/desktop-observer-r40.mjs +231 -0
- package/src/desktop-policy-r40.mjs +110 -0
- package/src/desktop-runtime-r40.mjs +318 -0
- package/src/desktop-uia-r40.mjs +123 -0
- package/src/mcp-server.mjs +74 -19
- package/src/r40-compact-disarmed-probe.mjs +71 -0
- package/src/r40-full-surface-probe.mjs +34 -0
- package/src/release-generation.mjs +38 -5
- package/src/remote-console.mjs +41 -0
- package/src/remote-live-log.mjs +215 -45
- package/src/remote-mcp-desktop-r40-smoke-client.mjs +70 -0
- package/src/remote-mcp-r40-parity-client.mjs +200 -0
- package/src/remote-mcp-smoke-client.mjs +20 -6
- package/src/remote-result-journal.mjs +10 -2
- package/src/runtime-update.mjs +55 -17
- package/src/tool-audit.mjs +29 -2
- package/src/version.mjs +1 -1
- package/test/autonomous-capability-r36.test.mjs +43 -1
- package/test/autonomous-desktop-r40.test.mjs +188 -0
- package/test/capability-ledger-r36.test.mjs +1 -0
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/compact-catalog-r36.test.mjs +2 -2
- package/test/compact-gateway-r36.test.mjs +56 -4
- package/test/compact-max-catalog-r40.test.mjs +95 -0
- package/test/controller-desktop-r40.test.mjs +85 -0
- package/test/core.test.mjs +1 -1
- package/test/desktop-adversarial-r40.test.mjs +62 -0
- package/test/desktop-contract-r40.test.mjs +200 -0
- package/test/desktop-input-r40.test.mjs +63 -0
- package/test/desktop-mcp-tools-r40.test.mjs +61 -0
- package/test/desktop-memory-r40.test.mjs +141 -0
- package/test/desktop-observer-r40.test.mjs +119 -0
- package/test/desktop-policy-r40.test.mjs +111 -0
- package/test/desktop-runtime-r40.test.mjs +278 -0
- package/test/desktop-uia-r40.test.mjs +42 -0
- package/test/full-max-catalog-r40.test.mjs +91 -0
- package/test/helpers/autonomous-r34-fixture.mjs +3 -3
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-closeout-regression.test.mjs +8 -8
- package/test/r33-finalization.test.mjs +14 -14
- package/test/r37-remote-tool-call-timeline.test.mjs +7 -6
- package/test/r39-finalization.test.mjs +74 -0
- package/test/r40-finalization.test.mjs +118 -0
- package/test/release-generation.test.mjs +41 -9
- package/test/release-version.test.mjs +19 -58
- package/test/remote-console-r40.test.mjs +38 -0
- package/test/remote-live-log-r37.test.mjs +89 -32
- package/test/remote-r24-catalog.test.mjs +12 -3
- package/test/remote-result-journal.test.mjs +15 -0
- package/test/remote-session-cli.test.mjs +112 -55
- package/test/runtime-self-update.test.mjs +44 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
export const DESKTOP_UIA_GROUND_SCHEMA='deadbyte.desktop-uia-ground.v1';
|
|
7
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
8
|
+
const HWND=/^0x[0-9a-f]{1,16}$/;
|
|
9
|
+
const QUERY_KINDS=new Set(['root','name_exact','name_contains','automation_id_exact','control_type']);
|
|
10
|
+
const sha256Bytes=value=>createHash('sha256').update(value).digest('hex');
|
|
11
|
+
|
|
12
|
+
function insist(c,m){if(!c)throw new Error(m);}
|
|
13
|
+
function integer(v,label,{min=Number.MIN_SAFE_INTEGER,max=Number.MAX_SAFE_INTEGER}={}){
|
|
14
|
+
insist(Number.isSafeInteger(v)&&v>=min&&v<=max,label+' invalid');return v;
|
|
15
|
+
}
|
|
16
|
+
function hwnd(v){const s=String(v??'').toLowerCase();insist(HWND.test(s),'desktop UIA hwnd invalid');return s;}
|
|
17
|
+
function queryInput(query){
|
|
18
|
+
insist(query&&typeof query==='object'&&!Array.isArray(query),'desktop UIA query invalid');
|
|
19
|
+
const kind=String(query.kind??'');
|
|
20
|
+
insist(QUERY_KINDS.has(kind),'desktop UIA query kind invalid');
|
|
21
|
+
const value=query.value??'';
|
|
22
|
+
insist(typeof value==='string'&&!value.includes('\0'),'desktop UIA query value invalid');
|
|
23
|
+
const bytes=Buffer.from(value,'utf8');
|
|
24
|
+
if(kind==='root') insist(bytes.length===0,'desktop UIA root query cannot carry value');
|
|
25
|
+
else insist(bytes.length>=1&&bytes.length<=512,'desktop UIA query value bytes invalid');
|
|
26
|
+
const maxResults=integer(query.max_results??8,'desktop UIA max_results',{min:1,max:16});
|
|
27
|
+
const ordinal=integer(query.ordinal??0,'desktop UIA ordinal',{min:0,max:maxResults-1});
|
|
28
|
+
const querySha256=sha256Bytes(Buffer.from(JSON.stringify({kind,value,max_results:maxResults,ordinal}),'utf8'));
|
|
29
|
+
return {kind,value,bytes,maxResults,ordinal,querySha256};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function serializeDesktopUiaRequest({expectedWindow,query}={}){
|
|
33
|
+
insist(expectedWindow&&typeof expectedWindow==='object','desktop UIA expected window missing');
|
|
34
|
+
const expectedHwnd=hwnd(expectedWindow.hwnd);
|
|
35
|
+
const expectedPid=integer(expectedWindow.pid,'desktop UIA expected pid',{min:1,max:0x7fffffff});
|
|
36
|
+
const q=queryInput(query);
|
|
37
|
+
const header=Buffer.from([
|
|
38
|
+
'DEADBYTE_DESKTOP_UIA_V1',
|
|
39
|
+
'expected_hwnd='+expectedHwnd,
|
|
40
|
+
'expected_pid='+expectedPid,
|
|
41
|
+
'query_kind='+q.kind,
|
|
42
|
+
'max_results='+q.maxResults,
|
|
43
|
+
'query_bytes='+q.bytes.length,
|
|
44
|
+
''
|
|
45
|
+
].join('\n'),'utf8');
|
|
46
|
+
const request=Buffer.concat([header,q.bytes]);
|
|
47
|
+
insist(request.length<=8192,'desktop UIA request exceeds bound');
|
|
48
|
+
return {request,query:q};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function parseDesktopUiaResponse(bytes,{expectedWindow,query,expectedBounds}={}){
|
|
52
|
+
insist(Buffer.isBuffer(bytes)&&bytes.length>=2&&bytes.length<=256*1024,'desktop UIA response invalid');
|
|
53
|
+
let raw;try{raw=JSON.parse(bytes.toString('utf8').trim());}catch{throw new Error('desktop UIA response JSON invalid');}
|
|
54
|
+
insist(raw?.schema===DESKTOP_UIA_GROUND_SCHEMA,'desktop UIA response schema mismatch');
|
|
55
|
+
const q=queryInput(query);
|
|
56
|
+
insist(String(raw.query_kind)===q.kind,'desktop UIA response query mismatch');
|
|
57
|
+
insist(hwnd(raw.foreground_hwnd)===hwnd(expectedWindow.hwnd),'desktop UIA response hwnd mismatch');
|
|
58
|
+
insist(integer(raw.foreground_pid,'desktop UIA response pid',{min:1,max:0x7fffffff})===expectedWindow.pid,'desktop UIA response pid mismatch');
|
|
59
|
+
integer(raw.visited,'desktop UIA visited',{min:0,max:2048});
|
|
60
|
+
insist(typeof raw.truncated==='boolean','desktop UIA truncated invalid');
|
|
61
|
+
insist(Array.isArray(raw.matches)&&raw.matches.length<=q.maxResults,'desktop UIA matches invalid');
|
|
62
|
+
const left=integer(expectedBounds.left,'desktop UIA bounds left',{min:-131072,max:131072});
|
|
63
|
+
const top=integer(expectedBounds.top,'desktop UIA bounds top',{min:-131072,max:131072});
|
|
64
|
+
const width=integer(expectedBounds.width,'desktop UIA bounds width',{min:1,max:131072});
|
|
65
|
+
const height=integer(expectedBounds.height,'desktop UIA bounds height',{min:1,max:131072});
|
|
66
|
+
const matches=[];
|
|
67
|
+
for(const item of raw.matches){
|
|
68
|
+
const rect={
|
|
69
|
+
left:integer(item.left,'desktop UIA rect left',{min:-131072,max:131072}),
|
|
70
|
+
top:integer(item.top,'desktop UIA rect top',{min:-131072,max:131072}),
|
|
71
|
+
width:integer(item.width,'desktop UIA rect width',{min:1,max:131072}),
|
|
72
|
+
height:integer(item.height,'desktop UIA rect height',{min:1,max:131072})
|
|
73
|
+
};
|
|
74
|
+
const inside=rect.left>=left&&rect.top>=top&&rect.left+rect.width<=left+width&&rect.top+rect.height<=top+height;
|
|
75
|
+
if(!inside) continue;
|
|
76
|
+
matches.push(Object.freeze({
|
|
77
|
+
rect:Object.freeze(rect),
|
|
78
|
+
control_type:integer(item.control_type,'desktop UIA control_type',{min:0,max:100000}),
|
|
79
|
+
enabled:item.enabled===true,offscreen:item.offscreen===true
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
return Object.freeze({
|
|
83
|
+
status:'ok',query_sha256:q.querySha256,query_kind:q.kind,ordinal:q.ordinal,
|
|
84
|
+
visited:raw.visited,truncated:raw.truncated,matches:Object.freeze(matches),
|
|
85
|
+
provider_receipt_sha256:sha256Bytes(bytes)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function invokeNativeDesktopGrounder({grounderPath,expectedGrounderSha256,timeoutMs=5000,expectedWindow,expectedBounds,query}={}){
|
|
90
|
+
insist(typeof grounderPath==='string'&&path.isAbsolute(grounderPath),'desktop grounderPath must be absolute');
|
|
91
|
+
insist(HEX64.test(expectedGrounderSha256??''),'desktop expectedGrounderSha256 invalid');
|
|
92
|
+
integer(timeoutMs,'desktop UIA timeoutMs',{min:100,max:30000});
|
|
93
|
+
const exe=await readFile(grounderPath);
|
|
94
|
+
insist(exe.length>0&&exe.length<=4*1024*1024,'desktop UIA binary size invalid');
|
|
95
|
+
insist(sha256Bytes(exe)===expectedGrounderSha256,'desktop UIA executable hash mismatch');
|
|
96
|
+
const packed=serializeDesktopUiaRequest({expectedWindow,query});
|
|
97
|
+
return await new Promise((resolve,reject)=>{
|
|
98
|
+
const child=spawn(grounderPath,[],{shell:false,windowsHide:true,stdio:['pipe','pipe','pipe']});
|
|
99
|
+
const stdout=[];let bytes=0,stderr='',done=false,timer=null;
|
|
100
|
+
const finish=(error,value)=>{if(done)return;done=true;if(timer!==null)clearTimeout(timer);error?reject(error):resolve(value);};
|
|
101
|
+
child.stdout.on('data',chunk=>{bytes+=chunk.length;if(bytes>256*1024){try{child.kill('SIGKILL');}catch{};return finish(new Error('desktop UIA stdout exceeds bound'));}stdout.push(Buffer.from(chunk));});
|
|
102
|
+
child.stderr.on('data',chunk=>{stderr+=chunk.toString('utf8');if(Buffer.byteLength(stderr,'utf8')>64*1024){try{child.kill('SIGKILL');}catch{};finish(new Error('desktop UIA stderr exceeds bound'));}});
|
|
103
|
+
child.once('error',error=>finish(error));
|
|
104
|
+
child.once('exit',(code,signal)=>{
|
|
105
|
+
if(code!==0)return finish(new Error('desktop UIA exit='+code+' signal='+(signal??'none')+' stderr='+stderr.slice(-8192)));
|
|
106
|
+
try{finish(null,parseDesktopUiaResponse(Buffer.concat(stdout),{expectedWindow,query,expectedBounds}));}
|
|
107
|
+
catch(error){finish(error);}
|
|
108
|
+
});
|
|
109
|
+
child.stdin.on('error',error=>finish(error));
|
|
110
|
+
child.stdin.end(packed.request);
|
|
111
|
+
timer=setTimeout(()=>{try{child.kill('SIGKILL');}catch{};finish(new Error('desktop UIA timed out after '+timeoutMs+'ms'));},timeoutMs);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function createNativeDesktopGrounder({grounderPath,expectedGrounderSha256,timeoutMs=5000}={}){
|
|
116
|
+
return async({frame,query}={})=>{
|
|
117
|
+
insist(frame?.active_window,'desktop UIA grounding requires foreground window');
|
|
118
|
+
return invokeNativeDesktopGrounder({
|
|
119
|
+
grounderPath,expectedGrounderSha256,timeoutMs,
|
|
120
|
+
expectedWindow:frame.active_window,expectedBounds:frame.bounds,query
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
}
|
package/src/mcp-server.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { executeProof } from './executor.mjs';
|
|
|
9
9
|
import { CONTAINED_TRANSFORM_OPERATIONS, executeContainedTransform } from './contained-executor.mjs';
|
|
10
10
|
import { resolveExistingInside, resolveWorkspace } from './path-policy.mjs';
|
|
11
11
|
import { verifyReceipt } from './verifier.mjs';
|
|
12
|
-
import { defaultPrivateKeyPath, defaultPublicKeyPath } from './trust.mjs';
|
|
12
|
+
import { defaultPrivateKeyPath, defaultPublicKeyPath, loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
|
|
13
13
|
import { DEADBYTE_VERSION } from './version.mjs';
|
|
14
14
|
import { currentIngressAdapterMode, TUNNEL_STDIO_PROBE_COMPAT_MODE } from './ingress-mode.mjs';
|
|
15
15
|
import { installTunnelStdioProbeCompat } from './tunnel-stdio-compat.mjs';
|
|
@@ -31,6 +31,13 @@ import { createMachineFsRuntime } from './machine-fs.mjs';
|
|
|
31
31
|
import { registerMachineFsTools } from './machine-fs-mcp-tools.mjs';
|
|
32
32
|
import { createObservationRuntime } from './observation-runtime.mjs';
|
|
33
33
|
import { registerObservationTools } from './observation-mcp-tools.mjs';
|
|
34
|
+
import { loadDesktopPolicy } from './desktop-policy-r40.mjs';
|
|
35
|
+
import { createDesktopObserverRuntime } from './desktop-observer-r40.mjs';
|
|
36
|
+
import { createNativeDesktopInputProvider } from './desktop-input-r40.mjs';
|
|
37
|
+
import { createNativeDesktopGrounder } from './desktop-uia-r40.mjs';
|
|
38
|
+
import { createDesktopRuntime } from './desktop-runtime-r40.mjs';
|
|
39
|
+
import { createDesktopMemoryProvider } from './desktop-memory-r40.mjs';
|
|
40
|
+
import { registerDesktopTools } from './desktop-mcp-tools-r40.mjs';
|
|
34
41
|
import { loadAutonomousPolicy } from './autonomous-policy.mjs';
|
|
35
42
|
import { createNativeDiffRuntime } from './native-diff.mjs';
|
|
36
43
|
import { createAutonomousRuntime } from './autonomous-runtime.mjs';
|
|
@@ -125,6 +132,44 @@ const observationRuntime = observationRuntimeMode === '1'
|
|
|
125
132
|
stateRoot: runtimeStateRoot })
|
|
126
133
|
: null;
|
|
127
134
|
|
|
135
|
+
const desktopRuntimeMode=process.env.DEADBYTE_DESKTOP_RUNTIME??'0';
|
|
136
|
+
if(!['0','1'].includes(desktopRuntimeMode)) throw new Error('DEADBYTE_DESKTOP_RUNTIME must be 0 or 1');
|
|
137
|
+
const localDesktopPolicyPath=path.join(packageRoot,'controller','deadbyte-desktop-policy.json');
|
|
138
|
+
let desktopPolicyPath=process.env.DEADBYTE_DESKTOP_POLICY??'';
|
|
139
|
+
if(desktopRuntimeMode==='1'&&!desktopPolicyPath){
|
|
140
|
+
try{await access(localDesktopPolicyPath);desktopPolicyPath=localDesktopPolicyPath;}
|
|
141
|
+
catch(error){if(error?.code!=='ENOENT')throw error;}
|
|
142
|
+
}
|
|
143
|
+
if(desktopRuntimeMode==='1'&&!desktopPolicyPath) throw new Error('DEADBYTE_DESKTOP_RUNTIME=1 requires a desktop policy');
|
|
144
|
+
const desktopPolicy=desktopRuntimeMode==='1'?await loadDesktopPolicy(desktopPolicyPath):null;
|
|
145
|
+
const desktopObserverRuntime=desktopPolicy?createDesktopObserverRuntime({
|
|
146
|
+
observerPath:desktopPolicy.observer.executable,expectedObserverSha256:desktopPolicy.observer.sha256,
|
|
147
|
+
maxFrameBytes:desktopPolicy.limits.max_frame_bytes,timeoutMs:desktopPolicy.limits.capture_timeout_ms,
|
|
148
|
+
allowInputDesktops:desktopPolicy.allowed_input_desktops
|
|
149
|
+
}):null;
|
|
150
|
+
const desktopInputProvider=desktopPolicy?createNativeDesktopInputProvider({
|
|
151
|
+
inputPath:desktopPolicy.input.executable,expectedInputSha256:desktopPolicy.input.sha256,
|
|
152
|
+
timeoutMs:desktopPolicy.limits.input_timeout_ms
|
|
153
|
+
}):null;
|
|
154
|
+
const desktopGrounderProvider=desktopPolicy?createNativeDesktopGrounder({
|
|
155
|
+
grounderPath:desktopPolicy.grounder.executable,expectedGrounderSha256:desktopPolicy.grounder.sha256,
|
|
156
|
+
timeoutMs:desktopPolicy.limits.ground_timeout_ms
|
|
157
|
+
}):null;
|
|
158
|
+
const desktopSigningKey=desktopPolicy?await loadPrivateSigningKey(signingKeyPath):null;
|
|
159
|
+
const desktopVerifyKey=desktopPolicy?await loadPublicVerifyKey(verifyKeyPath):null;
|
|
160
|
+
if(desktopPolicy&&desktopSigningKey.keyId!==desktopVerifyKey.keyId) throw new Error('Desktop signing/verify key mismatch');
|
|
161
|
+
const desktopRuntime=desktopPolicy?createDesktopRuntime({
|
|
162
|
+
policy:desktopPolicy,observerRuntime:desktopObserverRuntime,inputProvider:desktopInputProvider,
|
|
163
|
+
grounderProvider:desktopGrounderProvider,
|
|
164
|
+
privateKey:desktopSigningKey.privateKey,publicKey:desktopVerifyKey.publicKey,keyId:desktopSigningKey.keyId
|
|
165
|
+
}):null;
|
|
166
|
+
const desktopMemoryProvider=desktopPolicy?createDesktopMemoryProvider({
|
|
167
|
+
evidenceDir:desktopPolicy.evidence_dir,
|
|
168
|
+
snapshotPath:path.join(desktopPolicy.evidence_dir,'desktop-memory.json'),
|
|
169
|
+
policySha256:desktopPolicy.policy_sha256,
|
|
170
|
+
privateKey:desktopSigningKey.privateKey,publicKey:desktopVerifyKey.publicKey,keyId:desktopSigningKey.keyId
|
|
171
|
+
}):null;
|
|
172
|
+
|
|
128
173
|
const autonomousRuntimeMode = process.env.DEADBYTE_AUTONOMOUS_RUNTIME ?? '0';
|
|
129
174
|
if (!['0','1'].includes(autonomousRuntimeMode)) throw new Error('DEADBYTE_AUTONOMOUS_RUNTIME must be 0 or 1');
|
|
130
175
|
const autonomousSupervisorMode = process.env.DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE ?? 'in_process';
|
|
@@ -143,7 +188,7 @@ const autonomousPolicy = autonomousRuntimeMode === '1' ? await loadAutonomousPol
|
|
|
143
188
|
const nativeDiffRuntime = autonomousPolicy ? createNativeDiffRuntime({ policy:autonomousPolicy,codingRuntime,machineFsRuntime,signingKeyPath,verifyKeyPath }) : null;
|
|
144
189
|
semanticRuntime = codingRuntime ? createSemanticCodeRuntime({ codingRuntime, backends: semanticLsp.backends, nativeDiffRuntime }) : null;
|
|
145
190
|
const autonomousRuntime = autonomousPolicy ? createAutonomousRuntime({ policy:autonomousPolicy,codingRuntime,nativeDiffRuntime,
|
|
146
|
-
machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,signingKeyPath,verifyKeyPath }) : null;
|
|
191
|
+
machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,desktopMemoryProvider,signingKeyPath,verifyKeyPath }) : null;
|
|
147
192
|
const autonomousSupervisor = autonomousRuntime && autonomousPolicy.planner.backend === 'openai_compatible' &&
|
|
148
193
|
autonomousSupervisorMode === 'in_process'
|
|
149
194
|
? startAutonomousSupervisor({ runtime: autonomousRuntime, policy: autonomousPolicy })
|
|
@@ -185,11 +230,17 @@ function filteredToolServer(server,allowed){
|
|
|
185
230
|
|
|
186
231
|
serveStdio(() => {
|
|
187
232
|
const server = new McpServer({ name: 'deadbyte-mcp', version: DEADBYTE_VERSION });
|
|
233
|
+
const registeredToolNames=new Set();
|
|
234
|
+
const toolServer={registerTool(name,...args){
|
|
235
|
+
if(registeredToolNames.has(name)) throw new Error(`duplicate tool registration: ${name}`);
|
|
236
|
+
registeredToolNames.add(name);
|
|
237
|
+
return server.registerTool(name,...args);
|
|
238
|
+
}};
|
|
188
239
|
registerOperatorSurface(server,{packageRoot,surfaceProfile,compactToolLimit:COMPACT_SURFACE_TOOL_LIMIT});
|
|
189
240
|
if (toolAudit) installToolAudit(server,toolAudit);
|
|
190
241
|
|
|
191
242
|
if (surfaceProfile === 'full') {
|
|
192
|
-
|
|
243
|
+
toolServer.registerTool(
|
|
193
244
|
'workspace_open',
|
|
194
245
|
{
|
|
195
246
|
description: 'Open a named workspace below the configured DEADBYTE workspace root.',
|
|
@@ -206,7 +257,7 @@ serveStdio(() => {
|
|
|
206
257
|
}
|
|
207
258
|
);
|
|
208
259
|
|
|
209
|
-
|
|
260
|
+
toolServer.registerTool(
|
|
210
261
|
'file_read',
|
|
211
262
|
{
|
|
212
263
|
description: 'Read one UTF-8 file inside a named workspace. Path traversal and symlink escape are denied.',
|
|
@@ -229,7 +280,7 @@ serveStdio(() => {
|
|
|
229
280
|
}
|
|
230
281
|
);
|
|
231
282
|
|
|
232
|
-
|
|
283
|
+
toolServer.registerTool(
|
|
233
284
|
'proof_execute',
|
|
234
285
|
{
|
|
235
286
|
description: 'Run the fixed DEADBYTE V0 proof worker inside one workspace and produce an Ed25519-signed hashed receipt. This is not a general shell.',
|
|
@@ -263,7 +314,7 @@ serveStdio(() => {
|
|
|
263
314
|
}
|
|
264
315
|
);
|
|
265
316
|
|
|
266
|
-
|
|
317
|
+
toolServer.registerTool(
|
|
267
318
|
'contained_transform',
|
|
268
319
|
{
|
|
269
320
|
description: 'Run one compile-time allowlisted ASCII transform inside the proven Windows AppContainer + atomic Job boundary. Input is copied into a private sandbox profile; the host workspace is never mounted. This is not a shell and does not accept an executable path.',
|
|
@@ -306,7 +357,7 @@ serveStdio(() => {
|
|
|
306
357
|
|
|
307
358
|
}
|
|
308
359
|
|
|
309
|
-
|
|
360
|
+
toolServer.registerTool(
|
|
310
361
|
'evidence_verify',
|
|
311
362
|
{
|
|
312
363
|
description: 'Independently verify Ed25519 receipt authenticity and recompute file, stream, and worker hashes.',
|
|
@@ -327,7 +378,7 @@ serveStdio(() => {
|
|
|
327
378
|
);
|
|
328
379
|
|
|
329
380
|
if (machineRuntime) {
|
|
330
|
-
registerMachineTools(
|
|
381
|
+
registerMachineTools(toolServer, {
|
|
331
382
|
runtime: machineRuntime,
|
|
332
383
|
assertRevokeAuthority: async () => {
|
|
333
384
|
if (!hostPolicy) throw new Error('privileged host plane is not configured');
|
|
@@ -337,37 +388,41 @@ serveStdio(() => {
|
|
|
337
388
|
}
|
|
338
389
|
|
|
339
390
|
if (agentRuntime && surfaceProfile === 'full') {
|
|
340
|
-
registerAgentTools(
|
|
391
|
+
registerAgentTools(toolServer, { runtime: agentRuntime, workspacePathFromId });
|
|
341
392
|
}
|
|
342
393
|
|
|
343
394
|
if (processRuntime && surfaceProfile === 'full') {
|
|
344
|
-
registerProcessTools(
|
|
395
|
+
registerProcessTools(toolServer, { runtime: processRuntime });
|
|
345
396
|
}
|
|
346
397
|
|
|
347
398
|
if (codingRuntime && codingTaskRuntime && semanticRuntime) {
|
|
348
|
-
const codingServer=surfaceProfile==='full'?
|
|
399
|
+
const codingServer=surfaceProfile==='full'?toolServer:filteredToolServer(toolServer,COMPACT_CODING_DIRECT_TOOLS);
|
|
349
400
|
registerCodingTools(codingServer, { codingRuntime, taskRuntime: codingTaskRuntime, semanticRuntime });
|
|
350
401
|
}
|
|
351
402
|
|
|
352
403
|
if (machineFsRuntime && surfaceProfile === 'full') {
|
|
353
|
-
registerMachineFsTools(
|
|
404
|
+
registerMachineFsTools(toolServer, { runtime: machineFsRuntime });
|
|
354
405
|
}
|
|
355
406
|
|
|
356
407
|
if (observationRuntime && surfaceProfile === 'full') {
|
|
357
|
-
registerObservationTools(
|
|
408
|
+
registerObservationTools(toolServer, { runtime: observationRuntime });
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (desktopRuntime && surfaceProfile === 'full') {
|
|
412
|
+
registerDesktopTools(toolServer,{runtime:desktopRuntime});
|
|
358
413
|
}
|
|
359
414
|
|
|
360
415
|
if (autonomousRuntime && nativeDiffRuntime) {
|
|
361
|
-
registerAutonomousTools(
|
|
416
|
+
registerAutonomousTools(toolServer, { autonomousRuntime, nativeDiffRuntime });
|
|
362
417
|
}
|
|
363
418
|
|
|
364
|
-
registerCompactGatewayTools(
|
|
419
|
+
registerCompactGatewayTools(toolServer,{
|
|
365
420
|
profile:surfaceProfile,ledger:R36_CAPABILITY_LEDGER,codingRuntime,semanticRuntime,machineFsRuntime,
|
|
366
|
-
processRuntime,observationRuntime,machineRuntime
|
|
421
|
+
processRuntime,observationRuntime,machineRuntime,desktopRuntime,catalogProvider:()=>[...registeredToolNames]
|
|
367
422
|
});
|
|
368
423
|
|
|
369
424
|
if (hostPolicy) {
|
|
370
|
-
|
|
425
|
+
toolServer.registerTool(
|
|
371
426
|
'host_file_read',
|
|
372
427
|
{
|
|
373
428
|
description: 'Privileged host-plane read from a locally allowlisted root. Requires a live local arm lease; path traversal and symlink escape are denied.',
|
|
@@ -388,7 +443,7 @@ serveStdio(() => {
|
|
|
388
443
|
}
|
|
389
444
|
);
|
|
390
445
|
|
|
391
|
-
|
|
446
|
+
toolServer.registerTool(
|
|
392
447
|
'host_file_write',
|
|
393
448
|
{
|
|
394
449
|
description: 'Privileged host-plane hash-precondition file write inside a locally allowlisted root. Commit is same-directory atomic rename and post-commit SHA-256 verified. Requires a live local arm lease.',
|
|
@@ -417,7 +472,7 @@ serveStdio(() => {
|
|
|
417
472
|
}
|
|
418
473
|
);
|
|
419
474
|
|
|
420
|
-
|
|
475
|
+
toolServer.registerTool(
|
|
421
476
|
'host_exec',
|
|
422
477
|
{
|
|
423
478
|
description: 'Privileged host-plane script execution through a locally allowlisted and SHA-256-pinned executable. No raw shell command string is accepted. Requires a live local arm lease.',
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
|
|
7
|
+
import { R40_COMPACT_GATEWAYS,R40_COMPACT_TOOL_LIMIT } from './remote-mcp-r40-parity-client.mjs';
|
|
8
|
+
import { loadDesktopPolicy } from './desktop-policy-r40.mjs';
|
|
9
|
+
import { verifyDesktopReceiptObject } from './desktop-runtime-r40.mjs';
|
|
10
|
+
import { loadPublicVerifyKey } from './trust.mjs';
|
|
11
|
+
|
|
12
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
13
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
14
|
+
const verifyKeyPath=path.join(stateRoot,'trust','ed25519-public.pem');
|
|
15
|
+
function structured(result,label){
|
|
16
|
+
if(result.isError) throw new Error(label+' returned error: '+JSON.stringify(result.structuredContent??result.content));
|
|
17
|
+
if(!result.structuredContent||typeof result.structuredContent!=='object') throw new Error(label+' missing structured output');
|
|
18
|
+
return result.structuredContent;
|
|
19
|
+
}
|
|
20
|
+
function gateway(result,label){
|
|
21
|
+
const value=structured(result,label);
|
|
22
|
+
if(value.status!=='ok'||!value.result||typeof value.result!=='object') throw new Error(label+' gateway status='+String(value.status));
|
|
23
|
+
return value.result;
|
|
24
|
+
}
|
|
25
|
+
async function verifyDesktop(policy,verifyKey,id,sha,kind){
|
|
26
|
+
const receipt=JSON.parse(await readFile(path.join(policy.evidence_dir,id+'.json'),'utf8'));
|
|
27
|
+
const proof=verifyDesktopReceiptObject(receipt,{policySha256:policy.policy_sha256,publicKey:verifyKey.publicKey,keyId:verifyKey.keyId});
|
|
28
|
+
if(!proof.ok||proof.receipt_sha256!==sha||proof.kind!==kind) throw new Error('R40 desktop '+kind+' receipt verification failed');
|
|
29
|
+
}
|
|
30
|
+
export async function probeR40CompactDisarmed(endpointText){
|
|
31
|
+
const endpoint=new URL(endpointText);if(endpoint.protocol!=='https:')throw new Error('R40 compact disarmed probe requires HTTPS');
|
|
32
|
+
const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
|
|
33
|
+
const desktopPolicy=await loadDesktopPolicy(path.join(pointer.generation_root,'desktop-policy.json'));
|
|
34
|
+
const verifyKey=await loadPublicVerifyKey(verifyKeyPath);
|
|
35
|
+
const client=new Client({name:'deadbyte-r40-compact-disarmed-probe',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
36
|
+
const transport=new StreamableHTTPClientTransport(endpoint);
|
|
37
|
+
try{
|
|
38
|
+
await client.connect(transport);
|
|
39
|
+
const listed=await client.listTools(),names=listed.tools.map(t=>t.name).sort();
|
|
40
|
+
if(names.length!==R40_COMPACT_TOOL_LIMIT)throw new Error('R40 compact disarmed catalog must be exactly '+R40_COMPACT_TOOL_LIMIT+', got '+names.length);
|
|
41
|
+
for(const name of R40_COMPACT_GATEWAYS)if(!names.includes(name))throw new Error('R40 compact gateway missing '+name);
|
|
42
|
+
for(const tool of listed.tools)if(tool.outputSchema?.type!=='object'||tool.outputSchema?.additionalProperties!==false||!tool.outputSchema?.properties)throw new Error('tool '+tool.name+' missing strict output schema');
|
|
43
|
+
const manifest=gateway(await client.callTool({name:'capability_manifest',arguments:{}}),'capability_manifest');
|
|
44
|
+
if(manifest.schema!=='deadbyte.capability-manifest.v2'||manifest.profile!=='compact'||manifest.tool_count!==names.length||
|
|
45
|
+
JSON.stringify(manifest.tool_catalog)!==JSON.stringify(names))throw new Error('R40 compact manifest malformed');
|
|
46
|
+
const required=manifest.capabilities.filter(x=>x?.production===true&&x?.host_only!==true);
|
|
47
|
+
for(const item of required)if(typeof item.compact_tool!=='string'||!names.includes(item.compact_tool))throw new Error('compact route missing '+String(item.id));
|
|
48
|
+
const coding=structured(await client.callTool({name:'coding_session_status',arguments:{}}),'coding status');
|
|
49
|
+
const autonomous=structured(await client.callTool({name:'autonomous_session_status',arguments:{}}),'autonomous status');
|
|
50
|
+
const desktop=gateway(await client.callTool({name:'desktop_control',arguments:{op:'status'}}),'desktop status');
|
|
51
|
+
if(coding.armed!==false||autonomous.coding_armed!==false||autonomous.autonomous_armed!==false||desktop.armed!==false)throw new Error('R40 closure requires Coding/Autonomous/Desktop DISARMED');
|
|
52
|
+
const observed=gateway(await client.callTool({name:'desktop_control',arguments:{op:'observe',preview:false}}),'desktop observe');
|
|
53
|
+
await verifyDesktop(desktopPolicy,verifyKey,observed.receipt_id,observed.receipt_sha256,'desktop_observation');
|
|
54
|
+
const grounded=gateway(await client.callTool({name:'desktop_control',arguments:{op:'target',frame_id:observed.frame.frame_id,uia_query:{kind:'root',value:'',max_results:1,ordinal:0}}}),'desktop target');
|
|
55
|
+
await verifyDesktop(desktopPolicy,verifyKey,grounded.grounding.receipt_id,grounded.grounding.receipt_sha256,'desktop_grounding');
|
|
56
|
+
const machine=gateway(await client.callTool({name:'machine_observe',arguments:{op:'machine_status'}}),'machine status');
|
|
57
|
+
const nonce='r40-final-'+randomBytes(16).toString('hex');
|
|
58
|
+
const ping=gateway(await client.callTool({name:'machine_observe',arguments:{op:'machine_ping',nonce}}),'machine ping');
|
|
59
|
+
if(ping.nonce!==nonce||ping.release_id!==machine.release_id||ping.release_manifest_sha256!==machine.release_manifest_sha256)throw new Error('R40 machine ping binding mismatch');
|
|
60
|
+
return {status:'passed',profile:'compact',tool_count:names.length,tools:names,catalog_sha256:sha256(Buffer.from(JSON.stringify(names),'utf8')),
|
|
61
|
+
strict_output_schemas:true,ledger_sha256:manifest.ledger_sha256,capability_count:manifest.capabilities.length,
|
|
62
|
+
production_non_host_capabilities:required.length,capability_closure:true,coding_armed:false,autonomous_armed:false,desktop_armed:false,
|
|
63
|
+
desktop_observation_receipt_verified:true,desktop_grounding_receipt_verified:true,
|
|
64
|
+
desktop_frame_id:observed.frame.frame_id,desktop_target_id:grounded.target.target_id,
|
|
65
|
+
release_id:machine.release_id,release_manifest_sha256:machine.release_manifest_sha256,machine_status:machine,machine_ping:ping,nonce,
|
|
66
|
+
protocol_revision:client.getNegotiatedProtocolVersion()};
|
|
67
|
+
}finally{await client.close().catch(()=>{});}
|
|
68
|
+
}
|
|
69
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
70
|
+
if(invoked){const endpoint=process.argv[2];if(!endpoint)throw new Error('usage: node src/r40-compact-disarmed-probe.mjs https://host/mcp/path');
|
|
71
|
+
probeR40CompactDisarmed(endpoint).then(v=>console.log(JSON.stringify(v,null,2))).catch(e=>{console.error(e instanceof Error?e.stack:String(e));process.exitCode=1;});}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { Client } from '@modelcontextprotocol/client';
|
|
6
|
+
import { StdioClientTransport,getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { R40_EXPECTED_TOOLS } from './remote-mcp-r40-parity-client.mjs';
|
|
9
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
export async function probeR40FullSurface({packageRoot,generationRoot,stateRoot=path.join(os.homedir(),'.deadbyte-mcp')}={}){
|
|
11
|
+
const pkgRoot=packageRoot?path.resolve(packageRoot):path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
12
|
+
const gen=path.resolve(generationRoot),semanticPath=path.join(gen,'semantic-lsp-config.json');
|
|
13
|
+
const semanticSha=sha256(await readFile(semanticPath));
|
|
14
|
+
const env={...getDefaultEnvironment(),DEADBYTE_STATE_ROOT:path.resolve(stateRoot),DEADBYTE_CORE_ROOT:pkgRoot,
|
|
15
|
+
DEADBYTE_WORKSPACE_ROOT:path.join(pkgRoot,'workspaces'),DEADBYTE_CAPABILITY_PROFILE:'full',DEADBYTE_AUDIT_RUNTIME:'0',
|
|
16
|
+
DEADBYTE_MACHINE_RUNTIME:'1',DEADBYTE_AGENT_RUNTIME:'1',DEADBYTE_PROCESS_RUNTIME:'1',DEADBYTE_PROCESS_POLICY:path.join(gen,'process-policy.json'),
|
|
17
|
+
DEADBYTE_CODING_RUNTIME:'1',DEADBYTE_MACHINE_FS_RUNTIME:'1',DEADBYTE_OBSERVATION_RUNTIME:'1',
|
|
18
|
+
DEADBYTE_DESKTOP_RUNTIME:'1',DEADBYTE_DESKTOP_POLICY:path.join(gen,'desktop-policy.json'),
|
|
19
|
+
DEADBYTE_CODING_POLICY:path.join(gen,'coding-policy.json'),DEADBYTE_SEMANTIC_LSP_CONFIG:semanticPath,DEADBYTE_SEMANTIC_LSP_CONFIG_SHA256:semanticSha,
|
|
20
|
+
DEADBYTE_AUTONOMOUS_RUNTIME:'1',DEADBYTE_AUTONOMOUS_POLICY:path.join(gen,'autonomous-policy.json'),
|
|
21
|
+
DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external',DEADBYTE_PRIVILEGED_POLICY:path.join(gen,'host-policy.json')};
|
|
22
|
+
const client=new Client({name:'deadbyte-r40-full-surface-probe',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
23
|
+
const transport=new StdioClientTransport({command:process.execPath,args:[path.join(pkgRoot,'src','mcp-server.mjs')],cwd:pkgRoot,env,stderr:'pipe'});
|
|
24
|
+
try{
|
|
25
|
+
await client.connect(transport);const listed=await client.listTools(),names=listed.tools.map(t=>t.name).sort();
|
|
26
|
+
if(JSON.stringify(names)!==JSON.stringify(R40_EXPECTED_TOOLS))throw new Error('R40 full catalog mismatch count='+names.length);
|
|
27
|
+
for(const tool of listed.tools)if(tool.outputSchema?.type!=='object'||tool.outputSchema?.additionalProperties!==false||!tool.outputSchema?.properties)throw new Error('R40 full tool '+tool.name+' missing strict output schema');
|
|
28
|
+
return {status:'passed',profile:'full',tool_count:names.length,tools:names,catalog_sha256:sha256(Buffer.from(JSON.stringify(names),'utf8')),
|
|
29
|
+
strict_output_schemas:true,desktop_tools:['desktop_status','desktop_observe','desktop_target','desktop_action'],protocol_revision:client.getNegotiatedProtocolVersion()};
|
|
30
|
+
}finally{await client.close().catch(()=>{});}
|
|
31
|
+
}
|
|
32
|
+
if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){const [packageRoot,generationRoot]=process.argv.slice(2);
|
|
33
|
+
if(!packageRoot||!generationRoot)throw new Error('usage: node src/r40-full-surface-probe.mjs <package-root> <generation-root>');
|
|
34
|
+
probeR40FullSurface({packageRoot,generationRoot}).then(v=>console.log(JSON.stringify(v,null,2))).catch(e=>{console.error(e instanceof Error?e.stack:String(e));process.exitCode=1;});}
|
|
@@ -118,7 +118,31 @@ async function rebindProcessPolicy(raw,{developmentRoot,stateRoot,releaseRoot}){
|
|
|
118
118
|
return policy;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
function
|
|
121
|
+
async function rebindDesktopPolicy(raw,{stateRoot,releaseRoot}){
|
|
122
|
+
if(!raw) return null;
|
|
123
|
+
const policy=clone(raw);
|
|
124
|
+
if(policy.schema!=='deadbyte.desktop-policy.v1'||policy.enabled!==true) throw new Error('desktop policy schema/enabled mismatch');
|
|
125
|
+
policy.grant_file=path.join(stateRoot,'runtime','desktop-grant.json');
|
|
126
|
+
policy.evidence_dir=path.join(stateRoot,'runtime','desktop-evidence');
|
|
127
|
+
const providers={
|
|
128
|
+
observer:'deadbyte-desktop-observer-r40.exe',
|
|
129
|
+
input:'deadbyte-desktop-input-r40.exe',
|
|
130
|
+
grounder:'deadbyte-desktop-uia-r40.exe'
|
|
131
|
+
};
|
|
132
|
+
for(const [name,file] of Object.entries(providers)){
|
|
133
|
+
const provider=policy[name];
|
|
134
|
+
if(!provider||typeof provider.sha256!=='string'||!HEX64.test(provider.sha256)) throw new Error(`desktop ${name} policy pin invalid`);
|
|
135
|
+
const providerPath=path.join(releaseRoot,'bin',file);
|
|
136
|
+
const stat=await lstat(providerPath);
|
|
137
|
+
if(!stat.isFile()||stat.isSymbolicLink()) throw new Error(`desktop ${name} helper invalid in release slot`);
|
|
138
|
+
const actual=sha256(await readFile(providerPath));
|
|
139
|
+
if(actual!==provider.sha256) throw new Error(`desktop ${name} helper SHA-256 mismatch: policy=${provider.sha256} release=${actual}`);
|
|
140
|
+
provider.executable=providerPath;
|
|
141
|
+
}
|
|
142
|
+
return policy;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPolicyPath,desktopPolicyPath,codingPolicyPath,semanticConfigPath,semanticConfigSha256,autonomousPolicyPath,hostPolicyPath}){
|
|
122
146
|
const supervisor=currentConfig.Supervisor??{},bridge=currentConfig.Bridge??{},cloud=currentConfig.Cloud??{};
|
|
123
147
|
return [
|
|
124
148
|
'@{'," Schema = 'deadbyte.controller.v1'",` CoreRoot = ${psQuote(coreRoot)}`,
|
|
@@ -130,6 +154,8 @@ function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPol
|
|
|
130
154
|
` CodingRuntimeEnabled = ${psBool(currentConfig.CodingRuntimeEnabled)}`,
|
|
131
155
|
` MachineFsRuntimeEnabled = ${psBool(currentConfig.MachineFsRuntimeEnabled)}`,
|
|
132
156
|
` ObservationRuntimeEnabled = ${psBool(currentConfig.ObservationRuntimeEnabled)}`,
|
|
157
|
+
` DesktopRuntimeEnabled = ${psBool(currentConfig.DesktopRuntimeEnabled)}`,
|
|
158
|
+
` DesktopPolicyFile = ${psQuote(desktopPolicyPath??'')}`,
|
|
133
159
|
` CodingPolicyFile = ${psQuote(codingPolicyPath)}`,
|
|
134
160
|
` SemanticLspConfigFile = ${psQuote(semanticConfigPath??'')}`,
|
|
135
161
|
` SemanticLspConfigSha256 = ${psQuote(semanticConfigSha256??'')}`,
|
|
@@ -172,7 +198,7 @@ function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPol
|
|
|
172
198
|
'}',''
|
|
173
199
|
].join('\r\n');
|
|
174
200
|
}
|
|
175
|
-
export async function createGenerationBundle({stateRoot,releaseRoot,developmentRoot,workspaceRoot,releaseId,currentConfig,processPolicy,codingPolicy,autonomousPolicy,hostPolicy}){
|
|
201
|
+
export async function createGenerationBundle({stateRoot,releaseRoot,developmentRoot,workspaceRoot,releaseId,currentConfig,processPolicy,desktopPolicy,codingPolicy,autonomousPolicy,hostPolicy}){
|
|
176
202
|
const state=path.resolve(stateRoot),release=path.resolve(releaseRoot),development=path.resolve(developmentRoot),workspace=path.resolve(workspaceRoot);
|
|
177
203
|
if(typeof releaseId!=='string'||!/^[A-Za-z0-9._-]{1,96}$/.test(releaseId)) throw new Error('generation release id invalid');
|
|
178
204
|
if(typeof currentConfig?.Bridge?.FileSha256!=='string'||!HEX64.test(currentConfig.Bridge.FileSha256)) throw new Error('generation Bridge FileSha256 invalid');
|
|
@@ -230,6 +256,9 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
230
256
|
const hostBytes=host?jsonBytes(host):null;
|
|
231
257
|
const processBound=await rebindProcessPolicy(processPolicy,{developmentRoot:development,stateRoot:state,releaseRoot:release});
|
|
232
258
|
const processBytes=processBound?jsonBytes(processBound):null;
|
|
259
|
+
const desktopBound=await rebindDesktopPolicy(desktopPolicy,{stateRoot:state,releaseRoot:release});
|
|
260
|
+
const desktopBytes=desktopBound?jsonBytes(desktopBound):null;
|
|
261
|
+
if(currentConfig.DesktopRuntimeEnabled===true&&!desktopBytes) throw new Error('DesktopRuntimeEnabled requires desktop policy');
|
|
233
262
|
const seedBody=canonical({
|
|
234
263
|
release_id:releaseId,core_root:release,development_root:development,workspace_root:workspace,
|
|
235
264
|
machine_runtime_enabled:currentConfig.MachineRuntimeEnabled!==false,
|
|
@@ -238,6 +267,7 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
238
267
|
coding_runtime_enabled:currentConfig.CodingRuntimeEnabled===true,
|
|
239
268
|
machine_fs_runtime_enabled:currentConfig.MachineFsRuntimeEnabled===true,
|
|
240
269
|
observation_runtime_enabled:currentConfig.ObservationRuntimeEnabled===true,
|
|
270
|
+
desktop_runtime_enabled:currentConfig.DesktopRuntimeEnabled===true,
|
|
241
271
|
autonomous_runtime_enabled:currentConfig.AutonomousRuntimeEnabled===true,
|
|
242
272
|
autonomous_supervisor_enabled:currentConfig.AutonomousSupervisorEnabled===true,
|
|
243
273
|
supervisor:currentConfig.AutonomousSupervisorEnabled===true?(currentConfig.Supervisor??{}):null,
|
|
@@ -247,6 +277,7 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
247
277
|
start_timeout_seconds:Number(currentConfig.StartTimeoutSeconds??30),probe_timeout_seconds:Number(currentConfig.ProbeTimeoutSeconds??3),
|
|
248
278
|
semantic_lsp_config_sha256:semanticConfigSha256,
|
|
249
279
|
process_policy_sha256:processBytes?sha256(processBytes):null,
|
|
280
|
+
desktop_policy_sha256:desktopBytes?sha256(desktopBytes):null,
|
|
250
281
|
coding_policy_sha256:sha256(codingBytes),autonomous_policy_sha256:sha256(autonomousBytes),host_policy_sha256:hostBytes?sha256(hostBytes):null
|
|
251
282
|
});
|
|
252
283
|
const generationSeed=sha256(Buffer.from(JSON.stringify(seedBody),'utf8'));
|
|
@@ -254,6 +285,7 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
254
285
|
const generationsRoot=path.join(state,'generations');
|
|
255
286
|
const generationRoot=path.join(generationsRoot,generationId);
|
|
256
287
|
const processPath=processBytes?path.join(generationRoot,'process-policy.json'):null;
|
|
288
|
+
const desktopPath=desktopBytes?path.join(generationRoot,'desktop-policy.json'):null;
|
|
257
289
|
const codingPath=path.join(generationRoot,'coding-policy.json');
|
|
258
290
|
const semanticPath=semanticBytes?path.join(generationRoot,'semantic-lsp-config.json'):null;
|
|
259
291
|
const autonomousPath=path.join(generationRoot,'autonomous-policy.json');
|
|
@@ -262,15 +294,16 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
262
294
|
const files=new Map([
|
|
263
295
|
['coding-policy.json',codingBytes],
|
|
264
296
|
['autonomous-policy.json',autonomousBytes],
|
|
265
|
-
['controller-config.psd1',Buffer.from(renderControllerConfig({currentConfig,coreRoot:release,workspaceRoot:workspace,processPolicyPath:processPath,codingPolicyPath:codingPath,semanticConfigPath:semanticPath,semanticConfigSha256,autonomousPolicyPath:autonomousPath,hostPolicyPath:hostPath}),'utf8')]
|
|
297
|
+
['controller-config.psd1',Buffer.from(renderControllerConfig({currentConfig,coreRoot:release,workspaceRoot:workspace,processPolicyPath:processPath,desktopPolicyPath:desktopPath,codingPolicyPath:codingPath,semanticConfigPath:semanticPath,semanticConfigSha256,autonomousPolicyPath:autonomousPath,hostPolicyPath:hostPath}),'utf8')]
|
|
266
298
|
]);
|
|
267
299
|
if(semanticBytes) files.set('semantic-lsp-config.json',semanticBytes);
|
|
268
300
|
if(processBytes) files.set('process-policy.json',processBytes);
|
|
301
|
+
if(desktopBytes) files.set('desktop-policy.json',desktopBytes);
|
|
269
302
|
if(hostBytes) files.set('host-policy.json',hostBytes);
|
|
270
303
|
const manifestBytes=generationManifest(files),manifestSha256=sha256(manifestBytes);
|
|
271
304
|
if(existsSync(generationRoot)){
|
|
272
305
|
const verified=await verifyGenerationBundle(generationRoot,manifestSha256);
|
|
273
|
-
return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:true,...verified};
|
|
306
|
+
return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,desktop_policy_path:desktopPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:true,...verified};
|
|
274
307
|
}
|
|
275
308
|
await mkdir(generationsRoot,{recursive:true});
|
|
276
309
|
const staging=path.join(generationsRoot,`.staging-${generationId}-${process.pid}-${randomBytes(4).toString('hex')}`);
|
|
@@ -285,5 +318,5 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
285
318
|
throw error;
|
|
286
319
|
}
|
|
287
320
|
const verified=await verifyGenerationBundle(generationRoot,manifestSha256);
|
|
288
|
-
return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:false,...verified};
|
|
321
|
+
return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,desktop_policy_path:desktopPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:false,...verified};
|
|
289
322
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
|
|
4
|
+
export const REMOTE_DISPLAY_MODES=Object.freeze(['unicode','ascii']);
|
|
5
|
+
|
|
6
|
+
function checkedMode(mode){
|
|
7
|
+
if(!REMOTE_DISPLAY_MODES.includes(mode)) throw new Error(`remote display mode invalid: ${String(mode)}`);
|
|
8
|
+
return mode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function remoteDisplayText(value,mode='unicode'){
|
|
12
|
+
checkedMode(mode);
|
|
13
|
+
const text=String(value??'');
|
|
14
|
+
if(mode==='unicode') return text;
|
|
15
|
+
let out='';
|
|
16
|
+
for(const ch of text){
|
|
17
|
+
const cp=ch.codePointAt(0);
|
|
18
|
+
if(cp===9||cp===10||cp===13||(cp>=0x20&&cp<=0x7e)) out+=ch;
|
|
19
|
+
else out+=`\\u{${cp.toString(16).toUpperCase()}}`;
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function prepareRemoteConsole({
|
|
25
|
+
platform=process.platform,
|
|
26
|
+
isTTY=Boolean(process.stdout?.isTTY),
|
|
27
|
+
env=process.env,
|
|
28
|
+
spawnSyncFn=spawnSync
|
|
29
|
+
}={}){
|
|
30
|
+
if(String(env?.DEADBYTE_REMOTE_ASCII??'')==='1') return Object.freeze({mode:'ascii',code_page:null,reason:'forced_ascii'});
|
|
31
|
+
if(platform!=='win32') return Object.freeze({mode:'unicode',code_page:null,reason:'non_windows'});
|
|
32
|
+
if(!isTTY) return Object.freeze({mode:'ascii',code_page:null,reason:'stdout_not_tty'});
|
|
33
|
+
const systemRoot=env?.SystemRoot||env?.WINDIR;
|
|
34
|
+
if(typeof systemRoot!=='string'||!systemRoot) return Object.freeze({mode:'ascii',code_page:null,reason:'system_root_missing'});
|
|
35
|
+
const chcp=path.win32.join(systemRoot,'System32','chcp.com');
|
|
36
|
+
let result;
|
|
37
|
+
try{ result=spawnSyncFn(chcp,['65001'],{stdio:'ignore',windowsHide:true,shell:false}); }
|
|
38
|
+
catch{ return Object.freeze({mode:'ascii',code_page:null,reason:'codepage_probe_threw'}); }
|
|
39
|
+
if(result?.error||result?.status!==0) return Object.freeze({mode:'ascii',code_page:null,reason:'codepage_65001_unavailable'});
|
|
40
|
+
return Object.freeze({mode:'unicode',code_page:65001,reason:'codepage_65001'});
|
|
41
|
+
}
|