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.
Files changed (490) hide show
  1. package/README.md +1 -1
  2. package/dist/auth/importers.js +21 -5
  3. package/dist/auth/index.d.ts +1 -0
  4. package/dist/auth/index.js +1 -0
  5. package/dist/auth/profileStore.js +4 -1
  6. package/dist/auth/trainingGrokWriteback.d.ts +4 -0
  7. package/dist/auth/trainingGrokWriteback.js +119 -0
  8. package/dist/auth/trainingGrokWritebackLock.d.ts +5 -0
  9. package/dist/auth/trainingGrokWritebackLock.js +56 -0
  10. package/dist/cli/authCommands.js +9 -5
  11. package/dist/cli/compileBuildCommands.js +4 -0
  12. package/dist/cli/devCommands.js +4 -2
  13. package/dist/cli/downCommand.d.ts +2 -0
  14. package/dist/cli/downCommand.js +3 -0
  15. package/dist/cli/envFileOption.d.ts +18 -0
  16. package/dist/cli/envFileOption.js +20 -0
  17. package/dist/cli/lifecycleCommands.d.ts +1 -1
  18. package/dist/cli/lifecycleCommands.js +3 -1
  19. package/dist/cli/lifecyclePlan.d.ts +1 -0
  20. package/dist/cli/lifecyclePlan.js +2 -0
  21. package/dist/cli/paideiaDelegation.d.ts +2 -1
  22. package/dist/cli/paideiaDelegation.js +5 -4
  23. package/dist/cli/recordCommands.d.ts +17 -0
  24. package/dist/cli/recordCommands.js +43 -0
  25. package/dist/cli/releaseCommand.d.ts +6 -0
  26. package/dist/cli/releaseCommand.js +91 -0
  27. package/dist/cli/runCli.d.ts +7 -1
  28. package/dist/cli/runCli.js +11 -6
  29. package/dist/cli/runPublishCommands.js +5 -3
  30. package/dist/cli/trainCommand.js +73 -20
  31. package/dist/cli/upCommand.js +5 -1
  32. package/dist/cli/volumeCommands.d.ts +8 -0
  33. package/dist/cli/volumeCommands.js +53 -0
  34. package/dist/compiler/agentNodeMerge.js +3 -2
  35. package/dist/compiler/buildCompilePlan.js +4 -0
  36. package/dist/compiler/buildCompilePlanAgent.js +3 -1
  37. package/dist/compiler/buildCompilePlanTraversal.js +2 -0
  38. package/dist/compiler/buildCompilePlanTraversalHelpers.d.ts +2 -2
  39. package/dist/compiler/buildCompilePlanTraversalHelpers.js +4 -3
  40. package/dist/compiler/buildProject.d.ts +10 -0
  41. package/dist/compiler/buildProject.js +14 -2
  42. package/dist/compiler/cleanCompileOutput.d.ts +2 -0
  43. package/dist/compiler/cleanCompileOutput.js +14 -0
  44. package/dist/compiler/compilePlanHelpers.js +2 -0
  45. package/dist/compiler/compileProject.d.ts +10 -0
  46. package/dist/compiler/compileProject.js +29 -3
  47. package/dist/compiler/containerArtifactSummaries.js +1 -0
  48. package/dist/compiler/containerArtifacts.d.ts +4 -4
  49. package/dist/compiler/containerArtifacts.js +18 -34
  50. package/dist/compiler/containerArtifactsPlans.js +7 -0
  51. package/dist/compiler/containerArtifactsTypes.d.ts +7 -0
  52. package/dist/compiler/containerDaimonBrokerRender.d.ts +3 -1
  53. package/dist/compiler/containerDaimonBrokerRender.js +6 -3
  54. package/dist/compiler/containerDaimonGrokTaskClock.d.ts +5 -0
  55. package/dist/compiler/containerDaimonGrokTaskClock.js +29 -0
  56. package/dist/compiler/containerDaimonGrokWorkerProvisioning.d.ts +1 -1
  57. package/dist/compiler/containerDaimonGrokWorkerProvisioning.js +38 -16
  58. package/dist/compiler/containerDaimonGrokWorkerRender.d.ts +33 -8
  59. package/dist/compiler/containerDaimonGrokWorkerRender.js +18 -5
  60. package/dist/compiler/containerDaimonOwnershipGuardRender.d.ts +14 -1
  61. package/dist/compiler/containerDaimonOwnershipGuardRender.js +15 -2
  62. package/dist/compiler/containerDaimonUidEntrypointRender.js +6 -12
  63. package/dist/compiler/containerTargetResources.d.ts +10 -0
  64. package/dist/compiler/containerTargetResources.js +21 -13
  65. package/dist/compiler/containerWorkspaceResourceRender.js +4 -1
  66. package/dist/compiler/index.d.ts +2 -0
  67. package/dist/compiler/index.js +2 -0
  68. package/dist/compiler/mcpPlaceholders.d.ts +11 -0
  69. package/dist/compiler/mcpPlaceholders.js +58 -0
  70. package/dist/compiler/mcpServerInheritance.d.ts +18 -0
  71. package/dist/compiler/mcpServerInheritance.js +43 -0
  72. package/dist/compiler/moltnetArtifactTypes.d.ts +1 -0
  73. package/dist/compiler/moltnetArtifacts.js +1 -0
  74. package/dist/compiler/preflightCompilePlan.d.ts +9 -0
  75. package/dist/compiler/preflightCompilePlan.js +38 -0
  76. package/dist/compiler/recordingAgentFacts.d.ts +4 -0
  77. package/dist/compiler/recordingAgentFacts.js +35 -0
  78. package/dist/compiler/recordingCapability.d.ts +4 -0
  79. package/dist/compiler/recordingCapability.js +22 -0
  80. package/dist/compiler/recordingReport.d.ts +4 -0
  81. package/dist/compiler/recordingReport.js +18 -0
  82. package/dist/compiler/runProject.d.ts +2 -1
  83. package/dist/compiler/runProject.js +2 -0
  84. package/dist/compiler/surfaces.d.ts +1 -2
  85. package/dist/compiler/surfaces.js +0 -1
  86. package/dist/compiler/syncProjectAuth.js +2 -1
  87. package/dist/compiler/training/broker/clockProbe.d.ts +6 -0
  88. package/dist/compiler/training/broker/clockProbe.js +27 -0
  89. package/dist/compiler/training/broker/declaration.d.ts +4 -2
  90. package/dist/compiler/training/broker/declaration.js +4 -2
  91. package/dist/compiler/training/broker/entrypoint.js +33 -18
  92. package/dist/compiler/training/broker/evaluatorSecrets.d.ts +2 -0
  93. package/dist/compiler/training/broker/evaluatorSecrets.js +60 -0
  94. package/dist/compiler/training/broker/index.d.ts +3 -2
  95. package/dist/compiler/training/broker/index.js +3 -2
  96. package/dist/compiler/training/broker/main.js +1 -1
  97. package/dist/compiler/training/broker/paths.d.ts +96 -56
  98. package/dist/compiler/training/broker/paths.js +97 -89
  99. package/dist/compiler/training/broker/processes.d.ts +9 -0
  100. package/dist/compiler/training/broker/processes.js +4 -1
  101. package/dist/compiler/training/broker/projection.d.ts +3 -2
  102. package/dist/compiler/training/broker/projection.js +21 -9
  103. package/dist/compiler/training/broker/provisioning.d.ts +6 -15
  104. package/dist/compiler/training/broker/provisioning.js +71 -53
  105. package/dist/compiler/training/broker/registration.d.ts +3 -1
  106. package/dist/compiler/training/broker/registration.js +29 -21
  107. package/dist/compiler/training/broker/reset.d.ts +9 -0
  108. package/dist/compiler/training/broker/reset.js +71 -0
  109. package/dist/compiler/training/broker/runtime.d.ts +10 -13
  110. package/dist/compiler/training/broker/runtime.js +69 -115
  111. package/dist/compiler/training/broker/supervisor.d.ts +10 -11
  112. package/dist/compiler/training/broker/supervisor.js +22 -23
  113. package/dist/compiler/training/broker/taskClock.d.ts +5 -0
  114. package/dist/compiler/training/broker/taskClock.js +41 -0
  115. package/dist/compiler/training/compiledAgent.d.ts +21 -0
  116. package/dist/compiler/training/compiledAgent.js +62 -0
  117. package/dist/compiler/training/container/contract.d.ts +23 -0
  118. package/dist/compiler/training/container/contract.js +23 -5
  119. package/dist/compiler/training/container/evaluators.d.ts +48 -0
  120. package/dist/compiler/training/container/evaluators.js +50 -0
  121. package/dist/compiler/training/container/grokWriteback.d.ts +8 -0
  122. package/dist/compiler/training/container/grokWriteback.js +26 -0
  123. package/dist/compiler/training/container/index.d.ts +2 -1
  124. package/dist/compiler/training/container/index.js +2 -1
  125. package/dist/compiler/training/container/launch.d.ts +3 -0
  126. package/dist/compiler/training/container/launch.js +119 -28
  127. package/dist/compiler/training/container/nativeSlots.d.ts +14 -0
  128. package/dist/compiler/training/container/nativeSlots.js +13 -0
  129. package/dist/compiler/training/container/outputCopy.d.ts +2 -0
  130. package/dist/compiler/training/container/outputCopy.js +72 -0
  131. package/dist/compiler/training/container/outputVolume.d.ts +27 -0
  132. package/dist/compiler/training/container/outputVolume.js +161 -0
  133. package/dist/compiler/training/container/prepare.d.ts +3 -1
  134. package/dist/compiler/training/container/prepare.js +57 -12
  135. package/dist/compiler/training/container/security.d.ts +5 -3
  136. package/dist/compiler/training/container/security.js +15 -5
  137. package/dist/compiler/training/container/subjectIsolation.d.ts +5 -0
  138. package/dist/compiler/training/container/subjectIsolation.js +15 -0
  139. package/dist/compiler/training/context.d.ts +8 -0
  140. package/dist/compiler/training/context.js +12 -12
  141. package/dist/compiler/training/contract.d.ts +2 -0
  142. package/dist/compiler/training/contract.js +2 -0
  143. package/dist/compiler/training/execution.d.ts +417 -0
  144. package/dist/compiler/training/execution.js +160 -0
  145. package/dist/compiler/training/index.d.ts +8 -0
  146. package/dist/compiler/training/index.js +7 -0
  147. package/dist/compiler/training/moments.d.ts +80 -0
  148. package/dist/compiler/training/moments.js +118 -0
  149. package/dist/compiler/training/output.d.ts +2 -0
  150. package/dist/compiler/training/output.js +20 -0
  151. package/dist/compiler/training/plan/contract.d.ts +287 -0
  152. package/dist/compiler/training/plan/contract.js +49 -0
  153. package/dist/compiler/training/plan/index.d.ts +5 -0
  154. package/dist/compiler/training/plan/index.js +4 -0
  155. package/dist/compiler/training/plan/load.d.ts +9 -0
  156. package/dist/compiler/training/plan/load.js +153 -0
  157. package/dist/compiler/training/plan/references.d.ts +19 -0
  158. package/dist/compiler/training/plan/references.js +57 -0
  159. package/dist/compiler/training/plan/resolve.d.ts +4 -0
  160. package/dist/compiler/training/plan/resolve.js +73 -0
  161. package/dist/compiler/training/plan/targets.d.ts +11 -0
  162. package/dist/compiler/training/plan/targets.js +71 -0
  163. package/dist/compiler/training/plan/types.d.ts +36 -0
  164. package/dist/compiler/training/plan/types.js +1 -0
  165. package/dist/compiler/training/preparation/assets/Dockerfile +13 -2
  166. package/dist/compiler/training/preparation/assets/package-lock.json +2 -2
  167. package/dist/compiler/training/preparation/broker.d.ts +3 -3
  168. package/dist/compiler/training/preparation/broker.js +6 -4
  169. package/dist/compiler/training/preparation/contract.d.ts +27 -2
  170. package/dist/compiler/training/preparation/contract.js +23 -4
  171. package/dist/compiler/training/preparation/files.d.ts +5 -1
  172. package/dist/compiler/training/preparation/files.js +76 -15
  173. package/dist/compiler/training/preparation/image.js +9 -4
  174. package/dist/compiler/training/preparation/inputs.d.ts +1 -0
  175. package/dist/compiler/training/preparation/inputs.js +18 -10
  176. package/dist/compiler/training/preparation/prepare.d.ts +5 -0
  177. package/dist/compiler/training/preparation/prepare.js +66 -29
  178. package/dist/compiler/training/preparation/scratch.d.ts +6 -17
  179. package/dist/compiler/training/preparation/scratch.js +89 -23
  180. package/dist/compiler/training/repair/contract.d.ts +338 -4
  181. package/dist/compiler/training/repair/contract.js +2 -0
  182. package/dist/compiler/training/repair/index.d.ts +1 -1
  183. package/dist/compiler/training/repair/index.js +1 -1
  184. package/dist/compiler/training/repair/prepare.d.ts +155 -3
  185. package/dist/compiler/training/repair/prepare.js +6 -2
  186. package/dist/compiler/training/repair/witness.d.ts +152 -3
  187. package/dist/compiler/training/repair/witness.js +8 -2
  188. package/dist/compiler/training/resolution.d.ts +25 -0
  189. package/dist/compiler/training/resolution.js +69 -0
  190. package/dist/compiler/training/rewind/contract.d.ts +321 -0
  191. package/dist/compiler/training/rewind/contract.js +39 -0
  192. package/dist/compiler/training/rewind/deployedResource.d.ts +22 -0
  193. package/dist/compiler/training/rewind/deployedResource.js +19 -0
  194. package/dist/compiler/training/rewind/feed.d.ts +12 -0
  195. package/dist/compiler/training/rewind/feed.js +182 -0
  196. package/dist/compiler/training/rewind/feedContent.d.ts +6 -0
  197. package/dist/compiler/training/rewind/feedContent.js +38 -0
  198. package/dist/compiler/training/rewind/git.d.ts +8 -0
  199. package/dist/compiler/training/rewind/git.js +159 -0
  200. package/dist/compiler/training/rewind/gitArchive.d.ts +2 -0
  201. package/dist/compiler/training/rewind/gitArchive.js +7 -0
  202. package/dist/compiler/training/rewind/gitHistory.d.ts +3 -0
  203. package/dist/compiler/training/rewind/gitHistory.js +55 -0
  204. package/dist/compiler/training/rewind/gitProcess.d.ts +19 -0
  205. package/dist/compiler/training/rewind/gitProcess.js +81 -0
  206. package/dist/compiler/training/rewind/gitTree.d.ts +14 -0
  207. package/dist/compiler/training/rewind/gitTree.js +90 -0
  208. package/dist/compiler/training/rewind/history.d.ts +28 -0
  209. package/dist/compiler/training/rewind/history.js +239 -0
  210. package/dist/compiler/training/rewind/index.d.ts +18 -0
  211. package/dist/compiler/training/rewind/index.js +15 -0
  212. package/dist/compiler/training/rewind/inputs.d.ts +9 -0
  213. package/dist/compiler/training/rewind/inputs.js +67 -0
  214. package/dist/compiler/training/rewind/instant.d.ts +4 -0
  215. package/dist/compiler/training/rewind/instant.js +24 -0
  216. package/dist/compiler/training/rewind/leakCheck.d.ts +10 -0
  217. package/dist/compiler/training/rewind/leakCheck.js +56 -0
  218. package/dist/compiler/training/rewind/leakFeed.d.ts +5 -0
  219. package/dist/compiler/training/rewind/leakFeed.js +25 -0
  220. package/dist/compiler/training/rewind/leakFiles.d.ts +10 -0
  221. package/dist/compiler/training/rewind/leakFiles.js +106 -0
  222. package/dist/compiler/training/rewind/leakGit.d.ts +3 -0
  223. package/dist/compiler/training/rewind/leakGit.js +78 -0
  224. package/dist/compiler/training/rewind/leakGitPayload.d.ts +4 -0
  225. package/dist/compiler/training/rewind/leakGitPayload.js +57 -0
  226. package/dist/compiler/training/rewind/leakInputs.d.ts +7 -0
  227. package/dist/compiler/training/rewind/leakInputs.js +135 -0
  228. package/dist/compiler/training/rewind/leakLayout.d.ts +2 -0
  229. package/dist/compiler/training/rewind/leakLayout.js +17 -0
  230. package/dist/compiler/training/rewind/leakProvenance.d.ts +3 -0
  231. package/dist/compiler/training/rewind/leakProvenance.js +88 -0
  232. package/dist/compiler/training/rewind/materialize.d.ts +136 -0
  233. package/dist/compiler/training/rewind/materialize.js +212 -0
  234. package/dist/compiler/training/rewind/memory.d.ts +10 -0
  235. package/dist/compiler/training/rewind/memory.js +84 -0
  236. package/dist/compiler/training/rewind/memorySchemas.d.ts +87 -0
  237. package/dist/compiler/training/rewind/memorySchemas.js +50 -0
  238. package/dist/compiler/training/rewind/moltnet.d.ts +2 -0
  239. package/dist/compiler/training/rewind/moltnet.js +54 -0
  240. package/dist/compiler/training/rewind/moltnetJson.d.ts +13 -0
  241. package/dist/compiler/training/rewind/moltnetJson.js +180 -0
  242. package/dist/compiler/training/rewind/moltnetSchemas.d.ts +1226 -0
  243. package/dist/compiler/training/rewind/moltnetSchemas.js +75 -0
  244. package/dist/compiler/training/rewind/moltnetSqlite.d.ts +31 -0
  245. package/dist/compiler/training/rewind/moltnetSqlite.js +192 -0
  246. package/dist/compiler/training/rewind/moltnetSqliteData.d.ts +10 -0
  247. package/dist/compiler/training/rewind/moltnetSqliteData.js +113 -0
  248. package/dist/compiler/training/rewind/recordInput.d.ts +40 -0
  249. package/dist/compiler/training/rewind/recordInput.js +49 -0
  250. package/dist/compiler/training/rewind/recordedBundle.d.ts +4 -0
  251. package/dist/compiler/training/rewind/recordedBundle.js +58 -0
  252. package/dist/compiler/training/rewind/recordedCommit.d.ts +2 -0
  253. package/dist/compiler/training/rewind/recordedCommit.js +10 -0
  254. package/dist/compiler/training/rewind/select.d.ts +18 -0
  255. package/dist/compiler/training/rewind/select.js +64 -0
  256. package/dist/compiler/training/rewind/snapshotManifest.d.ts +42 -0
  257. package/dist/compiler/training/rewind/snapshotManifest.js +134 -0
  258. package/dist/compiler/training/rewind/taskClock.d.ts +12 -0
  259. package/dist/compiler/training/rewind/taskClock.js +40 -0
  260. package/dist/compiler/training/rewind/timestamps.d.ts +4 -0
  261. package/dist/compiler/training/rewind/timestamps.js +26 -0
  262. package/dist/compiler/training/rewind/types.d.ts +43 -0
  263. package/dist/compiler/training/rewind/types.js +1 -0
  264. package/dist/compiler/training/rewind/volumes.d.ts +12 -0
  265. package/dist/compiler/training/rewind/volumes.js +58 -0
  266. package/dist/compiler/training/selection.d.ts +7 -0
  267. package/dist/compiler/training/selection.js +15 -0
  268. package/dist/compiler/types.d.ts +6 -3
  269. package/dist/compiler/upProject.d.ts +1 -0
  270. package/dist/compiler/upProject.js +21 -5
  271. package/dist/compiler/workspaceBundleArtifacts.d.ts +16 -1
  272. package/dist/compiler/workspaceBundleArtifacts.js +69 -20
  273. package/dist/compiler/workspaceBundleCache.d.ts +23 -0
  274. package/dist/compiler/workspaceBundleCache.js +79 -0
  275. package/dist/compiler/workspaceBundleDependencies.d.ts +21 -0
  276. package/dist/compiler/workspaceBundleDependencies.js +122 -0
  277. package/dist/compiler/workspaceBundleFiles.d.ts +51 -0
  278. package/dist/compiler/workspaceBundleFiles.js +251 -0
  279. package/dist/compiler/workspaceBundleGenerated.d.ts +25 -0
  280. package/dist/compiler/workspaceBundleGenerated.js +103 -0
  281. package/dist/compiler/workspaceBundleGit.d.ts +64 -0
  282. package/dist/compiler/workspaceBundleGit.js +154 -0
  283. package/dist/compiler/workspaceBundleKey.d.ts +34 -0
  284. package/dist/compiler/workspaceBundleKey.js +17 -0
  285. package/dist/compiler/workspaceBundleResolve.d.ts +56 -0
  286. package/dist/compiler/workspaceBundleResolve.js +144 -0
  287. package/dist/compiler/workspaceBundleRun.d.ts +41 -0
  288. package/dist/compiler/workspaceBundleRun.js +79 -0
  289. package/dist/compiler/workspaceBundleTar.d.ts +47 -0
  290. package/dist/compiler/workspaceBundleTar.js +140 -0
  291. package/dist/compiler/workspaceBundleTree.d.ts +15 -0
  292. package/dist/compiler/workspaceBundleTree.js +58 -0
  293. package/dist/compiler/workspaceGitFetch.d.ts +41 -0
  294. package/dist/compiler/workspaceGitFetch.js +155 -0
  295. package/dist/compiler/workspaceGitPins.d.ts +33 -0
  296. package/dist/compiler/workspaceGitPins.js +127 -0
  297. package/dist/compiler/workspaceResources.d.ts +5 -1
  298. package/dist/compiler/workspaceResources.js +32 -5
  299. package/dist/deployment/activation.d.ts +3 -0
  300. package/dist/deployment/activation.js +12 -0
  301. package/dist/deployment/canaryCutover.d.ts +1 -1
  302. package/dist/deployment/candidateRecording.d.ts +8 -0
  303. package/dist/deployment/candidateRecording.js +22 -0
  304. package/dist/deployment/dockerManager.d.ts +5 -3
  305. package/dist/deployment/dockerManager.js +12 -2
  306. package/dist/deployment/downDeployment.d.ts +2 -0
  307. package/dist/deployment/downDeployment.js +6 -2
  308. package/dist/deployment/homeStore.d.ts +9 -1
  309. package/dist/deployment/homeStore.js +16 -53
  310. package/dist/deployment/index.d.ts +6 -0
  311. package/dist/deployment/index.js +6 -0
  312. package/dist/deployment/pidLock.d.ts +25 -0
  313. package/dist/deployment/pidLock.js +81 -0
  314. package/dist/deployment/record.d.ts +85 -1
  315. package/dist/deployment/record.js +11 -5
  316. package/dist/deployment/recordPublication.d.ts +15 -0
  317. package/dist/deployment/recordPublication.js +76 -0
  318. package/dist/deployment/recordedMemoryRetention.d.ts +18 -0
  319. package/dist/deployment/recordedMemoryRetention.js +27 -0
  320. package/dist/deployment/resourceLog.d.ts +140 -0
  321. package/dist/deployment/resourceLog.js +78 -0
  322. package/dist/distribution/buildDistributionReport.d.ts +3 -0
  323. package/dist/distribution/buildDistributionReport.js +5 -0
  324. package/dist/distribution/consumeImage.d.ts +3 -0
  325. package/dist/distribution/consumeImage.js +23 -31
  326. package/dist/distribution/consumeImageLifecycle.d.ts +4 -1
  327. package/dist/distribution/consumeImageLifecycle.js +14 -3
  328. package/dist/distribution/consumeImageSupport.d.ts +7 -0
  329. package/dist/distribution/consumeImageSupport.js +25 -0
  330. package/dist/distribution/distributionReportSchema.d.ts +47 -0
  331. package/dist/distribution/distributionReportSchema.js +20 -0
  332. package/dist/distribution/renderImageInterface.js +1 -1
  333. package/dist/distribution/types.d.ts +15 -0
  334. package/dist/filesystem/durableWrite.d.ts +10 -0
  335. package/dist/filesystem/durableWrite.js +87 -0
  336. package/dist/filesystem/index.d.ts +2 -0
  337. package/dist/filesystem/index.js +2 -0
  338. package/dist/filesystem/retainedJsonl.d.ts +23 -0
  339. package/dist/filesystem/retainedJsonl.js +123 -0
  340. package/dist/manifest/commandSchemas.d.ts +5 -0
  341. package/dist/manifest/commandSchemas.js +5 -0
  342. package/dist/manifest/executionSchemas.d.ts +20 -0
  343. package/dist/manifest/executionSchemas.js +13 -3
  344. package/dist/manifest/index.d.ts +2 -0
  345. package/dist/manifest/index.js +2 -0
  346. package/dist/manifest/loadManifest.js +3 -26
  347. package/dist/manifest/mcpSchemas.d.ts +61 -0
  348. package/dist/manifest/mcpSchemas.js +57 -11
  349. package/dist/manifest/memorySchemas.d.ts +1 -1
  350. package/dist/manifest/memorySchemas.js +1 -1
  351. package/dist/manifest/parseManifest.d.ts +7 -0
  352. package/dist/manifest/parseManifest.js +65 -0
  353. package/dist/manifest/renderSpawnfile.js +4 -1
  354. package/dist/manifest/renderSpawnfileWorkspace.js +6 -2
  355. package/dist/manifest/schemas.d.ts +1206 -132
  356. package/dist/manifest/schemas.js +14 -4
  357. package/dist/manifest/teamNetworkSchemas.d.ts +1 -1
  358. package/dist/manifest/trainingDataSchemas.d.ts +357 -0
  359. package/dist/manifest/trainingDataSchemas.js +31 -0
  360. package/dist/manifest/trainingSchemas.d.ts +49 -0
  361. package/dist/manifest/trainingSchemas.js +44 -0
  362. package/dist/manifest/volumeFeedSchemas.d.ts +52 -0
  363. package/dist/manifest/volumeFeedSchemas.js +104 -0
  364. package/dist/manifest/workspaceSchemas.d.ts +182 -5
  365. package/dist/manifest/workspaceSchemas.js +111 -7
  366. package/dist/recording/compose.d.ts +3 -0
  367. package/dist/recording/compose.js +93 -0
  368. package/dist/recording/deployment.d.ts +15 -0
  369. package/dist/recording/deployment.js +38 -0
  370. package/dist/recording/history.d.ts +16 -0
  371. package/dist/recording/history.js +47 -0
  372. package/dist/recording/index.d.ts +9 -0
  373. package/dist/recording/index.js +9 -0
  374. package/dist/recording/moment.d.ts +61 -0
  375. package/dist/recording/moment.js +36 -0
  376. package/dist/recording/parse.d.ts +15 -0
  377. package/dist/recording/parse.js +74 -0
  378. package/dist/recording/read.d.ts +8 -0
  379. package/dist/recording/read.js +61 -0
  380. package/dist/recording/store.d.ts +10 -0
  381. package/dist/recording/store.js +94 -0
  382. package/dist/recording/sync.d.ts +24 -0
  383. package/dist/recording/sync.js +126 -0
  384. package/dist/recording/types.d.ts +45 -0
  385. package/dist/recording/types.js +2 -0
  386. package/dist/release/drainControl.d.ts +54 -0
  387. package/dist/release/drainControl.js +137 -0
  388. package/dist/release/drainPhase.d.ts +37 -0
  389. package/dist/release/drainPhase.js +161 -0
  390. package/dist/release/index.d.ts +11 -0
  391. package/dist/release/index.js +11 -0
  392. package/dist/release/ledger.d.ts +75 -0
  393. package/dist/release/ledger.js +111 -0
  394. package/dist/release/notify.d.ts +58 -0
  395. package/dist/release/notify.js +126 -0
  396. package/dist/release/pending.d.ts +35 -0
  397. package/dist/release/pending.js +60 -0
  398. package/dist/release/postDeploy.d.ts +32 -0
  399. package/dist/release/postDeploy.js +62 -0
  400. package/dist/release/releaseDefaults.d.ts +9 -0
  401. package/dist/release/releaseDefaults.js +115 -0
  402. package/dist/release/releaseDocker.d.ts +38 -0
  403. package/dist/release/releaseDocker.js +94 -0
  404. package/dist/release/releaseTypes.d.ts +94 -0
  405. package/dist/release/releaseTypes.js +1 -0
  406. package/dist/release/runRelease.d.ts +9 -0
  407. package/dist/release/runRelease.js +218 -0
  408. package/dist/release/swap.d.ts +23 -0
  409. package/dist/release/swap.js +55 -0
  410. package/dist/release/types.d.ts +50 -0
  411. package/dist/release/types.js +29 -0
  412. package/dist/report/index.d.ts +1 -0
  413. package/dist/report/index.js +1 -0
  414. package/dist/report/recording.d.ts +57 -0
  415. package/dist/report/recording.js +26 -0
  416. package/dist/report/types.d.ts +58 -0
  417. package/dist/runtime/container.js +8 -13
  418. package/dist/runtime/daimon/adapter.js +15 -4
  419. package/dist/runtime/daimon/config.d.ts +4 -0
  420. package/dist/runtime/daimon/config.js +53 -4
  421. package/dist/runtime/daimon/contract-manifest.json +1 -0
  422. package/dist/runtime/daimon/contract-manifest.sha256 +1 -0
  423. package/dist/runtime/daimon/contractManifest.d.ts +59 -8
  424. package/dist/runtime/daimon/contractManifest.js +26 -10
  425. package/dist/runtime/daimon/grokTurnLimits.d.ts +25 -0
  426. package/dist/runtime/daimon/grokTurnLimits.js +51 -0
  427. package/dist/runtime/daimon/grokWorkerConfigBytes.d.ts +17 -0
  428. package/dist/runtime/daimon/grokWorkerConfigBytes.js +17 -0
  429. package/dist/runtime/daimon/grokWorkerContract.d.ts +5 -0
  430. package/dist/runtime/daimon/grokWorkerContract.js +13 -1
  431. package/dist/runtime/daimon/recording.d.ts +19 -0
  432. package/dist/runtime/daimon/recording.js +50 -0
  433. package/dist/runtime/daimon/scheduleAuthority.d.ts +8 -0
  434. package/dist/runtime/daimon/scheduleAuthority.js +19 -5
  435. package/dist/runtime/daimon/wakeMoments.d.ts +15 -0
  436. package/dist/runtime/daimon/wakeMoments.js +17 -0
  437. package/dist/runtime/daimonInstall.d.ts +3 -0
  438. package/dist/runtime/daimonInstall.js +25 -0
  439. package/dist/runtime/index.d.ts +1 -0
  440. package/dist/runtime/index.js +1 -0
  441. package/dist/runtime/install.d.ts +2 -0
  442. package/dist/runtime/install.js +1 -0
  443. package/dist/runtime/openclaw/adapter.js +3 -0
  444. package/dist/runtime/pi/adapter.js +3 -0
  445. package/dist/runtime/pi/containerTargets.js +6 -0
  446. package/dist/runtime/picoclaw/adapter.js +3 -0
  447. package/dist/runtime/registry.d.ts +3 -0
  448. package/dist/runtime/registry.js +13 -1
  449. package/dist/runtime/types.d.ts +15 -0
  450. package/dist/runtime/usageLedgerRead.d.ts +6 -0
  451. package/dist/runtime/usageLedgerRead.js +3 -3
  452. package/dist/shared/index.d.ts +1 -0
  453. package/dist/shared/index.js +1 -0
  454. package/dist/shared/recording.d.ts +8 -0
  455. package/dist/shared/recording.js +25 -0
  456. package/dist/target/dockerCommandExecutorCore.js +1 -1
  457. package/dist/target/dockerTargetBinding.js +2 -1
  458. package/dist/volume/feedLand.d.ts +31 -0
  459. package/dist/volume/feedLand.js +174 -0
  460. package/dist/volume/feedLandings.d.ts +45 -0
  461. package/dist/volume/feedLandings.js +111 -0
  462. package/dist/volume/feedLayout.d.ts +61 -0
  463. package/dist/volume/feedLayout.js +234 -0
  464. package/dist/volume/feedLock.d.ts +7 -0
  465. package/dist/volume/feedLock.js +131 -0
  466. package/dist/volume/feedManifest.d.ts +27 -0
  467. package/dist/volume/feedManifest.js +123 -0
  468. package/dist/volume/feedPrepare.d.ts +16 -0
  469. package/dist/volume/feedPrepare.js +228 -0
  470. package/dist/volume/feedProject.d.ts +39 -0
  471. package/dist/volume/feedProject.js +92 -0
  472. package/dist/volume/feedRecord.d.ts +55 -0
  473. package/dist/volume/feedRecord.js +148 -0
  474. package/dist/volume/feedRef.d.ts +43 -0
  475. package/dist/volume/feedRef.js +117 -0
  476. package/dist/volume/feedRefresh.d.ts +15 -0
  477. package/dist/volume/feedRefresh.js +187 -0
  478. package/dist/volume/feedSource.d.ts +66 -0
  479. package/dist/volume/feedSource.js +104 -0
  480. package/dist/volume/feedTarget.d.ts +70 -0
  481. package/dist/volume/feedTarget.js +9 -0
  482. package/dist/volume/feedTestKit.d.ts +11 -0
  483. package/dist/volume/feedTestKit.js +45 -0
  484. package/dist/volume/feedVerify.d.ts +18 -0
  485. package/dist/volume/feedVerify.js +104 -0
  486. package/dist/volume/index.d.ts +13 -0
  487. package/dist/volume/index.js +13 -0
  488. package/moltnet-releases.json +4 -4
  489. package/package.json +3 -3
  490. package/runtimes.yaml +8 -6
