spawnfile 0.1.17 → 0.1.18

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 (489) 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/package.json +3 -3
  489. package/runtimes.yaml +8 -6
@@ -0,0 +1,251 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createReadStream } from "node:fs";
3
+ import { lstat, readFile, realpath, stat } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { SpawnfileError } from "../shared/index.js";
6
+ import { listCommittedTree, listIndex, readStableSnapshot, readStatus, readStatusAgainstHead, resolveGitLocation, resolveCommit, resolveHead, streamBlobs } from "./workspaceBundleGit.js";
7
+ import { BundleTarWriter, normalizeBundleMode, WORKSPACE_BUNDLE_MAX_BYTES, WORKSPACE_BUNDLE_MAX_ENTRIES } from "./workspaceBundleTar.js";
8
+ const fail = (message) => {
9
+ throw new SpawnfileError("validation_error", message);
10
+ };
11
+ const SYMLINK = 0o120000, GITLINK = 0o160000;
12
+ const escapeRegExp = (value) => value.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
13
+ /**
14
+ * Exclude patterns match the path relative to the bundle root: `*` and `?`
15
+ * stay inside one segment, `**` crosses segments, and a pattern that matches
16
+ * a directory excludes everything beneath it.
17
+ */
18
+ export const compileExcludePatterns = (patterns = []) => {
19
+ const expressions = patterns.map((pattern) => {
20
+ const trimmed = pattern.replace(/^\.\//u, "").replace(/\/+$/u, "");
21
+ if (!trimmed || trimmed.startsWith("/") || trimmed.split("/").some((segment) => segment === ".." || segment === ".")) {
22
+ fail(`Workspace bundle exclude pattern must be a relative path glob: ${pattern}`);
23
+ }
24
+ let source = "";
25
+ for (let index = 0; index < trimmed.length;) {
26
+ if (trimmed.startsWith("**/", index)) {
27
+ source += "(?:.*/)?";
28
+ index += 3;
29
+ }
30
+ else if (trimmed.startsWith("**", index)) {
31
+ source += ".*";
32
+ index += 2;
33
+ }
34
+ else if (trimmed[index] === "*") {
35
+ source += "[^/]*";
36
+ index += 1;
37
+ }
38
+ else if (trimmed[index] === "?") {
39
+ source += "[^/]";
40
+ index += 1;
41
+ }
42
+ else {
43
+ source += escapeRegExp(trimmed[index]);
44
+ index += 1;
45
+ }
46
+ }
47
+ return new RegExp(`^${source}$`, "u");
48
+ });
49
+ return (relativePath) => {
50
+ if (expressions.length === 0)
51
+ return false;
52
+ const segments = relativePath.split("/");
53
+ for (let length = 1; length <= segments.length; length += 1) {
54
+ const candidate = segments.slice(0, length).join("/");
55
+ if (expressions.some((expression) => expression.test(candidate)))
56
+ return true;
57
+ }
58
+ return false;
59
+ };
60
+ };
61
+ const validEntryPath = (relativePath) => {
62
+ if (!relativePath || relativePath.includes("\\") || relativePath.split("/").some((part) => !part || part === "." || part === "..")) {
63
+ fail(`Workspace bundle input path cannot be archived safely: ${relativePath}`);
64
+ }
65
+ return relativePath;
66
+ };
67
+ const refuseLinks = (relativePath, mode) => {
68
+ if (mode === SYMLINK)
69
+ fail(`Workspace bundle input is a symlink; exclude it or replace it with a file: ${relativePath}`);
70
+ if (mode === GITLINK)
71
+ fail(`Workspace bundle input is a git submodule; exclude it or declare it as its own resource: ${relativePath}`);
72
+ };
73
+ const relativeTo = (status, prefix) => status.map((entry) => ({ ...entry, path: entry.path.startsWith(prefix) ? entry.path.slice(prefix.length) : entry.path }));
74
+ const sortEntries = (entries) => entries.sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
75
+ export const resolveBundleRoot = async (directory) => {
76
+ const info = await lstat(directory).catch(() => fail(`Workspace bundle root does not exist: ${directory}`));
77
+ if (!info.isDirectory())
78
+ fail(`Workspace bundle root must be a real directory, not a file or symlink: ${directory}`);
79
+ return realpath(directory);
80
+ };
81
+ /**
82
+ * Release identity: the root must be clean against HEAD (ignoring excluded
83
+ * paths), and the identity is the committed tree's blob ids and modes. No
84
+ * input file is read.
85
+ */
86
+ export const resolveReleaseFiles = async (directory, exclude = []) => {
87
+ const excluded = compileExcludePatterns(exclude);
88
+ const [{ prefix }, status] = await Promise.all([resolveGitLocation(directory), readStatusAgainstHead(directory)]);
89
+ if (!status.head)
90
+ fail(`Release workspace bundle requires a commit; the repository has none: ${directory}`);
91
+ const dirty = status.changed.map((changed) => changed.startsWith(prefix) ? changed.slice(prefix.length) : changed).filter((changed) => !excluded(changed));
92
+ if (dirty.length > 0) {
93
+ fail(`Release workspace bundle requires a clean commit; ${dirty.length} uncommitted change(s) under ${directory}, first: ${dirty[0]}`);
94
+ }
95
+ // The tree of exactly the commit status verified clean against; bytes come from its objects, so later edits cannot leak in.
96
+ // Status reads HEAD twice internally; if HEAD has moved since, it may have compared against a different commit.
97
+ const [tree, headNow] = await Promise.all([listCommittedTree(directory, status.head), resolveHead(directory)]);
98
+ if (headNow !== status.head)
99
+ fail(`Release workspace bundle HEAD moved while it was checked; retry: ${directory}`);
100
+ return { directory, entries: treeEntries(tree, excluded), mode: "release" };
101
+ };
102
+ const treeEntries = (tree, excluded) => {
103
+ const entries = [];
104
+ for (const entry of tree) {
105
+ if (excluded(entry.path))
106
+ continue;
107
+ refuseLinks(entry.path, entry.mode);
108
+ if (entry.type !== "blob")
109
+ fail(`Workspace bundle input has unsupported git type ${entry.type}: ${entry.path}`);
110
+ entries.push({ identity: `git:${entry.objectId}`, mode: normalizeBundleMode(entry.mode), objectId: entry.objectId, path: validEntryPath(entry.path) });
111
+ }
112
+ return sortEntries(entries);
113
+ };
114
+ /**
115
+ * Release rule for recipe inputs that are named files rather than a tree:
116
+ * each must be tracked at HEAD with no staged, unstaged or untracked change.
117
+ */
118
+ export const assertCommittedInputs = async (directory, names) => {
119
+ const [{ prefix }, status] = await Promise.all([resolveGitLocation(directory), readStatusAgainstHead(directory)]);
120
+ if (!status.head)
121
+ fail(`Release workspace bundle requires a commit; the repository has none: ${directory}`);
122
+ const wanted = new Set(names.map((name) => `${prefix}${name}`));
123
+ const dirty = status.changed.find((changed) => wanted.has(changed));
124
+ if (dirty)
125
+ fail(`Release workspace bundle requires a clean commit; ${dirty} has uncommitted changes`);
126
+ const committed = new Set((await listCommittedTree(directory, status.head)).map((entry) => entry.path));
127
+ const missing = names.find((name) => !committed.has(name));
128
+ if (missing)
129
+ fail(`Release workspace bundle input ${missing} is not committed in ${directory}`);
130
+ if (await resolveHead(directory) !== status.head)
131
+ fail(`Release workspace bundle HEAD moved while it was checked; retry: ${directory}`);
132
+ };
133
+ /**
134
+ * A pinned revision: identity and bytes come from the tree of `ref` (resolved
135
+ * to a commit once), so the work tree's state is irrelevant and no clean
136
+ * checkout is needed. The same in dev and release compiles.
137
+ */
138
+ export const resolveCommittedFiles = async (directory, ref, exclude = []) => {
139
+ const commit = await resolveCommit(directory, ref);
140
+ if (!commit)
141
+ fail(`Workspace bundle ref ${ref} does not name a commit in ${directory}; fetch it first`);
142
+ return { directory, entries: treeEntries(await listCommittedTree(directory, commit), compileExcludePatterns(exclude)), mode: "release" };
143
+ };
144
+ const HASH_CONCURRENCY = 8;
145
+ const hashFile = async (filePath, spend) => {
146
+ const hash = createHash("sha256");
147
+ for await (const chunk of createReadStream(filePath)) {
148
+ spend(chunk.length);
149
+ hash.update(chunk);
150
+ }
151
+ return `sha256:${hash.digest("hex")}`;
152
+ };
153
+ /**
154
+ * Dev identity: the working tree as it is now. Files git reports as unchanged
155
+ * keep their index blob id; modified and untracked (not ignored) files are
156
+ * hashed from disk, so an uncommitted edit or a new file always moves the key.
157
+ */
158
+ export const resolveDevFiles = async (directory, exclude = []) => {
159
+ const excluded = compileExcludePatterns(exclude), location = await resolveGitLocation(directory), { prefix } = location;
160
+ const [index, rawStatus] = await readStableSnapshot(location, () => Promise.all([listIndex(directory), readStatus(directory)]));
161
+ const status = relativeTo(rawStatus, prefix);
162
+ const untracked = status.filter((entry) => entry.index === "?").map((entry) => entry.path);
163
+ const changed = new Set(status.filter((entry) => entry.worktree !== " " && entry.worktree !== "?").map((entry) => entry.path));
164
+ const conflicted = new Set(index.filter((entry) => entry.stage !== 0).map((entry) => entry.path));
165
+ const entries = new Map(), toHash = [];
166
+ for (const entry of index) {
167
+ if (excluded(entry.path) || entries.has(entry.path))
168
+ continue;
169
+ if (changed.has(entry.path) || conflicted.has(entry.path)) {
170
+ toHash.push(entry.path);
171
+ continue;
172
+ }
173
+ refuseLinks(entry.path, entry.mode);
174
+ entries.set(entry.path, { identity: `git:${entry.objectId}`, mode: normalizeBundleMode(entry.mode), objectId: entry.objectId, path: validEntryPath(entry.path) });
175
+ }
176
+ for (const relativePath of untracked) {
177
+ if (excluded(relativePath.replace(/\/$/u, "")))
178
+ continue;
179
+ if (relativePath.endsWith("/"))
180
+ fail(`Workspace bundle input contains a nested git repository; exclude it: ${relativePath}`);
181
+ toHash.push(relativePath);
182
+ }
183
+ const pending = [...new Set(toHash)];
184
+ let hashedBytes = 0;
185
+ // Bounded: at most HASH_CONCURRENCY files open, and the byte budget is spent before a file is read.
186
+ const hashNext = async () => {
187
+ for (let relativePath = pending.pop(); relativePath !== undefined; relativePath = pending.pop()) {
188
+ const filePath = path.join(directory, relativePath);
189
+ const info = await lstat(filePath).catch(() => undefined);
190
+ if (!info)
191
+ continue; // deleted in the work tree: not an input any more
192
+ if (info.isSymbolicLink())
193
+ refuseLinks(relativePath, SYMLINK);
194
+ if (!info.isFile())
195
+ fail(`Workspace bundle input is not a regular file: ${relativePath}`);
196
+ entries.set(relativePath, { identity: await hashFile(filePath, spend), mode: normalizeBundleMode(info.mode), path: validEntryPath(relativePath) });
197
+ }
198
+ };
199
+ // Budget the bytes actually streamed, so a file that grows after lstat cannot overrun it.
200
+ const spend = (bytes) => { hashedBytes += bytes; if (hashedBytes > WORKSPACE_BUNDLE_MAX_BYTES)
201
+ fail("Workspace bundle exceeds the maximum archive size"); };
202
+ await Promise.all(Array.from({ length: HASH_CONCURRENCY }, hashNext));
203
+ if (entries.size > WORKSPACE_BUNDLE_MAX_ENTRIES)
204
+ fail("Workspace bundle exceeds the maximum entry count");
205
+ return { directory, entries: sortEntries([...entries.values()]), mode: "dev" };
206
+ };
207
+ /**
208
+ * Writes the archive for a resolved input set. Every `git:` entry is read from
209
+ * the object store, so its bytes are exactly the blob its identity names;
210
+ * every `sha256:` entry is read from disk and re-checked against its digest.
211
+ * The archive is therefore a pure function of the cache key.
212
+ */
213
+ export const writeBundleFiles = async (input, outputPath) => {
214
+ const writer = await BundleTarWriter.create(outputPath);
215
+ const objects = input.entries.filter((entry) => entry.objectId !== undefined);
216
+ const disk = input.entries.filter((entry) => entry.objectId === undefined);
217
+ let next = 0;
218
+ const writeDiskBefore = async (limit) => {
219
+ for (; next < disk.length && (limit === undefined || disk[next].path < limit); next += 1) {
220
+ const entry = disk[next], filePath = path.join(input.directory, entry.path);
221
+ if ((await stat(filePath)).size > WORKSPACE_BUNDLE_MAX_BYTES)
222
+ fail("Workspace bundle exceeds the maximum archive size");
223
+ const bytes = await readFile(filePath);
224
+ if (`sha256:${createHash("sha256").update(bytes).digest("hex")}` !== entry.identity) {
225
+ fail(`Workspace bundle input changed while the bundle was built: ${entry.path}`);
226
+ }
227
+ await writer.begin(entry.path, bytes.length, entry.mode);
228
+ await writer.data(bytes);
229
+ await writer.end();
230
+ }
231
+ };
232
+ try {
233
+ if (objects.length > 0) {
234
+ await streamBlobs(input.directory, objects.map((entry) => entry.objectId), {
235
+ begin: async (index, size) => {
236
+ const entry = objects[index];
237
+ await writeDiskBefore(entry.path);
238
+ await writer.begin(entry.path, size, entry.mode);
239
+ },
240
+ data: (chunk) => writer.data(chunk),
241
+ end: () => writer.end()
242
+ });
243
+ }
244
+ await writeDiskBefore();
245
+ return await writer.finish();
246
+ }
247
+ catch (error) {
248
+ await writer.abort();
249
+ throw error;
250
+ }
251
+ };
@@ -0,0 +1,25 @@
1
+ import type { WorkspaceBundleBuild } from "../manifest/index.js";
2
+ import { type BundleFilesInput } from "./workspaceBundleFiles.js";
3
+ import { type BundleBuildContext, type BundleBuildPlan } from "./workspaceBundleKey.js";
4
+ type FilesSpec = NonNullable<WorkspaceBundleBuild["files"]>;
5
+ type GeneratedSpec = NonNullable<WorkspaceBundleBuild["generated"]>;
6
+ export declare const GENERATED_OUTPUT_PLACEHOLDER = "${output}";
7
+ /** Git identity for one declared file input: a pinned ref, a clean commit (release) or the work tree (dev). */
8
+ export declare const resolveFilesInput: (spec: FilesSpec, context: BundleBuildContext) => Promise<BundleFilesInput>;
9
+ export declare const planFilesBundle: (spec: FilesSpec, context: BundleBuildContext) => Promise<BundleBuildPlan>;
10
+ /**
11
+ * `generated`: a declared command writes an output directory from declared
12
+ * inputs. The key is the recipe above plus the target platform; the command
13
+ * runs only on a cache miss. After it runs the recipe is computed again and
14
+ * the build fails if any input or tool changed meanwhile, so output is never
15
+ * cached under a key that does not describe what it was built from.
16
+ * Undeclared inputs and host environment are not part of the key, so declare
17
+ * everything the command reads. Inputs come from the work tree the command
18
+ * reads, so `ref` is refused; with `image`, only `cwd` is mounted, so every
19
+ * input must live inside it. `captureTools: false` keys tools by argv only and
20
+ * runs nothing (used for pins).
21
+ */
22
+ export declare const planGeneratedBundle: (spec: GeneratedSpec, context: BundleBuildContext, options?: {
23
+ captureTools?: boolean;
24
+ }) => Promise<BundleBuildPlan>;
25
+ export {};
@@ -0,0 +1,103 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, mkdtemp, rm } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { SpawnfileError } from "../shared/index.js";
5
+ import { resolveBundleRoot, resolveCommittedFiles, resolveDevFiles, resolveReleaseFiles, writeBundleFiles } from "./workspaceBundleFiles.js";
6
+ import { computeRecipeBundleKey, computeWorkspaceBundleKey, filesIdentity } from "./workspaceBundleKey.js";
7
+ import { bundleContainerName, runBundleCommand, runContainerStep } from "./workspaceBundleRun.js";
8
+ import { walkBuiltTree } from "./workspaceBundleTree.js";
9
+ const DEFAULT_TIMEOUT_SECONDS = 600;
10
+ const TOOL_TIMEOUT_MS = 120_000;
11
+ const CONTAINER_WORKDIR = "/spawnfile/work", CONTAINER_OUTPUT = "/spawnfile/output";
12
+ export const GENERATED_OUTPUT_PLACEHOLDER = "${output}";
13
+ /** The compile output is never an input, even when an input root contains it. */
14
+ const withOutputExcluded = (root, exclude, outputReal) => {
15
+ const inside = path.relative(root, outputReal);
16
+ return inside && !inside.startsWith("..") && !path.isAbsolute(inside) ? [...(exclude ?? []), inside.split(path.sep).join("/")] : exclude;
17
+ };
18
+ /** Git identity for one declared file input: a pinned ref, a clean commit (release) or the work tree (dev). */
19
+ export const resolveFilesInput = async (spec, context) => {
20
+ const root = await resolveBundleRoot(spec.root), exclude = withOutputExcluded(root, spec.exclude, context.outputReal);
21
+ if (spec.ref !== undefined)
22
+ return resolveCommittedFiles(root, spec.ref, exclude);
23
+ return context.identity === "release" ? resolveReleaseFiles(root, exclude) : resolveDevFiles(root, exclude);
24
+ };
25
+ export const planFilesBundle = async (spec, context) => {
26
+ const input = await resolveFilesInput(spec, context);
27
+ return { input: "files", key: computeWorkspaceBundleKey(input, context.platform), write: (temporaryTar) => writeBundleFiles(input, temporaryTar) };
28
+ };
29
+ const inside = (root, candidate) => {
30
+ const relative = path.relative(root, candidate);
31
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
32
+ };
33
+ /**
34
+ * The recipe a generated bundle is keyed by: every input's git identity, the
35
+ * command, `cwd`, the image, and each tool command with its exact captured
36
+ * stdout (or, without capture, just its argv). Captured output is hashed as
37
+ * bytes, so whitespace changes count.
38
+ */
39
+ const generatedRecipe = async (spec, cwd, context, captureTools) => {
40
+ const inputs = await Promise.all(spec.inputs.map((input) => resolveFilesInput(input, context)));
41
+ const tools = await Promise.all((spec.tools ?? []).map(async (tool) => ({
42
+ argv: tool,
43
+ output: captureTools ? createHash("sha256").update(spec.image
44
+ ? await runContainerStep({ argv: tool, dockerCommand: context.dockerCommand, image: spec.image, mounts: [[cwd, CONTAINER_WORKDIR]], name: bundleContainerName(), platform: context.platform, workdir: CONTAINER_WORKDIR }, TOOL_TIMEOUT_MS, `Generated bundle tool ${tool.join(" ")}`, cwd)
45
+ : await runBundleCommand({ argv: tool, cwd, timeoutMs: TOOL_TIMEOUT_MS }, `Generated bundle tool ${tool.join(" ")}`)).digest("hex") : null
46
+ })));
47
+ return {
48
+ command: spec.command, cwd, image: spec.image ?? null,
49
+ inputs: inputs.map((input) => ({ entries: filesIdentity(input), root: input.directory })),
50
+ tools
51
+ };
52
+ };
53
+ /**
54
+ * `generated`: a declared command writes an output directory from declared
55
+ * inputs. The key is the recipe above plus the target platform; the command
56
+ * runs only on a cache miss. After it runs the recipe is computed again and
57
+ * the build fails if any input or tool changed meanwhile, so output is never
58
+ * cached under a key that does not describe what it was built from.
59
+ * Undeclared inputs and host environment are not part of the key, so declare
60
+ * everything the command reads. Inputs come from the work tree the command
61
+ * reads, so `ref` is refused; with `image`, only `cwd` is mounted, so every
62
+ * input must live inside it. `captureTools: false` keys tools by argv only and
63
+ * runs nothing (used for pins).
64
+ */
65
+ export const planGeneratedBundle = async (spec, context, options = {}) => {
66
+ if (spec.inputs.some((input) => input.ref !== undefined)) {
67
+ throw new SpawnfileError("validation_error", "Generated bundle inputs cannot pin a ref: the command reads the work tree");
68
+ }
69
+ const cwd = await resolveBundleRoot(spec.cwd), captureTools = options.captureTools ?? true;
70
+ if (spec.image && (await Promise.all(spec.inputs.map((input) => resolveBundleRoot(input.root)))).some((root) => !inside(cwd, root))) {
71
+ throw new SpawnfileError("validation_error", "Generated bundle inputs must live inside cwd when the command runs in an image: only cwd is mounted");
72
+ }
73
+ const recipe = await generatedRecipe(spec, cwd, context, captureTools);
74
+ return {
75
+ input: "generated",
76
+ key: computeRecipeBundleKey("generated", recipe, context.platform),
77
+ write: async (temporaryTar) => {
78
+ await mkdir(context.workRoot, { mode: 0o700, recursive: true });
79
+ const output = await mkdtemp(path.join(context.workRoot, "generated-"));
80
+ try {
81
+ const target = spec.image ? CONTAINER_OUTPUT : output;
82
+ const argv = spec.command.map((part) => part.split(GENERATED_OUTPUT_PLACEHOLDER).join(target));
83
+ const label = `Generated bundle command ${spec.command.join(" ")}`, timeoutMs = (spec.timeout_seconds ?? DEFAULT_TIMEOUT_SECONDS) * 1_000;
84
+ if (spec.image) {
85
+ await runContainerStep({
86
+ argv, dockerCommand: context.dockerCommand, env: { SPAWNFILE_BUNDLE_OUTPUT: target }, image: spec.image,
87
+ mounts: [[cwd, CONTAINER_WORKDIR], [output, CONTAINER_OUTPUT]], name: bundleContainerName(), platform: context.platform, workdir: CONTAINER_WORKDIR
88
+ }, timeoutMs, label, cwd);
89
+ }
90
+ else {
91
+ await runBundleCommand({ argv, cwd, env: { ...process.env, SPAWNFILE_BUNDLE_OUTPUT: target }, timeoutMs }, label);
92
+ }
93
+ if (JSON.stringify(await generatedRecipe(spec, cwd, context, captureTools)) !== JSON.stringify(recipe)) {
94
+ throw new SpawnfileError("validation_error", `${label}: inputs or tools changed while it ran; nothing was cached, retry`);
95
+ }
96
+ return await writeBundleFiles(await walkBuiltTree(output), temporaryTar);
97
+ }
98
+ finally {
99
+ await rm(output, { force: true, recursive: true });
100
+ }
101
+ }
102
+ };
103
+ };
@@ -0,0 +1,64 @@
1
+ export interface GitTreeEntry {
2
+ mode: number;
3
+ objectId: string;
4
+ path: string;
5
+ type: string;
6
+ }
7
+ export interface GitIndexEntry {
8
+ mode: number;
9
+ objectId: string;
10
+ path: string;
11
+ stage: number;
12
+ }
13
+ export interface GitLocation {
14
+ indexPath: string;
15
+ prefix: string;
16
+ topLevel: string;
17
+ }
18
+ /** The repository top level, this directory's path prefix inside it ("" at the top), and the index file. */
19
+ export declare const resolveGitLocation: (directory: string) => Promise<GitLocation>;
20
+ /**
21
+ * Runs `read` against one consistent index: the index file must be identical
22
+ * before and after, else a concurrent `git add` could pair an old index
23
+ * listing with a new status. Retries a few times, then fails.
24
+ */
25
+ export declare const readStableSnapshot: <T>(location: GitLocation, read: () => Promise<T>) => Promise<T>;
26
+ /** Committed tree under `directory`: blob ids and modes from the tree objects alone, no blob or file reads. */
27
+ export declare const listCommittedTree: (directory: string, commit?: string) => Promise<GitTreeEntry[]>;
28
+ /** Index entries under `directory` (paths relative to it). */
29
+ export declare const listIndex: (directory: string) => Promise<GitIndexEntry[]>;
30
+ export interface GitStatusEntry {
31
+ index: string;
32
+ path: string;
33
+ worktree: string;
34
+ }
35
+ /**
36
+ * `git status` under `directory`, paths relative to the repository top level
37
+ * (strip the location prefix). Untracked, not-ignored files appear
38
+ * individually as `??`; a nested repository appears once with a trailing "/".
39
+ * Ignored paths never appear.
40
+ */
41
+ export declare const readStatus: (directory: string) => Promise<GitStatusEntry[]>;
42
+ /** The commit a revision names, or "" when it names none. */
43
+ export declare const resolveCommit: (directory: string, revision: string) => Promise<string>;
44
+ /** The HEAD commit id, or "" when the repository has none. */
45
+ export declare const resolveHead: (directory: string) => Promise<string>;
46
+ /**
47
+ * The commit `git status` compared against and every changed path under
48
+ * `directory` (repository-relative), from one porcelain v2 run. Pairing the
49
+ * two in one process means a concurrent commit can never split them.
50
+ */
51
+ export declare const readStatusAgainstHead: (directory: string) => Promise<{
52
+ changed: string[];
53
+ head: string;
54
+ }>;
55
+ /**
56
+ * Streams blob contents for `objectIds`, in order, through `onBlob`. One
57
+ * `git cat-file --batch` process serves the whole tree; content never touches
58
+ * the work tree, so the result is a pure function of the committed objects.
59
+ */
60
+ export declare const streamBlobs: (directory: string, objectIds: string[], handlers: {
61
+ begin: (index: number, size: number) => Promise<void>;
62
+ data: (chunk: Buffer) => Promise<void>;
63
+ end: () => Promise<void>;
64
+ }) => Promise<void>;
@@ -0,0 +1,154 @@
1
+ import { execFile, spawn } from "node:child_process";
2
+ import { stat } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { promisify } from "node:util";
5
+ import { SpawnfileError } from "../shared/index.js";
6
+ const run = promisify(execFile);
7
+ const MAX_BUFFER = 268_435_456;
8
+ // Never take git's optional index lock: a compile reads the repository, it does not maintain it.
9
+ // Never honour replacement refs: the cache key names object ids, so their bytes must be the objects'.
10
+ const gitEnv = () => ({ ...process.env, GIT_NO_REPLACE_OBJECTS: "1", GIT_OPTIONAL_LOCKS: "0" });
11
+ const git = async (cwd, args) => {
12
+ try {
13
+ const { stdout } = await run("git", args, { cwd, encoding: "buffer", env: gitEnv(), maxBuffer: MAX_BUFFER });
14
+ return stdout;
15
+ }
16
+ catch (error) {
17
+ const stderr = error.stderr?.toString("utf8").trim();
18
+ throw new SpawnfileError("validation_error", `Workspace bundle git query failed in ${cwd}: git ${args[0]}${stderr ? `: ${stderr}` : ""}`);
19
+ }
20
+ };
21
+ const records = (output) => output.toString("utf8").split("\0").filter(Boolean);
22
+ /** The repository top level, this directory's path prefix inside it ("" at the top), and the index file. */
23
+ export const resolveGitLocation = async (directory) => {
24
+ const [topLevel, prefix = "", indexPath = ""] = (await git(directory, ["rev-parse", "--show-toplevel", "--show-prefix", "--git-path", "index"])).toString("utf8").split("\n");
25
+ if (!topLevel || !indexPath)
26
+ throw new SpawnfileError("validation_error", `Workspace bundle root is not inside a git work tree: ${directory}`);
27
+ return { indexPath: path.resolve(directory, indexPath), prefix, topLevel };
28
+ };
29
+ const indexStamp = async (location) => {
30
+ const index = await stat(location.indexPath).catch(() => undefined);
31
+ return JSON.stringify([index?.ino, index?.size, index?.mtimeMs]);
32
+ };
33
+ /**
34
+ * Runs `read` against one consistent index: the index file must be identical
35
+ * before and after, else a concurrent `git add` could pair an old index
36
+ * listing with a new status. Retries a few times, then fails.
37
+ */
38
+ export const readStableSnapshot = async (location, read) => {
39
+ for (let attempt = 0; attempt < 3; attempt += 1) {
40
+ const before = await indexStamp(location);
41
+ const value = await read();
42
+ if (before === await indexStamp(location))
43
+ return value;
44
+ }
45
+ throw new SpawnfileError("validation_error", `Workspace bundle inputs kept changing while they were read: ${location.topLevel}`);
46
+ };
47
+ /** Committed tree under `directory`: blob ids and modes from the tree objects alone, no blob or file reads. */
48
+ export const listCommittedTree = async (directory, commit = "HEAD") => records(await git(directory, ["ls-tree", "-r", "-z", commit, "--", "."])).map((record) => {
49
+ const tab = record.indexOf("\t");
50
+ const [mode, type, objectId] = record.slice(0, tab).split(" ");
51
+ return { mode: Number.parseInt(mode, 8), objectId: objectId, path: record.slice(tab + 1), type: type };
52
+ });
53
+ /** Index entries under `directory` (paths relative to it). */
54
+ export const listIndex = async (directory) => records(await git(directory, ["ls-files", "-s", "-z", "--", "."])).map((record) => {
55
+ const tab = record.indexOf("\t");
56
+ const [mode, objectId, stage] = record.slice(0, tab).split(" ");
57
+ return { mode: Number.parseInt(mode, 8), objectId: objectId, path: record.slice(tab + 1), stage: Number(stage) };
58
+ });
59
+ /**
60
+ * `git status` under `directory`, paths relative to the repository top level
61
+ * (strip the location prefix). Untracked, not-ignored files appear
62
+ * individually as `??`; a nested repository appears once with a trailing "/".
63
+ * Ignored paths never appear.
64
+ */
65
+ export const readStatus = async (directory) => records(await git(directory, ["status", "--porcelain=v1", "-z", "--untracked-files=all", "--no-renames", "--", "."])).map((record) => ({ index: record[0], path: record.slice(3), worktree: record[1] }));
66
+ /** The commit a revision names, or "" when it names none. */
67
+ export const resolveCommit = async (directory, revision) => revision.startsWith("-") ? "" : git(directory, ["rev-parse", "-q", "--verify", "--end-of-options", `${revision}^{commit}`]).then((output) => output.toString("utf8").trim(), () => "");
68
+ /** The HEAD commit id, or "" when the repository has none. */
69
+ export const resolveHead = async (directory) => resolveCommit(directory, "HEAD");
70
+ /**
71
+ * The commit `git status` compared against and every changed path under
72
+ * `directory` (repository-relative), from one porcelain v2 run. Pairing the
73
+ * two in one process means a concurrent commit can never split them.
74
+ */
75
+ export const readStatusAgainstHead = async (directory) => {
76
+ let head = "";
77
+ const changed = [];
78
+ for (const record of records(await git(directory, ["status", "--porcelain=v2", "--branch", "-z", "--untracked-files=all", "--no-renames", "--", "."]))) {
79
+ if (record.startsWith("# branch.oid ")) {
80
+ const oid = record.slice(13);
81
+ head = /^[a-f0-9]{40,64}$/u.test(oid) ? oid : "";
82
+ continue;
83
+ }
84
+ if (record.startsWith("#"))
85
+ continue;
86
+ const fields = record.split(" "), skip = record[0] === "1" ? 8 : record[0] === "u" ? 10 : 1;
87
+ changed.push(fields.slice(skip).join(" "));
88
+ }
89
+ return { changed, head };
90
+ };
91
+ /**
92
+ * Streams blob contents for `objectIds`, in order, through `onBlob`. One
93
+ * `git cat-file --batch` process serves the whole tree; content never touches
94
+ * the work tree, so the result is a pure function of the committed objects.
95
+ */
96
+ export const streamBlobs = async (directory, objectIds, handlers) => {
97
+ const child = spawn("git", ["cat-file", "--batch"], { cwd: directory, env: gitEnv(), stdio: ["pipe", "pipe", "pipe"] });
98
+ const exited = new Promise((resolve, reject) => { child.once("error", reject); child.once("close", resolve); });
99
+ let stderr = "";
100
+ child.stderr.on("data", (chunk) => { stderr += chunk.toString("utf8"); });
101
+ child.stdin.on("error", () => undefined);
102
+ child.stdin.end(objectIds.map((id) => `${id}\n`).join(""));
103
+ let index = 0, remaining = -1, trailer = false, header = Buffer.alloc(0);
104
+ try {
105
+ for await (const raw of child.stdout) {
106
+ let chunk = raw;
107
+ while (chunk.length > 0) {
108
+ if (trailer) {
109
+ if (chunk[0] !== 0x0a)
110
+ throw new SpawnfileError("validation_error", "Workspace bundle blob stream is malformed");
111
+ chunk = chunk.subarray(1);
112
+ trailer = false;
113
+ await handlers.end();
114
+ index += 1;
115
+ continue;
116
+ }
117
+ if (remaining < 0) {
118
+ const newline = chunk.indexOf(0x0a);
119
+ if (newline < 0) {
120
+ header = Buffer.concat([header, chunk]);
121
+ break;
122
+ }
123
+ const line = Buffer.concat([header, chunk.subarray(0, newline)]).toString("utf8");
124
+ header = Buffer.alloc(0);
125
+ chunk = chunk.subarray(newline + 1);
126
+ const [objectId, type, size] = line.split(" ");
127
+ if (index >= objectIds.length || objectId !== objectIds[index] || type !== "blob" || !/^\d+$/u.test(size ?? "")) {
128
+ throw new SpawnfileError("validation_error", `Workspace bundle blob ${objectIds[index] ?? "?"} is not readable: ${line}`);
129
+ }
130
+ remaining = Number(size);
131
+ await handlers.begin(index, remaining);
132
+ }
133
+ const take = Math.min(remaining, chunk.length);
134
+ if (take > 0)
135
+ await handlers.data(chunk.subarray(0, take));
136
+ chunk = chunk.subarray(take);
137
+ remaining -= take;
138
+ if (remaining === 0) {
139
+ remaining = -1;
140
+ trailer = true;
141
+ }
142
+ }
143
+ }
144
+ }
145
+ catch (error) {
146
+ child.kill();
147
+ await exited.catch(() => undefined);
148
+ throw error;
149
+ }
150
+ const code = await exited;
151
+ if (code !== 0 || index !== objectIds.length || remaining !== -1 || trailer) {
152
+ throw new SpawnfileError("validation_error", `Workspace bundle blob stream ended early${stderr ? `: ${stderr.trim()}` : ""}`);
153
+ }
154
+ };
@@ -0,0 +1,34 @@
1
+ import type { BundleFilesInput, BundleIdentityMode } from "./workspaceBundleFiles.js";
2
+ import { type BundleTarSummary } from "./workspaceBundleTar.js";
3
+ export type BundleInputKind = "dependencies" | "files" | "generated";
4
+ /** Everything a build step needs that is not part of one bundle's declaration. */
5
+ export interface BundleBuildContext {
6
+ dockerCommand: string;
7
+ identity: BundleIdentityMode;
8
+ /** Real path of this compile's output directory, excluded from every input root that contains it. */
9
+ outputReal: string;
10
+ /** `linux/<arch>` of the target container. */
11
+ platform: string;
12
+ /** Private scratch space for installs and generated output. */
13
+ workRoot: string;
14
+ }
15
+ /** A bundle whose key is known; `write` builds its archive only on a cache miss. */
16
+ export interface BundleBuildPlan {
17
+ input: BundleInputKind;
18
+ key: string;
19
+ write: (temporaryTar: string) => Promise<BundleTarSummary>;
20
+ }
21
+ /** The identity of a file input set: every entry's path, mode and content identity. */
22
+ export declare const filesIdentity: (input: Pick<BundleFilesInput, "entries">) => Array<[string, number, string]>;
23
+ /**
24
+ * The cache key of a `files` bundle: every input file's path, mode and
25
+ * content identity, the archive writer that turns them into bytes, and the
26
+ * target platform.
27
+ */
28
+ export declare const computeWorkspaceBundleKey: (input: Pick<BundleFilesInput, "entries">, platform: string) => string;
29
+ /**
30
+ * The cache key of a recipe bundle: its kind, the full recipe (input
31
+ * identities, commands, pinned images, captured tool versions), the archive
32
+ * writer and the target platform. Callers build `recipe` with a fixed key order.
33
+ */
34
+ export declare const computeRecipeBundleKey: (input: Exclude<BundleInputKind, "files">, recipe: unknown, platform: string) => string;
@@ -0,0 +1,17 @@
1
+ import { createHash } from "node:crypto";
2
+ import { WORKSPACE_BUNDLE_TAR_WRITER } from "./workspaceBundleTar.js";
3
+ const KEY_VERSION = "spawnfile.workspace-bundle-key.v1";
4
+ /** The identity of a file input set: every entry's path, mode and content identity. */
5
+ export const filesIdentity = (input) => input.entries.map((entry) => [entry.path, entry.mode, entry.identity]);
6
+ /**
7
+ * The cache key of a `files` bundle: every input file's path, mode and
8
+ * content identity, the archive writer that turns them into bytes, and the
9
+ * target platform.
10
+ */
11
+ export const computeWorkspaceBundleKey = (input, platform) => createHash("sha256").update(JSON.stringify({ entries: filesIdentity(input), input: "files", platform, version: KEY_VERSION, writer: WORKSPACE_BUNDLE_TAR_WRITER })).digest("hex");
12
+ /**
13
+ * The cache key of a recipe bundle: its kind, the full recipe (input
14
+ * identities, commands, pinned images, captured tool versions), the archive
15
+ * writer and the target platform. Callers build `recipe` with a fixed key order.
16
+ */
17
+ export const computeRecipeBundleKey = (input, recipe, platform) => createHash("sha256").update(JSON.stringify({ input, platform, recipe, version: KEY_VERSION, writer: WORKSPACE_BUNDLE_TAR_WRITER })).digest("hex");