spawnfile 0.1.17 → 0.1.19
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 +1 -1
- package/dist/auth/importers.js +21 -5
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/profileStore.js +4 -1
- package/dist/auth/trainingGrokWriteback.d.ts +4 -0
- package/dist/auth/trainingGrokWriteback.js +119 -0
- package/dist/auth/trainingGrokWritebackLock.d.ts +5 -0
- package/dist/auth/trainingGrokWritebackLock.js +56 -0
- package/dist/cli/authCommands.js +9 -5
- package/dist/cli/compileBuildCommands.js +4 -0
- package/dist/cli/devCommands.js +4 -2
- package/dist/cli/downCommand.d.ts +2 -0
- package/dist/cli/downCommand.js +3 -0
- package/dist/cli/envFileOption.d.ts +18 -0
- package/dist/cli/envFileOption.js +20 -0
- package/dist/cli/lifecycleCommands.d.ts +1 -1
- package/dist/cli/lifecycleCommands.js +3 -1
- package/dist/cli/lifecyclePlan.d.ts +1 -0
- package/dist/cli/lifecyclePlan.js +2 -0
- package/dist/cli/paideiaDelegation.d.ts +2 -1
- package/dist/cli/paideiaDelegation.js +5 -4
- package/dist/cli/recordCommands.d.ts +17 -0
- package/dist/cli/recordCommands.js +43 -0
- package/dist/cli/releaseCommand.d.ts +6 -0
- package/dist/cli/releaseCommand.js +91 -0
- package/dist/cli/runCli.d.ts +7 -1
- package/dist/cli/runCli.js +11 -6
- package/dist/cli/runPublishCommands.js +5 -3
- package/dist/cli/trainCommand.js +73 -20
- package/dist/cli/upCommand.js +5 -1
- package/dist/cli/volumeCommands.d.ts +8 -0
- package/dist/cli/volumeCommands.js +53 -0
- package/dist/compiler/agentNodeMerge.js +3 -2
- package/dist/compiler/buildCompilePlan.js +4 -0
- package/dist/compiler/buildCompilePlanAgent.js +3 -1
- package/dist/compiler/buildCompilePlanTraversal.js +2 -0
- package/dist/compiler/buildCompilePlanTraversalHelpers.d.ts +2 -2
- package/dist/compiler/buildCompilePlanTraversalHelpers.js +4 -3
- package/dist/compiler/buildProject.d.ts +10 -0
- package/dist/compiler/buildProject.js +14 -2
- package/dist/compiler/cleanCompileOutput.d.ts +2 -0
- package/dist/compiler/cleanCompileOutput.js +14 -0
- package/dist/compiler/compilePlanHelpers.js +2 -0
- package/dist/compiler/compileProject.d.ts +10 -0
- package/dist/compiler/compileProject.js +29 -3
- package/dist/compiler/containerArtifactSummaries.js +1 -0
- package/dist/compiler/containerArtifacts.d.ts +4 -4
- package/dist/compiler/containerArtifacts.js +18 -34
- package/dist/compiler/containerArtifactsPlans.js +7 -0
- package/dist/compiler/containerArtifactsTypes.d.ts +7 -0
- package/dist/compiler/containerDaimonBrokerRender.d.ts +3 -1
- package/dist/compiler/containerDaimonBrokerRender.js +6 -3
- package/dist/compiler/containerDaimonGrokTaskClock.d.ts +5 -0
- package/dist/compiler/containerDaimonGrokTaskClock.js +29 -0
- package/dist/compiler/containerDaimonGrokWorkerProvisioning.d.ts +1 -1
- package/dist/compiler/containerDaimonGrokWorkerProvisioning.js +38 -16
- package/dist/compiler/containerDaimonGrokWorkerRender.d.ts +33 -8
- package/dist/compiler/containerDaimonGrokWorkerRender.js +18 -5
- package/dist/compiler/containerDaimonOwnershipGuardRender.d.ts +14 -1
- package/dist/compiler/containerDaimonOwnershipGuardRender.js +15 -2
- package/dist/compiler/containerDaimonUidEntrypointRender.js +6 -12
- package/dist/compiler/containerTargetResources.d.ts +10 -0
- package/dist/compiler/containerTargetResources.js +21 -13
- package/dist/compiler/containerWorkspaceResourceRender.js +4 -1
- package/dist/compiler/index.d.ts +2 -0
- package/dist/compiler/index.js +2 -0
- package/dist/compiler/mcpPlaceholders.d.ts +11 -0
- package/dist/compiler/mcpPlaceholders.js +58 -0
- package/dist/compiler/mcpServerInheritance.d.ts +18 -0
- package/dist/compiler/mcpServerInheritance.js +43 -0
- package/dist/compiler/moltnetArtifactTypes.d.ts +1 -0
- package/dist/compiler/moltnetArtifacts.js +1 -0
- package/dist/compiler/preflightCompilePlan.d.ts +9 -0
- package/dist/compiler/preflightCompilePlan.js +38 -0
- package/dist/compiler/recordingAgentFacts.d.ts +4 -0
- package/dist/compiler/recordingAgentFacts.js +35 -0
- package/dist/compiler/recordingCapability.d.ts +4 -0
- package/dist/compiler/recordingCapability.js +22 -0
- package/dist/compiler/recordingReport.d.ts +4 -0
- package/dist/compiler/recordingReport.js +18 -0
- package/dist/compiler/runProject.d.ts +2 -1
- package/dist/compiler/runProject.js +2 -0
- package/dist/compiler/surfaces.d.ts +1 -2
- package/dist/compiler/surfaces.js +0 -1
- package/dist/compiler/syncProjectAuth.js +2 -1
- package/dist/compiler/training/broker/clockProbe.d.ts +6 -0
- package/dist/compiler/training/broker/clockProbe.js +27 -0
- package/dist/compiler/training/broker/declaration.d.ts +4 -2
- package/dist/compiler/training/broker/declaration.js +4 -2
- package/dist/compiler/training/broker/entrypoint.js +33 -18
- package/dist/compiler/training/broker/evaluatorSecrets.d.ts +2 -0
- package/dist/compiler/training/broker/evaluatorSecrets.js +60 -0
- package/dist/compiler/training/broker/index.d.ts +3 -2
- package/dist/compiler/training/broker/index.js +3 -2
- package/dist/compiler/training/broker/main.js +1 -1
- package/dist/compiler/training/broker/paths.d.ts +96 -56
- package/dist/compiler/training/broker/paths.js +97 -89
- package/dist/compiler/training/broker/processes.d.ts +9 -0
- package/dist/compiler/training/broker/processes.js +4 -1
- package/dist/compiler/training/broker/projection.d.ts +3 -2
- package/dist/compiler/training/broker/projection.js +21 -9
- package/dist/compiler/training/broker/provisioning.d.ts +6 -15
- package/dist/compiler/training/broker/provisioning.js +71 -53
- package/dist/compiler/training/broker/registration.d.ts +3 -1
- package/dist/compiler/training/broker/registration.js +29 -21
- package/dist/compiler/training/broker/reset.d.ts +9 -0
- package/dist/compiler/training/broker/reset.js +71 -0
- package/dist/compiler/training/broker/runtime.d.ts +10 -13
- package/dist/compiler/training/broker/runtime.js +69 -115
- package/dist/compiler/training/broker/supervisor.d.ts +10 -11
- package/dist/compiler/training/broker/supervisor.js +22 -23
- package/dist/compiler/training/broker/taskClock.d.ts +5 -0
- package/dist/compiler/training/broker/taskClock.js +41 -0
- package/dist/compiler/training/compiledAgent.d.ts +21 -0
- package/dist/compiler/training/compiledAgent.js +62 -0
- package/dist/compiler/training/container/contract.d.ts +23 -0
- package/dist/compiler/training/container/contract.js +23 -5
- package/dist/compiler/training/container/evaluators.d.ts +48 -0
- package/dist/compiler/training/container/evaluators.js +50 -0
- package/dist/compiler/training/container/grokWriteback.d.ts +8 -0
- package/dist/compiler/training/container/grokWriteback.js +26 -0
- package/dist/compiler/training/container/index.d.ts +2 -1
- package/dist/compiler/training/container/index.js +2 -1
- package/dist/compiler/training/container/launch.d.ts +3 -0
- package/dist/compiler/training/container/launch.js +119 -28
- package/dist/compiler/training/container/nativeSlots.d.ts +14 -0
- package/dist/compiler/training/container/nativeSlots.js +13 -0
- package/dist/compiler/training/container/outputCopy.d.ts +2 -0
- package/dist/compiler/training/container/outputCopy.js +72 -0
- package/dist/compiler/training/container/outputVolume.d.ts +27 -0
- package/dist/compiler/training/container/outputVolume.js +161 -0
- package/dist/compiler/training/container/prepare.d.ts +3 -1
- package/dist/compiler/training/container/prepare.js +57 -12
- package/dist/compiler/training/container/security.d.ts +5 -3
- package/dist/compiler/training/container/security.js +15 -5
- package/dist/compiler/training/container/subjectIsolation.d.ts +5 -0
- package/dist/compiler/training/container/subjectIsolation.js +15 -0
- package/dist/compiler/training/context.d.ts +8 -0
- package/dist/compiler/training/context.js +12 -12
- package/dist/compiler/training/contract.d.ts +2 -0
- package/dist/compiler/training/contract.js +2 -0
- package/dist/compiler/training/execution.d.ts +417 -0
- package/dist/compiler/training/execution.js +160 -0
- package/dist/compiler/training/index.d.ts +8 -0
- package/dist/compiler/training/index.js +7 -0
- package/dist/compiler/training/moments.d.ts +80 -0
- package/dist/compiler/training/moments.js +118 -0
- package/dist/compiler/training/output.d.ts +2 -0
- package/dist/compiler/training/output.js +20 -0
- package/dist/compiler/training/plan/contract.d.ts +287 -0
- package/dist/compiler/training/plan/contract.js +49 -0
- package/dist/compiler/training/plan/index.d.ts +5 -0
- package/dist/compiler/training/plan/index.js +4 -0
- package/dist/compiler/training/plan/load.d.ts +9 -0
- package/dist/compiler/training/plan/load.js +153 -0
- package/dist/compiler/training/plan/references.d.ts +19 -0
- package/dist/compiler/training/plan/references.js +57 -0
- package/dist/compiler/training/plan/resolve.d.ts +4 -0
- package/dist/compiler/training/plan/resolve.js +73 -0
- package/dist/compiler/training/plan/targets.d.ts +11 -0
- package/dist/compiler/training/plan/targets.js +71 -0
- package/dist/compiler/training/plan/types.d.ts +36 -0
- package/dist/compiler/training/plan/types.js +1 -0
- package/dist/compiler/training/preparation/assets/Dockerfile +13 -2
- package/dist/compiler/training/preparation/assets/package-lock.json +2 -2
- package/dist/compiler/training/preparation/broker.d.ts +3 -3
- package/dist/compiler/training/preparation/broker.js +6 -4
- package/dist/compiler/training/preparation/contract.d.ts +27 -2
- package/dist/compiler/training/preparation/contract.js +23 -4
- package/dist/compiler/training/preparation/files.d.ts +5 -1
- package/dist/compiler/training/preparation/files.js +76 -15
- package/dist/compiler/training/preparation/image.js +9 -4
- package/dist/compiler/training/preparation/inputs.d.ts +1 -0
- package/dist/compiler/training/preparation/inputs.js +18 -10
- package/dist/compiler/training/preparation/prepare.d.ts +5 -0
- package/dist/compiler/training/preparation/prepare.js +66 -29
- package/dist/compiler/training/preparation/scratch.d.ts +6 -17
- package/dist/compiler/training/preparation/scratch.js +89 -23
- package/dist/compiler/training/repair/contract.d.ts +338 -4
- package/dist/compiler/training/repair/contract.js +2 -0
- package/dist/compiler/training/repair/index.d.ts +1 -1
- package/dist/compiler/training/repair/index.js +1 -1
- package/dist/compiler/training/repair/prepare.d.ts +155 -3
- package/dist/compiler/training/repair/prepare.js +6 -2
- package/dist/compiler/training/repair/witness.d.ts +152 -3
- package/dist/compiler/training/repair/witness.js +8 -2
- package/dist/compiler/training/resolution.d.ts +25 -0
- package/dist/compiler/training/resolution.js +69 -0
- package/dist/compiler/training/rewind/contract.d.ts +321 -0
- package/dist/compiler/training/rewind/contract.js +39 -0
- package/dist/compiler/training/rewind/deployedResource.d.ts +22 -0
- package/dist/compiler/training/rewind/deployedResource.js +19 -0
- package/dist/compiler/training/rewind/feed.d.ts +12 -0
- package/dist/compiler/training/rewind/feed.js +182 -0
- package/dist/compiler/training/rewind/feedContent.d.ts +6 -0
- package/dist/compiler/training/rewind/feedContent.js +38 -0
- package/dist/compiler/training/rewind/git.d.ts +8 -0
- package/dist/compiler/training/rewind/git.js +159 -0
- package/dist/compiler/training/rewind/gitArchive.d.ts +2 -0
- package/dist/compiler/training/rewind/gitArchive.js +7 -0
- package/dist/compiler/training/rewind/gitHistory.d.ts +3 -0
- package/dist/compiler/training/rewind/gitHistory.js +55 -0
- package/dist/compiler/training/rewind/gitProcess.d.ts +19 -0
- package/dist/compiler/training/rewind/gitProcess.js +81 -0
- package/dist/compiler/training/rewind/gitTree.d.ts +14 -0
- package/dist/compiler/training/rewind/gitTree.js +90 -0
- package/dist/compiler/training/rewind/history.d.ts +28 -0
- package/dist/compiler/training/rewind/history.js +239 -0
- package/dist/compiler/training/rewind/index.d.ts +18 -0
- package/dist/compiler/training/rewind/index.js +15 -0
- package/dist/compiler/training/rewind/inputs.d.ts +9 -0
- package/dist/compiler/training/rewind/inputs.js +67 -0
- package/dist/compiler/training/rewind/instant.d.ts +4 -0
- package/dist/compiler/training/rewind/instant.js +24 -0
- package/dist/compiler/training/rewind/leakCheck.d.ts +10 -0
- package/dist/compiler/training/rewind/leakCheck.js +56 -0
- package/dist/compiler/training/rewind/leakFeed.d.ts +5 -0
- package/dist/compiler/training/rewind/leakFeed.js +25 -0
- package/dist/compiler/training/rewind/leakFiles.d.ts +10 -0
- package/dist/compiler/training/rewind/leakFiles.js +106 -0
- package/dist/compiler/training/rewind/leakGit.d.ts +3 -0
- package/dist/compiler/training/rewind/leakGit.js +78 -0
- package/dist/compiler/training/rewind/leakGitPayload.d.ts +4 -0
- package/dist/compiler/training/rewind/leakGitPayload.js +57 -0
- package/dist/compiler/training/rewind/leakInputs.d.ts +7 -0
- package/dist/compiler/training/rewind/leakInputs.js +135 -0
- package/dist/compiler/training/rewind/leakLayout.d.ts +2 -0
- package/dist/compiler/training/rewind/leakLayout.js +17 -0
- package/dist/compiler/training/rewind/leakProvenance.d.ts +3 -0
- package/dist/compiler/training/rewind/leakProvenance.js +88 -0
- package/dist/compiler/training/rewind/materialize.d.ts +136 -0
- package/dist/compiler/training/rewind/materialize.js +212 -0
- package/dist/compiler/training/rewind/memory.d.ts +10 -0
- package/dist/compiler/training/rewind/memory.js +84 -0
- package/dist/compiler/training/rewind/memorySchemas.d.ts +87 -0
- package/dist/compiler/training/rewind/memorySchemas.js +50 -0
- package/dist/compiler/training/rewind/moltnet.d.ts +2 -0
- package/dist/compiler/training/rewind/moltnet.js +54 -0
- package/dist/compiler/training/rewind/moltnetJson.d.ts +13 -0
- package/dist/compiler/training/rewind/moltnetJson.js +180 -0
- package/dist/compiler/training/rewind/moltnetSchemas.d.ts +1226 -0
- package/dist/compiler/training/rewind/moltnetSchemas.js +75 -0
- package/dist/compiler/training/rewind/moltnetSqlite.d.ts +31 -0
- package/dist/compiler/training/rewind/moltnetSqlite.js +192 -0
- package/dist/compiler/training/rewind/moltnetSqliteData.d.ts +10 -0
- package/dist/compiler/training/rewind/moltnetSqliteData.js +113 -0
- package/dist/compiler/training/rewind/recordInput.d.ts +40 -0
- package/dist/compiler/training/rewind/recordInput.js +49 -0
- package/dist/compiler/training/rewind/recordedBundle.d.ts +4 -0
- package/dist/compiler/training/rewind/recordedBundle.js +58 -0
- package/dist/compiler/training/rewind/recordedCommit.d.ts +2 -0
- package/dist/compiler/training/rewind/recordedCommit.js +10 -0
- package/dist/compiler/training/rewind/select.d.ts +18 -0
- package/dist/compiler/training/rewind/select.js +64 -0
- package/dist/compiler/training/rewind/snapshotManifest.d.ts +42 -0
- package/dist/compiler/training/rewind/snapshotManifest.js +134 -0
- package/dist/compiler/training/rewind/taskClock.d.ts +12 -0
- package/dist/compiler/training/rewind/taskClock.js +40 -0
- package/dist/compiler/training/rewind/timestamps.d.ts +4 -0
- package/dist/compiler/training/rewind/timestamps.js +26 -0
- package/dist/compiler/training/rewind/types.d.ts +43 -0
- package/dist/compiler/training/rewind/types.js +1 -0
- package/dist/compiler/training/rewind/volumes.d.ts +12 -0
- package/dist/compiler/training/rewind/volumes.js +58 -0
- package/dist/compiler/training/selection.d.ts +7 -0
- package/dist/compiler/training/selection.js +15 -0
- package/dist/compiler/types.d.ts +6 -3
- package/dist/compiler/upProject.d.ts +1 -0
- package/dist/compiler/upProject.js +21 -5
- package/dist/compiler/workspaceBundleArtifacts.d.ts +16 -1
- package/dist/compiler/workspaceBundleArtifacts.js +69 -20
- package/dist/compiler/workspaceBundleCache.d.ts +23 -0
- package/dist/compiler/workspaceBundleCache.js +79 -0
- package/dist/compiler/workspaceBundleDependencies.d.ts +21 -0
- package/dist/compiler/workspaceBundleDependencies.js +122 -0
- package/dist/compiler/workspaceBundleFiles.d.ts +51 -0
- package/dist/compiler/workspaceBundleFiles.js +251 -0
- package/dist/compiler/workspaceBundleGenerated.d.ts +25 -0
- package/dist/compiler/workspaceBundleGenerated.js +103 -0
- package/dist/compiler/workspaceBundleGit.d.ts +64 -0
- package/dist/compiler/workspaceBundleGit.js +154 -0
- package/dist/compiler/workspaceBundleKey.d.ts +34 -0
- package/dist/compiler/workspaceBundleKey.js +17 -0
- package/dist/compiler/workspaceBundleResolve.d.ts +56 -0
- package/dist/compiler/workspaceBundleResolve.js +144 -0
- package/dist/compiler/workspaceBundleRun.d.ts +41 -0
- package/dist/compiler/workspaceBundleRun.js +79 -0
- package/dist/compiler/workspaceBundleTar.d.ts +47 -0
- package/dist/compiler/workspaceBundleTar.js +140 -0
- package/dist/compiler/workspaceBundleTree.d.ts +15 -0
- package/dist/compiler/workspaceBundleTree.js +58 -0
- package/dist/compiler/workspaceGitFetch.d.ts +41 -0
- package/dist/compiler/workspaceGitFetch.js +155 -0
- package/dist/compiler/workspaceGitPins.d.ts +33 -0
- package/dist/compiler/workspaceGitPins.js +127 -0
- package/dist/compiler/workspaceResources.d.ts +5 -1
- package/dist/compiler/workspaceResources.js +32 -5
- package/dist/deployment/activation.d.ts +3 -0
- package/dist/deployment/activation.js +12 -0
- package/dist/deployment/canaryCutover.d.ts +1 -1
- package/dist/deployment/candidateRecording.d.ts +8 -0
- package/dist/deployment/candidateRecording.js +22 -0
- package/dist/deployment/dockerManager.d.ts +5 -3
- package/dist/deployment/dockerManager.js +12 -2
- package/dist/deployment/downDeployment.d.ts +2 -0
- package/dist/deployment/downDeployment.js +6 -2
- package/dist/deployment/homeStore.d.ts +9 -1
- package/dist/deployment/homeStore.js +16 -53
- package/dist/deployment/index.d.ts +6 -0
- package/dist/deployment/index.js +6 -0
- package/dist/deployment/pidLock.d.ts +25 -0
- package/dist/deployment/pidLock.js +81 -0
- package/dist/deployment/record.d.ts +85 -1
- package/dist/deployment/record.js +11 -5
- package/dist/deployment/recordPublication.d.ts +15 -0
- package/dist/deployment/recordPublication.js +76 -0
- package/dist/deployment/recordedMemoryRetention.d.ts +18 -0
- package/dist/deployment/recordedMemoryRetention.js +27 -0
- package/dist/deployment/resourceLog.d.ts +140 -0
- package/dist/deployment/resourceLog.js +78 -0
- package/dist/distribution/buildDistributionReport.d.ts +3 -0
- package/dist/distribution/buildDistributionReport.js +5 -0
- package/dist/distribution/consumeImage.d.ts +3 -0
- package/dist/distribution/consumeImage.js +23 -31
- package/dist/distribution/consumeImageLifecycle.d.ts +4 -1
- package/dist/distribution/consumeImageLifecycle.js +14 -3
- package/dist/distribution/consumeImageSupport.d.ts +7 -0
- package/dist/distribution/consumeImageSupport.js +25 -0
- package/dist/distribution/distributionReportSchema.d.ts +47 -0
- package/dist/distribution/distributionReportSchema.js +20 -0
- package/dist/distribution/renderImageInterface.js +1 -1
- package/dist/distribution/types.d.ts +15 -0
- package/dist/filesystem/durableWrite.d.ts +10 -0
- package/dist/filesystem/durableWrite.js +87 -0
- package/dist/filesystem/index.d.ts +2 -0
- package/dist/filesystem/index.js +2 -0
- package/dist/filesystem/retainedJsonl.d.ts +23 -0
- package/dist/filesystem/retainedJsonl.js +123 -0
- package/dist/manifest/commandSchemas.d.ts +5 -0
- package/dist/manifest/commandSchemas.js +5 -0
- package/dist/manifest/executionSchemas.d.ts +20 -0
- package/dist/manifest/executionSchemas.js +13 -3
- package/dist/manifest/index.d.ts +2 -0
- package/dist/manifest/index.js +2 -0
- package/dist/manifest/loadManifest.js +3 -26
- package/dist/manifest/mcpSchemas.d.ts +61 -0
- package/dist/manifest/mcpSchemas.js +57 -11
- package/dist/manifest/memorySchemas.d.ts +1 -1
- package/dist/manifest/memorySchemas.js +1 -1
- package/dist/manifest/parseManifest.d.ts +7 -0
- package/dist/manifest/parseManifest.js +65 -0
- package/dist/manifest/renderSpawnfile.js +4 -1
- package/dist/manifest/renderSpawnfileWorkspace.js +6 -2
- package/dist/manifest/schemas.d.ts +1206 -132
- package/dist/manifest/schemas.js +14 -4
- package/dist/manifest/teamNetworkSchemas.d.ts +1 -1
- package/dist/manifest/trainingDataSchemas.d.ts +357 -0
- package/dist/manifest/trainingDataSchemas.js +31 -0
- package/dist/manifest/trainingSchemas.d.ts +49 -0
- package/dist/manifest/trainingSchemas.js +44 -0
- package/dist/manifest/volumeFeedSchemas.d.ts +52 -0
- package/dist/manifest/volumeFeedSchemas.js +104 -0
- package/dist/manifest/workspaceSchemas.d.ts +182 -5
- package/dist/manifest/workspaceSchemas.js +111 -7
- package/dist/recording/compose.d.ts +3 -0
- package/dist/recording/compose.js +93 -0
- package/dist/recording/deployment.d.ts +15 -0
- package/dist/recording/deployment.js +38 -0
- package/dist/recording/history.d.ts +16 -0
- package/dist/recording/history.js +47 -0
- package/dist/recording/index.d.ts +9 -0
- package/dist/recording/index.js +9 -0
- package/dist/recording/moment.d.ts +61 -0
- package/dist/recording/moment.js +36 -0
- package/dist/recording/parse.d.ts +15 -0
- package/dist/recording/parse.js +74 -0
- package/dist/recording/read.d.ts +8 -0
- package/dist/recording/read.js +61 -0
- package/dist/recording/store.d.ts +10 -0
- package/dist/recording/store.js +94 -0
- package/dist/recording/sync.d.ts +24 -0
- package/dist/recording/sync.js +126 -0
- package/dist/recording/types.d.ts +45 -0
- package/dist/recording/types.js +2 -0
- package/dist/release/drainControl.d.ts +54 -0
- package/dist/release/drainControl.js +137 -0
- package/dist/release/drainPhase.d.ts +37 -0
- package/dist/release/drainPhase.js +161 -0
- package/dist/release/index.d.ts +11 -0
- package/dist/release/index.js +11 -0
- package/dist/release/ledger.d.ts +75 -0
- package/dist/release/ledger.js +111 -0
- package/dist/release/notify.d.ts +58 -0
- package/dist/release/notify.js +126 -0
- package/dist/release/pending.d.ts +35 -0
- package/dist/release/pending.js +60 -0
- package/dist/release/postDeploy.d.ts +32 -0
- package/dist/release/postDeploy.js +62 -0
- package/dist/release/releaseDefaults.d.ts +9 -0
- package/dist/release/releaseDefaults.js +115 -0
- package/dist/release/releaseDocker.d.ts +38 -0
- package/dist/release/releaseDocker.js +94 -0
- package/dist/release/releaseTypes.d.ts +94 -0
- package/dist/release/releaseTypes.js +1 -0
- package/dist/release/runRelease.d.ts +9 -0
- package/dist/release/runRelease.js +218 -0
- package/dist/release/swap.d.ts +23 -0
- package/dist/release/swap.js +55 -0
- package/dist/release/types.d.ts +50 -0
- package/dist/release/types.js +29 -0
- package/dist/report/index.d.ts +1 -0
- package/dist/report/index.js +1 -0
- package/dist/report/recording.d.ts +57 -0
- package/dist/report/recording.js +26 -0
- package/dist/report/types.d.ts +58 -0
- package/dist/runtime/container.js +8 -13
- package/dist/runtime/daimon/adapter.js +15 -4
- package/dist/runtime/daimon/config.d.ts +4 -0
- package/dist/runtime/daimon/config.js +53 -4
- package/dist/runtime/daimon/contract-manifest.json +1 -0
- package/dist/runtime/daimon/contract-manifest.sha256 +1 -0
- package/dist/runtime/daimon/contractManifest.d.ts +59 -8
- package/dist/runtime/daimon/contractManifest.js +26 -10
- package/dist/runtime/daimon/grokTurnLimits.d.ts +25 -0
- package/dist/runtime/daimon/grokTurnLimits.js +51 -0
- package/dist/runtime/daimon/grokWorkerConfigBytes.d.ts +17 -0
- package/dist/runtime/daimon/grokWorkerConfigBytes.js +17 -0
- package/dist/runtime/daimon/grokWorkerContract.d.ts +5 -0
- package/dist/runtime/daimon/grokWorkerContract.js +13 -1
- package/dist/runtime/daimon/recording.d.ts +19 -0
- package/dist/runtime/daimon/recording.js +50 -0
- package/dist/runtime/daimon/scheduleAuthority.d.ts +8 -0
- package/dist/runtime/daimon/scheduleAuthority.js +19 -5
- package/dist/runtime/daimon/wakeMoments.d.ts +15 -0
- package/dist/runtime/daimon/wakeMoments.js +17 -0
- package/dist/runtime/daimonInstall.d.ts +3 -0
- package/dist/runtime/daimonInstall.js +25 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/install.d.ts +2 -0
- package/dist/runtime/install.js +1 -0
- package/dist/runtime/openclaw/adapter.js +3 -0
- package/dist/runtime/pi/adapter.js +3 -0
- package/dist/runtime/pi/containerTargets.js +6 -0
- package/dist/runtime/picoclaw/adapter.js +3 -0
- package/dist/runtime/registry.d.ts +3 -0
- package/dist/runtime/registry.js +13 -1
- package/dist/runtime/types.d.ts +15 -0
- package/dist/runtime/usageLedgerRead.d.ts +6 -0
- package/dist/runtime/usageLedgerRead.js +3 -3
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/recording.d.ts +8 -0
- package/dist/shared/recording.js +25 -0
- package/dist/target/dockerCommandExecutorCore.js +1 -1
- package/dist/target/dockerTargetBinding.js +2 -1
- package/dist/volume/feedLand.d.ts +31 -0
- package/dist/volume/feedLand.js +174 -0
- package/dist/volume/feedLandings.d.ts +45 -0
- package/dist/volume/feedLandings.js +111 -0
- package/dist/volume/feedLayout.d.ts +61 -0
- package/dist/volume/feedLayout.js +234 -0
- package/dist/volume/feedLock.d.ts +7 -0
- package/dist/volume/feedLock.js +131 -0
- package/dist/volume/feedManifest.d.ts +27 -0
- package/dist/volume/feedManifest.js +123 -0
- package/dist/volume/feedPrepare.d.ts +16 -0
- package/dist/volume/feedPrepare.js +228 -0
- package/dist/volume/feedProject.d.ts +39 -0
- package/dist/volume/feedProject.js +92 -0
- package/dist/volume/feedRecord.d.ts +55 -0
- package/dist/volume/feedRecord.js +148 -0
- package/dist/volume/feedRef.d.ts +43 -0
- package/dist/volume/feedRef.js +117 -0
- package/dist/volume/feedRefresh.d.ts +15 -0
- package/dist/volume/feedRefresh.js +187 -0
- package/dist/volume/feedSource.d.ts +66 -0
- package/dist/volume/feedSource.js +104 -0
- package/dist/volume/feedTarget.d.ts +70 -0
- package/dist/volume/feedTarget.js +9 -0
- package/dist/volume/feedTestKit.d.ts +11 -0
- package/dist/volume/feedTestKit.js +45 -0
- package/dist/volume/feedVerify.d.ts +18 -0
- package/dist/volume/feedVerify.js +104 -0
- package/dist/volume/index.d.ts +13 -0
- package/dist/volume/index.js +13 -0
- package/moltnet-releases.json +4 -4
- package/package.json +3 -3
- package/runtimes.yaml +8 -6
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { DeployResources } from "../deployment/index.js";
|
|
2
|
+
import type { FeedLanding } from "../volume/index.js";
|
|
3
|
+
export declare const WAKE_MOMENT_VERSION = "noopolis.daimon.wake-moment.v1";
|
|
4
|
+
export declare const MOMENT_RECORD_VERSION = "spawnfile.moment-record.v1";
|
|
5
|
+
/** The public, data-only row written by Daimon before each turn attempt. */
|
|
6
|
+
export interface WakeMomentRow {
|
|
7
|
+
version: typeof WAKE_MOMENT_VERSION;
|
|
8
|
+
agent_id: string;
|
|
9
|
+
execution_id: string;
|
|
10
|
+
started_at: string;
|
|
11
|
+
deliveries: {
|
|
12
|
+
acceptance_id: string;
|
|
13
|
+
delivery_id: string;
|
|
14
|
+
kind: string;
|
|
15
|
+
occurred_at: string;
|
|
16
|
+
}[];
|
|
17
|
+
snapshots: {
|
|
18
|
+
id: string;
|
|
19
|
+
snapshot: string;
|
|
20
|
+
files: number;
|
|
21
|
+
copied: number;
|
|
22
|
+
linked: number;
|
|
23
|
+
skipped: number;
|
|
24
|
+
}[];
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface MemoryLedgerEntry {
|
|
28
|
+
seq: number;
|
|
29
|
+
createdAt: string;
|
|
30
|
+
}
|
|
31
|
+
export interface MemoryLedgerRead {
|
|
32
|
+
entries: readonly MemoryLedgerEntry[];
|
|
33
|
+
errors?: readonly string[];
|
|
34
|
+
}
|
|
35
|
+
export interface ComposeMomentRecordInput {
|
|
36
|
+
row: WakeMomentRow;
|
|
37
|
+
agentNodeId: string;
|
|
38
|
+
deploymentRecordPath: string;
|
|
39
|
+
feedLandings: Readonly<Record<string, readonly FeedLanding[]>>;
|
|
40
|
+
deployResources: readonly DeployResources[];
|
|
41
|
+
memoryLedgers: Readonly<Record<string, MemoryLedgerRead>>;
|
|
42
|
+
errors?: readonly string[];
|
|
43
|
+
recordedAt: string;
|
|
44
|
+
}
|
|
45
|
+
export type { MomentRecord } from "./moment.js";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The running organization's control plane, reached the way every other
|
|
3
|
+
* Spawnfile probe reaches a container's loopback: an ephemeral helper from the
|
|
4
|
+
* same image inside the target's network namespace. The bearer token goes to
|
|
5
|
+
* curl on stdin (`-H @-`), never on a command line another host process could
|
|
6
|
+
* read.
|
|
7
|
+
*/
|
|
8
|
+
export interface RuntimeControlTarget {
|
|
9
|
+
containerRef: string;
|
|
10
|
+
dockerArgs: readonly string[];
|
|
11
|
+
dockerCommand: string;
|
|
12
|
+
imageRef: string;
|
|
13
|
+
token: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ControlResponse {
|
|
16
|
+
body: string;
|
|
17
|
+
status: number;
|
|
18
|
+
}
|
|
19
|
+
export type ControlCall = (target: RuntimeControlTarget, method: "GET" | "POST", route: string) => Promise<ControlResponse>;
|
|
20
|
+
export declare const CONTROL_CALL_TIMEOUT_MS = 60000;
|
|
21
|
+
export declare const controlHelperArgs: (target: RuntimeControlTarget, method: "GET" | "POST", route: string) => string[];
|
|
22
|
+
export declare const parseHelperOutput: (stdout: string) => ControlResponse;
|
|
23
|
+
export declare const helperControlCall: ControlCall;
|
|
24
|
+
export type DrainState = "draining" | "drained" | null;
|
|
25
|
+
export interface Availability {
|
|
26
|
+
drain: DrainState;
|
|
27
|
+
state: string;
|
|
28
|
+
}
|
|
29
|
+
/** The parts of `noopolis.daimon.work-availability.v1` a release reads. */
|
|
30
|
+
export declare const parseAvailability: (response: ControlResponse, route: string) => Availability;
|
|
31
|
+
export declare const requestDrain: (target: RuntimeControlTarget, call?: ControlCall) => Promise<Availability>;
|
|
32
|
+
export declare const requestResume: (target: RuntimeControlTarget, call?: ControlCall) => Promise<Availability>;
|
|
33
|
+
export interface WaitForDrainedOptions {
|
|
34
|
+
call?: ControlCall;
|
|
35
|
+
now?: () => number;
|
|
36
|
+
pollMs: number;
|
|
37
|
+
signal?: AbortSignal;
|
|
38
|
+
sleep?: (ms: number) => Promise<void>;
|
|
39
|
+
timeoutMs: number;
|
|
40
|
+
}
|
|
41
|
+
export type DrainWait = {
|
|
42
|
+
drained: true;
|
|
43
|
+
waitedMs: number;
|
|
44
|
+
} | {
|
|
45
|
+
drained: false;
|
|
46
|
+
reason: "interrupted" | "timeout";
|
|
47
|
+
waitedMs: number;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Polls until no turn is running or admitted ("drained"), or the bound runs
|
|
51
|
+
* out. Running turns are never touched: on timeout the caller abandons the
|
|
52
|
+
* release and resumes; nothing here can stop a turn.
|
|
53
|
+
*/
|
|
54
|
+
export declare const waitForDrained: (target: RuntimeControlTarget, options: WaitForDrainedOptions) => Promise<DrainWait>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { DAIMON_CONTROL_PORT } from "../runtime/daimon/config.js";
|
|
3
|
+
import { ReleaseError } from "./types.js";
|
|
4
|
+
export const CONTROL_CALL_TIMEOUT_MS = 60_000;
|
|
5
|
+
const MAX_CONTROL_OUTPUT_BYTES = 1024 * 1024;
|
|
6
|
+
export const controlHelperArgs = (target, method, route) => [
|
|
7
|
+
...target.dockerArgs,
|
|
8
|
+
"run", "--rm", "-i", "--pull", "never",
|
|
9
|
+
"--network", `container:${target.containerRef}`,
|
|
10
|
+
"--entrypoint", "curl",
|
|
11
|
+
target.imageRef,
|
|
12
|
+
"-sS", "--max-time", "15", "-X", method, "-H", "@-",
|
|
13
|
+
"--output", "-", "--write-out", "\\n%{http_code}",
|
|
14
|
+
`http://127.0.0.1:${DAIMON_CONTROL_PORT}${route}`
|
|
15
|
+
];
|
|
16
|
+
const redact = (text, token) => (token ? text.split(token).join("[redacted]") : text).replace(/\s+/gu, " ").slice(0, 240);
|
|
17
|
+
export const parseHelperOutput = (stdout) => {
|
|
18
|
+
const match = /\n(\d{3})$/u.exec(stdout);
|
|
19
|
+
if (!match || match.index === undefined) {
|
|
20
|
+
throw new ReleaseError("drain-failed", "the runtime control helper returned no HTTP status");
|
|
21
|
+
}
|
|
22
|
+
return { body: stdout.slice(0, match.index), status: Number(match[1]) };
|
|
23
|
+
};
|
|
24
|
+
export const helperControlCall = (target, method, route) => new Promise((resolve, reject) => {
|
|
25
|
+
const child = spawn(target.dockerCommand, controlHelperArgs(target, method, route), {
|
|
26
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
27
|
+
});
|
|
28
|
+
const out = [];
|
|
29
|
+
let bytes = 0;
|
|
30
|
+
let stderr = "";
|
|
31
|
+
let settled = false;
|
|
32
|
+
const finish = (error, value) => {
|
|
33
|
+
if (settled)
|
|
34
|
+
return;
|
|
35
|
+
settled = true;
|
|
36
|
+
clearTimeout(timer);
|
|
37
|
+
if (error)
|
|
38
|
+
reject(error);
|
|
39
|
+
else
|
|
40
|
+
resolve(value);
|
|
41
|
+
};
|
|
42
|
+
const timer = setTimeout(() => {
|
|
43
|
+
child.kill("SIGKILL");
|
|
44
|
+
finish(new ReleaseError("drain-failed", `runtime control ${method} ${route} timed out`));
|
|
45
|
+
}, CONTROL_CALL_TIMEOUT_MS);
|
|
46
|
+
child.stdout.on("data", (chunk) => {
|
|
47
|
+
bytes += chunk.length;
|
|
48
|
+
if (bytes > MAX_CONTROL_OUTPUT_BYTES) {
|
|
49
|
+
child.kill("SIGKILL");
|
|
50
|
+
finish(new ReleaseError("drain-failed", `runtime control ${method} ${route} answered more than the output bound`));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
out.push(chunk);
|
|
54
|
+
});
|
|
55
|
+
child.stderr.on("data", (chunk) => { stderr = `${stderr}${chunk.toString("utf8")}`.slice(-2000); });
|
|
56
|
+
child.once("error", (error) => finish(new ReleaseError("drain-failed", `cannot start the runtime control helper: ${error.message}`)));
|
|
57
|
+
child.once("close", (code) => {
|
|
58
|
+
if (code !== 0) {
|
|
59
|
+
finish(new ReleaseError("drain-failed", `runtime control ${method} ${route} failed (exit ${code ?? "signal"}): ${redact(stderr, target.token)}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
finish(null, parseHelperOutput(Buffer.concat(out).toString("utf8")));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
finish(error);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
child.stdin.on("error", () => undefined);
|
|
70
|
+
child.stdin.end(`Authorization: Bearer ${target.token}\n`);
|
|
71
|
+
});
|
|
72
|
+
/** The parts of `noopolis.daimon.work-availability.v1` a release reads. */
|
|
73
|
+
export const parseAvailability = (response, route) => {
|
|
74
|
+
if (response.status === 404) {
|
|
75
|
+
throw new ReleaseError("drain-failed", `the running runtime has no ${route} control route; it predates reversible drain, so it cannot be released without killing turns`);
|
|
76
|
+
}
|
|
77
|
+
if (response.status === 401) {
|
|
78
|
+
throw new ReleaseError("drain-failed", `the runtime refused the control token on ${route}; SPAWNFILE_DAIMON_CONTROL_TOKEN in the env file must be the running deployment's token`);
|
|
79
|
+
}
|
|
80
|
+
if (response.status !== 200) {
|
|
81
|
+
throw new ReleaseError("drain-failed", `runtime control ${route} answered HTTP ${response.status}`);
|
|
82
|
+
}
|
|
83
|
+
let parsed;
|
|
84
|
+
try {
|
|
85
|
+
parsed = JSON.parse(response.body);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
throw new ReleaseError("drain-failed", `runtime control ${route} answered invalid JSON`);
|
|
89
|
+
}
|
|
90
|
+
const document = parsed;
|
|
91
|
+
if (document?.version !== "noopolis.daimon.work-availability.v1" || typeof document.state !== "string") {
|
|
92
|
+
throw new ReleaseError("drain-failed", `runtime control ${route} answered something other than a work-availability document`);
|
|
93
|
+
}
|
|
94
|
+
const drain = document.drain === undefined ? null : document.drain.state;
|
|
95
|
+
if (drain !== null && drain !== "draining" && drain !== "drained") {
|
|
96
|
+
throw new ReleaseError("drain-failed", `runtime control ${route} reported an unknown drain state`);
|
|
97
|
+
}
|
|
98
|
+
return { drain, state: document.state };
|
|
99
|
+
};
|
|
100
|
+
export const requestDrain = async (target, call = helperControlCall) => {
|
|
101
|
+
const availability = parseAvailability(await call(target, "POST", "/v2/drain"), "/v2/drain");
|
|
102
|
+
if (availability.drain === null) {
|
|
103
|
+
throw new ReleaseError("drain-failed", "the runtime accepted /v2/drain but does not report a drain; refusing to deploy over turns it may still admit");
|
|
104
|
+
}
|
|
105
|
+
return availability;
|
|
106
|
+
};
|
|
107
|
+
export const requestResume = async (target, call = helperControlCall) => {
|
|
108
|
+
const availability = parseAvailability(await call(target, "POST", "/v2/resume"), "/v2/resume");
|
|
109
|
+
if (availability.drain !== null) {
|
|
110
|
+
throw new ReleaseError("resume-failed", "the runtime answered /v2/resume but still reports a drain");
|
|
111
|
+
}
|
|
112
|
+
return availability;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Polls until no turn is running or admitted ("drained"), or the bound runs
|
|
116
|
+
* out. Running turns are never touched: on timeout the caller abandons the
|
|
117
|
+
* release and resumes; nothing here can stop a turn.
|
|
118
|
+
*/
|
|
119
|
+
export const waitForDrained = async (target, options) => {
|
|
120
|
+
const now = options.now ?? Date.now;
|
|
121
|
+
const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
122
|
+
const started = now();
|
|
123
|
+
for (;;) {
|
|
124
|
+
if (options.signal?.aborted)
|
|
125
|
+
return { drained: false, reason: "interrupted", waitedMs: now() - started };
|
|
126
|
+
const availability = parseAvailability(await (options.call ?? helperControlCall)(target, "GET", "/v2/availability"), "/v2/availability");
|
|
127
|
+
if (availability.drain === null) {
|
|
128
|
+
throw new ReleaseError("drain-failed", "the runtime stopped reporting the drain while a release was waiting on it (did it restart?)");
|
|
129
|
+
}
|
|
130
|
+
if (availability.drain === "drained")
|
|
131
|
+
return { drained: true, waitedMs: now() - started };
|
|
132
|
+
const remaining = options.timeoutMs - (now() - started);
|
|
133
|
+
if (remaining <= 0)
|
|
134
|
+
return { drained: false, reason: "timeout", waitedMs: now() - started };
|
|
135
|
+
await sleep(Math.min(options.pollMs, remaining));
|
|
136
|
+
}
|
|
137
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { DockerDeploymentTarget } from "../deployment/index.js";
|
|
2
|
+
import type { RuntimeControlTarget } from "./drainControl.js";
|
|
3
|
+
import { type ReleasePaths } from "./ledger.js";
|
|
4
|
+
import type { RunningUnit } from "./releaseDocker.js";
|
|
5
|
+
import { type ReleaseDependencies, type ReleaseRequest } from "./releaseTypes.js";
|
|
6
|
+
export declare const RELEASE_DRAIN_MARKER_VERSION: "spawnfile.release-drain.v1";
|
|
7
|
+
/** Same precedence `up` applies: a process value overrides the env file. */
|
|
8
|
+
export declare const resolveControlToken: (request: ReleaseRequest) => string | null;
|
|
9
|
+
export declare const controlTargetFor: (request: ReleaseRequest, containerRef: string, imageRef: string) => RuntimeControlTarget;
|
|
10
|
+
/** The same request, addressed at exactly this Docker daemon. */
|
|
11
|
+
export declare const requestForTarget: (request: ReleaseRequest, target: DockerDeploymentTarget) => ReleaseRequest;
|
|
12
|
+
export declare const clearDrainMarker: (paths: ReleasePaths) => Promise<void>;
|
|
13
|
+
export declare const recoverInterruptedDrain: (request: ReleaseRequest, deps: ReleaseDependencies, paths: ReleasePaths) => Promise<boolean>;
|
|
14
|
+
export type DrainOutcome = {
|
|
15
|
+
drainMs: number;
|
|
16
|
+
kind: "drained";
|
|
17
|
+
} | {
|
|
18
|
+
drainMs: number;
|
|
19
|
+
kind: "skipped";
|
|
20
|
+
} | {
|
|
21
|
+
drainMs: number;
|
|
22
|
+
kind: "timeout";
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Admission pauses, queued wakes stay queued, running turns finish. On a
|
|
26
|
+
* timeout or interruption the release is abandoned and admission resumed:
|
|
27
|
+
* nothing in this module, or anything it calls, stops a turn.
|
|
28
|
+
*/
|
|
29
|
+
export declare const drainForRelease: (request: ReleaseRequest, deps: ReleaseDependencies, paths: ReleasePaths, unit: RunningUnit | null) => Promise<DrainOutcome>;
|
|
30
|
+
/** Resumes a container this release drained, keeping the marker if that fails. */
|
|
31
|
+
export declare const resumeDrained: (request: ReleaseRequest, deps: ReleaseDependencies, paths: ReleasePaths, unit: RunningUnit) => Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* After a failed deploy, whatever container now holds the name is either the
|
|
34
|
+
* restored previous one (restarted, so it admits) or the original, still
|
|
35
|
+
* drained because the deploy failed before touching it. Resume either way.
|
|
36
|
+
*/
|
|
37
|
+
export declare const resumeAfterFailedDeploy: (request: ReleaseRequest, deps: ReleaseDependencies, paths: ReleasePaths, containerName: string) => Promise<void>;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { readFile, rm } from "node:fs/promises";
|
|
2
|
+
import { resolveDockerBaseArgs } from "../distribution/index.js";
|
|
3
|
+
import { writeJsonAtomic } from "./ledger.js";
|
|
4
|
+
import { DAIMON_CONTROL_TOKEN_ENV } from "./releaseTypes.js";
|
|
5
|
+
import { ReleaseError } from "./types.js";
|
|
6
|
+
export const RELEASE_DRAIN_MARKER_VERSION = "spawnfile.release-drain.v1";
|
|
7
|
+
/** Same precedence `up` applies: a process value overrides the env file. */
|
|
8
|
+
export const resolveControlToken = (request) => {
|
|
9
|
+
const fromProcess = process.env[DAIMON_CONTROL_TOKEN_ENV];
|
|
10
|
+
if (typeof fromProcess === "string" && fromProcess.length > 0)
|
|
11
|
+
return fromProcess;
|
|
12
|
+
const fromFile = request.envFileEnv[DAIMON_CONTROL_TOKEN_ENV];
|
|
13
|
+
return typeof fromFile === "string" && fromFile.length > 0 ? fromFile : null;
|
|
14
|
+
};
|
|
15
|
+
export const controlTargetFor = (request, containerRef, imageRef) => {
|
|
16
|
+
const token = resolveControlToken(request);
|
|
17
|
+
if (token === null) {
|
|
18
|
+
throw new ReleaseError("blocked", `${DAIMON_CONTROL_TOKEN_ENV} is not set in the env file or environment; a drained release needs the running deployment's control token`);
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
containerRef,
|
|
22
|
+
dockerArgs: resolveDockerBaseArgs({ ...(request.dockerContext ? { dockerContext: request.dockerContext } : {}), ...(request.dockerHost ? { dockerHost: request.dockerHost } : {}) }),
|
|
23
|
+
dockerCommand: request.dockerCommand,
|
|
24
|
+
imageRef,
|
|
25
|
+
token
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Written BEFORE the drain request and removed only once admission is open
|
|
30
|
+
* again, so a release killed in between (a unit timeout, a reboot) leaves
|
|
31
|
+
* evidence that the organization may still be refusing work. Every later run
|
|
32
|
+
* resumes it first, even when it has nothing to release.
|
|
33
|
+
*/
|
|
34
|
+
const writeMarker = async (request, paths, unit) => {
|
|
35
|
+
const target = request.target;
|
|
36
|
+
if (target === undefined)
|
|
37
|
+
throw new ReleaseError("blocked", "the release has no pinned Docker target to record with its drain");
|
|
38
|
+
const marker = { container: unit.id, image: unit.imageId, since: new Date().toISOString(), target, version: RELEASE_DRAIN_MARKER_VERSION };
|
|
39
|
+
await writeJsonAtomic(paths.drainMarker, marker);
|
|
40
|
+
};
|
|
41
|
+
/** The same request, addressed at exactly this Docker daemon. */
|
|
42
|
+
export const requestForTarget = (request, target) => {
|
|
43
|
+
const { dockerContext: _context, dockerHost: _host, ...rest } = request;
|
|
44
|
+
if (target.kind === "host")
|
|
45
|
+
return { ...rest, dockerHost: target.value, target };
|
|
46
|
+
return { ...rest, dockerContext: target.kind === "context" ? target.name : target.context, target };
|
|
47
|
+
};
|
|
48
|
+
export const clearDrainMarker = async (paths) => {
|
|
49
|
+
await rm(paths.drainMarker, { force: true });
|
|
50
|
+
};
|
|
51
|
+
export const recoverInterruptedDrain = async (request, deps, paths) => {
|
|
52
|
+
let marker;
|
|
53
|
+
try {
|
|
54
|
+
marker = JSON.parse(await readFile(paths.drainMarker, "utf8"));
|
|
55
|
+
if (typeof marker.container !== "string" || typeof marker.target?.kind !== "string")
|
|
56
|
+
throw new Error("shape");
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error.code === "ENOENT")
|
|
60
|
+
return false;
|
|
61
|
+
throw new ReleaseError("resume-failed", `the drain marker ${paths.drainMarker} is unreadable; the organization may still be drained`);
|
|
62
|
+
}
|
|
63
|
+
// Recovery talks to the Docker daemon the drain went out through, whatever this run was invoked with,
|
|
64
|
+
// and refuses (keeping the marker) when that name now resolves to a different endpoint.
|
|
65
|
+
const target = requestForTarget(request, marker.target);
|
|
66
|
+
let unit;
|
|
67
|
+
try {
|
|
68
|
+
await deps.verifyTarget(target, marker.target);
|
|
69
|
+
unit = await deps.inspectUnit(target, marker.container);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throw new ReleaseError("resume-failed", `an earlier release may have left the organization drained and its container cannot be inspected: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
if (unit === null || !unit.running) {
|
|
75
|
+
// A drain lives in the runtime process: a container that is gone or stopped admits again when it next starts.
|
|
76
|
+
await clearDrainMarker(paths);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
await deps.requestResume(controlTargetFor(target, unit.id, unit.imageId));
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
throw new ReleaseError("resume-failed", `an earlier release left the organization drained and resuming it failed: ${error.message}`);
|
|
84
|
+
}
|
|
85
|
+
await clearDrainMarker(paths);
|
|
86
|
+
request.log("release: resumed admission left drained by an interrupted release");
|
|
87
|
+
return true;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Admission pauses, queued wakes stay queued, running turns finish. On a
|
|
91
|
+
* timeout or interruption the release is abandoned and admission resumed:
|
|
92
|
+
* nothing in this module, or anything it calls, stops a turn.
|
|
93
|
+
*/
|
|
94
|
+
export const drainForRelease = async (request, deps, paths, unit) => {
|
|
95
|
+
if (!request.drain) {
|
|
96
|
+
request.log("release: --no-drain: deploying without draining; in-flight turns will be killed");
|
|
97
|
+
return { drainMs: 0, kind: "skipped" };
|
|
98
|
+
}
|
|
99
|
+
if (unit === null || !unit.running) {
|
|
100
|
+
request.log("release: no running container, so no turn can be in flight; nothing to drain");
|
|
101
|
+
return { drainMs: 0, kind: "skipped" };
|
|
102
|
+
}
|
|
103
|
+
const target = controlTargetFor(request, unit.id, unit.imageId);
|
|
104
|
+
const started = Date.now();
|
|
105
|
+
await writeMarker(request, paths, unit);
|
|
106
|
+
try {
|
|
107
|
+
await deps.requestDrain(target);
|
|
108
|
+
request.log(`release: admission paused; waiting up to ${Math.round(request.drainTimeoutMs / 1000)}s for running turns to finish`);
|
|
109
|
+
const waited = await deps.waitForDrained(target, {
|
|
110
|
+
pollMs: request.drainPollMs,
|
|
111
|
+
...(request.signal ? { signal: request.signal } : {}),
|
|
112
|
+
timeoutMs: request.drainTimeoutMs
|
|
113
|
+
});
|
|
114
|
+
if (waited.drained && !request.signal?.aborted) {
|
|
115
|
+
request.log(`release: drained after ${Math.round(waited.waitedMs / 1000)}s`);
|
|
116
|
+
return { drainMs: Date.now() - started, kind: "drained" };
|
|
117
|
+
}
|
|
118
|
+
await resumeOrThrow(request, deps, paths, target);
|
|
119
|
+
if (waited.drained || waited.reason === "interrupted")
|
|
120
|
+
throw new ReleaseError("interrupted", "the release was interrupted while waiting for running turns; admission resumed, nothing deployed");
|
|
121
|
+
return { drainMs: Date.now() - started, kind: "timeout" };
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (error instanceof ReleaseError && (error.reason === "interrupted" || error.reason === "resume-failed"))
|
|
125
|
+
throw error;
|
|
126
|
+
await resumeOrThrow(request, deps, paths, target);
|
|
127
|
+
throw error instanceof ReleaseError ? error : new ReleaseError("drain-failed", error.message);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
/** Resumes a container this release drained, keeping the marker if that fails. */
|
|
131
|
+
export const resumeDrained = async (request, deps, paths, unit) => resumeOrThrow(request, deps, paths, controlTargetFor(request, unit.id, unit.imageId));
|
|
132
|
+
const resumeOrThrow = async (request, deps, paths, target) => {
|
|
133
|
+
try {
|
|
134
|
+
await deps.requestResume(target);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
throw new ReleaseError("resume-failed", `admission could not be resumed after abandoning the release; the organization is refusing new work: ${error.message}`);
|
|
138
|
+
}
|
|
139
|
+
await clearDrainMarker(paths);
|
|
140
|
+
request.log("release: admission resumed");
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* After a failed deploy, whatever container now holds the name is either the
|
|
144
|
+
* restored previous one (restarted, so it admits) or the original, still
|
|
145
|
+
* drained because the deploy failed before touching it. Resume either way.
|
|
146
|
+
*/
|
|
147
|
+
export const resumeAfterFailedDeploy = async (request, deps, paths, containerName) => {
|
|
148
|
+
let unit;
|
|
149
|
+
try {
|
|
150
|
+
unit = await deps.inspectUnit(request, containerName);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
// Not "absent": the marker stays, so the next run resumes it.
|
|
154
|
+
throw new ReleaseError("resume-failed", `the deploy failed and the container cannot be inspected to resume admission: ${error.message}`);
|
|
155
|
+
}
|
|
156
|
+
if (unit === null || !unit.running) {
|
|
157
|
+
await clearDrainMarker(paths);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
await resumeOrThrow(request, deps, paths, controlTargetFor(request, unit.id, unit.imageId));
|
|
161
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./drainControl.js";
|
|
2
|
+
export * from "./drainPhase.js";
|
|
3
|
+
export * from "./ledger.js";
|
|
4
|
+
export * from "./notify.js";
|
|
5
|
+
export * from "./pending.js";
|
|
6
|
+
export * from "./postDeploy.js";
|
|
7
|
+
export * from "./releaseDefaults.js";
|
|
8
|
+
export * from "./releaseDocker.js";
|
|
9
|
+
export * from "./releaseTypes.js";
|
|
10
|
+
export * from "./runRelease.js";
|
|
11
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./drainControl.js";
|
|
2
|
+
export * from "./drainPhase.js";
|
|
3
|
+
export * from "./ledger.js";
|
|
4
|
+
export * from "./notify.js";
|
|
5
|
+
export * from "./pending.js";
|
|
6
|
+
export * from "./postDeploy.js";
|
|
7
|
+
export * from "./releaseDefaults.js";
|
|
8
|
+
export * from "./releaseDocker.js";
|
|
9
|
+
export * from "./releaseTypes.js";
|
|
10
|
+
export * from "./runRelease.js";
|
|
11
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type PidLockHooks } from "../deployment/index.js";
|
|
3
|
+
export declare const RELEASE_LEDGER_VERSION: "spawnfile.release-ledger.v1";
|
|
4
|
+
export declare const RELEASE_LOG_VERSION: "spawnfile.release-log.v1";
|
|
5
|
+
declare const timings: z.ZodObject<{
|
|
6
|
+
build_ms: z.ZodNullable<z.ZodNumber>;
|
|
7
|
+
compile_ms: z.ZodNumber;
|
|
8
|
+
deploy_ms: z.ZodNullable<z.ZodNumber>;
|
|
9
|
+
drain_ms: z.ZodNullable<z.ZodNumber>;
|
|
10
|
+
total_ms: z.ZodNumber;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
/** What is running, as the last successful release recorded it. */
|
|
13
|
+
export declare const releaseLedgerSchema: z.ZodObject<{
|
|
14
|
+
compile_fingerprint: z.ZodString;
|
|
15
|
+
deployment: z.ZodString;
|
|
16
|
+
identity: z.ZodString;
|
|
17
|
+
image_id: z.ZodString;
|
|
18
|
+
image_tag: z.ZodString;
|
|
19
|
+
previous_image_tag: z.ZodNullable<z.ZodString>;
|
|
20
|
+
released_at: z.ZodString;
|
|
21
|
+
timings: z.ZodObject<{
|
|
22
|
+
build_ms: z.ZodNullable<z.ZodNumber>;
|
|
23
|
+
compile_ms: z.ZodNumber;
|
|
24
|
+
deploy_ms: z.ZodNullable<z.ZodNumber>;
|
|
25
|
+
drain_ms: z.ZodNullable<z.ZodNumber>;
|
|
26
|
+
total_ms: z.ZodNumber;
|
|
27
|
+
}, z.core.$strict>;
|
|
28
|
+
version: z.ZodLiteral<"spawnfile.release-ledger.v1">;
|
|
29
|
+
}, z.core.$strict>;
|
|
30
|
+
export type ReleaseLedger = z.infer<typeof releaseLedgerSchema>;
|
|
31
|
+
export interface ReleaseLogEntry {
|
|
32
|
+
at: string;
|
|
33
|
+
deployment: string;
|
|
34
|
+
identity: string | null;
|
|
35
|
+
image_tag?: string;
|
|
36
|
+
message?: string;
|
|
37
|
+
notified?: {
|
|
38
|
+
channel: "command" | "webhook" | "none";
|
|
39
|
+
delivered: boolean;
|
|
40
|
+
error?: string;
|
|
41
|
+
};
|
|
42
|
+
outcome: "deferred" | "failed" | "notified" | "released";
|
|
43
|
+
reason?: string;
|
|
44
|
+
timings?: z.infer<typeof timings>;
|
|
45
|
+
version: typeof RELEASE_LOG_VERSION;
|
|
46
|
+
}
|
|
47
|
+
export interface ReleasePaths {
|
|
48
|
+
directory: string;
|
|
49
|
+
drainMarker: string;
|
|
50
|
+
ledger: string;
|
|
51
|
+
lock: string;
|
|
52
|
+
log: string;
|
|
53
|
+
pending: string;
|
|
54
|
+
/** Present from just before a post-deploy command runs until the ledger records its success. */
|
|
55
|
+
postDeploy: string;
|
|
56
|
+
}
|
|
57
|
+
export declare const resolveReleasePaths: (deployment: string, root?: string) => ReleasePaths;
|
|
58
|
+
export declare const ensureReleaseDirectory: (paths: ReleasePaths) => Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* A missing ledger means nothing has been released into this deployment yet,
|
|
61
|
+
* which is a full release. A ledger that exists and cannot be read means this
|
|
62
|
+
* command does not know what is running, and it must not deploy: it could not
|
|
63
|
+
* write the record the next run depends on either. Unreadable is a refusal,
|
|
64
|
+
* never an assumed-stale.
|
|
65
|
+
*/
|
|
66
|
+
export declare const readReleaseLedger: (file: string) => Promise<ReleaseLedger | null>;
|
|
67
|
+
/** Anything but "definitely absent" counts as present: an unknown marker never reads as done. */
|
|
68
|
+
export declare const postDeployPending: (file: string) => Promise<boolean>;
|
|
69
|
+
/** Same-directory temp file + rename, so a reader sees the old record or the new one. */
|
|
70
|
+
export declare const writeJsonAtomic: (file: string, value: unknown) => Promise<void>;
|
|
71
|
+
export declare const writeReleaseLedger: (file: string, ledger: ReleaseLedger) => Promise<void>;
|
|
72
|
+
export declare const appendReleaseLog: (file: string, entry: Omit<ReleaseLogEntry, "version">) => Promise<void>;
|
|
73
|
+
/** One release per deployment at a time; see `acquirePidLock` for the guarantees and the residual. */
|
|
74
|
+
export declare const acquireReleaseLock: (paths: ReleasePaths, hooks?: PidLockHooks) => Promise<() => Promise<void>>;
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { appendFile, chmod, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { resolveSpawnfileHome } from "../auth/index.js";
|
|
6
|
+
import { acquirePidLock, normalizeDeploymentName, PidLockBusyError } from "../deployment/index.js";
|
|
7
|
+
import { ReleaseError } from "./types.js";
|
|
8
|
+
export const RELEASE_LEDGER_VERSION = "spawnfile.release-ledger.v1";
|
|
9
|
+
export const RELEASE_LOG_VERSION = "spawnfile.release-log.v1";
|
|
10
|
+
const digest = z.string().regex(/^sha256:[a-f0-9]{64}$/u);
|
|
11
|
+
const timings = z.object({
|
|
12
|
+
build_ms: z.number().nonnegative().nullable(),
|
|
13
|
+
compile_ms: z.number().nonnegative(),
|
|
14
|
+
deploy_ms: z.number().nonnegative().nullable(),
|
|
15
|
+
drain_ms: z.number().nonnegative().nullable(),
|
|
16
|
+
total_ms: z.number().nonnegative()
|
|
17
|
+
}).strict();
|
|
18
|
+
/** What is running, as the last successful release recorded it. */
|
|
19
|
+
export const releaseLedgerSchema = z.object({
|
|
20
|
+
compile_fingerprint: z.string().min(1),
|
|
21
|
+
deployment: z.string().min(1),
|
|
22
|
+
identity: digest,
|
|
23
|
+
image_id: z.string().min(1),
|
|
24
|
+
image_tag: z.string().min(1),
|
|
25
|
+
previous_image_tag: z.string().min(1).nullable(),
|
|
26
|
+
released_at: z.string().min(1),
|
|
27
|
+
timings,
|
|
28
|
+
version: z.literal(RELEASE_LEDGER_VERSION)
|
|
29
|
+
}).strict();
|
|
30
|
+
export const resolveReleasePaths = (deployment, root) => {
|
|
31
|
+
const directory = path.join(root ?? path.join(resolveSpawnfileHome(), "releases"), normalizeDeploymentName(deployment));
|
|
32
|
+
return {
|
|
33
|
+
directory,
|
|
34
|
+
drainMarker: path.join(directory, "drain.json"),
|
|
35
|
+
ledger: path.join(directory, "ledger.json"),
|
|
36
|
+
lock: path.join(directory, ".lock"),
|
|
37
|
+
log: path.join(directory, "log.jsonl"),
|
|
38
|
+
pending: path.join(directory, "pending.json"),
|
|
39
|
+
postDeploy: path.join(directory, "post-deploy.json")
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
export const ensureReleaseDirectory = async (paths) => {
|
|
43
|
+
await mkdir(paths.directory, { mode: 0o700, recursive: true });
|
|
44
|
+
await chmod(paths.directory, 0o700);
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* A missing ledger means nothing has been released into this deployment yet,
|
|
48
|
+
* which is a full release. A ledger that exists and cannot be read means this
|
|
49
|
+
* command does not know what is running, and it must not deploy: it could not
|
|
50
|
+
* write the record the next run depends on either. Unreadable is a refusal,
|
|
51
|
+
* never an assumed-stale.
|
|
52
|
+
*/
|
|
53
|
+
export const readReleaseLedger = async (file) => {
|
|
54
|
+
let source;
|
|
55
|
+
try {
|
|
56
|
+
source = await readFile(file, "utf8");
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error.code === "ENOENT")
|
|
60
|
+
return null;
|
|
61
|
+
throw new ReleaseError("blocked", `cannot read the release ledger ${file}: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
return releaseLedgerSchema.parse(JSON.parse(source));
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
throw new ReleaseError("blocked", `the release ledger ${file} is not a ${RELEASE_LEDGER_VERSION} record; refusing to release against a ledger this command cannot read`);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
/** Anything but "definitely absent" counts as present: an unknown marker never reads as done. */
|
|
71
|
+
export const postDeployPending = async (file) => {
|
|
72
|
+
try {
|
|
73
|
+
await stat(file);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return error.code !== "ENOENT";
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
/** Same-directory temp file + rename, so a reader sees the old record or the new one. */
|
|
81
|
+
export const writeJsonAtomic = async (file, value) => {
|
|
82
|
+
const scratch = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${randomUUID()}.tmp`);
|
|
83
|
+
try {
|
|
84
|
+
await writeFile(scratch, `${JSON.stringify(value, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
85
|
+
await rename(scratch, file);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
await rm(scratch, { force: true }).catch(() => undefined);
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
export const writeReleaseLedger = async (file, ledger) => {
|
|
93
|
+
await writeJsonAtomic(file, releaseLedgerSchema.parse(ledger));
|
|
94
|
+
};
|
|
95
|
+
export const appendReleaseLog = async (file, entry) => {
|
|
96
|
+
await appendFile(file, `${JSON.stringify({ ...entry, version: RELEASE_LOG_VERSION })}\n`, { encoding: "utf8", mode: 0o600 });
|
|
97
|
+
};
|
|
98
|
+
/** One release per deployment at a time; see `acquirePidLock` for the guarantees and the residual. */
|
|
99
|
+
export const acquireReleaseLock = async (paths, hooks = {}) => {
|
|
100
|
+
try {
|
|
101
|
+
return await acquirePidLock(paths.lock, hooks);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (error instanceof PidLockBusyError) {
|
|
105
|
+
throw new ReleaseError("blocked", error.ownerPid === null
|
|
106
|
+
? "another release of this deployment took the lock first"
|
|
107
|
+
: `another release of this deployment is running (pid ${error.ownerPid})`);
|
|
108
|
+
}
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
};
|