@@ -0,0 +1,61 @@
1
+ import { readlinkSync, renameSync, symlinkSync, unlinkSync } from "node:fs";
2
+ import type { Stats } from "node:fs";
3
+ import { SpawnfileError } from "../shared/index.js";
4
+ export declare const FEED_TREES_DIR = "trees";
5
+ export declare const FEED_CURRENT_LINK = "current";
6
+ export declare const FEED_IDENTITY_FILE = ".spawnfile-feed.json";
7
+ export declare const VOLUME_RESOURCE_SENTINEL = ".spawnfile-resource-identity";
8
+ export declare const VOLUME_ROOT_MODE = 493;
9
+ /** Every name the host itself ever writes at the volume root, plus the container's own sentinel. */
10
+ export declare const FEED_ROOT_NAMES: readonly string[];
11
+ export declare const feedError: (message: string) => SpawnfileError;
12
+ export declare const feedTreeLink: (revision: string) => string;
13
+ /** Read before any write, and a refusal rather than a repair: the start guard owns that contract. */
14
+ export declare const assertFeedVolumeRoot: (volume: string) => void;
15
+ /** Absent is not an error (null); a symlink or file where a host directory belongs is a refusal, never a repair. */
16
+ export declare const assertRealDirectory: (target: string, label: string) => Stats | null;
17
+ /** rename(2) is atomic only within one filesystem; a cross-device "rename" is a copy readers can watch. */
18
+ export declare const assertSameDevice: (volume: string, other: string, label: string) => void;
19
+ /** Host state must never live where an agent can write it: it decides what a root-run job deletes. */
20
+ export declare const assertOutsideVolume: (volume: string, target: string, label: string) => void;
21
+ /**
22
+ * Ownership and read-only modes for a staged tree, before it is reachable. Never follows a symlink.
23
+ * The top directory stays 0755 so the one rename into the volume works for a non-root writer;
24
+ * `landTree` freezes it the moment it lands.
25
+ */
26
+ export declare const ownAndFreeze: (stagingDir: string, { owner, volume }: {
27
+ owner?: string;
28
+ volume: string;
29
+ }) => void;
30
+ /** The one rename that makes a validated tree part of the volume, with its root frozen the moment it lands. */
31
+ export declare const landTree: (stagingDir: string, treePath: string) => void;
32
+ /** Every filesystem call the link swap makes, so a test can watch what the swap actually does. */
33
+ export declare const LINK_OPS: Readonly<{
34
+ lstat: import("fs").StatSyncFn;
35
+ readlink: typeof readlinkSync;
36
+ rename: typeof renameSync;
37
+ symlink: typeof symlinkSync;
38
+ unlink: typeof unlinkSync;
39
+ }>;
40
+ export type LinkOps = typeof LINK_OPS;
41
+ export declare const moveAsideCommand: (volume: string, target: string) => string;
42
+ /**
43
+ * ATOMIC OR NOTHING. The live name is never unlinked: a new link is created under a temporary name outside
44
+ * the volume and rename(2)'d over the live one, so a reader resolving `current` sees the old target or
45
+ * the new one, never a missing name. The target is relative so it resolves the same on the host and at
46
+ * the container mount. Returns false when the link already points at `target`.
47
+ */
48
+ export declare const pointCurrent: (volume: string, target: string, { ops, tmpDir, beforeRename }: {
49
+ ops?: LinkOps;
50
+ tmpDir: string;
51
+ beforeRename?: () => void;
52
+ }) => boolean;
53
+ /** Thaw and delete a tree OUTSIDE the volume. A recursive delete is only ever aimed at a real directory. */
54
+ export declare const removeTree: (target: string, { volume }: {
55
+ volume: string;
56
+ }) => void;
57
+ /** One rename out of the volume. Write on the moved directory is needed to change its parent. */
58
+ export declare const parkTree: (treePath: string, trash: string, stamp: string) => string;
59
+ /** Names at the volume root the host never writes. Reported, never deleted: they are evidence. */
60
+ export declare const auditFeedRoot: (volume: string) => string[];
61
+ export declare const treeShape: (target: string) => "missing" | "directory" | "symlink" | "file";
@@ -0,0 +1,234 @@
1
+ // The physical layout of a fed volume, and every rule about what the host writer may touch inside it.
2
+ //
3
+ // WHY THESE RULES ARE NOT NEGOTIABLE
4
+ // ----------------------------------
5
+ // A fed volume is mounted into running agents, and the container's start guard inspects the volume
6
+ // root on every start: the root must stay mode 0755 and `.spawnfile-resource-identity` must stay exactly
7
+ // the bytes, owner and mode the container wrote. Readers inside the agents never pause for a refresh.
8
+ // So a host writer can break every agent without writing a byte an agent reads:
9
+ //
10
+ // * a partially copied tree, or a tree mid-delete, inside the volume -> a reader (or the start
11
+ // guard) lists an entry that is gone by the time it is opened;
12
+ // * a recursive chown/chmod aimed at the volume root -> the identity sentinel changes -> the next
13
+ // container start refuses the volume;
14
+ // * the root's own mode moved off 0755 -> the start guard refuses the volume.
15
+ //
16
+ // Hence the shape every function here enforces: content is staged OUTSIDE the volume, enters it through
17
+ // exactly one rename(2), becomes visible through one rename(2) of the `current` link, leaves it through
18
+ // exactly one rename(2), and is deleted outside it. No recursive mode or ownership change is ever aimed
19
+ // at anything but a tree the host still holds privately.
20
+ //
21
+ // <state>/staging/<revision>-<pid>/ copied, validated, owned, frozen here
22
+ // | rename(2)
23
+ // <volume>/trees/<revision>/ immutable content, never rewritten in place
24
+ // <volume>/current -> trees/<revision> rename(2) over the old link
25
+ // <volume>/.spawnfile-feed.json which revision `current` serves
26
+ // | rename(2)
27
+ // <state>/trash/<revision>-<stamp>/ deleted out here, never inside the volume
28
+ //
29
+ // The volume root stays writable by the agent uid (the container chowns it on start), so the host never
30
+ // takes a decision from a name inside the volume: every path it is about to touch is lstat'd and refused
31
+ // unless it is the real directory the host created.
32
+ import { chmodSync, lchownSync, lstatSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, unlinkSync } from "node:fs";
33
+ import path from "node:path";
34
+ import { SpawnfileError } from "../shared/index.js";
35
+ export const FEED_TREES_DIR = "trees";
36
+ export const FEED_CURRENT_LINK = "current";
37
+ export const FEED_IDENTITY_FILE = ".spawnfile-feed.json";
38
+ export const VOLUME_RESOURCE_SENTINEL = ".spawnfile-resource-identity";
39
+ export const VOLUME_ROOT_MODE = 0o755;
40
+ /** Every name the host itself ever writes at the volume root, plus the container's own sentinel. */
41
+ export const FEED_ROOT_NAMES = Object.freeze([VOLUME_RESOURCE_SENTINEL, FEED_IDENTITY_FILE, FEED_TREES_DIR, FEED_CURRENT_LINK]);
42
+ export const feedError = (message) => new SpawnfileError("runtime_error", message);
43
+ const fail = (message) => { throw feedError(message); };
44
+ export const feedTreeLink = (revision) => `${FEED_TREES_DIR}/${revision}`;
45
+ const readlinkOrNull = (target) => { try {
46
+ return readlinkSync(target);
47
+ }
48
+ catch {
49
+ return null;
50
+ } };
51
+ /** Resolved through symlinks even when the leaf does not exist yet: the nearest existing ancestor is realpath'd. */
52
+ const canonical = (target) => {
53
+ const absolute = path.resolve(target);
54
+ try {
55
+ return realpathSync(absolute);
56
+ }
57
+ catch {
58
+ const parent = path.dirname(absolute);
59
+ return parent === absolute ? absolute : path.join(canonical(parent), path.basename(absolute));
60
+ }
61
+ };
62
+ /** Read before any write, and a refusal rather than a repair: the start guard owns that contract. */
63
+ export const assertFeedVolumeRoot = (volume) => {
64
+ let stat;
65
+ try {
66
+ stat = statSync(volume);
67
+ }
68
+ catch {
69
+ return fail(`fed volume ${volume} does not exist; deploy the organization once so the volume is created`);
70
+ }
71
+ if (!stat.isDirectory())
72
+ fail(`fed volume ${volume} is not a directory`);
73
+ const mode = stat.mode & 0o7777;
74
+ if (mode !== VOLUME_ROOT_MODE) {
75
+ fail(`fed volume root ${volume} is mode 0${mode.toString(8)}, must be 0755: the container start guard refuses any other mode. Repair with: chmod 0755 ${volume}`);
76
+ }
77
+ // The container bootstraps an EMPTY volume and refuses a non-empty one without its sentinel, so content
78
+ // landed before the first start would stop every agent from starting at all.
79
+ let sentinel = null;
80
+ try {
81
+ sentinel = lstatSync(path.join(volume, VOLUME_RESOURCE_SENTINEL));
82
+ }
83
+ catch { /* reported below */ }
84
+ if (!sentinel?.isFile()) {
85
+ fail(`fed volume ${volume} has no ${VOLUME_RESOURCE_SENTINEL}: start the organization once so the container initializes the volume, then refresh it`);
86
+ }
87
+ };
88
+ /** Absent is not an error (null); a symlink or file where a host directory belongs is a refusal, never a repair. */
89
+ export const assertRealDirectory = (target, label) => {
90
+ let stat;
91
+ try {
92
+ stat = lstatSync(target);
93
+ }
94
+ catch (error) {
95
+ if (error.code === "ENOENT")
96
+ return null;
97
+ throw error;
98
+ }
99
+ if (stat.isSymbolicLink()) {
100
+ fail(`${label} ${target} is a symlink to ${JSON.stringify(readlinkOrNull(target))}, not a directory; nothing on this host creates it, so the volume was written by something else. Refusing to operate through it.`);
101
+ }
102
+ if (!stat.isDirectory())
103
+ fail(`${label} ${target} is not a directory; refusing to operate on it`);
104
+ return stat;
105
+ };
106
+ /** rename(2) is atomic only within one filesystem; a cross-device "rename" is a copy readers can watch. */
107
+ export const assertSameDevice = (volume, other, label) => {
108
+ const want = statSync(volume).dev, got = statSync(other).dev;
109
+ if (want !== got)
110
+ fail(`${label} ${other} is on device ${got} but the fed volume ${volume} is on device ${want}; every swap depends on rename(2), which cannot cross filesystems. Put ${label} on the volume's filesystem.`);
111
+ };
112
+ /** Host state must never live where an agent can write it: it decides what a root-run job deletes. */
113
+ export const assertOutsideVolume = (volume, target, label) => {
114
+ const root = canonical(volume), scope = canonical(target);
115
+ // Whole path components: a sibling named `..state` is inside the volume, `../state` is not.
116
+ const escapes = (relative) => relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative);
117
+ if (!escapes(path.relative(root, scope)) || !escapes(path.relative(scope, root))) {
118
+ fail(`${label} ${scope} overlaps the fed volume ${root}; host state and staging must live outside the volume`);
119
+ }
120
+ };
121
+ const walkPostOrder = (root, visit) => {
122
+ const walk = (target) => {
123
+ const stat = lstatSync(target);
124
+ if (stat.isDirectory())
125
+ for (const name of readdirSync(target).sort())
126
+ walk(path.join(target, name));
127
+ visit(target, stat);
128
+ };
129
+ walk(root);
130
+ };
131
+ /**
132
+ * Ownership and read-only modes for a staged tree, before it is reachable. Never follows a symlink.
133
+ * The top directory stays 0755 so the one rename into the volume works for a non-root writer;
134
+ * `landTree` freezes it the moment it lands.
135
+ */
136
+ export const ownAndFreeze = (stagingDir, { owner, volume }) => {
137
+ assertOutsideVolume(volume, stagingDir, "the staging tree");
138
+ const [uid, gid] = owner ? owner.split(":").map(Number) : [undefined, undefined];
139
+ walkPostOrder(stagingDir, (target, stat) => {
140
+ if (!stat.isFile() && !stat.isDirectory() && !stat.isSymbolicLink())
141
+ fail(`${target} is a special file; a fed volume carries only files, directories and symlinks`);
142
+ if (uid !== undefined && gid !== undefined)
143
+ lchownSync(target, uid, gid);
144
+ if (stat.isSymbolicLink())
145
+ return;
146
+ chmodSync(target, target === stagingDir ? 0o755 : (stat.mode & 0o7777) & ~0o222);
147
+ });
148
+ };
149
+ /** The one rename that makes a validated tree part of the volume, with its root frozen the moment it lands. */
150
+ export const landTree = (stagingDir, treePath) => {
151
+ renameSync(stagingDir, treePath);
152
+ chmodSync(treePath, 0o555);
153
+ };
154
+ /** Every filesystem call the link swap makes, so a test can watch what the swap actually does. */
155
+ export const LINK_OPS = Object.freeze({ lstat: lstatSync, readlink: readlinkSync, rename: renameSync, symlink: symlinkSync, unlink: unlinkSync });
156
+ export const moveAsideCommand = (volume, target) => `mv -- ${target} ${path.join(path.dirname(volume), `${path.basename(target)}.planted`)}`;
157
+ /**
158
+ * ATOMIC OR NOTHING. The live name is never unlinked: a new link is created under a temporary name outside
159
+ * the volume and rename(2)'d over the live one, so a reader resolving `current` sees the old target or
160
+ * the new one, never a missing name. The target is relative so it resolves the same on the host and at
161
+ * the container mount. Returns false when the link already points at `target`.
162
+ */
163
+ export const pointCurrent = (volume, target, { ops = LINK_OPS, tmpDir, beforeRename }) => {
164
+ const live = path.join(volume, FEED_CURRENT_LINK);
165
+ let existing = null;
166
+ try {
167
+ existing = ops.lstat(live);
168
+ }
169
+ catch { /* absent: the first land */ }
170
+ if (existing && !existing.isSymbolicLink()) {
171
+ fail(`${live} is a real ${existing.isDirectory() ? "directory" : "file"}, not the host's link; nothing here deletes a name it did not write. Clear it with: ${moveAsideCommand(volume, live)}`);
172
+ }
173
+ if (existing && ops.readlink(live) === target)
174
+ return false;
175
+ const tmp = path.join(tmpDir, `.${FEED_CURRENT_LINK}.${process.pid}.tmp`);
176
+ try {
177
+ ops.unlink(tmp);
178
+ }
179
+ catch { /* no leftover from a crashed run */ }
180
+ ops.symlink(target, tmp);
181
+ beforeRename?.();
182
+ ops.rename(tmp, live);
183
+ return true;
184
+ };
185
+ /** Thaw and delete a tree OUTSIDE the volume. A recursive delete is only ever aimed at a real directory. */
186
+ export const removeTree = (target, { volume }) => {
187
+ assertOutsideVolume(volume, target, "the tree being deleted");
188
+ let stat;
189
+ try {
190
+ stat = lstatSync(target);
191
+ }
192
+ catch {
193
+ return;
194
+ }
195
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
196
+ rmSync(target, { force: true });
197
+ return;
198
+ }
199
+ const thaw = (directory) => {
200
+ chmodSync(directory, 0o700);
201
+ for (const entry of readdirSync(directory, { withFileTypes: true }))
202
+ if (entry.isDirectory())
203
+ thaw(path.join(directory, entry.name));
204
+ };
205
+ thaw(target);
206
+ rmSync(target, { recursive: true, force: true });
207
+ };
208
+ /** One rename out of the volume. Write on the moved directory is needed to change its parent. */
209
+ export const parkTree = (treePath, trash, stamp) => {
210
+ assertRealDirectory(treePath, "the fed tree being retired");
211
+ const parked = path.join(trash, `${path.basename(treePath)}-${stamp}`);
212
+ chmodSync(treePath, 0o755);
213
+ renameSync(treePath, parked);
214
+ return parked;
215
+ };
216
+ /** Names at the volume root the host never writes. Reported, never deleted: they are evidence. */
217
+ export const auditFeedRoot = (volume) => {
218
+ const findings = [];
219
+ for (const entry of readdirSync(volume, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
220
+ if (!FEED_ROOT_NAMES.includes(entry.name))
221
+ findings.push(`${entry.name} is a name this host never writes at the volume root`);
222
+ }
223
+ return findings;
224
+ };
225
+ export const treeShape = (target) => {
226
+ let stat;
227
+ try {
228
+ stat = lstatSync(target);
229
+ }
230
+ catch {
231
+ return "missing";
232
+ }
233
+ return stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
234
+ };
@@ -0,0 +1,7 @@
1
+ export declare const FEED_LOCK_FILE = "lock";
2
+ export interface FeedLock {
3
+ path: string;
4
+ release: () => void;
5
+ }
6
+ /** Null when another live writer holds the lock. */
7
+ export declare const acquireFeedLock: (stateDir: string) => FeedLock | null;
@@ -0,0 +1,131 @@
1
+ // ONE WRITER AT A TIME. Two refreshes racing are survivable (content is addressed by revision), but a
2
+ // refresh racing a garbage collection or a re-land is a reader watching entries move. The lock is a
3
+ // file created with O_EXCL in the host state directory, carrying the holder's pid and a random token.
4
+ //
5
+ // A crashed holder leaves the file behind, so a holder whose pid is gone on this host is stale. Reclaiming
6
+ // it is serialized by a second O_EXCL guard file that also names its holder's pid. A guard is cleared only
7
+ // when its own holder is provably dead, never by age: an age rule lets a live but slow reclaimer lose its
8
+ // guard and then delete the lock a second reclaimer just took. Under the guard the lock is re-read and
9
+ // removed only if it is still the exact stale bytes that were judged.
10
+ //
11
+ // A lock or guard that cannot be read as an owner (a crash between create and write) is not contention:
12
+ // it is reported, with the one command that clears it, instead of reading as "busy" forever.
13
+ //
14
+ // Every job that must not interleave with a swap (a release that recreates containers, for example)
15
+ // takes this same lock through `acquireFeedLock`.
16
+ import { randomUUID } from "node:crypto";
17
+ import { closeSync, mkdirSync, openSync, readFileSync, rmSync, unlinkSync, writeSync } from "node:fs";
18
+ import { hostname } from "node:os";
19
+ import path from "node:path";
20
+ import { feedError } from "./feedLayout.js";
21
+ export const FEED_LOCK_FILE = "lock";
22
+ const processAlive = (pid) => {
23
+ try {
24
+ process.kill(pid, 0);
25
+ return true;
26
+ }
27
+ catch (error) {
28
+ return error.code === "EPERM";
29
+ }
30
+ };
31
+ const createExclusive = (file, content) => {
32
+ let fd;
33
+ try {
34
+ fd = openSync(file, "wx", 0o600);
35
+ }
36
+ catch (error) {
37
+ if (error.code === "EEXIST")
38
+ return false;
39
+ throw error;
40
+ }
41
+ try {
42
+ writeSync(fd, content);
43
+ }
44
+ finally {
45
+ closeSync(fd);
46
+ }
47
+ return true;
48
+ };
49
+ const readOrNull = (file) => { try {
50
+ return readFileSync(file, "utf8");
51
+ }
52
+ catch {
53
+ return null;
54
+ } };
55
+ /** An owner record, allowing a live writer the moment between creating the file and writing it. */
56
+ const readOwnerText = (file) => {
57
+ const first = readOrNull(file);
58
+ if (first === null || parseOwner(first) !== null)
59
+ return first;
60
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100);
61
+ return readOrNull(file);
62
+ };
63
+ const parseOwner = (text) => {
64
+ try {
65
+ const value = JSON.parse(text ?? "");
66
+ return Number.isInteger(value.pid) && typeof value.host === "string" ? value : null;
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ };
72
+ /** Stale only when provably dead: same host and no such pid. An unreadable lock is a live one. */
73
+ const isStale = (text) => {
74
+ const owner = parseOwner(text);
75
+ return owner !== null && owner.host === hostname() && !processAlive(owner.pid);
76
+ };
77
+ const ownerContent = () => `${JSON.stringify({ at: new Date().toISOString(), host: hostname(), pid: process.pid, token: randomUUID() })}\n`;
78
+ const malformed = (file) => {
79
+ throw feedError(`the feed lock ${file} does not name a holder (a writer crashed while taking it); if no refresh is running, clear it with: rm -- ${file}`);
80
+ };
81
+ const reclaim = (file, judged) => {
82
+ const guard = `${file}.reclaim`, mine = ownerContent();
83
+ if (!createExclusive(guard, mine)) {
84
+ const held = readOwnerText(guard);
85
+ if (held === null)
86
+ return false;
87
+ if (parseOwner(held) === null)
88
+ malformed(guard);
89
+ if (!isStale(held))
90
+ return false;
91
+ // Clear the dead guard only if it is still the bytes judged dead, then compete for it again.
92
+ if (readOrNull(guard) !== held)
93
+ return false;
94
+ rmSync(guard, { force: true });
95
+ if (!createExclusive(guard, mine))
96
+ return false;
97
+ }
98
+ try {
99
+ if (readOrNull(file) !== judged)
100
+ return false;
101
+ unlinkSync(file);
102
+ return true;
103
+ }
104
+ finally {
105
+ if (readOrNull(guard) === mine)
106
+ rmSync(guard, { force: true });
107
+ }
108
+ };
109
+ /** Null when another live writer holds the lock. */
110
+ export const acquireFeedLock = (stateDir) => {
111
+ mkdirSync(stateDir, { mode: 0o700, recursive: true });
112
+ const file = path.join(stateDir, FEED_LOCK_FILE);
113
+ const content = ownerContent();
114
+ for (let attempt = 0; attempt < 2; attempt += 1) {
115
+ if (createExclusive(file, content)) {
116
+ return {
117
+ path: file,
118
+ release: () => { if (readOrNull(file) === content)
119
+ rmSync(file, { force: true }); }
120
+ };
121
+ }
122
+ const held = readOwnerText(file);
123
+ if (held === null)
124
+ continue;
125
+ if (parseOwner(held) === null)
126
+ malformed(file);
127
+ if (!isStale(held) || !reclaim(file, held))
128
+ return null;
129
+ }
130
+ return null;
131
+ };
@@ -0,0 +1,27 @@
1
+ export declare const FEED_MANIFEST_VERSION = "spawnfile.volume-feed-manifest.v1";
2
+ export type FeedManifestEntry = ["d", number] | ["f", number, number, number, string] | ["l", string];
3
+ export interface FeedManifest {
4
+ entries: Record<string, FeedManifestEntry>;
5
+ files: number;
6
+ /** Physical tree name the manifest describes. */
7
+ tree: string;
8
+ version: typeof FEED_MANIFEST_VERSION;
9
+ }
10
+ export interface FeedManifestComparison {
11
+ changed: string[];
12
+ extra: string[];
13
+ missing: string[];
14
+ touched: string[];
15
+ }
16
+ export declare const sha256Hex: (value: string | Buffer) => string;
17
+ /** Built from the frozen staged tree, so it records exactly the modes and bytes that land. */
18
+ export declare const buildFeedManifest: (root: string, tree: string) => FeedManifest;
19
+ /**
20
+ * `changed` is drift on its own evidence (kind, size, mode, link target, or a same-size rewrite whose
21
+ * digest moved); `touched` is a file whose mtime moved while its bytes did not.
22
+ */
23
+ export declare const compareFeedManifest: (root: string, manifest: FeedManifest) => FeedManifestComparison;
24
+ export declare const manifestDrift: (comparison: FeedManifestComparison) => string[];
25
+ export declare const writeFeedManifest: (stateDir: string, manifest: FeedManifest) => void;
26
+ export declare const readFeedManifest: (stateDir: string, tree: string) => FeedManifest | null;
27
+ export declare const removeFeedManifest: (stateDir: string, tree: string) => void;
@@ -0,0 +1,123 @@
1
+ // The land-time manifest of a fed tree: what the host put there, kept OUTSIDE the volume.
2
+ //
3
+ // WHY VERIFY INSTEAD OF PREVENT: the mount stays mutable (the container owns the volume root), and an
4
+ // owner can always restore its own write bit, so nothing on the host can make the landed tree
5
+ // tamper-proof. The honest guarantee is "cannot be changed unnoticed for longer than one refresh".
6
+ //
7
+ // EVERY FILE IS HASHED ON EVERY CHECK. A stat-only pass is cheaper, but an owner can rewrite a file with
8
+ // equal-length bytes and put its mtime back, which no stat tuple notices. A fed tree is read in full by
9
+ // agents anyway; reading it once per refresh is the price of a check that cannot be fooled that way.
10
+ // Every walk here uses lstat and never descends a symlink: an agent can plant one.
11
+ import { createHash } from "node:crypto";
12
+ import { lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, writeFileSync } from "node:fs";
13
+ import path from "node:path";
14
+ export const FEED_MANIFEST_VERSION = "spawnfile.volume-feed-manifest.v1";
15
+ export const sha256Hex = (value) => createHash("sha256").update(value).digest("hex");
16
+ const fileDigest = (file) => sha256Hex(readFileSync(file));
17
+ const walkShapes = (root) => {
18
+ const shapes = new Map();
19
+ const walk = (directory, prefix) => {
20
+ for (const name of readdirSync(directory).sort()) {
21
+ const full = path.join(directory, name), key = prefix ? `${prefix}/${name}` : name;
22
+ const stat = lstatSync(full);
23
+ const mode = stat.mode & 0o7777;
24
+ if (stat.isSymbolicLink())
25
+ shapes.set(key, ["l", readlinkSync(full)]);
26
+ else if (stat.isDirectory()) {
27
+ shapes.set(key, ["d", mode]);
28
+ walk(full, key);
29
+ }
30
+ else
31
+ shapes.set(key, ["f", stat.size, Math.round(stat.mtimeMs), mode]);
32
+ }
33
+ };
34
+ walk(root, "");
35
+ return shapes;
36
+ };
37
+ /** Built from the frozen staged tree, so it records exactly the modes and bytes that land. */
38
+ export const buildFeedManifest = (root, tree) => {
39
+ const entries = {};
40
+ let files = 0;
41
+ for (const [key, shape] of walkShapes(root)) {
42
+ if (shape[0] === "f") {
43
+ entries[key] = [...shape, fileDigest(path.join(root, key))];
44
+ files += 1;
45
+ }
46
+ else
47
+ entries[key] = shape;
48
+ }
49
+ return { entries, files, tree, version: FEED_MANIFEST_VERSION };
50
+ };
51
+ /**
52
+ * `changed` is drift on its own evidence (kind, size, mode, link target, or a same-size rewrite whose
53
+ * digest moved); `touched` is a file whose mtime moved while its bytes did not.
54
+ */
55
+ export const compareFeedManifest = (root, manifest) => {
56
+ const actual = walkShapes(root);
57
+ const result = { changed: [], extra: [], missing: [], touched: [] };
58
+ for (const [key, want] of Object.entries(manifest.entries)) {
59
+ const got = actual.get(key);
60
+ if (!got) {
61
+ result.missing.push(key);
62
+ continue;
63
+ }
64
+ if (got[0] !== want[0]) {
65
+ result.changed.push(key);
66
+ continue;
67
+ }
68
+ if (want[0] === "l") {
69
+ if (got[1] !== want[1])
70
+ result.changed.push(key);
71
+ continue;
72
+ }
73
+ if (want[0] === "d") {
74
+ if (got[1] !== want[1])
75
+ result.changed.push(key);
76
+ continue;
77
+ }
78
+ const file = got;
79
+ if (file[1] !== want[1] || file[3] !== want[3]) {
80
+ result.changed.push(key);
81
+ continue;
82
+ }
83
+ let digest = null;
84
+ try {
85
+ digest = fileDigest(path.join(root, key));
86
+ }
87
+ catch { /* unreadable is drift */ }
88
+ if (digest !== want[4])
89
+ result.changed.push(key);
90
+ else if (file[2] !== want[2])
91
+ result.touched.push(key);
92
+ }
93
+ for (const key of actual.keys())
94
+ if (!(key in manifest.entries))
95
+ result.extra.push(key);
96
+ return result;
97
+ };
98
+ export const manifestDrift = (comparison) => [
99
+ ...comparison.missing.map((key) => `${key} missing`),
100
+ ...comparison.extra.map((key) => `${key} added`),
101
+ ...comparison.changed.map((key) => `${key} changed`)
102
+ ];
103
+ const manifestPath = (stateDir, tree) => path.join(stateDir, "manifests", `${tree}.json`);
104
+ export const writeFeedManifest = (stateDir, manifest) => {
105
+ const file = manifestPath(stateDir, manifest.tree);
106
+ mkdirSync(path.dirname(file), { mode: 0o700, recursive: true });
107
+ const tmp = `${file}.${process.pid}.tmp`;
108
+ rmSync(tmp, { force: true });
109
+ writeFileSync(tmp, `${JSON.stringify(manifest)}\n`, { mode: 0o600 });
110
+ renameSync(tmp, file);
111
+ };
112
+ export const readFeedManifest = (stateDir, tree) => {
113
+ try {
114
+ const value = JSON.parse(readFileSync(manifestPath(stateDir, tree), "utf8"));
115
+ return value?.version === FEED_MANIFEST_VERSION && value.tree === tree && value.entries ? value : null;
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ };
121
+ export const removeFeedManifest = (stateDir, tree) => {
122
+ rmSync(manifestPath(stateDir, tree), { force: true });
123
+ };
@@ -0,0 +1,16 @@
1
+ import { type FeedExec, type ResolvedFeedSource } from "./feedSource.js";
2
+ import { type FeedTarget } from "./feedTarget.js";
3
+ /** The source revision, composed with `include` and `prepare` when the feed declares them. */
4
+ export declare const resolveFeedContent: (target: FeedTarget, { exec }: {
5
+ exec: FeedExec;
6
+ }) => ResolvedFeedSource;
7
+ /** Runs the declared prepare step in the staged tree; any failure lands nothing. */
8
+ export declare const runFeedPrepare: (target: FeedTarget, tree: string, resolved: ResolvedFeedSource) => void;
9
+ /**
10
+ * Stages the source, then the includes, then runs prepare, in `stagingDir` (existing, empty). Returns the
11
+ * revision of what was actually staged: a source or include edited mid-copy lands under its own revision.
12
+ */
13
+ export declare const stageFeedContent: (target: FeedTarget, resolved: ResolvedFeedSource, stagingDir: string, { exec, log }: {
14
+ exec: FeedExec;
15
+ log: (line: string) => void;
16
+ }) => ResolvedFeedSource;