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
|
@@ -5,6 +5,9 @@ import { createWorkspaceResourceCommands, createWorkspaceResourceShellFunctions
|
|
|
5
5
|
import { createConfigEnvMaterializationFunction, createConfigEnvWrites } from "./containerConfigEnvRender.js";
|
|
6
6
|
import { CLI_CREDENTIAL_SECRET_NAME, modelAuthMethodNeedsCliCredential } from "./modelEnv.js";
|
|
7
7
|
import { createCliCredentialMaterialization, createRecipeEnvAssignments, mergeRecipeEnv, shellQuote } from "./containerEntrypointShell.js";
|
|
8
|
+
import { MOLTNET_READINESS_DIRECTORY } from "./containerReadinessPaths.js";
|
|
9
|
+
import { createRuntimeReadinessWait } from "./containerRuntimeReadinessRender.js";
|
|
10
|
+
import { createBackedMountGuard } from "./containerBackedMountRender.js";
|
|
8
11
|
const MOLTNET_SERVER_DATA_DIRECTORY = "/var/lib/spawnfile/moltnet/servers";
|
|
9
12
|
// moltnet reads this path from MOLTNET_CAUSAL_EVENTS_PATH
|
|
10
13
|
// (internal/app/config_load.go's mergeEnvConfig) and, when set, stamps
|
|
@@ -59,28 +62,13 @@ const resolveStartCommand = (plan) => plan.meta.startCommand
|
|
|
59
62
|
.map((token) => token
|
|
60
63
|
.replaceAll("<config-path>", plan.instancePaths.configPath)
|
|
61
64
|
.replaceAll("<home-path>", plan.instancePaths.homePath ?? "")
|
|
65
|
+
.replaceAll("<instance-root>", plan.instancePaths.instanceRoot ?? "")
|
|
62
66
|
.replaceAll("<runtime-root>", plan.runtimeRoot)
|
|
63
67
|
.replaceAll("<workspace-path>", plan.instancePaths.workspacePath)
|
|
64
68
|
.replaceAll("<port>", plan.port ? String(plan.port) : ""))
|
|
65
69
|
.filter((token) => token.length > 0);
|
|
66
|
-
const createRuntimeReadinessWait = (plan) => {
|
|
67
|
-
if (!["openclaw", "pi"].includes(plan.runtimeName) || !plan.port) {
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
return [
|
|
71
|
-
"attempts=0",
|
|
72
|
-
`until curl -sf ${shellQuote(`http://127.0.0.1:${plan.port}/healthz`)} >/dev/null; do`,
|
|
73
|
-
" attempts=$((attempts + 1))",
|
|
74
|
-
' if [ "$attempts" -ge 180 ]; then',
|
|
75
|
-
` echo ${shellQuote(`Timed out waiting for ${plan.runtimeName} on port ${plan.port}`)} >&2`,
|
|
76
|
-
" exit 1",
|
|
77
|
-
" fi",
|
|
78
|
-
" sleep 1",
|
|
79
|
-
"done",
|
|
80
|
-
""
|
|
81
|
-
];
|
|
82
|
-
};
|
|
83
70
|
export const renderEntrypoint = (runtimePlans, requiredSecrets, options = {}) => {
|
|
71
|
+
const usesDaimonRuntime = runtimePlans.some((plan) => plan.runtimeName === "daimon");
|
|
84
72
|
const cliCredentialMaterialization = createCliCredentialMaterialization(runtimePlans);
|
|
85
73
|
const renderedRequiredSecrets = [
|
|
86
74
|
...new Set([
|
|
@@ -91,7 +79,19 @@ export const renderEntrypoint = (runtimePlans, requiredSecrets, options = {}) =>
|
|
|
91
79
|
const lines = [
|
|
92
80
|
"#!/usr/bin/env bash",
|
|
93
81
|
"set -euo pipefail",
|
|
82
|
+
...(usesDaimonRuntime ? [
|
|
83
|
+
'if [ "$#" -ne 3 ] || [ "$1" != "--spawnfile-runtime-identity" ]; then echo "Missing trusted Daimon runtime identity" >&2; exit 1; fi',
|
|
84
|
+
'volume_bootstrap_uid="$2"',
|
|
85
|
+
'volume_bootstrap_gid="$3"',
|
|
86
|
+
'if ! [[ "$volume_bootstrap_uid" =~ ^[1-9][0-9]{0,9}$ ]] || [ "$volume_bootstrap_uid" -gt 2147483647 ]; then echo "Invalid trusted Daimon runtime UID" >&2; exit 1; fi',
|
|
87
|
+
'if ! [[ "$volume_bootstrap_gid" =~ ^[1-9][0-9]{0,9}$ ]] || [ "$volume_bootstrap_gid" -gt 2147483647 ]; then echo "Invalid trusted Daimon runtime GID" >&2; exit 1; fi',
|
|
88
|
+
"shift 3"
|
|
89
|
+
] : [
|
|
90
|
+
"volume_bootstrap_uid=1001",
|
|
91
|
+
"volume_bootstrap_gid=1001"
|
|
92
|
+
]),
|
|
94
93
|
"",
|
|
94
|
+
...createBackedMountGuard(options.persistentMounts ?? []),
|
|
95
95
|
"require_env() {",
|
|
96
96
|
' local name=\"$1\"',
|
|
97
97
|
' if [ -z \"${!name:-}\" ]; then',
|
|
@@ -143,6 +143,11 @@ export const renderEntrypoint = (runtimePlans, requiredSecrets, options = {}) =>
|
|
|
143
143
|
return `${lines.join("\n").trimEnd()}\n`;
|
|
144
144
|
}
|
|
145
145
|
lines.push("PIDS=()", "", "terminate_children() {", ' for pid in "${PIDS[@]:-}"; do', ' kill "$pid" 2>/dev/null || true', " done", "}", "", "trap terminate_children INT TERM EXIT", "");
|
|
146
|
+
for (const receiptDirectory of [...new Set(moltnetNodePlans.flatMap((plan) => plan.receiptStorePath ? [path.posix.dirname(plan.receiptStorePath)] : []))].sort()) {
|
|
147
|
+
lines.push(`install -d -m 700 ${shellQuote(receiptDirectory)}`);
|
|
148
|
+
}
|
|
149
|
+
if (moltnetNodePlans.some((plan) => plan.receiptStorePath))
|
|
150
|
+
lines.push("");
|
|
146
151
|
const recipeEnvAssignments = createRecipeEnvAssignments(mergeRecipeEnv(runtimePlans));
|
|
147
152
|
const recipeEnvPrefix = recipeEnvAssignments.length > 0 ? `${recipeEnvAssignments.join(" ")} ` : "";
|
|
148
153
|
const managedMoltnetServerPlans = moltnetServerPlans.filter((serverPlan) => serverPlan.mode === "managed");
|
|
@@ -159,9 +164,9 @@ export const renderEntrypoint = (runtimePlans, requiredSecrets, options = {}) =>
|
|
|
159
164
|
serverLines.push(`apply_json_env_value ${shellQuote(serverPlan.configPath)} ${shellQuote(patch.envName)} ${shellQuote(patch.jsonPath)}`);
|
|
160
165
|
}
|
|
161
166
|
const causalEventsPath = moltnetCausalEventsPath(serverPlan.configPath);
|
|
162
|
-
serverLines.push(...createMoltnetStorePrepareCommands(serverPlan), `MOLTNET_CONFIG=${shellQuote(serverPlan.configPath)} MOLTNET_CAUSAL_EVENTS_PATH=${shellQuote(causalEventsPath)} ${recipeEnvPrefix}/usr/local/bin/moltnet &`, 'PIDS+=("
|
|
167
|
+
serverLines.push(...createMoltnetStorePrepareCommands(serverPlan), `MOLTNET_CONFIG=${shellQuote(serverPlan.configPath)} MOLTNET_CAUSAL_EVENTS_PATH=${shellQuote(causalEventsPath)} ${recipeEnvPrefix}/usr/local/bin/moltnet start --config ${shellQuote(serverPlan.configPath)} &`, 'moltnet_server_pid="$!"', 'PIDS+=("$moltnet_server_pid")');
|
|
163
168
|
if (serverPlan.port) {
|
|
164
|
-
serverLines.push(`until curl -sf ${shellQuote(`http://127.0.0.1:${serverPlan.port}/healthz`)} >/dev/null; do sleep 1; done`);
|
|
169
|
+
serverLines.push(`until curl -sf ${shellQuote(`http://127.0.0.1:${serverPlan.port}/healthz`)} >/dev/null; do if ! kill -0 "$moltnet_server_pid" 2>/dev/null; then wait "$moltnet_server_pid" || true; echo ${shellQuote(`Moltnet exited before readiness on port ${serverPlan.port}`)} >&2; exit 1; fi; sleep 1; done`);
|
|
165
170
|
}
|
|
166
171
|
const externalParticipants = moltnetExternalParticipants.filter((artifact) => artifact.network.id === serverPlan.networkId);
|
|
167
172
|
if (externalParticipants.length > 0) {
|
|
@@ -189,14 +194,15 @@ export const renderEntrypoint = (runtimePlans, requiredSecrets, options = {}) =>
|
|
|
189
194
|
for (const plan of runtimePlans) {
|
|
190
195
|
const commandTokens = resolveStartCommand(plan);
|
|
191
196
|
const envAssignments = createEnvironmentAssignments(plan);
|
|
192
|
-
lines.push(`mkdir -p ${shellQuote(plan.instancePaths.workspacePath)}`, ...createWorkspaceResourceCommands(plan), `require_file ${shellQuote(plan.instancePaths.configPath)}`, ...createEnvFileWrites(plan), ...createConfigEnvWrites(plan), `${envAssignments.join(" ")} ${commandTokens.map(shellQuote).join(" ")} &`, 'PIDS+=("
|
|
197
|
+
lines.push(`mkdir -p ${shellQuote(plan.instancePaths.workspacePath)}`, ...createWorkspaceResourceCommands(plan), `require_file ${shellQuote(plan.instancePaths.configPath)}`, ...createEnvFileWrites(plan), ...createConfigEnvWrites(plan), `${envAssignments.join(" ")} ${commandTokens.map(shellQuote).join(" ")} &`, 'runtime_pid="$!"', 'PIDS+=("$runtime_pid")', "", ...createRuntimeReadinessWait(plan, "runtime_pid"));
|
|
193
198
|
}
|
|
194
199
|
for (const nodePlan of moltnetNodePlans) {
|
|
195
200
|
const urlEnv = networkUrlEnvName(nodePlan.networkId);
|
|
201
|
+
const receiptPath = `${MOLTNET_READINESS_DIRECTORY}/${nodePlan.networkId}-${nodePlan.memberId}.json`;
|
|
196
202
|
lines.push(
|
|
197
203
|
// Rebind the bridge endpoint when an external network URL is provided.
|
|
198
|
-
`if [ -n "\${${urlEnv}:-}" ]; then`, ` apply_json_env_value ${shellQuote(nodePlan.configPath)} ${shellQuote(urlEnv)} ${shellQuote("moltnet.base_url")}`, "fi",
|
|
204
|
+
`if [ -n "\${${urlEnv}:-}" ]; then`, ` apply_json_env_value ${shellQuote(nodePlan.configPath)} ${shellQuote(urlEnv)} ${shellQuote("moltnet.base_url")}`, "fi", `install -d -m 700 ${shellQuote(path.posix.dirname(receiptPath))}`, `rm -f ${shellQuote(receiptPath)}`, `MOLTNET_NODE_READINESS_RECEIPT=${shellQuote(receiptPath)} ${recipeEnvPrefix}/usr/local/bin/moltnet node ${shellQuote(nodePlan.configPath)} &`, 'PIDS+=("$!")', "");
|
|
199
205
|
}
|
|
200
|
-
lines.push('if [ "${#PIDS[@]}" -eq 0 ]; then', ' echo "No runtime targets were generated for this compile output" >&2', " exit 1", "fi", "", "status
|
|
206
|
+
lines.push('if [ "${#PIDS[@]}" -eq 0 ]; then', ' echo "No runtime targets were generated for this compile output" >&2', " exit 1", "fi", "", "# Every child is critical. Exit on the first child termination so Docker", "# can restart the complete, mutually consistent organization unit.", "set +e", 'wait -n "${PIDS[@]}"', "status=$?", "set -e", "terminate_children", 'for pid in "${PIDS[@]}"; do wait "$pid" 2>/dev/null || true; done', 'if [ "$status" -eq 0 ]; then status=1; fi', 'exit "$status"');
|
|
201
207
|
return `${lines.join("\n").trimEnd()}\n`;
|
|
202
208
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ContainerPersistentMountReport } from "../report/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Merges every durable-mount source into one report list.
|
|
4
|
+
*
|
|
5
|
+
* Sources are independent (memory banks, workspace `kind: volume` resources,
|
|
6
|
+
* Daimon telemetry, runtime-declared mounts, Moltnet stores and token
|
|
7
|
+
* directories) and may legitimately name the same mount twice; two sources
|
|
8
|
+
* describing one id DIFFERENTLY is a compile error, as is two ids claiming one
|
|
9
|
+
* host volume name.
|
|
10
|
+
*/
|
|
11
|
+
export declare const mergePersistentMounts: (candidates: readonly ContainerPersistentMountReport[]) => ContainerPersistentMountReport[];
|
|
12
|
+
/**
|
|
13
|
+
* Durable mount names come from four independent sources — memory banks
|
|
14
|
+
* (`memoryArtifacts.ts`), workspace `kind: volume` resources
|
|
15
|
+
* (`containerTargetResources.ts`), Moltnet stores and token directories
|
|
16
|
+
* (`moltnetArtifacts.ts`), and runtime-declared mounts (`runtimePlans`) —
|
|
17
|
+
* and each source only ever checked itself.
|
|
18
|
+
*
|
|
19
|
+
* Author-declared names cross those boundaries. A resource with
|
|
20
|
+
* `name: X` and a managed Moltnet store with `persistence.name: X` used to
|
|
21
|
+
* compile to two mounts at two different paths carrying one `volume_name`, so
|
|
22
|
+
* `spawnfile run` mounted a single host volume at both. A sqlite store and a
|
|
23
|
+
* team's edition state then shared one directory, where their bootstrap-marker
|
|
24
|
+
* and replacement-sentinel protocols contradict each other.
|
|
25
|
+
*
|
|
26
|
+
* There is no safe interpretation of that declaration, so it fails at compile
|
|
27
|
+
* naming both sides. (Two mounts legitimately SHARE a name only when they are
|
|
28
|
+
* the same mount, which the id-keyed merge upstream has already collapsed.)
|
|
29
|
+
*/
|
|
30
|
+
export declare const assertPersistentMountVolumeNamesAreUnique: (mounts: readonly ContainerPersistentMountReport[]) => void;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Merges every durable-mount source into one report list.
|
|
4
|
+
*
|
|
5
|
+
* Sources are independent (memory banks, workspace `kind: volume` resources,
|
|
6
|
+
* Daimon telemetry, runtime-declared mounts, Moltnet stores and token
|
|
7
|
+
* directories) and may legitimately name the same mount twice; two sources
|
|
8
|
+
* describing one id DIFFERENTLY is a compile error, as is two ids claiming one
|
|
9
|
+
* host volume name.
|
|
10
|
+
*/
|
|
11
|
+
export const mergePersistentMounts = (candidates) => {
|
|
12
|
+
const byId = new Map();
|
|
13
|
+
const merged = [...candidates]
|
|
14
|
+
.sort((left, right) => left.id.localeCompare(right.id))
|
|
15
|
+
.filter((mount) => {
|
|
16
|
+
const existing = byId.get(mount.id);
|
|
17
|
+
if (!existing) {
|
|
18
|
+
byId.set(mount.id, mount);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (existing.mount_path !== mount.mount_path ||
|
|
22
|
+
existing.volume_name !== mount.volume_name ||
|
|
23
|
+
existing.reason !== mount.reason ||
|
|
24
|
+
existing.lifecycle !== mount.lifecycle) {
|
|
25
|
+
throw new SpawnfileError("validation_error", `Container persistent mount ${mount.id} resolves to conflicting targets`);
|
|
26
|
+
}
|
|
27
|
+
// Deliberately kept, not deduplicated: two sources describing one mount
|
|
28
|
+
// identically was already reported twice before this merge was extracted,
|
|
29
|
+
// and silently changing that is not this fix's business.
|
|
30
|
+
return true;
|
|
31
|
+
});
|
|
32
|
+
assertPersistentMountVolumeNamesAreUnique(merged);
|
|
33
|
+
return merged;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Durable mount names come from four independent sources — memory banks
|
|
37
|
+
* (`memoryArtifacts.ts`), workspace `kind: volume` resources
|
|
38
|
+
* (`containerTargetResources.ts`), Moltnet stores and token directories
|
|
39
|
+
* (`moltnetArtifacts.ts`), and runtime-declared mounts (`runtimePlans`) —
|
|
40
|
+
* and each source only ever checked itself.
|
|
41
|
+
*
|
|
42
|
+
* Author-declared names cross those boundaries. A resource with
|
|
43
|
+
* `name: X` and a managed Moltnet store with `persistence.name: X` used to
|
|
44
|
+
* compile to two mounts at two different paths carrying one `volume_name`, so
|
|
45
|
+
* `spawnfile run` mounted a single host volume at both. A sqlite store and a
|
|
46
|
+
* team's edition state then shared one directory, where their bootstrap-marker
|
|
47
|
+
* and replacement-sentinel protocols contradict each other.
|
|
48
|
+
*
|
|
49
|
+
* There is no safe interpretation of that declaration, so it fails at compile
|
|
50
|
+
* naming both sides. (Two mounts legitimately SHARE a name only when they are
|
|
51
|
+
* the same mount, which the id-keyed merge upstream has already collapsed.)
|
|
52
|
+
*/
|
|
53
|
+
export const assertPersistentMountVolumeNamesAreUnique = (mounts) => {
|
|
54
|
+
const byVolumeName = new Map();
|
|
55
|
+
for (const mount of mounts) {
|
|
56
|
+
const existing = byVolumeName.get(mount.volume_name);
|
|
57
|
+
if (existing && existing.mount_path !== mount.mount_path) {
|
|
58
|
+
throw new SpawnfileError("validation_error", `Durable volume name "${mount.volume_name}" is claimed by two different mounts: `
|
|
59
|
+
+ `${existing.id} (${existing.reason}) at ${existing.mount_path}, and `
|
|
60
|
+
+ `${mount.id} (${mount.reason}) at ${mount.mount_path}. `
|
|
61
|
+
+ "One host volume cannot back two container paths; give each declaration its own name.");
|
|
62
|
+
}
|
|
63
|
+
if (!existing)
|
|
64
|
+
byVolumeName.set(mount.volume_name, mount);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MOLTNET_READINESS_DIRECTORY = "/run/spawnfile/moltnet-readiness";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MOLTNET_READINESS_DIRECTORY = "/run/spawnfile/moltnet-readiness";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const RUNTIME_LINK_MATERIALIZER_PATH = "/opt/spawnfile/materialize-runtime-links.cjs";
|
|
2
|
+
export const renderRuntimeLinkMaterializer = () => `"use strict";
|
|
3
|
+
const fs=require("node:fs"),path=require("node:path"),root=path.resolve(process.argv[2]||"");
|
|
4
|
+
if(!root.startsWith("/opt/spawnfile/runtime-installs/"))throw Error("unsafe root");
|
|
5
|
+
const rootInfo=fs.lstatSync(root),links=[];
|
|
6
|
+
if(!rootInfo.isDirectory()||rootInfo.isSymbolicLink()||rootInfo.uid||rootInfo.gid)throw Error("unsafe root identity");
|
|
7
|
+
const walk=d=>{for(const n of fs.readdirSync(d).sort()){const p=path.join(d,n),s=fs.lstatSync(p);if(s.isSymbolicLink())links.push(p);else if(s.isDirectory())walk(p);else if(!s.isFile())throw Error("unsupported entry");}};
|
|
8
|
+
const inside=p=>p.startsWith(root+path.sep);
|
|
9
|
+
const resolve=p=>{let hops=0;for(;;){const rel=path.relative(root,p);if(!rel||rel.startsWith("..")||path.isAbsolute(rel))throw Error("link escape");const parts=rel.split(path.sep);let q=root,again=false;for(let i=0;i<parts.length;i++){q=path.join(q,parts[i]);const s=fs.lstatSync(q);if(!s.isSymbolicLink())continue;if(++hops>16||s.uid||s.gid)throw Error("unsafe link chain");const l=fs.readlinkSync(q);if(path.isAbsolute(l))throw Error("absolute link");p=path.resolve(path.dirname(q),l,...parts.slice(i+1));if(!inside(p))throw Error("link escape");again=true;break;}if(!again)return p;}};
|
|
10
|
+
walk(root);
|
|
11
|
+
for(const link of links){const s=fs.lstatSync(link);if(!s.isSymbolicLink()||s.uid||s.gid)throw Error("link changed");const target=resolve(link),t=fs.lstatSync(target);if(!t.isFile()||t.isSymbolicLink()||t.uid||t.gid||t.dev!==rootInfo.dev||t.nlink<1)throw Error("unsafe target");const b=fs.readFileSync(target);if(b.length>67108864)throw Error("target too large");const tmp=link+".spawnfile-materialize",fd=fs.openSync(tmp,fs.constants.O_CREAT|fs.constants.O_EXCL|fs.constants.O_WRONLY|fs.constants.O_NOFOLLOW,t.mode&0o111?0o555:0o444);try{fs.writeFileSync(fd,b);fs.fsyncSync(fd);}finally{fs.closeSync(fd);}fs.renameSync(tmp,link);}
|
|
12
|
+
`;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RuntimeTargetPlan } from "./containerArtifactsTypes.js";
|
|
2
|
+
/**
|
|
3
|
+
* Blocks until a runtime with an HTTP port answers /healthz, failing fast if
|
|
4
|
+
* the child dies first. Split out of containerEntrypointRender.ts to keep that
|
|
5
|
+
* module under the repository's 400-line ceiling.
|
|
6
|
+
*/
|
|
7
|
+
export declare const createRuntimeReadinessWait: (plan: RuntimeTargetPlan, pidVariable: string) => string[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { shellQuote } from "./containerEntrypointShell.js";
|
|
2
|
+
/**
|
|
3
|
+
* Blocks until a runtime with an HTTP port answers /healthz, failing fast if
|
|
4
|
+
* the child dies first. Split out of containerEntrypointRender.ts to keep that
|
|
5
|
+
* module under the repository's 400-line ceiling.
|
|
6
|
+
*/
|
|
7
|
+
export const createRuntimeReadinessWait = (plan, pidVariable) => {
|
|
8
|
+
if (!plan.port)
|
|
9
|
+
return [];
|
|
10
|
+
if (plan.runtimeName === "daimon") {
|
|
11
|
+
return [
|
|
12
|
+
"attempts=0",
|
|
13
|
+
`until curl -sf ${shellQuote(`http://127.0.0.1:${plan.port}/healthz`)} >/dev/null; do`,
|
|
14
|
+
` if ! kill -0 "$${pidVariable}" 2>/dev/null; then wait "$${pidVariable}" || true; echo ${shellQuote(`Daimon exited before readiness on port ${plan.port}`)} >&2; exit 1; fi`,
|
|
15
|
+
" attempts=$((attempts + 1))",
|
|
16
|
+
' if [ "$attempts" -ge 180 ]; then',
|
|
17
|
+
` echo ${shellQuote(`Timed out waiting for daimon on port ${plan.port}`)} >&2`,
|
|
18
|
+
" exit 1",
|
|
19
|
+
" fi",
|
|
20
|
+
" sleep 1",
|
|
21
|
+
"done",
|
|
22
|
+
""
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
if (!["openclaw", "pi"].includes(plan.runtimeName))
|
|
26
|
+
return [];
|
|
27
|
+
return [
|
|
28
|
+
"attempts=0",
|
|
29
|
+
`until curl -sf ${shellQuote(`http://127.0.0.1:${plan.port}/healthz`)} >/dev/null; do`,
|
|
30
|
+
` if ! kill -0 "$${pidVariable}" 2>/dev/null; then wait "$${pidVariable}" || true; echo ${shellQuote(`${plan.runtimeName} exited before readiness on port ${plan.port}`)} >&2; exit 1; fi`,
|
|
31
|
+
" attempts=$((attempts + 1))",
|
|
32
|
+
' if [ "$attempts" -ge 180 ]; then',
|
|
33
|
+
` echo ${shellQuote(`Timed out waiting for ${plan.runtimeName} on port ${plan.port}`)} >&2`,
|
|
34
|
+
" exit 1",
|
|
35
|
+
" fi",
|
|
36
|
+
" sleep 1",
|
|
37
|
+
"done",
|
|
38
|
+
""
|
|
39
|
+
];
|
|
40
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RuntimeTargetPlan } from "./containerArtifactsTypes.js";
|
|
2
|
+
import type { EntrypointOptions } from "./containerEntrypointRender.js";
|
|
3
|
+
export { VOLUME_BOOTSTRAP_MARKER, VOLUME_BOOTSTRAP_MARKER_CONTENT } from "./containerVolumeBootstrap.js";
|
|
4
|
+
export declare const createStateOwnershipCommand: (runtimePlans: RuntimeTargetPlan[], persistentMountPaths?: string[], moltnet?: EntrypointOptions["moltnet"]) => string;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { SpawnfileError } from "../shared/index.js";
|
|
3
|
+
import { DAIMON_RUNTIME_UID, resolveDaimonUidEntrypointStateRoots } from "./containerDaimonUidEntrypointRender.js";
|
|
4
|
+
import { VOLUME_BOOTSTRAP_MARKER, VOLUME_BOOTSTRAP_MARKER_CONTENT } from "./containerVolumeBootstrap.js";
|
|
5
|
+
export { VOLUME_BOOTSTRAP_MARKER, VOLUME_BOOTSTRAP_MARKER_CONTENT } from "./containerVolumeBootstrap.js";
|
|
6
|
+
const SPAWNFILE_STATE_ROOT = "/var/lib/spawnfile";
|
|
7
|
+
const MOLTNET_STATE_ROOT = `${SPAWNFILE_STATE_ROOT}/moltnet`;
|
|
8
|
+
const shellQuote = (value) => `'${value.replace(/'/g, `'\"'\"'`)}'`;
|
|
9
|
+
const privateDirectoriesThrough = (target) => {
|
|
10
|
+
const relative = path.posix.relative(SPAWNFILE_STATE_ROOT, target);
|
|
11
|
+
if (relative.startsWith("../") || path.posix.isAbsolute(relative)) {
|
|
12
|
+
throw new SpawnfileError("compile_error", `Generated Moltnet state path escapes ${SPAWNFILE_STATE_ROOT}: ${target}`);
|
|
13
|
+
}
|
|
14
|
+
const segments = relative === "" ? [] : relative.split("/");
|
|
15
|
+
return [
|
|
16
|
+
SPAWNFILE_STATE_ROOT,
|
|
17
|
+
...segments.map((_, index) => path.posix.join(SPAWNFILE_STATE_ROOT, ...segments.slice(0, index + 1)))
|
|
18
|
+
];
|
|
19
|
+
};
|
|
20
|
+
const createMoltnetPrivacyCommands = (runtimePlans, persistentMountPaths, moltnet) => {
|
|
21
|
+
if (!runtimePlans.some((plan) => plan.runtimeName === "daimon") || !moltnet)
|
|
22
|
+
return [];
|
|
23
|
+
const configPaths = [
|
|
24
|
+
...moltnet.serverPlans.flatMap((plan) => plan.mode === "managed" && plan.configPath ? [plan.configPath] : []),
|
|
25
|
+
...moltnet.nodePlans.map((plan) => plan.configPath)
|
|
26
|
+
].sort();
|
|
27
|
+
const receiptDirectories = moltnet.nodePlans.flatMap((plan) => plan.receiptStorePath ? [path.posix.dirname(plan.receiptStorePath)] : []);
|
|
28
|
+
if (configPaths.length === 0)
|
|
29
|
+
return [];
|
|
30
|
+
if (configPaths.some((configPath) => !configPath.startsWith(`${MOLTNET_STATE_ROOT}/`))) {
|
|
31
|
+
throw new SpawnfileError("compile_error", "Generated Moltnet config paths must stay beneath the private Moltnet state root");
|
|
32
|
+
}
|
|
33
|
+
const moltnetMountPaths = persistentMountPaths
|
|
34
|
+
.filter((mountPath) => mountPath.startsWith(`${MOLTNET_STATE_ROOT}/`));
|
|
35
|
+
const privateDirectories = [
|
|
36
|
+
...new Set([
|
|
37
|
+
...configPaths.flatMap((configPath) => privateDirectoriesThrough(path.posix.dirname(configPath))),
|
|
38
|
+
...receiptDirectories.flatMap(privateDirectoriesThrough),
|
|
39
|
+
...moltnetMountPaths.flatMap(privateDirectoriesThrough)
|
|
40
|
+
])
|
|
41
|
+
].filter((directory) => directory !== SPAWNFILE_STATE_ROOT).sort();
|
|
42
|
+
const ownership = `${DAIMON_RUNTIME_UID}:${DAIMON_RUNTIME_UID}`;
|
|
43
|
+
return [
|
|
44
|
+
`install -d -o ${DAIMON_RUNTIME_UID} -g ${DAIMON_RUNTIME_UID} -m 700 ${privateDirectories.map(shellQuote).join(" ")}`,
|
|
45
|
+
`chown root:root ${configPaths.map(shellQuote).join(" ")}`,
|
|
46
|
+
`chmod 600 ${configPaths.map(shellQuote).join(" ")}`,
|
|
47
|
+
`chown ${ownership} ${configPaths.map(shellQuote).join(" ")}`
|
|
48
|
+
];
|
|
49
|
+
};
|
|
50
|
+
export const createStateOwnershipCommand = (runtimePlans, persistentMountPaths = [], moltnet) => {
|
|
51
|
+
const mountPaths = [...new Set(persistentMountPaths)].sort();
|
|
52
|
+
const wrapperStateRoots = runtimePlans.some((plan) => plan.runtimeName === "daimon")
|
|
53
|
+
? resolveDaimonUidEntrypointStateRoots(runtimePlans)
|
|
54
|
+
: [];
|
|
55
|
+
const mkdirPaths = [...new Set([SPAWNFILE_STATE_ROOT, ...mountPaths])].sort();
|
|
56
|
+
const markerCommands = mountPaths.map((mountPath) => `printf '%s\\n' ${shellQuote(VOLUME_BOOTSTRAP_MARKER_CONTENT)} > ${shellQuote(path.posix.join(mountPath, VOLUME_BOOTSTRAP_MARKER))} && chmod 600 ${shellQuote(path.posix.join(mountPath, VOLUME_BOOTSTRAP_MARKER))}`);
|
|
57
|
+
const chownPaths = [
|
|
58
|
+
...new Set([
|
|
59
|
+
...(runtimePlans.some((plan) => plan.runtimeName === "daimon") ? [] : [SPAWNFILE_STATE_ROOT]),
|
|
60
|
+
...mountPaths.filter((mountPath) => !mountPath.startsWith(`${SPAWNFILE_STATE_ROOT}/`))
|
|
61
|
+
])
|
|
62
|
+
].sort();
|
|
63
|
+
return [
|
|
64
|
+
`mkdir -p ${mkdirPaths.map(shellQuote).join(" ")}`,
|
|
65
|
+
...markerCommands,
|
|
66
|
+
...(chownPaths.length > 0
|
|
67
|
+
? [`chown -R spawnfile:spawnfile ${chownPaths.map(shellQuote).join(" ")}`]
|
|
68
|
+
: []),
|
|
69
|
+
...(runtimePlans.some((plan) => plan.runtimeName === "daimon")
|
|
70
|
+
? [`chown root:root ${shellQuote(SPAWNFILE_STATE_ROOT)} && chmod 711 ${shellQuote(SPAWNFILE_STATE_ROOT)}`]
|
|
71
|
+
: []),
|
|
72
|
+
...wrapperStateRoots.map((stateRoot) => `install -d -o root -g root -m 700 ${shellQuote(stateRoot)}`),
|
|
73
|
+
...createMoltnetPrivacyCommands(runtimePlans, mountPaths, moltnet)
|
|
74
|
+
].join(" && ");
|
|
75
|
+
};
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import type { ContainerTarget, ContainerTargetInput, RuntimeContainerMeta } from "../runtime/index.js";
|
|
2
2
|
import type { RuntimeTargetPlan } from "./containerArtifactsTypes.js";
|
|
3
3
|
import { type WorkspaceResourcePlan } from "./workspaceResources.js";
|
|
4
|
-
export
|
|
4
|
+
export interface ResolvedTargetResourcePlan extends WorkspaceResourcePlan {
|
|
5
|
+
canonicalBackingPath: string;
|
|
6
|
+
ownerId: string;
|
|
7
|
+
persistentMountId?: string;
|
|
8
|
+
replacementSentinel?: string;
|
|
9
|
+
resolvedIdentity: string;
|
|
10
|
+
volumeName?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface WorkspaceResourcePersistentMount {
|
|
13
|
+
/** The author's `name`, when declared on the resource. */
|
|
14
|
+
declared_volume_name?: string;
|
|
15
|
+
id: string;
|
|
16
|
+
lifecycle: "exclusive-reattach";
|
|
17
|
+
mount_path: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
volume_name: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const resolveWorkspaceResourceVolumes: (runtimePlans: readonly RuntimeTargetPlan[]) => {
|
|
22
|
+
resources: ResolvedTargetResourcePlan[];
|
|
23
|
+
mounts: WorkspaceResourcePersistentMount[];
|
|
24
|
+
};
|
|
25
|
+
export declare const resolveTargetResources: (target: ContainerTarget, inputs: ContainerTargetInput[], instancePaths: RuntimeTargetPlan["instancePaths"], meta: RuntimeContainerMeta, planRoot: string, deploymentLineage?: string) => ResolvedTargetResourcePlan[];
|
|
@@ -1,10 +1,48 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { SpawnfileError } from "../shared/index.js";
|
|
4
|
+
import { createExclusiveReattachVolumeName } from "../shared/index.js";
|
|
3
5
|
import { mergeWorkspaceResourcePlans } from "./workspaceResources.js";
|
|
4
6
|
const CONFIG_FILE_PLACEHOLDER = "<config-file>";
|
|
5
7
|
const INSTANCE_ROOT_PLACEHOLDER = "<instance-root>";
|
|
6
8
|
const SOURCE_AGENT_PLACEHOLDER = "<agent-name>";
|
|
7
9
|
const SOURCE_SLUG_PLACEHOLDER = "<source-slug>";
|
|
10
|
+
const digest = (value) => createHash("sha256").update(value).digest("hex");
|
|
11
|
+
export const resolveWorkspaceResourceVolumes = (runtimePlans) => {
|
|
12
|
+
const resources = runtimePlans.flatMap((runtimePlan) => (runtimePlan.resources ?? []));
|
|
13
|
+
const byBackingPath = new Map();
|
|
14
|
+
const backingPathByVolumeName = new Map();
|
|
15
|
+
for (const resource of resources.filter((candidate) => candidate.kind === "volume")) {
|
|
16
|
+
const existing = byBackingPath.get(resource.canonicalBackingPath);
|
|
17
|
+
// Two DIFFERENT declared resources landing on one backing path is always a
|
|
18
|
+
// mistake: the path segment derives from an explicit `name` when one is
|
|
19
|
+
// given, so `id: a`/`id: b` both declaring `name: X` in one scope used to
|
|
20
|
+
// collapse into a single directory and volume, silently, with both
|
|
21
|
+
// workspace symlinks pointing at it. The same id across runtime plans is
|
|
22
|
+
// the legitimate `sharing: team` case and still merges.
|
|
23
|
+
if (existing && existing.id !== resource.id) {
|
|
24
|
+
throw new SpawnfileError("validation_error", `Workspace resources ${existing.id} and ${resource.id} resolve to the same backing volume ${resource.canonicalBackingPath}; a declared volume name must be unique within its scope`);
|
|
25
|
+
}
|
|
26
|
+
if (existing && (existing.mode !== resource.mode || existing.sharing !== resource.sharing || existing.volumeName !== resource.volumeName || (resource.sharing === "per_agent" && existing.ownerId !== resource.ownerId))) {
|
|
27
|
+
throw new SpawnfileError("validation_error", `Workspace volume ${resource.canonicalBackingPath} has incompatible mode, sharing, or owner declarations`);
|
|
28
|
+
}
|
|
29
|
+
const conflictingPath = backingPathByVolumeName.get(resource.volumeName);
|
|
30
|
+
if (conflictingPath && conflictingPath !== resource.canonicalBackingPath)
|
|
31
|
+
throw new SpawnfileError("validation_error", "Workspace volume name collision");
|
|
32
|
+
byBackingPath.set(resource.canonicalBackingPath, resource);
|
|
33
|
+
backingPathByVolumeName.set(resource.volumeName, resource.canonicalBackingPath);
|
|
34
|
+
}
|
|
35
|
+
// Workspace `kind: volume` resources are durable product state: an author
|
|
36
|
+
// declares one precisely so it outlives the container. They are therefore
|
|
37
|
+
// `exclusive-reattach` — deployment-stable name, reattached rather than
|
|
38
|
+
// recreated, one live holder at a time.
|
|
39
|
+
return { resources, mounts: [...byBackingPath.values()].map((resource) => ({
|
|
40
|
+
...(resource.name?.trim() ? { declared_volume_name: resource.name.trim() } : {}),
|
|
41
|
+
id: resource.persistentMountId, lifecycle: "exclusive-reattach",
|
|
42
|
+
mount_path: resource.canonicalBackingPath,
|
|
43
|
+
reason: `Workspace ${resource.sharing} resource ${resource.id}`, volume_name: resource.volumeName
|
|
44
|
+
})) };
|
|
45
|
+
};
|
|
8
46
|
const replaceSourceWorkspacePathTemplate = (template, input, instancePaths, meta) => {
|
|
9
47
|
const agentName = input.value.kind === "agent" ? input.value.name : input.slug;
|
|
10
48
|
const instanceRoot = instancePaths.instanceRoot ?? path.posix.dirname(instancePaths.workspacePath);
|
|
@@ -21,10 +59,12 @@ const sourceTargetId = (target, input, isMergedTarget) => isMergedTarget ? `${ta
|
|
|
21
59
|
const pathsOverlap = (left, right) => left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
|
|
22
60
|
const dedupeAndAssertResourcePlans = (target, resources) => {
|
|
23
61
|
const byLinkPath = new Map();
|
|
62
|
+
const volumesByBackingPath = new Map();
|
|
63
|
+
const backingPathByVolumeName = new Map();
|
|
24
64
|
for (const resource of resources) {
|
|
25
65
|
const existing = byLinkPath.get(resource.linkPath);
|
|
26
66
|
if (existing) {
|
|
27
|
-
if (existing.
|
|
67
|
+
if (existing.resolvedIdentity !== resource.resolvedIdentity) {
|
|
28
68
|
throw new SpawnfileError("validation_error", `Container target ${target.id} declares conflicting workspace resources at ${resource.linkPath}`);
|
|
29
69
|
}
|
|
30
70
|
continue;
|
|
@@ -34,10 +74,33 @@ const dedupeAndAssertResourcePlans = (target, resources) => {
|
|
|
34
74
|
throw new SpawnfileError("validation_error", `Container target ${target.id} declares overlapping workspace resource links ${overlapping.linkPath} and ${resource.linkPath}`);
|
|
35
75
|
}
|
|
36
76
|
byLinkPath.set(resource.linkPath, resource);
|
|
77
|
+
if (resource.kind !== "volume")
|
|
78
|
+
continue;
|
|
79
|
+
const existingVolume = volumesByBackingPath.get(resource.canonicalBackingPath);
|
|
80
|
+
if (existingVolume && existingVolume.id !== resource.id) {
|
|
81
|
+
throw new SpawnfileError("validation_error", `Container target ${target.id} declares workspace resources ${existingVolume.id} and ${resource.id} that resolve to the same backing volume ${resource.canonicalBackingPath}; a declared volume name must be unique within its scope`);
|
|
82
|
+
}
|
|
83
|
+
if (existingVolume) {
|
|
84
|
+
const incompatible = existingVolume.mode !== resource.mode ||
|
|
85
|
+
existingVolume.sharing !== resource.sharing ||
|
|
86
|
+
existingVolume.volumeName !== resource.volumeName ||
|
|
87
|
+
(resource.sharing === "per_agent" && existingVolume.ownerId !== resource.ownerId);
|
|
88
|
+
if (incompatible) {
|
|
89
|
+
throw new SpawnfileError("validation_error", `Container target ${target.id} declares incompatible workspace volume owners or modes at ${resource.canonicalBackingPath}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
volumesByBackingPath.set(resource.canonicalBackingPath, resource);
|
|
94
|
+
}
|
|
95
|
+
const conflictingBackingPath = backingPathByVolumeName.get(resource.volumeName);
|
|
96
|
+
if (conflictingBackingPath && conflictingBackingPath !== resource.canonicalBackingPath) {
|
|
97
|
+
throw new SpawnfileError("validation_error", `Container target ${target.id} workspace volume identity collision`);
|
|
98
|
+
}
|
|
99
|
+
backingPathByVolumeName.set(resource.volumeName, resource.canonicalBackingPath);
|
|
37
100
|
}
|
|
38
101
|
return [...byLinkPath.values()].sort((left, right) => left.linkPath.localeCompare(right.linkPath) || left.id.localeCompare(right.id));
|
|
39
102
|
};
|
|
40
|
-
export const resolveTargetResources = (target, inputs, instancePaths, meta) => {
|
|
103
|
+
export const resolveTargetResources = (target, inputs, instancePaths, meta, planRoot, deploymentLineage = "compile") => {
|
|
41
104
|
const sourceIds = new Set(target.sourceIds ?? []);
|
|
42
105
|
if (sourceIds.size === 0) {
|
|
43
106
|
return [];
|
|
@@ -50,6 +113,35 @@ export const resolveTargetResources = (target, inputs, instancePaths, meta) => {
|
|
|
50
113
|
return mergeWorkspaceResourcePlans(input.value.workspaceResources ?? [], `${target.id}/${input.id}`, {
|
|
51
114
|
targetId: sourceTargetId(target, input, isMergedTarget),
|
|
52
115
|
workspacePath: resolveSourceWorkspacePath(input, instancePaths, meta)
|
|
116
|
+
}).map((resource) => {
|
|
117
|
+
const canonicalBackingPath = path.posix.normalize(resource.backingPath);
|
|
118
|
+
const ownerId = resource.sharing === "per_agent" ? input.id : canonicalBackingPath;
|
|
119
|
+
const resolvedIdentity = `sha256:${digest(JSON.stringify({
|
|
120
|
+
backingPath: canonicalBackingPath,
|
|
121
|
+
kind: resource.kind,
|
|
122
|
+
mode: resource.mode,
|
|
123
|
+
ownerId,
|
|
124
|
+
...(resource.kind === "bundle" ? { archivePath: resource.archivePath, sha256: resource.sha256 } : {}),
|
|
125
|
+
sharing: resource.sharing
|
|
126
|
+
}))}`;
|
|
127
|
+
if (resource.kind !== "volume")
|
|
128
|
+
return { ...resource, backingPath: canonicalBackingPath, canonicalBackingPath, ownerId, resolvedIdentity };
|
|
129
|
+
const pathDigest = digest(canonicalBackingPath);
|
|
130
|
+
return {
|
|
131
|
+
...resource,
|
|
132
|
+
backingPath: canonicalBackingPath,
|
|
133
|
+
canonicalBackingPath,
|
|
134
|
+
ownerId,
|
|
135
|
+
persistentMountId: `workspace-resource-${pathDigest.slice(0, 24)}`,
|
|
136
|
+
replacementSentinel: path.posix.join(canonicalBackingPath, ".spawnfile-resource-identity"),
|
|
137
|
+
resolvedIdentity,
|
|
138
|
+
// Author-declared `name` verbatim (an operator can pre-create or
|
|
139
|
+
// migrate the host volume under exactly that name); otherwise a
|
|
140
|
+
// deployment-stable derived name. Never run-scoped: a fresh
|
|
141
|
+
// NOOPOLIS_RUN_ID must not strand the previous run's state.
|
|
142
|
+
volumeName: resource.name?.trim()
|
|
143
|
+
|| createExclusiveReattachVolumeName(`${planRoot}\0${deploymentLineage}`, `workspace-resource-${pathDigest.slice(0, 24)}`)
|
|
144
|
+
};
|
|
53
145
|
});
|
|
54
146
|
});
|
|
55
147
|
return dedupeAndAssertResourcePlans(target, resources);
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
import { VOLUME_BOOTSTRAP_MARKER, VOLUME_BOOTSTRAP_MARKER_CONTENT } from "./containerVolumeBootstrap.js";
|
|
1
2
|
const shellQuote = (value) => `'${value.replace(/'/g, `'\"'\"'`)}'`;
|
|
2
3
|
export const createWorkspaceResourceCommands = (plan) => (plan.resources ?? []).flatMap((resource) => {
|
|
3
4
|
const readonlyFlag = resource.mode === "readonly" ? "readonly" : "mutable";
|
|
4
5
|
if (resource.kind === "volume") {
|
|
6
|
+
const resolved = resource;
|
|
5
7
|
return [
|
|
6
|
-
`prepare_volume_resource ${shellQuote(resource.id)} ${shellQuote(resource.linkPath)} ${shellQuote(resource.backingPath)} ${shellQuote(readonlyFlag)}`
|
|
8
|
+
`prepare_volume_resource ${shellQuote(resource.id)} ${shellQuote(resource.linkPath)} ${shellQuote(resource.backingPath)} ${shellQuote(readonlyFlag)} ${shellQuote(resolved.resolvedIdentity ?? "")}`
|
|
7
9
|
];
|
|
8
10
|
}
|
|
11
|
+
if (resource.kind === "bundle")
|
|
12
|
+
return [
|
|
13
|
+
`prepare_bundle_resource ${shellQuote(resource.id)} ${shellQuote(resource.linkPath)} ${shellQuote(resource.backingPath)} ${shellQuote(resource.archivePath ?? "")} ${shellQuote(resource.sha256 ?? "")}`
|
|
14
|
+
];
|
|
9
15
|
const selectorKind = resource.branch
|
|
10
16
|
? "branch"
|
|
11
17
|
: resource.tag
|
|
@@ -58,15 +64,57 @@ export const createWorkspaceResourceShellFunctions = () => [
|
|
|
58
64
|
' local link_path="$2"',
|
|
59
65
|
' local backing_path="$3"',
|
|
60
66
|
' local mode="$4"',
|
|
67
|
+
' local identity="$5"',
|
|
61
68
|
' mkdir -p "$backing_path"',
|
|
62
69
|
' if [ ! -d "$backing_path" ]; then',
|
|
63
70
|
' echo "Workspace volume resource $id backing path is not a directory: $backing_path" >&2',
|
|
64
71
|
" exit 1",
|
|
65
72
|
" fi",
|
|
73
|
+
' local sentinel="$backing_path/.spawnfile-resource-identity"',
|
|
74
|
+
' if [ -f "$sentinel" ]; then',
|
|
75
|
+
' if [ "$(cat "$sentinel")" != "$identity" ]; then',
|
|
76
|
+
' echo "Workspace volume resource $id replacement sentinel mismatch" >&2',
|
|
77
|
+
" exit 1",
|
|
78
|
+
" fi",
|
|
79
|
+
" else",
|
|
80
|
+
` local bootstrap="$backing_path/${VOLUME_BOOTSTRAP_MARKER}"`,
|
|
81
|
+
` local bootstrap_claim="$backing_path/${VOLUME_BOOTSTRAP_MARKER}.claim"`,
|
|
82
|
+
' local first_entry',
|
|
83
|
+
' first_entry="$(find "$backing_path" -mindepth 1 -maxdepth 1 -print -quit)"',
|
|
84
|
+
' if [ -n "$first_entry" ]; then',
|
|
85
|
+
' if [ "$first_entry" = "$bootstrap" ]; then',
|
|
86
|
+
' mv -T --no-clobber -- "$bootstrap" "$bootstrap_claim"',
|
|
87
|
+
' elif [ "$first_entry" != "$bootstrap_claim" ]; then',
|
|
88
|
+
' echo "Workspace volume resource $id is nonempty without an authenticated replacement sentinel" >&2',
|
|
89
|
+
" exit 1",
|
|
90
|
+
" fi",
|
|
91
|
+
' if [ "$(find "$backing_path" -mindepth 1 -maxdepth 1 ! -name ' + `'${VOLUME_BOOTSTRAP_MARKER}.claim'` + ' -print -quit)" != "" ] || [ -L "$bootstrap_claim" ] || [ ! -f "$bootstrap_claim" ] || [ "$(stat -c "%u:%g:%a:%h" "$bootstrap_claim")" != "${volume_bootstrap_uid}:${volume_bootstrap_gid}:600:1" ] || [ "$(cat "$bootstrap_claim")" != "' + VOLUME_BOOTSTRAP_MARKER_CONTENT + '" ]; then',
|
|
92
|
+
' echo "Workspace volume resource $id bootstrap marker mismatch" >&2',
|
|
93
|
+
" exit 1",
|
|
94
|
+
" fi",
|
|
95
|
+
' rm -- "$bootstrap_claim"',
|
|
96
|
+
" fi",
|
|
97
|
+
' printf "%s\\n" "$identity" > "$sentinel"',
|
|
98
|
+
" fi",
|
|
66
99
|
' mark_readonly_resource "$backing_path" "$mode"',
|
|
67
100
|
' prepare_resource_link "$id" "$link_path" "$backing_path"',
|
|
68
101
|
"}",
|
|
69
102
|
"",
|
|
103
|
+
"prepare_bundle_resource() {",
|
|
104
|
+
' local id="$1" link_path="$2" backing_path="$3" archive="$4" identity="$5"',
|
|
105
|
+
' [ -f "$archive" ] || { echo "Workspace bundle $id archive missing" >&2; exit 1; }',
|
|
106
|
+
' local sentinel="$backing_path/.spawnfile-bundle-identity"',
|
|
107
|
+
' if [ -e "$backing_path" ]; then',
|
|
108
|
+
' [ -f "$sentinel" ] && [ "$(cat "$sentinel")" = "$identity" ] || { echo "Workspace bundle $id identity mismatch" >&2; exit 1; }',
|
|
109
|
+
" else",
|
|
110
|
+
' mkdir -p "$backing_path"',
|
|
111
|
+
' tar --no-same-owner --no-same-permissions -xf "$archive" -C "$backing_path"',
|
|
112
|
+
' printf %s "$identity" > "$sentinel"',
|
|
113
|
+
" fi",
|
|
114
|
+
' mark_readonly_resource "$backing_path" readonly',
|
|
115
|
+
' prepare_resource_link "$id" "$link_path" "$backing_path"',
|
|
116
|
+
"}",
|
|
117
|
+
"",
|
|
70
118
|
"prepare_git_resource() {",
|
|
71
119
|
' local id="$1"',
|
|
72
120
|
' local link_path="$2"',
|
|
@@ -2,7 +2,7 @@ import type { ContainerPersistentMountReport } from "../report/index.js";
|
|
|
2
2
|
import type { CompiledNodeArtifact, RuntimeTargetPlan } from "./containerArtifactsTypes.js";
|
|
3
3
|
import type { CompilePlan } from "./types.js";
|
|
4
4
|
export interface DaimonTelemetryArtifactBundle {
|
|
5
|
-
/** One durable volume per
|
|
5
|
+
/** One durable volume per legacy Pi agent, mounted onto its telemetry directory. */
|
|
6
6
|
mounts: ContainerPersistentMountReport[];
|
|
7
7
|
/** Runtime target plan id (e.g. "pi-app") -> { node id -> telemetry persistent mount id
|
|
8
8
|
* }, so `containerArtifacts.ts` can stamp `runtime_instances[].telemetry_mount_ids` and
|
|
@@ -11,7 +11,7 @@ export interface DaimonTelemetryArtifactBundle {
|
|
|
11
11
|
telemetryMountIdsByInstance: Map<string, Record<string, string>>;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* Registers one run-scoped durable volume per
|
|
14
|
+
* Registers one run-scoped durable volume per legacy Pi agent for its daimon turn/wake
|
|
15
15
|
* causal telemetry directory (`<instanceRoot>/runtime/agents/<slug>/telemetry` — the parent
|
|
16
16
|
* of `causal.jsonl`, see `appCoreSource.ts`'s `runtimeHomePath`/`instanceRoot`), mirroring
|
|
17
17
|
* exactly how `moltnetArtifacts.ts` mounts the Moltnet causal directory (Piece 4b,
|