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
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { createReadStream } from "node:fs";
3
- import { copyFile, lstat, mkdir, readdir, realpath, chmod } from "node:fs/promises";
3
+ import { copyFile, lstat, mkdir, readdir, readlink, realpath, chmod, symlink } from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  export const within = (root, file) => file === root || file.startsWith(root + path.sep);
@@ -37,27 +37,50 @@ export async function sealFile(source, destination) {
37
37
  throw Error("Training source changed while hashing");
38
38
  return { source, destination, sha256: `sha256:${digest.digest("hex")}`, mode: Number(before.mode & 511n), size };
39
39
  }
40
- /** Only explicitly selected trees are traversed. Symlinks never enter Docker context. */
40
+ function assertLink(root, file, target) {
41
+ // Absolute host paths cannot reproduce a confined link in a relocated snapshot.
42
+ if (!target || path.isAbsolute(target) || !within(root, path.resolve(path.dirname(file), target))) {
43
+ throw Error("Training source trees must not contain escaping symlinks");
44
+ }
45
+ }
46
+ async function sealLink(source, destination) {
47
+ const before = await lstat(source, { bigint: true }), target = await readlink(source);
48
+ const after = await lstat(source, { bigint: true });
49
+ if (!before.isSymbolicLink() || !after.isSymbolicLink() || after.ino !== before.ino ||
50
+ after.ctimeNs !== before.ctimeNs || after.dev !== before.dev)
51
+ throw Error("Training source changed while hashing");
52
+ return { source, destination, target, sha256: hashJson({ type: "symlink", target }), mode: 0o777, size: Buffer.byteLength(target) };
53
+ }
54
+ /** Only physical entries are walked; confined input links retain their own identity. */
41
55
  export async function sealTree(source, destination, options = {}) {
42
56
  await exactPath(source);
43
57
  const files = [];
58
+ let entries = 0;
44
59
  const walk = async (root, target, depth) => {
45
- if (depth > 32 || files.length > 10000)
60
+ if (depth > 32)
61
+ throw Error("Training source tree exceeds bounds");
62
+ const stat = await lstat(root);
63
+ if ((depth > 0 || !stat.isDirectory()) && ++entries > 10000)
46
64
  throw Error("Training source tree exceeds bounds");
47
- let stat = await lstat(root);
48
65
  if (stat.isSymbolicLink()) {
49
- const actual = await realpath(root);
50
- if (!options.internalSymlinks || !within(source, actual))
66
+ if (!options.internalSymlinks)
51
67
  throw Error("Training source trees must not contain escaping symlinks");
52
- root = actual;
53
- stat = await lstat(root);
68
+ const link = await sealLink(root, target);
69
+ assertLink(source, root, link.target);
70
+ // Resolve only for containment validation, never to enumerate or copy the target.
71
+ if (!within(source, await realpath(root)))
72
+ throw Error("Training source trees must not contain escaping symlinks");
73
+ files.push(link);
74
+ return;
54
75
  }
55
76
  if (stat.isDirectory()) {
56
- for (const entry of (await readdir(root)).sort()) {
57
- if (options.ignoreGit && entry === ".git")
58
- continue;
59
- if (options.ignoreDevelopment && (ignored.has(entry) || /(?:\.test\.[cm]?[jt]s|_test\.py|\.pyc)$/u.test(entry)))
60
- continue;
77
+ const children = (await readdir(root)).sort().filter(entry => !(options.ignoreGit && entry === ".git") &&
78
+ !(options.ignoreDevelopment && (ignored.has(entry) || /(?:\.test\.[cm]?[jt]s|_test\.py|\.pyc)$/u.test(entry))));
79
+ // Retain empty input directories so their preserved links still resolve after relocation.
80
+ if (options.internalSymlinks && !children.length && target)
81
+ files.push({ source: root, destination: target,
82
+ directory: true, sha256: hashJson({ type: "directory" }), mode: stat.mode & 0o777, size: 0 });
83
+ for (const entry of children) {
61
84
  await walk(path.join(root, entry), path.posix.join(target, entry), depth + 1);
62
85
  }
63
86
  }
@@ -70,16 +93,54 @@ export async function sealTree(source, destination, options = {}) {
70
93
  return files;
71
94
  }
72
95
  export async function copySealed(files, root) {
96
+ const directories = new Set();
97
+ const ensureDirectory = async (directory) => {
98
+ if (directories.has(directory))
99
+ return;
100
+ const parent = path.dirname(directory);
101
+ if (parent !== directory) {
102
+ await ensureDirectory(parent);
103
+ await mkdir(directory, { mode: 0o700 }).catch((error) => { if (error.code !== "EEXIST")
104
+ throw error; });
105
+ }
106
+ await exactPath(directory);
107
+ if (!(await lstat(directory)).isDirectory())
108
+ throw Error("Training staging parent must be a directory");
109
+ directories.add(directory);
110
+ };
111
+ await ensureDirectory(root);
73
112
  for (const file of files) {
74
113
  const target = path.resolve(root, file.destination);
75
114
  if (!within(root, target) || target === root)
76
115
  throw Error("Training destination escapes owned staging");
77
- await mkdir(path.dirname(target), { recursive: true, mode: 0o700 });
116
+ await ensureDirectory(path.dirname(target));
117
+ if (file.directory) {
118
+ await ensureDirectory(target);
119
+ await chmod(target, file.mode);
120
+ continue;
121
+ }
122
+ if (file.target !== undefined) {
123
+ assertLink(root, target, file.target);
124
+ const current = await sealLink(file.source, file.destination);
125
+ if (hashJson(fileIdentity([current])) !== hashJson(fileIdentity([file])))
126
+ throw Error("Training source changed during staging");
127
+ await symlink(file.target, target);
128
+ if (await readlink(target) !== file.target)
129
+ throw Error("Training source changed during staging");
130
+ continue;
131
+ }
132
+ await exactPath(file.source);
78
133
  await copyFile(file.source, target, 1);
79
134
  await chmod(target, file.mode);
80
135
  const copied = await sealFile(target, file.destination);
81
136
  if (copied.sha256 !== file.sha256 || copied.size !== file.size)
82
137
  throw Error("Training source changed during staging");
83
138
  }
139
+ // Validate chains after every target has been staged, including links ordered before files.
140
+ for (const file of files.filter(file => file.target !== undefined)) {
141
+ if (!within(root, await realpath(path.join(root, file.destination))))
142
+ throw Error("Training staged symlink escapes owned staging");
143
+ }
84
144
  }
85
- export const fileIdentity = (files) => files.map(({ destination, sha256, mode, size }) => ({ destination, sha256, mode, size }));
145
+ export const fileIdentity = (files) => files.map(({ destination, sha256, mode, size, target, directory }) => ({ destination, sha256, mode, size, ...(target !== undefined ? { type: "symlink", target } : {}),
146
+ ...(directory ? { type: "directory" } : {}) }));
@@ -1,6 +1,7 @@
1
1
  import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { TRAINING_EXECUTION_CAPABILITY_LABEL } from "../contract.js";
4
5
  import { assertInputRoot, copySealed, fileIdentity, hashJson, sealFile, sealTree } from "./files.js";
5
6
  import { bindDaimonParentVerification, resolveTrainingDaimonParent } from "./daimonParent.js";
6
7
  import { normalizeTrainingContext } from "./contextModes.js";
@@ -27,11 +28,15 @@ export async function planTrainingImage(build, root, auth, ownRoot = packageRoot
27
28
  // Before any Docker call, including --dry-run: the attested Daimon runtime identity
28
29
  // and the declared native parent must name one rebuild (see daimonParent.ts).
29
30
  const daimonParent = await resolveTrainingDaimonParent(build, declarationPath);
31
+ assertInputRoot(ownRoot, auth);
30
32
  for (const source of [build.paideia, build.bridge, build.claude, build.integration.source, ...build.compiler ? [build.compiler] : [], ...build.bootstrap ? [build.bootstrap] : []])
31
33
  assertInputRoot(resolve(source), auth);
32
34
  const assets = ownRoot === packageRoot ? trainingAssets : path.join(ownRoot, "runtime-images/training");
35
+ assertInputRoot(assets, auth);
33
36
  const recipe = await readFile(path.join(assets, "Dockerfile"), "utf8");
34
- const dockerfile = daimonParent ? bindDaimonParentVerification(recipe, daimonParent) : recipe;
37
+ // Always override the parent's label: compatibility belongs to this installed integration.
38
+ const capability = build.integration.execution ?? "";
39
+ const dockerfile = `${daimonParent ? bindDaimonParentVerification(recipe, daimonParent) : recipe}\nLABEL ${TRAINING_EXECUTION_CAPABILITY_LABEL}=${JSON.stringify(capability)}\n`;
35
40
  const ownLock = path.extname(fileURLToPath(import.meta.url)) === ".ts" || ownRoot !== packageRoot
36
41
  ? path.join(ownRoot, "package-lock.json") : path.join(assets, "package-lock.json");
37
42
  const files = [
@@ -64,12 +69,12 @@ export async function buildTrainingImage(plan, options) {
64
69
  timeoutMs: options.timeoutMs, signal: options.signal, ...stream ? options.streams : {}
65
70
  });
66
71
  const inspect = async () => {
67
- const result = await call(["image", "inspect", tag, "--format", '{{json .Id}}\n{{json (index .Config.Labels "com.spawnfile.training.recipe")}}']);
72
+ const result = await call(["image", "inspect", tag, "--format", `{{json .Id}}\n{{json (index .Config.Labels "com.spawnfile.training.recipe")}}\n{{json (index .Config.Labels "${TRAINING_EXECUTION_CAPABILITY_LABEL}")}}`]);
68
73
  if (result.code !== 0)
69
74
  return undefined;
70
75
  try {
71
- const [id, digest] = result.stdout.trim().split("\n").map(line => JSON.parse(line));
72
- return /^sha256:[a-f0-9]{64}$/u.test(id) && digest === plan.digest ? id : undefined;
76
+ const [id, digest, capability] = result.stdout.trim().split("\n").map(line => JSON.parse(line));
77
+ return /^sha256:[a-f0-9]{64}$/u.test(id) && digest === plan.digest && capability === (plan.build.integration.execution ?? "") ? id : undefined;
73
78
  }
74
79
  catch {
75
80
  return undefined;
@@ -11,6 +11,7 @@ export interface PlannedInput {
11
11
  tree: string;
12
12
  common: string;
13
13
  overlays: SealedFile[];
14
+ paths: string[];
14
15
  };
15
16
  }
16
17
  export declare function planInputs(config: TrainingPreparationConfig, root: string, auth: string[]): Promise<PlannedInput[]>;
@@ -1,16 +1,18 @@
1
1
  import { execFile } from "node:child_process";
2
- import { mkdir, readFile } from "node:fs/promises";
2
+ import { mkdir, readFile, realpath } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { promisify } from "node:util";
5
5
  import { pathToFileURL } from "node:url";
6
- import { assertInputRoot, copySealed, exactPath, fileIdentity, hashJson, sealFile, sealTree } from "./files.js";
6
+ import { assertInputRoot, copySealed, exactPath, fileIdentity, hashJson, sealFile, sealTree, within } from "./files.js";
7
7
  const execute = promisify(execFile);
8
- const git = async (cwd, args) => {
8
+ const git = async (cwd, args, input) => {
9
9
  const env = Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.startsWith("GIT_")));
10
- return (await execute("git", ["-c", "core.hooksPath=/dev/null", "-c", "core.fsmonitor=false", ...args], {
10
+ const operation = execute("git", ["-c", "core.hooksPath=/dev/null", "-c", "core.fsmonitor=false", ...args], {
11
11
  cwd, env: { ...env, GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", GIT_ATTR_NOSYSTEM: "1", GIT_TERMINAL_PROMPT: "0" },
12
12
  timeout: 120000, maxBuffer: 8 * 1024 * 1024
13
- })).stdout;
13
+ });
14
+ operation.child.stdin?.end(input);
15
+ return (await operation).stdout;
14
16
  };
15
17
  export async function planInputs(config, root, auth) {
16
18
  return Promise.all(config.inputs.map(async (input) => {
@@ -49,7 +51,7 @@ export async function planInputs(config, root, auth) {
49
51
  throw Error("Git overlay digest mismatch or duplicate destination");
50
52
  overlays.push(file);
51
53
  }
52
- return { id: input.id, source, destination: input.destination, git: { revision: input.git.revision, tree, common, overlays },
54
+ return { id: input.id, source, destination: input.destination, git: { revision: input.git.revision, tree, common, overlays, paths: entries.map(entry => entry.slice(entry.indexOf("\t") + 1)) },
53
55
  digest: hashJson({ revision: input.git.revision, tree, overlays: fileIdentity(overlays) }) };
54
56
  }));
55
57
  }
@@ -65,7 +67,12 @@ export async function stageInput(input, target) {
65
67
  await mkdir(target, { mode: 0o700 });
66
68
  await git(target, ["init", "--quiet", "--template="]);
67
69
  await git(target, ["-c", "protocol.file.allow=always", "fetch", "--quiet", "--depth=1", pathToFileURL(input.git.common).href, input.git.revision]);
68
- await git(target, ["-c", "advice.detachedHead=false", "checkout", "--quiet", "--detach", input.git.revision]);
70
+ // Populate the index/HEAD without writing paths that the overlay owns.
71
+ await git(target, ["update-ref", "--no-deref", "HEAD", input.git.revision]);
72
+ await git(target, ["read-tree", input.git.revision]);
73
+ const overlayPaths = new Set(input.git.overlays.map(file => file.destination));
74
+ const basePaths = input.git.paths.filter(file => !overlayPaths.has(file));
75
+ await git(target, ["checkout-index", "--stdin", "-z"], basePaths.map(file => `${file}\0`).join(""));
69
76
  if ((await git(target, ["rev-parse", "HEAD^{tree}"])).trim() !== input.git.tree ||
70
77
  (await git(target, ["rev-parse", "--path-format=absolute", "--git-common-dir"])).trim() !== path.join(target, ".git"))
71
78
  throw Error("Git training snapshot identity mismatch");
@@ -78,10 +85,11 @@ export async function verifyCanonicalPins(inputs, sources, staged) {
78
85
  const index = inputs.findIndex(input => source.sourcePath === input.source || source.sourcePath.startsWith(input.source + path.sep));
79
86
  if (index < 0)
80
87
  throw Error("Canonical training source is outside declared inputs");
81
- if (!inputs[index].git && !inputs[index].files)
82
- continue;
83
88
  const file = path.join(staged[index], path.relative(inputs[index].source, source.sourcePath));
84
- if ((await sealFile(file, "pin")).sha256 !== source.sha256)
89
+ const canonicalFile = await realpath(file);
90
+ if (!within(staged[index], canonicalFile))
91
+ throw Error("Canonical training source escapes its staged input");
92
+ if ((await sealFile(canonicalFile, "pin")).sha256 !== source.sha256)
85
93
  throw Error("Pinned Git snapshot differs from the selected canonical agent");
86
94
  }
87
95
  }
@@ -1,6 +1,10 @@
1
+ import { type TrainingExecution } from "../execution.js";
1
2
  import type { TrainingContext } from "../contract.js";
2
3
  import type { TrainingDockerProcess } from "../container/process.js";
3
4
  export interface PrepareTrainingOptions {
5
+ execution?: TrainingExecution;
6
+ outputOverride?: string;
7
+ imageOverride?: string;
4
8
  configPath: string;
5
9
  context: TrainingContext;
6
10
  args: readonly string[];
@@ -18,6 +22,7 @@ export interface PrepareTrainingOptions {
18
22
  repairWitness?: string;
19
23
  }
20
24
  export interface PreparedTraining {
25
+ execution?: TrainingExecution;
21
26
  digest: string;
22
27
  image: string;
23
28
  configPath: string;
@@ -1,13 +1,15 @@
1
- import { lstat, mkdir, readFile, realpath, rm, writeFile } from "node:fs/promises";
1
+ import { lstat, mkdir, readFile, realpath, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import { mapTrainingExecution, trainingExecutionIdentity } from "../execution.js";
3
4
  import { trainingContainerConfigSchema } from "../container/contract.js";
5
+ import { assertTrainingSubjectIsolation } from "../container/subjectIsolation.js";
4
6
  import { trainingPreparationSchema, parseTrainingMappedPreparation } from "./contract.js";
5
7
  import { assertInputRoot, exactPath, fileIdentity, hashJson, sealTree, within } from "./files.js";
6
8
  import { planInputs, readBoundedJson, stageInput, verifyCanonicalPins } from "./inputs.js";
7
9
  import { planTrainingImage, buildTrainingImage } from "./image.js";
8
- import { planMeasurementRepair, stageMeasurementRepair, writeTrainingWitness } from "../repair/index.js";
10
+ import { planMeasurementRepair, stageMeasurementRepair, validateMeasurementRepairOutput, writeTrainingWitness } from "../repair/index.js";
9
11
  import { prepareTrainingBroker } from "./broker.js";
10
- import { claimTrainingPreparationScratch } from "./scratch.js";
12
+ import { claimTrainingPreparationScratch, removeTrainingPreparationScratch } from "./scratch.js";
11
13
  function mappedReceipt(digest, image, config) {
12
14
  return parseTrainingMappedPreparation({ version: "spawnfile.training-preparation.v1", preparationDigest: digest, imageId: image,
13
15
  bindings: config.inputs.map(input => ({ inputId: input.id, destination: input.destination })), outputRoot: "/run/training/output",
@@ -17,11 +19,20 @@ function mappedReceipt(digest, image, config) {
17
19
  }
18
20
  /** Reads only until the explicit dry-run boundary; preparation never executes project code. */
19
21
  export async function prepareTraining(options) {
20
- const config = trainingPreparationSchema.parse(await readBoundedJson(options.configPath));
21
- // A v2 declaration still lowers to the unchanged v1 launch config; only v3 carries a broker slot into Docker.
22
- const launchVersion = config.version === "spawnfile.training-container.v3" ? config.version : "spawnfile.training-container.v1";
22
+ const raw = await readBoundedJson(options.configPath);
23
+ const config = trainingPreparationSchema.parse({ ...raw,
24
+ ...(options.outputOverride ? { output: { source: options.outputOverride, destination: "/run/training/output" } } : {}),
25
+ ...(options.imageOverride ? { image: { ref: options.imageOverride } } : {}) });
26
+ await assertTrainingSubjectIsolation(config, options.context);
27
+ // A v2 declaration still lowers to the unchanged v1 launch config; v4 carries the broker slots into Docker.
28
+ const launchVersion = config.version === "spawnfile.training-container.v4" ? config.version : "spawnfile.training-container.v1";
23
29
  const root = path.dirname(path.resolve(options.configPath));
24
30
  const auth = config.auth.map(entry => ({ ...entry, source: path.resolve(root, entry.source) }));
31
+ const evaluatorSecrets = config.evaluatorSecrets?.map(entry => ({ ...entry, source: path.resolve(root, entry.source) }));
32
+ const protectedLeaves = [...auth, ...evaluatorSecrets ?? []];
33
+ const evaluatorConfig = { ...(config.evaluatorRoutes ? { evaluatorRoutes: config.evaluatorRoutes } : {}),
34
+ ...(config.subjectIsolation ? { subjectIsolation: config.subjectIsolation } : {}),
35
+ ...(evaluatorSecrets ? { evaluatorSecrets } : {}) };
25
36
  const output = path.resolve(root, config.output.source), parent = path.dirname(output);
26
37
  for (let index = 0; index < options.args.length; index++) {
27
38
  if (options.args[index] !== "--out")
@@ -30,42 +41,58 @@ export async function prepareTraining(options) {
30
41
  if (declared === undefined || path.resolve(declared) !== output)
31
42
  throw Error("Training --out must match configured output");
32
43
  }
33
- assertInputRoot(output, auth.map(entry => entry.source));
44
+ // Refuse invalid repair destinations before auth validation or source/witness reads.
45
+ if (options.repairMeasurements)
46
+ await validateMeasurementRepairOutput({ parent: options.repairMeasurements,
47
+ output, resume: options.args.includes("--resume") });
48
+ // Validate references before traversing image/input sources; aliases must never bypass secret exclusions.
49
+ for (const entry of options.dryRun ? evaluatorSecrets ?? [] : protectedLeaves) {
50
+ const source = await exactPath(entry.source), info = await lstat(source);
51
+ if (source !== entry.source || !info.isFile())
52
+ throw Error("Training auth must be a canonical regular leaf");
53
+ if (evaluatorSecrets?.includes(entry) && info.nlink !== 1)
54
+ throw Error("Evaluator secrets must not have hardlink aliases");
55
+ }
56
+ assertInputRoot(output, protectedLeaves.map(entry => entry.source));
34
57
  if (await realpath(parent) !== parent)
35
58
  throw Error("Training output parent must be canonical and already exist");
36
- const inputs = await planInputs(config, root, auth.map(entry => entry.source));
59
+ const inputs = await planInputs(config, root, protectedLeaves.map(entry => entry.source));
60
+ const canonicalPins = [...options.context.sources, ...options.execution?.datasetPins ?? []];
37
61
  const roots = inputs.map(input => input.source);
38
62
  for (let index = 0; index < inputs.length; index++) {
39
63
  const input = inputs[index];
40
- if (within(input.source, output) || within(output, input.source))
64
+ // A selected/Git snapshot is staged before launch; never bind the source project over its own output.
65
+ const snapshotPaths = input.files?.map(file => path.join(input.source, file.destination))
66
+ ?? (input.git ? [...input.git.paths, ...input.git.overlays.map(file => file.destination)].map(file => path.join(input.source, file)) : undefined);
67
+ if (within(output, input.source) || (within(input.source, output) &&
68
+ (!snapshotPaths || snapshotPaths.some(file => within(output, file) || within(file, output))))) {
41
69
  throw Error("Training output overlaps readonly input");
70
+ }
42
71
  for (const other of inputs.slice(index + 1))
43
72
  if (within(input.source, other.source) || within(other.source, input.source) ||
44
73
  within(input.destination, other.destination) || within(other.destination, input.destination))
45
74
  throw Error("Training inputs overlap");
46
75
  }
47
76
  const imagePlan = "build" in config.image
48
- ? await planTrainingImage(config.image.build, root, auth.map(entry => entry.source), options.packageRoot, path.resolve(options.configPath))
77
+ ? await planTrainingImage(config.image.build, root, protectedLeaves.map(entry => entry.source), options.packageRoot, path.resolve(options.configPath))
49
78
  : undefined;
50
79
  if (options.repairWitness && !options.repairMeasurements)
51
80
  throw Error("A repair witness requires --repair-measurements");
52
81
  if (options.repairMeasurements && !imagePlan)
53
82
  throw Error("Measurement repair requires a verifiable image build recipe");
54
83
  const repair = options.repairMeasurements ? await planMeasurementRepair({ parent: options.repairMeasurements,
55
- witness: options.repairWitness, output, auth: auth.map(entry => entry.source), image: imagePlan, inputs,
84
+ witness: options.repairWitness, output, auth: protectedLeaves.map(entry => entry.source), image: imagePlan, inputs,
56
85
  context: options.context, resume: options.args.includes("--resume") }) : undefined;
57
86
  if (repair && hashJson(config.integration) !== hashJson(repair.witness.manifest.config.integration))
58
87
  throw Error("Repair integration settings binding changed");
59
88
  const digest = hashJson({ config, sources: inputs.map(input => ({ id: input.id, digest: input.digest })), image: imagePlan?.digest ?? config.image,
60
- canonical: options.context.project.sourceDigest, ...(repair ? { repair: { witness: repair.witness.digest, parent: repair.manifestDigest } } : {}) });
89
+ canonical: options.context.project.sourceDigest,
90
+ ...(options.execution ? { execution: trainingExecutionIdentity(options.execution) } : {}), ...(repair ? { repair: { witness: repair.witness.digest, parent: repair.manifestDigest } } : {}) });
61
91
  if (config.broker)
62
- await prepareTrainingBroker(config.broker, root, path.dirname(path.resolve(options.configPath)), false);
92
+ await prepareTrainingBroker(config.broker, root, path.dirname(path.resolve(options.configPath)), false, config.slots);
63
93
  if (options.dryRun)
64
94
  return { digest, dryRun: true };
65
95
  options.signal?.throwIfAborted();
66
- for (const entry of auth)
67
- if (await exactPath(entry.source) !== entry.source || !(await lstat(entry.source)).isFile())
68
- throw Error("Training auth must be a canonical regular leaf");
69
96
  const execute = (args) => options.process(args, { timeoutMs: options.timeoutMs, signal: options.signal });
70
97
  const endpoint = await execute(["context", "inspect", config.dockerContext, "--format", "{{json .Endpoints.docker.Host}}"]);
71
98
  if (endpoint.code !== 0 || !/^unix:\/\//u.test(JSON.parse(endpoint.stdout)))
@@ -82,6 +109,7 @@ export async function prepareTraining(options) {
82
109
  const resume = options.args.includes("--resume");
83
110
  let image, staged;
84
111
  if (resume) {
112
+ await claimTrainingPreparationScratch(staging, digest, options.streams.stderr, "resume");
85
113
  const previous = JSON.parse(await readFile(statePath, "utf8"));
86
114
  if (previous.digest !== digest || !/^sha256:[a-f0-9]{64}$/u.test(previous.image) ||
87
115
  JSON.stringify(previous.staged) !== JSON.stringify(inputs.map(input => input.git || input.files ? path.join(staging, input.id) : input.source)))
@@ -94,7 +122,7 @@ export async function prepareTraining(options) {
94
122
  const mapped = parseTrainingMappedPreparation(await readBoundedJson(preparationPath));
95
123
  if (mapped.preparationDigest !== digest || mapped.imageId !== image)
96
124
  throw Error("Persisted training preparation identity mismatch");
97
- await verifyCanonicalPins(inputs, options.context.sources, staged);
125
+ await verifyCanonicalPins(inputs, canonicalPins, staged);
98
126
  }
99
127
  else {
100
128
  let owned = false;
@@ -108,8 +136,13 @@ export async function prepareTraining(options) {
108
136
  if (error.code !== "EEXIST" || !(await lstat(output)).isDirectory() || await realpath(output) !== output)
109
137
  throw error;
110
138
  }
111
- staged = await Promise.all(inputs.map(input => stageInput(input, path.join(staging, input.id))));
112
- await verifyCanonicalPins(inputs, options.context.sources, staged);
139
+ // Do not remove owned staging while sibling input jobs can still write into it.
140
+ const results = await Promise.allSettled(inputs.map(input => stageInput(input, path.join(staging, input.id))));
141
+ const failure = results.find(result => result.status === "rejected");
142
+ if (failure?.status === "rejected")
143
+ throw failure.reason;
144
+ staged = results.map(result => result.value);
145
+ await verifyCanonicalPins(inputs, canonicalPins, staged);
113
146
  image = imagePlan ? (await buildTrainingImage(imagePlan, { parent, dockerContext: config.dockerContext, process: options.process,
114
147
  timeoutMs: options.timeoutMs, signal: options.signal, streams: options.streams })).imageId : "ref" in config.image ? config.image.ref : "";
115
148
  if (!image.startsWith("sha256:")) {
@@ -119,9 +152,9 @@ export async function prepareTraining(options) {
119
152
  image = inspected.stdout.trim();
120
153
  }
121
154
  const mapped = mappedReceipt(digest, image, config);
122
- const preparedBroker = config.broker ? await prepareTrainingBroker(config.broker, root, staging, true) : undefined;
123
- const launch = trainingContainerConfigSchema.parse({ version: launchVersion, dockerContext: config.dockerContext,
124
- inputs: inputs.map((input, index) => ({ source: staged[index], destination: input.destination })), output: { source: output, destination: "/run/training/output" }, auth,
155
+ const preparedBroker = config.broker ? await prepareTrainingBroker(config.broker, root, staging, true, config.slots) : undefined;
156
+ const launch = trainingContainerConfigSchema.parse({ version: launchVersion, slots: config.slots, dockerContext: config.dockerContext,
157
+ inputs: inputs.map((input, index) => ({ source: staged[index], destination: input.destination })), output: { source: output, destination: "/run/training/output" }, auth, ...evaluatorConfig,
125
158
  ...(preparedBroker ? { broker: preparedBroker.launch } : {}) });
126
159
  await writeFile(configPath, JSON.stringify(launch), { flag: "wx", mode: 0o600 });
127
160
  await writeFile(preparationPath, JSON.stringify(parseTrainingMappedPreparation(mapped)), { flag: "wx", mode: 0o400 });
@@ -129,18 +162,18 @@ export async function prepareTraining(options) {
129
162
  await writeFile(statePath, JSON.stringify({ digest, image, staged, snapshots }), { flag: "wx", mode: 0o600 });
130
163
  if (imagePlan)
131
164
  await writeTrainingWitness({ directory: staging, digest, image, config, context: options.context,
132
- args: options.args, inputs, staged, snapshots, plan: imagePlan,
165
+ args: options.args, execution: options.execution, inputs, staged, snapshots, plan: imagePlan,
133
166
  ...(repair ? { repair: { witness: repair.witness.digest, parent: repair.manifestDigest } } : {}) });
134
167
  }
135
168
  catch (error) {
136
169
  if (owned)
137
- await rm(staging, { recursive: true, force: true });
170
+ await removeTrainingPreparationScratch(staging);
138
171
  throw error;
139
172
  }
140
173
  }
141
- const expectedBroker = config.broker ? await prepareTrainingBroker(config.broker, root, staging, false) : undefined;
142
- const expectedLaunch = trainingContainerConfigSchema.parse({ version: launchVersion, dockerContext: config.dockerContext,
143
- inputs: inputs.map((input, index) => ({ source: staged[index], destination: input.destination })), output: { source: output, destination: "/run/training/output" }, auth,
174
+ const expectedBroker = config.broker ? await prepareTrainingBroker(config.broker, root, staging, false, config.slots) : undefined;
175
+ const expectedLaunch = trainingContainerConfigSchema.parse({ version: launchVersion, slots: config.slots, dockerContext: config.dockerContext,
176
+ inputs: inputs.map((input, index) => ({ source: staged[index], destination: input.destination })), output: { source: output, destination: "/run/training/output" }, auth, ...evaluatorConfig,
144
177
  ...(expectedBroker ? { broker: expectedBroker.launch } : {}) });
145
178
  if (hashJson(await readBoundedJson(configPath)) !== hashJson(expectedLaunch) ||
146
179
  hashJson(await readBoundedJson(preparationPath)) !== hashJson(mappedReceipt(digest, image, config)))
@@ -150,6 +183,8 @@ export async function prepareTraining(options) {
150
183
  throw Error("Saved training image is missing or changed");
151
184
  const map = (file) => {
152
185
  const absolute = path.resolve(file);
186
+ if (within(output, absolute))
187
+ return absolute;
153
188
  const index = roots.findIndex(source => within(source, absolute));
154
189
  return index < 0 ? absolute : path.join(staged[index], path.relative(roots[index], absolute));
155
190
  };
@@ -161,7 +196,7 @@ export async function prepareTraining(options) {
161
196
  skills: options.context.skills.map(source => ({ ...source, sourcePath: map(source.sourcePath) })) };
162
197
  const args = [...options.args];
163
198
  for (let index = 0; index < args.length; index++) {
164
- if (["--train", "--test", "--cost-config"].includes(args[index]))
199
+ if (["--train", "--validation", "--test", "--cost-config"].includes(args[index]))
165
200
  args[++index] = map(args[index]);
166
201
  else if (args[index] === "--resource") {
167
202
  const value = args[++index], split = value.indexOf("=");
@@ -180,7 +215,9 @@ export async function prepareTraining(options) {
180
215
  else
181
216
  await writeFile(repairConfig, JSON.stringify(repairLaunch), { flag: "wx", mode: 0o600 });
182
217
  return { digest, image, configPath: repairConfig, preparationPath, repairPath: projected.repairPath, context,
218
+ ...(options.execution ? { execution: mapTrainingExecution(options.execution, map) } : {}),
183
219
  args: [...args, "--repair-measurements", "/run/training/inputs/repair-parent", "--repair-context", "/run/paideia/repair.json"] };
184
220
  }
185
- return { digest, image, configPath, preparationPath, context, args };
221
+ return { digest, image, configPath, preparationPath, context, args,
222
+ ...(options.execution ? { execution: mapTrainingExecution(options.execution, map) } : {}) };
186
223
  }
@@ -1,19 +1,8 @@
1
1
  /**
2
- * Claims the private preparation scratch directory for one launch.
3
- *
4
- * A launch that fails anywhere past staging — a failed build, a container that
5
- * aborted in its entrypoint — leaves this directory behind, and the next
6
- * attempt used to die on `EEXIST: mkdir '.spawnfile-training-preparation-…'`
7
- * before it could report anything useful. A live P8 run lost its second attempt
8
- * to exactly that.
9
- *
10
- * Leftovers from the *same* preparation are reclaimed: `state.json` records the
11
- * preparation digest, which pins the config, every input identity, the image
12
- * plan and the canonical source digest, so a matching one can only have staged
13
- * the same bytes and nothing is lost by staging them again (the image is
14
- * content-addressed and resolves from its tag). Anything else — a different
15
- * digest, or a run that aborted before it wrote its state — is left untouched
16
- * and reported by name, because only the operator can know whether it is
17
- * wanted or whether another launch is still using it.
2
+ * The receipt binds identity AND the owning process before staging starts.
3
+ * A matching digest alone never permits deletion. Unknown/remote/reused PIDs
4
+ * fail closed; only ESRCH on this host proves that a writer has stopped.
18
5
  */
19
- export declare const claimTrainingPreparationScratch: (staging: string, digest: string, notify: (line: string) => void) => Promise<void>;
6
+ export declare const claimTrainingPreparationScratch: (staging: string, digest: string, notify: (line: string) => void, mode?: "fresh" | "resume") => Promise<void>;
7
+ /** Make sealed directories removable without following input links out of owned scratch. */
8
+ export declare const removeTrainingPreparationScratch: (staging: string) => Promise<void>;
@@ -1,31 +1,59 @@
1
- import { lstat, mkdir, readFile, realpath, rm } from "node:fs/promises";
1
+ import path from "node:path";
2
+ import { hostname } from "node:os";
3
+ import { chmod, lstat, mkdir, readFile, readdir, realpath, rm, writeFile } from "node:fs/promises";
2
4
  /**
3
- * Claims the private preparation scratch directory for one launch.
4
- *
5
- * A launch that fails anywhere past staging — a failed build, a container that
6
- * aborted in its entrypoint — leaves this directory behind, and the next
7
- * attempt used to die on `EEXIST: mkdir '.spawnfile-training-preparation-…'`
8
- * before it could report anything useful. A live P8 run lost its second attempt
9
- * to exactly that.
10
- *
11
- * Leftovers from the *same* preparation are reclaimed: `state.json` records the
12
- * preparation digest, which pins the config, every input identity, the image
13
- * plan and the canonical source digest, so a matching one can only have staged
14
- * the same bytes and nothing is lost by staging them again (the image is
15
- * content-addressed and resolves from its tag). Anything else — a different
16
- * digest, or a run that aborted before it wrote its state — is left untouched
17
- * and reported by name, because only the operator can know whether it is
18
- * wanted or whether another launch is still using it.
5
+ * The receipt binds identity AND the owning process before staging starts.
6
+ * A matching digest alone never permits deletion. Unknown/remote/reused PIDs
7
+ * fail closed; only ESRCH on this host proves that a writer has stopped.
19
8
  */
20
- export const claimTrainingPreparationScratch = async (staging, digest, notify) => {
9
+ export const claimTrainingPreparationScratch = async (staging, digest, notify, mode = "fresh") => {
10
+ const recordOwner = (flag = "wx") => writeFile(path.join(staging, "owner.json"), JSON.stringify({ digest, host: hostname(), pid: process.pid }), { flag, mode: 0o600 });
11
+ // This guard lives outside scratch so removal/recreation cannot drop the
12
+ // exclusion barrier. Never break a stranded guard automatically: its absence
13
+ // of a receipt cannot establish that its holder stopped.
14
+ const guard = `${staging}.reclaim`;
21
15
  try {
22
- await mkdir(staging, { mode: 0o700 });
23
- return;
16
+ await mkdir(guard, { mode: 0o700 });
24
17
  }
25
18
  catch (error) {
26
19
  if (error.code !== "EEXIST")
27
20
  throw error;
21
+ throw Error(`Training preparation scratch recovery is already claimed: ${guard}; if no preparation is running, remove that guard and retry`);
22
+ }
23
+ try {
24
+ if (mode === "fresh") {
25
+ let created = false;
26
+ try {
27
+ await mkdir(staging, { mode: 0o700 });
28
+ created = true;
29
+ }
30
+ catch (error) {
31
+ if (error.code !== "EEXIST")
32
+ throw error;
33
+ }
34
+ if (created) {
35
+ await recordOwner();
36
+ return;
37
+ }
38
+ }
39
+ // Re-read under exclusion, never delete based on an earlier observation.
40
+ await assertAbandonedScratch(staging, digest, mode === "resume");
41
+ if (mode === "resume") {
42
+ // Claim before validating or reading snapshots: fresh recovery must see
43
+ // this live process rather than the stopped original preparer.
44
+ await recordOwner("w");
45
+ return;
46
+ }
47
+ notify(`Reusing the preparation scratch left by an earlier launch of this exact preparation: ${staging}`);
48
+ await removeTrainingPreparationScratch(staging);
49
+ await mkdir(staging, { mode: 0o700 });
50
+ await recordOwner();
51
+ }
52
+ finally {
53
+ await rm(guard, { recursive: true });
28
54
  }
55
+ };
56
+ const assertAbandonedScratch = async (staging, digest, resume) => {
29
57
  const info = await lstat(staging);
30
58
  if (!info.isDirectory() || info.isSymbolicLink() || await realpath(staging) !== staging) {
31
59
  throw Error(`Training preparation scratch ${staging} is not a canonical directory; remove it and retry`);
@@ -35,14 +63,52 @@ export const claimTrainingPreparationScratch = async (staging, digest, notify) =
35
63
  previous = JSON.parse(await readFile(`${staging}/state.json`, "utf8"));
36
64
  }
37
65
  catch {
38
- previous = undefined;
66
+ try {
67
+ previous = JSON.parse(await readFile(path.join(staging, "owner.json"), "utf8"));
68
+ }
69
+ catch {
70
+ previous = undefined;
71
+ }
39
72
  }
40
73
  if (previous?.digest !== digest) {
74
+ if (resume)
75
+ throw Error("Training preparation changed; exact resume rejected");
41
76
  throw Error(previous === undefined
42
77
  ? `Training preparation scratch ${staging} is left over from an interrupted launch that never recorded its identity; remove it to retry: rm -rf ${JSON.stringify(staging)}`
43
78
  : `Training preparation scratch ${staging} belongs to a different preparation (${String(previous.digest)}); remove it to retry: rm -rf ${JSON.stringify(staging)}`);
44
79
  }
45
- notify(`Reusing the preparation scratch left by an earlier launch of this exact preparation: ${staging}`);
80
+ let owner;
81
+ try {
82
+ owner = JSON.parse(await readFile(path.join(staging, "owner.json"), "utf8"));
83
+ }
84
+ catch { /* Unknown ownership fails closed. */ }
85
+ if (owner?.digest !== digest || owner.host !== hostname() || typeof owner.pid !== "number" ||
86
+ !Number.isSafeInteger(owner.pid) || owner.pid <= 0) {
87
+ throw Error(`Training preparation scratch ${staging} has no verifiable local owner; cannot prove its previous launch stopped`);
88
+ }
89
+ // Revalidation by the owning process is idempotent; other live owners refuse.
90
+ if (resume && owner.pid === process.pid)
91
+ return;
92
+ let stopped = false;
93
+ try {
94
+ process.kill(owner.pid, 0);
95
+ }
96
+ catch (error) {
97
+ stopped = error.code === "ESRCH";
98
+ }
99
+ if (!stopped)
100
+ throw Error(`Training preparation scratch ${staging} still has an active or unverifiable owner (${owner.pid}); retry after it stops`);
101
+ };
102
+ /** Make sealed directories removable without following input links out of owned scratch. */
103
+ export const removeTrainingPreparationScratch = async (staging) => {
104
+ const writable = async (entry) => {
105
+ const info = await lstat(entry);
106
+ if (!info.isDirectory() || info.isSymbolicLink())
107
+ return;
108
+ await chmod(entry, (info.mode & 0o777) | 0o700);
109
+ for (const child of await readdir(entry))
110
+ await writable(path.join(entry, child));
111
+ };
112
+ await writable(staging);
46
113
  await rm(staging, { recursive: true, force: true });
47
- await mkdir(staging, { mode: 0o700 });
48
114
  };