@dharma-ai-labs/agent-fabric 0.2.153 → 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/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 +1642 -553
- 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/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
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { constants } from 'node:fs';
|
|
3
|
-
import { link, lstat, mkdir, open, opendir, realpath, rename, rmdir, unlink } from 'node:fs/promises';
|
|
3
|
+
import { link as nativeLink, lstat as nativeLstat, mkdir as nativeMkdir, open, opendir, realpath as nativeRealpath, rename as nativeRename, rmdir as nativeRmdir, unlink as nativeUnlink } from 'node:fs/promises';
|
|
4
4
|
import { isAbsolute, posix, relative, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { types } from 'node:util';
|
|
7
|
+
import { currentBootstrapHostScope } from './bootstrapHostScope.js';
|
|
6
8
|
import { canonicalize, validateContract } from '@dharma-ai-labs/agent-fabric-contracts';
|
|
7
9
|
import { redactValue } from '@dharma-ai-labs/agent-fabric-evidence-reduction';
|
|
8
10
|
import { readRepositoryKnowledgeSource, validateRepositoryKnowledgeCatalog, validateRepositoryKnowledgeRetention, REPOSITORY_KNOWLEDGE_CATALOG_PATH, REPOSITORY_KNOWLEDGE_RELEASE_MANIFEST_PATH } from './repositoryKnowledge.js';
|
|
@@ -14,7 +16,130 @@ const DEFAULT_LIMITS = { maximumEntries: 4096, maximumFiles: 512, maximumFileByt
|
|
|
14
16
|
maximumScannedSourceEntries: 65_536 };
|
|
15
17
|
function digest(value) { return `sha256:${createHash('sha256').update(value).digest('hex')}`; }
|
|
16
18
|
function compare(a, b) { return a < b ? -1 : a > b ? 1 : 0; }
|
|
17
|
-
function
|
|
19
|
+
function errorCode(error) {
|
|
20
|
+
if (!error || typeof error !== 'object' || types.isProxy(error))
|
|
21
|
+
return undefined;
|
|
22
|
+
const field = Object.getOwnPropertyDescriptor(error, 'code');
|
|
23
|
+
return field && Object.hasOwn(field, 'value') && typeof field.value === 'string' ? field.value : undefined;
|
|
24
|
+
}
|
|
25
|
+
function missing(error) { return ['ENOENT', 'ENOTDIR'].includes(errorCode(error) || ''); }
|
|
26
|
+
async function packageEffect(scope, operation) {
|
|
27
|
+
if (!scope)
|
|
28
|
+
return operation();
|
|
29
|
+
try {
|
|
30
|
+
return await scope.step(operation);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error && typeof error === 'object' && cleanupFailures.has(error))
|
|
34
|
+
throw error;
|
|
35
|
+
await scope.assert();
|
|
36
|
+
const code = errorCode(error);
|
|
37
|
+
throw Object.assign(new Error('repository_package_storage_unavailable'), ['ENOENT', 'ENOTDIR', 'EACCES', 'EPERM', 'EEXIST', 'ENOTEMPTY', 'ESRCH'].includes(code || '') ? { code } : {});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function admitted(operation) {
|
|
41
|
+
return ((...args) => packageEffect(currentBootstrapHostScope(), () => operation(...args)));
|
|
42
|
+
}
|
|
43
|
+
const lstat = admitted(nativeLstat), realpath = admitted(nativeRealpath), mkdir = admitted(nativeMkdir), link = admitted(nativeLink), rename = admitted(nativeRename), rmdir = admitted(nativeRmdir), unlink = admitted(nativeUnlink);
|
|
44
|
+
const cleanupFailures = new WeakSet();
|
|
45
|
+
function cleanupFailure() {
|
|
46
|
+
const failure = new Error('repository_package_cleanup_unconfirmed');
|
|
47
|
+
cleanupFailures.add(failure);
|
|
48
|
+
return failure;
|
|
49
|
+
}
|
|
50
|
+
async function closeReadHandle(handle, scope) {
|
|
51
|
+
try {
|
|
52
|
+
await handle?.close();
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (!scope)
|
|
56
|
+
throw error;
|
|
57
|
+
throw cleanupFailure();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function* directoryEntries(source) {
|
|
61
|
+
const scope = currentBootstrapHostScope();
|
|
62
|
+
let directory;
|
|
63
|
+
try {
|
|
64
|
+
await packageEffect(scope, async () => { directory = await opendir(source); });
|
|
65
|
+
for (;;) {
|
|
66
|
+
const entry = await packageEffect(scope, () => directory.read());
|
|
67
|
+
if (!entry)
|
|
68
|
+
break;
|
|
69
|
+
yield entry;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
await closeReadHandle(directory, scope);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function snapshotPackageInput(input, scope, keys) {
|
|
77
|
+
if (!scope)
|
|
78
|
+
return input;
|
|
79
|
+
let nodes = 0, bytes = 0;
|
|
80
|
+
const invalid = () => new Error('repository_package_input_invalid');
|
|
81
|
+
const clone = (value, depth) => {
|
|
82
|
+
if (++nodes > 65_536 || depth > 16)
|
|
83
|
+
throw invalid();
|
|
84
|
+
if (value === null || value === undefined || typeof value === 'boolean')
|
|
85
|
+
return value;
|
|
86
|
+
if (typeof value === 'number') {
|
|
87
|
+
if (!Number.isFinite(value))
|
|
88
|
+
throw invalid();
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
if (typeof value === 'string') {
|
|
92
|
+
bytes += Buffer.byteLength(value);
|
|
93
|
+
if (bytes > 16_777_216)
|
|
94
|
+
throw invalid();
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
if (typeof value !== 'object' || types.isProxy(value))
|
|
98
|
+
throw invalid();
|
|
99
|
+
if (types.isDate(value)) {
|
|
100
|
+
const time = Date.prototype.getTime.call(value);
|
|
101
|
+
if (!Number.isFinite(time))
|
|
102
|
+
throw invalid();
|
|
103
|
+
return new Date(time);
|
|
104
|
+
}
|
|
105
|
+
if (Buffer.isBuffer(value)) {
|
|
106
|
+
const length = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), 'byteLength').get.call(value);
|
|
107
|
+
bytes += length;
|
|
108
|
+
if (bytes > 16_777_216)
|
|
109
|
+
throw invalid();
|
|
110
|
+
const copy = Buffer.alloc(length);
|
|
111
|
+
Uint8Array.prototype.set.call(copy, value);
|
|
112
|
+
return copy;
|
|
113
|
+
}
|
|
114
|
+
const array = Array.isArray(value), prototype = Object.getPrototypeOf(value);
|
|
115
|
+
if (!array && prototype !== null && (types.isProxy(prototype) || Object.getPrototypeOf(prototype) !== null))
|
|
116
|
+
throw invalid();
|
|
117
|
+
const fields = Object.getOwnPropertyDescriptors(value), result = array ? [] : Object.create(null);
|
|
118
|
+
if (Object.getOwnPropertySymbols(value).length || array && (fields.length.value > 8192
|
|
119
|
+
|| Object.keys(fields).length !== fields.length.value + 1))
|
|
120
|
+
throw invalid();
|
|
121
|
+
for (const [key, field] of Object.entries(fields)) {
|
|
122
|
+
if (array && key === 'length')
|
|
123
|
+
continue;
|
|
124
|
+
if (!Object.hasOwn(field, 'value') || array && !/^(?:0|[1-9][0-9]*)$/.test(key))
|
|
125
|
+
throw invalid();
|
|
126
|
+
Object.defineProperty(result, key, { value: clone(field.value, depth + 1), enumerable: true });
|
|
127
|
+
}
|
|
128
|
+
return Object.freeze(result);
|
|
129
|
+
};
|
|
130
|
+
if (!input || typeof input !== 'object' || types.isProxy(input))
|
|
131
|
+
throw invalid();
|
|
132
|
+
const result = {};
|
|
133
|
+
for (const key of keys) {
|
|
134
|
+
const field = Object.getOwnPropertyDescriptor(input, key);
|
|
135
|
+
if (!field)
|
|
136
|
+
continue;
|
|
137
|
+
if (!Object.hasOwn(field, 'value'))
|
|
138
|
+
throw invalid();
|
|
139
|
+
result[key] = clone(field.value, 0);
|
|
140
|
+
}
|
|
141
|
+
return Object.freeze(result);
|
|
142
|
+
}
|
|
18
143
|
function pathKey(value) {
|
|
19
144
|
if (!value || value.includes('\\') || isAbsolute(value) || /^[A-Za-z]:/.test(value))
|
|
20
145
|
throw new Error('Invalid repository package path.');
|
|
@@ -96,12 +221,14 @@ async function checkedPath(workspace, path) {
|
|
|
96
221
|
return current;
|
|
97
222
|
}
|
|
98
223
|
async function readStable(workspace, path, maximumBytes) {
|
|
224
|
+
const scope = currentBootstrapHostScope();
|
|
99
225
|
const source = await checkedPath(workspace, path);
|
|
100
226
|
if (!(await lstat(source)).isFile())
|
|
101
227
|
throw new Error('Repository package requires regular files.');
|
|
102
|
-
|
|
228
|
+
let handle;
|
|
103
229
|
try {
|
|
104
|
-
|
|
230
|
+
await packageEffect(scope, async () => { handle = await open(source, constants.O_RDONLY | (constants.O_NOFOLLOW || 0) | (constants.O_NONBLOCK || 0)); });
|
|
231
|
+
const before = await packageEffect(scope, () => handle.stat());
|
|
105
232
|
if (!before.isFile())
|
|
106
233
|
throw new Error('Repository package requires regular files.');
|
|
107
234
|
if (before.size > maximumBytes)
|
|
@@ -109,12 +236,12 @@ async function readStable(workspace, path, maximumBytes) {
|
|
|
109
236
|
const buffer = Buffer.alloc(before.size + 1);
|
|
110
237
|
let size = 0;
|
|
111
238
|
while (size < buffer.length) {
|
|
112
|
-
const result = await handle.read(buffer, size, buffer.length - size, size);
|
|
239
|
+
const result = await packageEffect(scope, () => handle.read(buffer, size, buffer.length - size, size));
|
|
113
240
|
if (!result.bytesRead)
|
|
114
241
|
break;
|
|
115
242
|
size += result.bytesRead;
|
|
116
243
|
}
|
|
117
|
-
const after = await handle.stat();
|
|
244
|
+
const after = await packageEffect(scope, () => handle.stat());
|
|
118
245
|
const current = await lstat(await checkedPath(workspace, path));
|
|
119
246
|
if (size !== before.size || before.size !== after.size || before.mtimeMs !== after.mtimeMs
|
|
120
247
|
|| before.ctimeMs !== after.ctimeMs || current.ino !== before.ino || current.dev !== before.dev
|
|
@@ -123,7 +250,7 @@ async function readStable(workspace, path, maximumBytes) {
|
|
|
123
250
|
return buffer.subarray(0, size);
|
|
124
251
|
}
|
|
125
252
|
finally {
|
|
126
|
-
await handle
|
|
253
|
+
await closeReadHandle(handle, scope);
|
|
127
254
|
}
|
|
128
255
|
}
|
|
129
256
|
function dependencies(text) {
|
|
@@ -140,6 +267,11 @@ function sourceContentType(path) {
|
|
|
140
267
|
|| /^(?:README|LICENSE|Makefile|Dockerfile)$/i.test(posix.basename(path));
|
|
141
268
|
}
|
|
142
269
|
export async function inventoryRepositoryPackage(input) {
|
|
270
|
+
const scope = currentBootstrapHostScope();
|
|
271
|
+
input = snapshotPackageInput(input, scope, ['workspace', 'organizationId', 'workspaceId', 'repositoryAgentId', 'repositoryBindingId',
|
|
272
|
+
'sourceAuthorization', 'approvedOutputs', 'observations', 'limits', 'now', 'retainedKnowledge']);
|
|
273
|
+
if (scope)
|
|
274
|
+
await scope.assert();
|
|
143
275
|
if (![input.organizationId, input.workspaceId].every(value => /^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$/.test(value))) {
|
|
144
276
|
throw new Error('Repository package requires bounded organization and workspace identities.');
|
|
145
277
|
}
|
|
@@ -201,8 +333,7 @@ export async function inventoryRepositoryPackage(input) {
|
|
|
201
333
|
const metadata = await lstat(source);
|
|
202
334
|
if (metadata.isDirectory()) {
|
|
203
335
|
const names = [];
|
|
204
|
-
const
|
|
205
|
-
for await (const entry of directory) {
|
|
336
|
+
for await (const entry of directoryEntries(source)) {
|
|
206
337
|
const child = `${path}/${entry.name}`;
|
|
207
338
|
if (generated(child))
|
|
208
339
|
continue;
|
|
@@ -226,6 +357,8 @@ export async function inventoryRepositoryPackage(input) {
|
|
|
226
357
|
exclude(path, 'not_regular_file');
|
|
227
358
|
}
|
|
228
359
|
catch (error) {
|
|
360
|
+
if (scope)
|
|
361
|
+
await scope.assert();
|
|
229
362
|
if (missing(error))
|
|
230
363
|
return;
|
|
231
364
|
if (String(error).includes('symlink')) {
|
|
@@ -234,7 +367,7 @@ export async function inventoryRepositoryPackage(input) {
|
|
|
234
367
|
}
|
|
235
368
|
if (String(error).includes('limit'))
|
|
236
369
|
throw error;
|
|
237
|
-
if (['EACCES', 'EPERM'].includes(
|
|
370
|
+
if (['EACCES', 'EPERM'].includes(errorCode(error) || '')) {
|
|
238
371
|
exclude(path, 'unavailable');
|
|
239
372
|
return;
|
|
240
373
|
}
|
|
@@ -266,6 +399,8 @@ export async function inventoryRepositoryPackage(input) {
|
|
|
266
399
|
source = path === '.' ? workspace : await checkedPath(workspace, path);
|
|
267
400
|
}
|
|
268
401
|
catch (error) {
|
|
402
|
+
if (scope)
|
|
403
|
+
await scope.assert();
|
|
269
404
|
if (missing(error)) {
|
|
270
405
|
witnesses.set(path, 'missing');
|
|
271
406
|
return false;
|
|
@@ -280,8 +415,7 @@ export async function inventoryRepositoryPackage(input) {
|
|
|
280
415
|
const metadata = await lstat(source);
|
|
281
416
|
if (metadata.isDirectory()) {
|
|
282
417
|
const names = [];
|
|
283
|
-
const
|
|
284
|
-
for await (const entry of directory) {
|
|
418
|
+
for await (const entry of directoryEntries(source)) {
|
|
285
419
|
if (++scanned > limits.maximumScannedSourceEntries)
|
|
286
420
|
throw new Error('Repository source scan limit exceeded.');
|
|
287
421
|
const child = path === '.' ? entry.name : `${path}/${entry.name}`;
|
|
@@ -385,6 +519,10 @@ export async function inventoryRepositoryPackage(input) {
|
|
|
385
519
|
content = await readStable(workspace, path, limits.maximumFileBytes);
|
|
386
520
|
}
|
|
387
521
|
catch (error) {
|
|
522
|
+
if (error && typeof error === 'object' && cleanupFailures.has(error))
|
|
523
|
+
throw error;
|
|
524
|
+
if (scope)
|
|
525
|
+
await scope.assert();
|
|
388
526
|
if (String(error).includes('limit') || String(error).includes('changed during'))
|
|
389
527
|
throw error;
|
|
390
528
|
exclude(path, missing(error) ? 'missing_dependency' : String(error).includes('symlink') ? 'symlink' : 'unavailable');
|
|
@@ -531,6 +669,8 @@ export async function inventoryRepositoryPackage(input) {
|
|
|
531
669
|
...(knowledge ? { knowledge } : {}), ...(authorization ? { sourceAuthorization: authorization } : {}) };
|
|
532
670
|
const fingerprinted = authorization ? { ...base, sourceFingerprint: sourceFingerprint(base) } : base;
|
|
533
671
|
const snapshotHash = digest(canonicalize(fingerprinted));
|
|
672
|
+
if (scope)
|
|
673
|
+
await scope.assert();
|
|
534
674
|
return { schema: 'dharma.repository-package-snapshot/v1', capturedAt: (input.now || new Date()).toISOString(),
|
|
535
675
|
manifest: { ...fingerprinted, snapshotHash, snapshotId: `repository-package-${snapshotHash.slice(7)}` },
|
|
536
676
|
blobs: [...blobs].sort(([a], [b]) => compare(a, b)).map(([sha256, contentBase64]) => ({ sha256, contentBase64 })) };
|
|
@@ -701,8 +841,11 @@ export function serializeRepositoryPackageSnapshot(snapshot) {
|
|
|
701
841
|
return `${canonicalize({ manifest: snapshot.manifest, blobs: snapshot.blobs })}\n`;
|
|
702
842
|
}
|
|
703
843
|
async function persistSnapshot(input, workspace) {
|
|
844
|
+
const scope = currentBootstrapHostScope();
|
|
845
|
+
if (scope)
|
|
846
|
+
await scope.assert();
|
|
704
847
|
const serialized = serializeRepositoryPackageSnapshot(input.snapshot);
|
|
705
|
-
const validated = await validateContract(fileURLToPath(new URL('./schemas/', import.meta.url)), `https://schemas.dharma-ai.io/repository-package/${input.snapshot.manifest.schema.split('/')[1]}`, input.snapshot.manifest);
|
|
848
|
+
const validated = await packageEffect(scope, () => validateContract(fileURLToPath(new URL('./schemas/', import.meta.url)), `https://schemas.dharma-ai.io/repository-package/${input.snapshot.manifest.schema.split('/')[1]}`, input.snapshot.manifest));
|
|
706
849
|
if (!validated.ok)
|
|
707
850
|
throw new Error('Repository package manifest schema is invalid.');
|
|
708
851
|
const activeManifestPath = `${GENERATED_ROOT}/MANIFEST.json`;
|
|
@@ -723,6 +866,8 @@ async function persistSnapshot(input, workspace) {
|
|
|
723
866
|
marker = JSON.parse((await readStable(workspace, `${GENERATED_ROOT}/.dharma-agent-fabric.json`, 4096)).toString('utf8'));
|
|
724
867
|
}
|
|
725
868
|
catch (error) {
|
|
869
|
+
if (scope)
|
|
870
|
+
await scope.assert();
|
|
726
871
|
if (String(error).includes('symlink'))
|
|
727
872
|
throw error;
|
|
728
873
|
throw new Error('Refusing to write into an unmanaged repository skill.');
|
|
@@ -748,7 +893,7 @@ async function persistSnapshot(input, workspace) {
|
|
|
748
893
|
await mkdir(resolve(workspace, current), { mode: 0o700 });
|
|
749
894
|
}
|
|
750
895
|
catch (error) {
|
|
751
|
-
if (error
|
|
896
|
+
if (errorCode(error) !== 'EEXIST')
|
|
752
897
|
throw error;
|
|
753
898
|
}
|
|
754
899
|
await checkedPath(workspace, current);
|
|
@@ -756,21 +901,16 @@ async function persistSnapshot(input, workspace) {
|
|
|
756
901
|
await checkedPath(workspace, posix.dirname(snapshotPath));
|
|
757
902
|
for (const [path, content] of [[snapshotPath, serialized], [candidateManifestPath, `${canonicalize(input.snapshot.manifest)}\n`]]) {
|
|
758
903
|
const stagedSnapshot = `${snapshotRoot}/.snapshot-${randomUUID()}.tmp`;
|
|
904
|
+
let identity;
|
|
759
905
|
try {
|
|
760
|
-
|
|
761
|
-
try {
|
|
762
|
-
await handle.writeFile(content);
|
|
763
|
-
await handle.sync();
|
|
764
|
-
}
|
|
765
|
-
finally {
|
|
766
|
-
await handle.close();
|
|
767
|
-
}
|
|
906
|
+
identity = await freshFile(workspace, stagedSnapshot, content);
|
|
768
907
|
await checkedPath(workspace, snapshotRoot);
|
|
908
|
+
await verifyStaging(workspace, stagedSnapshot, identity);
|
|
769
909
|
try {
|
|
770
910
|
await link(resolve(workspace, stagedSnapshot), resolve(workspace, path));
|
|
771
911
|
}
|
|
772
912
|
catch (error) {
|
|
773
|
-
if (error
|
|
913
|
+
if (errorCode(error) !== 'EEXIST')
|
|
774
914
|
throw error;
|
|
775
915
|
const existing = await readStable(workspace, path, 8_388_608);
|
|
776
916
|
if (existing.toString('utf8') !== content)
|
|
@@ -778,8 +918,8 @@ async function persistSnapshot(input, workspace) {
|
|
|
778
918
|
}
|
|
779
919
|
}
|
|
780
920
|
finally {
|
|
781
|
-
|
|
782
|
-
|
|
921
|
+
if (identity)
|
|
922
|
+
await cleanupStaging(workspace, stagedSnapshot, identity);
|
|
783
923
|
}
|
|
784
924
|
}
|
|
785
925
|
await syncDirectory(workspace, posix.dirname(snapshotPath));
|
|
@@ -790,6 +930,8 @@ async function persistSnapshot(input, workspace) {
|
|
|
790
930
|
await prepareCopies(workspace, input.snapshot, input.onCopyCheckpoint);
|
|
791
931
|
});
|
|
792
932
|
}
|
|
933
|
+
if (scope)
|
|
934
|
+
await scope.assert();
|
|
793
935
|
return { manifestPath, snapshotPath, snapshotHash: input.snapshot.manifest.snapshotHash,
|
|
794
936
|
managedCopiesPath: candidateOnly ? null : `${GENERATED_ROOT}/skills/source`,
|
|
795
937
|
disposition: candidateOnly ? 'candidate_only' : 'local_bootstrap_inventory',
|
|
@@ -797,9 +939,20 @@ async function persistSnapshot(input, workspace) {
|
|
|
797
939
|
}
|
|
798
940
|
const writers = new Map();
|
|
799
941
|
export async function writeRepositoryPackageSnapshot(input) {
|
|
942
|
+
const scope = currentBootstrapHostScope();
|
|
943
|
+
if (scope) {
|
|
944
|
+
const copied = snapshotPackageInput(input, scope, ['workspace', 'snapshot', 'candidateOnly']);
|
|
945
|
+
const field = Object.getOwnPropertyDescriptor(input, 'onCopyCheckpoint');
|
|
946
|
+
if (field && (!Object.hasOwn(field, 'value') || field.value !== undefined
|
|
947
|
+
&& (typeof field.value !== 'function' || types.isProxy(field.value))))
|
|
948
|
+
throw new Error('repository_package_input_invalid');
|
|
949
|
+
input = Object.freeze({ ...copied, onCopyCheckpoint: field?.value });
|
|
950
|
+
await scope.assert();
|
|
951
|
+
}
|
|
800
952
|
const workspace = await realpath(input.workspace);
|
|
801
953
|
const previous = writers.get(workspace) || Promise.resolve();
|
|
802
|
-
const operation = previous.catch(() => { }).then(() =>
|
|
954
|
+
const operation = previous.catch(() => { }).then(async () => { if (scope)
|
|
955
|
+
await scope.assert(); return persistSnapshot(input, workspace); });
|
|
803
956
|
writers.set(workspace, operation);
|
|
804
957
|
try {
|
|
805
958
|
return await operation;
|
|
@@ -854,7 +1007,7 @@ async function directories(workspace, path) {
|
|
|
854
1007
|
await mkdir(resolve(workspace, current), { mode: 0o700 });
|
|
855
1008
|
}
|
|
856
1009
|
catch (error) {
|
|
857
|
-
if (error
|
|
1010
|
+
if (errorCode(error) !== 'EEXIST')
|
|
858
1011
|
throw error;
|
|
859
1012
|
}
|
|
860
1013
|
if (!(await lstat(await checkedPath(workspace, current))).isDirectory())
|
|
@@ -862,40 +1015,92 @@ async function directories(workspace, path) {
|
|
|
862
1015
|
}
|
|
863
1016
|
}
|
|
864
1017
|
async function syncDirectory(workspace, path) {
|
|
1018
|
+
const scope = currentBootstrapHostScope();
|
|
1019
|
+
if (scope)
|
|
1020
|
+
await scope.assert();
|
|
865
1021
|
if (process.platform === 'win32')
|
|
866
1022
|
return; // Windows does not support opening directories for fsync.
|
|
867
|
-
const
|
|
1023
|
+
const source = await checkedPath(workspace, path);
|
|
1024
|
+
let handle;
|
|
868
1025
|
try {
|
|
869
|
-
await handle.
|
|
1026
|
+
await packageEffect(scope, async () => { handle = await open(source, constants.O_RDONLY | (constants.O_NOFOLLOW || 0)); });
|
|
1027
|
+
await packageEffect(scope, () => handle.sync());
|
|
870
1028
|
}
|
|
871
1029
|
finally {
|
|
872
|
-
await handle
|
|
1030
|
+
await closeReadHandle(handle, scope);
|
|
873
1031
|
}
|
|
874
1032
|
}
|
|
875
1033
|
async function freshFile(workspace, path, bytes) {
|
|
1034
|
+
const scope = currentBootstrapHostScope();
|
|
1035
|
+
const content = Buffer.from(bytes);
|
|
876
1036
|
await directories(workspace, posix.dirname(path));
|
|
877
|
-
const
|
|
1037
|
+
const parent = await lstat(await checkedPath(workspace, posix.dirname(path)), { bigint: true });
|
|
1038
|
+
let handle, identity;
|
|
878
1039
|
try {
|
|
879
|
-
await
|
|
880
|
-
|
|
1040
|
+
await packageEffect(scope, async () => {
|
|
1041
|
+
handle = await open(resolve(workspace, path), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW || 0), 0o600);
|
|
1042
|
+
});
|
|
1043
|
+
const metadata = await packageEffect(scope, () => handle.stat({ bigint: true }));
|
|
1044
|
+
if (!metadata.isFile() || metadata.ino === 0n || metadata.nlink !== 1n)
|
|
1045
|
+
throw cleanupFailure();
|
|
1046
|
+
identity = { dev: metadata.dev, ino: metadata.ino, parentDev: parent.dev, parentIno: parent.ino,
|
|
1047
|
+
sizeBytes: content.length, sha256: digest(content) };
|
|
1048
|
+
await packageEffect(scope, () => handle.writeFile(content));
|
|
1049
|
+
await packageEffect(scope, () => handle.sync());
|
|
881
1050
|
}
|
|
882
1051
|
finally {
|
|
883
|
-
await handle
|
|
1052
|
+
await closeReadHandle(handle, scope);
|
|
884
1053
|
}
|
|
885
1054
|
await syncDirectory(workspace, posix.dirname(path));
|
|
1055
|
+
return identity;
|
|
1056
|
+
}
|
|
1057
|
+
async function verifyStaging(workspace, path, identity) {
|
|
1058
|
+
const parent = await lstat(await checkedPath(workspace, posix.dirname(path)), { bigint: true });
|
|
1059
|
+
const metadata = await lstat(await checkedPath(workspace, path), { bigint: true });
|
|
1060
|
+
if (!parent.isDirectory() || parent.dev !== identity.parentDev || parent.ino !== identity.parentIno
|
|
1061
|
+
|| !ownedLockMetadata(metadata, identity) || metadata.size !== BigInt(identity.sizeBytes))
|
|
1062
|
+
throw cleanupFailure();
|
|
1063
|
+
const bytes = await readStable(workspace, path, identity.sizeBytes);
|
|
1064
|
+
const current = await lstat(await checkedPath(workspace, path), { bigint: true });
|
|
1065
|
+
if (!ownedLockMetadata(current, identity) || bytes.length !== identity.sizeBytes || digest(bytes) !== identity.sha256)
|
|
1066
|
+
throw cleanupFailure();
|
|
1067
|
+
}
|
|
1068
|
+
async function cleanupStaging(workspace, path, identity) {
|
|
1069
|
+
const scope = currentBootstrapHostScope();
|
|
1070
|
+
if (scope && !await scope.current())
|
|
1071
|
+
return; // Interrupted generated evidence is retained.
|
|
1072
|
+
try {
|
|
1073
|
+
const parent = await lstat(await checkedPath(workspace, posix.dirname(path)), { bigint: true });
|
|
1074
|
+
if (parent.dev !== identity.parentDev || parent.ino !== identity.parentIno)
|
|
1075
|
+
throw cleanupFailure();
|
|
1076
|
+
const metadata = await lstat(resolve(workspace, path), { bigint: true }).catch(error => { if (missing(error))
|
|
1077
|
+
return null; throw error; });
|
|
1078
|
+
if (!metadata)
|
|
1079
|
+
return;
|
|
1080
|
+
if (metadata.isSymbolicLink() || !metadata.isFile() || metadata.dev !== identity.dev || metadata.ino !== identity.ino
|
|
1081
|
+
|| metadata.nlink < 1n || metadata.nlink > 2n)
|
|
1082
|
+
throw cleanupFailure();
|
|
1083
|
+
await verifyStaging(workspace, path, identity);
|
|
1084
|
+
await unlink(resolve(workspace, path));
|
|
1085
|
+
}
|
|
1086
|
+
catch (error) {
|
|
1087
|
+
if (scope)
|
|
1088
|
+
await scope.assert();
|
|
1089
|
+
throw cleanupFailure();
|
|
1090
|
+
}
|
|
886
1091
|
}
|
|
887
1092
|
async function atomicMetadata(workspace, path, value) {
|
|
888
1093
|
const temporary = `${GENERATED_ROOT}/.metadata-${randomUUID()}.tmp`;
|
|
889
|
-
await freshFile(workspace, temporary, `${canonicalize(value)}\n`);
|
|
1094
|
+
const identity = await freshFile(workspace, temporary, `${canonicalize(value)}\n`);
|
|
890
1095
|
try {
|
|
891
1096
|
await checkedPath(workspace, GENERATED_ROOT);
|
|
892
1097
|
await optionalBytes(workspace, path);
|
|
1098
|
+
await verifyStaging(workspace, temporary, identity);
|
|
893
1099
|
await rename(resolve(workspace, temporary), resolve(workspace, path));
|
|
894
1100
|
await syncDirectory(workspace, GENERATED_ROOT);
|
|
895
1101
|
}
|
|
896
1102
|
finally {
|
|
897
|
-
await
|
|
898
|
-
throw error; });
|
|
1103
|
+
await cleanupStaging(workspace, temporary, identity);
|
|
899
1104
|
}
|
|
900
1105
|
}
|
|
901
1106
|
async function snapshotFor(workspace, hash) {
|
|
@@ -907,7 +1112,7 @@ async function snapshotFor(workspace, hash) {
|
|
|
907
1112
|
if (snapshot.manifest.snapshotHash !== hash || serializeRepositoryPackageSnapshot(snapshot) !== bytes.toString('utf8')) {
|
|
908
1113
|
throw new Error('Managed copies snapshot integrity failed.');
|
|
909
1114
|
}
|
|
910
|
-
const validated = await validateContract(fileURLToPath(new URL('./schemas/', import.meta.url)), `https://schemas.dharma-ai.io/repository-package/${snapshot.manifest.schema.split('/')[1]}`, snapshot.manifest);
|
|
1115
|
+
const validated = await packageEffect(currentBootstrapHostScope(), () => validateContract(fileURLToPath(new URL('./schemas/', import.meta.url)), `https://schemas.dharma-ai.io/repository-package/${snapshot.manifest.schema.split('/')[1]}`, snapshot.manifest));
|
|
911
1116
|
if (!validated.ok)
|
|
912
1117
|
throw new Error('Managed copies snapshot schema integrity failed.');
|
|
913
1118
|
return snapshot;
|
|
@@ -1014,7 +1219,7 @@ async function prepareCopies(workspace, snapshot, checkpoint) {
|
|
|
1014
1219
|
await freshFile(workspace, staged, blobs.get(operation.after.sha256));
|
|
1015
1220
|
}
|
|
1016
1221
|
await atomicMetadata(workspace, JOURNAL_PATH, journal);
|
|
1017
|
-
await checkpoint
|
|
1222
|
+
await copyCheckpoint(checkpoint, 'journal_prepared');
|
|
1018
1223
|
await applyJournal(workspace, journal, snapshot, checkpoint);
|
|
1019
1224
|
}
|
|
1020
1225
|
async function recoverCopies(workspace) {
|
|
@@ -1090,7 +1295,7 @@ async function applyJournal(workspace, journal, snapshot, checkpoint) {
|
|
|
1090
1295
|
await rename(resolve(workspace, target), resolve(workspace, backup));
|
|
1091
1296
|
await syncDirectory(workspace, posix.dirname(target));
|
|
1092
1297
|
await syncDirectory(workspace, posix.dirname(backup));
|
|
1093
|
-
await checkpoint
|
|
1298
|
+
await copyCheckpoint(checkpoint, 'file_backed_up');
|
|
1094
1299
|
}
|
|
1095
1300
|
else if (operation.before && !saved && !installed)
|
|
1096
1301
|
throw new Error('Managed copies missing backup conflict.');
|
|
@@ -1102,21 +1307,21 @@ async function applyJournal(workspace, journal, snapshot, checkpoint) {
|
|
|
1102
1307
|
await link(resolve(workspace, staged), resolve(workspace, target));
|
|
1103
1308
|
}
|
|
1104
1309
|
catch (error) {
|
|
1105
|
-
if (error
|
|
1310
|
+
if (errorCode(error) === 'EEXIST')
|
|
1106
1311
|
throw new Error('Managed copies unmanaged destination conflict.');
|
|
1107
1312
|
throw error;
|
|
1108
1313
|
}
|
|
1109
1314
|
await syncDirectory(workspace, posix.dirname(target));
|
|
1110
|
-
await checkpoint
|
|
1315
|
+
await copyCheckpoint(checkpoint, 'file_installed');
|
|
1111
1316
|
}
|
|
1112
1317
|
}
|
|
1113
1318
|
await verifyCopies(workspace, journal);
|
|
1114
1319
|
await verifyMetadata(workspace, journal, desiredManifest);
|
|
1115
1320
|
await atomicMetadata(workspace, INDEX_PATH, journal.desired);
|
|
1116
|
-
await checkpoint
|
|
1321
|
+
await copyCheckpoint(checkpoint, 'copies_index_written');
|
|
1117
1322
|
await verifyMetadata(workspace, journal, desiredManifest);
|
|
1118
1323
|
await atomicMetadata(workspace, `${GENERATED_ROOT}/MANIFEST.json`, snapshot.manifest);
|
|
1119
|
-
await checkpoint
|
|
1324
|
+
await copyCheckpoint(checkpoint, 'manifest_written');
|
|
1120
1325
|
}
|
|
1121
1326
|
await verifyCopies(workspace, journal);
|
|
1122
1327
|
for (const [number, operation] of operations.entries()) {
|
|
@@ -1125,7 +1330,7 @@ async function applyJournal(workspace, journal, snapshot, checkpoint) {
|
|
|
1125
1330
|
if (await matches(workspace, path, file)) {
|
|
1126
1331
|
await unlink(resolve(workspace, path));
|
|
1127
1332
|
await syncDirectory(workspace, posix.dirname(path));
|
|
1128
|
-
await checkpoint
|
|
1333
|
+
await copyCheckpoint(checkpoint, 'cleanup_file_removed');
|
|
1129
1334
|
}
|
|
1130
1335
|
}
|
|
1131
1336
|
}
|
|
@@ -1148,7 +1353,7 @@ async function applyJournal(workspace, journal, snapshot, checkpoint) {
|
|
|
1148
1353
|
await rmdir(await checkedPath(workspace, folder));
|
|
1149
1354
|
}
|
|
1150
1355
|
catch (error) {
|
|
1151
|
-
if (!missing(error) && !['ENOTEMPTY', 'EEXIST'].includes(
|
|
1356
|
+
if (!missing(error) && !['ENOTEMPTY', 'EEXIST'].includes(errorCode(error) || ''))
|
|
1152
1357
|
throw error;
|
|
1153
1358
|
}
|
|
1154
1359
|
}
|
|
@@ -1158,21 +1363,88 @@ async function applyJournal(workspace, journal, snapshot, checkpoint) {
|
|
|
1158
1363
|
await unlink(resolve(workspace, JOURNAL_PATH));
|
|
1159
1364
|
await syncDirectory(workspace, GENERATED_ROOT);
|
|
1160
1365
|
}
|
|
1366
|
+
async function copyCheckpoint(checkpoint, point) {
|
|
1367
|
+
await packageEffect(currentBootstrapHostScope(), async () => { await checkpoint?.(point); });
|
|
1368
|
+
}
|
|
1369
|
+
function ownedLockMetadata(metadata, identity) {
|
|
1370
|
+
return metadata.isFile() && !metadata.isSymbolicLink() && metadata.dev === identity.dev && metadata.ino === identity.ino
|
|
1371
|
+
&& metadata.nlink >= 1n && metadata.nlink <= 2n;
|
|
1372
|
+
}
|
|
1373
|
+
// Cooperative release can inspect/delete only this acquired inode and nonce,
|
|
1374
|
+
// even after ordinary setup admission closes. It never resumes journal work.
|
|
1375
|
+
async function releaseLockFile(workspace, path, identity, text) {
|
|
1376
|
+
const scope = currentBootstrapHostScope(), parentPath = resolve(workspace, posix.dirname(path)), source = resolve(workspace, path);
|
|
1377
|
+
try {
|
|
1378
|
+
const parent = await nativeLstat(parentPath, { bigint: true });
|
|
1379
|
+
if (!parent.isDirectory() || parent.isSymbolicLink() || parent.dev !== identity.parentDev || parent.ino !== identity.parentIno)
|
|
1380
|
+
throw cleanupFailure();
|
|
1381
|
+
const metadata = await nativeLstat(source, { bigint: true }).catch(error => { if (missing(error))
|
|
1382
|
+
return null; throw error; });
|
|
1383
|
+
if (!metadata)
|
|
1384
|
+
return;
|
|
1385
|
+
if (!ownedLockMetadata(metadata, identity))
|
|
1386
|
+
throw cleanupFailure();
|
|
1387
|
+
const expected = Buffer.from(text), bytes = Buffer.alloc(expected.length + 1);
|
|
1388
|
+
let handle, before;
|
|
1389
|
+
try {
|
|
1390
|
+
handle = await open(source, constants.O_RDONLY | (constants.O_NOFOLLOW || 0) | (constants.O_NONBLOCK || 0));
|
|
1391
|
+
before = await handle.stat({ bigint: true });
|
|
1392
|
+
if (!ownedLockMetadata(before, identity) || before.size !== BigInt(expected.length))
|
|
1393
|
+
throw cleanupFailure();
|
|
1394
|
+
let size = 0;
|
|
1395
|
+
while (size < bytes.length) {
|
|
1396
|
+
const result = await handle.read(bytes, size, bytes.length - size, size);
|
|
1397
|
+
if (!result.bytesRead)
|
|
1398
|
+
break;
|
|
1399
|
+
size += result.bytesRead;
|
|
1400
|
+
}
|
|
1401
|
+
const after = await handle.stat({ bigint: true });
|
|
1402
|
+
if (!ownedLockMetadata(after, identity) || size !== expected.length || !bytes.subarray(0, size).equals(expected)
|
|
1403
|
+
|| before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs)
|
|
1404
|
+
throw cleanupFailure();
|
|
1405
|
+
}
|
|
1406
|
+
finally {
|
|
1407
|
+
await closeReadHandle(handle, scope);
|
|
1408
|
+
}
|
|
1409
|
+
const current = await nativeLstat(source, { bigint: true });
|
|
1410
|
+
if (!ownedLockMetadata(current, identity) || before.size !== current.size
|
|
1411
|
+
|| before.mtimeNs !== current.mtimeNs || before.ctimeNs !== current.ctimeNs)
|
|
1412
|
+
throw cleanupFailure();
|
|
1413
|
+
await nativeUnlink(source);
|
|
1414
|
+
if (process.platform !== 'win32') {
|
|
1415
|
+
let directory;
|
|
1416
|
+
try {
|
|
1417
|
+
directory = await open(parentPath, constants.O_RDONLY | (constants.O_NOFOLLOW || 0));
|
|
1418
|
+
const currentParent = await directory.stat({ bigint: true });
|
|
1419
|
+
if (!currentParent.isDirectory() || currentParent.dev !== identity.parentDev || currentParent.ino !== identity.parentIno)
|
|
1420
|
+
throw cleanupFailure();
|
|
1421
|
+
await directory.sync();
|
|
1422
|
+
}
|
|
1423
|
+
finally {
|
|
1424
|
+
await closeReadHandle(directory, scope);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
catch {
|
|
1429
|
+
throw cleanupFailure();
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1161
1432
|
async function withCopyLock(workspace, action) {
|
|
1433
|
+
const scope = currentBootstrapHostScope();
|
|
1162
1434
|
const lock = { schema: 'dharma.repository-package-copy-lock/v1', platform: process.platform, pid: process.pid, nonce: randomUUID() };
|
|
1163
1435
|
const text = `${canonicalize(lock)}\n`;
|
|
1164
1436
|
const temporary = `${GENERATED_ROOT}/.lock-${lock.nonce}.tmp`;
|
|
1165
|
-
await freshFile(workspace, temporary, text);
|
|
1437
|
+
const identity = await freshFile(workspace, temporary, text);
|
|
1166
1438
|
let acquired = false;
|
|
1167
1439
|
try {
|
|
1440
|
+
await verifyStaging(workspace, temporary, identity);
|
|
1168
1441
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1169
1442
|
try {
|
|
1170
|
-
await
|
|
1171
|
-
acquired = true;
|
|
1443
|
+
await packageEffect(scope, async () => { await nativeLink(resolve(workspace, temporary), resolve(workspace, LOCK_PATH)); acquired = true; });
|
|
1172
1444
|
break;
|
|
1173
1445
|
}
|
|
1174
1446
|
catch (error) {
|
|
1175
|
-
if (error
|
|
1447
|
+
if (errorCode(error) !== 'EEXIST')
|
|
1176
1448
|
throw error;
|
|
1177
1449
|
const bytes = await readStable(workspace, LOCK_PATH, 4096);
|
|
1178
1450
|
const existing = JSON.parse(bytes.toString('utf8'));
|
|
@@ -1180,11 +1452,11 @@ async function withCopyLock(workspace, action) {
|
|
|
1180
1452
|
throw new Error('Managed copies lock conflict.');
|
|
1181
1453
|
}
|
|
1182
1454
|
try {
|
|
1183
|
-
process.kill(existing.pid, 0);
|
|
1455
|
+
await packageEffect(scope, async () => { process.kill(existing.pid, 0); });
|
|
1184
1456
|
throw new Error('Managed copies writer is active; retry later.');
|
|
1185
1457
|
}
|
|
1186
1458
|
catch (error) {
|
|
1187
|
-
if (error
|
|
1459
|
+
if (errorCode(error) !== 'ESRCH')
|
|
1188
1460
|
throw error;
|
|
1189
1461
|
}
|
|
1190
1462
|
if (!(await readStable(workspace, LOCK_PATH, 4096)).equals(bytes))
|
|
@@ -1194,16 +1466,25 @@ async function withCopyLock(workspace, action) {
|
|
|
1194
1466
|
}
|
|
1195
1467
|
if (!acquired)
|
|
1196
1468
|
throw new Error('Managed copies lock conflict.');
|
|
1197
|
-
await action
|
|
1469
|
+
await packageEffect(scope, action);
|
|
1198
1470
|
}
|
|
1199
1471
|
finally {
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1472
|
+
let failed = false;
|
|
1473
|
+
if (acquired)
|
|
1474
|
+
try {
|
|
1475
|
+
await releaseLockFile(workspace, LOCK_PATH, identity, text);
|
|
1476
|
+
}
|
|
1477
|
+
catch {
|
|
1478
|
+
failed = true;
|
|
1479
|
+
}
|
|
1480
|
+
try {
|
|
1481
|
+
await releaseLockFile(workspace, temporary, identity, text);
|
|
1482
|
+
}
|
|
1483
|
+
catch {
|
|
1484
|
+
failed = true;
|
|
1205
1485
|
}
|
|
1206
|
-
|
|
1486
|
+
if (failed)
|
|
1487
|
+
throw cleanupFailure();
|
|
1207
1488
|
}
|
|
1208
1489
|
}
|
|
1209
1490
|
//# sourceMappingURL=repositoryPackage.js.map
|