deadbyte-mcp 0.15.61 → 0.15.63
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 +81 -32
- package/PROVENANCE.json +38 -11
- package/README.txt +18 -18
- package/RELEASE.json +59 -15
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +31 -2
- package/bridge/package.json +1 -1
- package/controller/deadbyte-controller.ps1 +78 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R156-LOOP-AND-EPOCH-ROUTING.md +86 -0
- package/docs/R157-ARCHITECTURE-DEPENDENCIES.md +58 -0
- package/docs/R157-ARCHITECTURE-GRAPH.json +2874 -0
- package/docs/R157-ENGINEERING-HANDOFF.md +71 -0
- package/docs/R157-FILE-DELTA.json +80 -0
- package/docs/R157-FOCUSED-VERIFICATION.log +0 -0
- package/docs/R157-REFERENCE-CAPABILITY-MATRIX.md +207 -0
- package/docs/R157-REFERENCE-DECISIONS.md +46 -0
- package/docs/R157-REFERENCE-INVENTORY.json +2849 -0
- package/docs/R158-COMBINED-REGRESSION.log +0 -0
- package/docs/R158-DELTA.json +80 -0
- package/docs/R158-EPOCH-BOOTSTRAP-GREEN.log +0 -0
- package/docs/R158-EPOCH-BOOTSTRAP-RED.log +0 -0
- package/docs/R158-EPOCH-IDENTITY-RED.log +0 -0
- package/docs/R158-FOCUSED-GREEN.log +0 -0
- package/docs/R158-PROTECTED-EPOCH-VERIFIED.md +43 -0
- package/docs/R158-PROTECTED-EXACT-SOURCE-LIVE.log +0 -0
- package/docs/R158-UPDATER-REGRESSION.log +0 -0
- package/docs/R159-COMPLETED-HISTORY-BACKUP/deadbyte-cli.mjs.before +965 -0
- package/docs/R159-COMPLETED-HISTORY-BACKUP/legacy-marker-evidence-r159.mjs.before +102 -0
- package/docs/R159-COMPLETED-HISTORY-BACKUP/r159-legacy-marker-evidence.test.mjs.before +79 -0
- package/docs/R159-COMPLETED-HISTORY-PATCH.json +26 -0
- package/docs/R159-MARKER-LOCATOR-CHECKPOINT.md +46 -0
- package/docs/R160-ENGINEERING-HANDOFF.md +32 -0
- package/docs/R160-REFERENCE-CAPABILITY-MATRIX.json +165 -0
- package/package.json +2 -2
- package/scripts/gate-windows-r160.ps1 +57 -0
- package/scripts/npm-release-r48.mjs +10 -0
- package/scripts/npm-release-readiness-r163.mjs +67 -0
- package/scripts/npm-release-readiness-r165.mjs +68 -0
- package/src/autonomous-supervisor.mjs +21 -4
- package/src/deadbyte-cli.mjs +90 -50
- package/src/legacy-marker-evidence-r159.mjs +128 -0
- package/src/package-identity-r160.mjs +61 -0
- package/src/release-generation.mjs +13 -0
- package/src/release-slot.mjs +2 -1
- package/src/remote-live-log.mjs +30 -2
- package/src/runtime-update.mjs +18 -6
- package/src/trusted-epoch-bootstrap-r158.mjs +42 -0
- package/src/version.mjs +1 -1
- package/src/windows-powershell-environment.mjs +5 -2
- package/test/r106-trusted-powershell-spawn.test.mjs +1 -0
- package/test/r130-release-security-contract.test.mjs +9 -9
- package/test/r132-release-identity.test.mjs +12 -10
- package/test/r153-prepublish-identity.test.mjs +25 -4
- package/test/r153-staging-leaf-acl.test.mjs +5 -2
- package/test/r155-commander-live-log.test.mjs +44 -0
- package/test/r155-default-live-log.test.mjs +89 -0
- package/test/r156-epoch-route-smoke.ps1 +48 -0
- package/test/r156-supervisor-pacing.test.mjs +59 -0
- package/test/r157-architecture-map.mjs +61 -0
- package/test/r157-reference-survey.mjs +91 -0
- package/test/r157-supervisor-singleflight.test.mjs +32 -0
- package/test/r158-epoch-bootstrap.test.mjs +34 -0
- package/test/r159-legacy-marker-evidence.test.mjs +103 -0
- package/test/r159-legacy-marker-evidence.test.mjs.before-assertion-fix-20261025-120246.bak +100 -0
- package/test/r160-marker-proof.test.mjs +87 -0
- package/test/r160-package-decision-manifest.test.mjs +67 -0
- package/test/r160-release-contract.test.mjs +30 -0
- package/test/r160-stable-launcher.test.mjs +10 -0
- package/test/r163-isolated-slot-rollback-restore.test.mjs +58 -0
- package/test/r163-publish-fence.test.mjs +13 -0
- package/test/r164-remote-history-skew.test.mjs +13 -0
- package/test/r165-live-cli-skew.test.mjs +90 -0
- package/test/r165-publish-fence.test.mjs +13 -0
- package/test/r48-release-contract.test.mjs +7 -1
- package/test/r58-launcher-shell.test.mjs +4 -1
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r65-cli-invocation.test.mjs +27 -7
- package/test/release-generation.test.mjs +16 -0
- package/test/remote-live-log-r37.test.mjs +5 -3
- package/test/remote-session-cli.test.mjs +11 -11
- package/test/stabilization-state-root.test.mjs +2 -1
- package/node_modules/which/CHANGELOG.md +0 -166
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdtemp,mkdir,writeFile,readFile,rm} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import {createHash} from 'node:crypto';
|
|
7
|
+
import {
|
|
8
|
+
R160_STARTUP_DECISION_FILES,
|
|
9
|
+
verifyPackageDecisionManifest
|
|
10
|
+
} from '../src/package-identity-r160.mjs';
|
|
11
|
+
|
|
12
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
13
|
+
|
|
14
|
+
async function fixture(t){
|
|
15
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r160-package-'));
|
|
16
|
+
t.after(()=>rm(root,{recursive:true,force:true}));
|
|
17
|
+
const bodies=new Map();
|
|
18
|
+
for(const rel of R160_STARTUP_DECISION_FILES){
|
|
19
|
+
const bytes=Buffer.from(rel==='package.json'
|
|
20
|
+
? `${JSON.stringify({name:'deadbyte-mcp',version:'0.15.62'})}\n`
|
|
21
|
+
: `fixture:${rel}\n`,'utf8');
|
|
22
|
+
const file=path.join(root,...rel.split('/'));
|
|
23
|
+
await mkdir(path.dirname(file),{recursive:true});
|
|
24
|
+
await writeFile(file,bytes);
|
|
25
|
+
bodies.set(rel,bytes);
|
|
26
|
+
}
|
|
27
|
+
const manifest=Buffer.from([...bodies].map(([rel,bytes])=>`${sha(bytes)} ${rel}`).join('\n')+'\n','utf8');
|
|
28
|
+
await writeFile(path.join(root,'MANIFEST.SHA256'),manifest);
|
|
29
|
+
return {root,bodies,manifest};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test('R160 startup decision manifest binds every decision file and package identity',async t=>{
|
|
33
|
+
const f=await fixture(t);
|
|
34
|
+
const result=await verifyPackageDecisionManifest({root:f.root,expectedVersion:'0.15.62'});
|
|
35
|
+
assert.equal(result.files,R160_STARTUP_DECISION_FILES.length);
|
|
36
|
+
assert.equal(result.manifest_sha256,sha(f.manifest));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('R160 startup decision manifest rejects changed bytes before foreground claim',async t=>{
|
|
40
|
+
const f=await fixture(t);
|
|
41
|
+
await writeFile(path.join(f.root,'src','deadbyte-cli.mjs'),'changed\n');
|
|
42
|
+
await assert.rejects(
|
|
43
|
+
verifyPackageDecisionManifest({root:f.root,expectedVersion:'0.15.62'}),
|
|
44
|
+
/startup decision file hash mismatch: src\/deadbyte-cli\.mjs/
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('R160 startup decision manifest rejects a newly required recovery module omitted from manifest',async t=>{
|
|
49
|
+
const f=await fixture(t);
|
|
50
|
+
const lines=(await readFile(path.join(f.root,'MANIFEST.SHA256'),'utf8')).split(/\r?\n/)
|
|
51
|
+
.filter(line=>!line.endsWith(' src/legacy-marker-evidence-r159.mjs'));
|
|
52
|
+
await writeFile(path.join(f.root,'MANIFEST.SHA256'),`${lines.join('\n')}\n`);
|
|
53
|
+
await assert.rejects(
|
|
54
|
+
verifyPackageDecisionManifest({root:f.root,expectedVersion:'0.15.62'}),
|
|
55
|
+
/startup decision manifest missing: src\/legacy-marker-evidence-r159\.mjs/
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('R160 remote startup verifies decision bytes before evidence recovery and foreground claim',async()=>{
|
|
60
|
+
const source=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
61
|
+
const verifyAt=source.indexOf('await verifyPackageDecisionManifest({root:CURRENT_PACKAGE_ROOT,expectedVersion:DEADBYTE_VERSION})');
|
|
62
|
+
const recoverAt=source.indexOf('const migrationContext=await recoverRemoteSession(ctx,{displayMode})');
|
|
63
|
+
const claimAt=source.indexOf('marker=await claimRemoteSession(ctx)');
|
|
64
|
+
assert.ok(verifyAt>0&&verifyAt<recoverAt&&recoverAt<claimAt,
|
|
65
|
+
'package decision manifest must be verified before recovery and claim');
|
|
66
|
+
});
|
|
67
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import {DEADBYTE_VERSION} from '../src/version.mjs';
|
|
7
|
+
|
|
8
|
+
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
9
|
+
const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
|
|
10
|
+
|
|
11
|
+
test('R160 package identity separates source and live runtime predecessors',async()=>{
|
|
12
|
+
const [pkg,lock,bridge,release,provenance,matrix,bom]=await Promise.all([
|
|
13
|
+
read('package.json'),read('package-lock.json'),read('bridge/package.json'),read('RELEASE.json'),
|
|
14
|
+
read('PROVENANCE.json'),read('TEST-MATRIX.json'),read('SBOM.cdx.json')]);
|
|
15
|
+
for(const version of [pkg.version,lock.version,lock.packages[''].version,bridge.version,release.version,
|
|
16
|
+
provenance.version,matrix.version,bom.metadata.component.version,DEADBYTE_VERSION]) assert.equal(version,'0.15.63');
|
|
17
|
+
assert.equal(release.train,'R165');
|
|
18
|
+
assert.equal(matrix.train,'R165');
|
|
19
|
+
assert.equal(release.runtime_predecessor.version,'0.15.61');
|
|
20
|
+
assert.equal(release.runtime_predecessor.release_id,'v0.15.61-055d1ff0aa99e60b');
|
|
21
|
+
assert.equal(release.runtime_predecessor.manifest_sha256,'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf');
|
|
22
|
+
assert.equal(release.runtime_predecessor.generation_manifest_sha256,'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4');
|
|
23
|
+
assert.equal(release.source_predecessor.train,'R163');
|
|
24
|
+
assert.match(release.source_predecessor.source_path,/deadbyte-mcp-0\.15\.62-r163-release-closure/i);
|
|
25
|
+
assert.equal(release.r160.public_tool_delta,0);
|
|
26
|
+
assert.equal(release.r160.authority_scope_delta,0);
|
|
27
|
+
assert.equal(release.r160.production_activation,'NOT_RUN');
|
|
28
|
+
assert.equal(release.r160.npm_publish,'NOT_RUN');
|
|
29
|
+
assert.equal(provenance.r160.source_bytes_copied_from_references,false);
|
|
30
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {renderStableLauncherCmd} from '../src/release-slot.mjs';
|
|
4
|
+
|
|
5
|
+
test('R160 stable CMD launcher selects the native Windows PowerShell explicitly', () => {
|
|
6
|
+
const cmd=renderStableLauncherCmd();
|
|
7
|
+
assert.match(cmd,/if not defined SystemRoot exit \/b 1/i);
|
|
8
|
+
assert.match(cmd,/"%SystemRoot%\\System32\\WindowsPowerShell\\v1\.0\\powershell\.exe"/i);
|
|
9
|
+
assert.doesNotMatch(cmd,/(?:^|\r?\n)powershell\.exe\s/im);
|
|
10
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {mkdtemp,mkdir,readFile,rm,writeFile,lstat} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
8
|
+
import {atomicWriteActivePointer,materializeReleaseSlot,parseActivePointer,parseManifest,renderStableLauncherPs1,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
9
|
+
|
|
10
|
+
const candidate=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const version='0.15.63';
|
|
12
|
+
const parent=path.join(os.homedir(),'.deadbyte-mcp','releases','v0.15.61-055d1ff0aa99e60b');
|
|
13
|
+
const parentDigest='055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf';
|
|
14
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
15
|
+
async function generation(state,label){
|
|
16
|
+
const root=path.join(state,'generations',label);
|
|
17
|
+
await mkdir(root,{recursive:true});
|
|
18
|
+
const config=Buffer.from(`@{ Label = '${label}' }\r\n`,'utf8');
|
|
19
|
+
await writeFile(path.join(root,'controller-config.psd1'),config,{flag:'wx'});
|
|
20
|
+
const manifest=Buffer.from(`${sha(config)} controller-config.psd1\n`,'utf8');
|
|
21
|
+
await writeFile(path.join(root,'GENERATION.SHA256'),manifest,{flag:'wx'});
|
|
22
|
+
return {root,manifest_sha256:sha(manifest),config_path:path.join(root,'controller-config.psd1')};
|
|
23
|
+
}
|
|
24
|
+
test('R165 isolated immutable slot activates, rolls back, and restores 0.15.63 without authority or production mutation',async()=>{
|
|
25
|
+
const stateBase=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r163-isolated-'));
|
|
26
|
+
const state=path.join(stateBase,'state');
|
|
27
|
+
try{
|
|
28
|
+
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
29
|
+
assert.equal(pkg.version,version);
|
|
30
|
+
const parentManifest=await readFile(path.join(parent,'MANIFEST.SHA256'));
|
|
31
|
+
const nextManifest=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
32
|
+
assert.equal(sha(parentManifest),parentDigest);
|
|
33
|
+
const nextDigest=sha(nextManifest);
|
|
34
|
+
const parentSlot=await materializeReleaseSlot({source:parent,stateRoot:state,version:'0.15.61',manifestSha256:parentDigest});
|
|
35
|
+
const nextSlot=await materializeReleaseSlot({source:candidate,stateRoot:state,version,manifestSha256:nextDigest});
|
|
36
|
+
const pGen=await generation(state,'r165-parent');
|
|
37
|
+
const nGen=await generation(state,'r165-next');
|
|
38
|
+
const launcherSha=sha(Buffer.from(renderStableLauncherPs1(),'utf8'));
|
|
39
|
+
const pointerPath=path.join(state,'active-release.json');
|
|
40
|
+
const ptr=(slot,ver,g)=>({schema:'deadbyte.active-release.v1',release_id:slot.release_id,version:ver,
|
|
41
|
+
manifest_sha256:slot.manifest_sha256,release_root:slot.release_root,generation_root:g.root,
|
|
42
|
+
generation_manifest_sha256:g.manifest_sha256,launcher_sha256:launcherSha,config_path:g.config_path});
|
|
43
|
+
const activated=await atomicWriteActivePointer(pointerPath,ptr(nextSlot,version,nGen));
|
|
44
|
+
assert.equal(activated.release_id,nextSlot.release_id);
|
|
45
|
+
await verifyManifestTreeExact(nextSlot.release_root,parseManifest(nextManifest.toString('utf8')));
|
|
46
|
+
const rolled=await atomicWriteActivePointer(pointerPath,ptr(parentSlot,'0.15.61',pGen));
|
|
47
|
+
assert.equal(rolled.release_id,parentSlot.release_id);
|
|
48
|
+
assert.equal(rolled.manifest_sha256,parentDigest);
|
|
49
|
+
await verifyManifestTreeExact(parentSlot.release_root,parseManifest(parentManifest.toString('utf8')));
|
|
50
|
+
const restored=await atomicWriteActivePointer(pointerPath,ptr(nextSlot,version,nGen));
|
|
51
|
+
assert.deepEqual(restored,activated);
|
|
52
|
+
assert.deepEqual(parseActivePointer(await readFile(pointerPath,'utf8')),activated);
|
|
53
|
+
await verifyManifestTreeExact(nextSlot.release_root,parseManifest(nextManifest.toString('utf8')));
|
|
54
|
+
for(const n of ['process-grant.json','coding-grant.json','autonomous-grant.json','desktop-grant.json','host-grant.json']){
|
|
55
|
+
await assert.rejects(()=>lstat(path.join(state,'runtime',n)),{code:'ENOENT'});
|
|
56
|
+
}
|
|
57
|
+
}finally{await rm(stateBase,{recursive:true,force:true});}
|
|
58
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
import {assertR163NpmPublishReady} from '../scripts/npm-release-readiness-r163.mjs';
|
|
6
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
7
|
+
const release={name:'deadbyte-mcp',version:'0.15.62',train:'R160',r163:{publication_mode:'PUBLISH_ONLY'},parent:{release_id:'v0.15.61-055d1ff0aa99e60b',manifest_sha256:'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf',generation_manifest_sha256:'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4'},runtime_predecessor:{release_id:'v0.15.61-055d1ff0aa99e60b'}};
|
|
8
|
+
test('R163 publish gate fails closed with missing evidence',async()=>{
|
|
9
|
+
await assert.rejects(()=>assertR163NpmPublishReady(release,{root,evidenceRoot:null}),/PUBLISH_BLOCKED R163/);
|
|
10
|
+
});
|
|
11
|
+
test('R163 publish gate rejects changed parent identity before evidence read',async()=>{
|
|
12
|
+
await assert.rejects(()=>assertR163NpmPublishReady({...release,parent:{...release.parent,release_id:'v0.15.60-wrong'}},{root}),/predecessor lineage mismatch/);
|
|
13
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
|
|
5
|
+
test('R164 current ONLINE predecessor permits completed-history classification, not receipt adoption',async()=>{
|
|
6
|
+
const cli=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
7
|
+
assert.match(cli,/assertRuntimeReadyObservation\(runtime,\{coreRoot:activePointer\.release_root\}\)/);
|
|
8
|
+
assert.match(cli,/allowCompletedHistory:historyAllowed\|\|onlinePredecessorReady/);
|
|
9
|
+
assert.match(cli,/R164 predecessor changed during completed-history classification/);
|
|
10
|
+
assert.match(cli,/assertRuntimeReadyObservation\(observedAuthority\(ctx\),\{coreRoot:readback\.release_root\}\)/);
|
|
11
|
+
assert.match(cli,/if\(!onlinePredecessorReady\)throw new Error/);
|
|
12
|
+
assert.match(cli,/do not adopt any receipt as an upgrade\/migration authority/i);
|
|
13
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Executable foreground CLI proof. All runtime state is created under a fresh OS temp dir.
|
|
2
|
+
// The fake controller intentionally rejects disarmhost, BEFORE any production deployment.
|
|
3
|
+
import test from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import {createHash} from 'node:crypto';
|
|
6
|
+
import {mkdtemp,mkdir,readFile,writeFile,readdir,rm} from 'node:fs/promises';
|
|
7
|
+
import {spawnSync} from 'node:child_process';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import {fileURLToPath} from 'node:url';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
12
|
+
const envRoot=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
13
|
+
const expectedManifest=null;
|
|
14
|
+
const predecessor={version:'0.15.61',release_id:'v0.15.61-055d1ff0aa99e60b',manifest_sha256:'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf',generation_manifest_sha256:'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4'};
|
|
15
|
+
const gate=process.platform==='win32'?test:test.skip;
|
|
16
|
+
const hashMarker=c=>c.repeat(64);
|
|
17
|
+
async function fixture(t,kind){
|
|
18
|
+
assert.equal(typeof envRoot,'string'); assert.ok(path.isAbsolute(envRoot));
|
|
19
|
+
assert.match(sha(await readFile(path.join(envRoot,'MANIFEST.SHA256'))),/^[0-9a-f]{64}$/);
|
|
20
|
+
assert.equal(JSON.parse(await readFile(path.join(envRoot,'package.json'),'utf8')).version,'0.15.63');
|
|
21
|
+
const dir=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r164-skew-e2e-'));
|
|
22
|
+
t.after(async()=>{await rm(dir,{recursive:true,force:true,maxRetries:10,retryDelay:150});});
|
|
23
|
+
const state=path.join(dir,'state'),bin=path.join(state,'bin');
|
|
24
|
+
const legacy=path.join(state,'evidence','legacy-migration');
|
|
25
|
+
const trace=path.join(dir,'controller.trace');
|
|
26
|
+
const releaseRoot=path.join(state,'releases','predecessor');
|
|
27
|
+
const generationRoot=path.join(state,'generations','predecessor');
|
|
28
|
+
await mkdir(bin,{recursive:true});await mkdir(legacy,{recursive:true});
|
|
29
|
+
const pointer={schema:'deadbyte.active-release.v1',...predecessor,release_root:releaseRoot,generation_root:generationRoot,launcher_sha256:hashMarker('9'),config_path:path.join(generationRoot,'controller-config.psd1')};
|
|
30
|
+
await writeFile(path.join(state,'active-release.json'),JSON.stringify(pointer)+'\n','utf8');
|
|
31
|
+
// The true CLI calls this local PowerShell stub; a hard stop precedes deploy.
|
|
32
|
+
const ps=`param([string]$Command,[switch]$Json)\n$ErrorActionPreference='Stop'\n[IO.File]::AppendAllText($env:DEADBYTE_SKEW_E2E_TRACE,($Command+[Environment]::NewLine),[Text.Encoding]::UTF8)\nif($Command -eq 'status' -and $Json){\n $auth=@{};foreach($id in @('process','coding','autonomous','desktop','host')){$auth[$id]=@{configured=$true;armed=$false;reason='disarmed'}}\n @{schema='deadbyte.controller-observation.v1';state=$env:DEADBYTE_SKEW_E2E_RUNTIME_STATE;local_ready=$true;core_root=$env:DEADBYTE_SKEW_E2E_CORE_ROOT;bridge=@{alive=$true;reason='verified'};core=@{alive=$true;reason='verified'};cloud=@{alive=$true;reason='verified'};supervisor=@{running=$true};authority=$auth}|ConvertTo-Json -Depth 6 -Compress;exit 0\n}\nif($Command -eq 'disarmhost'){[Console]::Error.WriteLine('R164_INTENTIONAL_PREDEPLOY_STOP');exit 93}\nWrite-Error ('R164_UNEXPECTED_CONTROLLER_OPERATION '+$Command);exit 99\n`;
|
|
33
|
+
await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),ps,'utf8');
|
|
34
|
+
const currentIds=['a','b'];
|
|
35
|
+
for(const id of currentIds){
|
|
36
|
+
const marker=hashMarker(id),intent={schema:'deadbyte.r153-marker-reconciliation-intent.v1',state:'RECONCILIATION_CLAIMED',marker_sha256:marker,predecessor,subject_sha256:hashMarker('7')};
|
|
37
|
+
const intentBytes=Buffer.from(JSON.stringify(intent)+'\n');
|
|
38
|
+
const terminal={schema:'deadbyte.r153-marker-reconciliation.v1',state:'MARKER_RECONCILED',marker_sha256:marker,predecessor,intent_sha256:sha(intentBytes),subject_sha256:hashMarker('8')};
|
|
39
|
+
await writeFile(path.join(legacy,`marker-${marker}.intent.json`),intentBytes,{flag:'wx'});
|
|
40
|
+
await writeFile(path.join(legacy,`marker-${marker}.terminal.json`),JSON.stringify(terminal)+'\n',{flag:'wx'});
|
|
41
|
+
}
|
|
42
|
+
if(kind==='pending'){
|
|
43
|
+
const marker=hashMarker('c'),intent={schema:'deadbyte.r153-marker-reconciliation-intent.v1',state:'RECONCILIATION_CLAIMED',marker_sha256:marker,predecessor,subject_sha256:hashMarker('7')};
|
|
44
|
+
await writeFile(path.join(legacy,`marker-${marker}.intent.json`),JSON.stringify(intent)+'\n',{flag:'wx'});
|
|
45
|
+
}
|
|
46
|
+
if(kind==='tampered'){
|
|
47
|
+
const p=path.join(legacy,`marker-${hashMarker('b')}.terminal.json`);
|
|
48
|
+
const terminal=JSON.parse(await readFile(p,'utf8'));
|
|
49
|
+
terminal.intent_sha256=hashMarker('0');
|
|
50
|
+
await writeFile(p,JSON.stringify(terminal)+'\n');
|
|
51
|
+
}
|
|
52
|
+
const before=await Promise.all((await readdir(legacy)).sort().map(async name=>[name,sha(await readFile(path.join(legacy,name)))]));
|
|
53
|
+
const env={...process.env,DEADBYTE_STATE_ROOT:state,DEADBYTE_SKEW_E2E_TRACE:trace,DEADBYTE_SKEW_E2E_CORE_ROOT:releaseRoot,DEADBYTE_SKEW_E2E_RUNTIME_STATE:kind==='offline'?'OFFLINE':'ONLINE'};
|
|
54
|
+
return {dir,state,legacy,trace,env,before};
|
|
55
|
+
}
|
|
56
|
+
async function run(fx){
|
|
57
|
+
const cli=path.join(envRoot,'src','deadbyte-cli.mjs');
|
|
58
|
+
const result=spawnSync(process.execPath,[cli,'remote'],{env:fx.env,cwd:fx.dir,windowsHide:true,shell:false,encoding:'utf8',timeout:45000,maxBuffer:2*1024*1024});
|
|
59
|
+
if(result.error)throw result.error;
|
|
60
|
+
assert.equal(result.status,1,'expected hard stop BEFORE deploy');
|
|
61
|
+
const after=await Promise.all((await readdir(fx.legacy)).sort().map(async name=>[name,sha(await readFile(path.join(fx.legacy,name)))]));
|
|
62
|
+
assert.deepEqual(after,fx.before,'reconciliation evidence mutated');
|
|
63
|
+
await assert.rejects(readFile(path.join(fx.state,'runtime','remote-session.json')),{code:'ENOENT'});
|
|
64
|
+
let trace='';try{trace=await readFile(fx.trace,'utf8');}catch(e){if(e.code!=='ENOENT')throw e;}
|
|
65
|
+
assert.doesNotMatch(trace,/\b(?:start|restart|armprocess|armcoding|armautonomous|deploy)\b/i,'unexpected runtime operation');
|
|
66
|
+
return {output:(result.stderr||'')+'\n'+(result.stdout||''),trace};
|
|
67
|
+
}
|
|
68
|
+
gate('R165 executable CLI passes completed R159 history, reaches 0.15.61 -> 0.15.63 synchronization safely',async t=>{
|
|
69
|
+
const fx=await fixture(t,'completed');
|
|
70
|
+
const r=await run(fx);
|
|
71
|
+
assert.match(r.output,/runtime coherence 'disarmhost' failed exit=93/i,r.output);
|
|
72
|
+
assert.doesNotMatch(r.output,/R159 ambiguous multiple completed/i);
|
|
73
|
+
assert.match(r.trace,/disarmhost/);
|
|
74
|
+
assert.doesNotMatch(r.trace,/disarmdesktop/);
|
|
75
|
+
});
|
|
76
|
+
gate('R165 executable CLI rejects pending current reconciliation before upgrade',async t=>{
|
|
77
|
+
const fx=await fixture(t,'pending'),r=await run(fx);
|
|
78
|
+
assert.match(r.output,/pending marker reconciliation requires an offline runtime/i,r.output);
|
|
79
|
+
assert.doesNotMatch(r.trace,/disarmhost/);
|
|
80
|
+
});
|
|
81
|
+
gate('R165 executable CLI rejects tampered terminal before upgrade',async t=>{
|
|
82
|
+
const fx=await fixture(t,'tampered'),r=await run(fx);
|
|
83
|
+
assert.match(r.output,/terminal intent\/subject mismatch|terminal.*mismatch/i,r.output);
|
|
84
|
+
assert.doesNotMatch(r.trace,/disarmhost/);
|
|
85
|
+
});
|
|
86
|
+
gate('R165 executable CLI keeps ambiguous history fail-closed when predecessor is not ONLINE',async t=>{
|
|
87
|
+
const fx=await fixture(t,'offline'),r=await run(fx);
|
|
88
|
+
assert.match(r.output,/R159 ambiguous multiple completed reconciliation receipts/i,r.output);
|
|
89
|
+
assert.doesNotMatch(r.trace,/disarmhost/);
|
|
90
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
import {assertR165NpmPublishReady} from '../scripts/npm-release-readiness-r165.mjs';
|
|
6
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
7
|
+
const release={name:'deadbyte-mcp',version:'0.15.63',train:'R165',r165:{publication_mode:'PUBLISH_ONLY'},parent:{release_id:'v0.15.61-055d1ff0aa99e60b',manifest_sha256:'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf',generation_manifest_sha256:'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4'},runtime_predecessor:{release_id:'v0.15.61-055d1ff0aa99e60b'}};
|
|
8
|
+
test('R165 publish gate fails closed with missing evidence',async()=>{
|
|
9
|
+
await assert.rejects(()=>assertR165NpmPublishReady(release,{root,evidenceRoot:null}),/PUBLISH_BLOCKED R165/);
|
|
10
|
+
});
|
|
11
|
+
test('R165 publish gate rejects changed parent identity before evidence read',async()=>{
|
|
12
|
+
await assert.rejects(()=>assertR165NpmPublishReady({...release,parent:{...release.parent,release_id:'v0.15.60-wrong'}},{root}),/predecessor lineage mismatch/);
|
|
13
|
+
});
|
|
@@ -30,5 +30,11 @@ test('R48 compatibility release contract remains coherent in the current success
|
|
|
30
30
|
assert.equal(provenance.parent_release_id, release.parent.release_id);
|
|
31
31
|
assert.equal(provenance.parent_manifest_sha256, release.parent.manifest_sha256);
|
|
32
32
|
assert.equal(provenance.parent_generation_manifest_sha256, release.parent.generation_manifest_sha256);
|
|
33
|
-
|
|
33
|
+
if(release.train==='R160'){
|
|
34
|
+
assert.equal(release.source_predecessor.train,'R159');
|
|
35
|
+
assert.equal(release.source_predecessor.manifest_status,'STALE_REJECTED');
|
|
36
|
+
assert.equal(provenance.r160.source_predecessor_manifest_status,'STALE_REJECTED');
|
|
37
|
+
}else{
|
|
38
|
+
assert.equal(provenance.source_predecessor_tarball_sha256, release.source_predecessor.tarball_sha256);
|
|
39
|
+
}
|
|
34
40
|
});
|
|
@@ -4,6 +4,7 @@ import { mkdir, mkdtemp, writeFile } from 'node:fs/promises';
|
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { renderStableLauncherPs1 } from '../src/release-slot.mjs';
|
|
7
|
+
import { windowsPowerShellEnvironment } from '../src/runtime-update.mjs';
|
|
7
8
|
|
|
8
9
|
test('R58 launcher dispatch uses native Windows PowerShell even when entered from pwsh', async () => {
|
|
9
10
|
const base = 'D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';
|
|
@@ -20,7 +21,9 @@ test('R58 launcher dispatch uses native Windows PowerShell even when entered fro
|
|
|
20
21
|
const native = path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
21
22
|
const modern = path.join(process.env.USERPROFILE, '.cache', 'codex-runtimes', 'codex-primary-runtime', 'dependencies', 'native', 'powershell', 'pwsh.exe');
|
|
22
23
|
for (const executable of [native, modern]) {
|
|
23
|
-
const observed = spawnSync(executable, ['-NoProfile', '-File', dispatch], {
|
|
24
|
+
const observed = spawnSync(executable, ['-NoProfile', '-File', dispatch], {
|
|
25
|
+
env: windowsPowerShellEnvironment(process.env), encoding: 'utf8', timeout: 20000
|
|
26
|
+
});
|
|
24
27
|
assert.ifError(observed.error);
|
|
25
28
|
assert.equal(observed.status, 7, observed.stderr);
|
|
26
29
|
assert.match(observed.stdout, /FIXTURE:status/);
|
|
@@ -83,7 +83,7 @@ test('R72 compact baseline permits only R64 read offset and authorized coding-on
|
|
|
83
83
|
assert.deepEqual(diff.added,[]);
|
|
84
84
|
assert.deepEqual(diff.removed,[]);
|
|
85
85
|
const packageVersion=JSON.parse(await readFile(path.join(repoRoot,'package.json'),'utf8')).version;
|
|
86
|
-
if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61'].includes(packageVersion)){
|
|
86
|
+
if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61','0.15.62','0.15.63'].includes(packageVersion)){
|
|
87
87
|
assert.deepEqual([...diff.schema_changed].sort(),['machine_fs','native_diff_preview']);
|
|
88
88
|
assert.equal(diff.unchanged.length,41);
|
|
89
89
|
const prior=baseline.tools.find(tool=>tool.name==='machine_fs');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {spawnSync} from 'node:child_process';
|
|
4
|
-
import {mkdtemp,rm,readFile} from 'node:fs/promises';
|
|
4
|
+
import {mkdtemp,mkdir,rm,readFile,writeFile} from 'node:fs/promises';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import {fileURLToPath} from 'node:url';
|
|
@@ -22,16 +22,36 @@ test('repository compatibility CMD refuses to masquerade as an exact npm package
|
|
|
22
22
|
assert.doesNotMatch(run.stderr,/deadbyte-controller\.ps1/);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
test('isolated exact local tarball
|
|
25
|
+
test('isolated exact local tarball install reaches its package bin outside the source tree', {skip:process.platform!=='win32',timeout:120000},async()=>{
|
|
26
26
|
const fixture=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-cli-package-'));
|
|
27
27
|
try{
|
|
28
|
-
const
|
|
28
|
+
const packDir=path.join(fixture,'pack');
|
|
29
|
+
const installDir=path.join(fixture,'install');
|
|
30
|
+
const tempDir=path.join(fixture,'temp');
|
|
31
|
+
await mkdir(packDir);
|
|
32
|
+
await mkdir(installDir);
|
|
33
|
+
await mkdir(tempDir);
|
|
34
|
+
await writeFile(path.join(installDir,'package.json'),'{"private":true}\n',{encoding:'utf8',flag:'wx'});
|
|
35
|
+
const npmCli=path.join(path.dirname(process.execPath),'node_modules','npm','bin','npm-cli.js');
|
|
36
|
+
const nodePath=path.dirname(process.execPath);
|
|
37
|
+
const systemRoot=process.env.SystemRoot??process.env.WINDIR??'C:\\Windows';
|
|
38
|
+
const npmEnv={
|
|
39
|
+
SystemRoot:systemRoot,WINDIR:systemRoot,TEMP:tempDir,TMP:tempDir,
|
|
40
|
+
USERPROFILE:process.env.USERPROFILE??os.homedir(),
|
|
41
|
+
ComSpec:process.env.ComSpec??path.join(systemRoot,'System32','cmd.exe'),
|
|
42
|
+
PATH:[nodePath,path.join(systemRoot,'System32')].join(path.delimiter),
|
|
43
|
+
PATHEXT:'.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC',
|
|
44
|
+
npm_config_cache:path.join(fixture,'npm-cache')};
|
|
29
45
|
const pkg=JSON.parse(await readFile(path.join(root,'package.json'),'utf8'));
|
|
30
|
-
const pack=spawnSync(process.execPath,[npmCli,'pack','--json','--pack-destination',
|
|
46
|
+
const pack=spawnSync(process.execPath,[npmCli,'pack','--json','--pack-destination',packDir],{cwd:root,env:npmEnv,encoding:'utf8',timeout:45000,windowsHide:true});
|
|
31
47
|
assert.equal(pack.status,0,(pack.stderr||'').slice(-1000));
|
|
32
|
-
const packed=JSON.parse(pack.stdout);const tarball=path.join(
|
|
33
|
-
const
|
|
34
|
-
cwd:
|
|
48
|
+
const packed=JSON.parse(pack.stdout);const tarball=path.join(packDir,packed[0]?.filename??packed['deadbyte-mcp']?.filename);
|
|
49
|
+
const install=spawnSync(process.execPath,[npmCli,'install','--offline','--ignore-scripts','--no-audit','--no-fund',tarball],{
|
|
50
|
+
cwd:installDir,env:npmEnv,encoding:'utf8',timeout:60000,windowsHide:true});
|
|
51
|
+
assert.equal(install.status,0,(install.stderr||'').slice(-1000));
|
|
52
|
+
const packageBin=path.join(installDir,'node_modules','.bin','deadbyte-mcp.cmd');
|
|
53
|
+
const run=spawnSync(process.env.ComSpec||'C:\\Windows\\System32\\cmd.exe',['/d','/c',packageBin,'--version'],{
|
|
54
|
+
cwd:installDir,env:npmEnv,encoding:'utf8',timeout:30000,windowsHide:true});
|
|
35
55
|
assert.equal(run.status,0,(run.stderr||'').slice(-1000));
|
|
36
56
|
assert.equal(run.stdout.trim(),pkg.version);
|
|
37
57
|
}finally{await rm(fixture,{recursive:true,force:true});}
|
|
@@ -156,6 +156,22 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
|
|
|
156
156
|
const second=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:changedConfig,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
|
|
157
157
|
assert.notEqual(second.generation_root,result.generation_root);
|
|
158
158
|
assert.notEqual(second.generation_seed_sha256,result.generation_seed_sha256);
|
|
159
|
+
// R158 regression: changing the trusted Epoch source changes generation identity.
|
|
160
|
+
const epochA=structuredClone(currentConfig);
|
|
161
|
+
epochA.AuthorityEpochRoot='C:\\Program Files\\DEADBYTE-R158-Epoch-A';
|
|
162
|
+
const generationA=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:epochA,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
|
|
163
|
+
assert.notEqual(generationA.generation_root,result.generation_root);
|
|
164
|
+
assert.match(await readFile(generationA.config_path,'utf8'),/AuthorityEpochRoot = 'C:\\Program Files\\DEADBYTE-R158-Epoch-A'/);
|
|
165
|
+
const epochB=structuredClone(epochA);
|
|
166
|
+
epochB.AuthorityEpochRoot='C:\\Program Files\\DEADBYTE-R158-Epoch-B';
|
|
167
|
+
const generationB=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:epochB,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
|
|
168
|
+
assert.notEqual(generationB.generation_root,generationA.generation_root);
|
|
169
|
+
assert.notEqual(generationB.generation_seed_sha256,generationA.generation_seed_sha256);
|
|
170
|
+
for(const invalidRoot of ['relative\\epoch','\\\\server\\share\\epoch','C:relative','']){
|
|
171
|
+
const invalid=structuredClone(currentConfig);
|
|
172
|
+
invalid.AuthorityEpochRoot=invalidRoot;
|
|
173
|
+
await assert.rejects(build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:invalid,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy}),/AuthorityEpochRoot.*drive-absolute/i);
|
|
174
|
+
}
|
|
159
175
|
const endpointChanged=structuredClone(currentConfig);
|
|
160
176
|
endpointChanged.McpUrlFileSha256='2'.repeat(64);
|
|
161
177
|
const third=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:endpointChanged,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
|
|
@@ -72,13 +72,15 @@ test('R40 CLI exposes explicit verbose mode while keeping default live mode comp
|
|
|
72
72
|
const {readFile}=await import('node:fs/promises');
|
|
73
73
|
const src=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
74
74
|
assert.match(src,/readLiveRootPaths\(ctx\.root\)/);
|
|
75
|
-
assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace
|
|
75
|
+
assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace,\.\.\.defaultForegroundTimelineOptions\(\{verbose\}\)\}\)/);
|
|
76
|
+
assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:false}),{commanderStyle:true,foldHeartbeats:false});
|
|
77
|
+
assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:true}),{commanderStyle:false,foldHeartbeats:false});
|
|
76
78
|
assert.match(src,/createLiveTimelineFilter\(filterExpression\)/);
|
|
77
79
|
assert.match(src,/createLiveTimelineWriter\(\{stream:process\.stdout,displayMode,rewrite:false\}\)/);
|
|
78
80
|
assert.match(src,/arg==='--verbose'\|\|arg==='-v'/);
|
|
79
81
|
assert.match(src,/arg==='--trace'/);
|
|
80
82
|
assert.match(src,/arg==='--filter'/);
|
|
81
|
-
assert.match(src,/Transport
|
|
83
|
+
assert.match(src,/Transport hidden; signed audit remains on disk/);
|
|
82
84
|
assert.doesNotMatch(src,/\[cursor \$\{recorded\.cursor\}\]/);
|
|
83
85
|
});
|
|
84
86
|
|
|
@@ -232,7 +234,7 @@ test('human mutation span labels START and preserves terminal audit status',()=>
|
|
|
232
234
|
test('foreground human log keeps START and DONE separate and folds heartbeat noise',async()=>{
|
|
233
235
|
const {readFile}=await import('node:fs/promises');
|
|
234
236
|
const src=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
235
|
-
assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace
|
|
237
|
+
assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace,\.\.\.defaultForegroundTimelineOptions\(\{verbose\}\)\}\)/);
|
|
236
238
|
assert.match(src,/createLiveTimelineWriter\(\{stream:process\.stdout,displayMode,rewrite:false\}\)/);
|
|
237
239
|
});
|
|
238
240
|
|
|
@@ -330,7 +330,7 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
330
330
|
assert.match(output.stdout,/\[OK\] ONLINE/);
|
|
331
331
|
assert.match(output.stdout,/\[READY\] DEADBYTE MCP online/);
|
|
332
332
|
assert.match(output.stdout,/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/);
|
|
333
|
-
assert.match(output.stdout,/\[LOG\]
|
|
333
|
+
assert.match(output.stdout,/\[LOG\] Live tool calls with individual START\/DONE, sanitized arguments and results\. Transport hidden; signed audit remains on disk\./);
|
|
334
334
|
assert.doesNotMatch(output.stdout,/Process plane\s*:/);
|
|
335
335
|
assert.doesNotMatch(output.stdout,/Coding plane\s*:/);
|
|
336
336
|
const auditDir=path.join(fx.state,'audit');
|
|
@@ -353,11 +353,11 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
353
353
|
'[TOOL OUT] 2026-09-16T00:00:00.002Z #000002 coding_file_read {"is_error":true}'
|
|
354
354
|
].join('\n')+'\n','utf8');
|
|
355
355
|
const logDeadline=Date.now()+fixtureTimeoutMs;
|
|
356
|
-
while(Date.now()<logDeadline
|
|
357
|
-
assert.
|
|
358
|
-
assert.match(output.stdout
|
|
359
|
-
assert.match(output.stdout
|
|
360
|
-
assert.equal((output.stdout.match(/\[abcdef12\]
|
|
356
|
+
while(Date.now()<logDeadline&&!/^\[.*\] \[FAIL\] Tool call coding_file_read failed \[fedcba98\] 1\.73s$/m.test(output.stdout)) await sleep(50);
|
|
357
|
+
assert.match(output.stdout,/\[CALL\] Received tool call \[abcdef12\] coding_tree Arguments:/,'default human view must show bounded sanitized arguments');
|
|
358
|
+
assert.match(output.stdout,/^\[.*\] \[DONE\] Tool call coding_tree completed \[abcdef12\] 42ms$/m);
|
|
359
|
+
assert.match(output.stdout,/^\[.*\] \[FAIL\] Tool call coding_file_read failed \[fedcba98\] 1\.73s$/m);
|
|
360
|
+
assert.equal((output.stdout.match(/\[DONE\] Tool call coding_tree completed \[abcdef12\] 42ms/g)||[]).length,1,'duplicate terminal result must print once');
|
|
361
361
|
assert.doesNotMatch(output.stdout,/\[cursor r1:/,'full replay cursor belongs in the journal, not every human line');
|
|
362
362
|
const resultJournal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',`${marker.result_session_id}.json`),'utf8'));
|
|
363
363
|
assert.deepEqual(resultJournal.events.map(event=>event.seq),[1,2,3,4]);
|
|
@@ -377,7 +377,7 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
377
377
|
const commands=await traceCommands(fx.trace);
|
|
378
378
|
assert.deepEqual(commands.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
|
|
379
379
|
assert.match(output.stdout,/^DEADBYTE MCP$/m);
|
|
380
|
-
assert.match(output.stdout,/
|
|
380
|
+
assert.match(output.stdout,/Live tool calls with individual START\/DONE/i);
|
|
381
381
|
assert.match(output.stdout,/\[AUTH\] Revoking session authority/);
|
|
382
382
|
assert.doesNotMatch(output.stdout,/disarmautonomous OK|disarmcoding OK|disarmprocess OK|disarmdesktop OK|disarmhost OK|DEADBYTE MCP = PARKED/);
|
|
383
383
|
assert.doesNotMatch(output.stdout,/\p{Extended_Pictographic}/u,'piped Windows fallback must remain ASCII-safe');
|
|
@@ -395,7 +395,7 @@ winTest('remote --resume replays sanitized tool-call and terminal result events
|
|
|
395
395
|
const firstOutput=await collect(first);
|
|
396
396
|
await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=4);
|
|
397
397
|
const liveDeadline=Date.now()+3000;
|
|
398
|
-
while(Date.now()<liveDeadline&&!/\[LOG\]
|
|
398
|
+
while(Date.now()<liveDeadline&&!/\[LOG\] Live tool calls/.test(firstOutput.stdout)) await sleep(50);
|
|
399
399
|
const marker=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8'));
|
|
400
400
|
assert.match(String(marker.result_session_id||''),/^[0-9a-f]{24}$/);
|
|
401
401
|
const auditDir=path.join(fx.state,'audit');await mkdir(auditDir,{recursive:true});
|
|
@@ -428,7 +428,7 @@ winTest('remote --resume replays sanitized tool-call and terminal result events
|
|
|
428
428
|
deadline=Date.now()+5000;
|
|
429
429
|
while(Date.now()<deadline&&(!/coding_file_read\s+\| Arguments: .*\| REPLAY #3/.test(secondOutput.stdout)||
|
|
430
430
|
!/\[REPLAY\] #4 coding_file_read OK/.test(secondOutput.stdout)||
|
|
431
|
-
!/\[LOG\]
|
|
431
|
+
!/\[LOG\] Live tool calls/.test(secondOutput.stdout))) await sleep(50);
|
|
432
432
|
assert.match(secondOutput.stdout,/^2026-09-18T23:30:01\.000\+07:00 \| coding_file_read\s+\| Arguments: .*"path":"src\/deadbyte-cli\.mjs".*\| REPLAY #3$/m);
|
|
433
433
|
assert.match(secondOutput.stdout,/\[REPLAY\] #4 coding_file_read OK/);
|
|
434
434
|
assert.match(secondOutput.stdout,/\[SESSION\] Previous result stream replayed/);
|
|
@@ -438,7 +438,7 @@ winTest('remote --resume replays sanitized tool-call and terminal result events
|
|
|
438
438
|
assert.equal((secondOutput.stdout.match(/\[REPLAY\] #4 coding_file_read OK/g)||[]).length,1,'terminal replay duplicated');
|
|
439
439
|
const callReplayIndex=secondOutput.stdout.indexOf('| coding_file_read');
|
|
440
440
|
const resultReplayIndex=secondOutput.stdout.indexOf('[REPLAY] #4 coding_file_read OK');
|
|
441
|
-
const liveIndex=secondOutput.stdout.indexOf('[LOG]
|
|
441
|
+
const liveIndex=secondOutput.stdout.indexOf('[LOG] Live tool calls');
|
|
442
442
|
assert.ok(callReplayIndex>=0&&resultReplayIndex>callReplayIndex&&liveIndex>resultReplayIndex,'resume ordering must be call -> result -> live tail');
|
|
443
443
|
second.stdin.end();
|
|
444
444
|
const secondEnded=await boundedCompletion(secondOutput.closed);
|
|
@@ -811,7 +811,7 @@ winTest('remote --trace --filter selects presentation without dropping journaled
|
|
|
811
811
|
await writeFile(path.join(auditDir,'segment-00000001.jsonl'),audit.map(JSON.stringify).join('\n')+'\n','utf8');
|
|
812
812
|
const deadline=Date.now()+4000;
|
|
813
813
|
while(Date.now()<deadline&&!/machine_fs\.write.*trace=goal-1234/.test(output.stdout)) await sleep(50);
|
|
814
|
-
assert.match(output.stdout,/machine_fs\.write.*trace=goal-123\.\.\. span=filter-w\.\.\..*10ms.*
|
|
814
|
+
assert.match(output.stdout,/Tool call machine_fs\.write failed \[filter-w\].*trace=goal-123\.\.\. span=filter-w\.\.\..*10ms.*denied/i);
|
|
815
815
|
assert.doesNotMatch(output.stdout,/coding_file_read.*missing/);
|
|
816
816
|
assert.doesNotMatch(output.stdout,/SECRET/);
|
|
817
817
|
const journal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',marker.result_session_id+'.json'),'utf8'));
|
|
@@ -54,7 +54,8 @@ test('CMD launcher transports explicit root with spaces to nested Node child',as
|
|
|
54
54
|
await writeFile(path.join(isolated,'bin','DEADBYTE-MCP.ps1'),`& '${process.execPath.replaceAll("'","''")}' -e 'console.log(process.env.DEADBYTE_STATE_ROOT)'\nexit $LASTEXITCODE\n`);
|
|
55
55
|
const cmd=path.join(fixture,'launcher.cmd');
|
|
56
56
|
await writeFile(cmd,renderStableLauncherCmd());
|
|
57
|
-
const
|
|
57
|
+
const nativeCmd=path.join(process.env.SystemRoot,'System32','cmd.exe');
|
|
58
|
+
const r=spawnSync(nativeCmd,['/d','/c',cmd,'status'],{encoding:'utf8',timeout:20000,env:windowsPowerShellEnvironment({...process.env,DEADBYTE_STATE_ROOT:isolated,USERPROFILE:fixture})});
|
|
58
59
|
assert.equal(r.status,0,r.stderr);
|
|
59
60
|
assert.equal(r.stdout.trim(),isolated);
|
|
60
61
|
});
|