deadbyte-mcp 0.15.43 → 0.15.44
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 +128 -67
- package/PROVENANCE.json +23 -12
- package/README.txt +18 -18
- package/RELEASE.json +28 -14
- package/SBOM.cdx.json +14 -2
- package/TEST-MATRIX.json +41 -10
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-desktop-input-r40.obj +0 -0
- package/bin/deadbyte-desktop-observer-r40.obj +0 -0
- package/bin/deadbyte-desktop-uia-r40.obj +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/bin/deadbyte-native-inspect-r67.exe +0 -0
- package/bin/deadbyte-native-inspect-r67.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/bridge/package.json +1 -1
- package/controller/deadbyte-desktop-policy.json +5 -5
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R67-ALPHA-REFERENCE-DIFFERENTIAL.md +42 -0
- package/docs/R67-ALPHA-STATIC-COMPARISON.json +205 -0
- package/docs/R67-CANDIDATE-PARENT.json +10 -0
- package/docs/R67-EVIDENCE-RECONCILE-CLOSEOUT-20261011.md +64 -0
- package/docs/R67-FULL-REGRESSION-UNKNOWN.json +27 -0
- package/docs/R67-NATIVE-INSPECT-BINARY.json +11 -0
- package/docs/R67-NPM-EXDEV-PRESERVATION-20261011.md +39 -0
- package/docs/R67-OBSERVATION-TRUTH.md +82 -0
- package/docs/R67-REFERENCE-GAP-MATRIX.json +57 -0
- package/docs/R67-REGRESSION-CLOSURE-20261011.md +24 -0
- package/native/deadbyte-native-inspect-r67.cpp +252 -0
- package/package.json +6 -3
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/build-native-inspector-r67.ps1 +28 -0
- package/scripts/gate-windows-r67.ps1 +56 -0
- package/scripts/npm-preserve-artifact-r67.mjs +99 -0
- package/scripts/npm-release-r48.mjs +10 -2
- package/scripts/npm-release-readiness-r67.mjs +26 -0
- package/scripts/r67-acquire-alpha-reference.mjs +95 -0
- package/scripts/r67-audit-nonpass-source.mjs +39 -0
- package/scripts/r67-compare-desktop-alpha.mjs +48 -0
- package/scripts/r67-diagnose-failed-shard.mjs +23 -0
- package/scripts/r67-durable-batch.mjs +42 -0
- package/scripts/r67-focused-gate.mjs +58 -0
- package/scripts/r67-full-regression.mjs +28 -0
- package/scripts/r67-independent-observation-verify.mjs +36 -0
- package/scripts/r67-independent-runtime-observation-verify.mjs +63 -0
- package/scripts/r67-nonpass-adjudicate.mjs +20 -0
- package/scripts/r67-publish-boundary-audit.mjs +32 -0
- package/scripts/r67-regression-reconcile.mjs +10 -0
- package/scripts/r67-regression-shard-run.mjs +70 -0
- package/scripts/r67-run-shard-batch.mjs +18 -0
- package/scripts/r67-stage-candidate.mjs +30 -0
- package/scripts/r67-verify-parent-ping.mjs +22 -0
- package/scripts/release-manifest.mjs +1 -0
- package/src/activation-continuity-engine-r65.mjs +2 -1
- package/src/autonomous-runtime.mjs +1 -1
- package/src/deadbyte-cli.mjs +33 -1
- package/src/machine-fs-smoke-client.mjs +13 -5
- package/src/machine-identity.mjs +59 -2
- package/src/machine-mcp-tools.mjs +8 -0
- package/src/mcp-server.mjs +11 -2
- package/src/native-inspector-r67.mjs +66 -0
- package/src/r67-diagnostic-export.mjs +118 -0
- package/src/r67-diagnostics-verify.mjs +82 -0
- package/src/r67-evidence-reconcile.mjs +128 -0
- package/src/r67-native-observation-authority.mjs +45 -0
- package/src/r67-observation-receipt.mjs +43 -0
- package/src/r67-regression-shards.mjs +176 -0
- package/src/remote-live-log.mjs +14 -10
- package/src/tool-audit.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/agent-runtime.test.mjs +1 -1
- package/test/autonomous-loop.test.mjs +11 -6
- package/test/autonomous-runtime.test.mjs +1 -1
- package/test/coding-plane.test.mjs +1 -1
- package/test/full-max-catalog-r46.test.mjs +1 -1
- package/test/output-schema.test.mjs +1 -1
- package/test/r51-live-log-polling.test.mjs +12 -0
- package/test/r53-release-contract.test.mjs +1 -1
- package/test/r54-release-contract.test.mjs +1 -1
- package/test/r55-release-contract.test.mjs +1 -1
- package/test/r56-release-contract.test.mjs +1 -1
- package/test/r57-release-contract.test.mjs +1 -1
- package/test/r58-bundled-bridge.test.mjs +1 -1
- package/test/r58-release-contract.test.mjs +2 -2
- package/test/r58-windows-gate.test.mjs +1 -1
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r60-release-contract.test.mjs +3 -3
- package/test/r61-release-contract.test.mjs +1 -1
- package/test/r62-release-contract.test.mjs +1 -1
- package/test/r63-release-contract.test.mjs +1 -1
- package/test/r64-release-contract.test.mjs +1 -1
- package/test/r65-epoch-native-activation-integration.test.mjs +4 -4
- package/test/r65-release-contract.test.mjs +1 -1
- package/test/r66-release-contract.test.mjs +2 -1
- package/test/r67-cli-integration.test.mjs +41 -0
- package/test/r67-diagnostics-cli.test.mjs +60 -0
- package/test/r67-diagnostics-verify.test.mjs +49 -0
- package/test/r67-diagnostics-zip-oracle.test.mjs +38 -0
- package/test/r67-diagnostics.test.mjs +60 -0
- package/test/r67-evidence-reconcile.test.mjs +186 -0
- package/test/r67-isolated-slot-rollback-restore.test.mjs +62 -0
- package/test/r67-live-log-secret-canary.test.mjs +44 -0
- package/test/r67-machine-fs-smoke-epoch.test.mjs +22 -0
- package/test/r67-native-inspector.test.mjs +131 -0
- package/test/r67-native-observation-authority.test.mjs +49 -0
- package/test/r67-npm-cross-volume.test.mjs +74 -0
- package/test/r67-npm-publish-gate.test.mjs +38 -0
- package/test/r67-regression-shards.test.mjs +184 -0
- package/test/r67-release-contract.test.mjs +44 -0
- package/test/r67-runtime-observation-binding.test.mjs +86 -0
- package/test/r67-server-observation-wiring.test.mjs +12 -0
- package/test/r67-signed-receipt.test.mjs +66 -0
- package/test/r67-skip-rationale.test.mjs +31 -0
- package/test/r67-tool-annotations.test.mjs +21 -0
- package/test/r67-windows-gate-argv.test.mjs +13 -0
- package/test/release-version.test.mjs +1 -1
- package/test/remote-live-log-r36.test.mjs +10 -0
- package/test/remote-live-log-r37.test.mjs +29 -0
- package/test/semantic-multilang.test.mjs +1 -1
|
@@ -39,7 +39,7 @@ deadbyte-process-host.cpp
|
|
|
39
39
|
exit_code=0
|
|
40
40
|
==== probe-dependencies ====
|
|
41
41
|
|
|
42
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
42
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\containment-probe.exe
|
|
43
43
|
|
|
44
44
|
File Type: EXECUTABLE IMAGE
|
|
45
45
|
|
|
@@ -59,7 +59,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
59
59
|
exit_code=0
|
|
60
60
|
==== contained-launcher-dependencies ====
|
|
61
61
|
|
|
62
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
62
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\deadbyte-exec.exe
|
|
63
63
|
|
|
64
64
|
File Type: EXECUTABLE IMAGE
|
|
65
65
|
|
|
@@ -83,7 +83,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
83
83
|
exit_code=0
|
|
84
84
|
==== contained-worker-dependencies ====
|
|
85
85
|
|
|
86
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
86
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\contained-transform-worker.exe
|
|
87
87
|
|
|
88
88
|
File Type: EXECUTABLE IMAGE
|
|
89
89
|
|
|
@@ -104,7 +104,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
104
104
|
exit_code=0
|
|
105
105
|
==== contained-reverse-worker-dependencies ====
|
|
106
106
|
|
|
107
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
107
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\contained-reverse-worker.exe
|
|
108
108
|
|
|
109
109
|
File Type: EXECUTABLE IMAGE
|
|
110
110
|
|
|
@@ -125,7 +125,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
125
125
|
exit_code=0
|
|
126
126
|
==== named-tunnel-host-dependencies ====
|
|
127
127
|
|
|
128
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
128
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\deadbyte-tunnel-host.exe
|
|
129
129
|
|
|
130
130
|
File Type: EXECUTABLE IMAGE
|
|
131
131
|
|
|
@@ -144,7 +144,7 @@ File Type: EXECUTABLE IMAGE
|
|
|
144
144
|
exit_code=0
|
|
145
145
|
==== process-host-dependencies ====
|
|
146
146
|
|
|
147
|
-
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.
|
|
147
|
+
Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\deadbyte-process-host.exe
|
|
148
148
|
|
|
149
149
|
File Type: EXECUTABLE IMAGE
|
|
150
150
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
param([Parameter(Mandatory=$true)][ValidatePattern('^[0-9a-f]{64}$')][string]$SourceSha256)
|
|
2
|
+
$ErrorActionPreference='Stop'
|
|
3
|
+
Set-StrictMode -Version Latest
|
|
4
|
+
$root=Split-Path -Parent $PSScriptRoot
|
|
5
|
+
$src=Join-Path $root 'native\deadbyte-native-inspect-r67.cpp'
|
|
6
|
+
$observed=(Get-FileHash -LiteralPath $src -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
7
|
+
if($observed -ne $SourceSha256){throw "R67 source SHA mismatch; refusing build"}
|
|
8
|
+
Write-Output "R67_SOURCE_SHA256=$observed"
|
|
9
|
+
$bin=Join-Path $root 'bin'
|
|
10
|
+
$exe=Join-Path $bin 'deadbyte-native-inspect-r67.exe'
|
|
11
|
+
$obj=Join-Path $bin 'deadbyte-native-inspect-r67.obj'
|
|
12
|
+
$vswhere='C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe'
|
|
13
|
+
if(!(Test-Path -LiteralPath $vswhere)){throw 'vswhere missing'}
|
|
14
|
+
$install=(& $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | Select-Object -First 1)
|
|
15
|
+
if(!$install){throw 'MSVC x64 C++ toolchain missing'}
|
|
16
|
+
$dev=Join-Path $install 'Common7\Tools\VsDevCmd.bat'
|
|
17
|
+
$cmd=if($env:ComSpec){$env:ComSpec}else{Join-Path $env:SystemRoot 'System32\cmd.exe'}
|
|
18
|
+
New-Item -ItemType Directory -Path $bin -Force | Out-Null
|
|
19
|
+
$common='/nologo /std:c++17 /permissive- /W4 /WX /O2 /MT /EHsc /guard:cf /diagnostics:caret /utf-8 /Brepro /DUNICODE /D_UNICODE /D_WIN32_WINNT=0x0A00'
|
|
20
|
+
$compile='cl {0} "{1}" /Fo:"{2}" /Fe:"{3}" /link bcrypt.lib /Brepro /INCREMENTAL:NO' -f $common,$src,$obj,$exe
|
|
21
|
+
$line='call "{0}" -no_logo -arch=x64 -host_arch=x64 && {1}' -f $dev,$compile
|
|
22
|
+
$output=& $cmd /d /s /c $line 2>&1
|
|
23
|
+
$rc=$LASTEXITCODE
|
|
24
|
+
$output | ForEach-Object { Write-Output ([string]$_) }
|
|
25
|
+
if($rc -ne 0){throw "R67 native build failed exit=$rc"}
|
|
26
|
+
if(!(Test-Path -LiteralPath $exe -PathType Leaf)){throw 'R67 native exe missing'}
|
|
27
|
+
$sha=(Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
28
|
+
Write-Output "R67_NATIVE_BUILD_PASS sha256=$sha path=$exe"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[ValidateSet('preflight','focused','evidence','manifest','doctor','all')]
|
|
3
|
+
[string]$Stage='preflight',
|
|
4
|
+
[string]$OperationId=''
|
|
5
|
+
)
|
|
6
|
+
$ErrorActionPreference='Stop'
|
|
7
|
+
Set-StrictMode -Version Latest
|
|
8
|
+
$root=Split-Path -Parent $PSScriptRoot
|
|
9
|
+
$parent='D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.43-r66'
|
|
10
|
+
$expected='4c4a64a4c8e9f5a6e9eecce85fd0007ebcac99121b29c07f1a1fc81d1643cd33'
|
|
11
|
+
$expectedGen='ef3b041e2a87c2e143d4733a98a52d9db80cd16ad7007e112fcb5202e70c77c3'
|
|
12
|
+
$expectedRelease='v0.15.43-4c4a64a4c8e9f5a6'
|
|
13
|
+
if(!(Test-Path -LiteralPath $parent -PathType Container)){throw 'R67 predecessor source missing'}
|
|
14
|
+
$prev=(Get-FileHash -LiteralPath (Join-Path $parent 'MANIFEST.SHA256') -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
15
|
+
if($prev -ne $expected){throw 'R67 predecessor manifest not exact'}
|
|
16
|
+
$stateRoot=Join-Path $env:USERPROFILE '.deadbyte-mcp'
|
|
17
|
+
$pointerPath=Join-Path $stateRoot 'active-release.json'
|
|
18
|
+
if(!(Test-Path -LiteralPath $pointerPath -PathType Leaf)){throw 'R67 active predecessor pointer missing'}
|
|
19
|
+
$pointer=Get-Content -LiteralPath $pointerPath -Raw | ConvertFrom-Json
|
|
20
|
+
if($pointer.release_id -ne $expectedRelease -or $pointer.manifest_sha256 -ne $expected -or
|
|
21
|
+
$pointer.generation_manifest_sha256 -ne $expectedGen){throw 'R67 active predecessor binding mismatch'}
|
|
22
|
+
$pkg=Get-Content -LiteralPath (Join-Path $root 'package.json') -Raw | ConvertFrom-Json
|
|
23
|
+
if($pkg.version -ne '0.15.44'){throw 'R67 package candidate version mismatch'}
|
|
24
|
+
Write-Output 'R67_PREDECESSOR_PREFLIGHT=PASS'
|
|
25
|
+
if($Stage -eq 'preflight'){return}
|
|
26
|
+
function Invoke-Node([string]$Label,[string[]]$NodeArgs){
|
|
27
|
+
Write-Output "R67_STAGE_START=$Label"
|
|
28
|
+
& node @NodeArgs
|
|
29
|
+
if($LASTEXITCODE -ne 0){throw "R67 stage $Label exit=$LASTEXITCODE"}
|
|
30
|
+
Write-Output "R67_STAGE_PASS=$Label"
|
|
31
|
+
}
|
|
32
|
+
if($Stage -eq 'all' -or $Stage -eq 'focused'){
|
|
33
|
+
Invoke-Node 'focused' @('scripts/r67-focused-gate.mjs')
|
|
34
|
+
Invoke-Node 'machine-fs-smoke' @('src/machine-fs-smoke-client.mjs')
|
|
35
|
+
if($Stage -eq 'focused'){return}
|
|
36
|
+
}
|
|
37
|
+
if($Stage -eq 'all' -or $Stage -eq 'evidence'){
|
|
38
|
+
if($OperationId -notmatch '^[a-z0-9][a-z0-9._-]{5,63}$'){throw 'R67 full evidence operation ID required'}
|
|
39
|
+
Invoke-Node 'evidence' @('scripts/r67-regression-reconcile.mjs',$OperationId)
|
|
40
|
+
if($Stage -eq 'evidence'){return}
|
|
41
|
+
}
|
|
42
|
+
if($Stage -eq 'all' -or $Stage -eq 'manifest'){
|
|
43
|
+
Invoke-Node 'manifest-verify' @('scripts/release-manifest.mjs','verify')
|
|
44
|
+
if($Stage -eq 'manifest'){return}
|
|
45
|
+
}
|
|
46
|
+
if($Stage -eq 'all' -or $Stage -eq 'doctor'){
|
|
47
|
+
$prior=$env:DEADBYTE_STATE_ROOT
|
|
48
|
+
$isolated='D:\Projects\deadbyte-mcp\testing\0.15.44-r67\doctor-isolated-empty'
|
|
49
|
+
if(Test-Path -LiteralPath (Join-Path $isolated 'active-release.json')){throw 'isolated doctor root unexpectedly has active release pointer'}
|
|
50
|
+
try{
|
|
51
|
+
$env:DEADBYTE_STATE_ROOT=$isolated
|
|
52
|
+
Invoke-Node 'isolated-doctor' @('src/deadbyte-cli.mjs','doctor')
|
|
53
|
+
}finally{$env:DEADBYTE_STATE_ROOT=$prior}
|
|
54
|
+
if($Stage -eq 'doctor'){return}
|
|
55
|
+
}
|
|
56
|
+
Write-Output 'R67_WINDOWS_GATE=PASS_PREDEPLOY_CANDIDATE'
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {createHash,randomBytes} from 'node:crypto';
|
|
2
|
+
import {lstat,readFile,open,link,unlink} from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
function sha256(bytes){return createHash('sha256').update(bytes).digest('hex');}
|
|
6
|
+
function integrity(bytes){return 'sha512-'+createHash('sha512').update(bytes).digest('base64');}
|
|
7
|
+
async function regular(pathname,label){
|
|
8
|
+
const stat=await lstat(pathname);
|
|
9
|
+
if(!stat.isFile()||stat.isSymbolicLink())throw new Error(label+' must be a regular non-symlink file');
|
|
10
|
+
return stat;
|
|
11
|
+
}
|
|
12
|
+
function assertSameIdentity(before,after){
|
|
13
|
+
if(before.dev!==after.dev||before.ino!==after.ino||before.size!==after.size)
|
|
14
|
+
throw new Error('source artifact identity changed during preservation; do not remove original');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Preserve an npm-packed immutable tarball across Windows volumes.
|
|
19
|
+
* Uses an exclusive staging file ON THE DESTINATION VOLUME, syncs complete
|
|
20
|
+
* verified bytes, and publishes with a same-volume, no-clobber hard link.
|
|
21
|
+
* No rename(D: -> C:), no partial destination visibility, no npm publish.
|
|
22
|
+
* Preserving exact bytes is NOT permission to send them to npm.
|
|
23
|
+
*/
|
|
24
|
+
export async function preserveNpmTarball({source,destination,expectedIntegrity,expectedShasum}={}){
|
|
25
|
+
if(typeof source!=='string'||typeof destination!=='string'||
|
|
26
|
+
!path.isAbsolute(source)||!path.isAbsolute(destination)||
|
|
27
|
+
path.resolve(source)===path.resolve(destination))throw new Error('absolute distinct source and destination required');
|
|
28
|
+
if(typeof expectedIntegrity!=='string'||!/^sha512-[A-Za-z0-9+/]+=*$/.test(expectedIntegrity))
|
|
29
|
+
throw new Error('npm SHA-512 integrity precondition required');
|
|
30
|
+
if(expectedShasum!==undefined&&!/^[a-f0-9]{40}$/.test(expectedShasum))
|
|
31
|
+
throw new Error('invalid npm packed artifact SHA-1 precondition');
|
|
32
|
+
|
|
33
|
+
const original=await regular(source,'source artifact');
|
|
34
|
+
if(original.size===0||original.size>128*1024*1024)throw new Error('npm artifact size outside permitted budget');
|
|
35
|
+
const bytes=await readFile(source);
|
|
36
|
+
assertSameIdentity(original,await regular(source,'source artifact'));
|
|
37
|
+
if(bytes.length!==original.size||integrity(bytes)!==expectedIntegrity)
|
|
38
|
+
throw new Error('npm packed artifact integrity mismatch; original retained');
|
|
39
|
+
if(expectedShasum!==undefined&&createHash('sha1').update(bytes).digest('hex')!==expectedShasum)
|
|
40
|
+
throw new Error('npm packed artifact shasum mismatch; original retained');
|
|
41
|
+
const hash=sha256(bytes);
|
|
42
|
+
let committed=false;
|
|
43
|
+
try{
|
|
44
|
+
const existing=await regular(destination,'preserved artifact');
|
|
45
|
+
const preserved=await readFile(destination);
|
|
46
|
+
if(existing.size!==bytes.length||sha256(preserved)!==hash||integrity(preserved)!==expectedIntegrity)
|
|
47
|
+
throw new Error('existing preserved artifact differs; manual conflict reconciliation required');
|
|
48
|
+
committed=true;
|
|
49
|
+
}catch(error){
|
|
50
|
+
if(error?.code!=='ENOENT')throw error;
|
|
51
|
+
}
|
|
52
|
+
let staging=null;
|
|
53
|
+
if(!committed){
|
|
54
|
+
staging=path.join(path.dirname(destination),'.'+path.basename(destination)+'.stage-'+process.pid+'-'+randomBytes(8).toString('hex'));
|
|
55
|
+
try{
|
|
56
|
+
const handle=await open(staging,'wx',0o600);
|
|
57
|
+
try{
|
|
58
|
+
await handle.writeFile(bytes);
|
|
59
|
+
await handle.sync();
|
|
60
|
+
}finally{await handle.close();}
|
|
61
|
+
const staged=await readFile(staging);
|
|
62
|
+
if(staged.length!==bytes.length||sha256(staged)!==hash||integrity(staged)!==expectedIntegrity)
|
|
63
|
+
throw new Error('staged artifact integrity mismatch; original retained');
|
|
64
|
+
// Atomic visibility of already-fsynced bytes, exclusively on the destination volume.
|
|
65
|
+
try{await link(staging,destination);}
|
|
66
|
+
catch(error){
|
|
67
|
+
if(error?.code!=='EEXIST')throw error;
|
|
68
|
+
const existing=await regular(destination,'preserved artifact');
|
|
69
|
+
const preserved=await readFile(destination);
|
|
70
|
+
if(existing.size!==bytes.length||sha256(preserved)!==hash||integrity(preserved)!==expectedIntegrity)
|
|
71
|
+
throw new Error('concurrent preserved artifact conflict; original retained');
|
|
72
|
+
}
|
|
73
|
+
committed=true;
|
|
74
|
+
}finally{
|
|
75
|
+
if(staging)await unlink(staging).catch(e=>{if(e?.code!=='ENOENT')throw e;});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Final post-commit verification before relinquishing the source artifact.
|
|
79
|
+
const final=await regular(destination,'preserved artifact');
|
|
80
|
+
const preserved=await readFile(destination);
|
|
81
|
+
if(final.size!==bytes.length||sha256(preserved)!==hash||integrity(preserved)!==expectedIntegrity)
|
|
82
|
+
throw new Error('preserved artifact changed after commit; original retained');
|
|
83
|
+
const current=await regular(source,'source artifact');
|
|
84
|
+
assertSameIdentity(original,current);
|
|
85
|
+
if(sha256(await readFile(source))!==hash)throw new Error('source bytes changed after commit; retain for reconciliation');
|
|
86
|
+
await unlink(source);
|
|
87
|
+
return {status:staging?'VERIFIED_PRESERVED':'VERIFIED_ALREADY_PRESERVED',
|
|
88
|
+
sha256:hash,bytes:bytes.length,integrity:expectedIntegrity};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function assertVacantPackDestination({root,filename}={}){
|
|
92
|
+
if(typeof root!=='string'||!path.isAbsolute(root)||typeof filename!=='string'||
|
|
93
|
+
!/^[A-Za-z0-9][A-Za-z0-9._-]*\.tgz$/.test(filename)||filename!==path.basename(filename))
|
|
94
|
+
throw new Error('invalid npm pack target');
|
|
95
|
+
const target=path.join(root,filename);
|
|
96
|
+
try{await lstat(target);}
|
|
97
|
+
catch(error){if(error?.code==='ENOENT')return target;throw error;}
|
|
98
|
+
throw new Error('PACK_BLOCKED existing npm tarball must be archived and reconciled before npm pack: '+target);
|
|
99
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { reconcileNpmPublication } from './npm-publication-r65.mjs';
|
|
4
|
-
import { mkdtemp, readFile, rm, unlink, mkdir
|
|
4
|
+
import { mkdtemp, readFile, rm, unlink, mkdir } from 'node:fs/promises';
|
|
5
|
+
import {preserveNpmTarball,assertVacantPackDestination} from './npm-preserve-artifact-r67.mjs';
|
|
6
|
+
import {assertR67NpmPublishReady} from './npm-release-readiness-r67.mjs';
|
|
5
7
|
import { existsSync } from 'node:fs';
|
|
6
8
|
import { resolveStateRoot } from '../src/state-root.mjs';
|
|
7
9
|
import path from 'node:path';
|
|
@@ -86,6 +88,7 @@ async function main() {
|
|
|
86
88
|
if (provenance.version !== version) {
|
|
87
89
|
throw new Error(`PROVENANCE.json version mismatch: package=${version} provenance=${provenance.version}`);
|
|
88
90
|
}
|
|
91
|
+
if (!isVerifyOnly && version==='0.15.44') assertR67NpmPublishReady(release);
|
|
89
92
|
if (release.parent?.release_id !== provenance.parent_release_id ||
|
|
90
93
|
release.parent?.manifest_sha256 !== provenance.parent_manifest_sha256 ||
|
|
91
94
|
release.parent?.generation_manifest_sha256 !== provenance.parent_generation_manifest_sha256) {
|
|
@@ -113,6 +116,7 @@ async function main() {
|
|
|
113
116
|
if(publicationDir)await mkdir(publicationDir,{recursive:true});
|
|
114
117
|
if(resuming&&!existsSync(preservedTarball))throw new Error('immutable npm publication claim exists but exact local tarball is missing; manual artifact reconciliation required');
|
|
115
118
|
if(!resuming&&preservedTarball&&existsSync(preservedTarball))throw new Error('unclaimed preserved npm tarball exists; inspect artifact evidence before another pack');
|
|
119
|
+
if(!resuming)await assertVacantPackDestination({root,filename:`deadbyte-mcp-${version}.tgz`});
|
|
116
120
|
const packed=resuming?null:parseJson(runNpm(['pack', '--json'], { timeout: 180000 }));
|
|
117
121
|
const packRecord=resuming?{filename:path.basename(preservedTarball)}:(Array.isArray(packed) ? packed[0] : packed?.[pkg.name]);
|
|
118
122
|
const filename = packRecord?.filename;
|
|
@@ -196,7 +200,11 @@ async function main() {
|
|
|
196
200
|
return;
|
|
197
201
|
}
|
|
198
202
|
|
|
199
|
-
if(!resuming)
|
|
203
|
+
if(!resuming){
|
|
204
|
+
const preserved=await preserveNpmTarball({source:tarball,destination:preservedTarball,
|
|
205
|
+
expectedIntegrity:packRecord.integrity,expectedShasum:packRecord.shasum});
|
|
206
|
+
console.log('Immutable cross-volume artifact preservation verified: '+JSON.stringify(preserved));
|
|
207
|
+
}
|
|
200
208
|
const publishTarball=preservedTarball;
|
|
201
209
|
const localBytes = await readFile(publishTarball);
|
|
202
210
|
const localIntegrity=`sha512-${createHash('sha512').update(localBytes).digest('base64')}`;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* R67 npm publish fence. Does not prove Windows release readiness by itself:
|
|
3
|
+
* only the independent release verifier may change these metadata gates.
|
|
4
|
+
* The package publisher must never interpret focused or package-only PASS as
|
|
5
|
+
* authority to make an immutable public release.
|
|
6
|
+
*/
|
|
7
|
+
export function assertR67NpmPublishReady(release){
|
|
8
|
+
if(release?.version!=='0.15.44'||release?.train!=='R67')
|
|
9
|
+
throw new Error('PUBLISH_BLOCKED R67 release identity does not match candidate');
|
|
10
|
+
const state=release.r67_native_observation;
|
|
11
|
+
const gates={
|
|
12
|
+
status:'CANDIDATE_SEALED',
|
|
13
|
+
full_release_gate:'PASS',
|
|
14
|
+
current_source_full_shards:'PASS',
|
|
15
|
+
native_fault_injection:'PASS',
|
|
16
|
+
protocol_parity:'PASS',
|
|
17
|
+
isolated_activation_rollback_restore:'PASS',
|
|
18
|
+
independent_machine_observation:'PASS'
|
|
19
|
+
};
|
|
20
|
+
for(const [key,expected] of Object.entries(gates)){
|
|
21
|
+
if(state?.[key]!==expected)
|
|
22
|
+
throw new Error('PUBLISH_BLOCKED R67 '+key+' '+String(state?.[key]??'MISSING')+
|
|
23
|
+
'; require independently verified gate '+expected+' before npm publication');
|
|
24
|
+
}
|
|
25
|
+
return {status:'R67_METADATA_GATES_MATCH',policy:'PUBLISH_REQUIRES_INDEPENDENT_RELEASE_EVIDENCE'};
|
|
26
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import https from 'node:https';
|
|
2
|
+
import {createHash} from 'node:crypto';
|
|
3
|
+
import {gunzipSync} from 'node:zlib';
|
|
4
|
+
import {mkdir,open,writeFile,readFile} from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {fileURLToPath} from 'node:url';
|
|
7
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
8
|
+
const ref=path.resolve(root,'..','..','references','desktop-commander-0.3.1-alpha.2');
|
|
9
|
+
const version='0.3.1-alpha.2';
|
|
10
|
+
const packageName='@wonderwhy-er/desktop-commander';
|
|
11
|
+
const url='https://registry.npmjs.org/@wonderwhy-er%2fdesktop-commander/'+version;
|
|
12
|
+
const hash=(b,algo='sha256')=>createHash(algo).update(b).digest('hex');
|
|
13
|
+
async function fetchBounded(url,maxSize){
|
|
14
|
+
const parsed=new URL(url);
|
|
15
|
+
if(parsed.protocol!=='https:'||parsed.hostname!=='registry.npmjs.org'||parsed.username||parsed.password||parsed.port)throw new Error('untrusted npm endpoint');
|
|
16
|
+
return await new Promise((resolve,reject)=>{
|
|
17
|
+
const req=https.get(parsed,{timeout:20000,headers:{'Accept':'application/json','User-Agent':'deadbyte-reference-verifier/1.0'}},res=>{
|
|
18
|
+
if(res.statusCode!==200){res.resume();reject(new Error('npm GET HTTP '+res.statusCode));return;}
|
|
19
|
+
const parts=[];let size=0;
|
|
20
|
+
res.on('data',b=>{size+=b.length;if(size>maxSize){req.destroy(new Error('download exceeded declared safety cap'));return;}parts.push(b);});
|
|
21
|
+
res.on('end',()=>resolve(Buffer.concat(parts)));
|
|
22
|
+
res.on('error',reject);
|
|
23
|
+
});
|
|
24
|
+
req.on('timeout',()=>req.destroy(new Error('npm request timeout')));
|
|
25
|
+
req.on('error',reject);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async function saveNew(file,data){
|
|
29
|
+
const h=await open(file,'wx',0o600);
|
|
30
|
+
try{await h.writeFile(data);await h.sync();}finally{await h.close();}
|
|
31
|
+
}
|
|
32
|
+
const metadata=JSON.parse((await fetchBounded(url,1024*1024)).toString('utf8'));
|
|
33
|
+
if(metadata.name!==packageName||metadata.version!==version)throw new Error('npm metadata identity mismatch');
|
|
34
|
+
const tarball=metadata.dist?.tarball;
|
|
35
|
+
const expected='https://registry.npmjs.org/@wonderwhy-er/desktop-commander/-/desktop-commander-'+version+'.tgz';
|
|
36
|
+
if(tarball!==expected)throw new Error('registry tarball URL is not exact pinned package');
|
|
37
|
+
if(!/^sha512-[A-Za-z0-9+/]+=*$/.test(metadata.dist.integrity??''))throw new Error('missing registry SHA512 integrity');
|
|
38
|
+
const tar=await fetchBounded(tarball,32*1024*1024);
|
|
39
|
+
const integrity='sha512-'+createHash('sha512').update(tar).digest('base64');
|
|
40
|
+
if(integrity!==metadata.dist.integrity)throw new Error('npm SHA512 integrity mismatch');
|
|
41
|
+
if(metadata.dist.shasum&&hash(tar,'sha1')!==metadata.dist.shasum)throw new Error('npm SHA1 mismatch');
|
|
42
|
+
const expanded=gunzipSync(tar,{maxOutputLength:128*1024*1024});
|
|
43
|
+
const files=[];let offset=0,total=0;
|
|
44
|
+
const selected=[];
|
|
45
|
+
const names=new Set();
|
|
46
|
+
function cstr(b){return b.toString('utf8').split('\0')[0];}
|
|
47
|
+
while(offset+512<=expanded.length){
|
|
48
|
+
const header=expanded.subarray(offset,offset+512);
|
|
49
|
+
if(header.every(v=>v===0))break;
|
|
50
|
+
const filename=cstr(header.subarray(0,100));
|
|
51
|
+
const prefix=cstr(header.subarray(345,500));
|
|
52
|
+
const name=prefix?prefix+'/'+filename:filename;
|
|
53
|
+
const szText=cstr(header.subarray(124,136)).trim();
|
|
54
|
+
if(!/^[0-7]*$/.test(szText))throw new Error('non-octal tar entry size');
|
|
55
|
+
const size=parseInt(szText||'0',8);
|
|
56
|
+
const type=header[156];
|
|
57
|
+
if(!Number.isSafeInteger(size)||size<0||offset+512+size>expanded.length)throw new Error('corrupt archive boundary');
|
|
58
|
+
if(type===50||type===49)throw new Error('tar link entry denied');
|
|
59
|
+
if(type===0||type===48){
|
|
60
|
+
if(!name.startsWith('package/')||name.includes('\\')||name.includes(':')||
|
|
61
|
+
name.split('/').some(c=>!c||c==='.'||c==='..')||names.has(name))throw new Error('unsafe tar entry '+name);
|
|
62
|
+
names.add(name);files.push({path:name,size});total+=size;
|
|
63
|
+
if(files.length>1000||total>128*1024*1024)throw new Error('tar inventory budget exceeded');
|
|
64
|
+
const rel=name.slice('package/'.length);
|
|
65
|
+
if(/^(?:package\.json|README\.md|LICENSE|LICENCE|dist\/[a-zA-Z0-9_./-]+\.(?:js|mjs|cjs|json|map))$/.test(rel)&&size<=8*1024*1024)
|
|
66
|
+
selected.push({rel,content:Buffer.from(expanded.subarray(offset+512,offset+512+size))});
|
|
67
|
+
}else if(![53,120,103].includes(type))throw new Error('unsupported tar type '+type+' for '+name);
|
|
68
|
+
offset+=512+Math.ceil(size/512)*512;
|
|
69
|
+
}
|
|
70
|
+
const packageEntry=selected.find(e=>e.rel==='package.json');
|
|
71
|
+
if(!packageEntry)throw new Error('tarball has no package.json');
|
|
72
|
+
const packaged=JSON.parse(packageEntry.content.toString('utf8'));
|
|
73
|
+
if(packaged.name!==packageName||packaged.version!==version)throw new Error('tarball package version disagrees with registry metadata');
|
|
74
|
+
await mkdir(path.join(ref,'static'),{recursive:true});
|
|
75
|
+
const archivePath=path.join(ref,'desktop-commander-'+version+'.tgz');
|
|
76
|
+
await saveNew(archivePath,tar);
|
|
77
|
+
let selectedBytes=0;
|
|
78
|
+
for(const e of selected){
|
|
79
|
+
const target=path.join(ref,'static',...e.rel.split('/'));
|
|
80
|
+
await mkdir(path.dirname(target),{recursive:true});
|
|
81
|
+
await saveNew(target,e.content);selectedBytes+=e.content.length;
|
|
82
|
+
}
|
|
83
|
+
const receipt={
|
|
84
|
+
schema:'deadbyte.competitor-reference-acquisition.v1',status:'VERIFIED_STATIC_ONLY',
|
|
85
|
+
package:packageName,version,metadata_source:url,
|
|
86
|
+
source_archive:archivePath,archive_bytes:tar.length,archive_sha256:hash(tar),
|
|
87
|
+
archive_sha512_integrity:integrity,registry_shasum_sha1:metadata.dist.shasum??null,
|
|
88
|
+
files_total:files.length,files_size_bytes:total,files_selected: selected.length,selected_bytes:selectedBytes,
|
|
89
|
+
has_lifecycle_scripts:Boolean(packaged.scripts&&(packaged.scripts.preinstall||packaged.scripts.install||packaged.scripts.postinstall)),
|
|
90
|
+
lifecycle_scripts_executed:false,dependencies_installed:false,remote_started:false,network_action:'registry_https_download_only',
|
|
91
|
+
selected_files_sha256:selected.map(e=>({path:e.rel,sha256:hash(e.content)})),
|
|
92
|
+
full_archive_inventory_sha256:hash(Buffer.from(JSON.stringify(files)))
|
|
93
|
+
};
|
|
94
|
+
await saveNew(path.join(ref,'ACQUISITION.json'),JSON.stringify(receipt,null,2)+'\n');
|
|
95
|
+
console.log(JSON.stringify({status:receipt.status,version,archive_sha256:receipt.archive_sha256,archive_bytes:tar.length,files_total:files.length,files_selected:selected.length,lifecycle_scripts_executed:false,reference_path:ref,source:metadata.dist.tarball},null,2));
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {readFile,readdir,open,mkdir} from 'node:fs/promises';
|
|
2
|
+
import {createHash} from 'node:crypto';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
6
|
+
const evidence=path.resolve(root,'..','..','testing','0.15.44-r67','regression-shards');
|
|
7
|
+
const operation='r67audit-20261011c';
|
|
8
|
+
const cases=[];
|
|
9
|
+
const files=(await readdir(path.join(root,'test'))).filter(n=>n.endsWith('.test.mjs'));
|
|
10
|
+
const texts=new Map();for(const file of files)texts.set(file,(await readFile(path.join(root,'test',file),'utf8')).split(/\r?\n/));
|
|
11
|
+
const fileSha=b=>createHash('sha256').update(b).digest('hex');
|
|
12
|
+
for(let index=0;index<32;index++){
|
|
13
|
+
const p=path.join(evidence,operation+'-shard-'+String(index).padStart(2,'0'),'result.json');
|
|
14
|
+
const r=JSON.parse(await readFile(p,'utf8'));
|
|
15
|
+
for(const c of r.nonpass_cases??[]){
|
|
16
|
+
const name=c.name;
|
|
17
|
+
const matches=[];
|
|
18
|
+
for(const [file,lines] of texts){
|
|
19
|
+
for(let line=0;line<lines.length;line++){
|
|
20
|
+
const hay=lines[line];
|
|
21
|
+
const short=name.replace(/^R\d+ /,'').slice(0,40);
|
|
22
|
+
if(hay.includes(name)||hay.includes(short)){
|
|
23
|
+
matches.push({file,line:line+1,context:lines.slice(Math.max(0,line-3),Math.min(lines.length,line+4)).join(' ').slice(0,780)});
|
|
24
|
+
if(matches.length>=5)break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if(matches.length>=5)break;
|
|
28
|
+
}
|
|
29
|
+
cases.push({shard:index,name,reason:c.reason,source_matches:matches});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const summary={schema:'deadbyte.r67-nonpass-source-audit.v1',operation,
|
|
33
|
+
count:cases.length,with_reason:cases.filter(c=>c.reason).length,
|
|
34
|
+
without_reason:cases.filter(c=>!c.reason).length,unmatched:cases.filter(c=>!c.source_matches.length).length,
|
|
35
|
+
cases};
|
|
36
|
+
const out=path.resolve(root,'..','..','testing','0.15.44-r67','R67-NONPASS-SOURCE-AUDIT-20261011.json');
|
|
37
|
+
const h=await open(out,'wx',0o600);try{await h.writeFile(JSON.stringify(summary,null,2)+'\n');await h.sync();}finally{await h.close();}
|
|
38
|
+
console.log(JSON.stringify({count:summary.count,with_reason:summary.with_reason,without_reason:summary.without_reason,
|
|
39
|
+
unmatched:summary.unmatched,findings:cases.filter(c=>!c.reason).map(c=>({shard:c.shard,name:c.name,matches:c.source_matches}))},null,2));
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {readdir,readFile,writeFile,mkdir,open} from 'node:fs/promises';
|
|
2
|
+
import {createHash} from 'node:crypto';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..','..','..');
|
|
6
|
+
const refs=path.join(root,'references');
|
|
7
|
+
const stable=path.join(refs,'desktop-commander','dist');
|
|
8
|
+
const alpha=path.join(refs,'desktop-commander-0.3.1-alpha.2','static','dist');
|
|
9
|
+
const output=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..','docs','R67-ALPHA-STATIC-COMPARISON.json');
|
|
10
|
+
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
11
|
+
async function inventory(dir,prefix=''){
|
|
12
|
+
const items=await readdir(path.join(dir,prefix),{withFileTypes:true});
|
|
13
|
+
const out=[];
|
|
14
|
+
for(const x of items){
|
|
15
|
+
const sub=[prefix,x.name].filter(Boolean).join('/');
|
|
16
|
+
if(x.isDirectory())out.push(...await inventory(dir,sub));
|
|
17
|
+
else if(x.isFile()&&/\.(?:js|mjs|cjs|json)$/.test(sub)){
|
|
18
|
+
const b=await readFile(path.join(dir,sub));
|
|
19
|
+
out.push({path:sub,sha256:sha(b),bytes:b.length});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
const older=await inventory(stable),newer=await inventory(alpha);
|
|
25
|
+
const map=new Map(older.map(f=>[f.path,f]));
|
|
26
|
+
const changes=[];
|
|
27
|
+
for(const f of newer){
|
|
28
|
+
const old=map.get(f.path);
|
|
29
|
+
changes.push({path:f.path,status:!old?'ADDED':old.sha256===f.sha256?'UNCHANGED':'CHANGED',
|
|
30
|
+
stable_sha256:old?.sha256??null,alpha_sha256:f.sha256,delta_bytes:old?f.bytes-old.bytes:null});
|
|
31
|
+
}
|
|
32
|
+
const relevant=changes.filter(x=>/diagnostic|remote-device|annotations|schema|mcp|pdf|process|reconnect|terminal/i.test(x.path));
|
|
33
|
+
const report={
|
|
34
|
+
schema:'deadbyte.r67-competitor-static-compare.v1',
|
|
35
|
+
scope:'static npm archives only; do not execute or install competitor',
|
|
36
|
+
stable_version:'0.2.52',alpha_version:'0.3.1-alpha.2',
|
|
37
|
+
alpha_tarball_sha256:'faaf3f34f114589c0630ce4eaa7114ecc628ce2030822dfe67feac0e17fedc4b',
|
|
38
|
+
stable_compared_files:older.length,alpha_selected_compared_files:newer.length,
|
|
39
|
+
added:changes.filter(x=>x.status==='ADDED').length,
|
|
40
|
+
changed:changes.filter(x=>x.status==='CHANGED').length,
|
|
41
|
+
unchanged:changes.filter(x=>x.status==='UNCHANGED').length,
|
|
42
|
+
removed_from_selected_scope:0,
|
|
43
|
+
relevant_changes:relevant,
|
|
44
|
+
caveat:'Alpha was selectively extracted; do not treat missing alpha inventory as deleted without checking full archive.'
|
|
45
|
+
};
|
|
46
|
+
const handle=await open(output,'wx');
|
|
47
|
+
try{await handle.writeFile(JSON.stringify(report,null,2)+'\n');await handle.sync();}finally{await handle.close();}
|
|
48
|
+
console.log(JSON.stringify({status:'STATIC_DIFFERENTIAL_PASS',stable_files:report.stable_compared_files,alpha_files:report.alpha_selected_compared_files,added:report.added,changed:report.changed,unchanged:report.unchanged,relevant:relevant.filter(x=>x.status!=='UNCHANGED').map(x=>({path:x.path,status:x.status,delta:x.delta_bytes})).slice(0,60)},null,2));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {readFile} from 'node:fs/promises';
|
|
2
|
+
import {spawnSync} from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
6
|
+
const op=process.argv[2],shard=process.argv[3];
|
|
7
|
+
if(!/^[a-z0-9._-]{6,64}$/.test(op??'')||!/^(0|[1-9]\d?)$/.test(shard??''))throw new Error('operation and shard required');
|
|
8
|
+
const file=path.resolve(root,'..','..','testing','0.15.44-r67','regression-shards',op+'-shard-'+String(shard).padStart(2,'0'),'intent.json');
|
|
9
|
+
const intent=JSON.parse(await readFile(file,'utf8'));
|
|
10
|
+
if(intent.operation!==op||intent.shard_index!==Number(shard))throw new Error('historical intent mismatch');
|
|
11
|
+
const r=spawnSync(process.execPath,['--test','--test-reporter=tap','--test-concurrency=2',...intent.files],{
|
|
12
|
+
cwd:root,encoding:'utf8',windowsHide:true,shell:false,timeout:60000,maxBuffer:8*1024*1024,
|
|
13
|
+
env:Object.fromEntries(Object.entries(process.env).filter(([k])=>k!=='NODE_TEST_CONTEXT'))
|
|
14
|
+
});
|
|
15
|
+
const output=String(r.stdout??'');
|
|
16
|
+
const lines=output.split(/\r?\n/);
|
|
17
|
+
const failures=[];
|
|
18
|
+
for(let i=0;i<lines.length;i++){
|
|
19
|
+
if(/^(?:not ok |# (?:error|failure)|✖ )/.test(lines[i]))failures.push(lines.slice(Math.max(0,i-1),Math.min(lines.length,i+28)).join('\n').slice(0,4200));
|
|
20
|
+
}
|
|
21
|
+
console.log(JSON.stringify({schema:'deadbyte.r67-diagnostic-replay.v1',role:'NON_RELEASE_TEST_DIAGNOSIS_ONLY',
|
|
22
|
+
from_historical_operation:op,shard:Number(shard),exit_code:r.status,child_error:r.error?.code??null,
|
|
23
|
+
failures:failures.slice(0,20),tail:output.slice(-1800),stderr:String(r.stderr??'').slice(-1000)},null,2));
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {readdir,mkdir,open,readFile,rename} from 'node:fs/promises';
|
|
2
|
+
import {spawnSync} from 'node:child_process';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
7
|
+
const evidence=path.resolve(root,'../../testing/0.15.44-r67/full-batches');
|
|
8
|
+
const tests=(await readdir(path.join(root,'test'))).filter(n=>n.endsWith('.test.mjs')).sort();
|
|
9
|
+
const size=27;
|
|
10
|
+
const indexRaw=process.argv[2];
|
|
11
|
+
if(!/^(?:0|[1-9]\d*)$/.test(indexRaw??''))throw new Error('batch index required');
|
|
12
|
+
const index=Number(indexRaw);
|
|
13
|
+
const total=Math.ceil(tests.length/size);
|
|
14
|
+
if(!Number.isInteger(index)||index<0||index>=total)throw new Error('batch index out of range');
|
|
15
|
+
await mkdir(evidence,{recursive:true});
|
|
16
|
+
const attempt=process.argv[3]??'initial';
|
|
17
|
+
if(!/^[a-zA-Z0-9_-]{1,24}$/.test(attempt))throw new Error('invalid attempt label');
|
|
18
|
+
const suffix=attempt==='initial'?'':'-'+attempt;
|
|
19
|
+
const file=path.join(evidence,'batch-'+String(index).padStart(2,'0')+suffix+'.json');
|
|
20
|
+
const start=new Date().toISOString();
|
|
21
|
+
const mode=process.argv[4]??'all';
|
|
22
|
+
if(!['all','core','remote'].includes(mode)||(mode!=='all'&&index!==10))throw new Error('invalid batch split mode');
|
|
23
|
+
const window=tests.slice(index*size,(index+1)*size);
|
|
24
|
+
const selected=mode==='core'?window.filter(n=>n!=='remote-session-cli.test.mjs'):mode==='remote'?window.filter(n=>n==='remote-session-cli.test.mjs'):window;
|
|
25
|
+
if(selected.length===0)throw new Error('batch split is empty');
|
|
26
|
+
const sha=s=>createHash('sha256').update(s).digest('hex');
|
|
27
|
+
const identity={schema:'deadbyte.r67-durable-test-batch.v1',batch_index:index,batch_count:total,total_test_files:tests.length,test_files:selected,split_mode:mode,candidate_root:root,started_at_utc:start};
|
|
28
|
+
const staged=await open(file,'wx',0o600);
|
|
29
|
+
try{await staged.writeFile(JSON.stringify({...identity,status:'RUNNING'})+'\n');await staged.sync();}finally{await staged.close();}
|
|
30
|
+
const cmd=['--test','--test-concurrency=4',...selected.map(f=>'test/'+f)];
|
|
31
|
+
const result=spawnSync(process.execPath,cmd,{cwd:root,shell:false,windowsHide:true,encoding:'utf8',timeout:mode==='remote'?180000:90000,maxBuffer:24*1024*1024});
|
|
32
|
+
const output=String(result.stdout??''),error=String(result.stderr??'');
|
|
33
|
+
const count=k=>{const matches=[...output.matchAll(new RegExp('(?:^|\n)[#ℹ ]*'+k+'\\s+(\\d+(?:\\.\\d+)?)','g'))];return matches.length?Number(matches[matches.length-1][1]):null;};
|
|
34
|
+
const testsCount=count('tests'),pass=count('pass'),fail=count('fail'),skip=count('skipped');
|
|
35
|
+
const status=result.status===0&&result.error===undefined&&testsCount!==null&&pass!==null&&fail===0?'PASS':(result.error?.code==='ETIMEDOUT'?'TIMED_OUT':'FAIL');
|
|
36
|
+
const completed={...identity,status,finished_at_utc:new Date().toISOString(),exit_code:result.status,timed_out:result.error?.code==='ETIMEDOUT',observed_test_count:testsCount,passed:pass,failed:fail,skipped:skip,stdout_sha256:sha(Buffer.from(output)),stderr_sha256:sha(Buffer.from(error)),...(status==='PASS'?{}:{failure_tail:(output+'\n'+error).slice(-3500)})};
|
|
37
|
+
const tmp=file+'.finished-'+process.pid;
|
|
38
|
+
const handle=await open(tmp,'wx',0o600);
|
|
39
|
+
try{await handle.writeFile(JSON.stringify(completed,null,2)+'\n');await handle.sync();}finally{await handle.close();}
|
|
40
|
+
await rename(tmp,file);
|
|
41
|
+
console.log(JSON.stringify({batch_index:index,batch_count:total,test_files:selected.length,status,passed:pass,failed:fail,skipped:skip,exit_code:result.status,evidence_path:file},null,2));
|
|
42
|
+
if(status!=='PASS')process.exitCode=1;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {createHash} from 'node:crypto';
|
|
2
|
+
import {readFile,lstat} from 'node:fs/promises';
|
|
3
|
+
import {spawnSync} from 'node:child_process';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import {parseR67TapSummary} from '../src/r67-regression-shards.mjs';
|
|
7
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
8
|
+
const hash=b=>createHash('sha256').update(b).digest('hex');
|
|
9
|
+
const manifest=JSON.parse(await readFile(path.join(root,'docs','R67-NATIVE-INSPECT-BINARY.json'),'utf8'));
|
|
10
|
+
const binary=path.join(root,manifest.exe);
|
|
11
|
+
const info=await lstat(binary);
|
|
12
|
+
if(manifest.schema!=='deadbyte.r67-native-inspector-binary.v1'||!info.isFile()||info.isSymbolicLink()||hash(await readFile(binary))!==manifest.sha256)throw new Error('R67 native observer hash pin invalid');
|
|
13
|
+
const specs=[
|
|
14
|
+
'test/r67-native-inspector.test.mjs',
|
|
15
|
+
'test/r67-signed-receipt.test.mjs',
|
|
16
|
+
'test/r67-cli-integration.test.mjs',
|
|
17
|
+
'test/r67-diagnostics.test.mjs',
|
|
18
|
+
'test/r67-diagnostics-cli.test.mjs',
|
|
19
|
+
'test/r67-diagnostics-zip-oracle.test.mjs',
|
|
20
|
+
'test/r67-diagnostics-verify.test.mjs',
|
|
21
|
+
'test/r67-regression-shards.test.mjs',
|
|
22
|
+
'test/r67-evidence-reconcile.test.mjs',
|
|
23
|
+
'test/r67-skip-rationale.test.mjs',
|
|
24
|
+
'test/r67-runtime-observation-binding.test.mjs',
|
|
25
|
+
'test/r67-native-observation-authority.test.mjs',
|
|
26
|
+
'test/r67-server-observation-wiring.test.mjs',
|
|
27
|
+
'test/r67-machine-fs-smoke-epoch.test.mjs',
|
|
28
|
+
'test/r67-isolated-slot-rollback-restore.test.mjs',
|
|
29
|
+
'test/r65-epoch-native-activation-integration.test.mjs',
|
|
30
|
+
'test/machine-identity.test.mjs',
|
|
31
|
+
'test/r67-tool-annotations.test.mjs',
|
|
32
|
+
'test/remote-live-log-r36.test.mjs',
|
|
33
|
+
'test/remote-live-log-r37.test.mjs',
|
|
34
|
+
'test/r67-live-log-secret-canary.test.mjs',
|
|
35
|
+
'test/r51-live-log-polling.test.mjs',
|
|
36
|
+
'test/cli-entrypoint.test.mjs',
|
|
37
|
+
'test/observation-runtime.test.mjs',
|
|
38
|
+
'test/observation-mcp-tools.test.mjs',
|
|
39
|
+
'test/windows-observation-r48.test.mjs',
|
|
40
|
+
'test/r67-release-contract.test.mjs',
|
|
41
|
+
'test/r67-npm-cross-volume.test.mjs',
|
|
42
|
+
'test/r67-npm-publish-gate.test.mjs',
|
|
43
|
+
'test/r65-npm-publication.test.mjs'
|
|
44
|
+
];
|
|
45
|
+
const args=['--test','--test-concurrency=1',...specs];
|
|
46
|
+
const result=spawnSync(process.execPath,args,{cwd:root,encoding:'utf8',windowsHide:true,shell:false,timeout:180000,maxBuffer:16*1024*1024});
|
|
47
|
+
const stdout=String(result.stdout??'');
|
|
48
|
+
const stderr=String(result.stderr??'');
|
|
49
|
+
const tap=parseR67TapSummary(stdout);
|
|
50
|
+
const counts=tap?{tests:tap.tests,pass:tap.pass,fail:tap.fail,skipped:tap.skipped,cancelled:tap.cancelled,todo:tap.todo}:null;
|
|
51
|
+
const complete=Boolean(tap&&tap.tests>=specs.length&&tap.fail===0&&tap.skipped===0&&tap.cancelled===0&&tap.todo===0&&result.status===0&&!result.error);
|
|
52
|
+
const receipt={schema:'deadbyte.r67-focused-gate.v1',status:complete?'pass':'fail',
|
|
53
|
+
candidate_version:'0.15.44-draft',predecessor_manifest_sha256:'4c4a64a4c8e9f5a6e9eecce85fd0007ebcac99121b29c07f1a1fc81d1643cd33',
|
|
54
|
+
native_exe_sha256:manifest.sha256,tests:specs,summary:counts,exit_code:result.status,timed_out:result.error?.code==='ETIMEDOUT',
|
|
55
|
+
stdout_sha256:hash(Buffer.from(stdout,'utf8')),stderr_sha256:hash(Buffer.from(stderr,'utf8'))};
|
|
56
|
+
console.log(JSON.stringify(receipt,null,2));
|
|
57
|
+
if(result.status!==0){console.error('TEST_OUTPUT_TAIL\n'+stdout.slice(-15000));console.error('TEST_ERROR_TAIL\n'+stderr.slice(-4000));}
|
|
58
|
+
if(!complete)process.exitCode=1;
|