@dharma-ai-labs/agent-fabric 0.2.152 → 0.2.154
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/dist/bootstrapGitScope.test.d.ts +2 -0
- package/dist/bootstrapGitScope.test.d.ts.map +1 -0
- package/dist/bootstrapGitScope.test.js +115 -0
- package/dist/bootstrapGitScope.test.js.map +1 -0
- package/dist/bootstrapHostFiles.d.ts +5 -0
- package/dist/bootstrapHostFiles.d.ts.map +1 -0
- package/dist/bootstrapHostFiles.js +150 -0
- package/dist/bootstrapHostFiles.js.map +1 -0
- package/dist/bootstrapHostFiles.test.d.ts +2 -0
- package/dist/bootstrapHostFiles.test.d.ts.map +1 -0
- package/dist/bootstrapHostFiles.test.js +127 -0
- package/dist/bootstrapHostFiles.test.js.map +1 -0
- package/dist/bootstrapHostScope.d.ts +42 -0
- package/dist/bootstrapHostScope.d.ts.map +1 -0
- package/dist/bootstrapHostScope.js +224 -0
- package/dist/bootstrapHostScope.js.map +1 -0
- package/dist/bootstrapHostScope.test.d.ts +2 -0
- package/dist/bootstrapHostScope.test.d.ts.map +1 -0
- package/dist/bootstrapHostScope.test.js +577 -0
- package/dist/bootstrapHostScope.test.js.map +1 -0
- package/dist/bootstrapInstallationScope.test.d.ts +2 -0
- package/dist/bootstrapInstallationScope.test.d.ts.map +1 -0
- package/dist/bootstrapInstallationScope.test.js +68 -0
- package/dist/bootstrapInstallationScope.test.js.map +1 -0
- package/dist/bootstrapLauncherCaller.test.d.ts +2 -0
- package/dist/bootstrapLauncherCaller.test.d.ts.map +1 -0
- package/dist/bootstrapLauncherCaller.test.js +91 -0
- package/dist/bootstrapLauncherCaller.test.js.map +1 -0
- package/dist/bootstrapOwnedChildren.test.d.ts +2 -0
- package/dist/bootstrapOwnedChildren.test.d.ts.map +1 -0
- package/dist/bootstrapOwnedChildren.test.js +93 -0
- package/dist/bootstrapOwnedChildren.test.js.map +1 -0
- package/dist/bootstrapPolicyScope.test.d.ts +2 -0
- package/dist/bootstrapPolicyScope.test.d.ts.map +1 -0
- package/dist/bootstrapPolicyScope.test.js +261 -0
- package/dist/bootstrapPolicyScope.test.js.map +1 -0
- package/dist/bootstrapProcessCaller.test.d.ts +2 -0
- package/dist/bootstrapProcessCaller.test.d.ts.map +1 -0
- package/dist/bootstrapProcessCaller.test.js +262 -0
- package/dist/bootstrapProcessCaller.test.js.map +1 -0
- package/dist/bootstrapRegistryCaller.test.d.ts +2 -0
- package/dist/bootstrapRegistryCaller.test.d.ts.map +1 -0
- package/dist/bootstrapRegistryCaller.test.js +128 -0
- package/dist/bootstrapRegistryCaller.test.js.map +1 -0
- package/dist/bootstrapVaultScope.test.d.ts +2 -0
- package/dist/bootstrapVaultScope.test.d.ts.map +1 -0
- package/dist/bootstrapVaultScope.test.js +105 -0
- package/dist/bootstrapVaultScope.test.js.map +1 -0
- package/dist/codexBootstrapCompletion.test.d.ts +2 -0
- package/dist/codexBootstrapCompletion.test.d.ts.map +1 -0
- package/dist/codexBootstrapCompletion.test.js +630 -0
- package/dist/codexBootstrapCompletion.test.js.map +1 -0
- package/dist/codexBoundSession.d.ts +2 -2
- package/dist/codexBoundSession.d.ts.map +1 -1
- package/dist/codexBoundSession.js +23 -17
- package/dist/codexBoundSession.js.map +1 -1
- package/dist/codexBoundSession.test.js +3 -0
- package/dist/codexBoundSession.test.js.map +1 -1
- package/dist/codexInboxSession.d.ts.map +1 -1
- package/dist/codexInboxSession.js +3 -3
- package/dist/codexInboxSession.js.map +1 -1
- package/dist/codexScopedDispatch.test.d.ts +2 -0
- package/dist/codexScopedDispatch.test.d.ts.map +1 -0
- package/dist/codexScopedDispatch.test.js +56 -0
- package/dist/codexScopedDispatch.test.js.map +1 -0
- package/dist/codexSetupAdmission.d.ts +108 -0
- package/dist/codexSetupAdmission.d.ts.map +1 -0
- package/dist/codexSetupAdmission.js +214 -0
- package/dist/codexSetupAdmission.js.map +1 -0
- package/dist/codexSetupAdmission.test.d.ts +2 -0
- package/dist/codexSetupAdmission.test.d.ts.map +1 -0
- package/dist/codexSetupAdmission.test.js +578 -0
- package/dist/codexSetupAdmission.test.js.map +1 -0
- package/dist/codexSetupChildProvider.test.d.ts +2 -0
- package/dist/codexSetupChildProvider.test.d.ts.map +1 -0
- package/dist/codexSetupChildProvider.test.js +210 -0
- package/dist/codexSetupChildProvider.test.js.map +1 -0
- package/dist/codexSetupChildStartup.d.ts +38 -0
- package/dist/codexSetupChildStartup.d.ts.map +1 -0
- package/dist/codexSetupChildStartup.js +115 -0
- package/dist/codexSetupChildStartup.js.map +1 -0
- package/dist/codexSetupChildStartup.test.d.ts +2 -0
- package/dist/codexSetupChildStartup.test.d.ts.map +1 -0
- package/dist/codexSetupChildStartup.test.js +141 -0
- package/dist/codexSetupChildStartup.test.js.map +1 -0
- package/dist/codexSetupCliDispatch.test.d.ts +2 -0
- package/dist/codexSetupCliDispatch.test.d.ts.map +1 -0
- package/dist/codexSetupCliDispatch.test.js +138 -0
- package/dist/codexSetupCliDispatch.test.js.map +1 -0
- package/dist/codexSetupJournalOpen.test.d.ts +2 -0
- package/dist/codexSetupJournalOpen.test.d.ts.map +1 -0
- package/dist/codexSetupJournalOpen.test.js +101 -0
- package/dist/codexSetupJournalOpen.test.js.map +1 -0
- package/dist/codexSetupNativeHost.d.ts +31 -0
- package/dist/codexSetupNativeHost.d.ts.map +1 -0
- package/dist/codexSetupNativeHost.js +208 -0
- package/dist/codexSetupNativeHost.js.map +1 -0
- package/dist/codexSetupNativeHost.test.d.ts +2 -0
- package/dist/codexSetupNativeHost.test.d.ts.map +1 -0
- package/dist/codexSetupNativeHost.test.js +456 -0
- package/dist/codexSetupNativeHost.test.js.map +1 -0
- package/dist/codexSetupNativeWire.test.d.ts +2 -0
- package/dist/codexSetupNativeWire.test.d.ts.map +1 -0
- package/dist/codexSetupNativeWire.test.js +243 -0
- package/dist/codexSetupNativeWire.test.js.map +1 -0
- package/dist/codexSetupOwnedConnection.d.ts +14 -0
- package/dist/codexSetupOwnedConnection.d.ts.map +1 -0
- package/dist/codexSetupOwnedConnection.js +64 -0
- package/dist/codexSetupOwnedConnection.js.map +1 -0
- package/dist/codexSetupOwnedConnection.test.d.ts +2 -0
- package/dist/codexSetupOwnedConnection.test.d.ts.map +1 -0
- package/dist/codexSetupOwnedConnection.test.js +192 -0
- package/dist/codexSetupOwnedConnection.test.js.map +1 -0
- package/dist/codexSetupPortalCommand.test.d.ts +2 -0
- package/dist/codexSetupPortalCommand.test.d.ts.map +1 -0
- package/dist/codexSetupPortalCommand.test.js +146 -0
- package/dist/codexSetupPortalCommand.test.js.map +1 -0
- package/dist/codexSetupReadiness.d.ts +23 -0
- package/dist/codexSetupReadiness.d.ts.map +1 -0
- package/dist/codexSetupReadiness.js +74 -0
- package/dist/codexSetupReadiness.js.map +1 -0
- package/dist/codexSetupReadiness.test.d.ts +2 -0
- package/dist/codexSetupReadiness.test.d.ts.map +1 -0
- package/dist/codexSetupReadiness.test.js +145 -0
- package/dist/codexSetupReadiness.test.js.map +1 -0
- package/dist/codexSetupSessionCaller.test.d.ts +2 -0
- package/dist/codexSetupSessionCaller.test.d.ts.map +1 -0
- package/dist/codexSetupSessionCaller.test.js +352 -0
- package/dist/codexSetupSessionCaller.test.js.map +1 -0
- package/dist/codexSetupSessionHandoff.d.ts +48 -0
- package/dist/codexSetupSessionHandoff.d.ts.map +1 -0
- package/dist/codexSetupSessionHandoff.js +146 -0
- package/dist/codexSetupSessionHandoff.js.map +1 -0
- package/dist/codexSetupSessionHandoff.test.d.ts +2 -0
- package/dist/codexSetupSessionHandoff.test.d.ts.map +1 -0
- package/dist/codexSetupSessionHandoff.test.js +289 -0
- package/dist/codexSetupSessionHandoff.test.js.map +1 -0
- package/dist/codexSetupTransport.test.d.ts +2 -0
- package/dist/codexSetupTransport.test.d.ts.map +1 -0
- package/dist/codexSetupTransport.test.js +206 -0
- package/dist/codexSetupTransport.test.js.map +1 -0
- package/dist/codexSetupVaultJournal.d.ts +13 -0
- package/dist/codexSetupVaultJournal.d.ts.map +1 -0
- package/dist/codexSetupVaultJournal.js +28 -0
- package/dist/codexSetupVaultJournal.js.map +1 -0
- package/dist/codexSetupVaultJournal.test.d.ts +2 -0
- package/dist/codexSetupVaultJournal.test.d.ts.map +1 -0
- package/dist/codexSetupVaultJournal.test.js +104 -0
- package/dist/codexSetupVaultJournal.test.js.map +1 -0
- package/dist/containerChildCleanup.test.d.ts +2 -0
- package/dist/containerChildCleanup.test.d.ts.map +1 -0
- package/dist/containerChildCleanup.test.js +145 -0
- package/dist/containerChildCleanup.test.js.map +1 -0
- package/dist/containerRelayLifecycle.d.ts +1 -1
- package/dist/containerRelayLifecycle.d.ts.map +1 -1
- package/dist/containerRelayLifecycle.js +29 -37
- package/dist/containerRelayLifecycle.js.map +1 -1
- package/dist/containerRelayLifecycle.test.js +13 -0
- package/dist/containerRelayLifecycle.test.js.map +1 -1
- package/dist/cooperativeInboxSession.d.ts +2 -2
- package/dist/cooperativeInboxSession.d.ts.map +1 -1
- package/dist/cooperativeInboxSession.js +21 -13
- package/dist/cooperativeInboxSession.js.map +1 -1
- package/dist/cooperativeInboxSession.test.js +65 -0
- package/dist/cooperativeInboxSession.test.js.map +1 -1
- package/dist/demoWatchControl.d.ts +3 -3
- package/dist/index.d.ts +55 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1650 -557
- package/dist/index.js.map +1 -1
- package/dist/namedCodexSkillDiscovery.d.ts +15 -0
- package/dist/namedCodexSkillDiscovery.d.ts.map +1 -0
- package/dist/namedCodexSkillDiscovery.js +90 -0
- package/dist/namedCodexSkillDiscovery.js.map +1 -0
- package/dist/namedCodexSkillDiscovery.test.d.ts +2 -0
- package/dist/namedCodexSkillDiscovery.test.d.ts.map +1 -0
- package/dist/namedCodexSkillDiscovery.test.js +37 -0
- package/dist/namedCodexSkillDiscovery.test.js.map +1 -0
- package/dist/namedCodexThread.d.ts +2 -0
- package/dist/namedCodexThread.d.ts.map +1 -1
- package/dist/namedCodexThread.js +13 -4
- package/dist/namedCodexThread.js.map +1 -1
- package/dist/namedSessionChildOwner.d.ts +17 -0
- package/dist/namedSessionChildOwner.d.ts.map +1 -0
- package/dist/namedSessionChildOwner.js +122 -0
- package/dist/namedSessionChildOwner.js.map +1 -0
- package/dist/namedSessionChildOwner.test.d.ts +2 -0
- package/dist/namedSessionChildOwner.test.d.ts.map +1 -0
- package/dist/namedSessionChildOwner.test.js +203 -0
- package/dist/namedSessionChildOwner.test.js.map +1 -0
- package/dist/namedSessionEvidence.d.ts +2 -2
- package/dist/namedSessionEvidence.d.ts.map +1 -1
- package/dist/namedSessionEvidence.js +2 -2
- package/dist/namedSessionEvidence.js.map +1 -1
- package/dist/namedSessionEvidence.test.js +24 -1
- package/dist/namedSessionEvidence.test.js.map +1 -1
- package/dist/namedSessionRepositoryState.d.ts +2 -2
- package/dist/namedSessionRepositoryState.d.ts.map +1 -1
- package/dist/namedSessionService.d.ts +4 -2
- package/dist/namedSessionService.d.ts.map +1 -1
- package/dist/namedSessionService.js +18 -1
- package/dist/namedSessionService.js.map +1 -1
- package/dist/namedSessionService.test.js +31 -2
- package/dist/namedSessionService.test.js.map +1 -1
- package/dist/namedSessionTaskExportSync.d.ts +3 -3
- package/dist/namedSessionTaskExportSync.d.ts.map +1 -1
- package/dist/namedSessionTaskExportSync.js +6 -6
- package/dist/namedSessionTaskExportSync.js.map +1 -1
- package/dist/namedSessionTrust.d.ts +2 -2
- package/dist/namedSessionTrust.d.ts.map +1 -1
- package/dist/namedSessionTrust.js +3 -2
- package/dist/namedSessionTrust.js.map +1 -1
- package/dist/namedSessionTrust.test.js +2 -1
- package/dist/namedSessionTrust.test.js.map +1 -1
- package/dist/ownedChildLifecycle.d.ts +13 -0
- package/dist/ownedChildLifecycle.d.ts.map +1 -0
- package/dist/ownedChildLifecycle.js +87 -0
- package/dist/ownedChildLifecycle.js.map +1 -0
- package/dist/ownedChildLifecycle.test.d.ts +2 -0
- package/dist/ownedChildLifecycle.test.d.ts.map +1 -0
- package/dist/ownedChildLifecycle.test.js +133 -0
- package/dist/ownedChildLifecycle.test.js.map +1 -0
- package/dist/pidLock.test.js +242 -9
- package/dist/pidLock.test.js.map +1 -1
- package/dist/providerSessionChannel.d.ts +1 -1
- package/dist/providerSessionChannel.d.ts.map +1 -1
- package/dist/providerSessionChannel.js +7 -3
- package/dist/providerSessionChannel.js.map +1 -1
- package/dist/providerSessionChannel.test.js +20 -0
- package/dist/providerSessionChannel.test.js.map +1 -1
- package/dist/providerSessionReplyRecovery.d.ts +2 -2
- package/dist/providerSessionReplyRecovery.d.ts.map +1 -1
- package/dist/providerSessionReplyRecovery.js +3 -3
- package/dist/providerSessionReplyRecovery.js.map +1 -1
- package/dist/relayAutostart.d.ts.map +1 -1
- package/dist/relayAutostart.js +12 -9
- package/dist/relayAutostart.js.map +1 -1
- package/dist/relayRuntimeUpgrade.d.ts +1 -1
- package/dist/relayRuntimeUpgrade.d.ts.map +1 -1
- package/dist/relayRuntimeUpgrade.js +3 -1
- package/dist/relayRuntimeUpgrade.js.map +1 -1
- package/dist/relayRuntimeUpgrade.test.js +113 -0
- package/dist/relayRuntimeUpgrade.test.js.map +1 -1
- package/dist/relayRuntimeUpgradeCaller.test.js +12 -1
- package/dist/relayRuntimeUpgradeCaller.test.js.map +1 -1
- package/dist/relaySupervisor.d.ts.map +1 -1
- package/dist/relaySupervisor.js +19 -14
- package/dist/relaySupervisor.js.map +1 -1
- package/dist/relaySupervisor.test.js +26 -0
- package/dist/relaySupervisor.test.js.map +1 -1
- package/dist/relaySupervisorCleanup.test.d.ts +2 -0
- package/dist/relaySupervisorCleanup.test.d.ts.map +1 -0
- package/dist/relaySupervisorCleanup.test.js +69 -0
- package/dist/relaySupervisorCleanup.test.js.map +1 -0
- package/dist/repositoryInstallerFiles.d.ts +19 -1
- package/dist/repositoryInstallerFiles.d.ts.map +1 -1
- package/dist/repositoryInstallerFiles.js +205 -29
- package/dist/repositoryInstallerFiles.js.map +1 -1
- package/dist/repositoryInstallerScope.test.d.ts +2 -0
- package/dist/repositoryInstallerScope.test.d.ts.map +1 -0
- package/dist/repositoryInstallerScope.test.js +513 -0
- package/dist/repositoryInstallerScope.test.js.map +1 -0
- package/dist/repositoryKnowledge.d.ts.map +1 -1
- package/dist/repositoryKnowledge.js +177 -37
- package/dist/repositoryKnowledge.js.map +1 -1
- package/dist/repositoryKnowledgeScope.test.d.ts +2 -0
- package/dist/repositoryKnowledgeScope.test.d.ts.map +1 -0
- package/dist/repositoryKnowledgeScope.test.js +316 -0
- package/dist/repositoryKnowledgeScope.test.js.map +1 -0
- package/dist/repositoryOnboardingCaller.test.js +61 -4
- package/dist/repositoryOnboardingCaller.test.js.map +1 -1
- package/dist/repositoryPackage.d.ts.map +1 -1
- package/dist/repositoryPackage.js +340 -59
- package/dist/repositoryPackage.js.map +1 -1
- package/dist/repositoryPackageDelivery.test.js +65 -0
- package/dist/repositoryPackageDelivery.test.js.map +1 -1
- package/dist/repositoryPackageScope.test.d.ts +2 -0
- package/dist/repositoryPackageScope.test.d.ts.map +1 -0
- package/dist/repositoryPackageScope.test.js +578 -0
- package/dist/repositoryPackageScope.test.js.map +1 -0
- package/dist/schemas/codex-setup-child-start.schema.json +12 -0
- package/dist/schemas/codex-setup-intent.schema.json +27 -0
- package/dist/schemas/local-codex-setup-session.schema.json +39 -0
- package/dist/schemas/setup-claim-failure.schema.json +15 -0
- package/dist/skillInstallationScope.test.d.ts +2 -0
- package/dist/skillInstallationScope.test.d.ts.map +1 -0
- package/dist/skillInstallationScope.test.js +64 -0
- package/dist/skillInstallationScope.test.js.map +1 -0
- package/dist/skillPreparationCache.test.js +12 -4
- package/dist/skillPreparationCache.test.js.map +1 -1
- package/dist/skillPreparationCaller.test.js +31 -1
- package/dist/skillPreparationCaller.test.js.map +1 -1
- package/dist/startupCommitScope.test.d.ts +2 -0
- package/dist/startupCommitScope.test.d.ts.map +1 -0
- package/dist/startupCommitScope.test.js +172 -0
- package/dist/startupCommitScope.test.js.map +1 -0
- package/dist/workspaceRegistry.d.ts.map +1 -1
- package/dist/workspaceRegistry.js +7 -3
- package/dist/workspaceRegistry.js.map +1 -1
- package/dist/workspaceRegistry.test.js +51 -0
- package/dist/workspaceRegistry.test.js.map +1 -1
- package/dist/workspaceRegistryRecovery.d.ts.map +1 -1
- package/dist/workspaceRegistryRecovery.js +171 -64
- package/dist/workspaceRegistryRecovery.js.map +1 -1
- package/dist/workspaceRegistryRecoveryScope.test.d.ts +2 -0
- package/dist/workspaceRegistryRecoveryScope.test.d.ts.map +1 -0
- package/dist/workspaceRegistryRecoveryScope.test.js +416 -0
- package/dist/workspaceRegistryRecoveryScope.test.js.map +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
import { loadOrCreateInstallationId } from './index.js';
|
|
8
|
+
import { runCodexBootstrapHost } from './bootstrapHostScope.js';
|
|
9
|
+
async function fixture(t) {
|
|
10
|
+
const root = await mkdtemp(resolve(tmpdir(), 'dharma-installation-scope-'));
|
|
11
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
12
|
+
const now = Date.now(), digest = `sha256:${'a'.repeat(64)}`;
|
|
13
|
+
const id = (n) => `${String(n).padStart(8, '0')}-1111-4111-8111-111111111111`;
|
|
14
|
+
return { root, path: resolve(root, 'state', 'installation.json'), input: { workspace: root,
|
|
15
|
+
signal: new AbortController().signal, current: async () => true, intent: {
|
|
16
|
+
schema: 'dharma.codex-setup-intent/v1', operationId: id(1), setupReference: id(2),
|
|
17
|
+
organizationId: 'org_demo', recipientMembershipId: id(3), origin: 'https://hq.example',
|
|
18
|
+
repositoryFingerprint: digest, policyRevision: 'policy-v1', scopeDigest: digest, contractDigest: digest,
|
|
19
|
+
hostContextId: id(4), issuedAt: new Date(now - 1000).toISOString(), expiresAt: new Date(now + 60_000).toISOString()
|
|
20
|
+
} } };
|
|
21
|
+
}
|
|
22
|
+
test('installation creation refuses a closed owning scope before any filesystem effect', async (t) => {
|
|
23
|
+
const f = await fixture(t);
|
|
24
|
+
let outcome = [];
|
|
25
|
+
await assert.rejects(runCodexBootstrapHost(f.input, async (prepared) => {
|
|
26
|
+
prepared.scope.close();
|
|
27
|
+
outcome = await Promise.allSettled([loadOrCreateInstallationId(f.path)]);
|
|
28
|
+
}), { message: 'codex_setup_host_scope_unavailable' });
|
|
29
|
+
assert.equal(outcome[0]?.status, 'rejected');
|
|
30
|
+
if (outcome[0]?.status === 'rejected')
|
|
31
|
+
assert.equal(outcome[0].reason.message, 'codex_setup_host_scope_unavailable');
|
|
32
|
+
assert.deepEqual(await readdir(f.root), []);
|
|
33
|
+
});
|
|
34
|
+
test('installation creation preserves a partial owned file after withdrawal without returning an identity', async (t) => {
|
|
35
|
+
const f = await fixture(t);
|
|
36
|
+
let returned = false;
|
|
37
|
+
await assert.rejects(runCodexBootstrapHost({ ...f.input,
|
|
38
|
+
current: async () => !existsSync(f.path) || readFileSync(f.path).length === 0 }, async () => { const value = await loadOrCreateInstallationId(f.path); returned = true; return value; }), { message: 'codex_setup_host_scope_unavailable' });
|
|
39
|
+
assert.equal(returned, false);
|
|
40
|
+
const value = JSON.parse(await readFile(f.path, 'utf8'));
|
|
41
|
+
assert.equal(value.schema, 'dharma.installation-identity/v1');
|
|
42
|
+
assert.match(value.installationId, /^[a-f0-9-]{36}$/);
|
|
43
|
+
// The cleanup must have closed the acquired handle; this is not a replacement identity.
|
|
44
|
+
await rm(f.path);
|
|
45
|
+
assert.deepEqual(await readdir(resolve(f.root, 'state')), []);
|
|
46
|
+
});
|
|
47
|
+
test('installation creation stops after owned acquisition when authority is withdrawn', async (t) => {
|
|
48
|
+
const f = await fixture(t);
|
|
49
|
+
await assert.rejects(runCodexBootstrapHost({ ...f.input, current: async () => !existsSync(f.path) }, () => loadOrCreateInstallationId(f.path)), { message: 'codex_setup_host_scope_unavailable' });
|
|
50
|
+
assert.equal((await readFile(f.path)).length, 0);
|
|
51
|
+
await rm(f.path);
|
|
52
|
+
});
|
|
53
|
+
test('current-scoped installation preserves ordinary durable UUID reuse', async (t) => {
|
|
54
|
+
const f = await fixture(t);
|
|
55
|
+
const first = await runCodexBootstrapHost(f.input, () => loadOrCreateInstallationId(f.path));
|
|
56
|
+
const before = await readFile(f.path, 'utf8');
|
|
57
|
+
assert.equal(await runCodexBootstrapHost(f.input, () => loadOrCreateInstallationId(f.path)), first);
|
|
58
|
+
assert.equal(await loadOrCreateInstallationId(f.path), first);
|
|
59
|
+
assert.equal(await readFile(f.path, 'utf8'), before);
|
|
60
|
+
});
|
|
61
|
+
test('scoped corrupt installation is preserved and parser diagnostics are withheld', async (t) => {
|
|
62
|
+
const f = await fixture(t), path = resolve(f.root, 'installation.json');
|
|
63
|
+
const original = '{private-installation-parser-canary';
|
|
64
|
+
await writeFile(path, original);
|
|
65
|
+
await assert.rejects(runCodexBootstrapHost(f.input, () => loadOrCreateInstallationId(path)), { message: 'codex_setup_host_installation_failed' });
|
|
66
|
+
assert.equal(await readFile(path, 'utf8'), original);
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=bootstrapInstallationScope.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrapInstallationScope.test.js","sourceRoot":"","sources":["../src/bootstrapInstallationScope.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrapLauncherCaller.test.d.ts","sourceRoot":"","sources":["../src/bootstrapLauncherCaller.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import * as fs from 'node:fs/promises';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { dirname, resolve } from 'node:path';
|
|
6
|
+
import { runInNewContext } from 'node:vm';
|
|
7
|
+
import test from 'node:test';
|
|
8
|
+
import ts from 'typescript';
|
|
9
|
+
import { currentBootstrapHostScope, runCodexBootstrapHost } from './bootstrapHostScope.js';
|
|
10
|
+
import { writeBootstrapHostText } from './bootstrapHostFiles.js';
|
|
11
|
+
import { stableRepositoryLauncherContents } from './repositoryLaunchers.js';
|
|
12
|
+
async function fixture(t) {
|
|
13
|
+
const root = await fs.realpath(await fs.mkdtemp(resolve(tmpdir(), 'dharma-launcher-caller-')));
|
|
14
|
+
t.after(() => fs.rm(root, { recursive: true, force: true }));
|
|
15
|
+
const now = Date.now(), digest = `sha256:${'a'.repeat(64)}`;
|
|
16
|
+
const id = (n) => `${String(n).padStart(8, '0')}-1111-4111-8111-111111111111`;
|
|
17
|
+
return { root, bin: resolve(root, '.dharma', 'bin'), input: { workspace: root,
|
|
18
|
+
current: async () => true, signal: new AbortController().signal,
|
|
19
|
+
intent: { schema: 'dharma.codex-setup-intent/v1', operationId: id(1), setupReference: id(2),
|
|
20
|
+
organizationId: 'org_demo', recipientMembershipId: id(3), origin: 'https://hq.example',
|
|
21
|
+
repositoryFingerprint: digest, policyRevision: 'policy-v1', scopeDigest: digest, contractDigest: digest,
|
|
22
|
+
hostContextId: id(4), issuedAt: new Date(now - 1000).toISOString(), expiresAt: new Date(now + 60_000).toISOString() } } };
|
|
23
|
+
}
|
|
24
|
+
// Execute the actual CLI installer declaration with real filesystem effects.
|
|
25
|
+
async function caller() {
|
|
26
|
+
const source = await fs.readFile(new URL('../src/index.ts', import.meta.url), 'utf8');
|
|
27
|
+
const ast = ts.createSourceFile('index.ts', source, ts.ScriptTarget.Latest, true);
|
|
28
|
+
const nodes = ast.statements.filter(node => ts.isFunctionDeclaration(node) && node.name?.text === 'installStableRepositoryLauncher');
|
|
29
|
+
assert.equal(nodes.length, 1);
|
|
30
|
+
const output = ts.transpileModule(nodes[0].getText(ast), { compilerOptions: {
|
|
31
|
+
target: ts.ScriptTarget.ES2023, module: ts.ModuleKind.None
|
|
32
|
+
}, reportDiagnostics: true });
|
|
33
|
+
assert.equal(output.diagnostics?.filter(item => item.category === ts.DiagnosticCategory.Error).length, 0);
|
|
34
|
+
return runInNewContext(`${output.outputText}\ninstallStableRepositoryLauncher`, { ...fs, process, resolve, dirname,
|
|
35
|
+
currentBootstrapHostScope, writeBootstrapHostText, stableRepositoryLauncherContents, VERSION: '0.2.153' }, { timeout: 1000, contextCodeGeneration: { strings: false, wasm: false } });
|
|
36
|
+
}
|
|
37
|
+
test('actual launcher installer denies a withdrawn owner before creating directories', async (t) => {
|
|
38
|
+
const f = await fixture(t), install = await caller();
|
|
39
|
+
await assert.rejects(runCodexBootstrapHost(f.input, async ({ scope }) => {
|
|
40
|
+
scope.close();
|
|
41
|
+
return install(f.root);
|
|
42
|
+
}), { message: 'codex_setup_host_scope_unavailable' });
|
|
43
|
+
assert.deepEqual(await fs.readdir(f.root), []);
|
|
44
|
+
});
|
|
45
|
+
test('actual launcher installer publishes exact pinned bytes under the original owning scope', async (t) => {
|
|
46
|
+
const f = await fixture(t), install = await caller();
|
|
47
|
+
const result = await runCodexBootstrapHost(f.input, () => install(f.root));
|
|
48
|
+
assert.equal(result.shell, '.dharma/bin/dharma');
|
|
49
|
+
assert.equal(result.windows, '.dharma/bin/dharma.cmd');
|
|
50
|
+
const expected = stableRepositoryLauncherContents('0.2.153', { platform: process.platform, nodeDirectory: dirname(process.execPath) });
|
|
51
|
+
assert.equal(await fs.readFile(resolve(f.bin, 'dharma'), 'utf8'), expected.shell);
|
|
52
|
+
assert.equal(await fs.readFile(resolve(f.bin, 'dharma.cmd'), 'utf8'), expected.windows);
|
|
53
|
+
assert.deepEqual((await fs.readdir(f.bin)).sort(), ['dharma', 'dharma.cmd']);
|
|
54
|
+
if (process.platform !== 'win32') {
|
|
55
|
+
assert.equal((await fs.stat(resolve(f.bin, 'dharma'))).mode & 0o777, 0o700);
|
|
56
|
+
assert.equal((await fs.stat(resolve(f.bin, 'dharma.cmd'))).mode & 0o777, 0o600);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
test('actual launcher installer cannot continue to the second file after authority is withdrawn', async (t) => {
|
|
60
|
+
const f = await fixture(t), install = await caller();
|
|
61
|
+
await fs.mkdir(f.bin, { recursive: true, mode: 0o700 });
|
|
62
|
+
await fs.writeFile(resolve(f.bin, 'dharma'), 'prior-shell\n');
|
|
63
|
+
await fs.writeFile(resolve(f.bin, 'dharma.cmd'), 'prior-cmd\n');
|
|
64
|
+
const input = { ...f.input, current: async () => readFileSync(resolve(f.bin, 'dharma'), 'utf8') === 'prior-shell\n' };
|
|
65
|
+
await assert.rejects(runCodexBootstrapHost(input, () => install(f.root)), { message: 'codex_setup_host_scope_unavailable' });
|
|
66
|
+
assert.match(await fs.readFile(resolve(f.bin, 'dharma'), 'utf8'), /agent-fabric@0\.2\.153/);
|
|
67
|
+
assert.equal(await fs.readFile(resolve(f.bin, 'dharma.cmd'), 'utf8'), 'prior-cmd\n');
|
|
68
|
+
// A completed first publication is retained, not falsely reported as rolled back.
|
|
69
|
+
assert.deepEqual((await fs.readdir(f.bin)).sort(), ['dharma', 'dharma.cmd']);
|
|
70
|
+
});
|
|
71
|
+
test('actual launcher installer rejects an aliased setup directory without writing its target', async (t) => {
|
|
72
|
+
const f = await fixture(t), other = await fixture(t), install = await caller();
|
|
73
|
+
await fs.writeFile(resolve(other.root, 'preserve'), 'sibling\n');
|
|
74
|
+
await fs.symlink(other.root, resolve(f.root, '.dharma'), process.platform === 'win32' ? 'junction' : 'dir');
|
|
75
|
+
await assert.rejects(runCodexBootstrapHost(f.input, () => install(f.root)), { message: 'codex_setup_host_launcher_path_invalid' });
|
|
76
|
+
assert.deepEqual(await fs.readdir(other.root), ['preserve']);
|
|
77
|
+
assert.equal(existsSync(resolve(other.root, 'bin')), false);
|
|
78
|
+
});
|
|
79
|
+
test('actual launcher installer cannot report success after its final publication loses acknowledgement', async (t) => {
|
|
80
|
+
const f = await fixture(t), install = await caller();
|
|
81
|
+
const cmd = resolve(f.bin, 'dharma.cmd');
|
|
82
|
+
await assert.rejects(runCodexBootstrapHost({ ...f.input, current: async () => !existsSync(cmd) }, () => install(f.root)), { message: 'codex_setup_host_scope_unavailable' });
|
|
83
|
+
assert.match(await fs.readFile(cmd, 'utf8'), /agent-fabric@0\.2\.153/);
|
|
84
|
+
assert.match(await fs.readFile(resolve(f.bin, 'dharma'), 'utf8'), /agent-fabric@0\.2\.153/);
|
|
85
|
+
});
|
|
86
|
+
test('legacy launcher installation retains its existing supported contents without a native scope', async (t) => {
|
|
87
|
+
const f = await fixture(t), install = await caller();
|
|
88
|
+
await install(f.root);
|
|
89
|
+
assert.match(await fs.readFile(resolve(f.bin, 'dharma'), 'utf8'), /agent-fabric@0\.2\.153/);
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=bootstrapLauncherCaller.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrapLauncherCaller.test.js","sourceRoot":"","sources":["../src/bootstrapLauncherCaller.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrapOwnedChildren.test.d.ts","sourceRoot":"","sources":["../src/bootstrapOwnedChildren.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { EventEmitter } from 'node:events';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
import { captureBootstrapHostChild, drainBootstrapHostChildren, prepareCodexBootstrapHost, runCodexBootstrapHost, runCodexBootstrapHostScope } from './bootstrapHostScope.js';
|
|
7
|
+
import { watchOwnedChild } from './ownedChildLifecycle.js';
|
|
8
|
+
const id = (n) => `${String(n).padStart(8, '0')}-1111-4111-8111-111111111111`;
|
|
9
|
+
function input() {
|
|
10
|
+
const now = Date.now(), hash = `sha256:${'a'.repeat(64)}`;
|
|
11
|
+
return { workspace: resolve('synthetic-source'), signal: new AbortController().signal, current: async () => true,
|
|
12
|
+
intent: { schema: 'dharma.codex-setup-intent/v1', operationId: id(1), setupReference: id(2),
|
|
13
|
+
organizationId: 'org_demo', recipientMembershipId: id(3), origin: 'https://hq.example',
|
|
14
|
+
repositoryFingerprint: hash, policyRevision: 'policy-v1', scopeDigest: hash, contractDigest: hash,
|
|
15
|
+
hostContextId: id(4), issuedAt: new Date(now - 1000).toISOString(), expiresAt: new Date(now + 60_000).toISOString() } };
|
|
16
|
+
}
|
|
17
|
+
function childFixture() {
|
|
18
|
+
const child = new EventEmitter();
|
|
19
|
+
Object.assign(child, { pid: 12345, exitCode: null, signalCode: null });
|
|
20
|
+
let signals = 0;
|
|
21
|
+
child.kill = () => { signals++; queueMicrotask(() => child.emit('exit', null, 'SIGTERM')); return true; };
|
|
22
|
+
return { child, get signals() { return signals; } };
|
|
23
|
+
}
|
|
24
|
+
test('original scope owns a captured child beyond a borrowed continuation, then drains it once', async () => {
|
|
25
|
+
const prepared = prepareCodexBootstrapHost(input()), f = childFixture();
|
|
26
|
+
await runCodexBootstrapHostScope(prepared.scope, async () => {
|
|
27
|
+
captureBootstrapHostChild(prepared.scope, f.child);
|
|
28
|
+
captureBootstrapHostChild(prepared.scope, f.child);
|
|
29
|
+
});
|
|
30
|
+
assert.equal(prepared.scope.signal.aborted, false);
|
|
31
|
+
assert.equal(f.signals, 0);
|
|
32
|
+
await Promise.all([drainBootstrapHostChildren(prepared.scope), drainBootstrapHostChildren(prepared.scope)]);
|
|
33
|
+
assert.equal(f.signals, 1);
|
|
34
|
+
assert.equal(prepared.scope.signal.aborted, true);
|
|
35
|
+
});
|
|
36
|
+
test('original scope cannot adopt another scope child or capture outside its borrowed frame', async () => {
|
|
37
|
+
const first = prepareCodexBootstrapHost(input()), second = prepareCodexBootstrapHost(input()), f = childFixture();
|
|
38
|
+
assert.throws(() => captureBootstrapHostChild(first.scope, f.child), /^Error: codex_setup_host_child_owner_invalid$/);
|
|
39
|
+
await runCodexBootstrapHostScope(first.scope, async () => captureBootstrapHostChild(first.scope, f.child));
|
|
40
|
+
await assert.rejects(runCodexBootstrapHostScope(second.scope, async () => captureBootstrapHostChild(second.scope, f.child)), /^Error: codex_setup_host_child_owner_invalid$/);
|
|
41
|
+
assert.equal(f.signals, 0);
|
|
42
|
+
await drainBootstrapHostChildren(second.scope);
|
|
43
|
+
assert.equal(f.signals, 0);
|
|
44
|
+
await drainBootstrapHostChildren(first.scope);
|
|
45
|
+
assert.equal(f.signals, 1);
|
|
46
|
+
});
|
|
47
|
+
test('original scope captures an acquired child even when spawn itself triggered withdrawal', async () => {
|
|
48
|
+
const prepared = prepareCodexBootstrapHost(input()), f = childFixture();
|
|
49
|
+
await assert.rejects(runCodexBootstrapHostScope(prepared.scope, async () => {
|
|
50
|
+
prepared.scope.close();
|
|
51
|
+
captureBootstrapHostChild(prepared.scope, f.child);
|
|
52
|
+
}), /^Error: codex_setup_host_scope_unavailable$/);
|
|
53
|
+
await drainBootstrapHostChildren(prepared.scope);
|
|
54
|
+
assert.equal(f.signals, 1);
|
|
55
|
+
});
|
|
56
|
+
test('original scope drain waits for exit rather than an owned signal error', async () => {
|
|
57
|
+
const prepared = prepareCodexBootstrapHost(input()), f = childFixture();
|
|
58
|
+
let settled = false;
|
|
59
|
+
f.child.kill = () => { f.child.emit('error', new Error('PRIVATE_SIGNAL_CANARY')); return true; };
|
|
60
|
+
await runCodexBootstrapHostScope(prepared.scope, async () => captureBootstrapHostChild(prepared.scope, f.child));
|
|
61
|
+
const drain = drainBootstrapHostChildren(prepared.scope).then(() => { settled = true; });
|
|
62
|
+
await new Promise(done => setImmediate(done));
|
|
63
|
+
assert.equal(settled, false);
|
|
64
|
+
f.child.emit('exit', null, 'SIGTERM');
|
|
65
|
+
await drain;
|
|
66
|
+
assert.equal(settled, true);
|
|
67
|
+
});
|
|
68
|
+
test('original scope rejects a forged drain and does not signal an uncaptured child', async () => {
|
|
69
|
+
const original = prepareCodexBootstrapHost(input()), f = childFixture();
|
|
70
|
+
await assert.rejects(drainBootstrapHostChildren({ ...original.scope }), /^Error: codex_setup_host_child_owner_invalid$/);
|
|
71
|
+
await drainBootstrapHostChildren(original.scope);
|
|
72
|
+
assert.equal(f.signals, 0);
|
|
73
|
+
});
|
|
74
|
+
test('direct original scope failure drains only its actual C-only spawned child', async () => {
|
|
75
|
+
const unrelated = spawn(process.execPath, ['-e', 'setInterval(() => {}, 1000)'], { stdio: 'ignore' });
|
|
76
|
+
let owned;
|
|
77
|
+
try {
|
|
78
|
+
await assert.rejects(runCodexBootstrapHost(input(), async (prepared) => {
|
|
79
|
+
owned = spawn(process.execPath, ['-e', 'setInterval(() => {}, 1000)'], { stdio: 'ignore' });
|
|
80
|
+
captureBootstrapHostChild(prepared.scope, owned);
|
|
81
|
+
throw new Error('synthetic continuation failure');
|
|
82
|
+
}), /^Error: synthetic continuation failure$/);
|
|
83
|
+
assert.ok(owned && (owned.exitCode !== null || owned.signalCode !== null));
|
|
84
|
+
assert.equal(unrelated.exitCode, null);
|
|
85
|
+
assert.equal(unrelated.signalCode, null);
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
await watchOwnedChild(unrelated).stop({ graceMs: 1000 });
|
|
89
|
+
if (owned)
|
|
90
|
+
await watchOwnedChild(owned).stop({ graceMs: 1000 });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=bootstrapOwnedChildren.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrapOwnedChildren.test.js","sourceRoot":"","sources":["../src/bootstrapOwnedChildren.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAC,KAAK,EAAoB,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AACzC,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAC,yBAAyB,EAAE,0BAA0B,EAAE,yBAAyB,EACtF,qBAAqB,EAAE,0BAA0B,EAAC,MAAM,yBAAyB,CAAC;AACpF,OAAO,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAEzD,MAAM,EAAE,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,8BAA8B,CAAC;AACtF,SAAS,KAAK;IACZ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,GAAG,UAAU,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IAC1D,OAAO,EAAC,SAAS,EAAE,OAAO,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,IAAI,eAAe,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;QAC7G,MAAM,EAAE,EAAC,MAAM,EAAE,8BAAuC,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC;YACjG,cAAc,EAAE,UAAU,EAAE,qBAAqB,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,oBAAoB;YACtF,qBAAqB,EAAE,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI;YACjG,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,EAAC,EAAC,CAAC;AAC5H,CAAC;AACD,SAAS,YAAY;IACnB,MAAM,KAAK,GAAG,IAAI,YAAY,EAAkB,CAAC;IACjD,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,EAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAC,CAAC,CAAC;IACrE,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,CAAC,IAAI,GAAG,GAAG,EAAE,GAAE,OAAO,EAAE,CAAC,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAA,CAAC,CAAC;IACxG,OAAO,EAAC,KAAK,EAAE,IAAI,OAAO,KAAI,OAAO,OAAO,CAAC,CAAA,CAAC,EAAC,CAAC;AAClD,CAAC;AAED,IAAI,CAAC,0FAA0F,EAAE,KAAK,IAAI,EAAE;IAC1G,MAAM,QAAQ,GAAG,yBAAyB,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC;IACxE,MAAM,0BAA0B,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;QAC1D,yBAAyB,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;QACnD,yBAAyB,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC/E,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,0BAA0B,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,0BAA0B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAChF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uFAAuF,EAAE,KAAK,IAAI,EAAE;IACvG,MAAM,KAAK,GAAG,yBAAyB,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,GAAG,yBAAyB,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC;IAClH,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,yBAAyB,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,+CAA+C,CAAC,CAAC;IACtH,MAAM,0BAA0B,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,yBAAyB,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3G,MAAM,MAAM,CAAC,OAAO,CAAC,0BAA0B,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,yBAAyB,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EACzH,+CAA+C,CAAC,CAAC;IACnD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC3B,MAAM,0BAA0B,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC3E,MAAM,0BAA0B,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC5E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uFAAuF,EAAE,KAAK,IAAI,EAAE;IACvG,MAAM,QAAQ,GAAG,yBAAyB,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC;IACxE,MAAM,MAAM,CAAC,OAAO,CAAC,0BAA0B,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;QACzE,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAAC,yBAAyB,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IAC7E,CAAC,CAAC,EAAE,6CAA6C,CAAC,CAAC;IACnD,MAAM,0BAA0B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC/E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;IACvF,MAAM,QAAQ,GAAG,yBAAyB,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC;IAAC,IAAI,OAAO,GAAG,KAAK,CAAC;IAC7F,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAA,CAAC,CAAC;IAC/F,MAAM,0BAA0B,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,yBAAyB,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACjH,MAAM,KAAK,GAAG,0BAA0B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAE,OAAO,GAAG,IAAI,CAAC,CAAA,CAAC,CAAC,CAAC;IACvF,MAAM,IAAI,OAAO,CAAO,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IAAC,MAAM,KAAK,CAAC;IAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAClF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+EAA+E,EAAE,KAAK,IAAI,EAAE;IAC/F,MAAM,QAAQ,GAAG,yBAAyB,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC;IACxE,MAAM,MAAM,CAAC,OAAO,CAAC,0BAA0B,CAAC,EAAC,GAAG,QAAQ,CAAC,KAAK,EAAC,CAAC,EAAE,+CAA+C,CAAC,CAAC;IACvH,MAAM,0BAA0B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC/E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;IAC3F,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,6BAA6B,CAAC,EAAE,EAAC,KAAK,EAAE,QAAQ,EAAC,CAAC,CAAC;IACpG,IAAI,KAA+B,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,KAAK,EAAE,EAAE,KAAK,EAAC,QAAQ,EAAC,EAAE;YACnE,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,6BAA6B,CAAC,EAAE,EAAC,KAAK,EAAE,QAAQ,EAAC,CAAC,CAAC;YAC1F,yBAAyB,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpD,CAAC,CAAC,EAAE,yCAAyC,CAAC,CAAC;QAC/C,MAAM,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACnF,CAAC;YAAS,CAAC;QAAA,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC;QAAC,IAAI,KAAK;YAAE,MAAM,eAAe,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC;IAAA,CAAC;AACpI,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrapPolicyScope.test.d.ts","sourceRoot":"","sources":["../src/bootstrapPolicyScope.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import * as fs from 'node:fs/promises';
|
|
3
|
+
import { createHash, generateKeyPairSync, randomUUID } from 'node:crypto';
|
|
4
|
+
import { dirname, resolve } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { runInNewContext } from 'node:vm';
|
|
7
|
+
import test from 'node:test';
|
|
8
|
+
import ts from 'typescript';
|
|
9
|
+
import { assertPolicy, loadOrganizationPolicy } from '@dharma-ai-labs/agent-fabric-policy';
|
|
10
|
+
import { signCanonicalObject } from '@dharma-ai-labs/agent-fabric-contracts';
|
|
11
|
+
import { currentBootstrapHostScope, runCodexBootstrapHost } from './bootstrapHostScope.js';
|
|
12
|
+
import { writeBootstrapHostJson } from './bootstrapHostFiles.js';
|
|
13
|
+
import { applyServerEvidencePolicy, pathExistsOrThrow } from './index.js';
|
|
14
|
+
async function fixture(t) {
|
|
15
|
+
const root = await fs.mkdtemp(resolve(tmpdir(), 'dharma-policy-scope-'));
|
|
16
|
+
t.after(() => fs.rm(root, { recursive: true, force: true }));
|
|
17
|
+
await fs.writeFile(resolve(root, 'package.json'), JSON.stringify({ scripts: { test: 'node --test' } }));
|
|
18
|
+
const now = Date.now(), digest = `sha256:${'a'.repeat(64)}`;
|
|
19
|
+
const id = (n) => `${String(n).padStart(8, '0')}-1111-4111-8111-111111111111`;
|
|
20
|
+
return { root, workspaceId: id(5), policyPath: resolve(root, '.dharma', 'approved-policy.json'),
|
|
21
|
+
input: { workspace: root, current: async () => true, signal: new AbortController().signal,
|
|
22
|
+
intent: { schema: 'dharma.codex-setup-intent/v1', operationId: id(1), setupReference: id(2),
|
|
23
|
+
organizationId: 'org_demo', recipientMembershipId: id(3), origin: 'https://hq.example',
|
|
24
|
+
repositoryFingerprint: digest, policyRevision: 'policy-v1', scopeDigest: digest, contractDigest: digest,
|
|
25
|
+
hostContextId: id(4), issuedAt: new Date(now - 1000).toISOString(), expiresAt: new Date(now + 60_000).toISOString() } } };
|
|
26
|
+
}
|
|
27
|
+
async function callers(root, overrides = {}) {
|
|
28
|
+
const source = await fs.readFile(new URL('../src/index.ts', import.meta.url), 'utf8');
|
|
29
|
+
const ast = ts.createSourceFile('index.ts', source, ts.ScriptTarget.Latest, true);
|
|
30
|
+
const names = ['materializeWorkspacePolicy', 'workspaceAuthorizationStatePath', 'assertWorkspaceAuthorizationCurrent',
|
|
31
|
+
'applyWorkspaceAuthorizationAtomically', 'newEvidenceUploadLedger', 'assertEvidenceLedger',
|
|
32
|
+
'evidenceLedgerForPolicyActivation', 'pathExists', 'acquirePidLock', 'withFileLock', 'writeJsonAtomic'];
|
|
33
|
+
const declarations = names.map(name => {
|
|
34
|
+
const nodes = ast.statements.filter(node => ts.isFunctionDeclaration(node) && node.name?.text === name);
|
|
35
|
+
assert.equal(nodes.length, 1);
|
|
36
|
+
return nodes[0].getText(ast);
|
|
37
|
+
});
|
|
38
|
+
const output = ts.transpileModule(declarations.join('\n'), { compilerOptions: {
|
|
39
|
+
target: ts.ScriptTarget.ES2023, module: ts.ModuleKind.None
|
|
40
|
+
}, reportDiagnostics: true });
|
|
41
|
+
assert.equal(output.diagnostics?.filter(item => item.category === ts.DiagnosticCategory.Error).length, 0);
|
|
42
|
+
return runInNewContext(`${output.outputText}\n({${names.join(',')}})`, { ...fs, dirname, resolve, createHash, randomUUID,
|
|
43
|
+
Buffer, process, Date, setTimeout, structuredClone, exports: {}, currentBootstrapHostScope,
|
|
44
|
+
// VM literals belong to another realm; production callers and the writer
|
|
45
|
+
// share a realm. Bridge fixture values without replacing actual file IO.
|
|
46
|
+
writeBootstrapHostJson: (path, value, scope) => writeBootstrapHostJson(path, structuredClone(value), scope),
|
|
47
|
+
pathExistsOrThrow, assertPolicy, loadOrganizationPolicy, applyServerEvidencePolicy,
|
|
48
|
+
dharmaHome: () => root, evidenceUploadLedgerPath: () => resolve(root, 'relay', 'evidence-upload-ledger.json'),
|
|
49
|
+
...overrides }, { timeout: 1000, contextCodeGeneration: { strings: false, wasm: false } });
|
|
50
|
+
}
|
|
51
|
+
function signed(workspaceId) {
|
|
52
|
+
const keys = generateKeyPairSync('ed25519'), now = Date.now();
|
|
53
|
+
const unsigned = { schema: 'dharma.workspace-policy-authorization/v1', organizationId: 'org_demo', workspaceId,
|
|
54
|
+
policy: { revision: 'signed-test-policy', evidence: { automaticDisclosure: { mode: 'customer_authorized_content',
|
|
55
|
+
consentReceiptId: 'synthetic-consent', allowedContentClasses: ['native_provider_payload'] },
|
|
56
|
+
maximumCapsuleBytes: 1000, maximumDailyUploadBytes: 10000, maximumExpansionBytes: 100,
|
|
57
|
+
excludePaths: ['**/.env', '**/*.key'], pseudonymizeIdentity: true } },
|
|
58
|
+
issuedAt: new Date(now - 1000).toISOString(), expiresAt: new Date(now + 60_000).toISOString(), keyVersion: 'synthetic' };
|
|
59
|
+
return { envelope: { ...unsigned, signature: signCanonicalObject(unsigned, keys.privateKey) },
|
|
60
|
+
publicKey: keys.publicKey.export({ format: 'jwk' }).x };
|
|
61
|
+
}
|
|
62
|
+
test('policy scope refuses materialization and replay inspection before closed-owner reads', async (t) => {
|
|
63
|
+
const f = await fixture(t);
|
|
64
|
+
let reads = 0;
|
|
65
|
+
const c = await callers(f.root, { readFile: async (...args) => { reads++; return fs.readFile(...args); } });
|
|
66
|
+
for (const replay of [false, true]) {
|
|
67
|
+
let result = [];
|
|
68
|
+
await assert.rejects(runCodexBootstrapHost(f.input, async ({ scope }) => {
|
|
69
|
+
scope.close();
|
|
70
|
+
result = await Promise.allSettled([replay
|
|
71
|
+
? c.assertWorkspaceAuthorizationCurrent(f.workspaceId, { issuedAt: new Date().toISOString(), signature: 'synthetic' }, false)
|
|
72
|
+
: c.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'local' })]);
|
|
73
|
+
}), { message: 'codex_setup_host_scope_unavailable' });
|
|
74
|
+
assert.equal(result[0]?.status, 'rejected');
|
|
75
|
+
}
|
|
76
|
+
assert.equal(reads, 0);
|
|
77
|
+
await assert.rejects(fs.lstat(f.policyPath), { code: 'ENOENT' });
|
|
78
|
+
});
|
|
79
|
+
test('policy scope cannot swallow cancellation in optional package inspection', async (t) => {
|
|
80
|
+
const f = await fixture(t);
|
|
81
|
+
let laterAccess = 0, returned = false;
|
|
82
|
+
await assert.rejects(runCodexBootstrapHost(f.input, async ({ scope }) => {
|
|
83
|
+
const c = await callers(f.root, { readFile: async (...args) => {
|
|
84
|
+
const value = await fs.readFile(...args);
|
|
85
|
+
scope.close();
|
|
86
|
+
return value;
|
|
87
|
+
}, access: async (...args) => { laterAccess++; return fs.access(...args); } });
|
|
88
|
+
await c.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'local' });
|
|
89
|
+
returned = true;
|
|
90
|
+
}), { message: 'codex_setup_host_scope_unavailable' });
|
|
91
|
+
assert.equal(returned, false);
|
|
92
|
+
assert.equal(laterAccess, 0);
|
|
93
|
+
await assert.rejects(fs.lstat(f.policyPath), { code: 'ENOENT' });
|
|
94
|
+
});
|
|
95
|
+
test('policy scope snapshots original materialization parameters before asynchronous reads', async (t) => {
|
|
96
|
+
const f = await fixture(t), input = { workspace: f.root, organizationId: 'org_demo', revision: 'original' };
|
|
97
|
+
let mutated = false;
|
|
98
|
+
const c = await callers(f.root, { readFile: async (...args) => {
|
|
99
|
+
const value = await fs.readFile(...args);
|
|
100
|
+
if (!mutated) {
|
|
101
|
+
mutated = true;
|
|
102
|
+
input.organizationId = 'foreign';
|
|
103
|
+
input.revision = 'changed';
|
|
104
|
+
}
|
|
105
|
+
return value;
|
|
106
|
+
} });
|
|
107
|
+
await runCodexBootstrapHost(f.input, async () => {
|
|
108
|
+
const result = await c.materializeWorkspacePolicy(input);
|
|
109
|
+
assert.equal(result.policy.organizationId, 'org_demo');
|
|
110
|
+
assert.equal(result.policy.revision, 'original');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
test('policy scope stops after native existing-policy load withdraws ownership', async (t) => {
|
|
114
|
+
const f = await fixture(t), c = await callers(f.root);
|
|
115
|
+
await c.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'original' });
|
|
116
|
+
let returned = false, laterDirectories = 0;
|
|
117
|
+
await assert.rejects(runCodexBootstrapHost(f.input, async ({ scope }) => {
|
|
118
|
+
const bound = await callers(f.root, { mkdir: async (...args) => {
|
|
119
|
+
if (scope.signal.aborted)
|
|
120
|
+
laterDirectories++;
|
|
121
|
+
return fs.mkdir(...args);
|
|
122
|
+
}, loadOrganizationPolicy: async (path) => {
|
|
123
|
+
const value = await loadOrganizationPolicy(path);
|
|
124
|
+
scope.close();
|
|
125
|
+
return value;
|
|
126
|
+
} });
|
|
127
|
+
await bound.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'new' });
|
|
128
|
+
returned = true;
|
|
129
|
+
}), { message: 'codex_setup_host_scope_unavailable' });
|
|
130
|
+
assert.equal(returned, false);
|
|
131
|
+
assert.equal(laterDirectories, 0);
|
|
132
|
+
assert.equal((await loadOrganizationPolicy(f.policyPath)).revision, 'original');
|
|
133
|
+
});
|
|
134
|
+
test('policy scope preserves cancellation classification after replay-state read loss', async (t) => {
|
|
135
|
+
const f = await fixture(t);
|
|
136
|
+
let captured;
|
|
137
|
+
await assert.rejects(runCodexBootstrapHost(f.input, async ({ scope }) => {
|
|
138
|
+
const c = await callers(f.root, { readFile: async () => {
|
|
139
|
+
scope.close();
|
|
140
|
+
throw Object.assign(new Error('private-state-canary'), { code: 'ENOENT' });
|
|
141
|
+
} });
|
|
142
|
+
try {
|
|
143
|
+
await c.assertWorkspaceAuthorizationCurrent(f.workspaceId, { issuedAt: new Date().toISOString(), signature: 'synthetic' }, false);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
captured = error;
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}), { message: 'codex_setup_host_scope_unavailable' });
|
|
150
|
+
assert.equal(captured?.message, 'codex_setup_host_scope_unavailable');
|
|
151
|
+
});
|
|
152
|
+
test('policy scope snapshots signed authorization before replay read', async (t) => {
|
|
153
|
+
const f = await fixture(t), issuedAt = new Date().toISOString();
|
|
154
|
+
const state = resolve(f.root, 'registry', 'workspace-authorizations', `${f.workspaceId}.json`);
|
|
155
|
+
await fs.mkdir(dirname(state), { recursive: true });
|
|
156
|
+
await fs.writeFile(state, JSON.stringify({ issuedAt, signature: 'original' }));
|
|
157
|
+
const incoming = { issuedAt, signature: 'original' };
|
|
158
|
+
const c = await callers(f.root, { readFile: async (...args) => {
|
|
159
|
+
const value = await fs.readFile(...args);
|
|
160
|
+
incoming.signature = 'changed';
|
|
161
|
+
return value;
|
|
162
|
+
} });
|
|
163
|
+
await runCodexBootstrapHost(f.input, () => c.assertWorkspaceAuthorizationCurrent(f.workspaceId, incoming));
|
|
164
|
+
});
|
|
165
|
+
test('policy scope refuses unreadable path probes instead of creating replacement authority', async (t) => {
|
|
166
|
+
const f = await fixture(t), c = await callers(f.root, { access: async () => {
|
|
167
|
+
throw Object.assign(new Error('private-access-canary'), { code: 'EACCES' });
|
|
168
|
+
}, pathExistsOrThrow: async () => { throw new Error('private-access-canary'); } });
|
|
169
|
+
await runCodexBootstrapHost(f.input, async () => {
|
|
170
|
+
await assert.rejects(c.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'local' }), (error) => error.message === 'workspace_policy_materialization_failed' && error.cause === undefined);
|
|
171
|
+
});
|
|
172
|
+
await assert.rejects(fs.lstat(f.policyPath), { code: 'ENOENT' });
|
|
173
|
+
});
|
|
174
|
+
test('policy activation scope stops inner replay reads before ledger and policy writes', async (t) => {
|
|
175
|
+
const f = await fixture(t), c = await callers(f.root);
|
|
176
|
+
const generated = await c.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'original' });
|
|
177
|
+
const original = await fs.readFile(f.policyPath, 'utf8');
|
|
178
|
+
const authorization = { issuedAt: new Date().toISOString(), signature: 'synthetic' };
|
|
179
|
+
let laterReads = 0, laterAccess = 0;
|
|
180
|
+
await assert.rejects(runCodexBootstrapHost(f.input, async ({ scope }) => {
|
|
181
|
+
const bound = await callers(f.root, { readFile: async (...args) => {
|
|
182
|
+
if (scope.signal.aborted)
|
|
183
|
+
laterReads++;
|
|
184
|
+
if (String(args[0]).includes('workspace-authorizations')) {
|
|
185
|
+
scope.close();
|
|
186
|
+
throw Object.assign(new Error('private-missing-state'), { code: 'ENOENT' });
|
|
187
|
+
}
|
|
188
|
+
return fs.readFile(...args);
|
|
189
|
+
}, access: async (...args) => {
|
|
190
|
+
if (scope.signal.aborted)
|
|
191
|
+
laterAccess++;
|
|
192
|
+
return fs.access(...args);
|
|
193
|
+
} });
|
|
194
|
+
await bound.applyWorkspaceAuthorizationAtomically({ workspaceId: f.workspaceId, authorization,
|
|
195
|
+
policyPath: f.policyPath, policy: generated.policy });
|
|
196
|
+
}), { message: 'codex_setup_host_scope_unavailable' });
|
|
197
|
+
assert.equal(await fs.readFile(f.policyPath, 'utf8'), original);
|
|
198
|
+
assert.equal(laterReads, 0);
|
|
199
|
+
assert.equal(laterAccess, 0);
|
|
200
|
+
const names = await fs.readdir(resolve(f.root, 'registry', 'workspace-authorizations'));
|
|
201
|
+
assert.deepEqual(names, []);
|
|
202
|
+
});
|
|
203
|
+
test('current policy scope admits an actual signed envelope and persists replay and content ledgers', async (t) => {
|
|
204
|
+
const f = await fixture(t), c = await callers(f.root), authorization = signed(f.workspaceId);
|
|
205
|
+
await runCodexBootstrapHost(f.input, async () => {
|
|
206
|
+
const result = await c.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'local',
|
|
207
|
+
workspaceId: f.workspaceId, serverPolicyAuthorization: authorization.envelope,
|
|
208
|
+
serverPublicKeyEd25519: authorization.publicKey });
|
|
209
|
+
assert.equal(result.policy.revision, 'signed-test-policy');
|
|
210
|
+
assert.equal(result.applied, true);
|
|
211
|
+
});
|
|
212
|
+
assert.equal((await loadOrganizationPolicy(f.policyPath)).serverAuthorization?.signature, authorization.envelope.signature);
|
|
213
|
+
const state = JSON.parse(await fs.readFile(resolve(f.root, 'registry', 'workspace-authorizations', `${f.workspaceId}.json`), 'utf8'));
|
|
214
|
+
assert.equal(state.signature, authorization.envelope.signature);
|
|
215
|
+
assert.equal(state.contentLedgerInitialized, true);
|
|
216
|
+
assert.equal(JSON.parse(await fs.readFile(resolve(f.root, 'relay', 'evidence-upload-ledger.json'), 'utf8')).totalBytes, 0);
|
|
217
|
+
});
|
|
218
|
+
test('policy scope rejects tampered or foreign envelopes without persisting authority', async (t) => {
|
|
219
|
+
for (const foreign of [false, true]) {
|
|
220
|
+
const f = await fixture(t), c = await callers(f.root), authorization = signed(f.workspaceId);
|
|
221
|
+
const envelope = foreign ? { ...authorization.envelope, organizationId: 'org_foreign' }
|
|
222
|
+
: { ...authorization.envelope, signature: 'tampered' };
|
|
223
|
+
await runCodexBootstrapHost(f.input, async () => {
|
|
224
|
+
await assert.rejects(c.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'local',
|
|
225
|
+
workspaceId: f.workspaceId, serverPolicyAuthorization: envelope, serverPublicKeyEd25519: authorization.publicKey }), { message: 'workspace_policy_materialization_failed' });
|
|
226
|
+
});
|
|
227
|
+
await assert.rejects(fs.lstat(f.policyPath), { code: 'ENOENT' });
|
|
228
|
+
await assert.rejects(fs.lstat(resolve(f.root, 'registry')), { code: 'ENOENT' });
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
test('policy scope dry-run verifies a signed envelope without creating any policy or ledger', async (t) => {
|
|
232
|
+
const f = await fixture(t), c = await callers(f.root), authorization = signed(f.workspaceId);
|
|
233
|
+
await runCodexBootstrapHost(f.input, async () => {
|
|
234
|
+
const result = await c.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'local',
|
|
235
|
+
workspaceId: f.workspaceId, serverPolicyAuthorization: authorization.envelope,
|
|
236
|
+
serverPublicKeyEd25519: authorization.publicKey, dryRun: true });
|
|
237
|
+
assert.equal(result.policy.revision, 'signed-test-policy');
|
|
238
|
+
assert.equal(result.applied, false);
|
|
239
|
+
});
|
|
240
|
+
assert.deepEqual(await fs.readdir(f.root), ['package.json']);
|
|
241
|
+
});
|
|
242
|
+
test('policy activation scope preserves partial replay evidence but denies the next policy write', async (t) => {
|
|
243
|
+
const f = await fixture(t), c = await callers(f.root), authorization = signed(f.workspaceId);
|
|
244
|
+
await c.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'original' });
|
|
245
|
+
const original = await fs.readFile(f.policyPath, 'utf8');
|
|
246
|
+
await assert.rejects(runCodexBootstrapHost(f.input, async ({ scope }) => {
|
|
247
|
+
const bound = await callers(f.root, { writeBootstrapHostJson: async (...args) => {
|
|
248
|
+
await writeBootstrapHostJson(args[0], structuredClone(args[1]), args[2]);
|
|
249
|
+
if (args[0].includes('workspace-authorizations'))
|
|
250
|
+
scope.close();
|
|
251
|
+
} });
|
|
252
|
+
await bound.materializeWorkspacePolicy({ workspace: f.root, organizationId: 'org_demo', revision: 'local',
|
|
253
|
+
workspaceId: f.workspaceId, serverPolicyAuthorization: authorization.envelope,
|
|
254
|
+
serverPublicKeyEd25519: authorization.publicKey });
|
|
255
|
+
}), { message: 'codex_setup_host_scope_unavailable' });
|
|
256
|
+
assert.equal(await fs.readFile(f.policyPath, 'utf8'), original);
|
|
257
|
+
const state = JSON.parse(await fs.readFile(resolve(f.root, 'registry', 'workspace-authorizations', `${f.workspaceId}.json`), 'utf8'));
|
|
258
|
+
assert.equal(state.signature, authorization.envelope.signature);
|
|
259
|
+
assert.deepEqual(await fs.readdir(resolve(f.root, 'registry', 'workspace-authorizations')), [`${f.workspaceId}.json`]);
|
|
260
|
+
});
|
|
261
|
+
//# sourceMappingURL=bootstrapPolicyScope.test.js.map
|