deadbyte-mcp 0.15.29 → 0.15.31
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 +64 -48
- package/PROVENANCE.json +28 -18
- package/README.txt +18 -14
- package/RELEASE.json +35 -17
- package/SBOM.cdx.json +3 -3
- package/STABILIZATION-PROGRESS.md +60 -0
- package/TEST-MATRIX.json +52 -118
- 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-exec.obj +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/deadbyte-desktop-policy.json +5 -5
- package/controller/deadbyte-process-policy.json +4 -4
- package/package.json +5 -3
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/gate-windows-r53.ps1 +58 -0
- package/scripts/gate-windows-r54.ps1 +26 -0
- package/scripts/verify-r42-parent.mjs +1 -1
- package/scripts/verify-r43-parent.mjs +1 -1
- package/scripts/verify-r44-parent.mjs +1 -1
- package/scripts/verify-r45-parent.mjs +1 -1
- package/scripts/windows-gate-evidence-r53.mjs +102 -0
- package/scripts/windows-gate-evidence-r54.mjs +76 -0
- package/src/authority-smoke.mjs +1 -0
- package/src/deadbyte-cli.mjs +4 -2
- package/src/doctor-r45.mjs +2 -1
- package/src/machine-fs-smoke-client.mjs +1 -0
- package/src/mcp-contained-smoke-client.mjs +1 -0
- package/src/mcp-host-smoke-client.mjs +1 -0
- package/src/mcp-smoke-client.mjs +1 -0
- package/src/process-smoke-client.mjs +1 -0
- package/src/release-slot.mjs +5 -2
- package/src/remote-session-guardian.mjs +12 -4
- package/src/runtime-coherence.mjs +13 -4
- package/src/runtime-update.mjs +51 -13
- package/src/state-root.mjs +8 -0
- package/src/trust.mjs +2 -1
- package/src/tunnel-stdio-compat-smoke.mjs +1 -0
- package/src/version.mjs +1 -1
- package/test/commander-biff-reference-r48.test.mjs +1 -1
- package/test/r44-finalizer.test.mjs +4 -3
- package/test/r49-windows-gate.test.mjs +36 -27
- package/test/r52-release-contract.test.mjs +9 -3
- package/test/r53-release-contract.test.mjs +68 -0
- package/test/r54-offline-predecessor-self-exclusion.test.mjs +56 -0
- package/test/r54-release-contract.test.mjs +59 -0
- package/test/release-slot.test.mjs +2 -1
- package/test/skill-mcp-r44.test.mjs +3 -0
- package/test/stabilization-guardian.test.mjs +25 -0
- package/test/stabilization-parent-context.test.mjs +18 -0
- package/test/stabilization-powershell.test.mjs +26 -0
- package/test/stabilization-semver.test.mjs +23 -0
- package/test/stabilization-smoke-root.test.mjs +23 -0
- package/test/stabilization-state-root.test.mjs +60 -0
- package/test/stabilization-update-child-root.test.mjs +20 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { doctor } from '../src/doctor-r45.mjs';
|
|
7
|
+
|
|
8
|
+
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
9
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
10
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
11
|
+
const insist=(ok,message)=>{if(!ok)throw new Error(message);};
|
|
12
|
+
const pkg=JSON.parse(await readFile(path.join(root,'package.json'),'utf8'));
|
|
13
|
+
const release=JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8'));
|
|
14
|
+
const provenance=JSON.parse(await readFile(path.join(root,'PROVENANCE.json'),'utf8'));
|
|
15
|
+
const matrix=JSON.parse(await readFile(path.join(root,'TEST-MATRIX.json'),'utf8'));
|
|
16
|
+
const active=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
|
|
17
|
+
const manifestSha256=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
|
|
18
|
+
|
|
19
|
+
insist(pkg.version==='0.15.31'&&release.version===pkg.version&&release.train==='R54','R54 candidate identity mismatch');
|
|
20
|
+
insist(release.parent.version==='0.15.30','R54 source parent mismatch');
|
|
21
|
+
insist(release.parent.manifest_sha256==='ddf7c7338d9a371f5667d7b138695e7f9cfced553d14073ac5a44fd2fe0a0399','R54 source parent manifest mismatch');
|
|
22
|
+
insist(release.parent.tarball_sha256==='72881981bf76eb45527946b310caed8387943e2ca8e37ea6e606c32189dd5a70','R54 source parent tarball mismatch');
|
|
23
|
+
insist(
|
|
24
|
+
active.release_id===release.runtime_predecessor.release_id&&
|
|
25
|
+
active.manifest_sha256===release.runtime_predecessor.manifest_sha256&&
|
|
26
|
+
active.generation_manifest_sha256===release.runtime_predecessor.generation_manifest_sha256,
|
|
27
|
+
'active runtime is not exact sealed 0.15.29 predecessor'
|
|
28
|
+
);
|
|
29
|
+
insist(release.hotfix?.activation_ancestry_excluded===true,'R54 activation ancestry fix missing');
|
|
30
|
+
insist(release.hotfix?.unrelated_stale_runtime_fail_closed===true,'R54 stale-runtime negative control missing');
|
|
31
|
+
insist(provenance.operator_override?.stable_final_claim===false,'R54 must not claim stable/final');
|
|
32
|
+
insist(matrix.operator_override?.four_hour_soak==='NOT_COMPLETED','R54 matrix must preserve soak bypass');
|
|
33
|
+
|
|
34
|
+
const health=await doctor();
|
|
35
|
+
insist(health.status==='ok'&&health.package_version===pkg.version&&health.manifest?.verified===true,'R54 doctor evidence invalid');
|
|
36
|
+
|
|
37
|
+
const evidence={
|
|
38
|
+
schema:'deadbyte.r54-windows-gate.v1',
|
|
39
|
+
status:'passed',
|
|
40
|
+
exit_code:0,
|
|
41
|
+
version:pkg.version,
|
|
42
|
+
train:'R54',
|
|
43
|
+
completed_at_utc:new Date().toISOString(),
|
|
44
|
+
manifest_sha256:manifestSha256,
|
|
45
|
+
parent:release.parent,
|
|
46
|
+
runtime_predecessor:release.runtime_predecessor,
|
|
47
|
+
hotfix:release.hotfix,
|
|
48
|
+
operator_override:provenance.operator_override,
|
|
49
|
+
active_predecessor:{
|
|
50
|
+
release_id:active.release_id,
|
|
51
|
+
version:active.version,
|
|
52
|
+
manifest_sha256:active.manifest_sha256,
|
|
53
|
+
generation_manifest_sha256:active.generation_manifest_sha256
|
|
54
|
+
},
|
|
55
|
+
doctor:{verified_entries:health.manifest.verified_entries},
|
|
56
|
+
gates:{
|
|
57
|
+
r54_focused:true,
|
|
58
|
+
package_boundary:true,
|
|
59
|
+
full_regression:true,
|
|
60
|
+
manifest_final_verify:true,
|
|
61
|
+
four_hour_soak_completed:false
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const bytes=Buffer.from(JSON.stringify(evidence,null,2)+'\n');
|
|
65
|
+
const dir=path.join(stateRoot,'evidence','release');
|
|
66
|
+
await mkdir(dir,{recursive:true});
|
|
67
|
+
const target=path.join(dir,`r54-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
68
|
+
try{
|
|
69
|
+
const handle=await open(target,'wx',0o600);
|
|
70
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
71
|
+
}catch(error){
|
|
72
|
+
if(error?.code!=='EEXIST')throw error;
|
|
73
|
+
const prior=JSON.parse(await readFile(target,'utf8'));
|
|
74
|
+
insist(prior.schema===evidence.schema&&prior.status==='passed'&&prior.version===evidence.version&&prior.manifest_sha256===manifestSha256,'conflicting R54 gate evidence identity');
|
|
75
|
+
}
|
|
76
|
+
console.log(JSON.stringify({status:'passed',path:target,sha256:sha256(bytes),manifest_sha256:manifestSha256}));
|
package/src/authority-smoke.mjs
CHANGED
|
@@ -60,6 +60,7 @@ const transport = new StdioClientTransport({
|
|
|
60
60
|
cwd: repoRoot,
|
|
61
61
|
env: {
|
|
62
62
|
...getDefaultEnvironment(),
|
|
63
|
+
DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(trust,'state')),
|
|
63
64
|
DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
|
|
64
65
|
DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
|
|
65
66
|
DEADBYTE_VERIFY_KEY: keys.publicKeyPath
|
package/src/deadbyte-cli.mjs
CHANGED
|
@@ -16,6 +16,8 @@ import { printDoctor } from './doctor-r45.mjs';
|
|
|
16
16
|
import { createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine, selectLiveLogNames } from './remote-live-log.mjs';
|
|
17
17
|
import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
|
|
18
18
|
import { ensureRuntimeCoherence } from './runtime-coherence.mjs';
|
|
19
|
+
import { windowsPowerShellEnvironment } from './runtime-update.mjs';
|
|
20
|
+
import { resolveStateRoot } from './state-root.mjs';
|
|
19
21
|
|
|
20
22
|
const REMOTE_COMMANDS=new Set([
|
|
21
23
|
'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
|
|
@@ -42,7 +44,7 @@ const CURRENT_DEPLOY_SCRIPT=path.join(CURRENT_PACKAGE_ROOT,'scripts','deploy-r48
|
|
|
42
44
|
const REMOTE_UPDATE_TIMEOUT_MS=900000;
|
|
43
45
|
const LIVE_LOG_DISCOVERY_INTERVAL_MS=1000;
|
|
44
46
|
|
|
45
|
-
function stateRoot(){return
|
|
47
|
+
function stateRoot(){return resolveStateRoot();}
|
|
46
48
|
async function requireRegular(file,label){const info=await lstat(file);if(!info.isFile()||info.isSymbolicLink()) throw new Error(`${label} must be a regular non-symlink file`);return file;}
|
|
47
49
|
function printHelp(){
|
|
48
50
|
const exact=`deadbyte-mcp@${DEADBYTE_VERSION}`;
|
|
@@ -171,7 +173,7 @@ function invokeController(ctx,command,{capture=false,allowFailure=false,echoCapt
|
|
|
171
173
|
stderrFd=openSync(redirectOutput.stderr_path,'w');
|
|
172
174
|
stdio=['ignore',stdoutFd,stderrFd];
|
|
173
175
|
}else stdio=capture?['ignore','pipe','pipe']:'inherit';
|
|
174
|
-
const options={stdio,encoding:capture?'utf8':undefined,windowsHide:false,shell:false};
|
|
176
|
+
const options={stdio,encoding:capture?'utf8':undefined,windowsHide:false,shell:false,env:windowsPowerShellEnvironment()};
|
|
175
177
|
if(Number.isInteger(timeoutMs)&&timeoutMs>0) options.timeout=timeoutMs;
|
|
176
178
|
result=spawnSync(ctx.powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.launcher,command],options);
|
|
177
179
|
}finally{
|
package/src/doctor-r45.mjs
CHANGED
|
@@ -4,9 +4,10 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { DEADBYTE_VERSION, MCP_PROTOCOL_VERSION } from './version.mjs';
|
|
7
|
+
import { resolveStateRoot } from './state-root.mjs';
|
|
7
8
|
|
|
8
9
|
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
9
|
-
const stateRoot=()=>
|
|
10
|
+
const stateRoot=()=>resolveStateRoot();
|
|
10
11
|
const sha256=b=>createHash('sha256').update(b).digest('hex');
|
|
11
12
|
async function jsonOrNull(file){try{return JSON.parse((await readFile(file,'utf8')).replace(/^\uFEFF/,''));}catch(e){if(e?.code==='ENOENT')return null;throw e;}}
|
|
12
13
|
async function fileHash(file){return sha256(await readFile(file));}
|
|
@@ -52,6 +52,7 @@ const client = new Client({ name: 'deadbyte-machine-fs-smoke', version: '0.8.0'
|
|
|
52
52
|
const transport = new StdioClientTransport({
|
|
53
53
|
command: process.execPath, args: [serverPath], cwd: repoRoot,
|
|
54
54
|
env: { ...getDefaultEnvironment(), DEADBYTE_CODING_RUNTIME: '1', DEADBYTE_MACHINE_FS_RUNTIME: '1',
|
|
55
|
+
DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(scratch,'state')),
|
|
55
56
|
DEADBYTE_PRIVILEGED_POLICY: '',
|
|
56
57
|
DEADBYTE_CODING_POLICY: policyPath, DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
|
|
57
58
|
DEADBYTE_VERIFY_KEY: keys.publicKeyPath }, stderr: 'pipe'
|
|
@@ -34,6 +34,7 @@ const transport = new StdioClientTransport({
|
|
|
34
34
|
cwd: repoRoot,
|
|
35
35
|
env: {
|
|
36
36
|
...getDefaultEnvironment(),
|
|
37
|
+
DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(trust,'state')),
|
|
37
38
|
DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
|
|
38
39
|
DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
|
|
39
40
|
DEADBYTE_VERIFY_KEY: keys.publicKeyPath
|
|
@@ -66,6 +66,7 @@ const transport = new StdioClientTransport({
|
|
|
66
66
|
cwd: repoRoot,
|
|
67
67
|
env: {
|
|
68
68
|
...getDefaultEnvironment(),
|
|
69
|
+
DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(temp,'state')),
|
|
69
70
|
DEADBYTE_WORKSPACE_ROOT: path.join(repoRoot, 'workspaces'),
|
|
70
71
|
DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
|
|
71
72
|
DEADBYTE_VERIFY_KEY: keys.publicKeyPath,
|
package/src/mcp-smoke-client.mjs
CHANGED
|
@@ -37,6 +37,7 @@ const transport = new StdioClientTransport({
|
|
|
37
37
|
cwd: repoRoot,
|
|
38
38
|
env: {
|
|
39
39
|
...getDefaultEnvironment(),
|
|
40
|
+
DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(smokeTrust,'state')),
|
|
40
41
|
DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
|
|
41
42
|
DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
|
|
42
43
|
DEADBYTE_VERIFY_KEY: keys.publicKeyPath,
|
|
@@ -39,6 +39,7 @@ const client = new Client({ name: 'deadbyte-process-smoke', version: '0.8.0' },
|
|
|
39
39
|
const transport = new StdioClientTransport({
|
|
40
40
|
command: process.execPath, args: [serverPath], cwd: repoRoot,
|
|
41
41
|
env: { ...getDefaultEnvironment(), DEADBYTE_PRIVILEGED_POLICY: '', DEADBYTE_MACHINE_RUNTIME:'0', DEADBYTE_AGENT_RUNTIME:'0',
|
|
42
|
+
DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(smokeTrust,'state')),
|
|
42
43
|
DEADBYTE_PROCESS_RUNTIME: '1', DEADBYTE_PROCESS_POLICY: smokePolicyPath, DEADBYTE_CODING_RUNTIME:'0', DEADBYTE_MACHINE_FS_RUNTIME:'0',
|
|
43
44
|
DEADBYTE_OBSERVATION_RUNTIME:'0', DEADBYTE_DESKTOP_RUNTIME:'0', DEADBYTE_AUTONOMOUS_RUNTIME:'0', DEADBYTE_AUDIT_RUNTIME:'0',
|
|
44
45
|
DEADBYTE_CAPABILITY_PROFILE:'full', DEADBYTE_SIGNING_KEY: keys.privateKeyPath, DEADBYTE_VERIFY_KEY: keys.publicKeyPath },
|
package/src/release-slot.mjs
CHANGED
|
@@ -195,7 +195,8 @@ export function renderStableLauncherCmd(){
|
|
|
195
195
|
return [
|
|
196
196
|
'@echo off',
|
|
197
197
|
'setlocal EnableExtensions DisableDelayedExpansion',
|
|
198
|
-
'
|
|
198
|
+
'if not defined DEADBYTE_STATE_ROOT set "DEADBYTE_STATE_ROOT=%USERPROFILE%\\.deadbyte-mcp"',
|
|
199
|
+
'powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%DEADBYTE_STATE_ROOT%\\bin\\DEADBYTE-MCP.ps1" %*',
|
|
199
200
|
'exit /b %ERRORLEVEL%',
|
|
200
201
|
''
|
|
201
202
|
].join('\r\n');
|
|
@@ -208,7 +209,9 @@ export function renderStableLauncherPs1(){
|
|
|
208
209
|
'param([Parameter(ValueFromRemainingArguments=$true)][string[]]$ControllerArgs)',
|
|
209
210
|
'Set-StrictMode -Version 3.0',
|
|
210
211
|
"$ErrorActionPreference = 'Stop'",
|
|
211
|
-
"$StateRoot = Join-Path $env:USERPROFILE '.deadbyte-mcp'",
|
|
212
|
+
"$StateRoot = if ([string]::IsNullOrEmpty($env:DEADBYTE_STATE_ROOT)) { Join-Path $env:USERPROFILE '.deadbyte-mcp' } else { $env:DEADBYTE_STATE_ROOT }",
|
|
213
|
+
'$StateRoot = [IO.Path]::GetFullPath($StateRoot)',
|
|
214
|
+
'$env:DEADBYTE_STATE_ROOT = $StateRoot',
|
|
212
215
|
"$PointerPath = Join-Path $StateRoot 'active-release.json'",
|
|
213
216
|
"function Fail([string]$m) { throw ('DEADBYTE launcher: ' + $m) }",
|
|
214
217
|
"if (-not (Test-Path -LiteralPath $PointerPath -PathType Leaf)) { Fail 'active release pointer missing' }",
|
|
@@ -4,6 +4,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
7
|
+
import { windowsPowerShellEnvironment } from './runtime-update.mjs';
|
|
8
|
+
import { resolveStateRoot } from './state-root.mjs';
|
|
7
9
|
|
|
8
10
|
const CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','park'];
|
|
9
11
|
|
|
@@ -36,9 +38,9 @@ function markerMatches(marker,{ownerPid,nonce}){
|
|
|
36
38
|
marker?.owner_pid===ownerPid&&
|
|
37
39
|
marker?.session_nonce===nonce;
|
|
38
40
|
}
|
|
39
|
-
function invokeCleanup(powershell,launcher,command){
|
|
41
|
+
function invokeCleanup(powershell,launcher,command,stateRoot){
|
|
40
42
|
const result=spawnSync(powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,command],{
|
|
41
|
-
stdio:'ignore',windowsHide:true,shell:false
|
|
43
|
+
stdio:'ignore',windowsHide:true,shell:false,env:windowsPowerShellEnvironment({...process.env,DEADBYTE_STATE_ROOT:stateRoot}),timeout:30000
|
|
42
44
|
});
|
|
43
45
|
return !result.error&&result.status===0;
|
|
44
46
|
}
|
|
@@ -46,7 +48,7 @@ function invokeCleanup(powershell,launcher,command){
|
|
|
46
48
|
async function main(){
|
|
47
49
|
if(process.platform!=='win32') return;
|
|
48
50
|
const args=parseArgs(process.argv.slice(2));
|
|
49
|
-
const root=
|
|
51
|
+
const root=resolveStateRoot(args['state-root']);
|
|
50
52
|
const ownerPid=Number(args['owner-pid']);
|
|
51
53
|
const nonce=String(args['session-nonce']||'');
|
|
52
54
|
if(!Number.isInteger(ownerPid)||ownerPid<1) throw new Error('guardian owner pid invalid');
|
|
@@ -66,7 +68,13 @@ async function main(){
|
|
|
66
68
|
continue;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
const failed=[];
|
|
72
|
+
for(const command of CLEANUP){
|
|
73
|
+
const latest=await readMarker(markerPath);
|
|
74
|
+
if(!latest||!markerMatches(latest,{ownerPid,nonce})) return;
|
|
75
|
+
if(!invokeCleanup(powershell,launcher,command,root)) failed.push(command);
|
|
76
|
+
}
|
|
77
|
+
if(failed.length) throw new Error('guardian cleanup failed; ownership marker retained: '+failed.join(','));
|
|
70
78
|
const current=await readMarker(markerPath);
|
|
71
79
|
if(current&&markerMatches(current,{ownerPid,nonce})) await rm(markerPath,{force:true});
|
|
72
80
|
return;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const SEMVER=/^(
|
|
1
|
+
const SEMVER=/^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
2
2
|
const HEX64=/^[0-9a-f]{64}$/;
|
|
3
3
|
|
|
4
4
|
function parseSemver(value){
|
|
5
5
|
const match=SEMVER.exec(String(value??''));
|
|
6
|
-
if(!match) throw new Error(`runtime version invalid: ${String(value)}`);
|
|
7
|
-
return {major:
|
|
6
|
+
if(!match||match[0]!==String(value??'')||(match[4]??'').split('.').some(id=>/^0[0-9]+$/.test(id))) throw new Error(`runtime version invalid: ${String(value)}`);
|
|
7
|
+
return {major:BigInt(match[1]),minor:BigInt(match[2]),patch:BigInt(match[3]),prerelease:match[4]??null};
|
|
8
8
|
}
|
|
9
9
|
export function compareRuntimeVersions(a,b){
|
|
10
10
|
const left=parseSemver(a),right=parseSemver(b);
|
|
@@ -12,7 +12,16 @@ export function compareRuntimeVersions(a,b){
|
|
|
12
12
|
if(left.prerelease===right.prerelease) return 0;
|
|
13
13
|
if(left.prerelease===null) return 1;
|
|
14
14
|
if(right.prerelease===null) return -1;
|
|
15
|
-
|
|
15
|
+
const aIds=left.prerelease.split('.'),bIds=right.prerelease.split('.');
|
|
16
|
+
for(let i=0;i<Math.min(aIds.length,bIds.length);i++){
|
|
17
|
+
const x=aIds[i],y=bIds[i];
|
|
18
|
+
if(x===y) continue;
|
|
19
|
+
const xn=/^[0-9]+$/.test(x),yn=/^[0-9]+$/.test(y);
|
|
20
|
+
if(xn&&yn) return BigInt(x)<BigInt(y)?-1:1;
|
|
21
|
+
if(xn!==yn) return xn?-1:1;
|
|
22
|
+
return x<y?-1:1;
|
|
23
|
+
}
|
|
24
|
+
return aIds.length<bIds.length?-1:aIds.length>bIds.length?1:0;
|
|
16
25
|
}
|
|
17
26
|
function assertManifest(value,label){
|
|
18
27
|
if(!HEX64.test(String(value??''))) throw new Error(`${label} must be lowercase SHA-256`);
|
package/src/runtime-update.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
verifyManifestTree
|
|
18
18
|
} from './release-slot.mjs';
|
|
19
19
|
import { createGenerationBundle, verifyGenerationBundle } from './release-generation.mjs';
|
|
20
|
+
import { resolveStateRoot } from './state-root.mjs';
|
|
20
21
|
|
|
21
22
|
const HEX64=/^[0-9a-f]{64}$/;
|
|
22
23
|
const SEMVER=/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -25,7 +26,7 @@ const UPDATE_SCHEMA='deadbyte.r27-slot-update-receipt.v1';
|
|
|
25
26
|
const HANDOFF_SCHEMA='deadbyte.r27-slot-update-handoff.v1';
|
|
26
27
|
const FAILURE_SCHEMA='deadbyte.r27-slot-update-failure.v1';
|
|
27
28
|
const ROLLBACK_SCHEMA='deadbyte.r31-slot-rollback-receipt.v1';
|
|
28
|
-
const DEFAULT_STATE_ROOT=
|
|
29
|
+
const DEFAULT_STATE_ROOT=undefined;
|
|
29
30
|
const TOP_LAUNCHER=path.join(os.homedir(),'Downloads','DEADBYTE-MCP.cmd');
|
|
30
31
|
const RUNTIME_STABILITY_MS=12000;
|
|
31
32
|
const RUNTIME_STABILITY_POLL_MS=500;
|
|
@@ -195,11 +196,21 @@ export function windowsPowerShellEnvironment(base=process.env){
|
|
|
195
196
|
const systemRoot=base.SystemRoot||base.SYSTEMROOT||'C:\\Windows';
|
|
196
197
|
const userProfile=base.USERPROFILE||os.homedir();
|
|
197
198
|
const programFiles=base.ProgramFiles||base.PROGRAMFILES||'C:\\Program Files';
|
|
198
|
-
|
|
199
|
+
const environment={...base};
|
|
200
|
+
const inherited=Object.entries(base).filter(([key])=>key.toLowerCase()==='psmodulepath').flatMap(([,value])=>String(value??'').split(';')).filter(Boolean);
|
|
201
|
+
for(const key of Object.keys(environment)) if(key.toLowerCase()==='psmodulepath') delete environment[key];
|
|
202
|
+
const modules=[
|
|
203
|
+
path.join(systemRoot,'System32','WindowsPowerShell','v1.0','Modules'),
|
|
199
204
|
path.join(userProfile,'Documents','WindowsPowerShell','Modules'),
|
|
200
205
|
path.join(programFiles,'WindowsPowerShell','Modules'),
|
|
201
|
-
|
|
202
|
-
]
|
|
206
|
+
...inherited
|
|
207
|
+
];
|
|
208
|
+
const seen=new Set();
|
|
209
|
+
return {...environment,PSModulePath:modules.filter(modulePath=>{
|
|
210
|
+
const key=modulePath.toLowerCase();
|
|
211
|
+
if(seen.has(key)) return false;
|
|
212
|
+
seen.add(key);return true;
|
|
213
|
+
}).join(';')};
|
|
203
214
|
}
|
|
204
215
|
function commandEnvironment(exe,base=process.env){
|
|
205
216
|
return path.resolve(exe).toLowerCase()===path.resolve(powershellPath()).toLowerCase()
|
|
@@ -582,7 +593,7 @@ function controllerInvocation(ctx,command){
|
|
|
582
593
|
exe:powershellPath(),
|
|
583
594
|
args:['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.controller_path,command],
|
|
584
595
|
cwd:ctx.cwd,
|
|
585
|
-
env:{...process.env,DEADBYTE_CONTROLLER_CONFIG:ctx.pointer.config_path,DEADBYTE_CONTROLLER_RUNTIME_ROOT:ctx.runtime_root}
|
|
596
|
+
env:{...process.env,DEADBYTE_STATE_ROOT:resolveStateRoot(ctx.state_root),DEADBYTE_CONTROLLER_CONFIG:ctx.pointer.config_path,DEADBYTE_CONTROLLER_RUNTIME_ROOT:ctx.runtime_root}
|
|
586
597
|
};
|
|
587
598
|
return {exe:powershellPath(),args:['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.controller_path,command],cwd:ctx.cwd};
|
|
588
599
|
}
|
|
@@ -636,6 +647,35 @@ async function readLedger(ctx,name){
|
|
|
636
647
|
function sameGeneration(a,b){
|
|
637
648
|
return a.pid===b.pid&&a.instance_nonce===b.instance_nonce&&a.creation_time_utc===b.creation_time_utc&&a.image_sha256===b.image_sha256;
|
|
638
649
|
}
|
|
650
|
+
export function selectOfflineRecoveryBlockers(processes,currentPid=process.pid){
|
|
651
|
+
const list=(Array.isArray(processes)?processes:[processes]).filter(Boolean);
|
|
652
|
+
const byPid=new Map();
|
|
653
|
+
for(const item of list){
|
|
654
|
+
const pid=Number(item?.ProcessId);
|
|
655
|
+
if(Number.isInteger(pid)&&pid>0) byPid.set(pid,item);
|
|
656
|
+
}
|
|
657
|
+
const selfLineage=new Set();
|
|
658
|
+
let cursor=Number(currentPid);
|
|
659
|
+
while(Number.isInteger(cursor)&&cursor>0&&!selfLineage.has(cursor)){
|
|
660
|
+
selfLineage.add(cursor);
|
|
661
|
+
const item=byPid.get(cursor);
|
|
662
|
+
if(!item) break;
|
|
663
|
+
const parent=Number(item?.ParentProcessId);
|
|
664
|
+
if(!Number.isInteger(parent)||parent<1||parent===cursor) break;
|
|
665
|
+
cursor=parent;
|
|
666
|
+
}
|
|
667
|
+
const runtimeNames=new Set(['node.exe','cloudflared.exe','deadbyte-tunnel-host.exe']);
|
|
668
|
+
return list.filter(item=>{
|
|
669
|
+
const pid=Number(item?.ProcessId);
|
|
670
|
+
if(selfLineage.has(pid)) return false;
|
|
671
|
+
const name=String(item?.Name??'').toLowerCase();
|
|
672
|
+
if(!runtimeNames.has(name)) return false;
|
|
673
|
+
const commandLine=String(item?.CommandLine??'').toLowerCase();
|
|
674
|
+
return commandLine.includes('deadbyte-mcp')||commandLine.includes('mcp-server.mjs')||
|
|
675
|
+
commandLine.includes('runtime-self-update.mjs')||commandLine.includes('remote-session-guardian.mjs')||
|
|
676
|
+
commandLine.includes('deadbyte-tunnel-host.exe');
|
|
677
|
+
});
|
|
678
|
+
}
|
|
639
679
|
function cleanOfflinePredecessor(ctx){
|
|
640
680
|
const runtimeRoot=path.resolve(ctx.runtime_root);
|
|
641
681
|
const blockers=[
|
|
@@ -645,19 +685,13 @@ function cleanOfflinePredecessor(ctx){
|
|
|
645
685
|
].filter(name=>existsSync(path.join(runtimeRoot,name)));
|
|
646
686
|
if(blockers.length) throw new Error('offline predecessor recovery blocked by runtime state: '+blockers.join(','));
|
|
647
687
|
|
|
648
|
-
const command="Get-CimInstance Win32_Process |
|
|
688
|
+
const command="Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,ExecutablePath,CommandLine | ConvertTo-Json -Compress";
|
|
649
689
|
const result=runCaptured(powershellPath(),['-NoLogo','-NoProfile','-Command',command],{timeoutMs:15000});
|
|
650
690
|
const text=assertRun(result,'runtime process query').trim();
|
|
651
691
|
if(text){
|
|
652
692
|
let parsed;
|
|
653
693
|
try { parsed=JSON.parse(text); } catch(error){ throw new Error('runtime process query JSON invalid: '+error.message); }
|
|
654
|
-
const
|
|
655
|
-
const relevant=processes.filter(item=>{
|
|
656
|
-
const commandLine=String(item?.CommandLine??'').toLowerCase();
|
|
657
|
-
return commandLine.includes('deadbyte-mcp')||commandLine.includes('mcp-server.mjs')||
|
|
658
|
-
commandLine.includes('runtime-self-update.mjs')||commandLine.includes('remote-session-guardian.mjs')||
|
|
659
|
-
commandLine.includes('deadbyte-tunnel-host.exe');
|
|
660
|
-
});
|
|
694
|
+
const relevant=selectOfflineRecoveryBlockers(Array.isArray(parsed)?parsed:[parsed],process.pid);
|
|
661
695
|
if(relevant.length) throw new Error('offline predecessor recovery blocked by DEADBYTE runtime processes: '+relevant.map(item=>String(item?.Name??'unknown')+'#'+String(item?.ProcessId??'?')).join(','));
|
|
662
696
|
}
|
|
663
697
|
return {
|
|
@@ -1411,6 +1445,7 @@ async function resumeSlotUpdate({handoffPath}){
|
|
|
1411
1445
|
}
|
|
1412
1446
|
}
|
|
1413
1447
|
export async function recoverInterruptedSlotUpdates({stateRoot=DEFAULT_STATE_ROOT}={}){
|
|
1448
|
+
stateRoot=resolveStateRoot(stateRoot);
|
|
1414
1449
|
const evidenceRoot=path.join(path.resolve(stateRoot),'evidence','updates');
|
|
1415
1450
|
if(!existsSync(evidenceRoot)) return {ok:true,launched:0,updates:[]};
|
|
1416
1451
|
const entries=await readdir(evidenceRoot,{withFileTypes:true});
|
|
@@ -1443,6 +1478,7 @@ export async function recoverInterruptedSlotUpdates({stateRoot=DEFAULT_STATE_ROO
|
|
|
1443
1478
|
}
|
|
1444
1479
|
|
|
1445
1480
|
export async function performSlotUpdate({stage,developmentRoot,expectedVersion,expectedManifest,sourceRoot=null,stateRoot=DEFAULT_STATE_ROOT,restoreStandingGrants=false,recoverOfflinePredecessor=false,cloudTransport=null,bridgeBootstrap=null,processPolicyBootstrap=null,hostPolicyBootstrap=null,codingPolicyBootstrap=null,desktopPolicyBootstrap=null,semanticLspBootstrap=null,enableMachineFs=false,enableObservation=false,enableDesktop=false,enableAutonomousSupervisor=false,hostCommandPins=[]}){
|
|
1481
|
+
stateRoot=resolveStateRoot(stateRoot);
|
|
1446
1482
|
if(process.platform!=='win32') throw new Error('R27 slot update is Windows-only');
|
|
1447
1483
|
const state=path.resolve(stateRoot),stageRoot=path.resolve(stage),development=assertDevelopmentRootSafe(developmentRoot,state);
|
|
1448
1484
|
const key=await loadUpdateKey(state),updateId='r27-'+expectedManifest.slice(0,16)+'-'+Date.now().toString(36)+'-'+randomBytes(4).toString('hex');
|
|
@@ -1527,6 +1563,7 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
|
|
|
1527
1563
|
}
|
|
1528
1564
|
|
|
1529
1565
|
export async function performSlotRollback({activationReceipt,stateRoot=DEFAULT_STATE_ROOT}){
|
|
1566
|
+
stateRoot=resolveStateRoot(stateRoot);
|
|
1530
1567
|
if(process.platform!=='win32') throw new Error('R31 slot rollback is Windows-only');
|
|
1531
1568
|
const state=path.resolve(stateRoot),receiptFile=path.resolve(activationReceipt);
|
|
1532
1569
|
const key=await loadUpdateKey(state);
|
|
@@ -1578,6 +1615,7 @@ export async function performSlotRollback({activationReceipt,stateRoot=DEFAULT_S
|
|
|
1578
1615
|
}
|
|
1579
1616
|
|
|
1580
1617
|
export async function runSlotUpdateCli(argv=process.argv.slice(2),{stateRoot=DEFAULT_STATE_ROOT}={}){
|
|
1618
|
+
stateRoot=resolveStateRoot(stateRoot);
|
|
1581
1619
|
if(argv.includes('--rollback-receipt')){
|
|
1582
1620
|
const args=parseRollbackArgs(argv);
|
|
1583
1621
|
const result=await performSlotRollback({activationReceipt:args.activationReceipt,stateRoot});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export function resolveStateRoot(explicit,environment=process.env){
|
|
5
|
+
const value=explicit===undefined?(environment.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')):explicit;
|
|
6
|
+
if(typeof value!=='string'||value.length===0||value.includes('\0')) throw new Error('state root invalid');
|
|
7
|
+
return path.resolve(value);
|
|
8
|
+
}
|
package/src/trust.mjs
CHANGED
|
@@ -9,11 +9,12 @@ import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
|
|
|
9
9
|
import os from 'node:os';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { sha256Bytes } from './hash.mjs';
|
|
12
|
+
import { resolveStateRoot } from './state-root.mjs';
|
|
12
13
|
|
|
13
14
|
const DOMAIN = Buffer.from('DEADBYTE-RECEIPT-V1\0', 'utf8');
|
|
14
15
|
|
|
15
16
|
export function defaultTrustDir() {
|
|
16
|
-
return path.join(
|
|
17
|
+
return path.join(resolveStateRoot(), 'trust');
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function defaultPrivateKeyPath() {
|
|
@@ -101,6 +101,7 @@ async function modernPassThrough() {
|
|
|
101
101
|
cwd: repoRoot,
|
|
102
102
|
env: {
|
|
103
103
|
...getDefaultEnvironment(),
|
|
104
|
+
...(process.env.DEADBYTE_STATE_ROOT?{DEADBYTE_STATE_ROOT:path.resolve(process.env.DEADBYTE_STATE_ROOT)}:{}),
|
|
104
105
|
DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
|
|
105
106
|
DEADBYTE_PRIVILEGED_POLICY: '',
|
|
106
107
|
DEADBYTE_TUNNEL_STDIO_PROBE_COMPAT: '1'
|
package/src/version.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import {mkdtemp,readFile,rm,writeFile} from 'node:fs/promises';
|
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
|
|
9
|
-
const oracleRoot='C:\\Users\\DEADBYTE\\.deadbyte-mcp\\evidence\\commander-biff-oracle';
|
|
9
|
+
const oracleRoot=process.env.DEADBYTE_BIFF_TEST_ORACLE||'C:\\Users\\DEADBYTE\\.deadbyte-mcp\\evidence\\commander-biff-oracle';
|
|
10
10
|
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
11
11
|
|
|
12
12
|
test('pinned reference rejects a real BIFF8 workbook and writes OOXML bytes under .xls',async()=>{
|
|
@@ -34,15 +34,16 @@ test('R44 detached finalizer accepts only exact signed-R43-parent feature gate e
|
|
|
34
34
|
assert.throws(()=>validateWindowsGateEvidence({...valid(),features:{...features(),skill_integrity:false}},manifest,parent),/skill_integrity/);
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
test('R44 detached finalizer
|
|
37
|
+
test('R44 detached finalizer prioritizes native Windows modules and preserves custom paths',()=>{
|
|
38
38
|
const env=windowsPowerShellEnvironment({
|
|
39
39
|
SystemRoot:'C:\\Windows',USERPROFILE:'C:\\Users\\operator',ProgramFiles:'C:\\Program Files',
|
|
40
40
|
PSModulePath:'C:\\poisoned-runtime-modules'
|
|
41
41
|
});
|
|
42
42
|
assert.equal(env.PSModulePath,[
|
|
43
|
+
'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules',
|
|
43
44
|
'C:\\Users\\operator\\Documents\\WindowsPowerShell\\Modules',
|
|
44
45
|
'C:\\Program Files\\WindowsPowerShell\\Modules',
|
|
45
|
-
'C:\\
|
|
46
|
+
'C:\\poisoned-runtime-modules'
|
|
46
47
|
].join(';'));
|
|
47
|
-
assert.
|
|
48
|
+
assert.equal(env.PSModulePath.split(';')[0],'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules');
|
|
48
49
|
});
|
|
@@ -2,11 +2,15 @@ import assert from 'node:assert/strict';
|
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import test from 'node:test';
|
|
4
4
|
|
|
5
|
-
const read
|
|
5
|
+
const read=path=>readFile(new URL(`../${path}`,import.meta.url),'utf8');
|
|
6
6
|
|
|
7
|
-
test('
|
|
8
|
-
const [pkgText,
|
|
7
|
+
test('R54 canonical Windows gate is successor-bound and preserves R53/R52/R51/R50/R49 historical gates',async()=>{
|
|
8
|
+
const [pkgText,gate,evidence,historicalR53,historicalEvidenceR53,historicalR52,historicalEvidenceR52,historicalR51,historicalEvidenceR51,historicalR50,historicalR49,deferred]=await Promise.all([
|
|
9
9
|
read('package.json'),
|
|
10
|
+
read('scripts/gate-windows-r54.ps1'),
|
|
11
|
+
read('scripts/windows-gate-evidence-r54.mjs'),
|
|
12
|
+
read('scripts/gate-windows-r53.ps1'),
|
|
13
|
+
read('scripts/windows-gate-evidence-r53.mjs'),
|
|
10
14
|
read('scripts/gate-windows-r52.ps1'),
|
|
11
15
|
read('scripts/windows-gate-evidence-r52.mjs'),
|
|
12
16
|
read('scripts/gate-windows-r51.ps1'),
|
|
@@ -15,28 +19,33 @@ test('R52 canonical Windows gate is successor-bound and preserves R51/R50/R49 hi
|
|
|
15
19
|
read('scripts/gate-windows-r49.ps1'),
|
|
16
20
|
read('scripts/deferred-slot-operation-r46.mjs')
|
|
17
21
|
]);
|
|
18
|
-
const pkg
|
|
19
|
-
assert.match(pkg.scripts['gate:windows']
|
|
20
|
-
assert.match(gate
|
|
21
|
-
assert.match(gate
|
|
22
|
-
assert.match(gate
|
|
23
|
-
assert.match(gate
|
|
24
|
-
assert.match(gate
|
|
25
|
-
assert.match(gate
|
|
26
|
-
assert.match(evidence
|
|
27
|
-
assert.match(evidence
|
|
28
|
-
assert.match(evidence
|
|
29
|
-
assert.match(evidence
|
|
30
|
-
assert.match(
|
|
31
|
-
assert.match(
|
|
32
|
-
assert.match(
|
|
33
|
-
assert.match(
|
|
34
|
-
assert.match(
|
|
35
|
-
assert.match(
|
|
36
|
-
assert.match(
|
|
37
|
-
assert.match(
|
|
38
|
-
assert.match(
|
|
39
|
-
assert.match(deferred
|
|
40
|
-
assert.match(deferred,
|
|
41
|
-
assert.
|
|
22
|
+
const pkg=JSON.parse(pkgText);
|
|
23
|
+
assert.match(pkg.scripts['gate:windows'],/gate-windows-r54\.ps1$/);
|
|
24
|
+
assert.match(gate,/DEADBYTE 0\.15\.31 \/ R54 WINDOWS GATE: PASS/);
|
|
25
|
+
assert.match(gate,/r54-package-boundary/);
|
|
26
|
+
assert.match(gate,/r54-focused-contracts/);
|
|
27
|
+
assert.match(gate,/full-regression/);
|
|
28
|
+
assert.match(gate,/manifest-verify/);
|
|
29
|
+
assert.match(gate,/doctor/);
|
|
30
|
+
assert.match(evidence,/deadbyte\.r54-windows-gate\.v1/);
|
|
31
|
+
assert.match(evidence,/pkg\.version==='0\.15\.31'/);
|
|
32
|
+
assert.match(evidence,/release\.train==='R54'/);
|
|
33
|
+
assert.match(evidence,/release\.parent\.version==='0\.15\.30'/);
|
|
34
|
+
assert.match(evidence,/four_hour_soak_completed:false/);
|
|
35
|
+
assert.match(historicalR53,/DEADBYTE 0\.15\.30 \/ R53 WINDOWS GATE: PASS/);
|
|
36
|
+
assert.match(historicalEvidenceR53,/deadbyte\.r53-windows-gate\.v1/);
|
|
37
|
+
assert.match(historicalR52,/DEADBYTE 0\.15\.29 \/ R52 WINDOWS GATE: PASS/);
|
|
38
|
+
assert.match(historicalEvidenceR52,/deadbyte\.r52-windows-gate\.v1/);
|
|
39
|
+
assert.match(historicalR51,/DEADBYTE 0\.15\.28 \/ R51 WINDOWS GATE: PASS/);
|
|
40
|
+
assert.match(historicalEvidenceR51,/deadbyte\.r51-windows-gate\.v1/);
|
|
41
|
+
assert.match(historicalR50,/DEADBYTE 0\.15\.27 \/ R50 WINDOWS GATE: PASS/);
|
|
42
|
+
assert.match(historicalR49,/DEADBYTE 0\.15\.26 \/ R49 WINDOWS GATE: PASS/);
|
|
43
|
+
assert.match(deferred,/const expectedVersion=packageIdentity\.version/);
|
|
44
|
+
assert.match(deferred,/'--expected-version',expectedVersion/);
|
|
45
|
+
assert.match(deferred,/async function awaitHandoffCompletion/);
|
|
46
|
+
assert.match(deferred,/verifySlotUpdateEnvelope\(envelope,publicKey\)/);
|
|
47
|
+
assert.match(deferred,/active pointer does not match signed activation receipt/);
|
|
48
|
+
assert.match(deferred,/failure-receipt\.json/);
|
|
49
|
+
assert.match(deferred,/await awaitHandoffCompletion\(await runSlotUpdateCli/);
|
|
50
|
+
assert.doesNotMatch(deferred,/'--expected-version','0\.15\.5'/);
|
|
42
51
|
});
|
|
@@ -5,8 +5,12 @@ import { readFile } from 'node:fs/promises';
|
|
|
5
5
|
const root=new URL('../',import.meta.url);
|
|
6
6
|
const read=rel=>readFile(new URL(rel,root),'utf8');
|
|
7
7
|
const json=rel=>read(rel).then(JSON.parse);
|
|
8
|
+
const current=await json('package.json');
|
|
9
|
+
const historical=current.version!=='0.15.29';
|
|
10
|
+
const skipHistorical=t=>{if(historical){t.skip('R52 historical release contract is frozen at 0.15.29; current successor is '+current.version);return true;}return false;};
|
|
8
11
|
|
|
9
|
-
test('R52 current release identity is coherent across package lock source metadata SBOM and gate',async
|
|
12
|
+
test('R52 current release identity is coherent across package lock source metadata SBOM and gate',async t=>{
|
|
13
|
+
if(skipHistorical(t))return;
|
|
10
14
|
const [pkg,lock,release,provenance,matrix,sbom,versionSource]=await Promise.all([
|
|
11
15
|
json('package.json'),json('package-lock.json'),json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json'),read('src/version.mjs')
|
|
12
16
|
]);
|
|
@@ -25,7 +29,8 @@ test('R52 current release identity is coherent across package lock source metada
|
|
|
25
29
|
assert.match(pkg.scripts['r52:focused:test'],/r52-offline-predecessor-recovery\.test\.mjs/);
|
|
26
30
|
});
|
|
27
31
|
|
|
28
|
-
test('R52 lineage binds published 0.15.28 parent separately from 0.15.27 runtime rollback target',async
|
|
32
|
+
test('R52 lineage binds published 0.15.28 parent separately from 0.15.27 runtime rollback target',async t=>{
|
|
33
|
+
if(skipHistorical(t))return;
|
|
29
34
|
const [release,provenance]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json')]);
|
|
30
35
|
const activeParent={
|
|
31
36
|
release_id:'v0.15.27-1b1dbe24b45ad06a',
|
|
@@ -52,7 +57,8 @@ test('R52 lineage binds published 0.15.28 parent separately from 0.15.27 runtime
|
|
|
52
57
|
assert.equal(provenance.source_predecessor_activation_failure_receipt_sha256,'090f165c96da0d5a8e9c0815fd389fe94e8fe36c23a6878ee825b8f46bfb0bc2');
|
|
53
58
|
});
|
|
54
59
|
|
|
55
|
-
test('R52 advertises updater-owned offline predecessor Host revocation only after implementation',async
|
|
60
|
+
test('R52 advertises updater-owned offline predecessor Host revocation only after implementation',async t=>{
|
|
61
|
+
if(skipHistorical(t))return;
|
|
56
62
|
const release=await json('RELEASE.json');
|
|
57
63
|
assert.equal(release.capabilities.offline_predecessor_host_revoke_before_capture,true);
|
|
58
64
|
});
|