deadbyte-mcp 0.15.20 → 0.15.22
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 +34 -25
- package/PROVENANCE.json +10 -10
- package/README.txt +7 -7
- package/RELEASE.json +47 -16
- package/SBOM.cdx.json +33 -7
- package/TEST-MATRIX.json +85 -15
- 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 +94 -93
- package/scripts/build-hardware-r45.ps1 +19 -0
- package/scripts/full-regression-r48-batch.mjs +27 -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/src/capability-ledger.mjs +7 -0
- package/src/capability-resolver.mjs +70 -14
- package/src/compact-gateway-mcp-tools.mjs +16 -2
- package/src/machine-fs.mjs +16 -3
- package/src/mcp-server.mjs +2 -1
- 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/version.mjs +1 -1
- package/test/capability-resolver.test.mjs +68 -1
- package/test/compact-max-catalog-r45.test.mjs +25 -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 +20 -14
- package/test/windows-observation-r48.test.mjs +63 -0
- package/test/windows-observation-surface-r48.test.mjs +70 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#requires -Version 5.1
|
|
2
|
+
[CmdletBinding()]
|
|
3
|
+
param()
|
|
4
|
+
Set-StrictMode -Version 3.0
|
|
5
|
+
$ErrorActionPreference = 'Stop'
|
|
6
|
+
|
|
7
|
+
function Fail([string]$Message) { throw "LOCAL_D_PROMOTION: $Message" }
|
|
8
|
+
function Sha256-File([string]$Path) {
|
|
9
|
+
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
10
|
+
}
|
|
11
|
+
function Sha256-Text([string]$Text) {
|
|
12
|
+
$sha = [System.Security.Cryptography.SHA256]::Create()
|
|
13
|
+
try {
|
|
14
|
+
$bytes = [System.Text.Encoding]::UTF8.GetBytes($Text)
|
|
15
|
+
return -join ($sha.ComputeHash($bytes) | ForEach-Object { $_.ToString('x2') })
|
|
16
|
+
} finally { $sha.Dispose() }
|
|
17
|
+
}
|
|
18
|
+
function Write-Utf8NoBom([string]$Path,[string]$Text) {
|
|
19
|
+
[System.IO.File]::WriteAllText($Path,$Text,(New-Object System.Text.UTF8Encoding($false)))
|
|
20
|
+
}
|
|
21
|
+
function Read-Raw([string]$Path) {
|
|
22
|
+
return [System.IO.File]::ReadAllText($Path,[System.Text.Encoding]::UTF8)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
$stateRoot = Join-Path $env:USERPROFILE '.deadbyte-mcp'
|
|
26
|
+
$pointerPath = Join-Path $stateRoot 'active-release.json'
|
|
27
|
+
$launcher = Join-Path $stateRoot 'bin\DEADBYTE-MCP.ps1'
|
|
28
|
+
$runtimeRoot = Join-Path $stateRoot 'runtime'
|
|
29
|
+
if (-not (Test-Path -LiteralPath $pointerPath -PathType Leaf)) { Fail 'active pointer missing' }
|
|
30
|
+
if (-not (Test-Path -LiteralPath $launcher -PathType Leaf)) { Fail 'stable launcher missing' }
|
|
31
|
+
|
|
32
|
+
$pointerText = Read-Raw $pointerPath
|
|
33
|
+
$pointer = $pointerText | ConvertFrom-Json
|
|
34
|
+
if ([string]$pointer.schema -ne 'deadbyte.active-release.v1') { Fail 'active pointer schema mismatch' }
|
|
35
|
+
$oldGeneration = [System.IO.Path]::GetFullPath([string]$pointer.generation_root)
|
|
36
|
+
$oldManifest = Join-Path $oldGeneration 'GENERATION.SHA256'
|
|
37
|
+
if (-not (Test-Path -LiteralPath $oldManifest -PathType Leaf)) { Fail 'old generation manifest missing' }
|
|
38
|
+
$oldManifestHash = Sha256-File $oldManifest
|
|
39
|
+
if ($oldManifestHash -ne [string]$pointer.generation_manifest_sha256) { Fail 'old generation manifest hash mismatch' }
|
|
40
|
+
|
|
41
|
+
$manifestLines = @(Get-Content -LiteralPath $oldManifest -Encoding UTF8)
|
|
42
|
+
$entries = @()
|
|
43
|
+
foreach ($line in $manifestLines) {
|
|
44
|
+
if ([string]::IsNullOrWhiteSpace([string]$line)) { continue }
|
|
45
|
+
if ([string]$line -notmatch '^([0-9a-f]{64}) ([A-Za-z0-9._-]+)$') { Fail "malformed old generation manifest line: $line" }
|
|
46
|
+
$expected = [string]$Matches[1]
|
|
47
|
+
$name = [string]$Matches[2]
|
|
48
|
+
$path = Join-Path $oldGeneration $name
|
|
49
|
+
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { Fail "old generation file missing: $name" }
|
|
50
|
+
$actual = Sha256-File $path
|
|
51
|
+
if ($actual -ne $expected) { Fail "old generation hash mismatch: $name" }
|
|
52
|
+
$entries += [pscustomobject]@{ Name=$name; Expected=$expected }
|
|
53
|
+
}
|
|
54
|
+
$names = @($entries | ForEach-Object { $_.Name })
|
|
55
|
+
foreach ($required in @('coding-policy.json','autonomous-policy.json','controller-config.psd1')) {
|
|
56
|
+
if ($required -notin $names) { Fail "required generation file not manifested: $required" }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
$oldCodingPath = Join-Path $oldGeneration 'coding-policy.json'
|
|
60
|
+
$oldAutonomousPath = Join-Path $oldGeneration 'autonomous-policy.json'
|
|
61
|
+
$oldCodingHash = Sha256-File $oldCodingPath
|
|
62
|
+
$oldAutonomousHash = Sha256-File $oldAutonomousPath
|
|
63
|
+
|
|
64
|
+
$coding = (Read-Raw $oldCodingPath) | ConvertFrom-Json
|
|
65
|
+
if ([string]$coding.schema -ne 'deadbyte.coding-policy.v1' -or $coding.enabled -ne $true) { Fail 'coding policy schema/enabled mismatch' }
|
|
66
|
+
if ($null -eq $coding.roots) { Fail 'coding roots missing' }
|
|
67
|
+
|
|
68
|
+
$localRoot = [pscustomobject][ordered]@{
|
|
69
|
+
path = 'D:\'
|
|
70
|
+
read = $true
|
|
71
|
+
write = $true
|
|
72
|
+
exec = $true
|
|
73
|
+
deny_paths = @()
|
|
74
|
+
}
|
|
75
|
+
if ($coding.roots.PSObject.Properties.Name -contains 'local_d') {
|
|
76
|
+
$coding.roots.local_d = $localRoot
|
|
77
|
+
} else {
|
|
78
|
+
$coding.roots | Add-Member -NotePropertyName 'local_d' -NotePropertyValue $localRoot
|
|
79
|
+
}
|
|
80
|
+
foreach ($commandProp in @($coding.commands.PSObject.Properties)) {
|
|
81
|
+
$command = $commandProp.Value
|
|
82
|
+
if ($command.PSObject.Properties.Name -contains 'root_ids') {
|
|
83
|
+
$ids = @($command.root_ids)
|
|
84
|
+
if ('local_d' -notin $ids) { $command.root_ids = @($ids + 'local_d') }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
$codingText = ($coding | ConvertTo-Json -Depth 32) + [Environment]::NewLine
|
|
88
|
+
$newCodingHash = Sha256-Text $codingText
|
|
89
|
+
|
|
90
|
+
$autonomous = (Read-Raw $oldAutonomousPath) | ConvertFrom-Json
|
|
91
|
+
if ([string]$autonomous.schema -ne 'deadbyte.autonomous-policy.v1' -or $autonomous.enabled -ne $true) { Fail 'autonomous policy schema/enabled mismatch' }
|
|
92
|
+
$autonomous.coding_policy_sha256 = $newCodingHash
|
|
93
|
+
$autonomousText = ($autonomous | ConvertTo-Json -Depth 32) + [Environment]::NewLine
|
|
94
|
+
$newAutonomousHash = Sha256-Text $autonomousText
|
|
95
|
+
|
|
96
|
+
$seed = $oldManifestHash + [Environment]::NewLine + $newCodingHash + [Environment]::NewLine + $newAutonomousHash + [Environment]::NewLine + 'local_d=D:\'
|
|
97
|
+
$generationSuffix = (Sha256-Text $seed).Substring(0,16)
|
|
98
|
+
$newGenerationId = ([string]$pointer.release_id) + '-g' + $generationSuffix
|
|
99
|
+
$generationStore = Join-Path $stateRoot 'generations'
|
|
100
|
+
$newGeneration = Join-Path $generationStore $newGenerationId
|
|
101
|
+
if (Test-Path -LiteralPath $newGeneration) { Fail "target generation already exists: $newGeneration" }
|
|
102
|
+
New-Item -ItemType Directory -Path $newGeneration | Out-Null
|
|
103
|
+
|
|
104
|
+
$pointerBackup = Join-Path $runtimeRoot 'active-release.before-local-d.json'
|
|
105
|
+
$codingGrantPath = Join-Path $runtimeRoot 'coding-grant.json'
|
|
106
|
+
$autonomousGrantPath = Join-Path $runtimeRoot 'autonomous-grant.json'
|
|
107
|
+
$hadCodingGrant = Test-Path -LiteralPath $codingGrantPath -PathType Leaf
|
|
108
|
+
$hadAutonomousGrant = Test-Path -LiteralPath $autonomousGrantPath -PathType Leaf
|
|
109
|
+
$oldCodingGrantText = if ($hadCodingGrant) { Read-Raw $codingGrantPath } else { $null }
|
|
110
|
+
$oldAutonomousGrantText = if ($hadAutonomousGrant) { Read-Raw $autonomousGrantPath } else { $null }
|
|
111
|
+
$wasAutonomousArmed = $false
|
|
112
|
+
if ($hadCodingGrant -and $hadAutonomousGrant) {
|
|
113
|
+
try {
|
|
114
|
+
$cg = $oldCodingGrantText | ConvertFrom-Json
|
|
115
|
+
$ag = $oldAutonomousGrantText | ConvertFrom-Json
|
|
116
|
+
$wasAutonomousArmed =
|
|
117
|
+
([string]$cg.policy_sha256 -eq $oldCodingHash) -and
|
|
118
|
+
([string]$ag.policy_sha256 -eq $oldAutonomousHash) -and
|
|
119
|
+
([string]$ag.coding_policy_sha256 -eq $oldCodingHash)
|
|
120
|
+
} catch { $wasAutonomousArmed = $false }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
$switched = $false
|
|
124
|
+
try {
|
|
125
|
+
foreach ($entry in $entries) {
|
|
126
|
+
$src = Join-Path $oldGeneration $entry.Name
|
|
127
|
+
$dst = Join-Path $newGeneration $entry.Name
|
|
128
|
+
Copy-Item -LiteralPath $src -Destination $dst
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
Write-Utf8NoBom (Join-Path $newGeneration 'coding-policy.json') $codingText
|
|
132
|
+
Write-Utf8NoBom (Join-Path $newGeneration 'autonomous-policy.json') $autonomousText
|
|
133
|
+
|
|
134
|
+
$configPath = Join-Path $newGeneration 'controller-config.psd1'
|
|
135
|
+
$configText = Read-Raw $configPath
|
|
136
|
+
if ($configText.IndexOf($oldGeneration,[System.StringComparison]::OrdinalIgnoreCase) -lt 0) { Fail 'controller config is not bound to old generation root' }
|
|
137
|
+
$configText = $configText.Replace($oldGeneration,$newGeneration)
|
|
138
|
+
Write-Utf8NoBom $configPath $configText
|
|
139
|
+
$cfg = Import-PowerShellDataFile -LiteralPath $configPath
|
|
140
|
+
if ([System.IO.Path]::GetFullPath([string]$cfg.CodingPolicyFile) -ne [System.IO.Path]::GetFullPath((Join-Path $newGeneration 'coding-policy.json'))) { Fail 'new controller CodingPolicyFile binding mismatch' }
|
|
141
|
+
if ([System.IO.Path]::GetFullPath([string]$cfg.AutonomousPolicyFile) -ne [System.IO.Path]::GetFullPath((Join-Path $newGeneration 'autonomous-policy.json'))) { Fail 'new controller AutonomousPolicyFile binding mismatch' }
|
|
142
|
+
|
|
143
|
+
foreach ($entry in $entries) {
|
|
144
|
+
$p = Join-Path $newGeneration $entry.Name
|
|
145
|
+
$text = $null
|
|
146
|
+
try { $text = Read-Raw $p } catch { $text = $null }
|
|
147
|
+
if ($null -ne $text -and $entry.Name -ne 'controller-config.psd1' -and $text.IndexOf($oldGeneration,[System.StringComparison]::OrdinalIgnoreCase) -ge 0) {
|
|
148
|
+
Fail "unexpected old generation binding remains in $($entry.Name)"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
$newManifestLines = @()
|
|
153
|
+
foreach ($entry in $entries) {
|
|
154
|
+
$p = Join-Path $newGeneration $entry.Name
|
|
155
|
+
$newManifestLines += ((Sha256-File $p) + ' ' + $entry.Name)
|
|
156
|
+
}
|
|
157
|
+
$newManifestText = ($newManifestLines -join [Environment]::NewLine) + [Environment]::NewLine
|
|
158
|
+
$newManifestPath = Join-Path $newGeneration 'GENERATION.SHA256'
|
|
159
|
+
Write-Utf8NoBom $newManifestPath $newManifestText
|
|
160
|
+
$newManifestHash = Sha256-File $newManifestPath
|
|
161
|
+
|
|
162
|
+
foreach ($line in $newManifestLines) {
|
|
163
|
+
if ($line -notmatch '^([0-9a-f]{64}) ([A-Za-z0-9._-]+)$') { Fail 'new manifest internal format error' }
|
|
164
|
+
$actual = Sha256-File (Join-Path $newGeneration ([string]$Matches[2]))
|
|
165
|
+
if ($actual -ne [string]$Matches[1]) { Fail "new generation verification failed: $($Matches[2])" }
|
|
166
|
+
}
|
|
167
|
+
if ((Sha256-File (Join-Path $newGeneration 'coding-policy.json')) -ne $newCodingHash) { Fail 'new coding hash self-check failed' }
|
|
168
|
+
if ((Sha256-File (Join-Path $newGeneration 'autonomous-policy.json')) -ne $newAutonomousHash) { Fail 'new autonomous hash self-check failed' }
|
|
169
|
+
|
|
170
|
+
Write-Utf8NoBom $pointerBackup $pointerText
|
|
171
|
+
$pointer.generation_root = $newGeneration
|
|
172
|
+
$pointer.config_path = Join-Path $newGeneration 'controller-config.psd1'
|
|
173
|
+
$pointer.generation_manifest_sha256 = $newManifestHash
|
|
174
|
+
if ($pointer.PSObject.Properties.Name -contains 'generation_id') { $pointer.generation_id = $newGenerationId }
|
|
175
|
+
if ($pointer.PSObject.Properties.Name -contains 'generation_manifest_path') { $pointer.generation_manifest_path = $newManifestPath }
|
|
176
|
+
if ($pointer.PSObject.Properties.Name -contains 'updated_at_utc') { $pointer.updated_at_utc = [DateTime]::UtcNow.ToString('o') }
|
|
177
|
+
$newPointerText = ($pointer | ConvertTo-Json -Depth 32) + [Environment]::NewLine
|
|
178
|
+
$pointerTmp = $pointerPath + '.local-d.tmp'
|
|
179
|
+
Write-Utf8NoBom $pointerTmp $newPointerText
|
|
180
|
+
Move-Item -LiteralPath $pointerTmp -Destination $pointerPath -Force
|
|
181
|
+
$switched = $true
|
|
182
|
+
|
|
183
|
+
$statusText = (& $launcher status 2>&1 | Out-String)
|
|
184
|
+
if ($LASTEXITCODE -ne 0) { Fail "launcher status failed after pointer switch: $statusText" }
|
|
185
|
+
|
|
186
|
+
$armCodingText = (& $launcher armcoding 2>&1 | Out-String)
|
|
187
|
+
if ($LASTEXITCODE -ne 0) { Fail "armcoding failed after pointer switch: $armCodingText" }
|
|
188
|
+
|
|
189
|
+
$armAutonomousText = ''
|
|
190
|
+
if ($wasAutonomousArmed) {
|
|
191
|
+
$armAutonomousText = (& $launcher armautonomous 2>&1 | Out-String)
|
|
192
|
+
if ($LASTEXITCODE -ne 0) { Fail "armautonomous failed after pointer switch: $armAutonomousText" }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
Write-Output 'LOCAL_D_PROMOTION=PASS'
|
|
196
|
+
Write-Output ('OLD_GENERATION=' + $oldGeneration)
|
|
197
|
+
Write-Output ('NEW_GENERATION=' + $newGeneration)
|
|
198
|
+
Write-Output ('OLD_GENERATION_MANIFEST_SHA256=' + $oldManifestHash)
|
|
199
|
+
Write-Output ('NEW_GENERATION_MANIFEST_SHA256=' + $newManifestHash)
|
|
200
|
+
Write-Output ('OLD_CODING_POLICY_SHA256=' + $oldCodingHash)
|
|
201
|
+
Write-Output ('NEW_CODING_POLICY_SHA256=' + $newCodingHash)
|
|
202
|
+
Write-Output ('NEW_AUTONOMOUS_POLICY_SHA256=' + $newAutonomousHash)
|
|
203
|
+
Write-Output ('AUTONOMOUS_REARMED=' + $wasAutonomousArmed)
|
|
204
|
+
Write-Output ('ROLLBACK_POINTER=' + $pointerBackup)
|
|
205
|
+
Write-Output 'RESTART_REQUIRED=YES'
|
|
206
|
+
} catch {
|
|
207
|
+
$message = $_.Exception.Message
|
|
208
|
+
if ($switched) {
|
|
209
|
+
try { Write-Utf8NoBom $pointerPath $pointerText } catch {}
|
|
210
|
+
if ($hadCodingGrant) { try { Write-Utf8NoBom $codingGrantPath $oldCodingGrantText } catch {} }
|
|
211
|
+
else { try { Remove-Item -LiteralPath $codingGrantPath -Force -ErrorAction SilentlyContinue } catch {} }
|
|
212
|
+
if ($hadAutonomousGrant) { try { Write-Utf8NoBom $autonomousGrantPath $oldAutonomousGrantText } catch {} }
|
|
213
|
+
else { try { Remove-Item -LiteralPath $autonomousGrantPath -Force -ErrorAction SilentlyContinue } catch {} }
|
|
214
|
+
}
|
|
215
|
+
try { if (Test-Path -LiteralPath $newGeneration) { Remove-Item -LiteralPath $newGeneration -Recurse -Force } } catch {}
|
|
216
|
+
Fail $message
|
|
217
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
$ErrorActionPreference='Stop'
|
|
2
|
+
Set-StrictMode -Version Latest
|
|
3
|
+
$Root=Split-Path -Parent $PSScriptRoot
|
|
4
|
+
Push-Location $Root
|
|
5
|
+
try {
|
|
6
|
+
& node --test --test-concurrency=1 test/r48-release-contract.test.mjs test/release-version.test.mjs test/cli-entrypoint.test.mjs test/package-boundary.test.mjs test/release-smoke-isolation.test.mjs test/full-max-catalog-r48.test.mjs
|
|
7
|
+
if($LASTEXITCODE -ne 0){throw "R48 post-bump release checks failed exit=$LASTEXITCODE"}
|
|
8
|
+
} finally { Pop-Location }
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
$ErrorActionPreference='Stop'
|
|
2
|
+
Set-StrictMode -Version Latest
|
|
3
|
+
$Root=Split-Path -Parent $PSScriptRoot
|
|
4
|
+
Push-Location $Root
|
|
5
|
+
try {
|
|
6
|
+
& node --test test/machine-fs-runtime.test.mjs test/windows-observation-r48.test.mjs test/windows-observation-surface-r48.test.mjs test/hardware-observation-r45.test.mjs test/observation-mcp-tools.test.mjs test/compact-gateway-r36.test.mjs test/capability-resolver.test.mjs test/compact-catalog-r36.test.mjs test/compact-max-catalog-r45.test.mjs test/full-max-catalog-r45.test.mjs test/full-max-catalog-r46.test.mjs test/full-max-catalog-r47.test.mjs test/full-max-catalog-r48.test.mjs
|
|
7
|
+
if($LASTEXITCODE -ne 0){throw "R48 focused regression failed exit=$LASTEXITCODE"}
|
|
8
|
+
} finally { Pop-Location }
|
|
@@ -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'}),
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { processSessionStatus } from './process-policy.mjs';
|
|
2
|
+
import { assertHostArmed } from './host-plane.mjs';
|
|
3
|
+
|
|
4
|
+
const CODING_ARMED_READ_CAPABILITIES = new Set([
|
|
5
|
+
'coding.tree','coding.stat','coding.search','coding.files_read','coding.file_read',
|
|
6
|
+
'semantic.outline','semantic.definition','semantic.references','semantic.diagnostics','semantic.implementations','semantic.rename_preview'
|
|
7
|
+
]);
|
|
8
|
+
|
|
1
9
|
export const CAPABILITY_STATES = Object.freeze({
|
|
2
10
|
AVAILABLE: 'AVAILABLE',
|
|
3
11
|
DEGRADED: 'DEGRADED',
|
|
@@ -59,6 +67,13 @@ const CAPABILITY_OPERATIONS = Object.freeze({
|
|
|
59
67
|
'observation.process_list': ['process_list'],
|
|
60
68
|
'observation.system_status': ['system_status'],
|
|
61
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'],
|
|
62
77
|
'desktop.status': ['status'],
|
|
63
78
|
'desktop.observe': ['observe'],
|
|
64
79
|
'desktop.target': ['target'],
|
|
@@ -88,12 +103,11 @@ export function deriveCapabilityRequirements(capability) {
|
|
|
88
103
|
reqs.push('skill_plane.valid');
|
|
89
104
|
return reqs;
|
|
90
105
|
}
|
|
106
|
+
if (CODING_ARMED_READ_CAPABILITIES.has(capability.id) && capability.authority !== 'coding') reqs.push('coding_session.armed');
|
|
91
107
|
if (capability.authority === 'coding') {
|
|
92
|
-
if (capability.mutating)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
reqs.push('coding_runtime.ready');
|
|
96
|
-
}
|
|
108
|
+
if (capability.mutating || CODING_ARMED_READ_CAPABILITIES.has(capability.id)) reqs.push('coding_session.armed');
|
|
109
|
+
else reqs.push('coding_runtime.ready');
|
|
110
|
+
if (capability.mutating) reqs.push('policy.write');
|
|
97
111
|
} else if (capability.authority === 'process') {
|
|
98
112
|
if (capability.mutating) {
|
|
99
113
|
reqs.push('process_session.armed');
|
|
@@ -113,11 +127,7 @@ export function deriveCapabilityRequirements(capability) {
|
|
|
113
127
|
reqs.push('desktop_runtime.ready');
|
|
114
128
|
}
|
|
115
129
|
} else if (capability.authority === 'host' || capability.host_only) {
|
|
116
|
-
|
|
117
|
-
reqs.push('host_policy.armed');
|
|
118
|
-
} else {
|
|
119
|
-
reqs.push('host_policy.configured');
|
|
120
|
-
}
|
|
130
|
+
reqs.push('host_policy.armed');
|
|
121
131
|
}
|
|
122
132
|
return reqs;
|
|
123
133
|
}
|
|
@@ -148,11 +158,33 @@ export async function resolveCapabilityStates({
|
|
|
148
158
|
try { codingStatus = await codingRuntime.sessionStatus(); } catch {}
|
|
149
159
|
}
|
|
150
160
|
|
|
161
|
+
let processStatus = null;
|
|
162
|
+
if (processRuntime?.sessionStatus) {
|
|
163
|
+
try { processStatus = await processRuntime.sessionStatus(); } catch {}
|
|
164
|
+
} else if (processRuntime?.policy) {
|
|
165
|
+
try { processStatus = await processSessionStatus(processRuntime.policy); } catch {}
|
|
166
|
+
}
|
|
167
|
+
|
|
151
168
|
let autonomousStatus = null;
|
|
152
169
|
if (autonomousRuntime?.sessionStatus) {
|
|
153
170
|
try { autonomousStatus = await autonomousRuntime.sessionStatus(); } catch {}
|
|
154
171
|
}
|
|
155
172
|
|
|
173
|
+
let desktopStatus = null;
|
|
174
|
+
if (desktopRuntime?.status) {
|
|
175
|
+
try { desktopStatus = await desktopRuntime.status(); } catch {}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let hostStatus = null;
|
|
179
|
+
if (hostPolicy) {
|
|
180
|
+
try {
|
|
181
|
+
const armed = await assertHostArmed(hostPolicy);
|
|
182
|
+
hostStatus = { configured:true, armed:true, reason:armed.standing ? 'standing-grant' : 'armed' };
|
|
183
|
+
} catch (error) {
|
|
184
|
+
hostStatus = { configured:true, armed:false, reason:String(error?.message ?? error) };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
156
188
|
let skillListResult = null;
|
|
157
189
|
if (skillRuntime?.list) {
|
|
158
190
|
try { skillListResult = await skillRuntime.list(); } catch (err) {
|
|
@@ -225,20 +257,44 @@ export async function resolveCapabilityStates({
|
|
|
225
257
|
};
|
|
226
258
|
}
|
|
227
259
|
// 4. Session Authority Checks
|
|
228
|
-
else if (
|
|
260
|
+
else if (requires.includes('coding_session.armed') && !codingStatus?.armed) {
|
|
229
261
|
state = CAPABILITY_STATES.DENIED;
|
|
230
262
|
ready = false;
|
|
231
263
|
diagnostic = {
|
|
232
264
|
code: 'CODING_SESSION_DISARMED',
|
|
233
|
-
reason: codingStatus
|
|
265
|
+
reason: codingStatus?.reason || 'Coding session is not armed for this capability',
|
|
234
266
|
recoverable: true
|
|
235
267
|
};
|
|
236
|
-
} else if (
|
|
268
|
+
} else if (requires.includes('process_session.armed') && !processStatus?.armed) {
|
|
269
|
+
state = CAPABILITY_STATES.DENIED;
|
|
270
|
+
ready = false;
|
|
271
|
+
diagnostic = {
|
|
272
|
+
code: 'PROCESS_SESSION_DISARMED',
|
|
273
|
+
reason: processStatus?.reason || 'Process session is not armed for mutation',
|
|
274
|
+
recoverable: true
|
|
275
|
+
};
|
|
276
|
+
} else if (requires.includes('autonomous_session.armed') && !autonomousStatus?.armed) {
|
|
237
277
|
state = CAPABILITY_STATES.DENIED;
|
|
238
278
|
ready = false;
|
|
239
279
|
diagnostic = {
|
|
240
280
|
code: 'AUTONOMOUS_SESSION_DISARMED',
|
|
241
|
-
reason: autonomousStatus
|
|
281
|
+
reason: autonomousStatus?.reason || 'Autonomous session is not armed for mutation',
|
|
282
|
+
recoverable: true
|
|
283
|
+
};
|
|
284
|
+
} else if (requires.includes('desktop_control.armed') && !desktopStatus?.armed) {
|
|
285
|
+
state = CAPABILITY_STATES.DENIED;
|
|
286
|
+
ready = false;
|
|
287
|
+
diagnostic = {
|
|
288
|
+
code: 'DESKTOP_SESSION_DISARMED',
|
|
289
|
+
reason: desktopStatus?.reason || 'Desktop control is not armed for mutation',
|
|
290
|
+
recoverable: true
|
|
291
|
+
};
|
|
292
|
+
} else if (requires.includes('host_policy.armed') && !hostStatus?.armed) {
|
|
293
|
+
state = CAPABILITY_STATES.DENIED;
|
|
294
|
+
ready = false;
|
|
295
|
+
diagnostic = {
|
|
296
|
+
code: 'HOST_POLICY_DISARMED',
|
|
297
|
+
reason: hostStatus?.reason || 'Privileged host policy is disarmed',
|
|
242
298
|
recoverable: true
|
|
243
299
|
};
|
|
244
300
|
}
|
|
@@ -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/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
|
package/src/mcp-server.mjs
CHANGED
|
@@ -479,7 +479,8 @@ serveStdio(() => {
|
|
|
479
479
|
|
|
480
480
|
registerCompactGatewayTools(toolServer,{
|
|
481
481
|
profile:surfaceProfile,ledger:R36_CAPABILITY_LEDGER,codingRuntime,semanticRuntime,machineFsRuntime,
|
|
482
|
-
processRuntime,observationRuntime,machineRuntime,desktopRuntime,
|
|
482
|
+
processRuntime,observationRuntime,machineRuntime,desktopRuntime,skillRuntime,autonomousRuntime,hostPolicy,
|
|
483
|
+
catalogProvider:()=>[...registeredToolNames]
|
|
483
484
|
});
|
|
484
485
|
|
|
485
486
|
if (hostPolicy) {
|
|
@@ -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
|
}
|
package/src/version.mjs
CHANGED