deadbyte-mcp 0.15.14 → 0.15.16
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 +65 -27
- package/PROVENANCE.json +6 -6
- package/README.txt +6 -6
- package/RELEASE.json +58 -53
- package/docs/COMMANDER_EXHAUSTIVE_SURFACE.json +49 -0
- package/docs/COMMANDER_REFERENCE_INVENTORY.json +70 -0
- package/docs/R47-FULL-TOOL-CATALOG.json +123 -0
- package/package.json +5 -2
- package/scripts/commander-hardening-gate-r47.mjs +35 -0
- package/scripts/commander-parity-benchmark.mjs +6 -0
- package/scripts/commander-real-performance-benchmark-r47.mjs +41 -0
- package/scripts/deploy-r47.mjs +67 -8
- package/scripts/full-regression-r47.mjs +28 -0
- package/scripts/inventory-reference-dist.mjs +17 -0
- package/scripts/npm-release-r47.mjs +25 -1
- package/scripts/r47-syntax-check.mjs +6 -0
- package/scripts/r47-wire-remote-plane.mjs +23 -0
- package/src/atomic-mutation.mjs +59 -29
- package/src/commander-compat-contracts.mjs +90 -0
- package/src/commander-compat-runtime.mjs +91 -0
- package/src/commander-core-bridge.mjs +30 -0
- package/src/commander-extended-runtime.mjs +74 -0
- package/src/commander-observability.mjs +33 -0
- package/src/commander-remote-bridge-r47.mjs +41 -0
- package/src/commander-remote-relay-r47.mjs +44 -0
- package/src/machine-fs.mjs +108 -22
- package/src/mcp-server.mjs +94 -0
- package/src/observation-output-contracts.mjs +9 -8
- package/src/observation-runtime.mjs +47 -17
- package/src/operation-history.mjs +95 -0
- package/src/operation-lifecycle.mjs +63 -0
- package/src/process-output-contracts.mjs +24 -1
- package/src/process-readiness.mjs +120 -0
- package/src/process-runtime.mjs +178 -6
- package/src/release-slot.mjs +296 -293
- package/src/runtime-update.mjs +61 -7
- package/src/tool-audit.mjs +15 -2
- package/src/version.mjs +1 -2
- package/test/OBSERVED-COMMANDER-TOOLS.json +1 -0
- package/test/agent-runtime.test.mjs +1 -1
- package/test/autonomous-runtime.test.mjs +1 -1
- package/test/coding-plane.test.mjs +1 -1
- package/test/coding-search-r36.test.mjs +14 -0
- package/test/commander-compat-runtime.test.mjs +34 -0
- package/test/commander-core-bridge-r47.test.mjs +31 -0
- package/test/commander-exhaustive-surface-r47.test.mjs +32 -0
- package/test/commander-extended-runtime-r47.test.mjs +46 -0
- package/test/commander-observability-r47.test.mjs +34 -0
- package/test/commander-parity-gate-r47.test.mjs +49 -0
- package/test/commander-protocol-r47.test.mjs +30 -0
- package/test/commander-real-parity-r47.test.mjs +121 -0
- package/test/commander-remote-bridge-r47.test.mjs +39 -0
- package/test/commander-remote-plane-r47.test.mjs +25 -0
- package/test/commander-torture-r47.test.mjs +146 -0
- package/test/full-max-catalog-r46.test.mjs +1 -1
- package/test/full-max-catalog-r47.test.mjs +78 -0
- package/test/machine-fs-runtime.test.mjs +97 -0
- package/test/observation-runtime.test.mjs +28 -0
- package/test/operation-history.test.mjs +61 -0
- package/test/operation-lifecycle.test.mjs +72 -0
- package/test/output-schema.test.mjs +1 -1
- package/test/process-readiness.test.mjs +32 -0
- package/test/process-runtime.test.mjs +147 -0
- package/test/r47-release-contract.test.mjs +37 -0
- package/test/release-activation-gate.test.mjs +30 -0
- package/test/release-version.test.mjs +1 -1
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "deadbyte.r47-full-catalog.v1",
|
|
3
|
+
"train": "R47",
|
|
4
|
+
"source": "runtime.tools/list",
|
|
5
|
+
"tool_count": 115,
|
|
6
|
+
"tools": [
|
|
7
|
+
"agent_task_approve",
|
|
8
|
+
"agent_task_cancel",
|
|
9
|
+
"agent_task_events",
|
|
10
|
+
"agent_task_run",
|
|
11
|
+
"agent_task_status",
|
|
12
|
+
"agent_task_submit",
|
|
13
|
+
"agent_task_verify",
|
|
14
|
+
"autonomous_goal_approve",
|
|
15
|
+
"autonomous_goal_cancel",
|
|
16
|
+
"autonomous_goal_events",
|
|
17
|
+
"autonomous_goal_run",
|
|
18
|
+
"autonomous_goal_status",
|
|
19
|
+
"autonomous_goal_submit",
|
|
20
|
+
"autonomous_goal_verify",
|
|
21
|
+
"autonomous_release_gate",
|
|
22
|
+
"autonomous_session_status",
|
|
23
|
+
"coding_benchmark_run",
|
|
24
|
+
"coding_command_run",
|
|
25
|
+
"coding_file_read",
|
|
26
|
+
"coding_file_write",
|
|
27
|
+
"coding_files_read",
|
|
28
|
+
"coding_receipt_verify",
|
|
29
|
+
"coding_search",
|
|
30
|
+
"coding_session_status",
|
|
31
|
+
"coding_stat",
|
|
32
|
+
"coding_symbol_definition",
|
|
33
|
+
"coding_symbol_diagnostics",
|
|
34
|
+
"coding_symbol_implementations",
|
|
35
|
+
"coding_symbol_outline",
|
|
36
|
+
"coding_symbol_references",
|
|
37
|
+
"coding_symbol_rename_preview",
|
|
38
|
+
"coding_symbol_replace",
|
|
39
|
+
"coding_task_approve",
|
|
40
|
+
"coding_task_cancel",
|
|
41
|
+
"coding_task_events",
|
|
42
|
+
"coding_task_run",
|
|
43
|
+
"coding_task_status",
|
|
44
|
+
"coding_task_submit",
|
|
45
|
+
"coding_task_verify",
|
|
46
|
+
"coding_text_replace",
|
|
47
|
+
"coding_tree",
|
|
48
|
+
"contained_transform",
|
|
49
|
+
"create_directory",
|
|
50
|
+
"desktop_action",
|
|
51
|
+
"desktop_observe",
|
|
52
|
+
"desktop_status",
|
|
53
|
+
"desktop_target",
|
|
54
|
+
"edit_block",
|
|
55
|
+
"evidence_verify",
|
|
56
|
+
"file_preview",
|
|
57
|
+
"file_read",
|
|
58
|
+
"force_terminate",
|
|
59
|
+
"get_config",
|
|
60
|
+
"get_file_info",
|
|
61
|
+
"get_more_search_results",
|
|
62
|
+
"get_prompts",
|
|
63
|
+
"get_recent_tool_calls",
|
|
64
|
+
"get_usage_stats",
|
|
65
|
+
"give_feedback_to_desktop_commander",
|
|
66
|
+
"host_exec",
|
|
67
|
+
"host_file_read",
|
|
68
|
+
"host_file_write",
|
|
69
|
+
"interact_with_process",
|
|
70
|
+
"kill_process",
|
|
71
|
+
"list_directory",
|
|
72
|
+
"list_processes",
|
|
73
|
+
"list_searches",
|
|
74
|
+
"list_sessions",
|
|
75
|
+
"machine_fs_delete",
|
|
76
|
+
"machine_fs_edit_exact",
|
|
77
|
+
"machine_fs_list",
|
|
78
|
+
"machine_fs_mkdir",
|
|
79
|
+
"machine_fs_move",
|
|
80
|
+
"machine_fs_read",
|
|
81
|
+
"machine_fs_stat",
|
|
82
|
+
"machine_fs_write",
|
|
83
|
+
"machine_hardware_query",
|
|
84
|
+
"machine_list",
|
|
85
|
+
"machine_ping",
|
|
86
|
+
"machine_process_list",
|
|
87
|
+
"machine_revoke",
|
|
88
|
+
"machine_search_cancel",
|
|
89
|
+
"machine_search_page",
|
|
90
|
+
"machine_search_start",
|
|
91
|
+
"machine_search_status",
|
|
92
|
+
"machine_status",
|
|
93
|
+
"machine_system_status",
|
|
94
|
+
"markdown_editor",
|
|
95
|
+
"move_file",
|
|
96
|
+
"native_diff_apply",
|
|
97
|
+
"native_diff_preview",
|
|
98
|
+
"native_diff_verify",
|
|
99
|
+
"process_session_action",
|
|
100
|
+
"process_session_cancel",
|
|
101
|
+
"process_session_list",
|
|
102
|
+
"process_session_read",
|
|
103
|
+
"process_session_start",
|
|
104
|
+
"process_session_status",
|
|
105
|
+
"process_session_terminate",
|
|
106
|
+
"process_session_write",
|
|
107
|
+
"proof_execute",
|
|
108
|
+
"read_file",
|
|
109
|
+
"read_multiple_files",
|
|
110
|
+
"read_process_output",
|
|
111
|
+
"remote_device",
|
|
112
|
+
"remote_mcp",
|
|
113
|
+
"resources",
|
|
114
|
+
"set_config_value",
|
|
115
|
+
"skill_catalog",
|
|
116
|
+
"start_process",
|
|
117
|
+
"start_search",
|
|
118
|
+
"stop_search",
|
|
119
|
+
"workspace_open",
|
|
120
|
+
"write_file",
|
|
121
|
+
"write_pdf"
|
|
122
|
+
]
|
|
123
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deadbyte-mcp",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.16",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Authenticated MCP agentic runtime with signed skills, persistent model backends, identity-bound remote devices, durable job supervision, immutable release slots, and separately revocable machine authority.",
|
|
@@ -65,7 +65,10 @@
|
|
|
65
65
|
"r45:full:test": "node scripts/full-regression-r45.mjs",
|
|
66
66
|
"finalize:r45": "node scripts/finalize-r45.mjs",
|
|
67
67
|
"r46:fileless:test": "node --test --test-concurrency=1 test/process-policy.test.mjs test/process-policy-refresh.test.mjs test/release-generation.test.mjs test/process-runtime.test.mjs test/process-mcp-tools.test.mjs test/compact-gateway-r36.test.mjs test/capability-ledger-r36.test.mjs test/remote-device-r44.test.mjs test/observation-runtime.test.mjs test/observation-mcp-tools.test.mjs",
|
|
68
|
-
"r46:full:test": "node scripts/full-regression-r46.mjs"
|
|
68
|
+
"r46:full:test": "node scripts/full-regression-r46.mjs",
|
|
69
|
+
"r47:full:test": "node scripts/full-regression-r47.mjs",
|
|
70
|
+
"r47:catalog:test": "node --test --test-concurrency=1 test/full-max-catalog-r47.test.mjs",
|
|
71
|
+
"r47:hardening:test": "node scripts/commander-hardening-gate-r47.mjs"
|
|
69
72
|
},
|
|
70
73
|
"bundleDependencies": [
|
|
71
74
|
"@modelcontextprotocol/server",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
const suites = [
|
|
4
|
+
'test/commander-compat-runtime.test.mjs',
|
|
5
|
+
'test/commander-parity-gate-r47.test.mjs',
|
|
6
|
+
'test/commander-real-parity-r47.test.mjs',
|
|
7
|
+
'test/commander-torture-r47.test.mjs',
|
|
8
|
+
'test/machine-fs-runtime.test.mjs',
|
|
9
|
+
'test/coding-search-r36.test.mjs',
|
|
10
|
+
'test/process-runtime.test.mjs',
|
|
11
|
+
'test/commander-observability-r47.test.mjs',
|
|
12
|
+
'test/commander-extended-runtime-r47.test.mjs',
|
|
13
|
+
'test/commander-exhaustive-surface-r47.test.mjs',
|
|
14
|
+
'test/commander-core-bridge-r47.test.mjs',
|
|
15
|
+
'test/commander-protocol-r47.test.mjs'
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const startedAt = new Date().toISOString();
|
|
19
|
+
const result = spawnSync(process.execPath, ['--test', '--test-concurrency=1', ...suites], {
|
|
20
|
+
cwd: process.cwd(),
|
|
21
|
+
stdio: 'inherit',
|
|
22
|
+
windowsHide: true
|
|
23
|
+
});
|
|
24
|
+
const exitCode = result.status ?? 1;
|
|
25
|
+
const signal = result.signal ?? null;
|
|
26
|
+
console.log(JSON.stringify({
|
|
27
|
+
schema: 'deadbyte.commander-hardening-gate.v1',
|
|
28
|
+
started_at_utc: startedAt,
|
|
29
|
+
completed_at_utc: new Date().toISOString(),
|
|
30
|
+
suites,
|
|
31
|
+
exit_code: exitCode,
|
|
32
|
+
signal,
|
|
33
|
+
passed: exitCode === 0
|
|
34
|
+
}, null, 2));
|
|
35
|
+
process.exit(exitCode);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks';
|
|
2
|
+
import { commanderCompatibilityCoverage, commanderCompatibilityContract, validateCommanderRequest } from '../src/commander-compat-runtime.mjs';
|
|
3
|
+
const N=5000;
|
|
4
|
+
const workloads=[['read_file',{root_id:'projects',path:'x'}],['list_directory',{root_id:'projects',path:'.'}],['get_file_info',{root_id:'projects',path:'x'}],['read_multiple_files',{root_id:'projects',paths:['a','b','c']}],['start_search',{root_id:'projects',path:'.',pattern:'*.mjs'}],['list_searches',{}],['start_process',{profile_id:'node_script',root_id:'projects'}],['read_process_output',{session_id:'0'.repeat(64)}],['write_file',{root_id:'projects',path:'x',content:'x',expected_sha256:'absent'}],['move_file',{root_id:'projects',source_path:'a',destination_path:'b',expected_source_identity:'0'.repeat(64)}]];
|
|
5
|
+
const rows=[]; for(const [name,args] of workloads){const t0=performance.now();for(let i=0;i<N;i++)validateCommanderRequest(name,args);const ms=performance.now()-t0;rows.push({name,iterations:N,elapsed_ms:+ms.toFixed(3),ops_per_sec:Math.round(N/(ms/1000)),native:commanderCompatibilityContract(name).native});}
|
|
6
|
+
console.log(JSON.stringify({schema:'deadbyte.commander-parity-benchmark.v1',coverage:commanderCompatibilityCoverage(),workloads:rows,total_iterations:N*workloads.length},null,2));
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks';
|
|
2
|
+
import { mkdtemp, writeFile, rm } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
6
|
+
import { pathToFileURL } from 'node:url';
|
|
7
|
+
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
8
|
+
import { loadCodingPolicy } from '../src/coding-plane.mjs';
|
|
9
|
+
import { createMachineFsRuntime } from '../src/machine-fs.mjs';
|
|
10
|
+
|
|
11
|
+
const N=50;
|
|
12
|
+
const commanderFs=await import(pathToFileURL('D:/Projects/desktop-commander-stage-20260922/desktop-commander/dist/tools/filesystem.js').href);
|
|
13
|
+
const { readFileFromDisk, readMultipleFiles, listDirectory, getFileInfo }=commanderFs;
|
|
14
|
+
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
15
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-commander-bench-'));
|
|
16
|
+
const fixture=path.join(root,'fixture');
|
|
17
|
+
await (await import('node:fs/promises')).mkdir(fixture);
|
|
18
|
+
await writeFile(path.join(fixture,'a.txt'),'alpha\\n'.repeat(64));
|
|
19
|
+
await writeFile(path.join(fixture,'b.txt'),'bravo\\n'.repeat(64));
|
|
20
|
+
await writeFile(path.join(fixture,'c.txt'),'charlie\\n'.repeat(64));
|
|
21
|
+
const policyPath=path.join(root,'policy.json'), grantPath=path.join(root,'grant.json');
|
|
22
|
+
const policyDoc={schema:'deadbyte.coding-policy.v1',enabled:true,lease_file:grantPath,evidence_dir:path.join(root,'evidence'),roots:{bench:{path:fixture,read:true,write:false,exec:false,deny_paths:[]}},commands:{},max_read_bytes:65536,max_write_bytes:65536};
|
|
23
|
+
await writeFile(policyPath,JSON.stringify(policyDoc));
|
|
24
|
+
const policy=await loadCodingPolicy(policyPath);
|
|
25
|
+
await writeFile(grantPath,JSON.stringify({schema:'deadbyte.coding-grant.v2',policy_sha256:policy.policy_sha256,instance_nonce:'e'.repeat(64),root_ids:['bench'],granted_at_utc:new Date().toISOString()}));
|
|
26
|
+
const keys=await generateTrustKeypair({privateKeyPath:path.join(root,'private.pem'),publicKeyPath:path.join(root,'public.pem')});
|
|
27
|
+
const db=createMachineFsRuntime({policy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
|
|
28
|
+
const commanderFile=path.join(fixture,'a.txt');
|
|
29
|
+
const commanderDir=fixture;
|
|
30
|
+
const t=async(label,fn,n=N)=>{const t0=performance.now();for(let i=0;i<n;i++)await fn();return {label,iterations:n,elapsed_ms:+(performance.now()-t0).toFixed(3),ops_per_sec:Math.round(n/((performance.now()-t0)/1000))};};
|
|
31
|
+
const rows=[];
|
|
32
|
+
rows.push(await t('Commander read_file',()=>readFileFromDisk(commanderFile,{offset:0,length:4096})));
|
|
33
|
+
rows.push(await t('DEADBYTE read_file',()=>db.read({rootId:'bench',relativePath:'a.txt',offset:0,maxBytes:4096})));
|
|
34
|
+
rows.push(await t('Commander list_directory',()=>listDirectory(commanderDir,1)));
|
|
35
|
+
rows.push(await t('DEADBYTE list_directory',()=>db.list({rootId:'bench',relativePath:'.',limit:100})));
|
|
36
|
+
rows.push(await t('Commander get_file_info',()=>getFileInfo(commanderFile)));
|
|
37
|
+
rows.push(await t('DEADBYTE get_file_info',()=>db.stat({rootId:'bench',relativePath:'a.txt',hash:true})));
|
|
38
|
+
rows.push(await t('Commander read_multiple_files',()=>readMultipleFiles([path.join(commanderDir,'a.txt'),path.join(commanderDir,'b.txt')])));
|
|
39
|
+
rows.push(await t('DEADBYTE read_multiple_files', async()=>{ await db.read({rootId:'bench',relativePath:'a.txt',offset:0,maxBytes:4096}); await db.read({rootId:'bench',relativePath:'b.txt',offset:0,maxBytes:4096}); }));
|
|
40
|
+
console.log(JSON.stringify({schema:'deadbyte.commander-real-performance-benchmark.v1',reference:'@wonderwhy-er/desktop-commander dist local stage',iterations:N,rows},null,2));
|
|
41
|
+
await rm(root,{recursive:true,force:true});
|
package/scripts/deploy-r47.mjs
CHANGED
|
@@ -1,13 +1,61 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { readFile, access } from 'node:fs/promises';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
5
6
|
import { fileURLToPath } from 'node:url';
|
|
6
7
|
import { loadPowerShellDataFile, runSlotUpdateCli } from '../src/runtime-update.mjs';
|
|
8
|
+
import { parseActivePointer } from '../src/release-slot.mjs';
|
|
7
9
|
|
|
8
10
|
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
9
11
|
const state=path.join(os.homedir(),'.deadbyte-mcp');
|
|
10
12
|
const sha=async f=>createHash('sha256').update(await readFile(f)).digest('hex');
|
|
13
|
+
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
|
14
|
+
|
|
15
|
+
async function waitForActivation({handoffPath,expectedVersion,expectedManifest,timeoutMs=900000,pollMs=1000}){
|
|
16
|
+
const evidenceDir=path.dirname(path.resolve(handoffPath));
|
|
17
|
+
const statePath=path.join(evidenceDir,'state.json');
|
|
18
|
+
const receiptPath=path.join(evidenceDir,'receipt.json');
|
|
19
|
+
const failurePath=path.join(evidenceDir,'failure-receipt.json');
|
|
20
|
+
const deadline=Date.now()+timeoutMs;
|
|
21
|
+
let lastPhase='unknown';
|
|
22
|
+
|
|
23
|
+
while(Date.now()<deadline){
|
|
24
|
+
// Do not poll state.json during activation: the detached supervisor atomically replaces it,
|
|
25
|
+
// and Windows can reject that rename while a read handle is still open. Final receipt/failure
|
|
26
|
+
// files are the synchronization boundary and avoid a cross-process read/write race.
|
|
27
|
+
try{
|
|
28
|
+
const receipt=JSON.parse(await readFile(receiptPath,'utf8'));
|
|
29
|
+
const body=receipt?.body;
|
|
30
|
+
const pointer=body?.active_pointer;
|
|
31
|
+
if(body?.schema!=='deadbyte.r27-slot-update-receipt.v1') throw new Error('activation receipt schema mismatch');
|
|
32
|
+
if(body?.rollback_performed!==false) throw new Error('activation receipt reports rollback');
|
|
33
|
+
if(pointer?.version!==expectedVersion||pointer?.manifest_sha256!==expectedManifest) {
|
|
34
|
+
throw new Error('activation receipt target mismatch');
|
|
35
|
+
}
|
|
36
|
+
parseActivePointer(JSON.stringify(pointer));
|
|
37
|
+
|
|
38
|
+
const activePath=path.join(state,'active-release.json');
|
|
39
|
+
const active=parseActivePointer(await readFile(activePath,'utf8'));
|
|
40
|
+
if(active.version!==expectedVersion||active.manifest_sha256!==expectedManifest) {
|
|
41
|
+
throw new Error('active pointer does not match expected release');
|
|
42
|
+
}
|
|
43
|
+
return {receiptPath,statePath,phase:lastPhase,release_id:active.release_id};
|
|
44
|
+
}catch(error){
|
|
45
|
+
if(error?.code!=='ENOENT') throw error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try{
|
|
49
|
+
await access(failurePath);
|
|
50
|
+
throw new Error('slot activation failure receipt detected');
|
|
51
|
+
}catch(error){
|
|
52
|
+
if(error?.code!=='ENOENT') throw error;
|
|
53
|
+
}
|
|
54
|
+
await sleep(pollMs);
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`slot activation timed out after ${timeoutMs}ms; last_phase=${lastPhase}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
11
59
|
const manifest=await sha(path.join(root,'MANIFEST.SHA256'));
|
|
12
60
|
const pointer=JSON.parse(await readFile(path.join(state,'active-release.json'),'utf8'));
|
|
13
61
|
const cfg=await loadPowerShellDataFile(pointer.config_path);
|
|
@@ -16,12 +64,23 @@ const bridgeScript=path.join(bridgeRoot,'src','bridge.mjs');
|
|
|
16
64
|
const pkg=JSON.parse(await readFile(path.join(root,'package.json'),'utf8'));
|
|
17
65
|
const expectedVersion=pkg.version;
|
|
18
66
|
const args=[
|
|
19
|
-
'--stage',root,'--development-root',root,'--expected-version',expectedVersion,'--expected-manifest',manifest,
|
|
20
|
-
'--process-policy',path.join(root,'controller','deadbyte-process-policy.json'),'--process-policy-sha256',await sha(path.join(root,'controller','deadbyte-process-policy.json')),
|
|
21
|
-
'--desktop-policy',path.join(root,'controller','deadbyte-desktop-policy.json'),'--desktop-policy-sha256',await sha(path.join(root,'controller','deadbyte-desktop-policy.json')),
|
|
22
|
-
'--semantic-lsp-config',path.join(root,'controller','deadbyte-semantic-lsp-config.json'),'--semantic-lsp-config-sha256',await sha(path.join(root,'controller','deadbyte-semantic-lsp-config.json')),
|
|
23
|
-
'--bridge-root',bridgeRoot,'--bridge-script-sha256',await sha(bridgeScript),
|
|
24
|
-
'--enable-machine-fs','--enable-observation','--enable-desktop','--enable-autonomous-supervisor','--
|
|
67
|
+
'--stage',root,'--development-root',root,'--expected-version',expectedVersion,'--expected-manifest',manifest,
|
|
68
|
+
'--process-policy',path.join(root,'controller','deadbyte-process-policy.json'),'--process-policy-sha256',await sha(path.join(root,'controller','deadbyte-process-policy.json')),
|
|
69
|
+
'--desktop-policy',path.join(root,'controller','deadbyte-desktop-policy.json'),'--desktop-policy-sha256',await sha(path.join(root,'controller','deadbyte-desktop-policy.json')),
|
|
70
|
+
'--semantic-lsp-config',path.join(root,'controller','deadbyte-semantic-lsp-config.json'),'--semantic-lsp-config-sha256',await sha(path.join(root,'controller','deadbyte-semantic-lsp-config.json')),
|
|
71
|
+
'--bridge-root',bridgeRoot,'--bridge-script-sha256',await sha(bridgeScript),
|
|
72
|
+
'--enable-machine-fs','--enable-observation','--enable-desktop','--enable-autonomous-supervisor','--recover-offline-predecessor'
|
|
25
73
|
];
|
|
26
74
|
const result=await runSlotUpdateCli(args,{stateRoot:state});
|
|
27
|
-
|
|
75
|
+
if(!result.handed_off) throw new Error('release activation unexpectedly completed inline; refusing to skip post-activation verification');
|
|
76
|
+
const verification=await waitForActivation({handoffPath:result.handoff_path,expectedVersion,expectedManifest:manifest});
|
|
77
|
+
console.log(JSON.stringify({
|
|
78
|
+
status:'activated',
|
|
79
|
+
manifest_sha256:manifest,
|
|
80
|
+
previous_release:pointer.release_id,
|
|
81
|
+
update_id:result.update_id,
|
|
82
|
+
supervisor_pid:result.supervisor_pid,
|
|
83
|
+
release_id:verification.release_id,
|
|
84
|
+
receipt_path:verification.receiptPath,
|
|
85
|
+
state_path:verification.statePath
|
|
86
|
+
},null,2));
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const root=path.resolve(here,'..');
|
|
8
|
+
const testDir=path.join(root,'test');
|
|
9
|
+
const excluded=new Set(['full-max-catalog-r44.test.mjs','full-max-catalog-r45.test.mjs','full-max-catalog-r46.test.mjs']);
|
|
10
|
+
const files=(await readdir(testDir,{withFileTypes:true}))
|
|
11
|
+
.filter(entry=>entry.isFile()&&entry.name.endsWith('.test.mjs')&&!excluded.has(entry.name));
|
|
12
|
+
const weighted=[];
|
|
13
|
+
for(const entry of files) weighted.push({path:path.join('test',entry.name),size:(await stat(path.join(testDir,entry.name))).size});
|
|
14
|
+
weighted.sort((a,b)=>b.size-a.size||a.path.localeCompare(b.path,'en'));
|
|
15
|
+
const batchCount=8;
|
|
16
|
+
const batches=Array.from({length:batchCount},()=>[]);
|
|
17
|
+
weighted.forEach((entry,index)=>batches[index%batchCount].push(entry.path));
|
|
18
|
+
let total=0;
|
|
19
|
+
for(let index=0;index<batchCount;index++){
|
|
20
|
+
const batch=batches[index].sort((a,b)=>a.localeCompare(b,'en'));
|
|
21
|
+
console.log(`R47-BOUNDED-REGRESSION batch=${index+1}/${batchCount} files=${batch.length}`);
|
|
22
|
+
const result=spawnSync(process.execPath,['--test','--test-concurrency=1',...batch],{cwd:root,stdio:'inherit',windowsHide:true,shell:false});
|
|
23
|
+
if(result.error) throw result.error;
|
|
24
|
+
if(result.signal) throw new Error(`regression batch ${index} terminated by ${result.signal}`);
|
|
25
|
+
if(result.status!==0) throw new Error(`regression batch ${index} failed with exit code ${result.status}`);
|
|
26
|
+
total+=batch.length;
|
|
27
|
+
}
|
|
28
|
+
console.log(`R47-BOUNDED-REGRESSION PASS files=${total} batches=${batchCount}`);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const target=process.argv[2];
|
|
5
|
+
if(!target) throw new Error('target path required');
|
|
6
|
+
async function walk(dir,out=[]){
|
|
7
|
+
for(const e of await readdir(dir,{withFileTypes:true})){
|
|
8
|
+
const p=path.join(dir,e.name);
|
|
9
|
+
if(e.isDirectory()) await walk(p,out);
|
|
10
|
+
else { const s=await stat(p); out.push({path:path.relative(target,p).replaceAll('\\\\','/'),bytes:s.size}); }
|
|
11
|
+
}
|
|
12
|
+
return out;
|
|
13
|
+
}
|
|
14
|
+
const files=await walk(target);
|
|
15
|
+
const groups=new Map();
|
|
16
|
+
for(const f of files){const ext=path.extname(f.path).toLowerCase()||'<none>'; groups.set(ext,(groups.get(ext)||0)+1);}
|
|
17
|
+
console.log(JSON.stringify({target,file_count:files.length,total_bytes:files.reduce((n,f)=>n+f.bytes,0),extensions:Object.fromEntries([...groups].sort((a,b)=>a[0].localeCompare(b[0]))),files:files.sort((a,b)=>a.path.localeCompare(b.path))},null,2));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import { mkdtemp, readFile, rm, unlink } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import os from 'node:os';
|
|
@@ -64,6 +64,27 @@ async function main() {
|
|
|
64
64
|
const version = pkg.version;
|
|
65
65
|
if (pkg.name !== 'deadbyte-mcp') throw new Error(`unexpected package name: ${pkg.name}`);
|
|
66
66
|
|
|
67
|
+
const releaseBytes = await readFile(path.join(root, 'RELEASE.json'));
|
|
68
|
+
if (releaseBytes.subarray(0, 3).equals(Buffer.from([0xEF, 0xBB, 0xBF]))) {
|
|
69
|
+
throw new Error('RELEASE.json must be UTF-8 without BOM');
|
|
70
|
+
}
|
|
71
|
+
const release = JSON.parse(releaseBytes.toString('utf8'));
|
|
72
|
+
const provenance = JSON.parse(await readFile(path.join(root, 'PROVENANCE.json'), 'utf8'));
|
|
73
|
+
if (release.schema !== 'deadbyte.release.v1' || release.name !== pkg.name) {
|
|
74
|
+
throw new Error('RELEASE.json schema/package identity mismatch');
|
|
75
|
+
}
|
|
76
|
+
if (release.version !== version) {
|
|
77
|
+
throw new Error(`RELEASE.json version mismatch: package=${version} release=${release.version}`);
|
|
78
|
+
}
|
|
79
|
+
if (provenance.version !== version) {
|
|
80
|
+
throw new Error(`PROVENANCE.json version mismatch: package=${version} provenance=${provenance.version}`);
|
|
81
|
+
}
|
|
82
|
+
if (release.parent?.release_id !== provenance.parent_release_id ||
|
|
83
|
+
release.parent?.manifest_sha256 !== provenance.parent_manifest_sha256 ||
|
|
84
|
+
release.parent?.generation_manifest_sha256 !== provenance.parent_generation_manifest_sha256) {
|
|
85
|
+
throw new Error('RELEASE.json parent identity does not match sealed PROVENANCE.json lineage');
|
|
86
|
+
}
|
|
87
|
+
|
|
67
88
|
const existing = spawnSync(node, [npmCli, 'view', `deadbyte-mcp@${version}`, 'version', '--json'], {
|
|
68
89
|
cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], shell: false, windowsHide: true,
|
|
69
90
|
});
|
|
@@ -189,6 +210,9 @@ async function main() {
|
|
|
189
210
|
timeout: 300000,
|
|
190
211
|
});
|
|
191
212
|
|
|
213
|
+
console.log('[8/8] Activating the published release into the live immutable slot and waiting for verified handoff...');
|
|
214
|
+
run(node, [path.join(root, 'scripts', 'deploy-r47.mjs')], { cwd: root, timeout: 900000 });
|
|
215
|
+
|
|
192
216
|
releaseSucceeded = true;
|
|
193
217
|
console.log('\nRELEASE SUCCESSFUL:');
|
|
194
218
|
console.log(JSON.stringify({
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
for(const file of ['src/mcp-server.mjs','src/commander-remote-relay-r47.mjs','src/commander-remote-bridge-r47.mjs']){
|
|
3
|
+
const result=spawnSync(process.execPath,['--check',file],{encoding:'utf8',windowsHide:true});
|
|
4
|
+
if(result.status!==0) throw new Error(`${file} syntax check failed: ${result.stderr||result.stdout}`);
|
|
5
|
+
}
|
|
6
|
+
console.log('R47 syntax PASS');
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const file=path.resolve('src/mcp-server.mjs');
|
|
6
|
+
const source=await readFile(file,'utf8');
|
|
7
|
+
const sha=createHash('sha256').update(source).digest('hex');
|
|
8
|
+
const expectedLength=64;
|
|
9
|
+
if(sha.length!==expectedLength) throw new Error(`source hash length invalid: ${sha.length}`);
|
|
10
|
+
const importNeedle="import { registerCommanderCoreBridge } from './commander-core-bridge.mjs';\n";
|
|
11
|
+
const importReplacement=importNeedle+"import { registerCommanderRemoteBridge } from './commander-remote-bridge-r47.mjs';\nimport { createCommanderRemoteRelayR47 } from './commander-remote-relay-r47.mjs';\nimport { createRemoteDevicePlaneR44 } from './remote-device-r44.mjs';\n";
|
|
12
|
+
const planeNeedle="const skillRoot=path.join(runtimeStateRoot,'skills');\nawait mkdir(skillRoot,{recursive:true});\nconst skillRuntime=await createSkillPlane({root:skillRoot,verifyKeyPath});\n";
|
|
13
|
+
const planeReplacement=planeNeedle+"\nconst remoteRelayEndpoint=process.env.DEADBYTE_REMOTE_RELAY_ENDPOINT||process.env.DEADBYTE_REMOTE_ENDPOINT||'';\nconst remoteRelayCapabilities=new Set(String(process.env.DEADBYTE_REMOTE_CAPABILITIES||'').split(',').map(value=>value.trim()).filter(Boolean));\nconst remoteRelay=remoteRelayEndpoint ? await createCommanderRemoteRelayR47({endpoint:remoteRelayEndpoint}) : null;\nconst remoteDevicePlane=remoteRelay\n ? await createRemoteDevicePlaneR44({\n stateDir:path.join(runtimeStateRoot,'remote-device-r44'),\n relay:remoteRelay,\n authorizeCapability:async({capabilityId})=>remoteRelayCapabilities.has(capabilityId)\n })\n : null;\n";
|
|
14
|
+
const registerNeedle=" if (surfaceProfile === 'full') registerCommanderCoreBridge(toolServer,{machineFsRuntime,processRuntime,searchRuntime:codingRuntime?.search??null});\n return server;\n";
|
|
15
|
+
const registerReplacement=" if (surfaceProfile === 'full') {\n registerCommanderCoreBridge(toolServer,{machineFsRuntime,processRuntime,searchRuntime:codingRuntime?.search??null});\n registerCommanderRemoteBridge(toolServer,{remoteDevicePlane});\n }\n return server;\n";
|
|
16
|
+
if(source.includes("commander-remote-relay-r47.mjs")) throw new Error('R47 remote relay already wired');
|
|
17
|
+
if(source.split(importNeedle).length!==2) throw new Error('import anchor mismatch');
|
|
18
|
+
if(source.split(planeNeedle).length!==2) throw new Error('plane anchor mismatch');
|
|
19
|
+
if(source.split(registerNeedle).length!==2) throw new Error('registration anchor mismatch');
|
|
20
|
+
const next=source.replace(importNeedle,importReplacement).replace(planeNeedle,planeReplacement).replace(registerNeedle,registerReplacement);
|
|
21
|
+
await writeFile(file,next,'utf8');
|
|
22
|
+
const verify=createHash('sha256').update(await readFile(file)).digest('hex');
|
|
23
|
+
console.log(JSON.stringify({status:'committed',source_sha256:sha,source_sha256_length:sha.length,result_sha256:verify,result_sha256_length:verify.length}));
|
package/src/atomic-mutation.mjs
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
-
import { open, rename, rm } from 'node:fs/promises';
|
|
2
|
+
import { open, rename, rm, stat } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
|
|
5
5
|
const sha256Bytes = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
6
|
+
const LOCK_WAIT_MS = 10;
|
|
7
|
+
const LOCK_TIMEOUT_MS = 30000;
|
|
8
|
+
const LOCK_STALE_MS = 120000;
|
|
9
|
+
|
|
10
|
+
async function acquireMutationLock(target) {
|
|
11
|
+
const lock = `${target}.deadbyte-mutation.lock`;
|
|
12
|
+
const started = Date.now();
|
|
13
|
+
while (true) {
|
|
14
|
+
try {
|
|
15
|
+
const handle = await open(lock, 'wx', 0o600);
|
|
16
|
+
await handle.writeFile(`${process.pid}\\n${Date.now()}\\n`);
|
|
17
|
+
await handle.sync();
|
|
18
|
+
await handle.close();
|
|
19
|
+
return async () => { await rm(lock, { force: true }).catch(() => {}); };
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (error?.code !== 'EEXIST') throw error;
|
|
22
|
+
if (Date.now() - started >= LOCK_TIMEOUT_MS) throw new Error('atomic mutation lock timed out');
|
|
23
|
+
try {
|
|
24
|
+
const info = await stat(lock);
|
|
25
|
+
if (Date.now() - info.mtimeMs > LOCK_STALE_MS) await rm(lock, { force: true }).catch(() => {});
|
|
26
|
+
} catch (statError) {
|
|
27
|
+
if (statError?.code !== 'ENOENT') throw statError;
|
|
28
|
+
}
|
|
29
|
+
await new Promise(resolve => setTimeout(resolve, LOCK_WAIT_MS));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
6
33
|
|
|
7
34
|
export async function atomicMutation({
|
|
8
35
|
target,
|
|
@@ -19,37 +46,40 @@ export async function atomicMutation({
|
|
|
19
46
|
if (typeof readState !== 'function' || typeof assertExpected !== 'function') {
|
|
20
47
|
throw new Error('atomic mutation requires readState and assertExpected');
|
|
21
48
|
}
|
|
22
|
-
const
|
|
23
|
-
assertExpected(before, expected);
|
|
24
|
-
const temp = path.join(
|
|
25
|
-
path.dirname(target),
|
|
26
|
-
`.\${path.basename(target)}.\${tempTag}-\${process.pid}-\${randomBytes(8).toString('hex')}.tmp`
|
|
27
|
-
);
|
|
28
|
-
let handle;
|
|
49
|
+
const releaseLock = await acquireMutationLock(target);
|
|
29
50
|
try {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
const before = await readState(target);
|
|
52
|
+
assertExpected(before, expected);
|
|
53
|
+
const temp = path.join(
|
|
54
|
+
path.dirname(target),
|
|
55
|
+
`.${path.basename(target)}.${tempTag}-${process.pid}-${randomBytes(8).toString('hex')}.tmp`
|
|
56
|
+
);
|
|
57
|
+
let handle;
|
|
58
|
+
try {
|
|
59
|
+
if (authorityCheck) await authorityCheck();
|
|
60
|
+
handle = await open(temp, 'wx', 0o600);
|
|
61
|
+
await handle.writeFile(bytes);
|
|
62
|
+
await handle.sync();
|
|
63
|
+
await handle.close();
|
|
64
|
+
handle = null;
|
|
36
65
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
66
|
+
const race = await readState(target);
|
|
67
|
+
assertExpected(race, expected);
|
|
68
|
+
if (authorityCheck) await authorityCheck();
|
|
40
69
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
70
|
+
await rename(temp, target);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (handle) await handle.close().catch(() => {});
|
|
73
|
+
await rm(temp, { force: true }).catch(() => {});
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
47
76
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
77
|
+
const after = await readState(target);
|
|
78
|
+
const expectedAfter = sha256Bytes(bytes);
|
|
79
|
+
if (!after.exists || after.sha256 !== expectedAfter) throw new Error('atomic mutation post-commit hash mismatch');
|
|
80
|
+
if (verifyPostcondition) await verifyPostcondition({ before, after, expected_after_sha256: expectedAfter });
|
|
81
|
+
return { before, after, expected_after_sha256: expectedAfter };
|
|
82
|
+
} finally {
|
|
83
|
+
await releaseLock();
|
|
52
84
|
}
|
|
53
|
-
if (verifyPostcondition) await verifyPostcondition({ before, after, expected_after_sha256: expectedAfter });
|
|
54
|
-
return { before, after, expected_after_sha256: expectedAfter };
|
|
55
85
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export const COMMANDER_COMPAT_SCHEMA = 'deadbyte.commander-compat.v2';
|
|
4
|
+
|
|
5
|
+
const sha256 = value => createHash('sha256').update(JSON.stringify(value), 'utf8').digest('hex');
|
|
6
|
+
|
|
7
|
+
// Behavioral/interface surface observed from the local reference package.
|
|
8
|
+
// Implementations are independent DEADBYTE native planes.
|
|
9
|
+
export const COMMANDER_COMPAT_TOOLS = Object.freeze([
|
|
10
|
+
'contained_transform', 'create_directory', 'edit_block', 'evidence_verify',
|
|
11
|
+
'file_preview', 'file_read', 'force_terminate', 'get_config', 'get_file_info',
|
|
12
|
+
'get_more_search_results', 'get_prompts', 'get_recent_tool_calls', 'get_usage_stats',
|
|
13
|
+
'give_feedback_to_desktop_commander', 'host_exec', 'host_file_read', 'host_file_write',
|
|
14
|
+
'interact_with_process', 'kill_process', 'list_directory', 'list_processes',
|
|
15
|
+
'list_searches', 'list_sessions', 'markdown_editor', 'move_file', 'proof_execute',
|
|
16
|
+
'read_file', 'read_multiple_files', 'read_process_output', 'remote_device',
|
|
17
|
+
'remote_mcp', 'resources', 'set_config_value', 'skill_catalog', 'start_process',
|
|
18
|
+
'start_search', 'stop_search', 'workspace_open', 'write_file', 'write_pdf'
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
export const DEADBYTE_NATIVE_TOOLS = Object.freeze({
|
|
22
|
+
contained_transform: 'contained.transform',
|
|
23
|
+
create_directory: 'machine_fs.mkdir',
|
|
24
|
+
edit_block: 'native_diff',
|
|
25
|
+
evidence_verify: 'evidence.verify',
|
|
26
|
+
file_preview: 'commander.file_preview',
|
|
27
|
+
file_read: 'machine_fs.read',
|
|
28
|
+
force_terminate: 'process.terminate',
|
|
29
|
+
get_config: 'commander.config.get',
|
|
30
|
+
get_file_info: 'machine_fs.stat',
|
|
31
|
+
get_more_search_results: 'machine_search.page',
|
|
32
|
+
get_prompts: 'commander.prompts',
|
|
33
|
+
get_recent_tool_calls: 'operation_history.recent',
|
|
34
|
+
get_usage_stats: 'operation_history.stats',
|
|
35
|
+
give_feedback_to_desktop_commander: 'commander.feedback',
|
|
36
|
+
host_exec: 'host.exec',
|
|
37
|
+
host_file_read: 'host.file_read',
|
|
38
|
+
host_file_write: 'host.file_write',
|
|
39
|
+
interact_with_process: 'process.input',
|
|
40
|
+
kill_process: 'process.terminate',
|
|
41
|
+
list_directory: 'machine_fs.list',
|
|
42
|
+
list_processes: 'process.sessions',
|
|
43
|
+
list_searches: 'machine_search.list',
|
|
44
|
+
list_sessions: 'process.sessions',
|
|
45
|
+
markdown_editor: 'native_diff',
|
|
46
|
+
move_file: 'machine_fs.move',
|
|
47
|
+
proof_execute: 'proof.execute',
|
|
48
|
+
read_file: 'machine_fs.read',
|
|
49
|
+
read_multiple_files: 'machine_fs.read_many',
|
|
50
|
+
read_process_output: 'process.output',
|
|
51
|
+
remote_device: 'remote_device',
|
|
52
|
+
remote_mcp: 'remote_mcp',
|
|
53
|
+
resources: 'commander.resources',
|
|
54
|
+
set_config_value: 'commander.config.set',
|
|
55
|
+
skill_catalog: 'skill.catalog',
|
|
56
|
+
start_process: 'process.start',
|
|
57
|
+
start_search: 'machine_search.start',
|
|
58
|
+
stop_search: 'machine_search.cancel',
|
|
59
|
+
workspace_open: 'workspace.open',
|
|
60
|
+
write_file: 'machine_fs.write',
|
|
61
|
+
write_pdf: 'document.write_pdf'
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export function commanderCompatibilitySnapshot() {
|
|
65
|
+
const tools = COMMANDER_COMPAT_TOOLS.map(name => ({
|
|
66
|
+
name,
|
|
67
|
+
native: DEADBYTE_NATIVE_TOOLS[name] ?? null
|
|
68
|
+
}));
|
|
69
|
+
return {
|
|
70
|
+
schema: COMMANDER_COMPAT_SCHEMA,
|
|
71
|
+
tools,
|
|
72
|
+
coverage: tools.filter(x => x.native).length,
|
|
73
|
+
total: tools.length,
|
|
74
|
+
snapshot_sha256: sha256(tools)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function assertCommanderCompatibilitySnapshot(snapshot) {
|
|
79
|
+
if (!snapshot || snapshot.schema !== COMMANDER_COMPAT_SCHEMA || !Array.isArray(snapshot.tools)) {
|
|
80
|
+
throw new Error('commander compatibility snapshot invalid');
|
|
81
|
+
}
|
|
82
|
+
if (snapshot.total !== COMMANDER_COMPAT_TOOLS.length) throw new Error('commander compatibility total mismatch');
|
|
83
|
+
if (snapshot.coverage !== snapshot.tools.filter(x => x.native).length) throw new Error('commander compatibility coverage mismatch');
|
|
84
|
+
if (snapshot.tools.length !== COMMANDER_COMPAT_TOOLS.length) throw new Error('commander compatibility surface length mismatch');
|
|
85
|
+
for (const item of snapshot.tools) {
|
|
86
|
+
if (!COMMANDER_COMPAT_TOOLS.includes(item.name)) throw new Error(`unknown commander compatibility tool: ${item.name}`);
|
|
87
|
+
if (typeof item.native !== 'string' || !item.native) throw new Error(`missing native mapping: ${item.name}`);
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|