spawnfile 0.1.14 → 0.1.17
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/README.md +60 -159
- package/dist/audit/checksShared.d.ts +1 -1
- package/dist/auth/importers.d.ts +10 -0
- package/dist/auth/importers.js +29 -4
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/paths.d.ts +1 -1
- package/dist/auth/profileStore.js +9 -9
- package/dist/auth/targetSecretSourceFsIdentity.d.ts +40 -0
- package/dist/auth/targetSecretSourceFsIdentity.js +42 -0
- package/dist/auth/targetSecretSourceFsPublish.d.ts +2 -0
- package/dist/auth/targetSecretSourceFsPublish.js +3 -1
- package/dist/auth/targetSecretSourceFsPublishImmutable.d.ts +2 -1
- package/dist/auth/targetSecretSourceFsPublishImmutable.js +104 -71
- package/dist/auth/targetSecretSourceFsRead.js +5 -14
- package/dist/auth/trainingAuth.d.ts +21 -0
- package/dist/auth/trainingAuth.js +86 -0
- package/dist/auth/types.d.ts +1 -1
- package/dist/cli/authCommands.d.ts +1 -1
- package/dist/cli/authCommands.js +9 -0
- package/dist/cli/canaryCutoverCommand.d.ts +2 -0
- package/dist/cli/canaryCutoverCommand.js +30 -0
- package/dist/cli/capabilitiesCommand.d.ts +3 -0
- package/dist/cli/capabilitiesCommand.js +12 -0
- package/dist/cli/capabilitiesReceipt.d.ts +56 -0
- package/dist/cli/capabilitiesReceipt.js +56 -0
- package/dist/cli/compileBuildCommands.js +13 -0
- package/dist/cli/composedLifecycleContractSet.d.ts +21 -0
- package/dist/cli/composedLifecycleContractSet.js +204 -0
- package/dist/cli/devCommands.js +2 -0
- package/dist/cli/downCommand.js +19 -8
- package/dist/cli/evidenceExportHelperCommand.d.ts +13 -0
- package/dist/cli/evidenceExportHelperCommand.js +51 -0
- package/dist/cli/index.js +12 -3
- package/dist/cli/lifecycleMachine.d.ts +7 -1
- package/dist/cli/lifecycleMachine.js +2 -2
- package/dist/cli/paideiaDelegation.d.ts +17 -0
- package/dist/cli/paideiaDelegation.js +210 -0
- package/dist/cli/paideiaSupervisor.d.ts +17 -0
- package/dist/cli/paideiaSupervisor.js +48 -0
- package/dist/cli/productStateCloneCommand.d.ts +2 -0
- package/dist/cli/productStateCloneCommand.js +34 -0
- package/dist/cli/runCli.d.ts +13 -3
- package/dist/cli/runCli.js +37 -9
- package/dist/cli/targetCommands.d.ts +4 -7
- package/dist/cli/targetCommands.js +16 -46
- package/dist/cli/targetConfigPreparedPlan.d.ts +3 -0
- package/dist/cli/targetConfigPreparedPlan.js +74 -0
- package/dist/cli/targetConfigResolver.d.ts +3 -0
- package/dist/cli/targetConfigResolver.js +111 -0
- package/dist/cli/targetConfigResolverCommand.d.ts +6 -0
- package/dist/cli/targetConfigResolverCommand.js +43 -0
- package/dist/cli/targetConfigResolverContracts.d.ts +56 -0
- package/dist/cli/targetConfigResolverContracts.js +30 -0
- package/dist/cli/targetConfigResolverDocker.d.ts +4 -0
- package/dist/cli/targetConfigResolverDocker.js +47 -0
- package/dist/cli/targetConfigResolverValidation.d.ts +10 -0
- package/dist/cli/targetConfigResolverValidation.js +94 -0
- package/dist/cli/targetDefaultAuthorities.d.ts +2 -0
- package/dist/cli/targetDefaultAuthorities.js +3 -0
- package/dist/cli/targetDefaultConfig.d.ts +7 -0
- package/dist/cli/targetDefaultConfig.js +22 -5
- package/dist/cli/targetDefaultConfigStdin.js +7 -1
- package/dist/cli/targetDefaultHandlerFactory.js +23 -2
- package/dist/cli/targetDefaultHandlers.d.ts +2 -2
- package/dist/cli/targetProductionCommands.d.ts +5 -0
- package/dist/cli/targetProductionCommands.js +45 -0
- package/dist/cli/trainCommand.d.ts +3 -0
- package/dist/cli/trainCommand.js +61 -0
- package/dist/cli/upCommand.js +35 -6
- package/dist/cli/upLifecycleRecovery.d.ts +2 -2
- package/dist/cli/upLifecycleRecovery.js +188 -44
- package/dist/cli/usageCommand.d.ts +40 -0
- package/dist/cli/usageCommand.js +284 -0
- package/dist/cli/usageCommandLive.d.ts +84 -0
- package/dist/cli/usageCommandLive.js +95 -0
- package/dist/cli/usageDeploymentSource.d.ts +9 -0
- package/dist/cli/usageDeploymentSource.js +22 -0
- package/dist/cli/workspaceResourceMigrationCommand.d.ts +8 -0
- package/dist/cli/workspaceResourceMigrationCommand.js +36 -0
- package/dist/compiler/buildCompilePlanTeams.d.ts +1 -1
- package/dist/compiler/buildCompilePlanTeams.js +6 -1
- package/dist/compiler/compilePlanHelpers.js +3 -0
- package/dist/compiler/compileProject.d.ts +2 -0
- package/dist/compiler/compileProject.js +5 -1
- package/dist/compiler/compileProjectSupport.js +9 -0
- package/dist/compiler/containerArtifacts.d.ts +3 -1
- package/dist/compiler/containerArtifacts.js +92 -40
- package/dist/compiler/containerArtifactsPlans.d.ts +1 -1
- package/dist/compiler/containerArtifactsPlans.js +24 -7
- package/dist/compiler/containerArtifactsRender.d.ts +2 -1
- package/dist/compiler/containerArtifactsRender.js +91 -58
- package/dist/compiler/containerArtifactsTypes.d.ts +8 -0
- package/dist/compiler/containerBackedMountRender.d.ts +56 -0
- package/dist/compiler/containerBackedMountRender.js +101 -0
- package/dist/compiler/containerDaimonBrokerRender.d.ts +112 -0
- package/dist/compiler/containerDaimonBrokerRender.js +191 -0
- package/dist/compiler/containerDaimonGrokWorkerProvisioning.d.ts +38 -0
- package/dist/compiler/containerDaimonGrokWorkerProvisioning.js +108 -0
- package/dist/compiler/containerDaimonGrokWorkerRender.d.ts +172 -0
- package/dist/compiler/containerDaimonGrokWorkerRender.js +268 -0
- package/dist/compiler/containerDaimonOwnershipGuardRender.d.ts +12 -0
- package/dist/compiler/containerDaimonOwnershipGuardRender.js +191 -0
- package/dist/compiler/containerDaimonUidEntrypointRender.d.ts +24 -0
- package/dist/compiler/containerDaimonUidEntrypointRender.js +297 -0
- package/dist/compiler/containerEntrypointRender.d.ts +7 -0
- package/dist/compiler/containerEntrypointRender.js +28 -22
- package/dist/compiler/containerPersistentMounts.d.ts +30 -0
- package/dist/compiler/containerPersistentMounts.js +66 -0
- package/dist/compiler/containerReadinessPaths.d.ts +1 -0
- package/dist/compiler/containerReadinessPaths.js +1 -0
- package/dist/compiler/containerRuntimeLinkMaterializer.d.ts +2 -0
- package/dist/compiler/containerRuntimeLinkMaterializer.js +12 -0
- package/dist/compiler/containerRuntimeReadinessRender.d.ts +7 -0
- package/dist/compiler/containerRuntimeReadinessRender.js +40 -0
- package/dist/compiler/containerStateOwnershipRender.d.ts +4 -0
- package/dist/compiler/containerStateOwnershipRender.js +75 -0
- package/dist/compiler/containerTargetResources.d.ts +22 -1
- package/dist/compiler/containerTargetResources.js +94 -2
- package/dist/compiler/containerVolumeBootstrap.d.ts +3 -0
- package/dist/compiler/containerVolumeBootstrap.js +3 -0
- package/dist/compiler/containerWorkspaceResourceRender.js +49 -1
- package/dist/compiler/daimonTelemetryArtifacts.d.ts +2 -2
- package/dist/compiler/daimonTelemetryArtifacts.js +5 -10
- package/dist/compiler/deploymentLineage.d.ts +40 -0
- package/dist/compiler/deploymentLineage.js +50 -0
- package/dist/compiler/index.d.ts +3 -0
- package/dist/compiler/index.js +3 -0
- package/dist/compiler/initProject.d.ts +2 -0
- package/dist/compiler/initProject.js +39 -1
- package/dist/compiler/localMoltnetAuthority.d.ts +64 -0
- package/dist/compiler/localMoltnetAuthority.js +177 -0
- package/dist/compiler/memoryArtifacts.d.ts +1 -1
- package/dist/compiler/memoryArtifacts.js +108 -32
- package/dist/compiler/modelEnv.d.ts +5 -0
- package/dist/compiler/modelEnv.js +8 -2
- package/dist/compiler/moltnetArtifactPaths.d.ts +17 -12
- package/dist/compiler/moltnetArtifactPaths.js +17 -15
- package/dist/compiler/moltnetArtifactTypes.d.ts +10 -0
- package/dist/compiler/moltnetArtifacts.d.ts +1 -1
- package/dist/compiler/moltnetArtifacts.js +52 -12
- package/dist/compiler/moltnetBinaries.d.ts +16 -4
- package/dist/compiler/moltnetBinaries.js +46 -6
- package/dist/compiler/moltnetClientConfig.js +1 -1
- package/dist/compiler/moltnetConfigLowering.d.ts +3 -1
- package/dist/compiler/moltnetConfigLowering.js +30 -6
- package/dist/compiler/moltnetNodeConfig.js +1 -1
- package/dist/compiler/moltnetReleaseAuthority.d.ts +24 -1
- package/dist/compiler/moltnetReleaseAuthority.js +33 -4
- package/dist/compiler/moltnetRepresentativeResolution.d.ts +1 -1
- package/dist/compiler/moltnetRepresentativeResolution.js +2 -2
- package/dist/compiler/moltnetResolution.js +9 -15
- package/dist/compiler/moltnetRoomMemberships.js +20 -5
- package/dist/compiler/moltnetRoomPolicyCompatibility.d.ts +1 -1
- package/dist/compiler/moltnetRoomPolicyCompatibility.js +3 -1
- package/dist/compiler/moltnetRuntimeConfig.d.ts +1 -1
- package/dist/compiler/moltnetRuntimeConfig.js +22 -2
- package/dist/compiler/moltnetServerPlans.js +19 -0
- package/dist/compiler/organizationExternalParticipants.d.ts +3 -0
- package/dist/compiler/organizationExternalParticipants.js +176 -0
- package/dist/compiler/organizationIdentity.d.ts +3 -9
- package/dist/compiler/organizationIdentity.js +37 -270
- package/dist/compiler/organizationIdentityGraph.d.ts +15 -0
- package/dist/compiler/organizationIdentityGraph.js +115 -0
- package/dist/compiler/organizationReadyEvidence.d.ts +8 -0
- package/dist/compiler/organizationReadyEvidence.js +23 -3
- package/dist/compiler/runProject.d.ts +4 -2
- package/dist/compiler/runProject.js +32 -6
- package/dist/compiler/runProjectAuth.d.ts +3 -1
- package/dist/compiler/runProjectAuth.js +19 -3
- package/dist/compiler/runProjectDaimonCodexDocker.d.ts +3 -0
- package/dist/compiler/runProjectDaimonCodexDocker.js +36 -0
- package/dist/compiler/runProjectDocker.d.ts +8 -4
- package/dist/compiler/runProjectDocker.js +43 -18
- package/dist/compiler/runProjectDockerDaimonGuards.d.ts +15 -0
- package/dist/compiler/runProjectDockerDaimonGuards.js +122 -0
- package/dist/compiler/runProjectDockerReservation.d.ts +3 -0
- package/dist/compiler/runProjectDockerReservation.js +118 -0
- package/dist/compiler/runProjectLifecycle.js +1 -1
- package/dist/compiler/training/broker/declaration.d.ts +45 -0
- package/dist/compiler/training/broker/declaration.js +55 -0
- package/dist/compiler/training/broker/entrypoint.d.ts +39 -0
- package/dist/compiler/training/broker/entrypoint.js +111 -0
- package/dist/compiler/training/broker/index.d.ts +10 -0
- package/dist/compiler/training/broker/index.js +10 -0
- package/dist/compiler/training/broker/main.d.ts +2 -0
- package/dist/compiler/training/broker/main.js +14 -0
- package/dist/compiler/training/broker/paths.d.ts +196 -0
- package/dist/compiler/training/broker/paths.js +241 -0
- package/dist/compiler/training/broker/processes.d.ts +42 -0
- package/dist/compiler/training/broker/processes.js +99 -0
- package/dist/compiler/training/broker/projection.d.ts +51 -0
- package/dist/compiler/training/broker/projection.js +76 -0
- package/dist/compiler/training/broker/provisioning.d.ts +87 -0
- package/dist/compiler/training/broker/provisioning.js +213 -0
- package/dist/compiler/training/broker/receipt.d.ts +77 -0
- package/dist/compiler/training/broker/receipt.js +110 -0
- package/dist/compiler/training/broker/registration.d.ts +41 -0
- package/dist/compiler/training/broker/registration.js +102 -0
- package/dist/compiler/training/broker/runtime.d.ts +25 -0
- package/dist/compiler/training/broker/runtime.js +173 -0
- package/dist/compiler/training/broker/seccompRoutes.d.ts +31 -0
- package/dist/compiler/training/broker/seccompRoutes.js +36 -0
- package/dist/compiler/training/broker/supervisor.d.ts +109 -0
- package/dist/compiler/training/broker/supervisor.js +173 -0
- package/dist/compiler/training/container/contract.d.ts +31 -0
- package/dist/compiler/training/container/contract.js +33 -0
- package/dist/compiler/training/container/index.d.ts +4 -0
- package/dist/compiler/training/container/index.js +2 -0
- package/dist/compiler/training/container/launch.d.ts +18 -0
- package/dist/compiler/training/container/launch.js +173 -0
- package/dist/compiler/training/container/prepare.d.ts +8 -0
- package/dist/compiler/training/container/prepare.js +80 -0
- package/dist/compiler/training/container/process.d.ts +14 -0
- package/dist/compiler/training/container/process.js +70 -0
- package/dist/compiler/training/container/security.d.ts +36 -0
- package/dist/compiler/training/container/security.js +58 -0
- package/dist/compiler/training/context.d.ts +7 -0
- package/dist/compiler/training/context.js +93 -0
- package/dist/compiler/training/contract.d.ts +142 -0
- package/dist/compiler/training/contract.js +34 -0
- package/dist/compiler/training/index.d.ts +4 -0
- package/dist/compiler/training/index.js +3 -0
- package/dist/compiler/training/preparation/assets/Dockerfile +98 -0
- package/dist/compiler/training/preparation/assets/package-lock.json +3787 -0
- package/dist/compiler/training/preparation/broker.d.ts +21 -0
- package/dist/compiler/training/preparation/broker.js +43 -0
- package/dist/compiler/training/preparation/contextModes.d.ts +12 -0
- package/dist/compiler/training/preparation/contextModes.js +38 -0
- package/dist/compiler/training/preparation/contract.d.ts +182 -0
- package/dist/compiler/training/preparation/contract.js +94 -0
- package/dist/compiler/training/preparation/copyAssets.d.ts +1 -0
- package/dist/compiler/training/preparation/copyAssets.js +8 -0
- package/dist/compiler/training/preparation/daimonParent.d.ts +42 -0
- package/dist/compiler/training/preparation/daimonParent.js +114 -0
- package/dist/compiler/training/preparation/files.d.ts +25 -0
- package/dist/compiler/training/preparation/files.js +85 -0
- package/dist/compiler/training/preparation/image.d.ts +28 -0
- package/dist/compiler/training/preparation/image.js +102 -0
- package/dist/compiler/training/preparation/index.d.ts +3 -0
- package/dist/compiler/training/preparation/index.js +2 -0
- package/dist/compiler/training/preparation/inputs.d.ts +23 -0
- package/dist/compiler/training/preparation/inputs.js +93 -0
- package/dist/compiler/training/preparation/prepare.d.ts +33 -0
- package/dist/compiler/training/preparation/prepare.js +186 -0
- package/dist/compiler/training/preparation/scratch.d.ts +19 -0
- package/dist/compiler/training/preparation/scratch.js +48 -0
- package/dist/compiler/training/repair/compatibility.d.ts +11 -0
- package/dist/compiler/training/repair/compatibility.js +34 -0
- package/dist/compiler/training/repair/contract.d.ts +506 -0
- package/dist/compiler/training/repair/contract.js +31 -0
- package/dist/compiler/training/repair/index.d.ts +3 -0
- package/dist/compiler/training/repair/index.js +3 -0
- package/dist/compiler/training/repair/prepare.d.ts +230 -0
- package/dist/compiler/training/repair/prepare.js +74 -0
- package/dist/compiler/training/repair/witness.d.ts +203 -0
- package/dist/compiler/training/repair/witness.js +42 -0
- package/dist/compiler/types.d.ts +2 -0
- package/dist/compiler/upProject.d.ts +26 -1
- package/dist/compiler/upProject.js +117 -178
- package/dist/compiler/upProjectHandoff.d.ts +62 -0
- package/dist/compiler/upProjectHandoff.js +204 -0
- package/dist/compiler/upReceipt.js +49 -4
- package/dist/compiler/updateProjectModels.js +1 -0
- package/dist/compiler/view/types.d.ts +1 -1
- package/dist/compiler/workspaceBundleArtifacts.d.ts +3 -0
- package/dist/compiler/workspaceBundleArtifacts.js +106 -0
- package/dist/compiler/workspaceResources.d.ts +4 -1
- package/dist/compiler/workspaceResources.js +12 -7
- package/dist/deployment/artifactsExportPlan.js +51 -1
- package/dist/deployment/canaryCutover.d.ts +42 -0
- package/dist/deployment/canaryCutover.js +140 -0
- package/dist/deployment/candidateDiagnostics.d.ts +15 -0
- package/dist/deployment/candidateDiagnostics.js +90 -0
- package/dist/deployment/dockerLogs.js +4 -1
- package/dist/deployment/dockerManager.js +9 -3
- package/dist/deployment/dockerProbeGateway.d.ts +17 -0
- package/dist/deployment/dockerProbeGateway.js +17 -2
- package/dist/deployment/downDeployment.d.ts +5 -0
- package/dist/deployment/downDeployment.js +109 -8
- package/dist/deployment/downReceiptTypes.d.ts +17 -2
- package/dist/deployment/downReceiptTypes.js +17 -2
- package/dist/deployment/index.d.ts +4 -0
- package/dist/deployment/index.js +4 -0
- package/dist/deployment/lifecycleCompletionContracts.d.ts +66 -0
- package/dist/deployment/lifecycleCompletionContracts.js +34 -0
- package/dist/deployment/lifecycleCompletionPaths.d.ts +4 -1
- package/dist/deployment/lifecycleCompletionPaths.js +3 -0
- package/dist/deployment/lifecycleCompletionPublication.d.ts +2 -0
- package/dist/deployment/lifecycleCompletionPublication.js +18 -4
- package/dist/deployment/lifecycleCompletionStore.d.ts +1 -1
- package/dist/deployment/lifecycleCompletionStore.js +7 -7
- package/dist/deployment/lifecycleUpRecords.d.ts +11 -0
- package/dist/deployment/lifecycleUpRecords.js +127 -0
- package/dist/deployment/native/AGENTS.md +11 -0
- package/dist/deployment/native/rename-noreplace-arm64 +0 -0
- package/dist/deployment/native/rename-noreplace-arm64.provenance.json +1 -0
- package/dist/deployment/native/rename-noreplace-x64 +0 -0
- package/dist/deployment/native/rename-noreplace-x64.provenance.json +1 -0
- package/dist/deployment/noReplaceActivation.d.ts +17 -0
- package/dist/deployment/noReplaceActivation.js +50 -0
- package/dist/deployment/organizationHandoffAuthorityFsBudget.d.ts +110 -0
- package/dist/deployment/organizationHandoffAuthorityFsBudget.js +156 -0
- package/dist/deployment/organizationHandoffAuthorityFsClient.js +82 -27
- package/dist/deployment/organizationHandoffAuthorityFsElection.d.ts +21 -0
- package/dist/deployment/organizationHandoffAuthorityFsElection.js +57 -0
- package/dist/deployment/organizationHandoffAuthorityFsWorker.js +254 -108
- package/dist/deployment/organizationHandoffAuthorityFsWorkerStall.fixture.d.ts +1 -0
- package/dist/deployment/organizationHandoffAuthorityFsWorkerStall.fixture.js +16 -0
- package/dist/deployment/organizationHandoffAuthorityStore.js +14 -7
- package/dist/deployment/organizationHandoffAuthorityTypes.d.ts +2 -2
- package/dist/deployment/organizationHandoffAuthorityTypes.js +2 -1
- package/dist/deployment/organizationReady.d.ts +2 -0
- package/dist/deployment/organizationReady.js +36 -4
- package/dist/deployment/productStateClone.d.ts +43 -0
- package/dist/deployment/productStateClone.js +319 -0
- package/dist/deployment/upLifecycleRecoveryState.d.ts +23 -0
- package/dist/deployment/upLifecycleRecoveryState.js +12 -0
- package/dist/deployment/upReceiptTypes.d.ts +32 -6
- package/dist/deployment/upReceiptTypes.js +29 -2
- package/dist/deployment/workspaceResourceMigration.d.ts +55 -0
- package/dist/deployment/workspaceResourceMigration.js +315 -0
- package/dist/dev/project.d.ts +7 -0
- package/dist/dev/project.js +9 -0
- package/dist/distribution/consumeImage.d.ts +2 -0
- package/dist/distribution/consumeImage.js +98 -73
- package/dist/distribution/consumeImageDaimonDocker.d.ts +3 -0
- package/dist/distribution/consumeImageDaimonDocker.js +45 -0
- package/dist/distribution/consumeImageLifecycle.d.ts +17 -0
- package/dist/distribution/consumeImageLifecycle.js +231 -0
- package/dist/distribution/consumeImageSupport.d.ts +12 -0
- package/dist/distribution/consumeImageSupport.js +15 -0
- package/dist/distribution/distributionReportSchema.d.ts +11 -1
- package/dist/distribution/distributionReportSchema.js +15 -2
- package/dist/distribution/dockerRunner.d.ts +6 -1
- package/dist/distribution/dockerRunner.js +31 -4
- package/dist/distribution/imageRuntimeAuth.d.ts +9 -1
- package/dist/distribution/imageRuntimeAuth.js +70 -1
- package/dist/distribution/index.d.ts +1 -1
- package/dist/distribution/index.js +1 -1
- package/dist/distribution/preflight.js +5 -1
- package/dist/distribution/types.d.ts +14 -1
- package/dist/distribution/types.js +3 -0
- package/dist/evidenceExportHelper/boundedExecutor.d.ts +3 -0
- package/dist/evidenceExportHelper/boundedExecutor.js +33 -0
- package/dist/evidenceExportHelper/helperProgram.mjs +156 -0
- package/dist/evidenceExportHelper/index.d.ts +4 -0
- package/dist/evidenceExportHelper/index.js +4 -0
- package/dist/evidenceExportHelper/preparedAuthority.d.ts +64 -0
- package/dist/evidenceExportHelper/preparedAuthority.js +228 -0
- package/dist/evidenceExportHelper/preparedBuilder.d.ts +10 -0
- package/dist/evidenceExportHelper/preparedBuilder.js +183 -0
- package/dist/evidenceExportHelper/preparedBuilderTypes.d.ts +19 -0
- package/dist/evidenceExportHelper/preparedBuilderTypes.js +1 -0
- package/dist/evidenceExportHelper/recipe.d.ts +7 -0
- package/dist/evidenceExportHelper/recipe.js +83 -0
- package/dist/filesystem/io.d.ts +2 -0
- package/dist/filesystem/io.js +16 -1
- package/dist/manifest/executionSchemas.d.ts +27 -0
- package/dist/manifest/executionSchemas.js +21 -2
- package/dist/manifest/mcpSchemas.d.ts +1 -0
- package/dist/manifest/mcpSchemas.js +3 -0
- package/dist/manifest/renderSpawnfileNetworks.js +11 -0
- package/dist/manifest/renderSpawnfileWorkspace.js +5 -0
- package/dist/manifest/scheduleSchemas.d.ts +3 -1
- package/dist/manifest/scheduleSchemas.js +91 -5
- package/dist/manifest/schemas.d.ts +237 -12
- package/dist/manifest/schemas.js +5 -2
- package/dist/manifest/teamNetworkAccessSchemas.d.ts +5 -1
- package/dist/manifest/teamNetworkAccessSchemas.js +11 -0
- package/dist/manifest/teamNetworkAuthSchemas.d.ts +1 -1
- package/dist/manifest/teamNetworkSchemas.d.ts +17 -3
- package/dist/manifest/teamNetworkSchemas.js +19 -1
- package/dist/manifest/teamNetworkServerSchemas.d.ts +8 -3
- package/dist/manifest/teamNetworkServerSchemas.js +18 -2
- package/dist/manifest/workspaceSchemas.d.ts +16 -0
- package/dist/manifest/workspaceSchemas.js +11 -0
- package/dist/report/types.d.ts +34 -4
- package/dist/runtime/common.d.ts +28 -1
- package/dist/runtime/common.js +33 -3
- package/dist/runtime/container.js +49 -33
- package/dist/runtime/daimon/adapter.d.ts +14 -0
- package/dist/runtime/daimon/adapter.js +243 -0
- package/dist/runtime/daimon/attention.d.ts +8 -0
- package/dist/runtime/daimon/attention.js +32 -0
- package/dist/runtime/daimon/config.d.ts +25 -0
- package/dist/runtime/daimon/config.js +272 -0
- package/dist/runtime/daimon/contractManifest.d.ts +295 -0
- package/dist/runtime/daimon/contractManifest.js +319 -0
- package/dist/runtime/daimon/grokModel.d.ts +19 -0
- package/dist/runtime/daimon/grokModel.js +33 -0
- package/dist/runtime/daimon/grokWorkerConfigBytes.d.ts +26 -0
- package/dist/runtime/daimon/grokWorkerConfigBytes.js +36 -0
- package/dist/runtime/daimon/grokWorkerContract.d.ts +22 -0
- package/dist/runtime/daimon/grokWorkerContract.js +48 -0
- package/dist/runtime/daimon/memory.d.ts +57 -0
- package/dist/runtime/daimon/memory.js +125 -0
- package/dist/runtime/daimon/runAuth.d.ts +51 -0
- package/dist/runtime/daimon/runAuth.js +279 -0
- package/dist/runtime/daimon/runtimeIdentity.d.ts +22 -0
- package/dist/runtime/daimon/runtimeIdentity.js +22 -0
- package/dist/runtime/daimon/scheduleAuthority.d.ts +3 -0
- package/dist/runtime/daimon/scheduleAuthority.js +17 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/install.d.ts +3 -0
- package/dist/runtime/install.js +3 -0
- package/dist/runtime/localDaimonAuthority.d.ts +11 -0
- package/dist/runtime/localDaimonAuthority.js +77 -0
- package/dist/runtime/mnemeMcp.d.ts +13 -0
- package/dist/runtime/mnemeMcp.js +26 -0
- package/dist/runtime/openclaw/adapter.js +2 -2
- package/dist/runtime/pi/adapter.d.ts +0 -1
- package/dist/runtime/pi/adapter.js +2 -6
- package/dist/runtime/pi/appCliSource.js +1 -1
- package/dist/runtime/pi/appScheduleSource.js +6 -2
- package/dist/runtime/pi/appTemplateTypes.d.ts +1 -1
- package/dist/runtime/picoclaw/adapter.js +2 -2
- package/dist/runtime/registry.d.ts +3 -0
- package/dist/runtime/registry.js +19 -2
- package/dist/runtime/scheduleUtils.d.ts +1 -0
- package/dist/runtime/scheduleUtils.js +5 -1
- package/dist/runtime/types.d.ts +25 -0
- package/dist/runtime/usageLedger.d.ts +158 -0
- package/dist/runtime/usageLedger.js +269 -0
- package/dist/runtime/usageLedgerRead.d.ts +48 -0
- package/dist/runtime/usageLedgerRead.js +102 -0
- package/dist/runtime/usageRequestLedger.d.ts +42 -0
- package/dist/runtime/usageRequestLedger.js +72 -0
- package/dist/shared/daimonCodexDocker.d.ts +32 -0
- package/dist/shared/daimonCodexDocker.js +120 -0
- package/dist/shared/daimonGrokSeccompProfile.d.ts +10 -0
- package/dist/shared/daimonGrokSeccompProfile.js +11 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +2 -0
- package/dist/shared/types.d.ts +1 -1
- package/dist/shared/volumeNames.d.ts +6 -0
- package/dist/shared/volumeNames.js +14 -0
- package/dist/target/dockerArtifactsProvider.js +34 -7
- package/dist/target/dockerBaseImage.d.ts +2 -0
- package/dist/target/dockerBaseImage.js +11 -0
- package/dist/target/dockerCommandExecutor.d.ts +11 -0
- package/dist/target/dockerCommandExecutor.js +53 -2
- package/dist/target/dockerCommandExecutorCore.d.ts +2 -1
- package/dist/target/dockerCommandExecutorCore.js +4 -2
- package/dist/target/dockerPublicArtifactSnapshot.d.ts +3 -3
- package/dist/target/dockerPublicArtifactSnapshot.js +17 -33
- package/dist/target/dockerTarget.d.ts +1 -0
- package/dist/target/dockerTargetBinding.d.ts +1 -2
- package/dist/target/dockerTargetBinding.js +2 -3
- package/dist/target/dockerTargetExecFile.d.ts +19 -0
- package/dist/target/dockerTargetExecFile.js +194 -0
- package/dist/target/dockerWorldServiceProvider.d.ts +4 -0
- package/dist/target/dockerWorldServiceProvider.js +13 -1
- package/dist/target/evidenceExport.d.ts +3 -1
- package/dist/target/evidenceExport.js +12 -3
- package/dist/target/evidenceExportOperationsTestKit.js +3 -3
- package/dist/target/evidenceExportProvider.d.ts +2 -0
- package/dist/target/evidenceExportProvider.js +15 -5
- package/dist/target/publicArtifactSnapshot.d.ts +31 -0
- package/dist/target/publicArtifactSnapshot.js +37 -3
- package/examples/README.md +45 -0
- package/examples/agent-with-subagents/AGENTS.md +3 -0
- package/examples/agent-with-subagents/Spawnfile +30 -0
- package/examples/agent-with-subagents/subagents/critic/AGENTS.md +3 -0
- package/examples/agent-with-subagents/subagents/critic/Spawnfile +15 -0
- package/examples/agent-with-subagents/subagents/researcher/AGENTS.md +3 -0
- package/examples/agent-with-subagents/subagents/researcher/Spawnfile +17 -0
- package/examples/daimon-org/Spawnfile +68 -0
- package/examples/daimon-org/TEAM.md +4 -0
- package/examples/daimon-org/agents/mapper/AGENTS.md +4 -0
- package/examples/daimon-org/agents/mapper/Spawnfile +37 -0
- package/examples/daimon-org/shared/skills/file-note/SKILL.md +9 -0
- package/examples/daimon-org/teams/review/Spawnfile +53 -0
- package/examples/daimon-org/teams/review/agents/reviewer/AGENTS.md +4 -0
- package/examples/daimon-org/teams/review/agents/reviewer/Spawnfile +37 -0
- package/examples/daimon-public-host/AGENTS.md +3 -0
- package/examples/daimon-public-host/Spawnfile +21 -0
- package/examples/distribution-org/Spawnfile +43 -0
- package/examples/distribution-org/TEAM.md +4 -0
- package/examples/distribution-org/agents/coordinator/AGENTS.md +1 -0
- package/examples/distribution-org/agents/coordinator/Spawnfile +20 -0
- package/examples/distribution-org/agents/researcher/AGENTS.md +1 -0
- package/examples/distribution-org/agents/researcher/Spawnfile +20 -0
- package/examples/jungian-daimon-org/Spawnfile +41 -0
- package/examples/jungian-daimon-org/TEAM.md +21 -0
- package/examples/jungian-daimon-org/teams/luna/Spawnfile +47 -0
- package/examples/jungian-daimon-org/teams/luna/TEAM.md +4 -0
- package/examples/jungian-daimon-org/teams/luna/agents/animus/AGENTS.md +10 -0
- package/examples/jungian-daimon-org/teams/luna/agents/animus/Spawnfile +33 -0
- package/examples/jungian-daimon-org/teams/luna/agents/representative/AGENTS.md +21 -0
- package/examples/jungian-daimon-org/teams/luna/agents/representative/Spawnfile +30 -0
- package/examples/jungian-daimon-org/teams/luna/agents/shadow/AGENTS.md +10 -0
- package/examples/jungian-daimon-org/teams/luna/agents/shadow/Spawnfile +33 -0
- package/examples/jungian-daimon-org/teams/selene/Spawnfile +47 -0
- package/examples/jungian-daimon-org/teams/selene/TEAM.md +4 -0
- package/examples/jungian-daimon-org/teams/selene/agents/animus/AGENTS.md +10 -0
- package/examples/jungian-daimon-org/teams/selene/agents/animus/Spawnfile +33 -0
- package/examples/jungian-daimon-org/teams/selene/agents/representative/AGENTS.md +19 -0
- package/examples/jungian-daimon-org/teams/selene/agents/representative/Spawnfile +33 -0
- package/examples/jungian-daimon-org/teams/selene/agents/shadow/AGENTS.md +10 -0
- package/examples/jungian-daimon-org/teams/selene/agents/shadow/Spawnfile +30 -0
- package/examples/mixed-runtime-org/Spawnfile +71 -0
- package/examples/mixed-runtime-org/TEAM.md +8 -0
- package/examples/mixed-runtime-org/agents/analyst/AGENTS.md +8 -0
- package/examples/mixed-runtime-org/agents/analyst/Spawnfile +27 -0
- package/examples/mixed-runtime-org/agents/conductor/AGENTS.md +8 -0
- package/examples/mixed-runtime-org/agents/conductor/Spawnfile +27 -0
- package/examples/mixed-runtime-org/agents/localist/AGENTS.md +7 -0
- package/examples/mixed-runtime-org/agents/localist/Spawnfile +50 -0
- package/examples/moltnet-team-chat/Spawnfile +43 -0
- package/examples/moltnet-team-chat/TEAM.md +5 -0
- package/examples/moltnet-team-chat/agents/coordinator/AGENTS.md +9 -0
- package/examples/moltnet-team-chat/agents/coordinator/Spawnfile +26 -0
- package/examples/moltnet-team-chat/teams/analysis/Spawnfile +23 -0
- package/examples/moltnet-team-chat/teams/analysis/TEAM.md +3 -0
- package/examples/moltnet-team-chat/teams/analysis/agents/observer/AGENTS.md +5 -0
- package/examples/moltnet-team-chat/teams/analysis/agents/observer/Spawnfile +19 -0
- package/examples/moltnet-team-chat/teams/analysis/agents/representative/AGENTS.md +5 -0
- package/examples/moltnet-team-chat/teams/analysis/agents/representative/Spawnfile +19 -0
- package/examples/moltnet-team-chat/teams/field/Spawnfile +41 -0
- package/examples/moltnet-team-chat/teams/field/TEAM.md +3 -0
- package/examples/moltnet-team-chat/teams/field/agents/observer/AGENTS.md +5 -0
- package/examples/moltnet-team-chat/teams/field/agents/observer/Spawnfile +26 -0
- package/examples/moltnet-team-chat/teams/field/agents/representative/AGENTS.md +11 -0
- package/examples/moltnet-team-chat/teams/field/agents/representative/Spawnfile +26 -0
- package/examples/single-agent/AGENTS.md +3 -0
- package/examples/single-agent/HEARTBEAT.md +3 -0
- package/examples/single-agent/IDENTITY.md +3 -0
- package/examples/single-agent/MEMORY.md +3 -0
- package/examples/single-agent/SOUL.md +3 -0
- package/examples/single-agent/Spawnfile +41 -0
- package/examples/single-agent/skills/web_search/SKILL.md +6 -0
- package/moltnet-releases.json +5 -4
- package/package.json +36 -9
- package/runtimes.yaml +5 -2
- package/dist/cli/targetWorldClockCrossProcess.test-helper.d.ts +0 -13
- package/dist/cli/targetWorldClockCrossProcess.test-helper.js +0 -109
- package/dist/deployment/providerRuntimeBoundaryScanner.test-helper.d.ts +0 -8
- package/dist/deployment/providerRuntimeBoundaryScanner.test-helper.js +0 -60
- package/dist/deployment/simfileFixtureBoundary.test-helper.d.ts +0 -8
- package/dist/deployment/simfileFixtureBoundary.test-helper.js +0 -103
- package/dist/ownership/simfileRunOperatorInputs.d.ts +0 -103
- package/dist/ownership/simfileRunOperatorInputs.js +0 -139
- package/dist/target/cleanupRunDocker.test-helper.d.ts +0 -67
- package/dist/target/cleanupRunDocker.test-helper.js +0 -238
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import { cp, lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat, statfs, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pipeline } from "node:stream/promises";
|
|
6
|
+
import { activateNoReplace } from "./noReplaceActivation.js";
|
|
7
|
+
export const WORKSPACE_RESOURCE_MANIFEST_VERSION = "spawnfile.workspace-resource-manifest.v1";
|
|
8
|
+
export const WORKSPACE_RESOURCE_MIGRATION_VERSION = "spawnfile.workspace-resource-migration.v1";
|
|
9
|
+
const METADATA_MANIFEST = ".spawnfile-resource-migration-manifest.json";
|
|
10
|
+
const ACTIVATION_PROVENANCE = ".spawnfile-resource-activation-provenance";
|
|
11
|
+
const RESOURCE_IDENTITY = ".spawnfile-resource-identity";
|
|
12
|
+
const MAX_MANIFEST_BYTES = 16 * 1024 * 1024;
|
|
13
|
+
const MAX_JOURNAL_BYTES = 16 * 1024;
|
|
14
|
+
const digestBytes = (bytes) => `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
15
|
+
const fileDigest = async (file) => {
|
|
16
|
+
const hash = createHash("sha256");
|
|
17
|
+
await pipeline(createReadStream(file), hash);
|
|
18
|
+
return `sha256:${hash.digest("hex")}`;
|
|
19
|
+
};
|
|
20
|
+
const safeRelativePath = (value) => {
|
|
21
|
+
if (typeof value !== "string" || !value || path.isAbsolute(value) || value.includes("\\"))
|
|
22
|
+
throw new Error("Workspace resource manifest contains an unsafe path");
|
|
23
|
+
const normalized = path.posix.normalize(value);
|
|
24
|
+
if (normalized === "." || normalized === ".." || normalized.startsWith("../") || normalized !== value)
|
|
25
|
+
throw new Error("Workspace resource manifest contains an unsafe path");
|
|
26
|
+
return normalized;
|
|
27
|
+
};
|
|
28
|
+
const integer = (value, label) => {
|
|
29
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
|
|
30
|
+
throw new Error(`Workspace resource manifest ${label} is invalid`);
|
|
31
|
+
return value;
|
|
32
|
+
};
|
|
33
|
+
const exact = (value, keys) => Object.keys(value).sort().join("\0") === [...keys].sort().join("\0");
|
|
34
|
+
const parseManifest = (value) => {
|
|
35
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
36
|
+
throw new Error("Workspace resource manifest is invalid");
|
|
37
|
+
const root = value;
|
|
38
|
+
if (!exact(root, ["version", "root", "directories", "files"]) || root.version !== WORKSPACE_RESOURCE_MANIFEST_VERSION || !Array.isArray(root.directories) || !Array.isArray(root.files) || !root.root || typeof root.root !== "object" || Array.isArray(root.root))
|
|
39
|
+
throw new Error("Workspace resource manifest is invalid");
|
|
40
|
+
const rootMetadata = root.root;
|
|
41
|
+
if (!exact(rootMetadata, ["gid", "mode", "uid"]))
|
|
42
|
+
throw new Error("Workspace resource manifest root metadata is invalid");
|
|
43
|
+
const files = root.files.map((raw) => {
|
|
44
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
45
|
+
throw new Error("Workspace resource manifest file is invalid");
|
|
46
|
+
const file = raw;
|
|
47
|
+
if (!exact(file, ["gid", "mode", "path", "sha256", "size", "uid"]) || typeof file.sha256 !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(file.sha256))
|
|
48
|
+
throw new Error("Workspace resource manifest file is invalid");
|
|
49
|
+
return { gid: integer(file.gid, "file gid"), mode: integer(file.mode, "file mode"), path: safeRelativePath(file.path), sha256: file.sha256, size: integer(file.size, "file size"), uid: integer(file.uid, "file uid") };
|
|
50
|
+
}).sort((left, right) => left.path.localeCompare(right.path));
|
|
51
|
+
const directories = root.directories.map((raw) => {
|
|
52
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
53
|
+
throw new Error("Workspace resource manifest directory is invalid");
|
|
54
|
+
const directory = raw;
|
|
55
|
+
if (!exact(directory, ["gid", "mode", "path", "uid"]))
|
|
56
|
+
throw new Error("Workspace resource manifest directory is invalid");
|
|
57
|
+
return { gid: integer(directory.gid, "directory gid"), mode: integer(directory.mode, "directory mode"), path: safeRelativePath(directory.path), uid: integer(directory.uid, "directory uid") };
|
|
58
|
+
}).sort((left, right) => left.path.localeCompare(right.path));
|
|
59
|
+
const allPaths = [...directories.map((entry) => entry.path), ...files.map((entry) => entry.path)];
|
|
60
|
+
if (new Set(allPaths).size !== allPaths.length || allPaths.some((entry) => entry === METADATA_MANIFEST || entry === ACTIVATION_PROVENANCE || entry === RESOURCE_IDENTITY))
|
|
61
|
+
throw new Error("Workspace resource manifest contains duplicate or reserved paths");
|
|
62
|
+
return { version: WORKSPACE_RESOURCE_MANIFEST_VERSION, root: { gid: integer(rootMetadata.gid, "root gid"), mode: integer(rootMetadata.mode, "root mode"), uid: integer(rootMetadata.uid, "root uid") }, directories, files };
|
|
63
|
+
};
|
|
64
|
+
const inventory = async (root, relative = "") => {
|
|
65
|
+
const directories = [];
|
|
66
|
+
const files = [];
|
|
67
|
+
for (const entry of await readdir(path.join(root, relative), { withFileTypes: true })) {
|
|
68
|
+
const child = relative ? path.posix.join(relative, entry.name) : entry.name;
|
|
69
|
+
if (entry.isSymbolicLink())
|
|
70
|
+
throw new Error(`Workspace resource contains a symlink: ${child}`);
|
|
71
|
+
if (entry.isDirectory()) {
|
|
72
|
+
directories.push(child);
|
|
73
|
+
const nested = await inventory(root, child);
|
|
74
|
+
directories.push(...nested.directories);
|
|
75
|
+
files.push(...nested.files);
|
|
76
|
+
}
|
|
77
|
+
else if (entry.isFile())
|
|
78
|
+
files.push(child);
|
|
79
|
+
else
|
|
80
|
+
throw new Error(`Workspace resource contains an unsupported entry: ${child}`);
|
|
81
|
+
}
|
|
82
|
+
return { directories: directories.sort(), files: files.sort() };
|
|
83
|
+
};
|
|
84
|
+
const verifyTree = async (root, manifest, metadataDigest, internal) => {
|
|
85
|
+
const rootEntry = await lstat(root);
|
|
86
|
+
if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink() || rootEntry.uid !== manifest.root.uid || rootEntry.gid !== manifest.root.gid || (rootEntry.mode & 0o7777) !== manifest.root.mode)
|
|
87
|
+
throw new Error("Workspace resource root metadata mismatch");
|
|
88
|
+
const actual = await inventory(root);
|
|
89
|
+
const internalFiles = new Set([METADATA_MANIFEST, ACTIVATION_PROVENANCE, RESOURCE_IDENTITY]);
|
|
90
|
+
const actualFiles = actual.files.filter((entry) => !internalFiles.has(entry));
|
|
91
|
+
if (actualFiles.join("\0") !== manifest.files.map((file) => file.path).join("\0") || actual.directories.join("\0") !== manifest.directories.map((directory) => directory.path).join("\0"))
|
|
92
|
+
throw new Error("Workspace resource inventory does not match its manifest");
|
|
93
|
+
for (const expected of manifest.directories) {
|
|
94
|
+
const entry = await lstat(path.join(root, expected.path));
|
|
95
|
+
if (!entry.isDirectory() || entry.isSymbolicLink() || entry.uid !== expected.uid || entry.gid !== expected.gid || (entry.mode & 0o7777) !== expected.mode)
|
|
96
|
+
throw new Error(`Workspace resource directory metadata mismatch: ${expected.path}`);
|
|
97
|
+
}
|
|
98
|
+
for (const expected of manifest.files) {
|
|
99
|
+
const entry = await stat(path.join(root, expected.path));
|
|
100
|
+
if (!entry.isFile() || entry.size !== expected.size || entry.uid !== expected.uid || entry.gid !== expected.gid || (entry.mode & 0o7777) !== expected.mode || await fileDigest(path.join(root, expected.path)) !== expected.sha256)
|
|
101
|
+
throw new Error(`Workspace resource checksum or metadata mismatch: ${expected.path}`);
|
|
102
|
+
}
|
|
103
|
+
if (metadataDigest !== undefined && await fileDigest(path.join(root, METADATA_MANIFEST)) !== metadataDigest)
|
|
104
|
+
throw new Error("Workspace resource metadata manifest checksum mismatch");
|
|
105
|
+
if (internal && ((await readFile(path.join(root, ACTIVATION_PROVENANCE), "utf8")) !== `${internal.activationToken}\n` || (await readFile(path.join(root, RESOURCE_IDENTITY), "utf8")) !== `${internal.resolvedIdentity}\n`))
|
|
106
|
+
throw new Error("Workspace resource authenticated migration identity mismatch");
|
|
107
|
+
};
|
|
108
|
+
const canonicalDestination = async (destinationPath) => {
|
|
109
|
+
if (!path.isAbsolute(destinationPath))
|
|
110
|
+
throw new Error("Workspace resource destination path must be absolute");
|
|
111
|
+
const resolved = path.resolve(destinationPath);
|
|
112
|
+
const parent = await realpath(path.dirname(resolved));
|
|
113
|
+
const destination = path.join(parent, path.basename(resolved));
|
|
114
|
+
return { destination, parent };
|
|
115
|
+
};
|
|
116
|
+
const directoryIdentity = (entry) => ({ dev: Number(entry.dev), ino: Number(entry.ino) });
|
|
117
|
+
const ownedDestinationIdentity = async (destination, token, expected) => {
|
|
118
|
+
try {
|
|
119
|
+
const entry = await lstat(destination);
|
|
120
|
+
const marker = path.join(destination, ACTIVATION_PROVENANCE);
|
|
121
|
+
const markerEntry = await lstat(marker);
|
|
122
|
+
if (!entry.isDirectory() || entry.isSymbolicLink() || !markerEntry.isFile() || markerEntry.isSymbolicLink() || (await readFile(marker, "utf8")) !== `${token}\n`)
|
|
123
|
+
return undefined;
|
|
124
|
+
const actual = directoryIdentity(entry);
|
|
125
|
+
return expected && (expected.dev !== actual.dev || expected.ino !== actual.ino) ? undefined : actual;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (error.code === "ENOENT")
|
|
129
|
+
return undefined;
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const writeJournal = async (journalPath, journal) => {
|
|
134
|
+
const bytes = `${JSON.stringify(journal)}\n`;
|
|
135
|
+
if (Buffer.byteLength(bytes) > MAX_JOURNAL_BYTES)
|
|
136
|
+
throw new Error("Workspace resource migration journal is too large");
|
|
137
|
+
const temporary = `${journalPath}.write-${randomUUID()}`;
|
|
138
|
+
const handle = await open(temporary, "wx", 0o600);
|
|
139
|
+
try {
|
|
140
|
+
await handle.writeFile(bytes);
|
|
141
|
+
await handle.sync();
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
await handle.close();
|
|
145
|
+
}
|
|
146
|
+
await rename(temporary, journalPath);
|
|
147
|
+
const parentHandle = await open(path.dirname(journalPath), "r");
|
|
148
|
+
try {
|
|
149
|
+
await parentHandle.sync();
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
await parentHandle.close();
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const syncDirectory = async (directory) => { const handle = await open(directory, "r"); try {
|
|
156
|
+
await handle.sync();
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
await handle.close();
|
|
160
|
+
} };
|
|
161
|
+
const readJournal = async (journalPath) => {
|
|
162
|
+
try {
|
|
163
|
+
const entry = await lstat(journalPath);
|
|
164
|
+
if (!entry.isFile() || entry.isSymbolicLink() || entry.size < 1 || entry.size > MAX_JOURNAL_BYTES)
|
|
165
|
+
throw new Error("Workspace resource migration journal is invalid");
|
|
166
|
+
const value = JSON.parse(await readFile(journalPath, "utf8"));
|
|
167
|
+
const keys = ["version", "status", "migration_id", "parent", "source_basename", "destination_basename", "source_path", "destination_path", "temporary_path", "temporary_dev", "temporary_ino", "activation_token", "manifest_sha256", "resolved_identity"];
|
|
168
|
+
if (!value || !exact(value, keys) || value.version !== "spawnfile.workspace-resource-migration-journal.v1" || !["prepared", "activated"].includes(String(value.status)) || typeof value.migration_id !== "string" || typeof value.parent !== "string" || typeof value.source_basename !== "string" || typeof value.destination_basename !== "string" || typeof value.source_path !== "string" || typeof value.destination_path !== "string" || typeof value.temporary_path !== "string" || !Number.isSafeInteger(value.temporary_dev) || !Number.isSafeInteger(value.temporary_ino) || typeof value.activation_token !== "string" || typeof value.manifest_sha256 !== "string" || typeof value.resolved_identity !== "string")
|
|
169
|
+
throw new Error("Workspace resource migration journal is invalid");
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
if (error.code === "ENOENT")
|
|
174
|
+
return undefined;
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
export const migrateWorkspaceResource = async (options) => {
|
|
179
|
+
if (!path.isAbsolute(options.sourcePath) || !path.isAbsolute(options.manifestPath))
|
|
180
|
+
throw new Error("Workspace resource source and manifest paths must be absolute");
|
|
181
|
+
if (!/^sha256:[a-f0-9]{64}$/u.test(options.resolvedIdentity))
|
|
182
|
+
throw new Error("Workspace resource resolved identity is invalid");
|
|
183
|
+
if (options.sourceQuiesced !== true && options.hooks?.withSourceWriteFence === undefined)
|
|
184
|
+
throw new Error("Workspace resource migration requires explicit source quiescence or a write-fence authority");
|
|
185
|
+
const sourceEntry = await lstat(options.sourcePath);
|
|
186
|
+
const source = await realpath(options.sourcePath);
|
|
187
|
+
if (!sourceEntry.isDirectory() || sourceEntry.isSymbolicLink())
|
|
188
|
+
throw new Error("Workspace resource source must be one canonical directory");
|
|
189
|
+
const { destination, parent } = await canonicalDestination(options.destinationPath);
|
|
190
|
+
if (destination === source || destination.startsWith(`${source}${path.sep}`) || source.startsWith(`${destination}${path.sep}`))
|
|
191
|
+
throw new Error("Workspace resource source and destination must not overlap");
|
|
192
|
+
const manifestEntry = await lstat(options.manifestPath);
|
|
193
|
+
if (!manifestEntry.isFile() || manifestEntry.isSymbolicLink() || manifestEntry.size < 1 || manifestEntry.size > MAX_MANIFEST_BYTES)
|
|
194
|
+
throw new Error("Workspace resource manifest must be a bounded regular file");
|
|
195
|
+
const manifestBytes = await readFile(options.manifestPath);
|
|
196
|
+
const manifest = parseManifest(JSON.parse(manifestBytes.toString("utf8")));
|
|
197
|
+
if (sourceEntry.uid !== manifest.root.uid || sourceEntry.gid !== manifest.root.gid || (sourceEntry.mode & 0o7777) !== manifest.root.mode)
|
|
198
|
+
throw new Error("Workspace resource source owner or mode does not match its manifest");
|
|
199
|
+
await verifyTree(source, manifest);
|
|
200
|
+
const requiredBytes = BigInt(manifest.files.reduce((total, file) => total + file.size, manifestBytes.length));
|
|
201
|
+
const freeBytes = options.hooks?.freeBytes ?? (async (target) => { const value = await statfs(target, { bigint: true }); return value.bavail * value.bsize; });
|
|
202
|
+
if (await freeBytes(parent) < requiredBytes)
|
|
203
|
+
throw new Error("Workspace resource destination has insufficient free space");
|
|
204
|
+
if (options.hooks?.activate === undefined && process.platform === "linux") {
|
|
205
|
+
const filesystem = await statfs(parent, { bigint: true });
|
|
206
|
+
const localTypes = new Set([0xef53n, 0x58465342n, 0x9123683en, 0x01021994n, 0x794c7630n]);
|
|
207
|
+
if (!localTypes.has(filesystem.type))
|
|
208
|
+
throw new Error("Workspace resource migration requires unambiguous local filesystem authority");
|
|
209
|
+
}
|
|
210
|
+
const copy = options.hooks?.copy ?? (async (from, to) => await cp(from, to, { errorOnExist: true, force: false, preserveTimestamps: true, recursive: true }));
|
|
211
|
+
const activate = options.hooks?.activate ?? activateNoReplace;
|
|
212
|
+
const withSourceWriteFence = options.hooks?.withSourceWriteFence ?? (async (operation) => await operation());
|
|
213
|
+
const manifestSha256 = digestBytes(manifestBytes);
|
|
214
|
+
const journalPath = `${destination}.migration-journal.json`;
|
|
215
|
+
const recovered = await readJournal(journalPath);
|
|
216
|
+
if (recovered && (recovered.parent !== parent || recovered.source_basename !== path.basename(source) || recovered.destination_basename !== path.basename(destination) || recovered.source_path !== source || recovered.destination_path !== destination || path.dirname(recovered.temporary_path) !== parent || !path.basename(recovered.temporary_path).startsWith(`${path.basename(destination)}.migration-`) || recovered.manifest_sha256 !== manifestSha256 || recovered.resolved_identity !== options.resolvedIdentity))
|
|
217
|
+
throw new Error("Workspace resource migration journal does not match this migration");
|
|
218
|
+
let temporary = recovered?.temporary_path ?? `${destination}.migration-${randomUUID()}`;
|
|
219
|
+
let activationToken = recovered?.activation_token ?? randomUUID();
|
|
220
|
+
let migrationId = recovered?.migration_id ?? randomUUID();
|
|
221
|
+
let activatedIdentity = recovered ? { dev: recovered.temporary_dev, ino: recovered.temporary_ino } : undefined;
|
|
222
|
+
let resume = false;
|
|
223
|
+
if (recovered) {
|
|
224
|
+
const destinationOwned = await ownedDestinationIdentity(destination, activationToken, activatedIdentity);
|
|
225
|
+
const destinationExact = await (async () => { try {
|
|
226
|
+
const entry = await lstat(destination);
|
|
227
|
+
const actual = directoryIdentity(entry);
|
|
228
|
+
return actual.dev === activatedIdentity?.dev && actual.ino === activatedIdentity?.ino;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
if (error.code === "ENOENT")
|
|
232
|
+
return false;
|
|
233
|
+
throw error;
|
|
234
|
+
} })();
|
|
235
|
+
if (destinationOwned || (recovered.status === "activated" && destinationExact && await readFile(path.join(destination, RESOURCE_IDENTITY), "utf8") === `${options.resolvedIdentity}\n`)) {
|
|
236
|
+
await verifyTree(destination, manifest, manifestSha256, destinationOwned ? { activationToken, resolvedIdentity: options.resolvedIdentity } : undefined);
|
|
237
|
+
await writeJournal(journalPath, { ...recovered, status: "activated" });
|
|
238
|
+
await rm(path.join(destination, ACTIVATION_PROVENANCE), { force: true });
|
|
239
|
+
return { version: WORKSPACE_RESOURCE_MIGRATION_VERSION, active_path: destination, destination_path: destination, manifest_sha256: manifestSha256, rollback: false, source_path: source, source_retained: true, status: "activated" };
|
|
240
|
+
}
|
|
241
|
+
if (await ownedDestinationIdentity(temporary, activationToken, activatedIdentity)) {
|
|
242
|
+
await verifyTree(temporary, manifest, manifestSha256, { activationToken, resolvedIdentity: options.resolvedIdentity });
|
|
243
|
+
resume = true;
|
|
244
|
+
}
|
|
245
|
+
else
|
|
246
|
+
throw new Error("Workspace resource migration journal has no recoverable owned state");
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
try {
|
|
250
|
+
await lstat(destination);
|
|
251
|
+
throw new Error("Workspace resource destination already exists");
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
if (error.code !== "ENOENT")
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
if (!resume) {
|
|
260
|
+
await copy(source, temporary);
|
|
261
|
+
await mkdir(temporary, { recursive: true });
|
|
262
|
+
await writeFile(path.join(temporary, METADATA_MANIFEST), manifestBytes, { flag: "wx", mode: 0o600 });
|
|
263
|
+
await writeFile(path.join(temporary, ACTIVATION_PROVENANCE), `${activationToken}\n`, { flag: "wx", mode: 0o600 });
|
|
264
|
+
await writeFile(path.join(temporary, RESOURCE_IDENTITY), `${options.resolvedIdentity}\n`, { flag: "wx", mode: 0o600 });
|
|
265
|
+
await options.hooks?.afterCopy?.(temporary);
|
|
266
|
+
await verifyTree(temporary, manifest, manifestSha256, { activationToken, resolvedIdentity: options.resolvedIdentity });
|
|
267
|
+
activatedIdentity = directoryIdentity(await lstat(temporary));
|
|
268
|
+
}
|
|
269
|
+
if (!activatedIdentity)
|
|
270
|
+
throw new Error("Workspace resource temporary identity is unavailable");
|
|
271
|
+
const journal = { version: "spawnfile.workspace-resource-migration-journal.v1", status: "prepared", migration_id: migrationId, parent, source_basename: path.basename(source), destination_basename: path.basename(destination), source_path: source, destination_path: destination, temporary_path: temporary, temporary_dev: activatedIdentity.dev, temporary_ino: activatedIdentity.ino, activation_token: activationToken, manifest_sha256: manifestSha256, resolved_identity: options.resolvedIdentity };
|
|
272
|
+
await writeJournal(journalPath, journal);
|
|
273
|
+
await withSourceWriteFence(async () => {
|
|
274
|
+
const currentSource = await lstat(source);
|
|
275
|
+
if (currentSource.dev !== sourceEntry.dev || currentSource.ino !== sourceEntry.ino)
|
|
276
|
+
throw new Error("Workspace resource source identity changed before activation");
|
|
277
|
+
await verifyTree(source, manifest);
|
|
278
|
+
await options.hooks?.beforeActivation?.(destination);
|
|
279
|
+
await activate(temporary, destination);
|
|
280
|
+
await syncDirectory(parent);
|
|
281
|
+
});
|
|
282
|
+
if (await ownedDestinationIdentity(destination, activationToken, activatedIdentity) === undefined)
|
|
283
|
+
throw new Error("Workspace resource activation provenance is unavailable");
|
|
284
|
+
await verifyTree(destination, manifest, manifestSha256, { activationToken, resolvedIdentity: options.resolvedIdentity });
|
|
285
|
+
await writeJournal(journalPath, { ...journal, status: "activated" });
|
|
286
|
+
await options.hooks?.afterActivation?.(destination);
|
|
287
|
+
await rm(path.join(destination, ACTIVATION_PROVENANCE));
|
|
288
|
+
return { version: WORKSPACE_RESOURCE_MIGRATION_VERSION, active_path: destination, destination_path: destination, manifest_sha256: manifestSha256, rollback: false, source_path: source, source_retained: true, status: "activated" };
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
const owned = await ownedDestinationIdentity(destination, activationToken, activatedIdentity);
|
|
292
|
+
if (owned === undefined) {
|
|
293
|
+
await rm(temporary, { force: true, recursive: true });
|
|
294
|
+
await rm(journalPath, { force: true });
|
|
295
|
+
throw error;
|
|
296
|
+
}
|
|
297
|
+
await rm(destination, { force: true, recursive: true });
|
|
298
|
+
await rm(journalPath, { force: true });
|
|
299
|
+
return { version: WORKSPACE_RESOURCE_MIGRATION_VERSION, active_path: source, destination_path: destination, manifest_sha256: manifestSha256, rollback: true, source_path: source, source_retained: true, status: "rolled_back" };
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
export const buildWorkspaceResourceManifest = async (sourcePath) => {
|
|
303
|
+
const source = await realpath(sourcePath);
|
|
304
|
+
const root = await stat(source);
|
|
305
|
+
const entries = await inventory(source);
|
|
306
|
+
const directories = await Promise.all(entries.directories.map(async (relative) => {
|
|
307
|
+
const entry = await lstat(path.join(source, relative));
|
|
308
|
+
return { gid: entry.gid, mode: entry.mode & 0o7777, path: relative, uid: entry.uid };
|
|
309
|
+
}));
|
|
310
|
+
const files = await Promise.all(entries.files.map(async (relative) => {
|
|
311
|
+
const entry = await stat(path.join(source, relative));
|
|
312
|
+
return { gid: entry.gid, mode: entry.mode & 0o7777, path: relative, sha256: await fileDigest(path.join(source, relative)), size: entry.size, uid: entry.uid };
|
|
313
|
+
}));
|
|
314
|
+
return { version: WORKSPACE_RESOURCE_MANIFEST_VERSION, root: { gid: root.gid, mode: root.mode & 0o7777, uid: root.uid }, directories, files };
|
|
315
|
+
};
|
package/dist/dev/project.d.ts
CHANGED
|
@@ -26,6 +26,13 @@ export interface DevStopProjectResult {
|
|
|
26
26
|
outputDirectory: string;
|
|
27
27
|
}
|
|
28
28
|
export declare const resolveDevOutputDirectory: (inputPath: string, outputDirectory?: string) => Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* A dev deployment gets its own lineage namespace so it can never resolve to
|
|
31
|
+
* the derived volumes of a production `spawnfile up` of the same project —
|
|
32
|
+
* before this, both defaulted to the lineage `default` and dev wrote into
|
|
33
|
+
* production's state whenever production was stopped. The deployment NAME is
|
|
34
|
+
* untouched, so dev records, labels, and `dev stop --deployment` are unchanged.
|
|
35
|
+
*/
|
|
29
36
|
export declare const devUpProject: (inputPath: string, options?: UpProjectOptions) => Promise<UpProjectResult>;
|
|
30
37
|
export declare const selectDeploymentRecord: (outputDirectory: string, deploymentName?: string) => Promise<DeploymentRecord>;
|
|
31
38
|
export declare const devApplyProject: (inputPath: string, options: DevApplyProjectOptions) => Promise<DevApplyProjectResult>;
|
package/dist/dev/project.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readFile } from "node:fs/promises";
|
|
|
3
3
|
import { buildCompilePlan, upProject } from "../compiler/index.js";
|
|
4
4
|
import { listDeploymentRecords, normalizeDeploymentName } from "../deployment/index.js";
|
|
5
5
|
import { SpawnfileError } from "../shared/index.js";
|
|
6
|
+
import { DEV_DEPLOYMENT_LINEAGE_NAMESPACE } from "../compiler/deploymentLineage.js";
|
|
6
7
|
import { chownContainerPaths, compileForDevApply, copyIntoContainer, defaultExecFile, dockerArgsForRecord, ensureContainerDirectories, firstContainerName, runDocker } from "./docker.js";
|
|
7
8
|
const DEV_OUTPUT_DIRECTORY = ".spawn-dev";
|
|
8
9
|
export const PI_CONTROL_URL = "http://127.0.0.1:19690";
|
|
@@ -13,8 +14,16 @@ export const resolveDevOutputDirectory = async (inputPath, outputDirectory) => {
|
|
|
13
14
|
const plan = await buildCompilePlan(inputPath);
|
|
14
15
|
return path.join(path.dirname(plan.root), DEV_OUTPUT_DIRECTORY);
|
|
15
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* A dev deployment gets its own lineage namespace so it can never resolve to
|
|
19
|
+
* the derived volumes of a production `spawnfile up` of the same project —
|
|
20
|
+
* before this, both defaulted to the lineage `default` and dev wrote into
|
|
21
|
+
* production's state whenever production was stopped. The deployment NAME is
|
|
22
|
+
* untouched, so dev records, labels, and `dev stop --deployment` are unchanged.
|
|
23
|
+
*/
|
|
16
24
|
export const devUpProject = async (inputPath, options = {}) => upProject(inputPath, {
|
|
17
25
|
...options,
|
|
26
|
+
deploymentLineageNamespace: DEV_DEPLOYMENT_LINEAGE_NAMESPACE,
|
|
18
27
|
detach: true,
|
|
19
28
|
outputDirectory: await resolveDevOutputDirectory(inputPath, options.outputDirectory)
|
|
20
29
|
});
|
|
@@ -4,6 +4,8 @@ import type { ResolvedAuthProfile } from "../auth/index.js";
|
|
|
4
4
|
export interface ConsumeImageUpOptions {
|
|
5
5
|
authProfile?: ResolvedAuthProfile | null;
|
|
6
6
|
authValues?: Record<string, string>;
|
|
7
|
+
/** See `ImageRuntimeAuthInput.daimonContainerCredentialUid`. */
|
|
8
|
+
daimonContainerCredentialUid?: number;
|
|
7
9
|
authProfileName?: string | null;
|
|
8
10
|
deploymentName?: string;
|
|
9
11
|
dockerCommand?: string;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import os from "node:os";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
4
|
-
import { acquireHomeDeploymentLock, createDockerDeploymentLabels, homeDeploymentExists, normalizeDeploymentName, readHomeDeploymentRecord, resolveDockerDeploymentTarget, verifyDockerDeploymentTarget, writeHomeDeployment } from "../deployment/index.js";
|
|
4
|
+
import { acquireHomeDeploymentLock, captureCandidateDiagnostics, createDockerDeploymentLabels, homeDeploymentExists, normalizeDeploymentName, readHomeDeploymentRecord, resolveDockerDeploymentTarget, sanitizeCandidateDiagnostic, verifyDockerDeploymentTarget, writeHomeDeployment } from "../deployment/index.js";
|
|
5
5
|
import { SpawnfileError } from "../shared/index.js";
|
|
6
|
-
import { deriveDeploymentName,
|
|
6
|
+
import { deriveDeploymentName, derivePersistentMountVolumeName, renderEnvFileContent, resolveImageEnvironment } from "./consumeImageSupport.js";
|
|
7
|
+
import { acquireExclusiveVolumeReservations, assertCandidateContainerReady, assertContainerStopped, inspectContainerSnapshot, restorePreviousContainer, rollbackCandidateContainer } from "./consumeImageLifecycle.js";
|
|
7
8
|
import { createConsumerDockerRunner } from "./dockerRunner.js";
|
|
9
|
+
import { resolveDaimonDockerSecurityArgsForImage } from "./consumeImageDaimonDocker.js";
|
|
8
10
|
import { extractImageReport, resolveDockerBaseArgs } from "./extractImage.js";
|
|
9
11
|
import { parseImageReference } from "./imageRef.js";
|
|
10
12
|
import { prepareImageRuntimeAuthMounts } from "./imageRuntimeAuth.js";
|
|
@@ -35,15 +37,6 @@ const resolveRegistryDigest = async (imageRef, runDocker) => {
|
|
|
35
37
|
return null;
|
|
36
38
|
}
|
|
37
39
|
};
|
|
38
|
-
const containerExists = async (runDocker, containerName) => {
|
|
39
|
-
try {
|
|
40
|
-
await runDocker(["container", "inspect", containerName]);
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
40
|
const resolveLocalImageId = async (imageRef, runDocker) => {
|
|
48
41
|
try {
|
|
49
42
|
const raw = (await runDocker([
|
|
@@ -131,6 +124,7 @@ const consumeImageUpLocked = async (imageRef, parsedRef, deploymentName, isExpli
|
|
|
131
124
|
// port for two containers at once. There is a brief restart gap, unavoidable
|
|
132
125
|
// when reusing host ports without a proxy.
|
|
133
126
|
const backupName = `${containerName}-previous-${Date.now().toString(36)}`;
|
|
127
|
+
let volumeReservation;
|
|
134
128
|
try {
|
|
135
129
|
await writeFile(envFilePath, renderEnvFileContent(env), "utf8");
|
|
136
130
|
// Prepare import-based model auth (the consumer's Claude/Codex login) and
|
|
@@ -138,24 +132,41 @@ const consumeImageUpLocked = async (imageRef, parsedRef, deploymentName, isExpli
|
|
|
138
132
|
// config is already baked into the image; this only resolves the credential
|
|
139
133
|
// mounts. Doing it first guarantees an unusable import fails while the live
|
|
140
134
|
// container is still untouched, never mid-swap.
|
|
141
|
-
const authMountArgs =
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
:
|
|
148
|
-
|
|
149
|
-
|
|
135
|
+
const authMountArgs = (await prepareImageRuntimeAuthMounts({
|
|
136
|
+
authProfile: options.authProfile ?? null,
|
|
137
|
+
...(options.daimonContainerCredentialUid === undefined
|
|
138
|
+
? {}
|
|
139
|
+
: { daimonContainerCredentialUid: options.daimonContainerCredentialUid }),
|
|
140
|
+
report,
|
|
141
|
+
tempRoot: workDir
|
|
142
|
+
})).mountArgs;
|
|
143
|
+
const daimonDockerSecurityArgs = await resolveDaimonDockerSecurityArgsForImage(imageRef, report, runDocker, workDir);
|
|
144
|
+
volumeReservation = await acquireExclusiveVolumeReservations(report, deploymentName, containerName, imageRef, runDocker);
|
|
145
|
+
const previousContainer = await inspectContainerSnapshot(runDocker, containerName);
|
|
146
|
+
if (previousContainer !== null) {
|
|
150
147
|
await runDocker(["rename", containerName, backupName]);
|
|
151
|
-
|
|
148
|
+
try {
|
|
149
|
+
await runDocker(["stop", backupName]);
|
|
150
|
+
await assertContainerStopped(runDocker, backupName, previousContainer.id);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
try {
|
|
154
|
+
await restorePreviousContainer(runDocker, previousContainer, backupName, containerName);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
throw new SpawnfileError("runtime_error", "Prior deployment stop failed and its original state could not be restored");
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
152
161
|
}
|
|
153
|
-
const runArgs = [
|
|
162
|
+
const runArgs = [
|
|
163
|
+
"run", "-d", "--name", containerName, ...daimonDockerSecurityArgs, "--env-file", envFilePath
|
|
164
|
+
];
|
|
154
165
|
for (const port of report.ports) {
|
|
155
166
|
runArgs.push("-p", `${port}:${port}`);
|
|
156
167
|
}
|
|
157
168
|
for (const mount of report.persistent_mounts) {
|
|
158
|
-
runArgs.push("-v", `${
|
|
169
|
+
runArgs.push("-v", `${derivePersistentMountVolumeName(deploymentName, mount)}:${mount.target}`);
|
|
159
170
|
}
|
|
160
171
|
runArgs.push(...authMountArgs);
|
|
161
172
|
const labels = createDockerDeploymentLabels({
|
|
@@ -169,63 +180,77 @@ const consumeImageUpLocked = async (imageRef, parsedRef, deploymentName, isExpli
|
|
|
169
180
|
runArgs.push("--label", `${name}=${value}`);
|
|
170
181
|
}
|
|
171
182
|
runArgs.push(imageRef);
|
|
172
|
-
let
|
|
183
|
+
let candidateId;
|
|
173
184
|
try {
|
|
174
|
-
runOutput = (await runDocker(runArgs)).toString("utf8").trim();
|
|
185
|
+
const runOutput = (await runDocker(runArgs)).toString("utf8").trim();
|
|
186
|
+
const observedId = runOutput.split("\n").pop()?.trim();
|
|
187
|
+
if (!observedId || !/^[a-f0-9]{64}$/u.test(observedId)) {
|
|
188
|
+
throw new SpawnfileError("runtime_error", "Candidate container returned invalid identity");
|
|
189
|
+
}
|
|
190
|
+
candidateId = observedId;
|
|
191
|
+
await assertCandidateContainerReady(runDocker, candidateId, containerName);
|
|
192
|
+
const imageId = await resolveLocalImageId(imageRef, runDocker);
|
|
193
|
+
const digest = await resolveRegistryDigest(imageRef, runDocker);
|
|
194
|
+
const record = {
|
|
195
|
+
auth_profile: options.authProfileName ?? null,
|
|
196
|
+
compile_fingerprint: inspection.compileFingerprint,
|
|
197
|
+
created_at: new Date().toISOString(),
|
|
198
|
+
...(options.envFilePath ? { env_file: path.resolve(options.envFilePath) } : {}),
|
|
199
|
+
manager: "docker",
|
|
200
|
+
name: deploymentName,
|
|
201
|
+
output_directory: null,
|
|
202
|
+
source: { digest, kind: "image", ref: imageRef },
|
|
203
|
+
target,
|
|
204
|
+
units: [
|
|
205
|
+
{
|
|
206
|
+
container_id: candidateId,
|
|
207
|
+
container_name: containerName,
|
|
208
|
+
contains: buildContainsEntries(report),
|
|
209
|
+
id: unitIdFor(deploymentName),
|
|
210
|
+
image_id: imageId,
|
|
211
|
+
image_tag: imageRef,
|
|
212
|
+
kind: "container",
|
|
213
|
+
runtime_instances: report.runtime_instances.map((instance) => instance.id).sort()
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
version: "spawnfile.deployment.v2"
|
|
217
|
+
};
|
|
218
|
+
const written = await writeHomeDeployment(record, report);
|
|
219
|
+
if (previousContainer !== null) {
|
|
220
|
+
await runDocker(["rm", "-f", previousContainer.id]).catch(() => undefined);
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
containerName,
|
|
224
|
+
deploymentName,
|
|
225
|
+
imageRef,
|
|
226
|
+
previous,
|
|
227
|
+
record,
|
|
228
|
+
recordPath: written.recordPath
|
|
229
|
+
};
|
|
175
230
|
}
|
|
176
231
|
catch (error) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
232
|
+
const diagnostics = candidateId ? await captureCandidateDiagnostics({
|
|
233
|
+
candidateId, deploymentName, runDocker, secretValues: Object.values(env)
|
|
234
|
+
}) : null;
|
|
235
|
+
const detail = diagnostics ? ` (${diagnostics.summary}); ${diagnostics.path
|
|
236
|
+
? `private diagnostics: ${diagnostics.path}` : "private diagnostics unavailable"}` : "";
|
|
237
|
+
const failure = (cause) => new SpawnfileError("runtime_error", sanitizeCandidateDiagnostic(cause instanceof Error ? cause.message : String(cause), Object.values(env)) + detail);
|
|
238
|
+
try {
|
|
239
|
+
await rollbackCandidateContainer(runDocker, candidateId, containerName, previousContainer, backupName);
|
|
183
240
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
await runDocker(["rm", "-f", backupName]).catch(() => undefined);
|
|
241
|
+
catch (rollbackError) {
|
|
242
|
+
throw failure(rollbackError);
|
|
243
|
+
}
|
|
244
|
+
throw failure(error);
|
|
189
245
|
}
|
|
190
|
-
const containerId = runOutput.split("\n").pop()?.trim() || null;
|
|
191
|
-
const imageId = await resolveLocalImageId(imageRef, runDocker);
|
|
192
|
-
const digest = await resolveRegistryDigest(imageRef, runDocker);
|
|
193
|
-
const record = {
|
|
194
|
-
auth_profile: options.authProfileName ?? null,
|
|
195
|
-
compile_fingerprint: inspection.compileFingerprint,
|
|
196
|
-
created_at: new Date().toISOString(),
|
|
197
|
-
...(options.envFilePath ? { env_file: path.resolve(options.envFilePath) } : {}),
|
|
198
|
-
manager: "docker",
|
|
199
|
-
name: deploymentName,
|
|
200
|
-
output_directory: null,
|
|
201
|
-
source: { digest, kind: "image", ref: imageRef },
|
|
202
|
-
target,
|
|
203
|
-
units: [
|
|
204
|
-
{
|
|
205
|
-
container_id: containerId,
|
|
206
|
-
container_name: containerName,
|
|
207
|
-
contains: buildContainsEntries(report),
|
|
208
|
-
id: unitIdFor(deploymentName),
|
|
209
|
-
image_id: imageId,
|
|
210
|
-
image_tag: imageRef,
|
|
211
|
-
kind: "container",
|
|
212
|
-
runtime_instances: report.runtime_instances.map((instance) => instance.id).sort()
|
|
213
|
-
}
|
|
214
|
-
],
|
|
215
|
-
version: "spawnfile.deployment.v2"
|
|
216
|
-
};
|
|
217
|
-
const written = await writeHomeDeployment(record, report);
|
|
218
|
-
return {
|
|
219
|
-
containerName,
|
|
220
|
-
deploymentName,
|
|
221
|
-
imageRef,
|
|
222
|
-
previous,
|
|
223
|
-
record,
|
|
224
|
-
recordPath: written.recordPath
|
|
225
|
-
};
|
|
226
246
|
}
|
|
227
247
|
finally {
|
|
228
|
-
|
|
248
|
+
try {
|
|
249
|
+
await volumeReservation?.release();
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
await rm(workDir, { force: true, recursive: true }).catch(() => undefined);
|
|
253
|
+
}
|
|
229
254
|
}
|
|
230
255
|
};
|
|
231
256
|
const readPreviousSource = async (deploymentName) => {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { DockerCommandRunner } from "./dockerRunner.js";
|
|
2
|
+
import type { DistributionReport } from "./types.js";
|
|
3
|
+
export declare const resolveDaimonDockerSecurityArgsForImage: (imageRef: string, report: DistributionReport, runDocker: DockerCommandRunner, securityDirectory: string) => Promise<string[]>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { SpawnfileError } from "../shared/errors.js";
|
|
3
|
+
import { DAIMON_DOCKER_RUNTIME_SECURITY_ARGS, daimonEngineDockerSecurityArgsForConfigs, materializeDaimonGrokSeccompProfile } from "../shared/daimonCodexDocker.js";
|
|
4
|
+
import { extractSingleFileFromTar } from "./tarReader.js";
|
|
5
|
+
const DAIMON_CONFIG_SIZE_CAP_BYTES = 4 * 1024 * 1024;
|
|
6
|
+
const daimonInstancesFrom = (report) => report.runtime_instances.filter((instance) => instance.runtime === "daimon");
|
|
7
|
+
const assertAbsoluteConfigPath = (instance) => {
|
|
8
|
+
if (!instance.config_path.startsWith("/")) {
|
|
9
|
+
throw new SpawnfileError("validation_error", "Daimon runtime instance has a non-absolute generated config path");
|
|
10
|
+
}
|
|
11
|
+
return instance.config_path;
|
|
12
|
+
};
|
|
13
|
+
const readDaimonConfigSourcesFromImage = async (imageRef, instances, runDocker) => {
|
|
14
|
+
const helperName = `spawnfile-daimon-config-${randomUUID()}`;
|
|
15
|
+
await runDocker(["create", "--name", helperName, imageRef]);
|
|
16
|
+
try {
|
|
17
|
+
const sources = [];
|
|
18
|
+
for (const instance of instances) {
|
|
19
|
+
let tar;
|
|
20
|
+
try {
|
|
21
|
+
tar = await runDocker(["cp", `${helperName}:${assertAbsoluteConfigPath(instance)}`, "-"]);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (error instanceof SpawnfileError)
|
|
25
|
+
throw error;
|
|
26
|
+
throw new SpawnfileError("validation_error", "Could not read Daimon's generated organization config before Docker launch");
|
|
27
|
+
}
|
|
28
|
+
sources.push(extractSingleFileFromTar(tar, { maxBytes: DAIMON_CONFIG_SIZE_CAP_BYTES }).toString("utf8"));
|
|
29
|
+
}
|
|
30
|
+
return sources;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
await runDocker(["rm", "-f", helperName]).catch(() => undefined);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
export const resolveDaimonDockerSecurityArgsForImage = async (imageRef, report, runDocker, securityDirectory) => {
|
|
37
|
+
const daimonInstances = daimonInstancesFrom(report);
|
|
38
|
+
if (daimonInstances.length === 0)
|
|
39
|
+
return [];
|
|
40
|
+
const sources = await readDaimonConfigSourcesFromImage(imageRef, daimonInstances, runDocker);
|
|
41
|
+
return [
|
|
42
|
+
...DAIMON_DOCKER_RUNTIME_SECURITY_ARGS,
|
|
43
|
+
...(await daimonEngineDockerSecurityArgsForConfigs(sources, () => materializeDaimonGrokSeccompProfile(securityDirectory)))
|
|
44
|
+
];
|
|
45
|
+
};
|