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.
Files changed (546) hide show
  1. package/README.md +60 -159
  2. package/dist/audit/checksShared.d.ts +1 -1
  3. package/dist/auth/importers.d.ts +10 -0
  4. package/dist/auth/importers.js +29 -4
  5. package/dist/auth/index.d.ts +2 -0
  6. package/dist/auth/index.js +1 -0
  7. package/dist/auth/paths.d.ts +1 -1
  8. package/dist/auth/profileStore.js +9 -9
  9. package/dist/auth/targetSecretSourceFsIdentity.d.ts +40 -0
  10. package/dist/auth/targetSecretSourceFsIdentity.js +42 -0
  11. package/dist/auth/targetSecretSourceFsPublish.d.ts +2 -0
  12. package/dist/auth/targetSecretSourceFsPublish.js +3 -1
  13. package/dist/auth/targetSecretSourceFsPublishImmutable.d.ts +2 -1
  14. package/dist/auth/targetSecretSourceFsPublishImmutable.js +104 -71
  15. package/dist/auth/targetSecretSourceFsRead.js +5 -14
  16. package/dist/auth/trainingAuth.d.ts +21 -0
  17. package/dist/auth/trainingAuth.js +86 -0
  18. package/dist/auth/types.d.ts +1 -1
  19. package/dist/cli/authCommands.d.ts +1 -1
  20. package/dist/cli/authCommands.js +9 -0
  21. package/dist/cli/canaryCutoverCommand.d.ts +2 -0
  22. package/dist/cli/canaryCutoverCommand.js +30 -0
  23. package/dist/cli/capabilitiesCommand.d.ts +3 -0
  24. package/dist/cli/capabilitiesCommand.js +12 -0
  25. package/dist/cli/capabilitiesReceipt.d.ts +56 -0
  26. package/dist/cli/capabilitiesReceipt.js +56 -0
  27. package/dist/cli/compileBuildCommands.js +13 -0
  28. package/dist/cli/composedLifecycleContractSet.d.ts +21 -0
  29. package/dist/cli/composedLifecycleContractSet.js +204 -0
  30. package/dist/cli/devCommands.js +2 -0
  31. package/dist/cli/downCommand.js +19 -8
  32. package/dist/cli/evidenceExportHelperCommand.d.ts +13 -0
  33. package/dist/cli/evidenceExportHelperCommand.js +51 -0
  34. package/dist/cli/index.js +12 -3
  35. package/dist/cli/lifecycleMachine.d.ts +7 -1
  36. package/dist/cli/lifecycleMachine.js +2 -2
  37. package/dist/cli/paideiaDelegation.d.ts +17 -0
  38. package/dist/cli/paideiaDelegation.js +210 -0
  39. package/dist/cli/paideiaSupervisor.d.ts +17 -0
  40. package/dist/cli/paideiaSupervisor.js +48 -0
  41. package/dist/cli/productStateCloneCommand.d.ts +2 -0
  42. package/dist/cli/productStateCloneCommand.js +34 -0
  43. package/dist/cli/runCli.d.ts +13 -3
  44. package/dist/cli/runCli.js +37 -9
  45. package/dist/cli/targetCommands.d.ts +4 -7
  46. package/dist/cli/targetCommands.js +16 -46
  47. package/dist/cli/targetConfigPreparedPlan.d.ts +3 -0
  48. package/dist/cli/targetConfigPreparedPlan.js +74 -0
  49. package/dist/cli/targetConfigResolver.d.ts +3 -0
  50. package/dist/cli/targetConfigResolver.js +111 -0
  51. package/dist/cli/targetConfigResolverCommand.d.ts +6 -0
  52. package/dist/cli/targetConfigResolverCommand.js +43 -0
  53. package/dist/cli/targetConfigResolverContracts.d.ts +56 -0
  54. package/dist/cli/targetConfigResolverContracts.js +30 -0
  55. package/dist/cli/targetConfigResolverDocker.d.ts +4 -0
  56. package/dist/cli/targetConfigResolverDocker.js +47 -0
  57. package/dist/cli/targetConfigResolverValidation.d.ts +10 -0
  58. package/dist/cli/targetConfigResolverValidation.js +94 -0
  59. package/dist/cli/targetDefaultAuthorities.d.ts +2 -0
  60. package/dist/cli/targetDefaultAuthorities.js +3 -0
  61. package/dist/cli/targetDefaultConfig.d.ts +7 -0
  62. package/dist/cli/targetDefaultConfig.js +22 -5
  63. package/dist/cli/targetDefaultConfigStdin.js +7 -1
  64. package/dist/cli/targetDefaultHandlerFactory.js +23 -2
  65. package/dist/cli/targetDefaultHandlers.d.ts +2 -2
  66. package/dist/cli/targetProductionCommands.d.ts +5 -0
  67. package/dist/cli/targetProductionCommands.js +45 -0
  68. package/dist/cli/trainCommand.d.ts +3 -0
  69. package/dist/cli/trainCommand.js +61 -0
  70. package/dist/cli/upCommand.js +35 -6
  71. package/dist/cli/upLifecycleRecovery.d.ts +2 -2
  72. package/dist/cli/upLifecycleRecovery.js +188 -44
  73. package/dist/cli/usageCommand.d.ts +40 -0
  74. package/dist/cli/usageCommand.js +284 -0
  75. package/dist/cli/usageCommandLive.d.ts +84 -0
  76. package/dist/cli/usageCommandLive.js +95 -0
  77. package/dist/cli/usageDeploymentSource.d.ts +9 -0
  78. package/dist/cli/usageDeploymentSource.js +22 -0
  79. package/dist/cli/workspaceResourceMigrationCommand.d.ts +8 -0
  80. package/dist/cli/workspaceResourceMigrationCommand.js +36 -0
  81. package/dist/compiler/buildCompilePlanTeams.d.ts +1 -1
  82. package/dist/compiler/buildCompilePlanTeams.js +6 -1
  83. package/dist/compiler/compilePlanHelpers.js +3 -0
  84. package/dist/compiler/compileProject.d.ts +2 -0
  85. package/dist/compiler/compileProject.js +5 -1
  86. package/dist/compiler/compileProjectSupport.js +9 -0
  87. package/dist/compiler/containerArtifacts.d.ts +3 -1
  88. package/dist/compiler/containerArtifacts.js +92 -40
  89. package/dist/compiler/containerArtifactsPlans.d.ts +1 -1
  90. package/dist/compiler/containerArtifactsPlans.js +24 -7
  91. package/dist/compiler/containerArtifactsRender.d.ts +2 -1
  92. package/dist/compiler/containerArtifactsRender.js +91 -58
  93. package/dist/compiler/containerArtifactsTypes.d.ts +8 -0
  94. package/dist/compiler/containerBackedMountRender.d.ts +56 -0
  95. package/dist/compiler/containerBackedMountRender.js +101 -0
  96. package/dist/compiler/containerDaimonBrokerRender.d.ts +112 -0
  97. package/dist/compiler/containerDaimonBrokerRender.js +191 -0
  98. package/dist/compiler/containerDaimonGrokWorkerProvisioning.d.ts +38 -0
  99. package/dist/compiler/containerDaimonGrokWorkerProvisioning.js +108 -0
  100. package/dist/compiler/containerDaimonGrokWorkerRender.d.ts +172 -0
  101. package/dist/compiler/containerDaimonGrokWorkerRender.js +268 -0
  102. package/dist/compiler/containerDaimonOwnershipGuardRender.d.ts +12 -0
  103. package/dist/compiler/containerDaimonOwnershipGuardRender.js +191 -0
  104. package/dist/compiler/containerDaimonUidEntrypointRender.d.ts +24 -0
  105. package/dist/compiler/containerDaimonUidEntrypointRender.js +297 -0
  106. package/dist/compiler/containerEntrypointRender.d.ts +7 -0
  107. package/dist/compiler/containerEntrypointRender.js +28 -22
  108. package/dist/compiler/containerPersistentMounts.d.ts +30 -0
  109. package/dist/compiler/containerPersistentMounts.js +66 -0
  110. package/dist/compiler/containerReadinessPaths.d.ts +1 -0
  111. package/dist/compiler/containerReadinessPaths.js +1 -0
  112. package/dist/compiler/containerRuntimeLinkMaterializer.d.ts +2 -0
  113. package/dist/compiler/containerRuntimeLinkMaterializer.js +12 -0
  114. package/dist/compiler/containerRuntimeReadinessRender.d.ts +7 -0
  115. package/dist/compiler/containerRuntimeReadinessRender.js +40 -0
  116. package/dist/compiler/containerStateOwnershipRender.d.ts +4 -0
  117. package/dist/compiler/containerStateOwnershipRender.js +75 -0
  118. package/dist/compiler/containerTargetResources.d.ts +22 -1
  119. package/dist/compiler/containerTargetResources.js +94 -2
  120. package/dist/compiler/containerVolumeBootstrap.d.ts +3 -0
  121. package/dist/compiler/containerVolumeBootstrap.js +3 -0
  122. package/dist/compiler/containerWorkspaceResourceRender.js +49 -1
  123. package/dist/compiler/daimonTelemetryArtifacts.d.ts +2 -2
  124. package/dist/compiler/daimonTelemetryArtifacts.js +5 -10
  125. package/dist/compiler/deploymentLineage.d.ts +40 -0
  126. package/dist/compiler/deploymentLineage.js +50 -0
  127. package/dist/compiler/index.d.ts +3 -0
  128. package/dist/compiler/index.js +3 -0
  129. package/dist/compiler/initProject.d.ts +2 -0
  130. package/dist/compiler/initProject.js +39 -1
  131. package/dist/compiler/localMoltnetAuthority.d.ts +64 -0
  132. package/dist/compiler/localMoltnetAuthority.js +177 -0
  133. package/dist/compiler/memoryArtifacts.d.ts +1 -1
  134. package/dist/compiler/memoryArtifacts.js +108 -32
  135. package/dist/compiler/modelEnv.d.ts +5 -0
  136. package/dist/compiler/modelEnv.js +8 -2
  137. package/dist/compiler/moltnetArtifactPaths.d.ts +17 -12
  138. package/dist/compiler/moltnetArtifactPaths.js +17 -15
  139. package/dist/compiler/moltnetArtifactTypes.d.ts +10 -0
  140. package/dist/compiler/moltnetArtifacts.d.ts +1 -1
  141. package/dist/compiler/moltnetArtifacts.js +52 -12
  142. package/dist/compiler/moltnetBinaries.d.ts +16 -4
  143. package/dist/compiler/moltnetBinaries.js +46 -6
  144. package/dist/compiler/moltnetClientConfig.js +1 -1
  145. package/dist/compiler/moltnetConfigLowering.d.ts +3 -1
  146. package/dist/compiler/moltnetConfigLowering.js +30 -6
  147. package/dist/compiler/moltnetNodeConfig.js +1 -1
  148. package/dist/compiler/moltnetReleaseAuthority.d.ts +24 -1
  149. package/dist/compiler/moltnetReleaseAuthority.js +33 -4
  150. package/dist/compiler/moltnetRepresentativeResolution.d.ts +1 -1
  151. package/dist/compiler/moltnetRepresentativeResolution.js +2 -2
  152. package/dist/compiler/moltnetResolution.js +9 -15
  153. package/dist/compiler/moltnetRoomMemberships.js +20 -5
  154. package/dist/compiler/moltnetRoomPolicyCompatibility.d.ts +1 -1
  155. package/dist/compiler/moltnetRoomPolicyCompatibility.js +3 -1
  156. package/dist/compiler/moltnetRuntimeConfig.d.ts +1 -1
  157. package/dist/compiler/moltnetRuntimeConfig.js +22 -2
  158. package/dist/compiler/moltnetServerPlans.js +19 -0
  159. package/dist/compiler/organizationExternalParticipants.d.ts +3 -0
  160. package/dist/compiler/organizationExternalParticipants.js +176 -0
  161. package/dist/compiler/organizationIdentity.d.ts +3 -9
  162. package/dist/compiler/organizationIdentity.js +37 -270
  163. package/dist/compiler/organizationIdentityGraph.d.ts +15 -0
  164. package/dist/compiler/organizationIdentityGraph.js +115 -0
  165. package/dist/compiler/organizationReadyEvidence.d.ts +8 -0
  166. package/dist/compiler/organizationReadyEvidence.js +23 -3
  167. package/dist/compiler/runProject.d.ts +4 -2
  168. package/dist/compiler/runProject.js +32 -6
  169. package/dist/compiler/runProjectAuth.d.ts +3 -1
  170. package/dist/compiler/runProjectAuth.js +19 -3
  171. package/dist/compiler/runProjectDaimonCodexDocker.d.ts +3 -0
  172. package/dist/compiler/runProjectDaimonCodexDocker.js +36 -0
  173. package/dist/compiler/runProjectDocker.d.ts +8 -4
  174. package/dist/compiler/runProjectDocker.js +43 -18
  175. package/dist/compiler/runProjectDockerDaimonGuards.d.ts +15 -0
  176. package/dist/compiler/runProjectDockerDaimonGuards.js +122 -0
  177. package/dist/compiler/runProjectDockerReservation.d.ts +3 -0
  178. package/dist/compiler/runProjectDockerReservation.js +118 -0
  179. package/dist/compiler/runProjectLifecycle.js +1 -1
  180. package/dist/compiler/training/broker/declaration.d.ts +45 -0
  181. package/dist/compiler/training/broker/declaration.js +55 -0
  182. package/dist/compiler/training/broker/entrypoint.d.ts +39 -0
  183. package/dist/compiler/training/broker/entrypoint.js +111 -0
  184. package/dist/compiler/training/broker/index.d.ts +10 -0
  185. package/dist/compiler/training/broker/index.js +10 -0
  186. package/dist/compiler/training/broker/main.d.ts +2 -0
  187. package/dist/compiler/training/broker/main.js +14 -0
  188. package/dist/compiler/training/broker/paths.d.ts +196 -0
  189. package/dist/compiler/training/broker/paths.js +241 -0
  190. package/dist/compiler/training/broker/processes.d.ts +42 -0
  191. package/dist/compiler/training/broker/processes.js +99 -0
  192. package/dist/compiler/training/broker/projection.d.ts +51 -0
  193. package/dist/compiler/training/broker/projection.js +76 -0
  194. package/dist/compiler/training/broker/provisioning.d.ts +87 -0
  195. package/dist/compiler/training/broker/provisioning.js +213 -0
  196. package/dist/compiler/training/broker/receipt.d.ts +77 -0
  197. package/dist/compiler/training/broker/receipt.js +110 -0
  198. package/dist/compiler/training/broker/registration.d.ts +41 -0
  199. package/dist/compiler/training/broker/registration.js +102 -0
  200. package/dist/compiler/training/broker/runtime.d.ts +25 -0
  201. package/dist/compiler/training/broker/runtime.js +173 -0
  202. package/dist/compiler/training/broker/seccompRoutes.d.ts +31 -0
  203. package/dist/compiler/training/broker/seccompRoutes.js +36 -0
  204. package/dist/compiler/training/broker/supervisor.d.ts +109 -0
  205. package/dist/compiler/training/broker/supervisor.js +173 -0
  206. package/dist/compiler/training/container/contract.d.ts +31 -0
  207. package/dist/compiler/training/container/contract.js +33 -0
  208. package/dist/compiler/training/container/index.d.ts +4 -0
  209. package/dist/compiler/training/container/index.js +2 -0
  210. package/dist/compiler/training/container/launch.d.ts +18 -0
  211. package/dist/compiler/training/container/launch.js +173 -0
  212. package/dist/compiler/training/container/prepare.d.ts +8 -0
  213. package/dist/compiler/training/container/prepare.js +80 -0
  214. package/dist/compiler/training/container/process.d.ts +14 -0
  215. package/dist/compiler/training/container/process.js +70 -0
  216. package/dist/compiler/training/container/security.d.ts +36 -0
  217. package/dist/compiler/training/container/security.js +58 -0
  218. package/dist/compiler/training/context.d.ts +7 -0
  219. package/dist/compiler/training/context.js +93 -0
  220. package/dist/compiler/training/contract.d.ts +142 -0
  221. package/dist/compiler/training/contract.js +34 -0
  222. package/dist/compiler/training/index.d.ts +4 -0
  223. package/dist/compiler/training/index.js +3 -0
  224. package/dist/compiler/training/preparation/assets/Dockerfile +98 -0
  225. package/dist/compiler/training/preparation/assets/package-lock.json +3787 -0
  226. package/dist/compiler/training/preparation/broker.d.ts +21 -0
  227. package/dist/compiler/training/preparation/broker.js +43 -0
  228. package/dist/compiler/training/preparation/contextModes.d.ts +12 -0
  229. package/dist/compiler/training/preparation/contextModes.js +38 -0
  230. package/dist/compiler/training/preparation/contract.d.ts +182 -0
  231. package/dist/compiler/training/preparation/contract.js +94 -0
  232. package/dist/compiler/training/preparation/copyAssets.d.ts +1 -0
  233. package/dist/compiler/training/preparation/copyAssets.js +8 -0
  234. package/dist/compiler/training/preparation/daimonParent.d.ts +42 -0
  235. package/dist/compiler/training/preparation/daimonParent.js +114 -0
  236. package/dist/compiler/training/preparation/files.d.ts +25 -0
  237. package/dist/compiler/training/preparation/files.js +85 -0
  238. package/dist/compiler/training/preparation/image.d.ts +28 -0
  239. package/dist/compiler/training/preparation/image.js +102 -0
  240. package/dist/compiler/training/preparation/index.d.ts +3 -0
  241. package/dist/compiler/training/preparation/index.js +2 -0
  242. package/dist/compiler/training/preparation/inputs.d.ts +23 -0
  243. package/dist/compiler/training/preparation/inputs.js +93 -0
  244. package/dist/compiler/training/preparation/prepare.d.ts +33 -0
  245. package/dist/compiler/training/preparation/prepare.js +186 -0
  246. package/dist/compiler/training/preparation/scratch.d.ts +19 -0
  247. package/dist/compiler/training/preparation/scratch.js +48 -0
  248. package/dist/compiler/training/repair/compatibility.d.ts +11 -0
  249. package/dist/compiler/training/repair/compatibility.js +34 -0
  250. package/dist/compiler/training/repair/contract.d.ts +506 -0
  251. package/dist/compiler/training/repair/contract.js +31 -0
  252. package/dist/compiler/training/repair/index.d.ts +3 -0
  253. package/dist/compiler/training/repair/index.js +3 -0
  254. package/dist/compiler/training/repair/prepare.d.ts +230 -0
  255. package/dist/compiler/training/repair/prepare.js +74 -0
  256. package/dist/compiler/training/repair/witness.d.ts +203 -0
  257. package/dist/compiler/training/repair/witness.js +42 -0
  258. package/dist/compiler/types.d.ts +2 -0
  259. package/dist/compiler/upProject.d.ts +26 -1
  260. package/dist/compiler/upProject.js +117 -178
  261. package/dist/compiler/upProjectHandoff.d.ts +62 -0
  262. package/dist/compiler/upProjectHandoff.js +204 -0
  263. package/dist/compiler/upReceipt.js +49 -4
  264. package/dist/compiler/updateProjectModels.js +1 -0
  265. package/dist/compiler/view/types.d.ts +1 -1
  266. package/dist/compiler/workspaceBundleArtifacts.d.ts +3 -0
  267. package/dist/compiler/workspaceBundleArtifacts.js +106 -0
  268. package/dist/compiler/workspaceResources.d.ts +4 -1
  269. package/dist/compiler/workspaceResources.js +12 -7
  270. package/dist/deployment/artifactsExportPlan.js +51 -1
  271. package/dist/deployment/canaryCutover.d.ts +42 -0
  272. package/dist/deployment/canaryCutover.js +140 -0
  273. package/dist/deployment/candidateDiagnostics.d.ts +15 -0
  274. package/dist/deployment/candidateDiagnostics.js +90 -0
  275. package/dist/deployment/dockerLogs.js +4 -1
  276. package/dist/deployment/dockerManager.js +9 -3
  277. package/dist/deployment/dockerProbeGateway.d.ts +17 -0
  278. package/dist/deployment/dockerProbeGateway.js +17 -2
  279. package/dist/deployment/downDeployment.d.ts +5 -0
  280. package/dist/deployment/downDeployment.js +109 -8
  281. package/dist/deployment/downReceiptTypes.d.ts +17 -2
  282. package/dist/deployment/downReceiptTypes.js +17 -2
  283. package/dist/deployment/index.d.ts +4 -0
  284. package/dist/deployment/index.js +4 -0
  285. package/dist/deployment/lifecycleCompletionContracts.d.ts +66 -0
  286. package/dist/deployment/lifecycleCompletionContracts.js +34 -0
  287. package/dist/deployment/lifecycleCompletionPaths.d.ts +4 -1
  288. package/dist/deployment/lifecycleCompletionPaths.js +3 -0
  289. package/dist/deployment/lifecycleCompletionPublication.d.ts +2 -0
  290. package/dist/deployment/lifecycleCompletionPublication.js +18 -4
  291. package/dist/deployment/lifecycleCompletionStore.d.ts +1 -1
  292. package/dist/deployment/lifecycleCompletionStore.js +7 -7
  293. package/dist/deployment/lifecycleUpRecords.d.ts +11 -0
  294. package/dist/deployment/lifecycleUpRecords.js +127 -0
  295. package/dist/deployment/native/AGENTS.md +11 -0
  296. package/dist/deployment/native/rename-noreplace-arm64 +0 -0
  297. package/dist/deployment/native/rename-noreplace-arm64.provenance.json +1 -0
  298. package/dist/deployment/native/rename-noreplace-x64 +0 -0
  299. package/dist/deployment/native/rename-noreplace-x64.provenance.json +1 -0
  300. package/dist/deployment/noReplaceActivation.d.ts +17 -0
  301. package/dist/deployment/noReplaceActivation.js +50 -0
  302. package/dist/deployment/organizationHandoffAuthorityFsBudget.d.ts +110 -0
  303. package/dist/deployment/organizationHandoffAuthorityFsBudget.js +156 -0
  304. package/dist/deployment/organizationHandoffAuthorityFsClient.js +82 -27
  305. package/dist/deployment/organizationHandoffAuthorityFsElection.d.ts +21 -0
  306. package/dist/deployment/organizationHandoffAuthorityFsElection.js +57 -0
  307. package/dist/deployment/organizationHandoffAuthorityFsWorker.js +254 -108
  308. package/dist/deployment/organizationHandoffAuthorityFsWorkerStall.fixture.d.ts +1 -0
  309. package/dist/deployment/organizationHandoffAuthorityFsWorkerStall.fixture.js +16 -0
  310. package/dist/deployment/organizationHandoffAuthorityStore.js +14 -7
  311. package/dist/deployment/organizationHandoffAuthorityTypes.d.ts +2 -2
  312. package/dist/deployment/organizationHandoffAuthorityTypes.js +2 -1
  313. package/dist/deployment/organizationReady.d.ts +2 -0
  314. package/dist/deployment/organizationReady.js +36 -4
  315. package/dist/deployment/productStateClone.d.ts +43 -0
  316. package/dist/deployment/productStateClone.js +319 -0
  317. package/dist/deployment/upLifecycleRecoveryState.d.ts +23 -0
  318. package/dist/deployment/upLifecycleRecoveryState.js +12 -0
  319. package/dist/deployment/upReceiptTypes.d.ts +32 -6
  320. package/dist/deployment/upReceiptTypes.js +29 -2
  321. package/dist/deployment/workspaceResourceMigration.d.ts +55 -0
  322. package/dist/deployment/workspaceResourceMigration.js +315 -0
  323. package/dist/dev/project.d.ts +7 -0
  324. package/dist/dev/project.js +9 -0
  325. package/dist/distribution/consumeImage.d.ts +2 -0
  326. package/dist/distribution/consumeImage.js +98 -73
  327. package/dist/distribution/consumeImageDaimonDocker.d.ts +3 -0
  328. package/dist/distribution/consumeImageDaimonDocker.js +45 -0
  329. package/dist/distribution/consumeImageLifecycle.d.ts +17 -0
  330. package/dist/distribution/consumeImageLifecycle.js +231 -0
  331. package/dist/distribution/consumeImageSupport.d.ts +12 -0
  332. package/dist/distribution/consumeImageSupport.js +15 -0
  333. package/dist/distribution/distributionReportSchema.d.ts +11 -1
  334. package/dist/distribution/distributionReportSchema.js +15 -2
  335. package/dist/distribution/dockerRunner.d.ts +6 -1
  336. package/dist/distribution/dockerRunner.js +31 -4
  337. package/dist/distribution/imageRuntimeAuth.d.ts +9 -1
  338. package/dist/distribution/imageRuntimeAuth.js +70 -1
  339. package/dist/distribution/index.d.ts +1 -1
  340. package/dist/distribution/index.js +1 -1
  341. package/dist/distribution/preflight.js +5 -1
  342. package/dist/distribution/types.d.ts +14 -1
  343. package/dist/distribution/types.js +3 -0
  344. package/dist/evidenceExportHelper/boundedExecutor.d.ts +3 -0
  345. package/dist/evidenceExportHelper/boundedExecutor.js +33 -0
  346. package/dist/evidenceExportHelper/helperProgram.mjs +156 -0
  347. package/dist/evidenceExportHelper/index.d.ts +4 -0
  348. package/dist/evidenceExportHelper/index.js +4 -0
  349. package/dist/evidenceExportHelper/preparedAuthority.d.ts +64 -0
  350. package/dist/evidenceExportHelper/preparedAuthority.js +228 -0
  351. package/dist/evidenceExportHelper/preparedBuilder.d.ts +10 -0
  352. package/dist/evidenceExportHelper/preparedBuilder.js +183 -0
  353. package/dist/evidenceExportHelper/preparedBuilderTypes.d.ts +19 -0
  354. package/dist/evidenceExportHelper/preparedBuilderTypes.js +1 -0
  355. package/dist/evidenceExportHelper/recipe.d.ts +7 -0
  356. package/dist/evidenceExportHelper/recipe.js +83 -0
  357. package/dist/filesystem/io.d.ts +2 -0
  358. package/dist/filesystem/io.js +16 -1
  359. package/dist/manifest/executionSchemas.d.ts +27 -0
  360. package/dist/manifest/executionSchemas.js +21 -2
  361. package/dist/manifest/mcpSchemas.d.ts +1 -0
  362. package/dist/manifest/mcpSchemas.js +3 -0
  363. package/dist/manifest/renderSpawnfileNetworks.js +11 -0
  364. package/dist/manifest/renderSpawnfileWorkspace.js +5 -0
  365. package/dist/manifest/scheduleSchemas.d.ts +3 -1
  366. package/dist/manifest/scheduleSchemas.js +91 -5
  367. package/dist/manifest/schemas.d.ts +237 -12
  368. package/dist/manifest/schemas.js +5 -2
  369. package/dist/manifest/teamNetworkAccessSchemas.d.ts +5 -1
  370. package/dist/manifest/teamNetworkAccessSchemas.js +11 -0
  371. package/dist/manifest/teamNetworkAuthSchemas.d.ts +1 -1
  372. package/dist/manifest/teamNetworkSchemas.d.ts +17 -3
  373. package/dist/manifest/teamNetworkSchemas.js +19 -1
  374. package/dist/manifest/teamNetworkServerSchemas.d.ts +8 -3
  375. package/dist/manifest/teamNetworkServerSchemas.js +18 -2
  376. package/dist/manifest/workspaceSchemas.d.ts +16 -0
  377. package/dist/manifest/workspaceSchemas.js +11 -0
  378. package/dist/report/types.d.ts +34 -4
  379. package/dist/runtime/common.d.ts +28 -1
  380. package/dist/runtime/common.js +33 -3
  381. package/dist/runtime/container.js +49 -33
  382. package/dist/runtime/daimon/adapter.d.ts +14 -0
  383. package/dist/runtime/daimon/adapter.js +243 -0
  384. package/dist/runtime/daimon/attention.d.ts +8 -0
  385. package/dist/runtime/daimon/attention.js +32 -0
  386. package/dist/runtime/daimon/config.d.ts +25 -0
  387. package/dist/runtime/daimon/config.js +272 -0
  388. package/dist/runtime/daimon/contractManifest.d.ts +295 -0
  389. package/dist/runtime/daimon/contractManifest.js +319 -0
  390. package/dist/runtime/daimon/grokModel.d.ts +19 -0
  391. package/dist/runtime/daimon/grokModel.js +33 -0
  392. package/dist/runtime/daimon/grokWorkerConfigBytes.d.ts +26 -0
  393. package/dist/runtime/daimon/grokWorkerConfigBytes.js +36 -0
  394. package/dist/runtime/daimon/grokWorkerContract.d.ts +22 -0
  395. package/dist/runtime/daimon/grokWorkerContract.js +48 -0
  396. package/dist/runtime/daimon/memory.d.ts +57 -0
  397. package/dist/runtime/daimon/memory.js +125 -0
  398. package/dist/runtime/daimon/runAuth.d.ts +51 -0
  399. package/dist/runtime/daimon/runAuth.js +279 -0
  400. package/dist/runtime/daimon/runtimeIdentity.d.ts +22 -0
  401. package/dist/runtime/daimon/runtimeIdentity.js +22 -0
  402. package/dist/runtime/daimon/scheduleAuthority.d.ts +3 -0
  403. package/dist/runtime/daimon/scheduleAuthority.js +17 -0
  404. package/dist/runtime/index.d.ts +4 -0
  405. package/dist/runtime/index.js +4 -0
  406. package/dist/runtime/install.d.ts +3 -0
  407. package/dist/runtime/install.js +3 -0
  408. package/dist/runtime/localDaimonAuthority.d.ts +11 -0
  409. package/dist/runtime/localDaimonAuthority.js +77 -0
  410. package/dist/runtime/mnemeMcp.d.ts +13 -0
  411. package/dist/runtime/mnemeMcp.js +26 -0
  412. package/dist/runtime/openclaw/adapter.js +2 -2
  413. package/dist/runtime/pi/adapter.d.ts +0 -1
  414. package/dist/runtime/pi/adapter.js +2 -6
  415. package/dist/runtime/pi/appCliSource.js +1 -1
  416. package/dist/runtime/pi/appScheduleSource.js +6 -2
  417. package/dist/runtime/pi/appTemplateTypes.d.ts +1 -1
  418. package/dist/runtime/picoclaw/adapter.js +2 -2
  419. package/dist/runtime/registry.d.ts +3 -0
  420. package/dist/runtime/registry.js +19 -2
  421. package/dist/runtime/scheduleUtils.d.ts +1 -0
  422. package/dist/runtime/scheduleUtils.js +5 -1
  423. package/dist/runtime/types.d.ts +25 -0
  424. package/dist/runtime/usageLedger.d.ts +158 -0
  425. package/dist/runtime/usageLedger.js +269 -0
  426. package/dist/runtime/usageLedgerRead.d.ts +48 -0
  427. package/dist/runtime/usageLedgerRead.js +102 -0
  428. package/dist/runtime/usageRequestLedger.d.ts +42 -0
  429. package/dist/runtime/usageRequestLedger.js +72 -0
  430. package/dist/shared/daimonCodexDocker.d.ts +32 -0
  431. package/dist/shared/daimonCodexDocker.js +120 -0
  432. package/dist/shared/daimonGrokSeccompProfile.d.ts +10 -0
  433. package/dist/shared/daimonGrokSeccompProfile.js +11 -0
  434. package/dist/shared/index.d.ts +2 -0
  435. package/dist/shared/index.js +2 -0
  436. package/dist/shared/types.d.ts +1 -1
  437. package/dist/shared/volumeNames.d.ts +6 -0
  438. package/dist/shared/volumeNames.js +14 -0
  439. package/dist/target/dockerArtifactsProvider.js +34 -7
  440. package/dist/target/dockerBaseImage.d.ts +2 -0
  441. package/dist/target/dockerBaseImage.js +11 -0
  442. package/dist/target/dockerCommandExecutor.d.ts +11 -0
  443. package/dist/target/dockerCommandExecutor.js +53 -2
  444. package/dist/target/dockerCommandExecutorCore.d.ts +2 -1
  445. package/dist/target/dockerCommandExecutorCore.js +4 -2
  446. package/dist/target/dockerPublicArtifactSnapshot.d.ts +3 -3
  447. package/dist/target/dockerPublicArtifactSnapshot.js +17 -33
  448. package/dist/target/dockerTarget.d.ts +1 -0
  449. package/dist/target/dockerTargetBinding.d.ts +1 -2
  450. package/dist/target/dockerTargetBinding.js +2 -3
  451. package/dist/target/dockerTargetExecFile.d.ts +19 -0
  452. package/dist/target/dockerTargetExecFile.js +194 -0
  453. package/dist/target/dockerWorldServiceProvider.d.ts +4 -0
  454. package/dist/target/dockerWorldServiceProvider.js +13 -1
  455. package/dist/target/evidenceExport.d.ts +3 -1
  456. package/dist/target/evidenceExport.js +12 -3
  457. package/dist/target/evidenceExportOperationsTestKit.js +3 -3
  458. package/dist/target/evidenceExportProvider.d.ts +2 -0
  459. package/dist/target/evidenceExportProvider.js +15 -5
  460. package/dist/target/publicArtifactSnapshot.d.ts +31 -0
  461. package/dist/target/publicArtifactSnapshot.js +37 -3
  462. package/examples/README.md +45 -0
  463. package/examples/agent-with-subagents/AGENTS.md +3 -0
  464. package/examples/agent-with-subagents/Spawnfile +30 -0
  465. package/examples/agent-with-subagents/subagents/critic/AGENTS.md +3 -0
  466. package/examples/agent-with-subagents/subagents/critic/Spawnfile +15 -0
  467. package/examples/agent-with-subagents/subagents/researcher/AGENTS.md +3 -0
  468. package/examples/agent-with-subagents/subagents/researcher/Spawnfile +17 -0
  469. package/examples/daimon-org/Spawnfile +68 -0
  470. package/examples/daimon-org/TEAM.md +4 -0
  471. package/examples/daimon-org/agents/mapper/AGENTS.md +4 -0
  472. package/examples/daimon-org/agents/mapper/Spawnfile +37 -0
  473. package/examples/daimon-org/shared/skills/file-note/SKILL.md +9 -0
  474. package/examples/daimon-org/teams/review/Spawnfile +53 -0
  475. package/examples/daimon-org/teams/review/agents/reviewer/AGENTS.md +4 -0
  476. package/examples/daimon-org/teams/review/agents/reviewer/Spawnfile +37 -0
  477. package/examples/daimon-public-host/AGENTS.md +3 -0
  478. package/examples/daimon-public-host/Spawnfile +21 -0
  479. package/examples/distribution-org/Spawnfile +43 -0
  480. package/examples/distribution-org/TEAM.md +4 -0
  481. package/examples/distribution-org/agents/coordinator/AGENTS.md +1 -0
  482. package/examples/distribution-org/agents/coordinator/Spawnfile +20 -0
  483. package/examples/distribution-org/agents/researcher/AGENTS.md +1 -0
  484. package/examples/distribution-org/agents/researcher/Spawnfile +20 -0
  485. package/examples/jungian-daimon-org/Spawnfile +41 -0
  486. package/examples/jungian-daimon-org/TEAM.md +21 -0
  487. package/examples/jungian-daimon-org/teams/luna/Spawnfile +47 -0
  488. package/examples/jungian-daimon-org/teams/luna/TEAM.md +4 -0
  489. package/examples/jungian-daimon-org/teams/luna/agents/animus/AGENTS.md +10 -0
  490. package/examples/jungian-daimon-org/teams/luna/agents/animus/Spawnfile +33 -0
  491. package/examples/jungian-daimon-org/teams/luna/agents/representative/AGENTS.md +21 -0
  492. package/examples/jungian-daimon-org/teams/luna/agents/representative/Spawnfile +30 -0
  493. package/examples/jungian-daimon-org/teams/luna/agents/shadow/AGENTS.md +10 -0
  494. package/examples/jungian-daimon-org/teams/luna/agents/shadow/Spawnfile +33 -0
  495. package/examples/jungian-daimon-org/teams/selene/Spawnfile +47 -0
  496. package/examples/jungian-daimon-org/teams/selene/TEAM.md +4 -0
  497. package/examples/jungian-daimon-org/teams/selene/agents/animus/AGENTS.md +10 -0
  498. package/examples/jungian-daimon-org/teams/selene/agents/animus/Spawnfile +33 -0
  499. package/examples/jungian-daimon-org/teams/selene/agents/representative/AGENTS.md +19 -0
  500. package/examples/jungian-daimon-org/teams/selene/agents/representative/Spawnfile +33 -0
  501. package/examples/jungian-daimon-org/teams/selene/agents/shadow/AGENTS.md +10 -0
  502. package/examples/jungian-daimon-org/teams/selene/agents/shadow/Spawnfile +30 -0
  503. package/examples/mixed-runtime-org/Spawnfile +71 -0
  504. package/examples/mixed-runtime-org/TEAM.md +8 -0
  505. package/examples/mixed-runtime-org/agents/analyst/AGENTS.md +8 -0
  506. package/examples/mixed-runtime-org/agents/analyst/Spawnfile +27 -0
  507. package/examples/mixed-runtime-org/agents/conductor/AGENTS.md +8 -0
  508. package/examples/mixed-runtime-org/agents/conductor/Spawnfile +27 -0
  509. package/examples/mixed-runtime-org/agents/localist/AGENTS.md +7 -0
  510. package/examples/mixed-runtime-org/agents/localist/Spawnfile +50 -0
  511. package/examples/moltnet-team-chat/Spawnfile +43 -0
  512. package/examples/moltnet-team-chat/TEAM.md +5 -0
  513. package/examples/moltnet-team-chat/agents/coordinator/AGENTS.md +9 -0
  514. package/examples/moltnet-team-chat/agents/coordinator/Spawnfile +26 -0
  515. package/examples/moltnet-team-chat/teams/analysis/Spawnfile +23 -0
  516. package/examples/moltnet-team-chat/teams/analysis/TEAM.md +3 -0
  517. package/examples/moltnet-team-chat/teams/analysis/agents/observer/AGENTS.md +5 -0
  518. package/examples/moltnet-team-chat/teams/analysis/agents/observer/Spawnfile +19 -0
  519. package/examples/moltnet-team-chat/teams/analysis/agents/representative/AGENTS.md +5 -0
  520. package/examples/moltnet-team-chat/teams/analysis/agents/representative/Spawnfile +19 -0
  521. package/examples/moltnet-team-chat/teams/field/Spawnfile +41 -0
  522. package/examples/moltnet-team-chat/teams/field/TEAM.md +3 -0
  523. package/examples/moltnet-team-chat/teams/field/agents/observer/AGENTS.md +5 -0
  524. package/examples/moltnet-team-chat/teams/field/agents/observer/Spawnfile +26 -0
  525. package/examples/moltnet-team-chat/teams/field/agents/representative/AGENTS.md +11 -0
  526. package/examples/moltnet-team-chat/teams/field/agents/representative/Spawnfile +26 -0
  527. package/examples/single-agent/AGENTS.md +3 -0
  528. package/examples/single-agent/HEARTBEAT.md +3 -0
  529. package/examples/single-agent/IDENTITY.md +3 -0
  530. package/examples/single-agent/MEMORY.md +3 -0
  531. package/examples/single-agent/SOUL.md +3 -0
  532. package/examples/single-agent/Spawnfile +41 -0
  533. package/examples/single-agent/skills/web_search/SKILL.md +6 -0
  534. package/moltnet-releases.json +5 -4
  535. package/package.json +36 -9
  536. package/runtimes.yaml +5 -2
  537. package/dist/cli/targetWorldClockCrossProcess.test-helper.d.ts +0 -13
  538. package/dist/cli/targetWorldClockCrossProcess.test-helper.js +0 -109
  539. package/dist/deployment/providerRuntimeBoundaryScanner.test-helper.d.ts +0 -8
  540. package/dist/deployment/providerRuntimeBoundaryScanner.test-helper.js +0 -60
  541. package/dist/deployment/simfileFixtureBoundary.test-helper.d.ts +0 -8
  542. package/dist/deployment/simfileFixtureBoundary.test-helper.js +0 -103
  543. package/dist/ownership/simfileRunOperatorInputs.d.ts +0 -103
  544. package/dist/ownership/simfileRunOperatorInputs.js +0 -139
  545. package/dist/target/cleanupRunDocker.test-helper.d.ts +0 -67
  546. package/dist/target/cleanupRunDocker.test-helper.js +0 -238
