deadbyte-mcp 0.15.21 → 0.15.23
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 +53 -28
- package/PROVENANCE.json +10 -10
- package/README.txt +18 -7
- package/RELEASE.json +51 -16
- package/SBOM.cdx.json +33 -7
- package/TEST-MATRIX.json +95 -18
- package/bin/deadbyte-hardware-observer-r45.exe +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/docs/R48-FULL-TOOL-CATALOG.json +124 -0
- package/native/deadbyte-hardware-observer.cpp +535 -12
- package/package.json +95 -93
- package/scripts/activate-skill-creator-1.1.1.mjs +46 -0
- package/scripts/build-advanced-pcb-skill.mjs +208 -0
- package/scripts/build-hardware-r45.ps1 +19 -0
- package/scripts/diagnose-skill-tree.mjs +8 -0
- package/scripts/full-regression-r48-batch.mjs +27 -0
- package/scripts/npm-release-r48.mjs +252 -0
- package/scripts/pcb-skill-src-SKILL.md.txt +67 -0
- package/scripts/pcb-skill-src-bringup-evidence.md.txt +22 -0
- package/scripts/pcb-skill-src-constraint-model.md.txt +17 -0
- package/scripts/pcb-skill-src-emc-mechanical-dfm.md.txt +17 -0
- package/scripts/pcb-skill-src-high-speed-layout.md.txt +27 -0
- package/scripts/pcb-skill-src-kicad-native.md.txt +30 -0
- package/scripts/pcb-skill-src-kicad_pcb_probe.py.txt +159 -0
- package/scripts/pcb-skill-src-pcb_constraint_gate.py.txt +76 -0
- package/scripts/pcb-skill-src-power-integrity.md.txt +15 -0
- package/scripts/prepare-skill-creator-1.1.1.mjs +113 -0
- package/scripts/promote-local-c-generation.ps1 +217 -0
- package/scripts/promote-local-d-generation.ps1 +217 -0
- package/scripts/test-r48-post-bump.ps1 +8 -0
- package/scripts/test-r48-windows-observation.ps1 +8 -0
- package/scripts/test-skill-creator-semver-matrix.mjs +30 -0
- package/scripts/test-skill-creator-semver.mjs +22 -0
- package/src/capability-ledger.mjs +7 -0
- package/src/capability-resolver.mjs +7 -0
- package/src/compact-gateway-mcp-tools.mjs +16 -2
- package/src/deadbyte-cli.mjs +70 -15
- package/src/machine-fs.mjs +16 -3
- package/src/observation-mcp-tools.mjs +13 -1
- package/src/observation-output-contracts.mjs +8 -0
- package/src/observation-runtime.mjs +54 -2
- package/src/remote-live-log.mjs +189 -35
- package/src/tool-audit.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/full-max-catalog-r47.test.mjs +1 -1
- package/test/full-max-catalog-r48.test.mjs +78 -0
- package/test/machine-fs-runtime.test.mjs +28 -3
- package/test/observation-mcp-tools.test.mjs +4 -3
- package/test/r48-release-contract.test.mjs +24 -14
- package/test/remote-live-log-r37.test.mjs +106 -7
- package/test/remote-session-cli.test.mjs +40 -0
- package/test/tool-audit.test.mjs +17 -0
- package/test/windows-observation-r48.test.mjs +63 -0
- package/test/windows-observation-surface-r48.test.mjs +70 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { execFile } from "node:child_process";
|
|
6
|
+
const ex=promisify(execFile);
|
|
7
|
+
const creator=process.argv[2];
|
|
8
|
+
if(!creator) throw new Error("creator path required");
|
|
9
|
+
const valid=["1.0.0","0.0.1","1.1.1","12.34.56","1.0.0-rc.1"];
|
|
10
|
+
const invalid=["1.0","v1.0.0","1.a.0","",String.fromCharCode(49,92,97,48,92,98,48)];
|
|
11
|
+
const results=[];
|
|
12
|
+
for(const version of valid){
|
|
13
|
+
const tmp=await mkdtemp(path.join(os.tmpdir(),"deadbyte-semver-valid-"));
|
|
14
|
+
try{
|
|
15
|
+
await ex(process.execPath,[creator,tmp,"fixture",version],{timeout:30000,maxBuffer:1048576});
|
|
16
|
+
results.push({version,expected:"PASS",observed:"PASS"});
|
|
17
|
+
}finally{await rm(tmp,{recursive:true,force:true});}
|
|
18
|
+
}
|
|
19
|
+
for(const version of invalid){
|
|
20
|
+
const tmp=await mkdtemp(path.join(os.tmpdir(),"deadbyte-semver-invalid-"));
|
|
21
|
+
let rejected=false;
|
|
22
|
+
try{
|
|
23
|
+
await ex(process.execPath,[creator,tmp,"fixture",version],{timeout:30000,maxBuffer:1048576});
|
|
24
|
+
}catch(e){
|
|
25
|
+
if(e.code===1 && String(e.stderr).includes("invalid version")) rejected=true;
|
|
26
|
+
}finally{await rm(tmp,{recursive:true,force:true});}
|
|
27
|
+
if(!rejected) throw new Error("invalid version accepted: "+JSON.stringify(version));
|
|
28
|
+
results.push({version,expected:"FAIL",observed:"FAIL"});
|
|
29
|
+
}
|
|
30
|
+
console.log(JSON.stringify({status:"PASS",cases:results},null,2));
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { execFile } from "node:child_process";
|
|
6
|
+
|
|
7
|
+
const ex = promisify(execFile);
|
|
8
|
+
const creator = process.argv[2];
|
|
9
|
+
if (!creator) throw new Error("usage: node test-skill-creator-semver.mjs <create-skill.mjs>");
|
|
10
|
+
const tmp = await mkdtemp(path.join(os.tmpdir(), "deadbyte-skill-creator-semver-"));
|
|
11
|
+
try {
|
|
12
|
+
const r = await ex(process.execPath, [creator, tmp, "semver-regression-fixture", "1.0.0"], {
|
|
13
|
+
timeout: 30000,
|
|
14
|
+
maxBuffer: 1024 * 1024
|
|
15
|
+
});
|
|
16
|
+
if (!r.stdout.includes("semver-regression-fixture")) {
|
|
17
|
+
throw new Error("create-skill returned success without fixture path");
|
|
18
|
+
}
|
|
19
|
+
console.log(JSON.stringify({status:"PASS", version:"1.0.0", stdout:r.stdout.trim()}));
|
|
20
|
+
} finally {
|
|
21
|
+
await rm(tmp, {recursive:true, force:true});
|
|
22
|
+
}
|
|
@@ -137,6 +137,13 @@ export const R36_CAPABILITY_ENTRIES=Object.freeze([
|
|
|
137
137
|
gateway('observation.process_list','machine_process_list','machine_observe','process_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
138
138
|
gateway('observation.system_status','machine_system_status','machine_observe','system_status','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
139
139
|
gateway('observation.hardware_query','machine_hardware_query','machine_observe','hardware_query','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
140
|
+
gateway('observation.registry_query','machine_windows_query','machine_observe','registry_query','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
141
|
+
gateway('observation.service_list','machine_windows_query','machine_observe','service_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
142
|
+
gateway('observation.driver_list','machine_windows_query','machine_observe','driver_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
143
|
+
gateway('observation.socket_list','machine_windows_query','machine_observe','socket_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
144
|
+
gateway('observation.volume_list','machine_windows_query','machine_observe','volume_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
145
|
+
gateway('observation.usb_list','machine_windows_query','machine_observe','usb_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
146
|
+
gateway('observation.wsl_list','machine_windows_query','machine_observe','wsl_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
140
147
|
|
|
141
148
|
gateway('desktop.status','desktop_status','desktop_control','status','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
|
|
142
149
|
gateway('desktop.observe','desktop_observe','desktop_control','observe','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
|
|
@@ -67,6 +67,13 @@ const CAPABILITY_OPERATIONS = Object.freeze({
|
|
|
67
67
|
'observation.process_list': ['process_list'],
|
|
68
68
|
'observation.system_status': ['system_status'],
|
|
69
69
|
'observation.hardware_query': ['hardware_query'],
|
|
70
|
+
'observation.registry_query': ['registry_query'],
|
|
71
|
+
'observation.service_list': ['service_list'],
|
|
72
|
+
'observation.driver_list': ['driver_list'],
|
|
73
|
+
'observation.socket_list': ['socket_list'],
|
|
74
|
+
'observation.volume_list': ['volume_list'],
|
|
75
|
+
'observation.usb_list': ['usb_list'],
|
|
76
|
+
'observation.wsl_list': ['wsl_list'],
|
|
70
77
|
'desktop.status': ['status'],
|
|
71
78
|
'desktop.observe': ['observe'],
|
|
72
79
|
'desktop.target': ['target'],
|
|
@@ -55,7 +55,14 @@ export const machineObserveInputSchema=z.discriminatedUnion('op',[
|
|
|
55
55
|
z.strictObject({op:z.literal('search_cancel'),search_id:hex64}),
|
|
56
56
|
z.strictObject({op:z.literal('process_list'),limit:z.number().int().min(1).max(256).default(100),include_external:z.boolean().default(false),cursor:z.string().max(512).nullable().default(null)}),
|
|
57
57
|
z.strictObject({op:z.literal('system_status')}),
|
|
58
|
-
z.strictObject({op:z.literal('hardware_query'),query:z.enum(['cpu','gpu','memory','storage','network','pci'])})
|
|
58
|
+
z.strictObject({op:z.literal('hardware_query'),query:z.enum(['cpu','gpu','memory','storage','network','pci'])}),
|
|
59
|
+
z.strictObject({op:z.literal('registry_query'),hive:z.enum(['HKLM','HKCU']),key:z.string().min(1).max(1024),value:z.string().max(260).nullable().default(null)}),
|
|
60
|
+
z.strictObject({op:z.literal('service_list')}),
|
|
61
|
+
z.strictObject({op:z.literal('driver_list')}),
|
|
62
|
+
z.strictObject({op:z.literal('socket_list')}),
|
|
63
|
+
z.strictObject({op:z.literal('volume_list')}),
|
|
64
|
+
z.strictObject({op:z.literal('usb_list')}),
|
|
65
|
+
z.strictObject({op:z.literal('wsl_list')})
|
|
59
66
|
]);
|
|
60
67
|
|
|
61
68
|
const capabilityManifestInputSchema=z.strictObject({});
|
|
@@ -119,7 +126,14 @@ async function machineObserve(args,machineRuntime,observationRuntime){
|
|
|
119
126
|
search_cancel:r=>r.searchCancel({searchId:args.search_id}),
|
|
120
127
|
process_list:r=>r.processList({limit:args.limit,includeExternal:args.include_external,cursor:args.cursor}),
|
|
121
128
|
system_status:r=>r.systemStatus(),
|
|
122
|
-
hardware_query:r=>r.hardwareQuery({query:args.query})
|
|
129
|
+
hardware_query:r=>r.hardwareQuery({query:args.query}),
|
|
130
|
+
registry_query:r=>r.windowsQuery({query:'registry',hive:args.hive,key:args.key,value:args.value}),
|
|
131
|
+
service_list:r=>r.windowsQuery({query:'services'}),
|
|
132
|
+
driver_list:r=>r.windowsQuery({query:'drivers'}),
|
|
133
|
+
socket_list:r=>r.windowsQuery({query:'sockets'}),
|
|
134
|
+
volume_list:r=>r.windowsQuery({query:'volumes'}),
|
|
135
|
+
usb_list:r=>r.windowsQuery({query:'usb'}),
|
|
136
|
+
wsl_list:r=>r.windowsQuery({query:'wsl'})
|
|
123
137
|
};
|
|
124
138
|
return invoke(args.op,observationRuntime,'observation',map[args.op]);
|
|
125
139
|
}
|
package/src/deadbyte-cli.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { openRemoteResultJournal, parseRemoteResultCursor } from './remote-resul
|
|
|
13
13
|
import { DEADBYTE_VERSION } from './version.mjs';
|
|
14
14
|
import { parseActivePointer } from './release-slot.mjs';
|
|
15
15
|
import { printDoctor } from './doctor-r45.mjs';
|
|
16
|
-
import { createLiveTimelineTracker, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine } from './remote-live-log.mjs';
|
|
16
|
+
import { createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine } from './remote-live-log.mjs';
|
|
17
17
|
import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
|
|
18
18
|
|
|
19
19
|
const REMOTE_COMMANDS=new Set([
|
|
@@ -46,6 +46,8 @@ function printHelp(){
|
|
|
46
46
|
` npx -y ${exact} mcp # stdio MCP server for local clients`,
|
|
47
47
|
` npx -y ${exact} remote # unified foreground device session + live timeline`,
|
|
48
48
|
` npx -y ${exact} remote --resume <cursor> # replay missed results, then reconnect`,
|
|
49
|
+
` npx -y ${exact} remote --trace # show trace/span identity without payload detail`,
|
|
50
|
+
` npx -y ${exact} remote --filter <expr> # presentation filter: tool/category/status/trace`,
|
|
49
51
|
` npx -y ${exact} remote status`,
|
|
50
52
|
` npx -y ${exact} remote restart`,
|
|
51
53
|
` npx -y ${exact} supervisor status`,
|
|
@@ -71,6 +73,21 @@ async function readActiveRuntimeBinding(root){
|
|
|
71
73
|
try{return parseActivePointer(await readFile(file,'utf8'));}
|
|
72
74
|
catch(error){if(error?.code==='ENOENT') return null;throw new Error(`active runtime pointer invalid: ${boundedSingleLine(error?.message||error,512)}`);}
|
|
73
75
|
}
|
|
76
|
+
async function readLiveRootPaths(root){
|
|
77
|
+
const active=await readActiveRuntimeBinding(root);
|
|
78
|
+
if(!active) return Object.freeze({});
|
|
79
|
+
const file=path.join(active.generation_root,'coding-policy.json');
|
|
80
|
+
let raw;
|
|
81
|
+
try{raw=JSON.parse(await readFile(file,'utf8'));}
|
|
82
|
+
catch(error){if(error?.code==='ENOENT') return Object.freeze({});throw new Error(`live root policy invalid: ${boundedSingleLine(error?.message||error,512)}`);}
|
|
83
|
+
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');
|
|
84
|
+
const roots={};
|
|
85
|
+
for(const [id,spec] of Object.entries(raw.roots)){
|
|
86
|
+
if(!/^[A-Za-z0-9._-]{1,64}$/.test(id)||!spec||typeof spec!=='object'||typeof spec.path!=='string'||!path.isAbsolute(spec.path)) continue;
|
|
87
|
+
roots[id]=path.resolve(spec.path);
|
|
88
|
+
}
|
|
89
|
+
return Object.freeze(roots);
|
|
90
|
+
}
|
|
74
91
|
async function reportVersionCoherence(root,{displayMode='ascii'}={}){
|
|
75
92
|
const active=await readActiveRuntimeBinding(root);
|
|
76
93
|
if(!active||active.version===DEADBYTE_VERSION) return active;
|
|
@@ -227,7 +244,22 @@ async function snapshotRemoteLogOffsets(root){
|
|
|
227
244
|
}
|
|
228
245
|
return offsets;
|
|
229
246
|
}
|
|
230
|
-
|
|
247
|
+
function timelineFilterEvent(timeline){
|
|
248
|
+
if(!timeline) return null;
|
|
249
|
+
if(timeline.event_type==='tool.call') return {
|
|
250
|
+
event_type:'tool.request',request_id:timeline.request_id,tool:timeline.tool,args:timeline.arguments??{}
|
|
251
|
+
};
|
|
252
|
+
return {
|
|
253
|
+
event_type:'tool.result',request_id:timeline.request_id,tool:timeline.tool,
|
|
254
|
+
outcome:{
|
|
255
|
+
is_error:timeline.status==='error',
|
|
256
|
+
...(Number.isFinite(Number(timeline.duration_ms))?{duration_ms:Number(timeline.duration_ms)}:{}),
|
|
257
|
+
...(timeline.side_effect_state?{side_effect_state:timeline.side_effect_state}:{}),
|
|
258
|
+
...(timeline.reason?{reason:timeline.reason}:{})
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTracker=null,timelineFilter=null,timelineWriter=null,verbose=false,displayMode='unicode'}={}){
|
|
231
263
|
process.stdout.write(verbose
|
|
232
264
|
? '[LOG] Verbose live timeline. Full sanitized request detail enabled; Ctrl+C disconnects and revokes authority.\n'
|
|
233
265
|
: '[LOG] Human live timeline. Transport IN/OUT hidden; full replay/audit detail remains on disk. Ctrl+C disconnects and revokes authority.\n');
|
|
@@ -244,10 +276,14 @@ async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTra
|
|
|
244
276
|
? await journal.recordCall(timeline)
|
|
245
277
|
: await journal.recordTerminal(timeline);
|
|
246
278
|
if(recorded?.duplicate) continue;
|
|
279
|
+
const accepted=timelineFilter&&timeline?timelineFilter.accept(timelineFilterEvent(timeline)):true;
|
|
247
280
|
const formatted=formatLogLine(file,line,{activityTracker,sequence:recorded?.event?.seq??null,verbose,displayMode});
|
|
248
|
-
if(!formatted) continue;
|
|
281
|
+
if(!formatted||!accepted) continue;
|
|
249
282
|
const output=Array.isArray(formatted)?formatted:[formatted];
|
|
250
|
-
for(const item of output) if(item)
|
|
283
|
+
for(const item of output) if(item){
|
|
284
|
+
if(timelineWriter) timelineWriter.write(item,{event_type:timeline?.event_type??null,request_id:timeline?.request_id??null});
|
|
285
|
+
else process.stdout.write(`${item}\n`);
|
|
286
|
+
}
|
|
251
287
|
}
|
|
252
288
|
}
|
|
253
289
|
try{await sleep(250,null,{signal});}catch(error){if(error?.name!=='AbortError') throw error;}
|
|
@@ -341,7 +377,7 @@ async function releaseRemoteSession(ctx,marker,{displayMode='unicode'}={}){
|
|
|
341
377
|
const same=current?.schema==='deadbyte.remote-session.v2'&¤t.owner_pid===marker.owner_pid&¤t.session_nonce===marker.session_nonce;
|
|
342
378
|
if(same) await rm(remoteSessionMarker(ctx.root),{force:true});
|
|
343
379
|
}
|
|
344
|
-
async function runRemoteSession({resumeCursor=null,verbose=false}={}){
|
|
380
|
+
async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,filterExpression=''}={}){
|
|
345
381
|
const display=prepareRemoteConsole();
|
|
346
382
|
const displayMode=display.mode;
|
|
347
383
|
const ctx=await remoteContext();
|
|
@@ -353,7 +389,10 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
|
|
|
353
389
|
const resultJournal=await openRemoteResultJournal({
|
|
354
390
|
filePath:remoteResultJournalPath(ctx.root,marker.result_session_id),sessionId:marker.result_session_id,maxEntries:2048
|
|
355
391
|
});
|
|
356
|
-
const
|
|
392
|
+
const rootPaths=await readLiveRootPaths(ctx.root);
|
|
393
|
+
const activityTracker=createLiveTimelineTracker({verbose,displayMode,rootPaths,trace});
|
|
394
|
+
const timelineFilter=filterExpression?createLiveTimelineFilter(filterExpression):null;
|
|
395
|
+
const timelineWriter=createLiveTimelineWriter({stream:process.stdout,displayMode,rewrite:!verbose});
|
|
357
396
|
const aborter=new AbortController();
|
|
358
397
|
const abort=()=>aborter.abort();
|
|
359
398
|
const watchStdin=!process.stdin.isTTY;
|
|
@@ -366,7 +405,7 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
|
|
|
366
405
|
process.stdout.write(`${formatRemoteLifecycle('device_start',{displayMode})}\n`);
|
|
367
406
|
process.stdout.write(`${formatRemoteLifecycle(resumeCursor!==null?'session_restored':'session_new',{displayMode})}\n`);
|
|
368
407
|
const logOffsets=await snapshotRemoteLogOffsets(ctx.root);
|
|
369
|
-
logTask=tailRemoteLogs(ctx.root,aborter.signal,{journal:resultJournal,offsets:logOffsets,activityTracker,verbose,displayMode});
|
|
408
|
+
logTask=tailRemoteLogs(ctx.root,aborter.signal,{journal:resultJournal,offsets:logOffsets,activityTracker,timelineFilter,timelineWriter,verbose,displayMode});
|
|
370
409
|
process.stdout.write('[BOOT] Starting local + remote runtime...\n');
|
|
371
410
|
const preflight=invokeController(ctx,'status',{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
372
411
|
let startText=preflight.stdout||'';
|
|
@@ -415,6 +454,7 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
|
|
|
415
454
|
const wasAborted=aborter.signal.aborted;
|
|
416
455
|
aborter.abort();
|
|
417
456
|
if(logTask) await logTask.catch(()=>{});
|
|
457
|
+
timelineWriter.flush();
|
|
418
458
|
for(const line of activityTracker.reconcileInterrupted('session_end')) process.stdout.write(`${line}\n`);
|
|
419
459
|
process.stdout.write(`${activityTracker.summary()}\n`);
|
|
420
460
|
process.removeListener('SIGINT',abort);process.removeListener('SIGTERM',abort);
|
|
@@ -426,14 +466,29 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
|
|
|
426
466
|
}
|
|
427
467
|
}
|
|
428
468
|
async function runRemote(args){
|
|
429
|
-
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
469
|
+
let verbose=false,trace=false,filterExpression='',resumeCursor=null;
|
|
470
|
+
const positional=[];
|
|
471
|
+
for(let index=0;index<args.length;index++){
|
|
472
|
+
const arg=args[index];
|
|
473
|
+
if(arg==='--verbose'||arg==='-v'){verbose=true;continue;}
|
|
474
|
+
if(arg==='--trace'){trace=true;continue;}
|
|
475
|
+
if(arg==='--filter'){
|
|
476
|
+
if(filterExpression) throw new Error('remote --filter may be specified once');
|
|
477
|
+
if(index+1>=args.length) throw new Error('remote --filter requires an expression');
|
|
478
|
+
filterExpression=args[++index];continue;
|
|
479
|
+
}
|
|
480
|
+
if(arg==='--resume'){
|
|
481
|
+
if(resumeCursor!==null) throw new Error('remote --resume may be specified once');
|
|
482
|
+
if(index+1>=args.length) throw new Error('remote --resume requires a cursor');
|
|
483
|
+
resumeCursor=args[++index];continue;
|
|
484
|
+
}
|
|
485
|
+
positional.push(arg);
|
|
486
|
+
}
|
|
487
|
+
const presentation=verbose||trace||Boolean(filterExpression)||resumeCursor!==null;
|
|
488
|
+
if(positional.length===0){await runRemoteSession({resumeCursor,verbose,trace,filterExpression});return;}
|
|
489
|
+
if(presentation) throw new Error('remote presentation flags are valid only for foreground session');
|
|
490
|
+
if(positional.length>1) throw new Error('remote accepts one controller command');
|
|
491
|
+
const command=positional[0];if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
|
|
437
492
|
const ctx=await remoteContext();
|
|
438
493
|
await reportVersionCoherence(ctx.root,{displayMode:'ascii'});
|
|
439
494
|
const result=invokeController(ctx,command,{allowFailure:true});
|
package/src/machine-fs.mjs
CHANGED
|
@@ -338,7 +338,7 @@ async function atomicWrite(target, bytes, expectedSha256, authorityCheck = null)
|
|
|
338
338
|
assertExpected: checkExpectedFile, authorityCheck, tempTag: 'deadbyte-machine-fs' });
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
-
export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath, readWorkerPath = machineFsReadWorkerPath }) {
|
|
341
|
+
export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath, readWorkerPath = machineFsReadWorkerPath, listEntryLstat = lstat, listEntrySha256File = sha256File }) {
|
|
342
342
|
if (!policy) throw new Error('machine fs runtime requires coding policy');
|
|
343
343
|
if (!signingKeyPath || !verifyKeyPath) throw new Error('machine fs runtime requires trust key paths');
|
|
344
344
|
|
|
@@ -384,9 +384,22 @@ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath,
|
|
|
384
384
|
const rel = found.relative === '.' ? child.name : `${found.relative}/${child.name}`;
|
|
385
385
|
if (child.isSymbolicLink()) return { path: rel, kind: 'reparse', bytes: 0, mtime_utc: null, sha256: null };
|
|
386
386
|
const childPath = path.join(found.resolved, child.name);
|
|
387
|
-
|
|
387
|
+
let childInfo;
|
|
388
|
+
try { childInfo = await listEntryLstat(childPath); }
|
|
389
|
+
catch (error) {
|
|
390
|
+
const code = typeof error?.code === 'string' && /^[A-Z0-9_]+$/.test(error.code) ? error.code : 'UNKNOWN';
|
|
391
|
+
return { path: rel, kind: 'unreadable', bytes: 0, mtime_utc: null, sha256: null, error: { code, operation: 'lstat' } };
|
|
392
|
+
}
|
|
388
393
|
const kind = kindOf(childInfo);
|
|
389
|
-
|
|
394
|
+
let digest = null;
|
|
395
|
+
if (hashFiles && kind === 'file') {
|
|
396
|
+
try { digest = await listEntrySha256File(childPath); }
|
|
397
|
+
catch (error) {
|
|
398
|
+
const code = typeof error?.code === 'string' && /^[A-Z0-9_]+$/.test(error.code) ? error.code : 'UNKNOWN';
|
|
399
|
+
return { path: rel, kind, bytes: childInfo.size, mtime_utc: childInfo.mtime.toISOString(), sha256: null,
|
|
400
|
+
error: { code, operation: 'sha256' } };
|
|
401
|
+
}
|
|
402
|
+
}
|
|
390
403
|
return { path: rel, kind, bytes: kind === 'file' ? childInfo.size : 0, mtime_utc: childInfo.mtime.toISOString(), sha256: digest };
|
|
391
404
|
}));
|
|
392
405
|
const nextCursor = filtered.length > selected.length && selected.length
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as z from 'zod/v4';
|
|
2
2
|
import {
|
|
3
3
|
observationHardwareQueryOutputSchema,observationProcessListOutputSchema,observationSearchCancelOutputSchema,observationSearchPageOutputSchema,
|
|
4
|
-
observationSearchStartOutputSchema,observationSearchStatusOutputSchema,observationStructuredResult,observationSystemStatusOutputSchema
|
|
4
|
+
observationSearchStartOutputSchema,observationSearchStatusOutputSchema,observationStructuredResult,observationSystemStatusOutputSchema,observationWindowsQueryOutputSchema
|
|
5
5
|
} from './observation-output-contracts.mjs';
|
|
6
6
|
|
|
7
7
|
const hex64=z.string().regex(/^[0-9a-f]{64}$/);
|
|
@@ -45,4 +45,16 @@ export function registerObservationTools(server,{runtime}){
|
|
|
45
45
|
description:'Read one typed native hardware snapshot (CPU, GPU, memory, storage, network, or PCI) with signed observation evidence. No shell or Host authority is used.',
|
|
46
46
|
inputSchema:z.strictObject({query:z.enum(['cpu','gpu','memory','storage','network','pci'])}),outputSchema:observationHardwareQueryOutputSchema
|
|
47
47
|
},async args=>{try{return ok(observationHardwareQueryOutputSchema,await runtime.hardwareQuery({query:args.query}));}catch(error){return fail(observationHardwareQueryOutputSchema,error);}});
|
|
48
|
+
server.registerTool('machine_windows_query',{
|
|
49
|
+
description:'Read one typed native Windows snapshot for Registry, services, drivers, sockets, volumes, USB, or WSL. No shell parsing is used.',
|
|
50
|
+
inputSchema:z.discriminatedUnion('query',[
|
|
51
|
+
z.strictObject({query:z.literal('registry'),hive:z.enum(['HKLM','HKCU']),key:z.string().min(1).max(1024),value:z.string().max(260).nullable().default(null)}),
|
|
52
|
+
z.strictObject({query:z.literal('services')}),
|
|
53
|
+
z.strictObject({query:z.literal('drivers')}),
|
|
54
|
+
z.strictObject({query:z.literal('sockets')}),
|
|
55
|
+
z.strictObject({query:z.literal('volumes')}),
|
|
56
|
+
z.strictObject({query:z.literal('usb')}),
|
|
57
|
+
z.strictObject({query:z.literal('wsl')})
|
|
58
|
+
]),outputSchema:observationWindowsQueryOutputSchema
|
|
59
|
+
},async args=>{try{return ok(observationWindowsQueryOutputSchema,await runtime.windowsQuery(args));}catch(error){return fail(observationWindowsQueryOutputSchema,error);}});
|
|
48
60
|
}
|
|
@@ -55,6 +55,14 @@ export const observationHardwareQueryOutputSchema=statusContract({
|
|
|
55
55
|
...Object.fromEntries(Object.entries(evidenceShape).map(([k,v])=>[k,v.optional()]))
|
|
56
56
|
},['observed_at_utc','query','data','request_sha256','result_head_sha256','receipt_id','receipt_sha256']);
|
|
57
57
|
|
|
58
|
+
export const observationWindowsQueryOutputSchema=statusContract({
|
|
59
|
+
observed_at_utc:z.string().min(1).optional(),
|
|
60
|
+
query:z.enum(['registry','services','drivers','sockets','volumes','usb','wsl']).optional(),
|
|
61
|
+
data:z.record(z.string(),z.json()).optional(),
|
|
62
|
+
observer_sha256:hex64.optional(),
|
|
63
|
+
...Object.fromEntries(Object.entries(evidenceShape).map(([k,v])=>[k,v.optional()]))
|
|
64
|
+
},['observed_at_utc','query','data','request_sha256','result_head_sha256','receipt_id','receipt_sha256']);
|
|
65
|
+
|
|
58
66
|
export function observationStructuredResult(schema,value,isError=false){
|
|
59
67
|
return structuredToolResult(schema,value,isError);
|
|
60
68
|
}
|
|
@@ -15,6 +15,8 @@ const ZERO='0'.repeat(64);
|
|
|
15
15
|
const HEX64=/^[0-9a-f]{64}$/;
|
|
16
16
|
const ID=/^[A-Za-z0-9._-]{1,64}$/;
|
|
17
17
|
const HARDWARE_QUERIES=new Set(['cpu','gpu','memory','storage','network','pci']);
|
|
18
|
+
const WINDOWS_QUERIES=new Set(['registry','services','drivers','sockets','volumes','usb','wsl']);
|
|
19
|
+
const REGISTRY_HIVES=new Set(['HKLM','HKCU']);
|
|
18
20
|
const sha256Bytes=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
19
21
|
const sha256Object=value=>sha256Bytes(Buffer.from(canonicalJson(value),'utf8'));
|
|
20
22
|
const nowUtc=()=>new Date().toISOString();
|
|
@@ -131,12 +133,49 @@ async function defaultHardwareSnapshotProvider(query){
|
|
|
131
133
|
}
|
|
132
134
|
return {...parsed,observer_sha256:executableSha256};
|
|
133
135
|
}
|
|
136
|
+
function normalizeWindowsQueryArgs(input={}){
|
|
137
|
+
const query=String(input.query??'');
|
|
138
|
+
if(!WINDOWS_QUERIES.has(query)) throw new Error('windows query must be one of registry,services,drivers,sockets,volumes,usb,wsl');
|
|
139
|
+
if(query==='registry'){
|
|
140
|
+
const hive=String(input.hive??'');
|
|
141
|
+
const key=String(input.key??'');
|
|
142
|
+
const value=input.value===null||input.value===undefined?null:String(input.value);
|
|
143
|
+
if(!REGISTRY_HIVES.has(hive)) throw new Error('registry hive must be HKLM or HKCU');
|
|
144
|
+
if(key.length<1||key.length>1024||key.includes('\0')) throw new Error('registry key must contain 1-1024 non-NUL characters');
|
|
145
|
+
if(value!==null&&(value.length>260||value.includes('\0'))) throw new Error('registry value must be null or at most 260 non-NUL characters');
|
|
146
|
+
return {query,hive,key,value};
|
|
147
|
+
}
|
|
148
|
+
const extra=Object.keys(input).filter(k=>k!=='query'&&input[k]!==undefined&&input[k]!==null);
|
|
149
|
+
if(extra.length) throw new Error(`windows query '${query}' does not accept additional selectors`);
|
|
150
|
+
return {query};
|
|
151
|
+
}
|
|
152
|
+
async function defaultWindowsSnapshotProvider(input){
|
|
153
|
+
if(process.platform!=='win32') throw new Error('native Windows observation is supported on win32 only');
|
|
154
|
+
const args=normalizeWindowsQueryArgs(input);
|
|
155
|
+
const executable=fileURLToPath(new URL('../bin/deadbyte-hardware-observer-r45.exe',import.meta.url));
|
|
156
|
+
const executableSha256=sha256Bytes(await readFile(executable));
|
|
157
|
+
const argv=[args.query];
|
|
158
|
+
if(args.query==='registry'){
|
|
159
|
+
argv.push(args.hive,args.key);
|
|
160
|
+
if(args.value!==null) argv.push(args.value);
|
|
161
|
+
}
|
|
162
|
+
const result=spawnSync(executable,argv,{encoding:'utf8',windowsHide:true,shell:false,timeout:15000,maxBuffer:16*1024*1024});
|
|
163
|
+
if(result.error) throw result.error;
|
|
164
|
+
if(result.status!==0) throw new Error(`Windows observation failed query=${args.query}: ${String(result.stderr??result.stdout??'').trim()}`);
|
|
165
|
+
let parsed;
|
|
166
|
+
try{parsed=JSON.parse(String(result.stdout??'').trim());}
|
|
167
|
+
catch{throw new Error(`Windows observation returned invalid JSON query=${args.query}`);}
|
|
168
|
+
if(parsed?.schema!=='deadbyte.windows-query.v1'||parsed?.query!==args.query||!parsed.data||typeof parsed.data!=='object'||Array.isArray(parsed.data)){
|
|
169
|
+
throw new Error(`Windows observation contract mismatch query=${args.query}`);
|
|
170
|
+
}
|
|
171
|
+
return {...parsed,observer_sha256:executableSha256};
|
|
172
|
+
}
|
|
134
173
|
function requestSha(policy,operation,args){
|
|
135
174
|
return sha256Object({schema:'deadbyte.observation-request.v1',policy_sha256:policy.policy_sha256,operation,args});
|
|
136
175
|
}
|
|
137
176
|
export function createObservationRuntime({policy,signingKeyPath,verifyKeyPath,processRuntime=null,stateRoot=path.join(os.homedir(),'.deadbyte-mcp'),
|
|
138
177
|
processSnapshotProvider=defaultProcessSnapshotProvider,systemSnapshotProvider=defaultSystemSnapshotProvider,
|
|
139
|
-
ownedRuntimePidProvider=defaultOwnedRuntimePidProvider,hardwareSnapshotProvider=defaultHardwareSnapshotProvider,
|
|
178
|
+
ownedRuntimePidProvider=defaultOwnedRuntimePidProvider,hardwareSnapshotProvider=defaultHardwareSnapshotProvider,windowsSnapshotProvider=defaultWindowsSnapshotProvider,
|
|
140
179
|
searchSessionTtlMs=5*60*1000,maxSearchSessions=128,clockMs=()=>Date.now()}={}){
|
|
141
180
|
if(!policy||!signingKeyPath||!verifyKeyPath) throw new Error('observation runtime requires policy and trust keys');
|
|
142
181
|
if(!Number.isInteger(searchSessionTtlMs)||searchSessionTtlMs<10||searchSessionTtlMs>24*60*60*1000) throw new Error('searchSessionTtlMs out of range');
|
|
@@ -337,5 +376,18 @@ export function createObservationRuntime({policy,signingKeyPath,verifyKeyPath,pr
|
|
|
337
376
|
const receipt=await writeReceipt(runtime,'machine_hardware_query',req,head,{observed_at_utc:observedAt,query,observer_sha256:result.observer_sha256??null});
|
|
338
377
|
return {...result,request_sha256:req,result_head_sha256:head,...receipt};
|
|
339
378
|
}
|
|
340
|
-
|
|
379
|
+
async function windowsQuery(input={}){
|
|
380
|
+
const args=normalizeWindowsQueryArgs(input);
|
|
381
|
+
const observedAt=nowUtc();
|
|
382
|
+
const snapshot=await windowsSnapshotProvider(args);
|
|
383
|
+
if(!snapshot||snapshot.schema!=='deadbyte.windows-query.v1'||snapshot.query!==args.query||!snapshot.data||typeof snapshot.data!=='object'||Array.isArray(snapshot.data)){
|
|
384
|
+
throw new Error('windows query provider contract mismatch');
|
|
385
|
+
}
|
|
386
|
+
const result={status:'ok',observed_at_utc:observedAt,query:args.query,data:snapshot.data};
|
|
387
|
+
if(typeof snapshot.observer_sha256==='string'&&HEX64.test(snapshot.observer_sha256)) result.observer_sha256=snapshot.observer_sha256;
|
|
388
|
+
const req=requestSha(policy,'machine_windows_query',args); const head=sha256Object(result);
|
|
389
|
+
const receipt=await writeReceipt(runtime,'machine_windows_query',req,head,{observed_at_utc:observedAt,query:args.query,observer_sha256:result.observer_sha256??null});
|
|
390
|
+
return {...result,request_sha256:req,result_head_sha256:head,...receipt};
|
|
391
|
+
}
|
|
392
|
+
return Object.freeze({policy,searchStart,searchPage,searchStatus,searchCancel,processList,systemStatus,hardwareQuery,windowsQuery});
|
|
341
393
|
}
|