@@ -29,6 +29,54 @@ export const resolveCompiledEngines = (report) => (report.container?.runtime_ins
29
29
  .flatMap((instance) => Object.entries(instance.engine_by_node_id ?? {}))
30
30
  .map(([agent, engine]) => ({ agent, engine }))
31
31
  .sort((left, right) => left.agent.localeCompare(right.agent));
32
+ const createReceiptMoltnetIdentity = (release) => {
33
+ if (!release)
34
+ return undefined;
35
+ // Discriminate on PROVENANCE, never on capability count. A published release
36
+ // now advertises `daimon-bridge` too (moltnet v0.1.18), so `length === 1` would
37
+ // route it into the local-development branch and reject it for lacking a
38
+ // `development` marker it is never supposed to have.
39
+ //
40
+ // The canonical list lives in `moltnetReleaseAuthority.ts`, but the H2 boundary
41
+ // (`../deployment/organizationHandoffTypes.test.ts`) forbids this file from
42
+ // importing any `moltnet*` module, so the ordering is compared inline here the
43
+ // same way the local branch below already compares it. `upReceiptTypes.ts`'s
44
+ // schema is the authority that rejects any other list on parse.
45
+ const joined = release.capabilities.join("\0");
46
+ const receiptCapabilities = joined === "daimon-bridge\0pi-bridge"
47
+ ? ["daimon-bridge", "pi-bridge"]
48
+ : joined === "pi-bridge" ? ["pi-bridge"] : null;
49
+ if (receiptCapabilities === null) {
50
+ throw new SpawnfileError("runtime_error", "Moltnet receipt advertises unknown bridge capabilities");
51
+ }
52
+ if (!release.development) {
53
+ if (!release.release_version || !release.source_revision) {
54
+ throw new SpawnfileError("runtime_error", "Published Moltnet receipt lacks its pinned source identity");
55
+ }
56
+ return {
57
+ architecture: release.architecture,
58
+ asset: release.asset,
59
+ asset_sha256: release.asset_sha256,
60
+ capabilities: receiptCapabilities,
61
+ release_version: release.release_version,
62
+ source_revision: release.source_revision,
63
+ version: release.version
64
+ };
65
+ }
66
+ if (release.capabilities.join("\0") !== "daimon-bridge\0pi-bridge" ||
67
+ !release.source_sha256) {
68
+ throw new SpawnfileError("runtime_error", "Local Moltnet receipt lacks its development provenance");
69
+ }
70
+ return {
71
+ architecture: release.architecture,
72
+ asset: release.asset,
73
+ asset_sha256: release.asset_sha256,
74
+ capabilities: ["daimon-bridge", "pi-bridge"],
75
+ development: release.development,
76
+ source_sha256: release.source_sha256,
77
+ version: release.version
78
+ };
79
+ };
32
80
  /**
33
81
  * Builds `spawnfile.up-receipt.v1` from a project-path `upProject()` result. Reads back
34
82
  * the deployment record `upProject` already wrote (for `run_id`/deployment name/container
@@ -70,10 +118,7 @@ export const buildUpReceipt = async (inputPath, result) => {
70
118
  compiled_schedule: compiledSchedule,
71
119
  engines: compiledEngines,
72
120
  ...(result.report.container?.moltnet?.release
73
- ? { moltnet_release: {
74
- ...result.report.container.moltnet.release,
75
- capabilities: ["pi-bridge"]
76
- } }
121
+ ? { moltnet_release: createReceiptMoltnetIdentity(result.report.container.moltnet.release) }
77
122
  : {}),
78
123
  ...(record?.organization_handoff ? { organization_handoff: record.organization_handoff } : {}),
79
124
  ...(record?.organization_handoff_handle ? { organization_handoff_handle: record.organization_handoff_handle } : {}),
@@ -9,6 +9,7 @@ const AUTH_METHOD_PROVIDER_HINTS = {
9
9
  "api_key": "use a model provider like openai or anthropic, then pass --auth api_key",
10
10
  "claude-code": "use provider anthropic, then pass --auth claude-code",
11
11
  codex: "use provider openai, then pass --auth codex",
12
+ grok: "use provider xai with a Daimon grok engine agent, then pass --auth grok and declare reasoning_effort",
12
13
  none: "use a model provider like local, then pass --auth none"
13
14
  };
14
15
  const resolveTargetManifestPath = (inputPath) => getManifestPath(path.resolve(inputPath ?? process.cwd()));
@@ -10,7 +10,7 @@ export interface OrganizationViewSkillSummary {
10
10
  }
11
11
  export interface OrganizationViewResourceSummary {
12
12
  id: string;
13
- kind: "git" | "volume";
13
+ kind: "bundle" | "git" | "volume";
14
14
  mode: "mutable" | "readonly";
15
15
  mount: string;
16
16
  sharing: "per_agent" | "team";
@@ -0,0 +1,3 @@
1
+ import type { CompilePlan } from "./types.js";
2
+ export declare const validateWorkspaceBundleTar: (bytes: Buffer) => void;
3
+ export declare const stageWorkspaceBundles: (outputDirectory: string, plan: CompilePlan) => Promise<boolean>;
@@ -0,0 +1,106 @@
1
+ import { createHash } from "node:crypto";
2
+ import { copyFile, mkdir, readFile, stat } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { SpawnfileError } from "../shared/index.js";
5
+ // Sanity bound for an operator-declared local tar; integrity comes from its digest, not its size.
6
+ const CAP = 536_870_912, BLOCK = 512;
7
+ // Sanity bound on an operator-declared local tar; integrity comes from the declared digest.
8
+ const MAX_WORKSPACE_BUNDLE_ENTRIES = 65_536;
9
+ const fail = (message = "Workspace bundle contains an invalid or unsafe tar entry") => { throw new SpawnfileError("validation_error", message); };
10
+ const textField = (field) => { const nul = field.indexOf(0); return field.subarray(0, nul < 0 ? field.length : nul).toString("utf8"); };
11
+ const octal = (field, allowEmpty = false) => {
12
+ const value = field.toString("ascii").replace(/\0.*$/u, "").trim();
13
+ if (value === "" && allowEmpty)
14
+ return 0;
15
+ if (!/^[0-7]+$/u.test(value))
16
+ fail();
17
+ const parsed = Number.parseInt(value, 8);
18
+ if (!Number.isSafeInteger(parsed) || parsed < 0)
19
+ fail();
20
+ return parsed;
21
+ };
22
+ const validPath = (value, directory) => {
23
+ const name = directory && value.endsWith("/") ? value.slice(0, -1) : value;
24
+ if (!name || name.startsWith("/") || name.includes("\\") || name.includes("\0"))
25
+ fail();
26
+ const parts = name.split("/");
27
+ if (parts.some((part) => !part || part === "." || part === ".."))
28
+ fail();
29
+ return parts.join("/");
30
+ };
31
+ const validChecksum = (header) => {
32
+ const expected = octal(header.subarray(148, 156));
33
+ let actual = 0;
34
+ for (let index = 0; index < BLOCK; index += 1)
35
+ actual += index >= 148 && index < 156 ? 32 : header[index];
36
+ return actual === expected;
37
+ };
38
+ export const validateWorkspaceBundleTar = (bytes) => {
39
+ if (bytes.length < BLOCK * 2 || bytes.length % BLOCK !== 0)
40
+ fail("Workspace bundle is truncated or lacks exact ustar termination");
41
+ let offset = 0, entries = 0, terminated = false;
42
+ const names = new Set();
43
+ while (offset + BLOCK <= bytes.length) {
44
+ const header = bytes.subarray(offset, offset + BLOCK);
45
+ if (header.every((byte) => byte === 0)) {
46
+ if (offset + BLOCK * 2 > bytes.length || !bytes.subarray(offset, offset + BLOCK * 2).every((byte) => byte === 0) || !bytes.subarray(offset + BLOCK * 2).every((byte) => byte === 0))
47
+ fail("Workspace bundle has invalid termination or trailing bytes");
48
+ terminated = true;
49
+ break;
50
+ }
51
+ if (textField(header.subarray(257, 263)) !== "ustar" || !validChecksum(header))
52
+ fail();
53
+ for (const [start, end, empty] of [[100, 108, false], [108, 116, true], [116, 124, true], [124, 136, false], [136, 148, true], [329, 337, true], [337, 345, true]])
54
+ octal(header.subarray(start, end), empty);
55
+ const type = header[156] === 0 ? "0" : String.fromCharCode(header[156]);
56
+ if (type !== "0" && type !== "5")
57
+ fail();
58
+ const size = octal(header.subarray(124, 136));
59
+ if (type === "5" && size !== 0)
60
+ fail();
61
+ const prefix = textField(header.subarray(345, 500)), rawName = textField(header.subarray(0, 100));
62
+ const effective = validPath(prefix ? `${prefix}/${rawName}` : rawName, type === "5");
63
+ if (names.has(effective))
64
+ fail("Workspace bundle contains duplicate effective paths");
65
+ names.add(effective);
66
+ offset += BLOCK + Math.ceil(size / BLOCK) * BLOCK;
67
+ entries += 1;
68
+ if (offset > bytes.length)
69
+ fail("Workspace bundle is truncated or exceeds entry bounds");
70
+ if (entries > MAX_WORKSPACE_BUNDLE_ENTRIES)
71
+ fail("Workspace bundle exceeds the maximum entry count");
72
+ }
73
+ if (!terminated)
74
+ fail("Workspace bundle is truncated or lacks exact ustar termination");
75
+ if (entries === 0)
76
+ fail("Workspace bundle is empty");
77
+ };
78
+ export const stageWorkspaceBundles = async (outputDirectory, plan) => {
79
+ const bundles = new Map();
80
+ for (const node of plan.nodes)
81
+ if (node.kind === "agent")
82
+ for (const resource of node.value.workspaceResources ?? []) {
83
+ if (resource.kind !== "bundle")
84
+ continue;
85
+ const source = path.resolve(path.dirname(resource.scope.key), resource.source), prior = bundles.get(resource.sha256);
86
+ if (prior && prior !== source)
87
+ throw new SpawnfileError("validation_error", "Workspace bundle digest maps to multiple sources");
88
+ bundles.set(resource.sha256, source);
89
+ }
90
+ if (bundles.size === 0)
91
+ return false;
92
+ const destination = path.join(outputDirectory, "container/workspace-bundles");
93
+ await mkdir(destination, { recursive: true });
94
+ for (const [identity, source] of bundles) {
95
+ const info = await stat(source);
96
+ if (!info.isFile() || info.size < 1 || info.size > CAP)
97
+ throw new SpawnfileError("validation_error", "Workspace bundle must be a bounded regular tar file");
98
+ const bytes = await readFile(source);
99
+ const actual = `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
100
+ if (actual !== identity)
101
+ throw new SpawnfileError("validation_error", "Workspace bundle checksum mismatch");
102
+ validateWorkspaceBundleTar(bytes);
103
+ await copyFile(source, path.join(destination, `${identity.slice(7)}.tar`));
104
+ }
105
+ return true;
106
+ };
@@ -10,16 +10,19 @@ export type ResolvedWorkspaceResource = TeamWorkspaceResource & {
10
10
  sharing: WorkspaceResourceSharing;
11
11
  };
12
12
  export interface WorkspaceResourcePlan {
13
+ archivePath?: string;
13
14
  backingPath: string;
14
15
  branch?: string;
15
16
  id: string;
16
- kind: "git" | "volume";
17
+ kind: "bundle" | "git" | "volume";
17
18
  linkPath: string;
18
19
  mode: "mutable" | "readonly";
19
20
  mount: string;
20
21
  name?: string;
21
22
  ref?: string;
22
23
  sharing: WorkspaceResourceSharing;
24
+ sha256?: string;
25
+ source?: string;
23
26
  tag?: string;
24
27
  url?: string;
25
28
  }
@@ -14,6 +14,8 @@ const normalizeMount = (value) => {
14
14
  return collapsed.length > 1 ? collapsed.replace(/\/+$/u, "") : "/";
15
15
  };
16
16
  const normalizeResourceIdentity = (resource) => {
17
+ if (resource.kind === "bundle")
18
+ return JSON.stringify({ kind: resource.kind, mode: resource.mode, mount: normalizeMount(resource.mount), sha256: resource.sha256, source: resource.source, sharing: resource.sharing });
17
19
  if (resource.kind === "git") {
18
20
  return JSON.stringify({
19
21
  branch: resource.branch?.trim() ?? "",
@@ -39,12 +41,9 @@ const mountsOverlap = (left, right) => left === right ||
39
41
  left.startsWith(`${right}/`) ||
40
42
  right.startsWith(`${left}/`);
41
43
  const resolveSharing = (resource) => resource.sharing ?? "per_agent";
42
- const toResolvedResource = (resource, scope) => ({
43
- ...resource,
44
- mount: normalizeMount(resource.mount),
45
- scope,
46
- sharing: resolveSharing(resource)
47
- });
44
+ const toResolvedResource = (resource, scope) => resource.kind === "bundle"
45
+ ? { ...resource, mount: normalizeMount(resource.mount), scope, sharing: "per_agent" }
46
+ : { ...resource, mount: normalizeMount(resource.mount), scope, sharing: resolveSharing(resource) };
48
47
  const createPathSegment = (value) => {
49
48
  const slug = slugify(value);
50
49
  const hash = createShortHash(value);
@@ -103,7 +102,13 @@ export const toWorkspaceResourcePlan = (resource, context) => resource.kind ===
103
102
  ...(resource.tag ? { tag: resource.tag } : {}),
104
103
  url: resource.url
105
104
  }
106
- : {
105
+ : resource.kind === "bundle" ? {
106
+ archivePath: `/opt/spawnfile/workspace-bundles/${resource.sha256.slice(7)}.tar`,
107
+ backingPath: resolveBackingPath(resource, context.targetId), id: resource.id, kind: "bundle",
108
+ linkPath: resolveLinkPath(normalizeMount(resource.mount), context.workspacePath), mode: resource.mode,
109
+ mount: normalizeMount(resource.mount), sharing: resource.sharing, sha256: resource.sha256,
110
+ source: path.resolve(path.dirname(resource.scope.key), resource.source)
111
+ } : {
107
112
  backingPath: resolveBackingPath(resource, context.targetId),
108
113
  id: resource.id,
109
114
  kind: "volume",
@@ -1,4 +1,5 @@
1
1
  import path from "node:path/posix";
2
+ import { DAIMON_GROK_TURN_USAGE_LEDGER } from "../runtime/daimon/contractManifest.js";
2
3
  const AGENT_NODE_ID_PREFIX = "agent:";
3
4
  const TEAM_NODE_ID_PREFIX = "team:";
4
5
  /** Strips the compiler's `agent:` node-id prefix down to the generated runtime slug, the
@@ -122,6 +123,54 @@ const planDaimonFiles = (report, mounts) => (report.container?.runtime_instances
122
123
  }
123
124
  }];
124
125
  }));
126
+ /** The persistent-mount id the Daimon adapter gives the per-turn engine usage ledger
127
+ * (`src/runtime/daimon/config.ts`). Deliberately still says `grok` now that AGY writes
128
+ * there too: the id is the volume's identity, and renaming it orphans every existing
129
+ * deployment's accumulated ledger. */
130
+ const DAIMON_USAGE_MOUNT_ID = "daimon-grok-usage-ledger";
131
+ /** Daimon usage: the organization-wide per-turn engine token ledger.
132
+ *
133
+ * Unlike the causal streams above this is NOT per agent -- one exclusive-reattach volume
134
+ * carries every metered turn for the whole Daimon host -- so it exports flat under
135
+ * `raw/daimon/`, the way a single managed Moltnet network exports flat under
136
+ * `raw/moltnet/`. Flat also keeps it clear of the `raw/daimon/<slug>/` directories the
137
+ * per-agent causal files use.
138
+ *
139
+ * BOTH generations ship. The broker rotates by size at
140
+ * `DAIMON_GROK_TURN_USAGE_LEDGER.rotateBytes`, so a long-lived deployment's earlier turns
141
+ * live in `usage.jsonl.1`; exporting only the live file would silently truncate history
142
+ * for exactly the runs expensive enough to care about. This is the same rotation the read
143
+ * path already handles (`src/runtime/usageLedgerRead.ts` reads both generations).
144
+ *
145
+ * The mount root IS the ledger directory (`mountPath` is
146
+ * `DAIMON_GROK_TURN_USAGE_LEDGER.directoryPath`), so both names are resolved as
147
+ * mount-relative paths off the pinned contract constants rather than written out here --
148
+ * if the contract ever moves the ledger, this follows it instead of silently exporting
149
+ * nothing.
150
+ *
151
+ * Optional, because absent must stay distinguishable from empty: a codex-only
152
+ * organization is never provisioned this volume and writes no ledger at all, which is
153
+ * legitimate, and the mount is absent from the report entirely in that case. Where the
154
+ * volume exists but a generation does not (no rotation yet, or no metered turn yet), the
155
+ * executor records it under `missingOptionalFiles` rather than as an exported empty file
156
+ * (`artifactsExport.ts`). */
157
+ const planDaimonUsageFiles = (mounts) => {
158
+ const mount = mounts.get(DAIMON_USAGE_MOUNT_ID);
159
+ if (!mount) {
160
+ return [];
161
+ }
162
+ return [
163
+ DAIMON_GROK_TURN_USAGE_LEDGER.filePath,
164
+ DAIMON_GROK_TURN_USAGE_LEDGER.rotatedFilePath
165
+ ].map((absolutePath) => {
166
+ const volumePath = path.relative(DAIMON_GROK_TURN_USAGE_LEDGER.directoryPath, absolutePath);
167
+ return {
168
+ optional: true,
169
+ relativePath: `raw/daimon/${volumePath}`,
170
+ source: { kind: "volume", volumeName: mount.volume_name, volumePath }
171
+ };
172
+ });
173
+ };
125
174
  /** Resolves every durable artifact file this run's compile report declares, and where each
126
175
  * one is read from. Pure function of the compile report alone (no deployment record, no
127
176
  * Docker) — `artifactsExport.ts` supplies the live container name at execution time. */
@@ -130,6 +179,7 @@ export const planArtifactExports = (report) => {
130
179
  return [
131
180
  ...planMoltnetFiles(report, mounts),
132
181
  ...planMnemeFiles(report, mounts),
133
- ...planDaimonFiles(report, mounts)
182
+ ...planDaimonFiles(report, mounts),
183
+ ...planDaimonUsageFiles(mounts)
134
184
  ];
135
185
  };
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ export declare const deploymentIdentitySchema: z.ZodObject<{
3
+ version: z.ZodLiteral<"spawnfile.deployment-identity.v1">;
4
+ run_id: z.ZodString;
5
+ fingerprint: z.ZodString;
6
+ deployment_name: z.ZodString;
7
+ deployment_mode: z.ZodEnum<{
8
+ project: "project";
9
+ image: "image";
10
+ }>;
11
+ image_id: z.ZodString;
12
+ organization_unit_id: z.ZodString;
13
+ organization_compile_fingerprint: z.ZodString;
14
+ organization_world_binding_digest: z.ZodNullable<z.ZodString>;
15
+ topology_sha256: z.ZodString;
16
+ }, z.core.$strict>;
17
+ type Execute = (command: string, args: string[]) => Promise<string | void>;
18
+ type Inspect = (command: string, args: string[]) => Promise<string>;
19
+ export declare const verifyCandidatePortIsolation: (liveReport: unknown, candidateReport: unknown) => number[];
20
+ export declare const issueDeploymentIdentity: (readinessPath: string, deploymentMode: "project" | "image", dockerCommand: string, imageInspect?: Inspect) => Promise<Record<string, unknown>>;
21
+ export declare const runCanaryCutover: (input: {
22
+ liveReportPath: string;
23
+ candidateReportPath: string;
24
+ readinessPath: string;
25
+ expectedIdentity: unknown;
26
+ dockerCommand: string;
27
+ nonce: string;
28
+ transactionPath: string;
29
+ ingressCommand: string;
30
+ ingressArgs: string[];
31
+ ingressReceiptPath: string;
32
+ teardownCommand: string;
33
+ teardownArgs: string[];
34
+ teardownPolicy: "export" | "force";
35
+ teardownProjectPath: string;
36
+ teardownCompiledPath: string;
37
+ decisionReceiptPath: string;
38
+ fromDeployment: string;
39
+ toDeployment: string;
40
+ }, runner?: Execute, imageInspect?: Inspect) => Promise<Record<string, unknown>>;
41
+ export declare const verifyEquivalentRollbackReadiness: (rollbackReadinessPath: string, expectedIdentity: unknown) => Promise<void>;
42
+ export {};
@@ -0,0 +1,140 @@
1
+ import { createHash } from "node:crypto";
2
+ import { execFile as execFileCallback } from "node:child_process";
3
+ import { link, open, readFile, rm } from "node:fs/promises";
4
+ import { promisify } from "node:util";
5
+ import path from "node:path";
6
+ import { z } from "zod";
7
+ import { SpawnfileError } from "../shared/index.js";
8
+ import { parseUpReceipt } from "./upReceiptTypes.js";
9
+ import { parseDownReceipt } from "./downReceiptTypes.js";
10
+ const execFile = promisify(execFileCallback);
11
+ const digest = z.string().regex(/^sha256:[a-f0-9]{64}$/u), runId = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/u), nonce = z.string().regex(/^[a-f0-9]{32}$/u);
12
+ const ingressReceiptSchema = z.object({ version: z.literal("spawnfile.ingress-cutover-receipt.v1"), state: z.literal("switched"), nonce, from_deployment: z.string().min(1), to_deployment: z.string().min(1), target_run_id: runId, readiness_sha256: digest }).strict();
13
+ const decisionReceiptSchema = z.object({ version: z.literal("spawnfile.canary-decision-receipt.v1"), decision: z.literal("cutover"), nonce, target_run_id: runId, deployment_mode: z.enum(["project", "image"]), target_identity_sha256: digest, ingress_receipt_sha256: digest, teardown_checkpoint_sha256: digest, down_receipt_sha256: digest }).strict();
14
+ const reportSchema = z.object({
15
+ persistent_mounts: z.array(z.object({
16
+ id: z.string().min(1),
17
+ lifecycle: z.literal("exclusive-reattach").optional()
18
+ }).passthrough()).max(4096).optional().default([]),
19
+ published_ports: z.array(z.number().int().min(1).max(65535)).max(64)
20
+ }).passthrough();
21
+ export const deploymentIdentitySchema = z.object({ version: z.literal("spawnfile.deployment-identity.v1"), run_id: runId, fingerprint: z.string().min(1), deployment_name: z.string().min(1), deployment_mode: z.enum(["project", "image"]), image_id: z.string().regex(/^sha256:[a-f0-9]{64}$/u), organization_unit_id: z.string().min(1), organization_compile_fingerprint: z.string().min(1), organization_world_binding_digest: digest.nullable(), topology_sha256: digest }).strict();
22
+ const execute = async (command, args) => (await execFile(command, args, { encoding: "utf8", timeout: 30_000, maxBuffer: 1_048_576 })).stdout;
23
+ const inspect = async (command, args) => (await execFile(command, args, { encoding: "utf8", timeout: 10_000, maxBuffer: 1_048_576 })).stdout;
24
+ const sha = (bytes) => `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
25
+ const canonical = (value) => JSON.stringify(value, (_key, item) => item && typeof item === "object" && !Array.isArray(item) ? Object.fromEntries(Object.entries(item).sort(([left], [right]) => left.localeCompare(right))) : item);
26
+ const topologyDigest = (receipt) => sha(canonical({ compiled_schedule: receipt.compiled_schedule, engines: receipt.engines ?? [], moltnet_release: receipt.moltnet_release ?? null }));
27
+ export const verifyCandidatePortIsolation = (liveReport, candidateReport) => {
28
+ const liveReportValue = reportSchema.parse(liveReport), candidateReportValue = reportSchema.parse(candidateReport);
29
+ const live = new Set(liveReportValue.published_ports), candidate = candidateReportValue.published_ports;
30
+ if (candidate.some((port) => live.has(port)) || new Set(candidate).size !== candidate.length)
31
+ throw new SpawnfileError("validation_error", "Candidate published ports are not mechanically isolated from live");
32
+ const liveMounts = new Map(liveReportValue.persistent_mounts.map((mount) => [mount.id, mount]));
33
+ const sharedExclusive = candidateReportValue.persistent_mounts.find((mount) => liveMounts.has(mount.id)
34
+ && (mount.lifecycle === "exclusive-reattach" || liveMounts.get(mount.id)?.lifecycle === "exclusive-reattach"));
35
+ if (sharedExclusive)
36
+ throw new SpawnfileError("validation_error", `Candidate shares exclusive persistent mount ${sharedExclusive.id}; use stop-and-reattach deployment instead of concurrent canary`);
37
+ return candidate;
38
+ };
39
+ const verifyIdentity = (receipt, expected) => {
40
+ const ready = receipt.organization_ready;
41
+ if (receipt.run_id !== expected.run_id || receipt.fingerprint !== expected.fingerprint || receipt.deployment.name !== expected.deployment_name || ready?.state !== "ready" || ready.unit_id !== expected.organization_unit_id || ready.compile_fingerprint !== expected.organization_compile_fingerprint || ready.world_binding_digest !== expected.organization_world_binding_digest || topologyDigest(receipt) !== expected.topology_sha256)
42
+ throw new SpawnfileError("validation_error", "Readiness does not bind the exact intended deployment identity and topology");
43
+ };
44
+ export const issueDeploymentIdentity = async (readinessPath, deploymentMode, dockerCommand, imageInspect = inspect) => {
45
+ const receipt = parseUpReceipt(JSON.parse(await readFile(readinessPath, "utf8"))), ready = receipt.organization_ready;
46
+ if (receipt.run_id === null || receipt.deployment.name === null || receipt.deployment.container_ids.length < 1 || ready?.state !== "ready")
47
+ throw new SpawnfileError("validation_error", "Deployment is not identity-ready");
48
+ const images = await Promise.all(receipt.deployment.container_ids.map(async (container) => (await imageInspect(dockerCommand, ["inspect", "--format", "{{.Image}}", container])).trim()));
49
+ if (!images[0] || images.some((image) => image !== images[0]))
50
+ throw new SpawnfileError("validation_error", "Deployment containers do not share one exact image identity");
51
+ return deploymentIdentitySchema.parse({ version: "spawnfile.deployment-identity.v1", run_id: receipt.run_id, fingerprint: receipt.fingerprint, deployment_name: receipt.deployment.name, deployment_mode: deploymentMode, image_id: images[0], organization_unit_id: ready.unit_id, organization_compile_fingerprint: ready.compile_fingerprint, organization_world_binding_digest: ready.world_binding_digest, topology_sha256: topologyDigest(receipt) });
52
+ };
53
+ const syncParent = async (filePath) => { const directory = await open(path.dirname(filePath), "r"); try {
54
+ await directory.sync();
55
+ }
56
+ finally {
57
+ await directory.close();
58
+ } };
59
+ const publishExclusive = async (filePath, value) => { const temporary = `${filePath}.tmp-${process.pid}`; const file = await open(temporary, "wx", 0o600); try {
60
+ await file.writeFile(`${JSON.stringify(value)}\n`);
61
+ await file.sync();
62
+ }
63
+ finally {
64
+ await file.close();
65
+ } try {
66
+ await link(temporary, filePath);
67
+ await syncParent(filePath);
68
+ }
69
+ finally {
70
+ await rm(temporary, { force: true });
71
+ await syncParent(filePath);
72
+ } };
73
+ const exists = async (filePath) => await readFile(filePath).then(() => true, (error) => error.code === "ENOENT" ? false : Promise.reject(error));
74
+ export const runCanaryCutover = async (input, runner = execute, imageInspect = inspect) => {
75
+ const expected = deploymentIdentitySchema.parse(input.expectedIdentity), operationNonce = nonce.parse(input.nonce);
76
+ if (input.ingressArgs.length > 32 || input.teardownArgs.length > 32 || [...input.ingressArgs, ...input.teardownArgs].some((arg) => arg.length > 4096))
77
+ throw new SpawnfileError("validation_error", "Canary command arguments exceed bounds");
78
+ const lifecycleId = `lci_canary_${operationNonce}`, exportIndex = input.teardownArgs.indexOf("--export-to"), prefix = ["down", input.teardownProjectPath, "--compiled", input.teardownCompiledPath, "--deployment", input.fromDeployment], expectedTeardownArgs = input.teardownPolicy === "export" ? [...prefix, "--export-to", input.teardownArgs[exportIndex + 1] ?? "", "--json", "--lifecycle-invocation", lifecycleId] : [...prefix, "--force", "--json", "--lifecycle-invocation", lifecycleId];
79
+ const canonicalPath = (value) => path.isAbsolute(value) && path.resolve(value) === value && path.normalize(value) === value;
80
+ if (!canonicalPath(input.teardownProjectPath) || !canonicalPath(input.teardownCompiledPath) || path.basename(input.teardownCommand) !== "spawnfile" || (input.teardownPolicy === "export" && (exportIndex < 0 || !expectedTeardownArgs[7])) || canonical(input.teardownArgs) !== canonical(expectedTeardownArgs))
81
+ throw new SpawnfileError("validation_error", "Teardown is not the exact canonical Spawnfile lifecycle down operation");
82
+ verifyCandidatePortIsolation(JSON.parse(await readFile(input.liveReportPath, "utf8")), JSON.parse(await readFile(input.candidateReportPath, "utf8")));
83
+ const readinessBytes = await readFile(input.readinessPath), readiness = parseUpReceipt(JSON.parse(readinessBytes.toString("utf8")));
84
+ verifyIdentity(readiness, expected);
85
+ if (readiness.deployment.container_ids.length < 1)
86
+ throw new SpawnfileError("validation_error", "Ready deployment has no image-bearing containers");
87
+ for (const container of readiness.deployment.container_ids)
88
+ if ((await imageInspect(input.dockerCommand, ["inspect", "--format", "{{.Image}}", container])).trim() !== expected.image_id)
89
+ throw new SpawnfileError("validation_error", "Ready deployment image does not match intended identity");
90
+ const binding = sha(canonical({ ...input, expectedIdentity: expected }));
91
+ const transaction = { version: "spawnfile.canary-transaction.v1", state: "reserved", nonce: operationNonce, binding_sha256: binding };
92
+ const recovering = await exists(input.transactionPath);
93
+ if (recovering) {
94
+ const prior = JSON.parse(await readFile(input.transactionPath, "utf8"));
95
+ if (prior.version !== transaction.version || prior.nonce !== operationNonce || prior.binding_sha256 !== binding)
96
+ throw new SpawnfileError("validation_error", "Canary transaction authority does not match");
97
+ }
98
+ else {
99
+ if (await exists(input.ingressReceiptPath) || await exists(input.decisionReceiptPath))
100
+ throw new SpawnfileError("validation_error", "Stale canary receipt exists before transaction reservation");
101
+ await publishExclusive(input.transactionPath, transaction);
102
+ }
103
+ if (!await exists(input.ingressReceiptPath))
104
+ await runner(input.ingressCommand, input.ingressArgs);
105
+ const ingressBytes = await readFile(input.ingressReceiptPath), ingress = ingressReceiptSchema.parse(JSON.parse(ingressBytes.toString("utf8")));
106
+ if (ingress.nonce !== operationNonce || ingress.from_deployment !== input.fromDeployment || ingress.to_deployment !== input.toDeployment || ingress.target_run_id !== expected.run_id || ingress.readiness_sha256 !== sha(readinessBytes))
107
+ throw new SpawnfileError("validation_error", "Ingress receipt does not bind the exact ready target and transaction nonce");
108
+ const teardownStartedPath = `${input.transactionPath}.teardown-started`, teardownReceiptPath = `${input.transactionPath}.teardown-receipt`, teardownCheckpointPath = `${input.transactionPath}.teardown-complete`, checkpointBase = { nonce: operationNonce, binding_sha256: binding, ingress_receipt_sha256: sha(ingressBytes), lifecycle_invocation: lifecycleId };
109
+ if (!await exists(teardownCheckpointPath)) {
110
+ if (await exists(input.decisionReceiptPath))
111
+ throw new SpawnfileError("validation_error", "Canary decision exists without its teardown checkpoint");
112
+ if (!await exists(teardownStartedPath))
113
+ await publishExclusive(teardownStartedPath, { version: "spawnfile.canary-teardown-started.v1", ...checkpointBase });
114
+ if (!await exists(teardownReceiptPath)) {
115
+ const output = await runner(input.teardownCommand, input.teardownArgs), down = parseDownReceipt(JSON.parse(typeof output === "string" ? output : ""));
116
+ if (down.deployment !== input.fromDeployment || down.errors.length !== 0)
117
+ throw new SpawnfileError("validation_error", "Down receipt does not prove exact teardown completion");
118
+ await publishExclusive(teardownReceiptPath, down);
119
+ }
120
+ const downBytes = await readFile(teardownReceiptPath), down = parseDownReceipt(JSON.parse(downBytes.toString("utf8")));
121
+ if (down.deployment !== input.fromDeployment || down.errors.length !== 0)
122
+ throw new SpawnfileError("validation_error", "Stored down receipt does not bind the teardown operation");
123
+ await publishExclusive(teardownCheckpointPath, { version: "spawnfile.canary-teardown-checkpoint.v1", ...checkpointBase, down_receipt_sha256: sha(downBytes) });
124
+ }
125
+ else {
126
+ const downBytes = await readFile(teardownReceiptPath), down = parseDownReceipt(JSON.parse(downBytes.toString("utf8"))), expectedCheckpoint = { version: "spawnfile.canary-teardown-checkpoint.v1", ...checkpointBase, down_receipt_sha256: sha(downBytes) };
127
+ if (down.deployment !== input.fromDeployment || down.errors.length !== 0 || canonical(JSON.parse(await readFile(teardownCheckpointPath, "utf8"))) !== canonical(expectedCheckpoint))
128
+ throw new SpawnfileError("validation_error", "Canary teardown checkpoint does not match the recovered transaction");
129
+ }
130
+ const checkpointBytes = await readFile(teardownCheckpointPath), downBytes = await readFile(teardownReceiptPath), decision = { version: "spawnfile.canary-decision-receipt.v1", decision: "cutover", nonce: operationNonce, target_run_id: expected.run_id, deployment_mode: expected.deployment_mode, target_identity_sha256: sha(canonical(expected)), ingress_receipt_sha256: sha(ingressBytes), teardown_checkpoint_sha256: sha(checkpointBytes), down_receipt_sha256: sha(downBytes) };
131
+ if (!await exists(input.decisionReceiptPath)) {
132
+ await publishExclusive(input.decisionReceiptPath, decision);
133
+ return decision;
134
+ }
135
+ const priorDecision = decisionReceiptSchema.parse(JSON.parse(await readFile(input.decisionReceiptPath, "utf8")));
136
+ if (canonical(priorDecision) !== canonical(decision))
137
+ throw new SpawnfileError("validation_error", "Canary decision receipt does not match the recovered transaction");
138
+ return priorDecision;
139
+ };
140
+ export const verifyEquivalentRollbackReadiness = async (rollbackReadinessPath, expectedIdentity) => { const expected = deploymentIdentitySchema.parse(expectedIdentity), rollback = parseUpReceipt(JSON.parse(await readFile(rollbackReadinessPath, "utf8"))); verifyIdentity(rollback, expected); };
@@ -0,0 +1,15 @@
1
+ import type { DockerCommandRunner } from "../distribution/dockerRunner.js";
2
+ export interface CandidateDiagnosticsInput {
3
+ candidateId: string;
4
+ deploymentName: string;
5
+ runDocker: DockerCommandRunner;
6
+ secretValues: string[];
7
+ }
8
+ export interface CandidateDiagnosticsResult {
9
+ path: string | null;
10
+ summary: string;
11
+ }
12
+ /** Redaction is best effort; diagnostic artifacts remain private operator data. */
13
+ export declare const sanitizeCandidateDiagnostic: (text: string, secrets: string[]) => string;
14
+ /** Collect before rollback deletes the only container-local startup evidence. */
15
+ export declare const captureCandidateDiagnostics: (input: CandidateDiagnosticsInput) => Promise<CandidateDiagnosticsResult>;
@@ -0,0 +1,90 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { lstat, mkdir, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { resolveSpawnfileHome } from "../auth/index.js";
5
+ import { redactDockerLogText } from "./dockerLogs.js";
6
+ import { resolveHomeDeploymentDirectory } from "./homeStore.js";
7
+ const outputLimit = 64 * 1024;
8
+ const commandOptions = { captureStderr: true, maxOutputBytes: outputLimit, timeoutMs: 5_000 };
9
+ const containerIdPattern = /^[a-f0-9]{64}$/u;
10
+ /** Redaction is best effort; diagnostic artifacts remain private operator data. */
11
+ export const sanitizeCandidateDiagnostic = (text, secrets) => redactDockerLogText(text, secrets)
12
+ .replace(/-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?(?:-----END [^-]*PRIVATE KEY-----|$)/gu, "[REDACTED PRIVATE KEY]")
13
+ .replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[REDACTED JWT]")
14
+ .replace(/https?:\/\/[^\s<>"']+/giu, "[REDACTED URL]")
15
+ .replace(/\b([a-z0-9_-]*(?:authorization|cookie|password|secret|token|api[_-]?key)[a-z0-9_-]*)["']?\s*[:=]\s*[^\r\n]+/giu, "$1: [REDACTED]");
16
+ const collect = async (runDocker, args) => {
17
+ try {
18
+ const bytes = await runDocker(args, commandOptions);
19
+ // Injectable runners must obey the same limit as the default Docker runner.
20
+ if (bytes.length > outputLimit)
21
+ throw new Error("limit");
22
+ return { available: true, text: bytes.toString("utf8") };
23
+ }
24
+ catch {
25
+ // Docker transport errors can contain command/env details; never persist them.
26
+ return { available: false, text: "Diagnostic command failed, timed out, or exceeded its byte limit" };
27
+ }
28
+ };
29
+ const summarizeState = (raw, secrets) => {
30
+ try {
31
+ const state = JSON.parse(raw);
32
+ const health = state.Health?.Status;
33
+ const status = ["created", "running", "paused", "restarting", "removing", "exited", "dead"].includes(String(state.Status))
34
+ ? state.Status : "unknown";
35
+ const parts = [`state=${status}`];
36
+ if (["starting", "healthy", "unhealthy"].includes(String(health)))
37
+ parts.push(`health=${health}`);
38
+ if (Number.isSafeInteger(state.ExitCode))
39
+ parts.push(`exit=${state.ExitCode}`);
40
+ if (state.OOMKilled === true)
41
+ parts.push("oom-killed");
42
+ const healthState = state.Health;
43
+ const text = (value) => typeof value === "string"
44
+ ? sanitizeCandidateDiagnostic(value, secrets).slice(0, 4_096) : "";
45
+ return { available: true, summary: parts.join(", "), error: text(state.Error),
46
+ healthOutput: Array.isArray(healthState?.Log) ? healthState.Log.slice(-3).map((entry) => text(entry && typeof entry === "object" ? entry.Output : null)) : [] };
47
+ }
48
+ catch {
49
+ return { available: false, summary: "state unavailable", error: "", healthOutput: [] };
50
+ }
51
+ };
52
+ /** Collect before rollback deletes the only container-local startup evidence. */
53
+ export const captureCandidateDiagnostics = async (input) => {
54
+ if (!containerIdPattern.test(input.candidateId))
55
+ return { path: null, summary: "invalid candidate identity" };
56
+ const [state, logs] = await Promise.all([
57
+ collect(input.runDocker, ["container", "inspect", "--format", "{{json .State}}", input.candidateId]),
58
+ collect(input.runDocker, ["logs", "--tail", "100", input.candidateId])
59
+ ]);
60
+ const health = summarizeState(state.available ? state.text : "", input.secretValues);
61
+ const summary = health.summary;
62
+ try {
63
+ const deploymentDirectory = resolveHomeDeploymentDirectory(input.deploymentName);
64
+ for (const parent of [resolveSpawnfileHome(), path.dirname(deploymentDirectory), deploymentDirectory]) {
65
+ const info = await lstat(parent);
66
+ if (!info.isDirectory() || info.isSymbolicLink() || (info.mode & 0o022) !== 0
67
+ || (process.getuid && info.uid !== process.getuid()))
68
+ throw new Error("unsafe diagnostics parent");
69
+ }
70
+ const directory = path.join(deploymentDirectory, "diagnostics");
71
+ await mkdir(directory, { mode: 0o700 }).catch((error) => {
72
+ if (error.code !== "EEXIST")
73
+ throw error;
74
+ });
75
+ const info = await lstat(directory);
76
+ if (!info.isDirectory() || info.isSymbolicLink() || (info.mode & 0o777) !== 0o700
77
+ || (process.getuid && info.uid !== process.getuid()))
78
+ throw new Error("unsafe diagnostics directory");
79
+ const destination = path.join(directory, `candidate-${Date.now()}-${randomUUID()}.json`);
80
+ await writeFile(destination, `${JSON.stringify({
81
+ version: "spawnfile.candidate-diagnostics.v1", capturedAt: new Date().toISOString(),
82
+ candidateId: input.candidateId, deployment: input.deploymentName, summary, health,
83
+ logs: { available: logs.available, text: sanitizeCandidateDiagnostic(logs.text, input.secretValues) }
84
+ }, null, 2)}\n`, { flag: "wx", mode: 0o600 });
85
+ return { path: destination, summary };
86
+ }
87
+ catch {
88
+ return { path: null, summary };
89
+ }
90
+ };
@@ -15,10 +15,13 @@ const withDockerTarget = (record, args) => {
15
15
  return args;
16
16
  };
17
17
  const normalizeTail = (tail) => typeof tail === "number" && Number.isInteger(tail) && tail > 0 ? tail : 100;
18
+ const stripTerminalControls = (value) => value.replace(/\x1b\[[0-?]*[ -/]*[@-~]|[\x00-\x08\x0b-\x1f\x7f]/gu, "");
18
19
  const redactKnownSecrets = (text, secretValues) => secretValues
20
+ .map(stripTerminalControls)
19
21
  .filter((secret) => secret.length > 0)
22
+ .sort((left, right) => right.length - left.length)
20
23
  .reduce((redacted, secret) => redacted.replaceAll(secret, "[REDACTED]"), text);
21
- export const redactDockerLogText = (text, secretValues = []) => redactSensitiveText(redactKnownSecrets(text, secretValues));
24
+ export const redactDockerLogText = (text, secretValues = []) => redactSensitiveText(redactKnownSecrets(stripTerminalControls(text), secretValues));
22
25
  const combineLogStreams = (stdout, stderr) => {
23
26
  if (!stdout) {
24
27
  return stderr;