@mysten-incubation/devstack 0.0.1 → 0.1.1

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 (838) hide show
  1. package/README.md +8 -4
  2. package/dashboard-ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  3. package/dashboard-ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  4. package/dashboard-ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  5. package/dashboard-ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  6. package/dashboard-ui/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
  7. package/dashboard-ui/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
  8. package/dashboard-ui/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
  9. package/dashboard-ui/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
  10. package/dashboard-ui/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
  11. package/dashboard-ui/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
  12. package/dashboard-ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  13. package/dashboard-ui/assets/index-Bmi1UtAg.js +1356 -0
  14. package/dashboard-ui/assets/index-D5EShVt4.js +3 -0
  15. package/dashboard-ui/assets/index-Deml9drg.css +1 -0
  16. package/dashboard-ui/index.html +13 -0
  17. package/dist/api/define-capabilities.d.mts +14 -3
  18. package/dist/api/define-capabilities.mjs +18 -1
  19. package/dist/api/define-capabilities.mjs.map +1 -1
  20. package/dist/api/define-devstack.d.mts +1 -1
  21. package/dist/api/define-devstack.mjs +6 -26
  22. package/dist/api/define-devstack.mjs.map +1 -1
  23. package/dist/api/define-plugin.d.mts +1 -0
  24. package/dist/api/define-plugin.mjs +2 -0
  25. package/dist/api/inference-network.d.mts +132 -0
  26. package/dist/api/inference-network.mjs +105 -16
  27. package/dist/api/inference-network.mjs.map +1 -1
  28. package/dist/api/plugin-errors.d.mts +7 -0
  29. package/dist/api/run-stack.d.mts +20 -9
  30. package/dist/api/run-stack.mjs +56 -29
  31. package/dist/api/run-stack.mjs.map +1 -1
  32. package/dist/build-integrations/playwright/config.d.mts +6 -4
  33. package/dist/build-integrations/playwright/config.mjs +5 -3
  34. package/dist/build-integrations/playwright/config.mjs.map +1 -1
  35. package/dist/build-integrations/playwright/errors.d.mts +1 -1
  36. package/dist/build-integrations/playwright/global-setup.d.mts +43 -17
  37. package/dist/build-integrations/playwright/global-setup.mjs +250 -13
  38. package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
  39. package/dist/build-integrations/playwright/index.d.mts +5 -3
  40. package/dist/build-integrations/playwright/index.mjs +4 -2
  41. package/dist/build-integrations/playwright/stack-context.d.mts +0 -1
  42. package/dist/build-integrations/playwright/stack-context.mjs +24 -63
  43. package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
  44. package/dist/build-integrations/playwright/wallet-context.d.mts +7 -10
  45. package/dist/build-integrations/playwright/wallet-context.mjs +8 -13
  46. package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
  47. package/dist/build-integrations/runtime/cold-start-url.d.mts +5 -0
  48. package/dist/build-integrations/runtime/cold-start-url.mjs +4 -3
  49. package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
  50. package/dist/build-integrations/runtime/conventional-routes.d.mts +51 -0
  51. package/dist/build-integrations/runtime/conventional-routes.mjs +92 -0
  52. package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -0
  53. package/dist/build-integrations/runtime/discover.d.mts +38 -30
  54. package/dist/build-integrations/runtime/discover.mjs +73 -70
  55. package/dist/build-integrations/runtime/discover.mjs.map +1 -1
  56. package/dist/build-integrations/runtime/endpoint-registry.d.mts +3 -1
  57. package/dist/build-integrations/runtime/endpoint-registry.mjs +3 -1
  58. package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
  59. package/dist/build-integrations/runtime/index.d.mts +7 -2
  60. package/dist/build-integrations/runtime/index.mjs +9 -2
  61. package/dist/build-integrations/runtime/manifest-types.d.mts +2 -0
  62. package/dist/build-integrations/runtime/manifest-types.mjs +3 -0
  63. package/dist/build-integrations/runtime/playwright-stack-context-slot.d.mts +29 -0
  64. package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs +10 -0
  65. package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs.map +1 -0
  66. package/dist/build-integrations/runtime/read-stack-context.d.mts +1 -1
  67. package/dist/build-integrations/runtime/read-stack-context.mjs +10 -4
  68. package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
  69. package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +48 -0
  70. package/dist/build-integrations/runtime/resolve-discovery-env.mjs +42 -0
  71. package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -0
  72. package/dist/build-integrations/runtime/stack-context.d.mts +1 -1
  73. package/dist/build-integrations/runtime/wallet-paths.mjs +2 -0
  74. package/dist/build-integrations/vite/index.d.mts +51 -0
  75. package/dist/build-integrations/vite/index.mjs +73 -0
  76. package/dist/build-integrations/vite/index.mjs.map +1 -0
  77. package/dist/build-integrations/vitest/env.d.mts +4 -3
  78. package/dist/build-integrations/vitest/env.mjs +6 -4
  79. package/dist/build-integrations/vitest/env.mjs.map +1 -1
  80. package/dist/build-integrations/vitest/errors.d.mts +25 -4
  81. package/dist/build-integrations/vitest/errors.mjs +15 -2
  82. package/dist/build-integrations/vitest/errors.mjs.map +1 -1
  83. package/dist/build-integrations/vitest/index.d.mts +2 -2
  84. package/dist/build-integrations/vitest/index.mjs +3 -3
  85. package/dist/build-integrations/vitest/setup.d.mts +4 -2
  86. package/dist/build-integrations/vitest/setup.mjs +2 -2
  87. package/dist/build-integrations/vitest/setup.mjs.map +1 -1
  88. package/dist/build-integrations/vitest/stack-context.d.mts +0 -1
  89. package/dist/build-integrations/vitest/stack-context.mjs +33 -13
  90. package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
  91. package/dist/cli/bail.mjs +23 -0
  92. package/dist/cli/bail.mjs.map +1 -0
  93. package/dist/{surfaces/cli/commands → cli}/doctor-probes.mjs +26 -16
  94. package/dist/cli/doctor-probes.mjs.map +1 -0
  95. package/dist/cli/main.d.mts +15 -1
  96. package/dist/cli/main.mjs +166 -482
  97. package/dist/cli/main.mjs.map +1 -1
  98. package/dist/cli/prune-direct.mjs +31 -201
  99. package/dist/cli/prune-direct.mjs.map +1 -1
  100. package/dist/cli/snapshot-reader.mjs +14 -11
  101. package/dist/cli/snapshot-reader.mjs.map +1 -1
  102. package/dist/cli/up-lifecycle.mjs +4 -1
  103. package/dist/cli/up-lifecycle.mjs.map +1 -1
  104. package/dist/cli/wirings/apply.mjs +95 -0
  105. package/dist/cli/wirings/apply.mjs.map +1 -0
  106. package/dist/cli/wirings/build-verb-layers.mjs +42 -0
  107. package/dist/cli/wirings/build-verb-layers.mjs.map +1 -0
  108. package/dist/cli/wirings/config-loader.mjs +69 -0
  109. package/dist/cli/wirings/config-loader.mjs.map +1 -0
  110. package/dist/cli/wirings/engine-command.mjs +30 -0
  111. package/dist/cli/wirings/engine-command.mjs.map +1 -0
  112. package/dist/cli/wirings/identity.mjs +72 -0
  113. package/dist/cli/wirings/identity.mjs.map +1 -0
  114. package/dist/cli/wirings/provide-file-system.mjs +7 -0
  115. package/dist/cli/wirings/provide-file-system.mjs.map +1 -0
  116. package/dist/cli/wirings/snapshot.mjs +177 -0
  117. package/dist/cli/wirings/snapshot.mjs.map +1 -0
  118. package/dist/cli/wirings/up.mjs +287 -0
  119. package/dist/cli/wirings/up.mjs.map +1 -0
  120. package/dist/cli/wirings/wipe.mjs +40 -0
  121. package/dist/cli/wirings/wipe.mjs.map +1 -0
  122. package/dist/contracts/capability-decl.d.mts +0 -2
  123. package/dist/contracts/codegenable.d.mts +43 -0
  124. package/dist/contracts/container-runtime.d.mts +26 -1
  125. package/dist/contracts/faucet-strategy.d.mts +25 -0
  126. package/dist/contracts/funding-strategy.d.mts +42 -0
  127. package/dist/contracts/plugin-expander.mjs +60 -0
  128. package/dist/contracts/plugin-expander.mjs.map +1 -0
  129. package/dist/contracts/projection.d.mts +5 -3
  130. package/dist/contracts/renderer.d.mts +23 -0
  131. package/dist/contracts/routable.d.mts +14 -1
  132. package/dist/contracts/snapshotable.d.mts +25 -3
  133. package/dist/contracts/wallet-protocol.d.mts +52 -0
  134. package/dist/contracts/wallet-protocol.mjs +52 -0
  135. package/dist/contracts/wallet-protocol.mjs.map +1 -0
  136. package/dist/index.d.mts +26 -25
  137. package/dist/index.mjs +20 -13
  138. package/dist/{runtime → orchestrators}/built-in-plugin-layers.mjs +10 -5
  139. package/dist/orchestrators/built-in-plugin-layers.mjs.map +1 -0
  140. package/dist/orchestrators/codegen/bindings.d.mts +1 -2
  141. package/dist/orchestrators/codegen/bindings.mjs +52 -186
  142. package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
  143. package/dist/orchestrators/codegen/emit.mjs +19 -1
  144. package/dist/orchestrators/codegen/emit.mjs.map +1 -1
  145. package/dist/orchestrators/codegen/errors.mjs +8 -3
  146. package/dist/orchestrators/codegen/errors.mjs.map +1 -1
  147. package/dist/orchestrators/codegen/format.mjs +47 -39
  148. package/dist/orchestrators/codegen/format.mjs.map +1 -1
  149. package/dist/orchestrators/codegen/gitignore.mjs +3 -1
  150. package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
  151. package/dist/orchestrators/codegen/output-location.mjs +41 -0
  152. package/dist/orchestrators/codegen/output-location.mjs.map +1 -0
  153. package/dist/orchestrators/codegen/paths.d.mts +1 -1
  154. package/dist/orchestrators/codegen/paths.mjs +40 -14
  155. package/dist/orchestrators/codegen/paths.mjs.map +1 -1
  156. package/dist/orchestrators/codegen/permissions.mjs +3 -1
  157. package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
  158. package/dist/orchestrators/codegen/service.mjs +157 -105
  159. package/dist/orchestrators/codegen/service.mjs.map +1 -1
  160. package/dist/orchestrators/lifecycle-prune/errors.mjs +31 -0
  161. package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -0
  162. package/dist/orchestrators/lifecycle-prune/index.mjs +350 -0
  163. package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -0
  164. package/dist/orchestrators/router/cleanup.mjs +26 -27
  165. package/dist/orchestrators/router/cleanup.mjs.map +1 -1
  166. package/dist/orchestrators/router/entrypoints.mjs +2 -2
  167. package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
  168. package/dist/orchestrators/router/file-provider.mjs +14 -5
  169. package/dist/orchestrators/router/file-provider.mjs.map +1 -1
  170. package/dist/orchestrators/router/hostname.mjs +19 -55
  171. package/dist/orchestrators/router/hostname.mjs.map +1 -1
  172. package/dist/orchestrators/router/index.mjs +11 -0
  173. package/dist/orchestrators/router/profile.mjs +2 -2
  174. package/dist/orchestrators/router/sentinels.mjs +26 -0
  175. package/dist/orchestrators/router/sentinels.mjs.map +1 -0
  176. package/dist/orchestrators/router/service.mjs +40 -37
  177. package/dist/orchestrators/router/service.mjs.map +1 -1
  178. package/dist/orchestrators/router/traefik-container.mjs +27 -16
  179. package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
  180. package/dist/{substrate/runtime → orchestrators}/run.mjs +26 -21
  181. package/dist/orchestrators/run.mjs.map +1 -0
  182. package/dist/orchestrators/runtime-composition.d.mts +1 -1
  183. package/dist/orchestrators/runtime-composition.mjs +123 -39
  184. package/dist/orchestrators/runtime-composition.mjs.map +1 -1
  185. package/dist/orchestrators/snapshot/capture-command.d.mts +1 -0
  186. package/dist/orchestrators/snapshot/capture-command.mjs +25 -0
  187. package/dist/orchestrators/snapshot/capture-command.mjs.map +1 -0
  188. package/dist/orchestrators/snapshot/capture.mjs +42 -28
  189. package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
  190. package/dist/orchestrators/snapshot/descriptor.mjs +35 -10
  191. package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
  192. package/dist/orchestrators/snapshot/identity-guard.mjs +1 -1
  193. package/dist/orchestrators/snapshot/index.mjs +13 -0
  194. package/dist/orchestrators/snapshot/integrity.d.mts +1 -1
  195. package/dist/orchestrators/snapshot/integrity.mjs +73 -17
  196. package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
  197. package/dist/orchestrators/snapshot/pending-marker.d.mts +1 -0
  198. package/dist/orchestrators/snapshot/pending-marker.mjs +99 -0
  199. package/dist/orchestrators/snapshot/pending-marker.mjs.map +1 -0
  200. package/dist/orchestrators/snapshot/phase-error.mjs +23 -0
  201. package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -0
  202. package/dist/orchestrators/snapshot/prune.mjs +20 -55
  203. package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
  204. package/dist/orchestrators/snapshot/recover-pending.d.mts +1 -0
  205. package/dist/orchestrators/snapshot/recover-pending.mjs +223 -0
  206. package/dist/orchestrators/snapshot/recover-pending.mjs.map +1 -0
  207. package/dist/orchestrators/snapshot/restore.mjs +118 -84
  208. package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
  209. package/dist/orchestrators/snapshot/service.mjs +53 -56
  210. package/dist/orchestrators/snapshot/service.mjs.map +1 -1
  211. package/dist/orchestrators/snapshot/state-document.d.mts +1 -0
  212. package/dist/orchestrators/snapshot/state-document.mjs +47 -7
  213. package/dist/orchestrators/snapshot/state-document.mjs.map +1 -1
  214. package/dist/orchestrators/snapshot/wipe.mjs +67 -10
  215. package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
  216. package/dist/plugins/account/codegen.mjs +9 -8
  217. package/dist/plugins/account/codegen.mjs.map +1 -1
  218. package/dist/plugins/account/errors.d.mts +33 -6
  219. package/dist/plugins/account/errors.mjs.map +1 -1
  220. package/dist/plugins/account/funding.d.mts +29 -19
  221. package/dist/plugins/account/funding.mjs +84 -66
  222. package/dist/plugins/account/funding.mjs.map +1 -1
  223. package/dist/plugins/account/index.d.mts +2 -4
  224. package/dist/plugins/account/index.mjs +64 -31
  225. package/dist/plugins/account/index.mjs.map +1 -1
  226. package/dist/plugins/account/keypair.mjs +6 -9
  227. package/dist/plugins/account/keypair.mjs.map +1 -1
  228. package/dist/plugins/account/lease.mjs +1 -0
  229. package/dist/plugins/account/lease.mjs.map +1 -1
  230. package/dist/plugins/account/registry.d.mts +14 -1
  231. package/dist/plugins/account/registry.mjs +18 -18
  232. package/dist/plugins/account/registry.mjs.map +1 -1
  233. package/dist/plugins/account/service.d.mts +23 -4
  234. package/dist/plugins/account/service.mjs +83 -45
  235. package/dist/plugins/account/service.mjs.map +1 -1
  236. package/dist/plugins/account/snapshot.mjs +2 -5
  237. package/dist/plugins/account/snapshot.mjs.map +1 -1
  238. package/dist/plugins/account/spans.mjs +16 -0
  239. package/dist/plugins/account/spans.mjs.map +1 -0
  240. package/dist/plugins/account/variants/keystore.mjs +6 -8
  241. package/dist/plugins/account/variants/keystore.mjs.map +1 -1
  242. package/dist/plugins/action/build-context.d.mts +3 -2
  243. package/dist/plugins/action/errors.d.mts +16 -12
  244. package/dist/plugins/action/errors.mjs.map +1 -1
  245. package/dist/plugins/action/execute.mjs +65 -103
  246. package/dist/plugins/action/execute.mjs.map +1 -1
  247. package/dist/plugins/action/index.d.mts +0 -2
  248. package/dist/plugins/action/index.mjs +17 -13
  249. package/dist/plugins/action/index.mjs.map +1 -1
  250. package/dist/plugins/action/service.mjs +38 -35
  251. package/dist/plugins/action/service.mjs.map +1 -1
  252. package/dist/plugins/action/spans.mjs +11 -0
  253. package/dist/plugins/action/spans.mjs.map +1 -0
  254. package/dist/plugins/coin/codegen.mjs +9 -8
  255. package/dist/plugins/coin/codegen.mjs.map +1 -1
  256. package/dist/plugins/coin/discovery.mjs +8 -4
  257. package/dist/plugins/coin/discovery.mjs.map +1 -1
  258. package/dist/plugins/coin/index.d.mts +11 -29
  259. package/dist/plugins/coin/index.mjs +56 -27
  260. package/dist/plugins/coin/index.mjs.map +1 -1
  261. package/dist/plugins/coin/metadata.mjs +8 -15
  262. package/dist/plugins/coin/metadata.mjs.map +1 -1
  263. package/dist/plugins/coin/mint.d.mts +30 -11
  264. package/dist/plugins/coin/mint.mjs +78 -74
  265. package/dist/plugins/coin/mint.mjs.map +1 -1
  266. package/dist/plugins/coin/registry.mjs +4 -7
  267. package/dist/plugins/coin/registry.mjs.map +1 -1
  268. package/dist/plugins/coin/service.d.mts +39 -1
  269. package/dist/plugins/coin/service.mjs +11 -4
  270. package/dist/plugins/coin/service.mjs.map +1 -1
  271. package/dist/plugins/coin/spans.mjs +20 -0
  272. package/dist/plugins/coin/spans.mjs.map +1 -0
  273. package/dist/plugins/dashboard/domain.mjs +402 -0
  274. package/dist/plugins/dashboard/domain.mjs.map +1 -0
  275. package/dist/plugins/dashboard/index.d.mts +29 -0
  276. package/dist/plugins/dashboard/index.mjs +91 -0
  277. package/dist/plugins/dashboard/index.mjs.map +1 -0
  278. package/dist/plugins/dashboard/origin-policy.mjs +36 -0
  279. package/dist/plugins/dashboard/origin-policy.mjs.map +1 -0
  280. package/dist/plugins/dashboard/routable.mjs +27 -0
  281. package/dist/plugins/dashboard/routable.mjs.map +1 -0
  282. package/dist/plugins/dashboard/schema/builder.mjs +21 -0
  283. package/dist/plugins/dashboard/schema/builder.mjs.map +1 -0
  284. package/dist/plugins/dashboard/schema/enums.mjs +72 -0
  285. package/dist/plugins/dashboard/schema/enums.mjs.map +1 -0
  286. package/dist/plugins/dashboard/schema/root.mjs +351 -0
  287. package/dist/plugins/dashboard/schema/root.mjs.map +1 -0
  288. package/dist/plugins/dashboard/schema/types.mjs +508 -0
  289. package/dist/plugins/dashboard/schema/types.mjs.map +1 -0
  290. package/dist/plugins/dashboard/schema.mjs +11 -0
  291. package/dist/plugins/dashboard/schema.mjs.map +1 -0
  292. package/dist/plugins/dashboard/server.mjs +166 -0
  293. package/dist/plugins/dashboard/server.mjs.map +1 -0
  294. package/dist/plugins/deepbook/deploy.mjs +53 -115
  295. package/dist/plugins/deepbook/deploy.mjs.map +1 -1
  296. package/dist/plugins/deepbook/errors.mjs +1 -0
  297. package/dist/plugins/deepbook/errors.mjs.map +1 -1
  298. package/dist/plugins/deepbook/faucet-strategy.mjs +47 -34
  299. package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
  300. package/dist/plugins/deepbook/hash.mjs +9 -0
  301. package/dist/plugins/deepbook/hash.mjs.map +1 -0
  302. package/dist/plugins/deepbook/index.d.mts +1 -10
  303. package/dist/plugins/deepbook/index.mjs +25 -22
  304. package/dist/plugins/deepbook/index.mjs.map +1 -1
  305. package/dist/plugins/deepbook/pyth/index.mjs +26 -55
  306. package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
  307. package/dist/plugins/deepbook/snapshot.mjs +17 -29
  308. package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
  309. package/dist/plugins/deepbook/spans.mjs +18 -0
  310. package/dist/plugins/deepbook/spans.mjs.map +1 -0
  311. package/dist/plugins/deepbook/type-strings.mjs +65 -0
  312. package/dist/plugins/deepbook/type-strings.mjs.map +1 -0
  313. package/dist/plugins/deepbook/types.d.mts +2 -3
  314. package/dist/plugins/deepbook/types.mjs.map +1 -1
  315. package/dist/plugins/faucet/dispatcher.d.mts +21 -2
  316. package/dist/plugins/faucet/dispatcher.mjs +1 -2
  317. package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
  318. package/dist/plugins/faucet/errors.d.mts +11 -24
  319. package/dist/plugins/faucet/errors.mjs.map +1 -1
  320. package/dist/plugins/faucet/http.mjs +33 -14
  321. package/dist/plugins/faucet/http.mjs.map +1 -1
  322. package/dist/plugins/faucet/index.d.mts +15 -5
  323. package/dist/plugins/faucet/index.mjs +3 -0
  324. package/dist/plugins/faucet/index.mjs.map +1 -1
  325. package/dist/plugins/faucet/spans.mjs +12 -0
  326. package/dist/plugins/faucet/spans.mjs.map +1 -0
  327. package/dist/plugins/host-service/errors.d.mts +1 -1
  328. package/dist/plugins/host-service/index.mjs +34 -8
  329. package/dist/plugins/host-service/index.mjs.map +1 -1
  330. package/dist/plugins/host-service/routable.mjs +2 -1
  331. package/dist/plugins/host-service/routable.mjs.map +1 -1
  332. package/dist/plugins/host-service/service.d.mts +21 -0
  333. package/dist/plugins/host-service/service.mjs +136 -81
  334. package/dist/plugins/host-service/service.mjs.map +1 -1
  335. package/dist/plugins/internal/acquire-on-chain-artifact.mjs +23 -0
  336. package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -0
  337. package/dist/plugins/internal/codegen-helpers.mjs +18 -0
  338. package/dist/plugins/internal/codegen-helpers.mjs.map +1 -0
  339. package/dist/plugins/internal/funding-failure-error.mjs +32 -0
  340. package/dist/plugins/internal/funding-failure-error.mjs.map +1 -0
  341. package/dist/plugins/package/build.mjs +1 -1
  342. package/dist/plugins/package/build.mjs.map +1 -1
  343. package/dist/plugins/package/codegen.mjs +26 -0
  344. package/dist/plugins/package/codegen.mjs.map +1 -1
  345. package/dist/plugins/package/errors.d.mts +16 -6
  346. package/dist/plugins/package/errors.mjs.map +1 -1
  347. package/dist/plugins/package/index.d.mts +0 -4
  348. package/dist/plugins/package/index.mjs +27 -21
  349. package/dist/plugins/package/index.mjs.map +1 -1
  350. package/dist/plugins/package/mode-known.mjs +26 -12
  351. package/dist/plugins/package/mode-known.mjs.map +1 -1
  352. package/dist/plugins/package/mode-local.mjs +47 -41
  353. package/dist/plugins/package/mode-local.mjs.map +1 -1
  354. package/dist/plugins/package/publish-executor.mjs +55 -114
  355. package/dist/plugins/package/publish-executor.mjs.map +1 -1
  356. package/dist/plugins/package/registry.mjs +2 -1
  357. package/dist/plugins/package/registry.mjs.map +1 -1
  358. package/dist/plugins/package/service.mjs.map +1 -1
  359. package/dist/plugins/package/spans.mjs +14 -0
  360. package/dist/plugins/package/spans.mjs.map +1 -0
  361. package/dist/plugins/postgres/db-ensure.mjs +19 -12
  362. package/dist/plugins/postgres/db-ensure.mjs.map +1 -1
  363. package/dist/plugins/postgres/errors.d.mts +5 -0
  364. package/dist/plugins/postgres/errors.mjs.map +1 -1
  365. package/dist/plugins/postgres/index.mjs +10 -17
  366. package/dist/plugins/postgres/index.mjs.map +1 -1
  367. package/dist/plugins/postgres/service.mjs +58 -24
  368. package/dist/plugins/postgres/service.mjs.map +1 -1
  369. package/dist/plugins/postgres/snapshot.mjs.map +1 -1
  370. package/dist/plugins/postgres/spans.mjs +11 -0
  371. package/dist/plugins/postgres/spans.mjs.map +1 -0
  372. package/dist/plugins/router-entrypoints.mjs +4 -4
  373. package/dist/plugins/router-entrypoints.mjs.map +1 -1
  374. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +15 -13
  375. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
  376. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +15 -19
  377. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
  378. package/dist/plugins/seal/codegen.d.mts +2 -3
  379. package/dist/plugins/seal/codegen.mjs.map +1 -1
  380. package/dist/plugins/seal/config-render.mjs +35 -8
  381. package/dist/plugins/seal/config-render.mjs.map +1 -1
  382. package/dist/plugins/seal/deploy.mjs +31 -39
  383. package/dist/plugins/seal/deploy.mjs.map +1 -1
  384. package/dist/plugins/seal/errors.d.mts +1 -3
  385. package/dist/plugins/seal/errors.mjs +7 -1
  386. package/dist/plugins/seal/errors.mjs.map +1 -1
  387. package/dist/plugins/seal/index.d.mts +1 -6
  388. package/dist/plugins/seal/index.mjs +40 -14
  389. package/dist/plugins/seal/index.mjs.map +1 -1
  390. package/dist/plugins/seal/key-manager.d.mts +1 -9
  391. package/dist/plugins/seal/key-manager.mjs +2 -18
  392. package/dist/plugins/seal/key-manager.mjs.map +1 -1
  393. package/dist/plugins/seal/key-server.mjs +37 -36
  394. package/dist/plugins/seal/key-server.mjs.map +1 -1
  395. package/dist/plugins/seal/keygen.mjs +16 -17
  396. package/dist/plugins/seal/keygen.mjs.map +1 -1
  397. package/dist/plugins/seal/mode/fork-known.mjs +36 -14
  398. package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
  399. package/dist/plugins/seal/mode/live.d.mts +6 -4
  400. package/dist/plugins/seal/mode/live.mjs +14 -21
  401. package/dist/plugins/seal/mode/live.mjs.map +1 -1
  402. package/dist/plugins/seal/mode/local-keygen.mjs +57 -21
  403. package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
  404. package/dist/plugins/seal/registry-publish.mjs +1 -0
  405. package/dist/plugins/seal/registry-publish.mjs.map +1 -1
  406. package/dist/plugins/seal/routable.mjs.map +1 -1
  407. package/dist/plugins/seal/service.mjs.map +1 -1
  408. package/dist/plugins/seal/spans.mjs +18 -0
  409. package/dist/plugins/seal/spans.mjs.map +1 -0
  410. package/dist/plugins/sui/auto-tick.mjs +17 -23
  411. package/dist/plugins/sui/auto-tick.mjs.map +1 -1
  412. package/dist/plugins/sui/chain-build-container.mjs +18 -0
  413. package/dist/plugins/sui/chain-build-container.mjs.map +1 -0
  414. package/dist/plugins/sui/chain-probe.d.mts +5 -5
  415. package/dist/plugins/sui/chain-probe.mjs +28 -58
  416. package/dist/plugins/sui/chain-probe.mjs.map +1 -1
  417. package/dist/plugins/sui/codegen.mjs +26 -0
  418. package/dist/plugins/sui/codegen.mjs.map +1 -1
  419. package/dist/plugins/sui/errors.d.mts +2 -29
  420. package/dist/plugins/sui/errors.mjs +1 -3
  421. package/dist/plugins/sui/errors.mjs.map +1 -1
  422. package/dist/plugins/sui/fork-faucet-strategy.d.mts +1 -0
  423. package/dist/plugins/sui/fork-faucet-strategy.mjs +55 -0
  424. package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -0
  425. package/dist/plugins/sui/fork-orchestration.d.mts +1 -0
  426. package/dist/plugins/sui/fork-orchestration.mjs +170 -2
  427. package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
  428. package/dist/plugins/sui/fork-transaction.d.mts +2 -0
  429. package/dist/plugins/sui/fork-transaction.mjs +92 -28
  430. package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
  431. package/dist/plugins/sui/index.d.mts +33 -70
  432. package/dist/plugins/sui/index.mjs +58 -35
  433. package/dist/plugins/sui/index.mjs.map +1 -1
  434. package/dist/plugins/{faucet/strategies/sui-local.mjs → sui/local-faucet-strategy.mjs} +10 -7
  435. package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -0
  436. package/dist/plugins/sui/mode/external.mjs +7 -13
  437. package/dist/plugins/sui/mode/external.mjs.map +1 -1
  438. package/dist/plugins/sui/mode/fork.mjs +136 -59
  439. package/dist/plugins/sui/mode/fork.mjs.map +1 -1
  440. package/dist/plugins/sui/mode/live.mjs +10 -16
  441. package/dist/plugins/sui/mode/live.mjs.map +1 -1
  442. package/dist/plugins/sui/mode/local.mjs +37 -36
  443. package/dist/plugins/sui/mode/local.mjs.map +1 -1
  444. package/dist/plugins/sui/mode/shared-boot.mjs +38 -115
  445. package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
  446. package/dist/plugins/sui/mode/shared.d.mts +1 -5
  447. package/dist/plugins/sui/mode/shared.mjs.map +1 -1
  448. package/dist/plugins/sui/mode/spec.d.mts +18 -0
  449. package/dist/plugins/sui/move-summary-runner.mjs +207 -0
  450. package/dist/plugins/sui/move-summary-runner.mjs.map +1 -0
  451. package/dist/plugins/sui/network-resolver.d.mts +4 -3
  452. package/dist/plugins/sui/service.mjs.map +1 -1
  453. package/dist/plugins/sui/snapshot.mjs +3 -0
  454. package/dist/plugins/sui/snapshot.mjs.map +1 -1
  455. package/dist/plugins/sui/spans.mjs +17 -0
  456. package/dist/plugins/sui/spans.mjs.map +1 -0
  457. package/dist/plugins/wallet/codegen.d.mts +0 -1
  458. package/dist/plugins/wallet/codegen.mjs +8 -9
  459. package/dist/plugins/wallet/codegen.mjs.map +1 -1
  460. package/dist/plugins/wallet/index.d.mts +6 -8
  461. package/dist/plugins/wallet/index.mjs +46 -35
  462. package/dist/plugins/wallet/index.mjs.map +1 -1
  463. package/dist/plugins/wallet/origin-policy.mjs +12 -28
  464. package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
  465. package/dist/plugins/wallet/pairing.mjs +6 -4
  466. package/dist/plugins/wallet/pairing.mjs.map +1 -1
  467. package/dist/plugins/wallet/protocol.d.mts +1 -0
  468. package/dist/plugins/wallet/protocol.mjs +5 -44
  469. package/dist/plugins/wallet/protocol.mjs.map +1 -1
  470. package/dist/plugins/wallet/routable.mjs +2 -8
  471. package/dist/plugins/wallet/routable.mjs.map +1 -1
  472. package/dist/plugins/wallet/server.mjs +36 -44
  473. package/dist/plugins/wallet/server.mjs.map +1 -1
  474. package/dist/plugins/wallet/service.d.mts +1 -5
  475. package/dist/plugins/wallet/service.mjs +22 -18
  476. package/dist/plugins/wallet/service.mjs.map +1 -1
  477. package/dist/plugins/wallet/spans.mjs +22 -0
  478. package/dist/plugins/wallet/spans.mjs.map +1 -0
  479. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +33 -8
  480. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
  481. package/dist/plugins/walrus/codegen.d.mts +3 -3
  482. package/dist/plugins/walrus/codegen.mjs.map +1 -1
  483. package/dist/plugins/walrus/deploy-paths.mjs +21 -5
  484. package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
  485. package/dist/plugins/walrus/deploy.mjs +67 -69
  486. package/dist/plugins/walrus/deploy.mjs.map +1 -1
  487. package/dist/plugins/walrus/errors.mjs +1 -0
  488. package/dist/plugins/walrus/errors.mjs.map +1 -1
  489. package/dist/plugins/walrus/faucet-strategy.mjs +12 -7
  490. package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
  491. package/dist/plugins/walrus/index.d.mts +2 -13
  492. package/dist/plugins/walrus/index.mjs +18 -23
  493. package/dist/plugins/walrus/index.mjs.map +1 -1
  494. package/dist/plugins/walrus/mode/known-deploy.mjs +7 -5
  495. package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
  496. package/dist/plugins/walrus/mode/local-cluster.mjs +26 -7
  497. package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
  498. package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
  499. package/dist/plugins/walrus/routable.mjs +7 -23
  500. package/dist/plugins/walrus/routable.mjs.map +1 -1
  501. package/dist/plugins/walrus/service.mjs.map +1 -1
  502. package/dist/plugins/walrus/spans.mjs +18 -0
  503. package/dist/plugins/walrus/spans.mjs.map +1 -0
  504. package/dist/plugins/walrus/storage-nodes.d.mts +6 -2
  505. package/dist/plugins/walrus/storage-nodes.mjs +15 -8
  506. package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
  507. package/dist/plugins/walrus/wal-swap.mjs +58 -15
  508. package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
  509. package/dist/primitives/artifact-publisher.d.mts +1 -1
  510. package/dist/primitives/artifact-publisher.mjs +15 -0
  511. package/dist/primitives/artifact-publisher.mjs.map +1 -0
  512. package/dist/runtime/docker/client.mjs +17 -4
  513. package/dist/runtime/docker/client.mjs.map +1 -1
  514. package/dist/runtime/docker/container.mjs +219 -132
  515. package/dist/runtime/docker/container.mjs.map +1 -1
  516. package/dist/runtime/docker/errors.mjs +0 -6
  517. package/dist/runtime/docker/errors.mjs.map +1 -1
  518. package/dist/runtime/docker/exec.mjs +21 -18
  519. package/dist/runtime/docker/exec.mjs.map +1 -1
  520. package/dist/runtime/docker/image.mjs +36 -13
  521. package/dist/runtime/docker/image.mjs.map +1 -1
  522. package/dist/runtime/docker/index.mjs +14 -0
  523. package/dist/runtime/docker/inspect-and-decode.mjs +51 -0
  524. package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -0
  525. package/dist/runtime/docker/inventory.mjs +14 -11
  526. package/dist/runtime/docker/inventory.mjs.map +1 -1
  527. package/dist/runtime/docker/labels.mjs +38 -5
  528. package/dist/runtime/docker/labels.mjs.map +1 -1
  529. package/dist/runtime/docker/logs.d.mts +1 -1
  530. package/dist/runtime/docker/logs.mjs.map +1 -1
  531. package/dist/runtime/docker/network.mjs +83 -47
  532. package/dist/runtime/docker/network.mjs.map +1 -1
  533. package/dist/runtime/docker/render-run-args.mjs +82 -0
  534. package/dist/runtime/docker/render-run-args.mjs.map +1 -0
  535. package/dist/runtime/docker/service.d.mts +1 -1
  536. package/dist/runtime/docker/service.mjs +12 -7
  537. package/dist/runtime/docker/service.mjs.map +1 -1
  538. package/dist/runtime/docker/sweep.mjs +118 -43
  539. package/dist/runtime/docker/sweep.mjs.map +1 -1
  540. package/dist/runtime/docker/volume.mjs +2 -0
  541. package/dist/runtime/docker/volume.mjs.map +1 -1
  542. package/dist/runtime/docker/wrap.mjs +30 -1
  543. package/dist/runtime/docker/wrap.mjs.map +1 -1
  544. package/dist/substrate/cross-process.mjs +14 -8
  545. package/dist/substrate/cross-process.mjs.map +1 -1
  546. package/dist/substrate/event-time.mjs +33 -0
  547. package/dist/substrate/event-time.mjs.map +1 -0
  548. package/dist/substrate/events.d.mts +28 -8
  549. package/dist/substrate/identity.d.mts +2 -1
  550. package/dist/substrate/manifest.d.mts +68 -5
  551. package/dist/substrate/manifest.mjs +34 -5
  552. package/dist/substrate/manifest.mjs.map +1 -1
  553. package/dist/substrate/network.d.mts +3 -5
  554. package/dist/substrate/options.d.mts +0 -2
  555. package/dist/substrate/plugin.d.mts +13 -0
  556. package/dist/substrate/plugin.mjs +4 -2
  557. package/dist/substrate/plugin.mjs.map +1 -1
  558. package/dist/substrate/projection.d.mts +35 -3
  559. package/dist/substrate/runtime/artifact-publisher/index.mjs +19 -9
  560. package/dist/substrate/runtime/artifact-publisher/index.mjs.map +1 -1
  561. package/dist/substrate/runtime/atomic-write.mjs +97 -32
  562. package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
  563. package/dist/substrate/runtime/cache/index.mjs +3 -0
  564. package/dist/substrate/runtime/cache/schema.mjs +2 -2
  565. package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
  566. package/dist/substrate/runtime/cache/service.mjs +9 -7
  567. package/dist/substrate/runtime/cache/service.mjs.map +1 -1
  568. package/dist/substrate/runtime/capability-sinks/index.d.mts +1 -1
  569. package/dist/substrate/runtime/capability-sinks/index.mjs +3 -0
  570. package/dist/substrate/runtime/capability-sinks/layer.mjs +1 -1
  571. package/dist/substrate/runtime/capability-sinks/service.d.mts +61 -2
  572. package/dist/substrate/runtime/capability-sinks/service.mjs +21 -26
  573. package/dist/substrate/runtime/capability-sinks/service.mjs.map +1 -1
  574. package/dist/substrate/runtime/config-validation.d.mts +1 -13
  575. package/dist/substrate/runtime/config-validation.mjs +2 -91
  576. package/dist/substrate/runtime/config-validation.mjs.map +1 -1
  577. package/dist/substrate/runtime/container-runtime.d.mts +1 -0
  578. package/dist/substrate/runtime/container-runtime.mjs +2 -0
  579. package/dist/substrate/runtime/control-plane/domain.mjs +101 -0
  580. package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -0
  581. package/dist/substrate/runtime/control-plane/service.mjs +7 -0
  582. package/dist/substrate/runtime/control-plane/service.mjs.map +1 -0
  583. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +46 -19
  584. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
  585. package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs +30 -12
  586. package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
  587. package/dist/substrate/runtime/cross-process/command-channel/index.mjs +5 -0
  588. package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs +11 -2
  589. package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs.map +1 -1
  590. package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs +1 -1
  591. package/dist/substrate/runtime/cross-process/index.mjs +11 -0
  592. package/dist/substrate/runtime/cross-process/live-clock.mjs +34 -0
  593. package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -0
  594. package/dist/substrate/runtime/cross-process/liveness.mjs +52 -10
  595. package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
  596. package/dist/substrate/runtime/cross-process/lock.d.mts +1 -0
  597. package/dist/substrate/runtime/cross-process/lock.mjs +23 -0
  598. package/dist/substrate/runtime/cross-process/lock.mjs.map +1 -0
  599. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +64 -0
  600. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -0
  601. package/dist/substrate/runtime/cross-process/roster.mjs +97 -83
  602. package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
  603. package/dist/substrate/runtime/cross-process/self-pid.mjs +8 -0
  604. package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -0
  605. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +24 -28
  606. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +1 -1
  607. package/dist/substrate/runtime/cross-process/stack-lock.mjs +41 -20
  608. package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
  609. package/dist/substrate/runtime/current-plugin.mjs +2 -2
  610. package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
  611. package/dist/substrate/runtime/errors.mjs +12 -6
  612. package/dist/substrate/runtime/errors.mjs.map +1 -1
  613. package/dist/substrate/runtime/format-unknown-error.mjs +30 -0
  614. package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -0
  615. package/dist/substrate/runtime/host-bind-mount-owner.mjs +10 -0
  616. package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -0
  617. package/dist/substrate/runtime/host-gateway.mjs +20 -0
  618. package/dist/substrate/runtime/host-gateway.mjs.map +1 -0
  619. package/dist/substrate/runtime/host-tree-tar/index.mjs +61 -50
  620. package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
  621. package/dist/substrate/runtime/http-probe.d.mts +1 -1
  622. package/dist/substrate/runtime/index.mjs +14 -0
  623. package/dist/substrate/runtime/lease-broker/index.d.mts +1 -1
  624. package/dist/substrate/runtime/lease-broker/index.mjs +2 -0
  625. package/dist/substrate/runtime/lease-broker/service.d.mts +1 -64
  626. package/dist/substrate/runtime/lease-broker/service.mjs +1 -1
  627. package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
  628. package/dist/substrate/runtime/lifecycle/dep-graph.mjs +22 -3
  629. package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
  630. package/dist/substrate/runtime/lifecycle/index.mjs +9 -0
  631. package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +1 -1
  632. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +20 -3
  633. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
  634. package/dist/substrate/runtime/lifecycle/selective-restart.mjs +1 -1
  635. package/dist/substrate/runtime/lifecycle/signals.mjs +5 -5
  636. package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
  637. package/dist/substrate/runtime/lifecycle/state-machine.mjs +1 -1
  638. package/dist/substrate/runtime/managed-container.d.mts +19 -1
  639. package/dist/substrate/runtime/managed-container.mjs +20 -1
  640. package/dist/substrate/runtime/managed-container.mjs.map +1 -1
  641. package/dist/substrate/runtime/manifest/index.mjs +3 -0
  642. package/dist/substrate/runtime/manifest/manifest.mjs +15 -11
  643. package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
  644. package/dist/substrate/runtime/mode-errors.mjs +20 -0
  645. package/dist/substrate/runtime/mode-errors.mjs.map +1 -0
  646. package/dist/substrate/runtime/observability/cascade-formatter.mjs +9 -13
  647. package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
  648. package/dist/substrate/runtime/observability/formatter-registry.mjs +19 -36
  649. package/dist/substrate/runtime/observability/formatter-registry.mjs.map +1 -1
  650. package/dist/substrate/runtime/observability/ignore-with-log.d.mts +1 -0
  651. package/dist/substrate/runtime/observability/ignore-with-log.mjs +17 -0
  652. package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -0
  653. package/dist/substrate/runtime/observability/index.d.mts +1 -1
  654. package/dist/substrate/runtime/observability/index.mjs +13 -0
  655. package/dist/substrate/runtime/observability/log-store.d.mts +1 -0
  656. package/dist/substrate/runtime/observability/log-store.mjs +175 -0
  657. package/dist/substrate/runtime/observability/log-store.mjs.map +1 -0
  658. package/dist/substrate/runtime/observability/logger.d.mts +2 -1
  659. package/dist/substrate/runtime/observability/logger.mjs +5 -9
  660. package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
  661. package/dist/substrate/runtime/observability/output-truncate.mjs +35 -0
  662. package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -0
  663. package/dist/substrate/runtime/observability/pretty-error.mjs +9 -12
  664. package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
  665. package/dist/substrate/runtime/observability/redaction.d.mts +2 -13
  666. package/dist/substrate/runtime/observability/redaction.mjs +8 -14
  667. package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
  668. package/dist/substrate/runtime/observability/span-store.d.mts +1 -0
  669. package/dist/substrate/runtime/observability/span-store.mjs +110 -0
  670. package/dist/substrate/runtime/observability/span-store.mjs.map +1 -0
  671. package/dist/substrate/runtime/observability/spans.mjs +14 -14
  672. package/dist/substrate/runtime/observability/spans.mjs.map +1 -1
  673. package/dist/substrate/runtime/observability/subprocess-capture.mjs +1 -1
  674. package/dist/substrate/runtime/passthrough-or-wrap.mjs +33 -0
  675. package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -0
  676. package/dist/substrate/runtime/paths.d.mts +1 -1
  677. package/dist/substrate/runtime/paths.mjs +4 -3
  678. package/dist/substrate/runtime/paths.mjs.map +1 -1
  679. package/dist/substrate/runtime/phase-preserving-produce.mjs +40 -0
  680. package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -0
  681. package/dist/substrate/runtime/port-broker/index.mjs +3 -0
  682. package/dist/substrate/runtime/port-broker/service.mjs +170 -126
  683. package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
  684. package/dist/substrate/runtime/post-acquire-tasks.mjs +1 -1
  685. package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
  686. package/dist/substrate/runtime/probes.d.mts +7 -5
  687. package/dist/substrate/runtime/probes.mjs +3 -1
  688. package/dist/substrate/runtime/probes.mjs.map +1 -1
  689. package/dist/substrate/runtime/projection/index.mjs +4 -0
  690. package/dist/substrate/runtime/projection/operational-endpoints.mjs +1 -2
  691. package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
  692. package/dist/substrate/runtime/projection/persisted.mjs +33 -15
  693. package/dist/substrate/runtime/projection/persisted.mjs.map +1 -1
  694. package/dist/substrate/runtime/projection/state-ref.mjs +26 -2
  695. package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
  696. package/dist/substrate/runtime/projection/update.mjs +88 -24
  697. package/dist/substrate/runtime/projection/update.mjs.map +1 -1
  698. package/dist/substrate/runtime/random-suffix.mjs +11 -0
  699. package/dist/substrate/runtime/random-suffix.mjs.map +1 -0
  700. package/dist/substrate/runtime/retry-policy.d.mts +59 -1
  701. package/dist/substrate/runtime/retry-policy.mjs +66 -1
  702. package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
  703. package/dist/substrate/runtime/routed-url.d.mts +1 -0
  704. package/dist/substrate/runtime/routed-url.mjs +79 -0
  705. package/dist/substrate/runtime/routed-url.mjs.map +1 -0
  706. package/dist/substrate/runtime/runtime-decode.d.mts +1 -1
  707. package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
  708. package/dist/substrate/runtime/scoped-http-server.mjs +2 -3
  709. package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
  710. package/dist/substrate/runtime/scoped-multimap/index.mjs +2 -0
  711. package/dist/substrate/runtime/scoped-multimap/service.mjs +52 -0
  712. package/dist/substrate/runtime/scoped-multimap/service.mjs.map +1 -0
  713. package/dist/substrate/runtime/scoped-ref-map/index.mjs +2 -0
  714. package/dist/substrate/runtime/scoped-ref-map/service.mjs +2 -2
  715. package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +1 -1
  716. package/dist/substrate/runtime/stage-and-swap/index.mjs +72 -4
  717. package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
  718. package/dist/substrate/runtime/state-store/index.d.mts +1 -0
  719. package/dist/substrate/runtime/state-store/index.mjs +3 -0
  720. package/dist/substrate/runtime/state-store/schema.d.mts +1 -0
  721. package/dist/substrate/runtime/state-store/schema.mjs +19 -14
  722. package/dist/substrate/runtime/state-store/schema.mjs.map +1 -1
  723. package/dist/substrate/runtime/state-store/service.d.mts +1 -0
  724. package/dist/substrate/runtime/state-store/service.mjs +145 -0
  725. package/dist/substrate/runtime/state-store/service.mjs.map +1 -0
  726. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.d.mts +1 -0
  727. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs +33 -0
  728. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -0
  729. package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs +1 -1
  730. package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
  731. package/dist/substrate/runtime/strategy-registry/index.mjs +4 -0
  732. package/dist/substrate/runtime/strategy-registry/service.mjs +19 -33
  733. package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
  734. package/dist/substrate/runtime/subnet-broker.mjs +60 -0
  735. package/dist/substrate/runtime/subnet-broker.mjs.map +1 -0
  736. package/dist/substrate/runtime/sui-execute/index.d.mts +21 -1
  737. package/dist/substrate/runtime/sui-execute/index.mjs +84 -20
  738. package/dist/substrate/runtime/sui-execute/index.mjs.map +1 -1
  739. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.d.mts +1 -0
  740. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs +32 -0
  741. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +1 -0
  742. package/dist/substrate/runtime/sui-ledger/object-ref.mjs +32 -0
  743. package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +1 -0
  744. package/dist/substrate/runtime/sui-move-build/index.mjs +93 -19
  745. package/dist/substrate/runtime/sui-move-build/index.mjs.map +1 -1
  746. package/dist/substrate/runtime/supervisor/acquire-node.mjs +181 -0
  747. package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -0
  748. package/dist/substrate/runtime/supervisor/background-tasks.mjs +144 -0
  749. package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -0
  750. package/dist/substrate/runtime/supervisor/command-loop.mjs +68 -0
  751. package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -0
  752. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +151 -0
  753. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +1 -0
  754. package/dist/substrate/runtime/supervisor/errors.d.mts +1 -0
  755. package/dist/substrate/runtime/supervisor/errors.mjs +9 -0
  756. package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -0
  757. package/dist/substrate/runtime/supervisor/index.d.mts +1 -0
  758. package/dist/substrate/runtime/supervisor/index.mjs +3 -0
  759. package/dist/substrate/runtime/supervisor/shutdown.mjs +51 -0
  760. package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -0
  761. package/dist/substrate/runtime/supervisor/start-supervisor.d.mts +1 -0
  762. package/dist/substrate/runtime/supervisor/start-supervisor.mjs +250 -0
  763. package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -0
  764. package/dist/substrate/runtime/supervisor/state.d.mts +1 -0
  765. package/dist/substrate/runtime/supervisor/state.mjs +22 -0
  766. package/dist/substrate/runtime/supervisor/state.mjs.map +1 -0
  767. package/dist/substrate/runtime/supervisor/teardown.mjs +57 -0
  768. package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -0
  769. package/dist/substrate/runtime/supervisor/types.d.mts +1 -0
  770. package/dist/substrate/runtime/supervisor/wiring.mjs +133 -0
  771. package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -0
  772. package/dist/substrate/runtime/typed-env.mjs +8 -0
  773. package/dist/substrate/runtime/typed-env.mjs.map +1 -0
  774. package/dist/substrate/state-store.d.mts +1 -0
  775. package/dist/substrate/versioned-doc-schema.mjs +17 -0
  776. package/dist/substrate/versioned-doc-schema.mjs.map +1 -0
  777. package/dist/substrate/versioned-doc-sync.mjs +94 -0
  778. package/dist/substrate/versioned-doc-sync.mjs.map +1 -0
  779. package/dist/surfaces/cli/command-tree.mjs.map +1 -1
  780. package/dist/surfaces/cli/commands/index.mjs +9 -0
  781. package/dist/surfaces/cli/commands/prune-picker-entry.mjs +13 -0
  782. package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -0
  783. package/dist/surfaces/cli/commands/prune-picker.mjs +3 -2
  784. package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
  785. package/dist/surfaces/cli/commands/prune.mjs +34 -17
  786. package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
  787. package/dist/surfaces/cli/commands/status.mjs +2 -2
  788. package/dist/surfaces/cli/commands/status.mjs.map +1 -1
  789. package/dist/surfaces/cli/commands/supervisor-presence.mjs +5 -3
  790. package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
  791. package/dist/surfaces/cli/commands/wipe.mjs +38 -4
  792. package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
  793. package/dist/surfaces/cli/errors.mjs +1 -1
  794. package/dist/surfaces/cli/errors.mjs.map +1 -1
  795. package/dist/surfaces/cli/flags.mjs +0 -1
  796. package/dist/surfaces/cli/flags.mjs.map +1 -1
  797. package/dist/surfaces/cli/index.mjs +98 -18
  798. package/dist/surfaces/cli/index.mjs.map +1 -1
  799. package/dist/surfaces/cli/output.mjs +1 -0
  800. package/dist/surfaces/cli/output.mjs.map +1 -1
  801. package/dist/surfaces/cli/sysexits.mjs +2 -1
  802. package/dist/surfaces/cli/sysexits.mjs.map +1 -1
  803. package/dist/surfaces/tui/app.mjs +25 -3
  804. package/dist/surfaces/tui/app.mjs.map +1 -1
  805. package/dist/surfaces/tui/display-derivation.mjs +23 -74
  806. package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
  807. package/dist/surfaces/tui/event-log.mjs +35 -23
  808. package/dist/surfaces/tui/event-log.mjs.map +1 -1
  809. package/dist/surfaces/tui/input.mjs +5 -1
  810. package/dist/surfaces/tui/input.mjs.map +1 -1
  811. package/dist/surfaces/tui/plain-renderer.mjs +70 -34
  812. package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
  813. package/package.json +24 -4
  814. package/dist/contracts/liveness-classifier.d.mts +0 -19
  815. package/dist/contracts/network-resolver.d.mts +0 -15
  816. package/dist/contracts/network-resolver.mjs +0 -7
  817. package/dist/contracts/network-resolver.mjs.map +0 -1
  818. package/dist/orchestrators/codegen/extras.mjs +0 -16
  819. package/dist/orchestrators/codegen/extras.mjs.map +0 -1
  820. package/dist/plugins/deepbook/routable.mjs +0 -21
  821. package/dist/plugins/deepbook/routable.mjs.map +0 -1
  822. package/dist/plugins/faucet/service.d.mts +0 -20
  823. package/dist/plugins/faucet/strategies/sui-local.d.mts +0 -23
  824. package/dist/plugins/faucet/strategies/sui-local.mjs.map +0 -1
  825. package/dist/plugins/sui/seed-objects.d.mts +0 -18
  826. package/dist/plugins/sui/seed-objects.mjs +0 -25
  827. package/dist/plugins/sui/seed-objects.mjs.map +0 -1
  828. package/dist/runtime/built-in-plugin-layers.mjs.map +0 -1
  829. package/dist/substrate/runtime/context-helpers.mjs +0 -32
  830. package/dist/substrate/runtime/context-helpers.mjs.map +0 -1
  831. package/dist/substrate/runtime/run.mjs.map +0 -1
  832. package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs +0 -30
  833. package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs.map +0 -1
  834. package/dist/substrate/runtime/supervisor.d.mts +0 -1
  835. package/dist/substrate/runtime/supervisor.mjs +0 -831
  836. package/dist/substrate/runtime/supervisor.mjs.map +0 -1
  837. package/dist/surfaces/cli/commands/doctor-probes.mjs.map +0 -1
  838. /package/dist/{substrate/runtime/strategy-registry/faucet-capability-for.d.mts → plugins/faucet/http.d.mts} +0 -0
@@ -1,17 +1,21 @@
1
1
  import { IdentityContext, StackPathsService } from "../../substrate/runtime/paths.mjs";
2
+ import { decodeJsonText } from "../../substrate/runtime/runtime-decode.mjs";
2
3
  import { ownHolder } from "../../substrate/runtime/cross-process/liveness.mjs";
3
4
  import { acquireStackLock } from "../../substrate/runtime/cross-process/stack-lock.mjs";
5
+ import { mintRandomSuffix } from "../../substrate/runtime/random-suffix.mjs";
4
6
  import { ContainerRuntimeService } from "../../runtime/docker/service.mjs";
7
+ import "../../runtime/docker/index.mjs";
5
8
  import { acquireReservation } from "../../substrate/runtime/cross-process/snapshot-reservation.mjs";
6
- import { SNAPSHOT_ID_RULE, SnapshotLayout, SnapshotMetadataSchema, parseSnapshotId } from "./descriptor.mjs";
9
+ import { SNAPSHOT_ID_RULE, SnapshotDescriptorError, SnapshotLayout, SnapshotMetadataSchema, parseSnapshotId } from "./descriptor.mjs";
10
+ import { planWipe, runWipe } from "./wipe.mjs";
11
+ import "./identity-guard.mjs";
7
12
  import { runCapture } from "./capture.mjs";
8
- import { runWipe } from "./wipe.mjs";
9
13
  import { runPrune } from "./prune.mjs";
14
+ import { recoverPendingRestore } from "./recover-pending.mjs";
10
15
  import { stageAndSwap } from "../../substrate/runtime/stage-and-swap/index.mjs";
11
16
  import { runRestore } from "./restore.mjs";
12
17
  import { Context, Effect, FileSystem, Layer, Ref, Schema } from "effect";
13
- import { randomUUID } from "node:crypto";
14
- Schema.TaggedErrorClass()("SnapshotBootError", { detail: Schema.String });
18
+ //#region src/orchestrators/snapshot/service.ts
15
19
  var SnapshotIdError = class extends Schema.TaggedErrorClass()("SnapshotIdError", {
16
20
  operation: Schema.Literals([
17
21
  "capture",
@@ -22,18 +26,22 @@ var SnapshotIdError = class extends Schema.TaggedErrorClass()("SnapshotIdError",
22
26
  value: Schema.String,
23
27
  detail: Schema.String
24
28
  }) {};
25
- var SnapshotOrchestratorService = class extends Context.Service()("@devstack-rewrite/orchestrators/Snapshot") {};
29
+ var SnapshotOrchestratorService = class extends Context.Service()("@devstack/orchestrators/Snapshot") {};
26
30
  /** Mint a snapshot id when the caller didn't pass one. Carries an
27
31
  * 8-hex random suffix from `crypto.randomUUID()` (STYLE_GUIDE §17) so
28
32
  * concurrent saves don't silently overwrite. */
29
- const mintId = (prefix = "snap") => `${prefix}-${Date.now()}-${randomUUID().replace(/-/g, "").slice(0, 8)}`;
33
+ const mintId = (prefix = "snap") => `${prefix}-${Date.now()}-${mintRandomSuffix(8)}`;
30
34
  const mintSnapshotId = () => {
31
- const id = parseSnapshotId(mintId());
32
- if (id === null) throw new Error("internal snapshot id minting produced an invalid id");
33
- return id;
35
+ const raw = mintId();
36
+ const id = parseSnapshotId(raw);
37
+ return id === null ? Effect.fail(new SnapshotDescriptorError({
38
+ kind: "invalid-id",
39
+ detail: "internal snapshot id minting produced an invalid id",
40
+ value: raw
41
+ })) : Effect.succeed(id);
34
42
  };
35
43
  const mintSnapshotName = () => {
36
- return `manual-${(/* @__PURE__ */ new Date()).toISOString().replaceAll("-", "").replaceAll(":", "").slice(0, 15)}-${randomUUID().replace(/-/g, "").slice(0, 4)}`;
44
+ return `manual-${(/* @__PURE__ */ new Date()).toISOString().replaceAll("-", "").replaceAll(":", "").slice(0, 15)}-${mintRandomSuffix(8)}`;
37
45
  };
38
46
  const validateSnapshotId = (operation, value) => {
39
47
  const id = parseSnapshotId(value);
@@ -56,7 +64,10 @@ const normalizeSnapshotName = (operation, name) => {
56
64
  return Effect.succeed(normalized);
57
65
  };
58
66
  /** Read this process's canonical startTime stamp for the snapshot
59
- * reservation liveness check. */
67
+ * reservation liveness check. Returns `null` when the kernel-probe
68
+ * couldn't determine startTime — matches `RosterHolderSchema.startTime`
69
+ * semantics and is honored by the sweep's `checkHolderLiveness`
70
+ * null-conservative short-circuit. */
60
71
  const ownStartTime = () => ownHolder("snapshot").startTime;
61
72
  const normalizeIdentityValue = (value) => {
62
73
  if (Array.isArray(value)) return value.map(normalizeIdentityValue);
@@ -72,7 +83,6 @@ const layerSnapshotOrchestrator = Layer.effect(SnapshotOrchestratorService, Effe
72
83
  const identity = yield* IdentityContext;
73
84
  const runtime = yield* ContainerRuntimeService;
74
85
  const participantsRef = yield* Ref.make([]);
75
- const classifiersRef = yield* Ref.make([]);
76
86
  const seqRef = yield* Ref.make(0);
77
87
  const registerParticipant = (pluginKey, decl, hooks) => Effect.gen(function* () {
78
88
  const seq = yield* Ref.updateAndGet(seqRef, (n) => n + 1);
@@ -99,21 +109,8 @@ const layerSnapshotOrchestrator = Layer.effect(SnapshotOrchestratorService, Effe
99
109
  "snapshot.participant.subtrees": decl.subtrees.length
100
110
  });
101
111
  }).pipe(Effect.withSpan("orchestrator.snapshot.registerParticipant"));
102
- const registerClassifier = (pluginKey, decl) => Effect.gen(function* () {
103
- const seq = yield* Ref.updateAndGet(seqRef, (n) => n + 1);
104
- const entry = {
105
- dispatch: {
106
- plugin: pluginKey,
107
- decl
108
- },
109
- seq
110
- };
111
- yield* Ref.update(classifiersRef, (xs) => [...xs, entry]);
112
- yield* Effect.addFinalizer(() => Ref.update(classifiersRef, (xs) => xs.filter((e) => e.seq !== seq)));
113
- yield* Effect.annotateCurrentSpan({ "snapshot.classifier.plugin": pluginKey });
114
- }).pipe(Effect.withSpan("orchestrator.snapshot.registerClassifier"));
115
112
  const capture = ({ id, label, participants, onProgress }) => Effect.gen(function* () {
116
- const snapshotId = id === void 0 ? mintSnapshotId() : yield* validateSnapshotId("capture", id);
113
+ const snapshotId = id === void 0 ? yield* mintSnapshotId() : yield* validateSnapshotId("capture", id);
117
114
  const snapshotName = yield* normalizeSnapshotName("capture", label);
118
115
  const effectiveParticipants = participants ?? (yield* Ref.get(participantsRef)).map((e) => e.capture);
119
116
  const artifactDir = `${paths.snapshotDir}/${snapshotId}`;
@@ -206,19 +203,10 @@ const layerSnapshotOrchestrator = Layer.effect(SnapshotOrchestratorService, Effe
206
203
  });
207
204
  continue;
208
205
  }
209
- const raw = yield* Effect.try({
210
- try: () => JSON.parse(text),
211
- catch: () => null
206
+ const decoded = yield* decodeJsonText(SnapshotMetadataSchema, text, {
207
+ source: metaPath,
208
+ mkError: () => null
212
209
  }).pipe(Effect.catch(() => Effect.succeed(null)));
213
- if (raw === null) {
214
- entries.push({
215
- id,
216
- directory: dir,
217
- metadata: null
218
- });
219
- continue;
220
- }
221
- const decoded = yield* Schema.decodeUnknownEffect(SnapshotMetadataSchema)(raw).pipe(Effect.catch(() => Effect.succeed(null)));
222
210
  if (decoded !== null && parseSnapshotId(decoded.id) === null) {
223
211
  yield* Effect.logWarning(`ignoring snapshot metadata with unsafe id: ${decoded.id}`);
224
212
  entries.push({
@@ -264,33 +252,42 @@ const layerSnapshotOrchestrator = Layer.effect(SnapshotOrchestratorService, Effe
264
252
  });
265
253
  }));
266
254
  }).pipe(Effect.withSpan("orchestrator.snapshot.wipe.entry"));
267
- const prune = ({ classifiers }) => Effect.gen(function* () {
268
- const effectiveClassifiers = classifiers ?? (yield* Ref.get(classifiersRef)).map((e) => e.dispatch);
269
- return yield* Effect.scoped(Effect.gen(function* () {
270
- yield* acquireReservation(paths.snapshotReservationFile, ownStartTime());
271
- return yield* runPrune({
272
- stackRoot: paths.stackRoot,
273
- imageLabelFilter: {
274
- app: identity.app,
275
- stack: identity.stack
276
- },
277
- classifiers: effectiveClassifiers,
278
- runtime
279
- });
280
- }));
281
- }).pipe(Effect.withSpan("orchestrator.snapshot.prune.entry"));
255
+ const wipePlan = (args) => planWipe({
256
+ labelMatch: {
257
+ app: identity.app,
258
+ stack: identity.stack
259
+ },
260
+ stackRoot: paths.stackRoot,
261
+ stateFilePath: paths.stateFile,
262
+ runtime,
263
+ keepSnapshots: args.keepSnapshots,
264
+ keepCache: args.keepCache
265
+ }).pipe(Effect.withSpan("orchestrator.snapshot.wipe.plan.entry"));
266
+ const prune = () => Effect.scoped(Effect.gen(function* () {
267
+ yield* acquireReservation(paths.snapshotReservationFile, ownStartTime());
268
+ return yield* runPrune({
269
+ stackRoot: paths.stackRoot,
270
+ imageLabelFilter: {
271
+ app: identity.app,
272
+ stack: identity.stack
273
+ },
274
+ runtime
275
+ });
276
+ })).pipe(Effect.withSpan("orchestrator.snapshot.prune.entry"));
277
+ const recoverPendingRestoreImpl = recoverPendingRestore(paths.stackRoot, runtime).pipe(Effect.withSpan("orchestrator.snapshot.recover-pending.entry"));
282
278
  return SnapshotOrchestratorService.of({
283
279
  registerParticipant,
284
- registerClassifier,
285
280
  capture,
286
281
  restore,
287
282
  list,
288
283
  delete: del,
289
284
  wipe,
290
- prune
285
+ wipePlan,
286
+ prune,
287
+ recoverPendingRestore: recoverPendingRestoreImpl
291
288
  });
292
289
  }));
293
290
  //#endregion
294
- export { SnapshotOrchestratorService, layerSnapshotOrchestrator };
291
+ export { SnapshotIdError, SnapshotOrchestratorService, layerSnapshotOrchestrator };
295
292
 
296
293
  //# sourceMappingURL=service.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.mjs","names":[],"sources":["../../../src/orchestrators/snapshot/service.ts"],"sourcesContent":["// Snapshot orchestrator service.\n//\n// Architecture § L3 § Snapshot orchestrator:\n// \"Collects all `Snapshotable` decls, applies one capture-wide\n// pause window for managed containers, tars host subtrees with mode\n// round-trip, commits container writable layers, threads a typed metadata record\n// (identity guard + per-participant slice), atomic stage-and-swap\n// for restore, identity-guard fires before any destructive mutation.\"\n//\n// This file is the orchestrator's typed entry point — `capture`,\n// `restore`, `list`, `delete`, `wipe`, `prune` — wired to the\n// cross-process safety primitives (`snapshot.reservation`,\n// `stack.lock`) and the substrate path resolver.\n//\n// The service surface is name-blind. The two participant lists\n// (capture and restore) are populated by the substrate at boot from\n// the `Snapshotable` decls of registered plugins; service-specific\n// behavior never reaches this file. Registration is scope-bound: the\n// supervisor calls `registerParticipant(pluginKey, decl)` from inside\n// each plugin's scope so the registry entry is reaped on plugin\n// teardown.\n\nimport { randomUUID } from 'node:crypto';\n\nimport { Context, Effect, FileSystem, Layer, Ref, Schema, Scope } from 'effect';\n\nimport type { LivenessClassifierDecl } from '../../contracts/liveness-classifier.ts';\nimport type { SnapshotableDecl } from '../../contracts/snapshotable.ts';\nimport type { ContainerRuntime } from '../../contracts/container-runtime.ts';\nimport { ContainerRuntimeService } from '../../runtime/docker/index.ts';\nimport { ownHolder } from '../../substrate/runtime/cross-process/liveness.ts';\nimport {\n\tacquireReservation,\n\ttype SnapshotReservationError,\n} from '../../substrate/runtime/cross-process/snapshot-reservation.ts';\nimport {\n\tacquireStackLock,\n\ttype StackLockError,\n} from '../../substrate/runtime/cross-process/stack-lock.ts';\nimport { IdentityContext, StackPathsService } from '../../substrate/runtime/paths.ts';\nimport {\n\trunCapture,\n\ttype CapturePhaseError,\n\ttype SnapshotParticipant,\n\ttype SnapshotProgressReporter,\n} from './capture.ts';\nimport {\n\tSnapshotLayout,\n\tSnapshotMetadataSchema,\n\tSNAPSHOT_ID_RULE,\n\tparseSnapshotId,\n\ttype SnapshotId,\n\ttype SnapshotCatalogEntry,\n\ttype SnapshotMetadata,\n} from './descriptor.ts';\nimport {\n\ttype IdentityContributionConflictError,\n\ttype IdentityGuardError,\n} from './identity-guard.ts';\nimport {\n\trunPrune,\n\ttype ClassifierDispatch,\n\ttype PruneResult,\n\ttype PrunePhaseError,\n} from './prune.ts';\nimport { runRestore, type RestoreParticipant, type RestorePhaseError } from './restore.ts';\nimport {\n\tstageAndSwap,\n\ttype StageAndSwapError,\n} from '../../substrate/runtime/stage-and-swap/index.ts';\nimport { runWipe, type WipePhaseError } from './wipe.ts';\n\n// -----------------------------------------------------------------------------\n// The service shape\n// -----------------------------------------------------------------------------\n\n/** Tagged union of every error the orchestrator surface can surface.\n * Callers `catchTags` on the precise tags they care about. */\nexport type SnapshotOrchestratorError =\n\t| CapturePhaseError\n\t| RestorePhaseError\n\t| WipePhaseError\n\t| PrunePhaseError\n\t| IdentityGuardError\n\t| IdentityContributionConflictError\n\t| StageAndSwapError\n\t| SnapshotReservationError\n\t| StackLockError\n\t| SnapshotIdError;\n\n/** Tagged failure when the substrate's startTime probe could not be\n * reduced to a number. */\nexport class SnapshotBootError extends Schema.TaggedErrorClass<SnapshotBootError>()(\n\t'SnapshotBootError',\n\t{\n\t\tdetail: Schema.String,\n\t},\n) {}\n\nexport class SnapshotIdError extends Schema.TaggedErrorClass<SnapshotIdError>()('SnapshotIdError', {\n\toperation: Schema.Literals(['capture', 'restore', 'delete']),\n\tfield: Schema.Literals(['id', 'name']),\n\tvalue: Schema.String,\n\tdetail: Schema.String,\n}) {}\n\nexport interface SnapshotOrchestrator {\n\t/** Register a `SnapshotableDecl` from a plugin. Scope-bound — when\n\t * the caller's scope (the plugin's acquire scope) closes, the\n\t * registration is reaped. The supervisor calls this once per\n\t * `SnapshotableDecl` on each plugin's `capabilities` tuple.\n\t *\n\t * `captureIdentity` / `captureContribution` are separately wired\n\t * hooks (not on `SnapshotableDecl` itself) — they plumb through\n\t * the plugin's resolved value. Identity is validated by the\n\t * orchestrator; contribution payloads are persisted as opaque JSON\n\t * that plugins must validate at their own boundary if they read it. */\n\treadonly registerParticipant: (\n\t\tpluginKey: string,\n\t\tdecl: SnapshotableDecl,\n\t\thooks?: {\n\t\t\treadonly captureIdentity?: Effect.Effect<Readonly<Record<string, string>>>;\n\t\t\treadonly captureContribution?: Effect.Effect<unknown>;\n\t\t\treadonly liveIdentity?: Effect.Effect<Readonly<Record<string, string>>>;\n\t\t},\n\t) => Effect.Effect<void, never, Scope.Scope>;\n\n\t/** Register a plugin-emitted `LivenessClassifier`. The decl is\n\t * consumed by `prune()`. Scope-bound. */\n\treadonly registerClassifier: (\n\t\tpluginKey: string,\n\t\tdecl: LivenessClassifierDecl,\n\t) => Effect.Effect<void, never, Scope.Scope>;\n\n\t/** Capture a new snapshot. The id MAY be caller-supplied; if\n\t * omitted, the substrate mints a random suffix to sidestep the\n\t * concurrent-saves-against-same-id silent overwrite.\n\t *\n\t * `participants` overrides the registered set — primarily for\n\t * tests. Production callers omit it; the orchestrator reads the\n\t * registered list. */\n\treadonly capture: (args: {\n\t\treadonly id?: string;\n\t\treadonly label?: string;\n\t\treadonly participants?: ReadonlyArray<SnapshotParticipant>;\n\t\treadonly onProgress?: SnapshotProgressReporter;\n\t}) => Effect.Effect<SnapshotMetadata, SnapshotOrchestratorError, FileSystem.FileSystem>;\n\n\t/** Restore from a previously-captured artifact id. Refuses on\n\t * identity mismatch BEFORE any destructive mutation. */\n\treadonly restore: (args: {\n\t\treadonly id: string;\n\t\treadonly participants?: ReadonlyArray<RestoreParticipant>;\n\t}) => Effect.Effect<SnapshotMetadata, SnapshotOrchestratorError, FileSystem.FileSystem>;\n\n\t/** Catalog list — tolerates partial / corrupt entries (they appear\n\t * with `metadata: null`). */\n\treadonly list: Effect.Effect<\n\t\tReadonlyArray<SnapshotCatalogEntry>,\n\t\tSnapshotOrchestratorError,\n\t\tFileSystem.FileSystem\n\t>;\n\n\t/** Delete one artifact by id. Idempotent — missing id is a no-op. */\n\treadonly delete: (\n\t\tid: string,\n\t) => Effect.Effect<void, SnapshotOrchestratorError, FileSystem.FileSystem>;\n\n\t/** Wipe the live (`app`, `stack`) footprint; preserves the\n\t * snapshot catalog by default. Stack-local artifact cache is\n\t * removed unless `keepCache` is explicitly true. */\n\treadonly wipe: (args: {\n\t\treadonly keepSnapshots?: boolean;\n\t\treadonly keepCache?: boolean;\n\t}) => Effect.Effect<void, SnapshotOrchestratorError, FileSystem.FileSystem>;\n\n\t/** Prune the snapshot catalog + byproduct images via plugin-\n\t * contributed classifiers. `classifiers` overrides the registered\n\t * set; omit to consume registered classifiers. */\n\treadonly prune: (args: {\n\t\treadonly classifiers?: ReadonlyArray<ClassifierDispatch>;\n\t}) => Effect.Effect<PruneResult, SnapshotOrchestratorError, FileSystem.FileSystem>;\n}\n\nexport class SnapshotOrchestratorService extends Context.Service<\n\tSnapshotOrchestratorService,\n\tSnapshotOrchestrator\n>()('@devstack-rewrite/orchestrators/Snapshot') {}\n\n// -----------------------------------------------------------------------------\n// Helpers\n// -----------------------------------------------------------------------------\n\n/** Mint a snapshot id when the caller didn't pass one. Carries an\n * 8-hex random suffix from `crypto.randomUUID()` (STYLE_GUIDE §17) so\n * concurrent saves don't silently overwrite. */\nconst mintId = (prefix = 'snap'): string =>\n\t`${prefix}-${Date.now()}-${randomUUID().replace(/-/g, '').slice(0, 8)}`;\n\nconst mintSnapshotId = (): SnapshotId => {\n\tconst id = parseSnapshotId(mintId());\n\tif (id === null) {\n\t\tthrow new Error('internal snapshot id minting produced an invalid id');\n\t}\n\treturn id;\n};\n\nconst mintSnapshotName = (): string => {\n\tconst stamp = new Date().toISOString().replaceAll('-', '').replaceAll(':', '').slice(0, 15);\n\treturn `manual-${stamp}-${randomUUID().replace(/-/g, '').slice(0, 4)}`;\n};\n\nconst validateSnapshotId = (\n\toperation: SnapshotIdError['operation'],\n\tvalue: string,\n): Effect.Effect<SnapshotId, SnapshotIdError> => {\n\tconst id = parseSnapshotId(value);\n\treturn id === null\n\t\t? Effect.fail(\n\t\t\t\tnew SnapshotIdError({\n\t\t\t\t\toperation,\n\t\t\t\t\tfield: 'id',\n\t\t\t\t\tvalue,\n\t\t\t\t\tdetail: SNAPSHOT_ID_RULE,\n\t\t\t\t}),\n\t\t\t)\n\t\t: Effect.succeed(id);\n};\n\nconst normalizeSnapshotName = (\n\toperation: SnapshotIdError['operation'],\n\tname: string | undefined,\n): Effect.Effect<string, SnapshotIdError> => {\n\tif (name === undefined) return Effect.succeed(mintSnapshotName());\n\tconst normalized = name.trim();\n\tif (normalized.length === 0 || normalized.includes('\\0') || normalized.length > 128) {\n\t\treturn Effect.fail(\n\t\t\tnew SnapshotIdError({\n\t\t\t\toperation,\n\t\t\t\tfield: 'name',\n\t\t\t\tvalue: name,\n\t\t\t\tdetail:\n\t\t\t\t\t'snapshot names must be 1-128 characters after trimming and cannot contain NUL bytes',\n\t\t\t}),\n\t\t);\n\t}\n\treturn Effect.succeed(normalized);\n};\n\n/** Read this process's canonical startTime stamp for the snapshot\n * reservation liveness check. */\nconst ownStartTime = (): number => ownHolder('snapshot').startTime;\n\nconst normalizeIdentityValue = (value: unknown): unknown => {\n\tif (Array.isArray(value)) return value.map(normalizeIdentityValue);\n\tif (value !== null && typeof value === 'object') {\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(value)\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([key, nested]) => [key, normalizeIdentityValue(nested)]),\n\t\t);\n\t}\n\treturn value;\n};\n\nconst stableIdentityString = (value: unknown): string => {\n\tconst encoded = JSON.stringify(normalizeIdentityValue(value));\n\treturn encoded ?? String(value);\n};\n\nconst identityHookFromDecl = (\n\tpluginKey: string,\n\tdecl: SnapshotableDecl,\n): Effect.Effect<Readonly<Record<string, string>>> =>\n\tdecl.preRestore === undefined\n\t\t? Effect.succeed({})\n\t\t: decl.preRestore.pipe(Effect.map((value) => ({ [pluginKey]: stableIdentityString(value) })));\n\n// -----------------------------------------------------------------------------\n// Layer\n// -----------------------------------------------------------------------------\n\n/**\n * Wire the orchestrator from the substrate's path resolver + the\n * runtime container adapter.\n *\n * Discipline:\n * - Capture/restore acquire `snapshot.reservation` (O_EXCL,\n * no retry) — concurrent capture is structurally refused.\n * - List/delete/wipe acquire `stack.lock` briefly (no reservation\n * needed; they don't pause containers).\n * - Prune acquires both — it walks the catalog AND sweeps images.\n */\n/** Per-plugin participant registration entry kept on the orchestrator's\n * inner ref. Sequence number lets parallel registrations under the\n * same key be reaped independently when their owning scopes close. */\ninterface RegisteredParticipantEntry {\n\treadonly pluginKey: string;\n\treadonly capture: SnapshotParticipant;\n\treadonly restore: RestoreParticipant;\n\treadonly seq: number;\n}\n\ninterface RegisteredClassifierEntry {\n\treadonly dispatch: ClassifierDispatch;\n\treadonly seq: number;\n}\n\nexport const layerSnapshotOrchestrator: Layer.Layer<\n\tSnapshotOrchestratorService,\n\tnever,\n\tStackPathsService | IdentityContext | ContainerRuntimeService\n> = Layer.effect(\n\tSnapshotOrchestratorService,\n\tEffect.gen(function* () {\n\t\tconst paths = yield* StackPathsService;\n\t\tconst identity = yield* IdentityContext;\n\t\tconst runtime: ContainerRuntime = yield* ContainerRuntimeService;\n\n\t\t// Scope-local participant + classifier registries. The supervisor\n\t\t// adds entries from each plugin's acquire scope; finalizers\n\t\t// remove them on scope close. Sequence number protects against\n\t\t// the parallel-registration-same-key race when two stack\n\t\t// instances register concurrently.\n\t\tconst participantsRef = yield* Ref.make<ReadonlyArray<RegisteredParticipantEntry>>([]);\n\t\tconst classifiersRef = yield* Ref.make<ReadonlyArray<RegisteredClassifierEntry>>([]);\n\t\tconst seqRef = yield* Ref.make(0);\n\n\t\tconst registerParticipant: SnapshotOrchestrator['registerParticipant'] = (\n\t\t\tpluginKey,\n\t\t\tdecl,\n\t\t\thooks,\n\t\t) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst seq = yield* Ref.updateAndGet(seqRef, (n) => n + 1);\n\t\t\t\tconst declIdentity = identityHookFromDecl(pluginKey, decl);\n\t\t\t\tconst capture: SnapshotParticipant = {\n\t\t\t\t\tplugin: pluginKey,\n\t\t\t\t\tdecl,\n\t\t\t\t\tcaptureIdentity: hooks?.captureIdentity ?? declIdentity,\n\t\t\t\t\tcaptureContribution: hooks?.captureContribution ?? Effect.succeed<unknown>(undefined),\n\t\t\t\t};\n\t\t\t\tconst restore: RestoreParticipant = {\n\t\t\t\t\tplugin: pluginKey,\n\t\t\t\t\tliveIdentity: hooks?.liveIdentity ?? declIdentity,\n\t\t\t\t\tpostRestore: decl.postRestore as Effect.Effect<void> | undefined,\n\t\t\t\t};\n\t\t\t\tconst entry: RegisteredParticipantEntry = { pluginKey, capture, restore, seq };\n\t\t\t\tyield* Ref.update(participantsRef, (xs) => [...xs, entry]);\n\t\t\t\tyield* Effect.addFinalizer(() =>\n\t\t\t\t\tRef.update(participantsRef, (xs) => xs.filter((e) => e.seq !== seq)),\n\t\t\t\t);\n\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t'snapshot.participant.plugin': pluginKey,\n\t\t\t\t\t'snapshot.participant.subtrees': decl.subtrees.length,\n\t\t\t\t});\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.registerParticipant')) as Effect.Effect<\n\t\t\t\tvoid,\n\t\t\t\tnever,\n\t\t\t\tScope.Scope\n\t\t\t>;\n\n\t\tconst registerClassifier: SnapshotOrchestrator['registerClassifier'] = (pluginKey, decl) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst seq = yield* Ref.updateAndGet(seqRef, (n) => n + 1);\n\t\t\t\tconst dispatch: ClassifierDispatch = { plugin: pluginKey, decl };\n\t\t\t\tconst entry: RegisteredClassifierEntry = { dispatch, seq };\n\t\t\t\tyield* Ref.update(classifiersRef, (xs) => [...xs, entry]);\n\t\t\t\tyield* Effect.addFinalizer(() =>\n\t\t\t\t\tRef.update(classifiersRef, (xs) => xs.filter((e) => e.seq !== seq)),\n\t\t\t\t);\n\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t'snapshot.classifier.plugin': pluginKey,\n\t\t\t\t});\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.registerClassifier')) as Effect.Effect<\n\t\t\t\tvoid,\n\t\t\t\tnever,\n\t\t\t\tScope.Scope\n\t\t\t>;\n\n\t\tconst capture: SnapshotOrchestrator['capture'] = ({ id, label, participants, onProgress }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst snapshotId =\n\t\t\t\t\tid === undefined ? mintSnapshotId() : yield* validateSnapshotId('capture', id);\n\t\t\t\tconst snapshotName = yield* normalizeSnapshotName('capture', label);\n\t\t\t\tconst effectiveParticipants =\n\t\t\t\t\tparticipants ?? (yield* Ref.get(participantsRef)).map((e) => e.capture);\n\t\t\t\tconst artifactDir = `${paths.snapshotDir}/${snapshotId}`;\n\t\t\t\tconst stagingDir = `${paths.snapshotDir}/.staging.${snapshotId}`;\n\t\t\t\tconst backupDir = `${paths.snapshotDir}/.bak.${snapshotId}`;\n\n\t\t\t\t// Acquire `snapshot.reservation` (O_EXCL, no retry) for the\n\t\t\t\t// duration of the capture. The stack.lock is acquired\n\t\t\t\t// briefly inside `stageAndSwap` for the rename publish.\n\t\t\t\treturn yield* Effect.scoped(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* acquireReservation(paths.snapshotReservationFile, ownStartTime());\n\t\t\t\t\t\tconst fs = yield* FileSystem.FileSystem;\n\t\t\t\t\t\tconst targetExists = yield* fs\n\t\t\t\t\t\t\t.exists(artifactDir)\n\t\t\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\t\t\t\tif (targetExists) {\n\t\t\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\t\t\tnew SnapshotIdError({\n\t\t\t\t\t\t\t\t\toperation: 'capture',\n\t\t\t\t\t\t\t\t\tfield: 'id',\n\t\t\t\t\t\t\t\t\tvalue: snapshotId,\n\t\t\t\t\t\t\t\t\tdetail: 'snapshot id already exists',\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst existing = yield* list;\n\t\t\t\t\t\tif (existing.some((entry) => entry.metadata?.label === snapshotName)) {\n\t\t\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\t\t\tnew SnapshotIdError({\n\t\t\t\t\t\t\t\t\toperation: 'capture',\n\t\t\t\t\t\t\t\t\tfield: 'name',\n\t\t\t\t\t\t\t\t\tvalue: snapshotName,\n\t\t\t\t\t\t\t\t\tdetail: 'snapshot name already exists',\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Stage-and-swap publishes the artifact directory\n\t\t\t\t\t\t// atomically — external watchers (and `list`)\n\t\t\t\t\t\t// never observe a half-written tree.\n\t\t\t\t\t\tconst meta = yield* stageAndSwap({\n\t\t\t\t\t\t\ttargetPath: artifactDir,\n\t\t\t\t\t\t\tstagingPath: stagingDir,\n\t\t\t\t\t\t\tbackupPath: backupDir,\n\t\t\t\t\t\t\tbuild: runCapture({\n\t\t\t\t\t\t\t\tstagingDir,\n\t\t\t\t\t\t\t\tsnapshotId,\n\t\t\t\t\t\t\t\tlabel: snapshotName,\n\t\t\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\t\t\tnetwork: identity.chain,\n\t\t\t\t\t\t\t\truntimeStackRoot: paths.stackRoot,\n\t\t\t\t\t\t\t\tstateFilePath: paths.stateFile,\n\t\t\t\t\t\t\t\tparticipants: effectiveParticipants,\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tonProgress,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn meta;\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.capture.entry'));\n\n\t\tconst restore: SnapshotOrchestrator['restore'] = ({ id, participants }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst snapshotId = yield* validateSnapshotId('restore', id);\n\t\t\t\tconst artifactDir = `${paths.snapshotDir}/${snapshotId}`;\n\t\t\t\tconst effectiveParticipants =\n\t\t\t\t\tparticipants ?? (yield* Ref.get(participantsRef)).map((e) => e.restore);\n\t\t\t\treturn yield* Effect.scoped(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* acquireReservation(paths.snapshotReservationFile, ownStartTime());\n\t\t\t\t\t\treturn yield* runRestore({\n\t\t\t\t\t\t\tsnapshotId,\n\t\t\t\t\t\t\tartifactDir,\n\t\t\t\t\t\t\truntimeStackRoot: paths.stackRoot,\n\t\t\t\t\t\t\truntimeStagingPath: `${paths.stackRoot}.staging.${snapshotId}`,\n\t\t\t\t\t\t\truntimeBackupPath: `${paths.stackRoot}.bak.${snapshotId}`,\n\t\t\t\t\t\t\tparticipants: effectiveParticipants,\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\truntimeIdentity: {\n\t\t\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\t\t\tnetwork: identity.chain,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.restore.entry'));\n\n\t\tconst list: SnapshotOrchestrator['list'] = Effect.gen(function* () {\n\t\t\tconst fs = yield* FileSystem.FileSystem;\n\t\t\tconst exists = yield* fs\n\t\t\t\t.exists(paths.snapshotDir)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\tif (!exists) return [];\n\t\t\tconst ids = yield* fs\n\t\t\t\t.readDirectory(paths.snapshotDir)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed([] as ReadonlyArray<string>)));\n\t\t\tconst entries: SnapshotCatalogEntry[] = [];\n\t\t\tfor (const id of ids) {\n\t\t\t\t// Skip transient staging/backup directories — they're\n\t\t\t\t// invisible to the catalog by construction (the\n\t\t\t\t// `.staging.<id>` / `.bak.<id>` naming).\n\t\t\t\tif (id.startsWith('.staging.') || id.startsWith('.bak.')) continue;\n\t\t\t\tconst parsedId = parseSnapshotId(id);\n\t\t\t\tif (parsedId === null) {\n\t\t\t\t\tyield* Effect.logWarning(`ignoring unsafe snapshot catalog entry: ${id}`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst dir = `${paths.snapshotDir}/${id}`;\n\t\t\t\tconst metaPath = `${dir}/${SnapshotLayout.metaFile}`;\n\t\t\t\tconst metaExists = yield* fs\n\t\t\t\t\t.exists(metaPath)\n\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\t\tif (!metaExists) {\n\t\t\t\t\tentries.push({ id, directory: dir, metadata: null });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst text = yield* fs\n\t\t\t\t\t.readFileString(metaPath)\n\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed('')));\n\t\t\t\tif (text === '') {\n\t\t\t\t\tentries.push({ id, directory: dir, metadata: null });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst raw = yield* Effect.try({\n\t\t\t\t\ttry: () => JSON.parse(text) as unknown,\n\t\t\t\t\tcatch: () => null,\n\t\t\t\t}).pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\t\tif (raw === null) {\n\t\t\t\t\tentries.push({ id, directory: dir, metadata: null });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst decoded = yield* Schema.decodeUnknownEffect(SnapshotMetadataSchema)(raw).pipe(\n\t\t\t\t\tEffect.catch(() => Effect.succeed(null)),\n\t\t\t\t);\n\t\t\t\tif (decoded !== null && parseSnapshotId(decoded.id) === null) {\n\t\t\t\t\tyield* Effect.logWarning(`ignoring snapshot metadata with unsafe id: ${decoded.id}`);\n\t\t\t\t\tentries.push({ id, directory: dir, metadata: null });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tentries.push({ id: parsedId, directory: dir, metadata: decoded });\n\t\t\t}\n\t\t\treturn entries;\n\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.list'));\n\n\t\tconst del: SnapshotOrchestrator['delete'] = (id) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst snapshotId = yield* validateSnapshotId('delete', id);\n\t\t\t\tconst fs = yield* FileSystem.FileSystem;\n\t\t\t\tconst dir = `${paths.snapshotDir}/${snapshotId}`;\n\t\t\t\tyield* Effect.scoped(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* acquireStackLock(paths.stackLockFile);\n\t\t\t\t\t\tyield* fs.remove(dir, { recursive: true, force: true }).pipe(Effect.ignore);\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.delete'));\n\n\t\tconst wipe: SnapshotOrchestrator['wipe'] = (args) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tyield* Effect.scoped(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* acquireStackLock(paths.stackLockFile);\n\t\t\t\t\t\tyield* runWipe({\n\t\t\t\t\t\t\tlabelMatch: { app: identity.app, stack: identity.stack },\n\t\t\t\t\t\t\tstackRoot: paths.stackRoot,\n\t\t\t\t\t\t\tstateFilePath: paths.stateFile,\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tkeepSnapshots: args.keepSnapshots,\n\t\t\t\t\t\t\tkeepCache: args.keepCache,\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.wipe.entry'));\n\n\t\tconst prune: SnapshotOrchestrator['prune'] = ({ classifiers }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst effectiveClassifiers =\n\t\t\t\t\tclassifiers ?? (yield* Ref.get(classifiersRef)).map((e) => e.dispatch);\n\t\t\t\treturn yield* Effect.scoped(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* acquireReservation(paths.snapshotReservationFile, ownStartTime());\n\t\t\t\t\t\treturn yield* runPrune({\n\t\t\t\t\t\t\tstackRoot: paths.stackRoot,\n\t\t\t\t\t\t\timageLabelFilter: { app: identity.app, stack: identity.stack },\n\t\t\t\t\t\t\tclassifiers: effectiveClassifiers,\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.prune.entry'));\n\n\t\treturn SnapshotOrchestratorService.of({\n\t\t\tregisterParticipant,\n\t\t\tregisterClassifier,\n\t\t\tcapture,\n\t\t\trestore,\n\t\t\tlist,\n\t\t\tdelete: del,\n\t\t\twipe,\n\t\t\tprune,\n\t\t});\n\t}),\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.mjs","names":[],"sources":["../../../src/orchestrators/snapshot/service.ts"],"sourcesContent":["// Snapshot orchestrator service.\n//\n// Architecture § L3 § Snapshot orchestrator:\n// \"Collects all `Snapshotable` decls, applies one capture-wide\n// pause window for managed containers, tars host subtrees with mode\n// round-trip, commits container writable layers, threads a typed metadata record\n// (identity guard + per-participant slice), atomic stage-and-swap\n// for restore, identity-guard fires before any destructive mutation.\"\n//\n// This file is the orchestrator's typed entry point — `capture`,\n// `restore`, `list`, `delete`, `wipe`, `prune` — wired to the\n// cross-process safety primitives (`snapshot.reservation`,\n// `stack.lock`) and the substrate path resolver.\n//\n// The service surface is name-blind. The two participant lists\n// (capture and restore) are populated by the substrate at boot from\n// the `Snapshotable` decls of registered plugins; service-specific\n// behavior never reaches this file. Registration is scope-bound: the\n// supervisor calls `registerParticipant(pluginKey, decl)` from inside\n// each plugin's scope so the registry entry is reaped on plugin\n// teardown.\n\nimport { mintRandomSuffix } from '../../substrate/runtime/random-suffix.ts';\n\nimport { Context, Effect, FileSystem, Layer, Ref, Schema, Scope } from 'effect';\n\nimport type { SnapshotableDecl } from '../../contracts/snapshotable.ts';\nimport type { ContainerRuntime } from '../../contracts/container-runtime.ts';\nimport { ContainerRuntimeService } from '../../runtime/docker/index.ts';\nimport { decodeJsonText } from '../../substrate/runtime/runtime-decode.ts';\nimport { ownHolder } from '../../substrate/runtime/cross-process/liveness.ts';\nimport {\n\tacquireReservation,\n\ttype SnapshotReservationError,\n} from '../../substrate/runtime/cross-process/snapshot-reservation.ts';\nimport {\n\tacquireStackLock,\n\ttype StackLockError,\n} from '../../substrate/runtime/cross-process/stack-lock.ts';\nimport { IdentityContext, StackPathsService } from '../../substrate/runtime/paths.ts';\nimport {\n\trunCapture,\n\ttype CapturePhaseError,\n\ttype SnapshotParticipant,\n\ttype SnapshotProgressReporter,\n} from './capture.ts';\nimport {\n\tSnapshotDescriptorError,\n\tSnapshotLayout,\n\tSnapshotMetadataSchema,\n\tSNAPSHOT_ID_RULE,\n\tparseSnapshotId,\n\ttype SnapshotId,\n\ttype SnapshotCatalogEntry,\n\ttype SnapshotMetadata,\n} from './descriptor.ts';\nimport {\n\ttype IdentityContributionConflictError,\n\ttype IdentityGuardError,\n} from './identity-guard.ts';\nimport { runPrune, type PruneResult, type PrunePhaseError } from './prune.ts';\nimport {\n\trecoverPendingRestore,\n\ttype RestorePendingRecoveryError,\n\ttype RestorePendingRecoverySummary,\n} from './recover-pending.ts';\nimport { runRestore, type RestoreParticipant, type RestorePhaseError } from './restore.ts';\nimport {\n\tstageAndSwap,\n\ttype StageAndSwapError,\n} from '../../substrate/runtime/stage-and-swap/index.ts';\nimport { planWipe, runWipe, type WipePhaseError, type WipeTargets } from './wipe.ts';\n\n// -----------------------------------------------------------------------------\n// The service shape\n// -----------------------------------------------------------------------------\n\n/** Tagged union of every error the orchestrator surface can surface.\n * Callers `catchTags` on the precise tags they care about. */\nexport type SnapshotOrchestratorError =\n\t| CapturePhaseError\n\t| RestorePhaseError\n\t| RestorePendingRecoveryError\n\t| WipePhaseError\n\t| PrunePhaseError\n\t| IdentityGuardError\n\t| IdentityContributionConflictError\n\t| StageAndSwapError\n\t| SnapshotReservationError\n\t| StackLockError\n\t| SnapshotIdError\n\t| SnapshotDescriptorError;\n\nexport class SnapshotIdError extends Schema.TaggedErrorClass<SnapshotIdError>()('SnapshotIdError', {\n\toperation: Schema.Literals(['capture', 'restore', 'delete']),\n\tfield: Schema.Literals(['id', 'name']),\n\tvalue: Schema.String,\n\tdetail: Schema.String,\n}) {}\n\nexport interface SnapshotOrchestrator {\n\t/** Register a `SnapshotableDecl` from a plugin. Scope-bound — when\n\t * the caller's scope (the plugin's acquire scope) closes, the\n\t * registration is reaped. The supervisor calls this once per\n\t * `SnapshotableDecl` on each plugin's `capabilities` tuple.\n\t *\n\t * `captureIdentity` / `captureContribution` are separately wired\n\t * hooks (not on `SnapshotableDecl` itself) — they plumb through\n\t * the plugin's resolved value. Identity is validated by the\n\t * orchestrator; contribution payloads are persisted as opaque JSON\n\t * that plugins must validate at their own boundary if they read it. */\n\treadonly registerParticipant: (\n\t\tpluginKey: string,\n\t\tdecl: SnapshotableDecl,\n\t\thooks?: {\n\t\t\treadonly captureIdentity?: Effect.Effect<Readonly<Record<string, string>>>;\n\t\t\treadonly captureContribution?: Effect.Effect<unknown>;\n\t\t\treadonly liveIdentity?: Effect.Effect<Readonly<Record<string, string>>>;\n\t\t},\n\t) => Effect.Effect<void, never, Scope.Scope>;\n\n\t/** Capture a new snapshot. The id MAY be caller-supplied; if\n\t * omitted, the substrate mints a random suffix to sidestep the\n\t * concurrent-saves-against-same-id silent overwrite.\n\t *\n\t * `participants` overrides the registered set — primarily for\n\t * tests. Production callers omit it; the orchestrator reads the\n\t * registered list. */\n\treadonly capture: (args: {\n\t\treadonly id?: string;\n\t\treadonly label?: string;\n\t\treadonly participants?: ReadonlyArray<SnapshotParticipant>;\n\t\treadonly onProgress?: SnapshotProgressReporter;\n\t}) => Effect.Effect<SnapshotMetadata, SnapshotOrchestratorError, FileSystem.FileSystem>;\n\n\t/** Restore from a previously-captured artifact id. Refuses on\n\t * identity mismatch BEFORE any destructive mutation. */\n\treadonly restore: (args: {\n\t\treadonly id: string;\n\t\treadonly participants?: ReadonlyArray<RestoreParticipant>;\n\t}) => Effect.Effect<SnapshotMetadata, SnapshotOrchestratorError, FileSystem.FileSystem>;\n\n\t/** Catalog list — tolerates partial / corrupt entries (they appear\n\t * with `metadata: null`). */\n\treadonly list: Effect.Effect<\n\t\tReadonlyArray<SnapshotCatalogEntry>,\n\t\tSnapshotOrchestratorError,\n\t\tFileSystem.FileSystem\n\t>;\n\n\t/** Delete one artifact by id. Idempotent — missing id is a no-op. */\n\treadonly delete: (\n\t\tid: string,\n\t) => Effect.Effect<void, SnapshotOrchestratorError, FileSystem.FileSystem>;\n\n\t/** Wipe the live (`app`, `stack`) footprint; preserves the\n\t * snapshot catalog by default. Stack-local artifact cache is\n\t * removed unless `keepCache` is explicitly true. */\n\treadonly wipe: (args: {\n\t\treadonly keepSnapshots?: boolean;\n\t\treadonly keepCache?: boolean;\n\t}) => Effect.Effect<void, SnapshotOrchestratorError, FileSystem.FileSystem>;\n\n\t/** Enumerate the concrete teardown targets a `wipe` of the same\n\t * `(app, stack)` would remove WITHOUT removing anything — the\n\t * read-only preview behind `devstack wipe --dry-run`. Same args as\n\t * `wipe` so the preview honors the same `keepSnapshots`/`keepCache`\n\t * preservation policy the real wipe applies. */\n\treadonly wipePlan: (args: {\n\t\treadonly keepSnapshots?: boolean;\n\t\treadonly keepCache?: boolean;\n\t}) => Effect.Effect<WipeTargets, SnapshotOrchestratorError, FileSystem.FileSystem>;\n\n\t/** Prune the snapshot catalog (reaps partial artifacts) and sweeps\n\t * byproduct images via the runtime adapter's label-scoped cleanup. */\n\treadonly prune: () => Effect.Effect<\n\t\tPruneResult,\n\t\tSnapshotOrchestratorError,\n\t\tFileSystem.FileSystem\n\t>;\n\n\t/** Recover from a snapshot-restore that crashed mid-way through the\n\t * post-publish Docker handoff. Reads the on-disk pending marker,\n\t * retries each outstanding image promote, and clears the marker.\n\t * Idempotent — safe to call on every supervise startup; a no-op\n\t * when no marker is present. */\n\treadonly recoverPendingRestore: Effect.Effect<\n\t\tRestorePendingRecoverySummary,\n\t\tSnapshotOrchestratorError,\n\t\tFileSystem.FileSystem\n\t>;\n}\n\nexport class SnapshotOrchestratorService extends Context.Service<\n\tSnapshotOrchestratorService,\n\tSnapshotOrchestrator\n>()('@devstack/orchestrators/Snapshot') {}\n\n// -----------------------------------------------------------------------------\n// Helpers\n// -----------------------------------------------------------------------------\n\n/** Mint a snapshot id when the caller didn't pass one. Carries an\n * 8-hex random suffix from `crypto.randomUUID()` (STYLE_GUIDE §17) so\n * concurrent saves don't silently overwrite. */\nconst mintId = (prefix = 'snap'): string => `${prefix}-${Date.now()}-${mintRandomSuffix(8)}`;\n\nconst mintSnapshotId = (): Effect.Effect<SnapshotId, SnapshotDescriptorError> => {\n\tconst raw = mintId();\n\tconst id = parseSnapshotId(raw);\n\treturn id === null\n\t\t? Effect.fail(\n\t\t\t\tnew SnapshotDescriptorError({\n\t\t\t\t\tkind: 'invalid-id',\n\t\t\t\t\tdetail: 'internal snapshot id minting produced an invalid id',\n\t\t\t\t\tvalue: raw,\n\t\t\t\t}),\n\t\t\t)\n\t\t: Effect.succeed(id);\n};\n\nconst mintSnapshotName = (): string => {\n\tconst stamp = new Date().toISOString().replaceAll('-', '').replaceAll(':', '').slice(0, 15);\n\treturn `manual-${stamp}-${mintRandomSuffix(8)}`;\n};\n\nconst validateSnapshotId = (\n\toperation: SnapshotIdError['operation'],\n\tvalue: string,\n): Effect.Effect<SnapshotId, SnapshotIdError> => {\n\tconst id = parseSnapshotId(value);\n\treturn id === null\n\t\t? Effect.fail(\n\t\t\t\tnew SnapshotIdError({\n\t\t\t\t\toperation,\n\t\t\t\t\tfield: 'id',\n\t\t\t\t\tvalue,\n\t\t\t\t\tdetail: SNAPSHOT_ID_RULE,\n\t\t\t\t}),\n\t\t\t)\n\t\t: Effect.succeed(id);\n};\n\nconst normalizeSnapshotName = (\n\toperation: SnapshotIdError['operation'],\n\tname: string | undefined,\n): Effect.Effect<string, SnapshotIdError> => {\n\tif (name === undefined) return Effect.succeed(mintSnapshotName());\n\tconst normalized = name.trim();\n\tif (normalized.length === 0 || normalized.includes('\\0') || normalized.length > 128) {\n\t\treturn Effect.fail(\n\t\t\tnew SnapshotIdError({\n\t\t\t\toperation,\n\t\t\t\tfield: 'name',\n\t\t\t\tvalue: name,\n\t\t\t\tdetail:\n\t\t\t\t\t'snapshot names must be 1-128 characters after trimming and cannot contain NUL bytes',\n\t\t\t}),\n\t\t);\n\t}\n\treturn Effect.succeed(normalized);\n};\n\n/** Read this process's canonical startTime stamp for the snapshot\n * reservation liveness check. Returns `null` when the kernel-probe\n * couldn't determine startTime — matches `RosterHolderSchema.startTime`\n * semantics and is honored by the sweep's `checkHolderLiveness`\n * null-conservative short-circuit. */\nconst ownStartTime = (): number | null => ownHolder('snapshot').startTime;\n\nconst normalizeIdentityValue = (value: unknown): unknown => {\n\tif (Array.isArray(value)) return value.map(normalizeIdentityValue);\n\tif (value !== null && typeof value === 'object') {\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(value)\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([key, nested]) => [key, normalizeIdentityValue(nested)]),\n\t\t);\n\t}\n\treturn value;\n};\n\nconst stableIdentityString = (value: unknown): string => {\n\tconst encoded = JSON.stringify(normalizeIdentityValue(value));\n\treturn encoded ?? String(value);\n};\n\nconst identityHookFromDecl = (\n\tpluginKey: string,\n\tdecl: SnapshotableDecl,\n): Effect.Effect<Readonly<Record<string, string>>> =>\n\tdecl.preRestore === undefined\n\t\t? Effect.succeed({})\n\t\t: decl.preRestore.pipe(Effect.map((value) => ({ [pluginKey]: stableIdentityString(value) })));\n\n// -----------------------------------------------------------------------------\n// Layer\n// -----------------------------------------------------------------------------\n\n/**\n * Wire the orchestrator from the substrate's path resolver + the\n * runtime container adapter.\n *\n * Discipline:\n * - Capture/restore acquire `snapshot.reservation` (O_EXCL,\n * no retry) — concurrent capture is structurally refused.\n * - List/delete/wipe acquire `stack.lock` briefly (no reservation\n * needed; they don't pause containers).\n * - Prune acquires both — it walks the catalog AND sweeps images.\n */\n/** Per-plugin participant registration entry kept on the orchestrator's\n * inner ref. Sequence number lets parallel registrations under the\n * same key be reaped independently when their owning scopes close. */\ninterface RegisteredParticipantEntry {\n\treadonly pluginKey: string;\n\treadonly capture: SnapshotParticipant;\n\treadonly restore: RestoreParticipant;\n\treadonly seq: number;\n}\n\nexport const layerSnapshotOrchestrator: Layer.Layer<\n\tSnapshotOrchestratorService,\n\tnever,\n\tStackPathsService | IdentityContext | ContainerRuntimeService\n> = Layer.effect(\n\tSnapshotOrchestratorService,\n\tEffect.gen(function* () {\n\t\tconst paths = yield* StackPathsService;\n\t\tconst identity = yield* IdentityContext;\n\t\tconst runtime: ContainerRuntime = yield* ContainerRuntimeService;\n\n\t\t// Scope-local participant registry. The supervisor adds entries\n\t\t// from each plugin's acquire scope; finalizers remove them on\n\t\t// scope close. Sequence number protects against the parallel-\n\t\t// registration-same-key race when two stack instances register\n\t\t// concurrently.\n\t\tconst participantsRef = yield* Ref.make<ReadonlyArray<RegisteredParticipantEntry>>([]);\n\t\tconst seqRef = yield* Ref.make(0);\n\n\t\tconst registerParticipant: SnapshotOrchestrator['registerParticipant'] = (\n\t\t\tpluginKey,\n\t\t\tdecl,\n\t\t\thooks,\n\t\t) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst seq = yield* Ref.updateAndGet(seqRef, (n) => n + 1);\n\t\t\t\tconst declIdentity = identityHookFromDecl(pluginKey, decl);\n\t\t\t\tconst capture: SnapshotParticipant = {\n\t\t\t\t\tplugin: pluginKey,\n\t\t\t\t\tdecl,\n\t\t\t\t\tcaptureIdentity: hooks?.captureIdentity ?? declIdentity,\n\t\t\t\t\tcaptureContribution: hooks?.captureContribution ?? Effect.succeed<unknown>(undefined),\n\t\t\t\t};\n\t\t\t\tconst restore: RestoreParticipant = {\n\t\t\t\t\tplugin: pluginKey,\n\t\t\t\t\tliveIdentity: hooks?.liveIdentity ?? declIdentity,\n\t\t\t\t\tpostRestore: decl.postRestore as Effect.Effect<void> | undefined,\n\t\t\t\t};\n\t\t\t\tconst entry: RegisteredParticipantEntry = { pluginKey, capture, restore, seq };\n\t\t\t\tyield* Ref.update(participantsRef, (xs) => [...xs, entry]);\n\t\t\t\tyield* Effect.addFinalizer(() =>\n\t\t\t\t\tRef.update(participantsRef, (xs) => xs.filter((e) => e.seq !== seq)),\n\t\t\t\t);\n\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t'snapshot.participant.plugin': pluginKey,\n\t\t\t\t\t'snapshot.participant.subtrees': decl.subtrees.length,\n\t\t\t\t});\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.registerParticipant')) as Effect.Effect<\n\t\t\t\tvoid,\n\t\t\t\tnever,\n\t\t\t\tScope.Scope\n\t\t\t>;\n\n\t\tconst capture: SnapshotOrchestrator['capture'] = ({ id, label, participants, onProgress }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst snapshotId =\n\t\t\t\t\tid === undefined ? yield* mintSnapshotId() : yield* validateSnapshotId('capture', id);\n\t\t\t\tconst snapshotName = yield* normalizeSnapshotName('capture', label);\n\t\t\t\tconst effectiveParticipants =\n\t\t\t\t\tparticipants ?? (yield* Ref.get(participantsRef)).map((e) => e.capture);\n\t\t\t\tconst artifactDir = `${paths.snapshotDir}/${snapshotId}`;\n\t\t\t\t// Snapshot stages at the SNAPSHOT-DIR level (siblings of\n\t\t\t\t// other artifact dirs, NOT siblings of `artifactDir`) so\n\t\t\t\t// the `list` walker's `.staging.` / `.bak.` prefix skip\n\t\t\t\t// at line 517 keeps transient dirs invisible without\n\t\t\t\t// having to descend a level. This layout is structurally\n\t\t\t\t// distinct from `${target}.staging.<id>` so we pass\n\t\t\t\t// explicit `stagingPath`/`backupPath` instead of\n\t\t\t\t// `idSuffix` (the substrate primitive supports both\n\t\t\t\t// shapes — see `stageAndSwap`'s union arg).\n\t\t\t\tconst stagingDir = `${paths.snapshotDir}/.staging.${snapshotId}`;\n\t\t\t\tconst backupDir = `${paths.snapshotDir}/.bak.${snapshotId}`;\n\n\t\t\t\t// Acquire `snapshot.reservation` (O_EXCL, no retry) for the\n\t\t\t\t// duration of the capture. The stack.lock is acquired\n\t\t\t\t// briefly inside `stageAndSwap` for the rename publish.\n\t\t\t\treturn yield* Effect.scoped(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* acquireReservation(paths.snapshotReservationFile, ownStartTime());\n\t\t\t\t\t\tconst fs = yield* FileSystem.FileSystem;\n\t\t\t\t\t\tconst targetExists = yield* fs\n\t\t\t\t\t\t\t.exists(artifactDir)\n\t\t\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\t\t\t\tif (targetExists) {\n\t\t\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\t\t\tnew SnapshotIdError({\n\t\t\t\t\t\t\t\t\toperation: 'capture',\n\t\t\t\t\t\t\t\t\tfield: 'id',\n\t\t\t\t\t\t\t\t\tvalue: snapshotId,\n\t\t\t\t\t\t\t\t\tdetail: 'snapshot id already exists',\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// O(N) catalog scan for label uniqueness — acceptable\n\t\t\t\t\t\t// because N (snapshot count per stack) is small in\n\t\t\t\t\t\t// practice and capture is a rare, human-paced verb. A\n\t\t\t\t\t\t// future optimization could maintain a sibling name-\n\t\t\t\t\t\t// index (e.g. `${snapshotDir}/.names/${label}` empty\n\t\t\t\t\t\t// marker files) that lets uniqueness collapse to a\n\t\t\t\t\t\t// single `fs.exists`, but that would need a paired\n\t\t\t\t\t\t// cleanup-on-delete pathway + a corruption-tolerance\n\t\t\t\t\t\t// story for the sidecar, which is outside this scope.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// Forward-reference safety: `list` is declared LATER\n\t\t\t\t\t\t// in this outer `Effect.gen` body. No TDZ fires\n\t\t\t\t\t\t// because `capture` is an arrow function returned\n\t\t\t\t\t\t// from the gen — by the time any caller invokes it,\n\t\t\t\t\t\t// the outer gen has fully resolved and all `const`\n\t\t\t\t\t\t// bindings (including `list`) are bound. The\n\t\t\t\t\t\t// regression guard at\n\t\t\t\t\t\t// test/orchestrators/snapshot/capture-collision-tdz.test.ts\n\t\t\t\t\t\t// pins this; if a future refactor wraps `list` in\n\t\t\t\t\t\t// an `Effect.fn` decorator or extracts it through a\n\t\t\t\t\t\t// hoisting-sensitive path, the guard will fail.\n\t\t\t\t\t\tconst existing = yield* list;\n\t\t\t\t\t\tif (existing.some((entry) => entry.metadata?.label === snapshotName)) {\n\t\t\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\t\t\tnew SnapshotIdError({\n\t\t\t\t\t\t\t\t\toperation: 'capture',\n\t\t\t\t\t\t\t\t\tfield: 'name',\n\t\t\t\t\t\t\t\t\tvalue: snapshotName,\n\t\t\t\t\t\t\t\t\tdetail: 'snapshot name already exists',\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Stage-and-swap publishes the artifact directory\n\t\t\t\t\t\t// atomically — external watchers (and `list`)\n\t\t\t\t\t\t// never observe a half-written tree.\n\t\t\t\t\t\tconst meta = yield* stageAndSwap({\n\t\t\t\t\t\t\ttargetPath: artifactDir,\n\t\t\t\t\t\t\tstagingPath: stagingDir,\n\t\t\t\t\t\t\tbackupPath: backupDir,\n\t\t\t\t\t\t\tbuild: runCapture({\n\t\t\t\t\t\t\t\tstagingDir,\n\t\t\t\t\t\t\t\tsnapshotId,\n\t\t\t\t\t\t\t\tlabel: snapshotName,\n\t\t\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\t\t\tnetwork: identity.chain,\n\t\t\t\t\t\t\t\truntimeStackRoot: paths.stackRoot,\n\t\t\t\t\t\t\t\tstateFilePath: paths.stateFile,\n\t\t\t\t\t\t\t\tparticipants: effectiveParticipants,\n\t\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\t\tonProgress,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn meta;\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.capture.entry'));\n\n\t\tconst restore: SnapshotOrchestrator['restore'] = ({ id, participants }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst snapshotId = yield* validateSnapshotId('restore', id);\n\t\t\t\tconst artifactDir = `${paths.snapshotDir}/${snapshotId}`;\n\t\t\t\tconst effectiveParticipants =\n\t\t\t\t\tparticipants ?? (yield* Ref.get(participantsRef)).map((e) => e.restore);\n\t\t\t\treturn yield* Effect.scoped(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* acquireReservation(paths.snapshotReservationFile, ownStartTime());\n\t\t\t\t\t\treturn yield* runRestore({\n\t\t\t\t\t\t\tsnapshotId,\n\t\t\t\t\t\t\tartifactDir,\n\t\t\t\t\t\t\truntimeStackRoot: paths.stackRoot,\n\t\t\t\t\t\t\truntimeStagingPath: `${paths.stackRoot}.staging.${snapshotId}`,\n\t\t\t\t\t\t\truntimeBackupPath: `${paths.stackRoot}.bak.${snapshotId}`,\n\t\t\t\t\t\t\tparticipants: effectiveParticipants,\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\truntimeIdentity: {\n\t\t\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\t\t\tnetwork: identity.chain,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.restore.entry'));\n\n\t\tconst list: SnapshotOrchestrator['list'] = Effect.gen(function* () {\n\t\t\tconst fs = yield* FileSystem.FileSystem;\n\t\t\tconst exists = yield* fs\n\t\t\t\t.exists(paths.snapshotDir)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\tif (!exists) return [];\n\t\t\tconst ids = yield* fs\n\t\t\t\t.readDirectory(paths.snapshotDir)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed([] as ReadonlyArray<string>)));\n\t\t\tconst entries: SnapshotCatalogEntry[] = [];\n\t\t\tfor (const id of ids) {\n\t\t\t\t// Skip transient staging/backup directories — they're\n\t\t\t\t// invisible to the catalog by construction (the\n\t\t\t\t// `.staging.<id>` / `.bak.<id>` naming).\n\t\t\t\tif (id.startsWith('.staging.') || id.startsWith('.bak.')) continue;\n\t\t\t\tconst parsedId = parseSnapshotId(id);\n\t\t\t\tif (parsedId === null) {\n\t\t\t\t\tyield* Effect.logWarning(`ignoring unsafe snapshot catalog entry: ${id}`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst dir = `${paths.snapshotDir}/${id}`;\n\t\t\t\tconst metaPath = `${dir}/${SnapshotLayout.metaFile}`;\n\t\t\t\tconst metaExists = yield* fs\n\t\t\t\t\t.exists(metaPath)\n\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\t\tif (!metaExists) {\n\t\t\t\t\tentries.push({ id, directory: dir, metadata: null });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst text = yield* fs\n\t\t\t\t\t.readFileString(metaPath)\n\t\t\t\t\t.pipe(Effect.catch(() => Effect.succeed('')));\n\t\t\t\tif (text === '') {\n\t\t\t\t\tentries.push({ id, directory: dir, metadata: null });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst decoded = yield* decodeJsonText(SnapshotMetadataSchema, text, {\n\t\t\t\t\tsource: metaPath,\n\t\t\t\t\tmkError: () => null,\n\t\t\t\t}).pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\t\tif (decoded !== null && parseSnapshotId(decoded.id) === null) {\n\t\t\t\t\tyield* Effect.logWarning(`ignoring snapshot metadata with unsafe id: ${decoded.id}`);\n\t\t\t\t\tentries.push({ id, directory: dir, metadata: null });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tentries.push({ id: parsedId, directory: dir, metadata: decoded });\n\t\t\t}\n\t\t\treturn entries;\n\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.list'));\n\n\t\tconst del: SnapshotOrchestrator['delete'] = (id) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst snapshotId = yield* validateSnapshotId('delete', id);\n\t\t\t\tconst fs = yield* FileSystem.FileSystem;\n\t\t\t\tconst dir = `${paths.snapshotDir}/${snapshotId}`;\n\t\t\t\tyield* Effect.scoped(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* acquireStackLock(paths.stackLockFile);\n\t\t\t\t\t\tyield* fs.remove(dir, { recursive: true, force: true }).pipe(Effect.ignore);\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.delete'));\n\n\t\tconst wipe: SnapshotOrchestrator['wipe'] = (args) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tyield* Effect.scoped(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* acquireStackLock(paths.stackLockFile);\n\t\t\t\t\t\tyield* runWipe({\n\t\t\t\t\t\t\tlabelMatch: { app: identity.app, stack: identity.stack },\n\t\t\t\t\t\t\tstackRoot: paths.stackRoot,\n\t\t\t\t\t\t\tstateFilePath: paths.stateFile,\n\t\t\t\t\t\t\truntime,\n\t\t\t\t\t\t\tkeepSnapshots: args.keepSnapshots,\n\t\t\t\t\t\t\tkeepCache: args.keepCache,\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.wipe.entry'));\n\n\t\tconst wipePlan: SnapshotOrchestrator['wipePlan'] = (args) =>\n\t\t\t// Read-only: no stack-lock / reservation. `planWipe` only lists\n\t\t\t// matching containers and reads the stack-root directory, so it\n\t\t\t// is safe to run without serializing against peers (a concurrent\n\t\t\t// mutation just makes the preview slightly stale — acceptable for\n\t\t\t// a dry-run estimate).\n\t\t\tplanWipe({\n\t\t\t\tlabelMatch: { app: identity.app, stack: identity.stack },\n\t\t\t\tstackRoot: paths.stackRoot,\n\t\t\t\tstateFilePath: paths.stateFile,\n\t\t\t\truntime,\n\t\t\t\tkeepSnapshots: args.keepSnapshots,\n\t\t\t\tkeepCache: args.keepCache,\n\t\t\t}).pipe(Effect.withSpan('orchestrator.snapshot.wipe.plan.entry'));\n\n\t\tconst prune: SnapshotOrchestrator['prune'] = () =>\n\t\t\tEffect.scoped(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* acquireReservation(paths.snapshotReservationFile, ownStartTime());\n\t\t\t\t\treturn yield* runPrune({\n\t\t\t\t\t\tstackRoot: paths.stackRoot,\n\t\t\t\t\t\timageLabelFilter: { app: identity.app, stack: identity.stack },\n\t\t\t\t\t\truntime,\n\t\t\t\t\t});\n\t\t\t\t}),\n\t\t\t).pipe(Effect.withSpan('orchestrator.snapshot.prune.entry'));\n\n\t\tconst recoverPendingRestoreImpl: SnapshotOrchestrator['recoverPendingRestore'] =\n\t\t\trecoverPendingRestore(paths.stackRoot, runtime).pipe(\n\t\t\t\tEffect.withSpan('orchestrator.snapshot.recover-pending.entry'),\n\t\t\t);\n\n\t\treturn SnapshotOrchestratorService.of({\n\t\t\tregisterParticipant,\n\t\t\tcapture,\n\t\t\trestore,\n\t\t\tlist,\n\t\t\tdelete: del,\n\t\t\twipe,\n\t\t\twipePlan,\n\t\t\tprune,\n\t\t\trecoverPendingRestore: recoverPendingRestoreImpl,\n\t\t});\n\t}),\n);\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ import { Effect, FileSystem, Schema } from "effect";
@@ -1,18 +1,58 @@
1
+ import { decodeUnknown, parseJsonText } from "../../substrate/runtime/runtime-decode.mjs";
1
2
  import { StateDocument } from "../../substrate/runtime/state-store/schema.mjs";
3
+ import "../../substrate/runtime/state-store/index.mjs";
2
4
  import { Effect, FileSystem, Schema } from "effect";
3
5
  //#region src/orchestrators/snapshot/state-document.ts
6
+ /** Tagged failure raised by snapshot state-document helpers. `kind`
7
+ * discriminates the failure class so downstream phase classifiers can
8
+ * branch by tag, not by message substring. */
9
+ var SnapshotStateDocumentError = class extends Schema.TaggedErrorClass()("SnapshotStateDocumentError", {
10
+ /**
11
+ * - `'read'` — filesystem read of the state document file failed.
12
+ * - `'parse'` — file contents are not valid JSON.
13
+ * - `'decode'` — JSON parses but does not match `StateDocument`
14
+ * schema.
15
+ * - `'write'` — filesystem write of the state document file failed.
16
+ */
17
+ kind: Schema.Literals([
18
+ "read",
19
+ "parse",
20
+ "decode",
21
+ "write"
22
+ ]),
23
+ detail: Schema.String,
24
+ path: Schema.String,
25
+ cause: Schema.optional(Schema.Defect)
26
+ }) {};
27
+ const fail = (kind, detail, path, cause) => Effect.fail(new SnapshotStateDocumentError({
28
+ kind,
29
+ detail,
30
+ path,
31
+ cause
32
+ }));
4
33
  const readSnapshotStateDocument = (path) => Effect.gen(function* () {
5
- const text = yield* (yield* FileSystem.FileSystem).readFileString(path).pipe(Effect.catch((cause) => Effect.fail(new Error(`read ${path} failed`, { cause }))));
6
- const raw = yield* Effect.try({
7
- try: () => JSON.parse(text),
8
- catch: (cause) => new Error(`${path} is not valid JSON`, { cause })
34
+ return yield* decodeUnknown(StateDocument, yield* parseJsonText(yield* (yield* FileSystem.FileSystem).readFileString(path).pipe(Effect.catch((cause) => fail("read", `read ${path} failed`, path, cause))), {
35
+ source: path,
36
+ mkError: (issue) => new SnapshotStateDocumentError({
37
+ kind: "parse",
38
+ detail: `${path} is not valid JSON`,
39
+ path,
40
+ cause: issue.cause
41
+ })
42
+ }), {
43
+ source: path,
44
+ mkError: (issue) => new SnapshotStateDocumentError({
45
+ kind: "decode",
46
+ detail: `${path} failed StateDocument schema decode`,
47
+ path,
48
+ cause: issue.cause
49
+ })
9
50
  });
10
- return yield* Schema.decodeUnknownEffect(StateDocument)(raw).pipe(Effect.catch((cause) => Effect.fail(new Error(`${path} failed StateDocument schema decode`, { cause }))));
11
51
  });
12
52
  const writeSnapshotStateDocument = (path, doc) => Effect.gen(function* () {
13
- yield* (yield* FileSystem.FileSystem).writeFileString(path, `${JSON.stringify(doc, null, 2)}\n`).pipe(Effect.catch((cause) => Effect.fail(new Error(`write ${path} failed`, { cause }))));
53
+ yield* (yield* FileSystem.FileSystem).writeFileString(path, `${JSON.stringify(doc, null, 2)}\n`).pipe(Effect.catch((cause) => fail("write", `write ${path} failed`, path, cause)));
14
54
  });
15
55
  //#endregion
16
- export { readSnapshotStateDocument, writeSnapshotStateDocument };
56
+ export { SnapshotStateDocumentError, readSnapshotStateDocument, writeSnapshotStateDocument };
17
57
 
18
58
  //# sourceMappingURL=state-document.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-document.mjs","names":["StateDocumentSchema"],"sources":["../../../src/orchestrators/snapshot/state-document.ts"],"sourcesContent":["import { Effect, FileSystem, Schema } from 'effect';\n\nimport {\n\tStateDocument as StateDocumentSchema,\n\ttype StateDocument,\n} from '../../substrate/runtime/state-store/index.ts';\n\nexport const readSnapshotStateDocument = (\n\tpath: string,\n): Effect.Effect<StateDocument, Error, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst text = yield* fs\n\t\t\t.readFileString(path)\n\t\t\t.pipe(Effect.catch((cause) => Effect.fail(new Error(`read ${path} failed`, { cause }))));\n\t\tconst raw = yield* Effect.try({\n\t\t\ttry: () => JSON.parse(text) as unknown,\n\t\t\tcatch: (cause) => new Error(`${path} is not valid JSON`, { cause }),\n\t\t});\n\t\treturn yield* Schema.decodeUnknownEffect(StateDocumentSchema)(raw).pipe(\n\t\t\tEffect.catch((cause) =>\n\t\t\t\tEffect.fail(new Error(`${path} failed StateDocument schema decode`, { cause })),\n\t\t\t),\n\t\t);\n\t});\n\nexport const writeSnapshotStateDocument = (\n\tpath: string,\n\tdoc: StateDocument,\n): Effect.Effect<void, Error, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tyield* fs\n\t\t\t.writeFileString(path, `${JSON.stringify(doc, null, 2)}\\n`)\n\t\t\t.pipe(Effect.catch((cause) => Effect.fail(new Error(`write ${path} failed`, { cause }))));\n\t});\n"],"mappings":";;;AAOA,MAAa,6BACZ,SAEA,OAAO,IAAI,aAAa;CAEvB,MAAM,OAAO,QAAO,OADF,WAAW,YAE3B,eAAe,KAAK,CACpB,KAAK,OAAO,OAAO,UAAU,OAAO,KAAK,IAAI,MAAM,QAAQ,KAAK,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CACzF,MAAM,MAAM,OAAO,OAAO,IAAI;EAC7B,WAAW,KAAK,MAAM,KAAK;EAC3B,QAAQ,UAAU,IAAI,MAAM,GAAG,KAAK,qBAAqB,EAAE,OAAO,CAAC;EACnE,CAAC;AACF,QAAO,OAAO,OAAO,oBAAoBA,cAAoB,CAAC,IAAI,CAAC,KAClE,OAAO,OAAO,UACb,OAAO,KAAK,IAAI,MAAM,GAAG,KAAK,sCAAsC,EAAE,OAAO,CAAC,CAAC,CAC/E,CACD;EACA;AAEH,MAAa,8BACZ,MACA,QAEA,OAAO,IAAI,aAAa;AAEvB,SAAO,OADW,WAAW,YAE3B,gBAAgB,MAAM,GAAG,KAAK,UAAU,KAAK,MAAM,EAAE,CAAC,IAAI,CAC1D,KAAK,OAAO,OAAO,UAAU,OAAO,KAAK,IAAI,MAAM,SAAS,KAAK,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;EACzF"}
1
+ {"version":3,"file":"state-document.mjs","names":["StateDocumentSchema"],"sources":["../../../src/orchestrators/snapshot/state-document.ts"],"sourcesContent":["import { Effect, FileSystem, Schema } from 'effect';\n\nimport { decodeUnknown, parseJsonText } from '../../substrate/runtime/runtime-decode.ts';\nimport {\n\tStateDocument as StateDocumentSchema,\n\ttype StateDocument,\n} from '../../substrate/runtime/state-store/index.ts';\n\n/** Tagged failure raised by snapshot state-document helpers. `kind`\n * discriminates the failure class so downstream phase classifiers can\n * branch by tag, not by message substring. */\nexport class SnapshotStateDocumentError extends Schema.TaggedErrorClass<SnapshotStateDocumentError>()(\n\t'SnapshotStateDocumentError',\n\t{\n\t\t/**\n\t\t * - `'read'` — filesystem read of the state document file failed.\n\t\t * - `'parse'` — file contents are not valid JSON.\n\t\t * - `'decode'` — JSON parses but does not match `StateDocument`\n\t\t * schema.\n\t\t * - `'write'` — filesystem write of the state document file failed.\n\t\t */\n\t\tkind: Schema.Literals(['read', 'parse', 'decode', 'write']),\n\t\tdetail: Schema.String,\n\t\tpath: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\nconst fail = (\n\tkind: SnapshotStateDocumentError['kind'],\n\tdetail: string,\n\tpath: string,\n\tcause?: unknown,\n): Effect.Effect<never, SnapshotStateDocumentError> =>\n\tEffect.fail(new SnapshotStateDocumentError({ kind, detail, path, cause }));\n\nexport const readSnapshotStateDocument = (\n\tpath: string,\n): Effect.Effect<StateDocument, SnapshotStateDocumentError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst text = yield* fs\n\t\t\t.readFileString(path)\n\t\t\t.pipe(Effect.catch((cause) => fail('read', `read ${path} failed`, path, cause)));\n\t\tconst raw = yield* parseJsonText(text, {\n\t\t\tsource: path,\n\t\t\tmkError: (issue) =>\n\t\t\t\tnew SnapshotStateDocumentError({\n\t\t\t\t\tkind: 'parse',\n\t\t\t\t\tdetail: `${path} is not valid JSON`,\n\t\t\t\t\tpath,\n\t\t\t\t\tcause: issue.cause,\n\t\t\t\t}),\n\t\t});\n\t\treturn yield* decodeUnknown(StateDocumentSchema, raw, {\n\t\t\tsource: path,\n\t\t\tmkError: (issue) =>\n\t\t\t\tnew SnapshotStateDocumentError({\n\t\t\t\t\tkind: 'decode',\n\t\t\t\t\tdetail: `${path} failed StateDocument schema decode`,\n\t\t\t\t\tpath,\n\t\t\t\t\tcause: issue.cause,\n\t\t\t\t}),\n\t\t});\n\t});\n\nexport const writeSnapshotStateDocument = (\n\tpath: string,\n\tdoc: StateDocument,\n): Effect.Effect<void, SnapshotStateDocumentError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tyield* fs\n\t\t\t.writeFileString(path, `${JSON.stringify(doc, null, 2)}\\n`)\n\t\t\t.pipe(Effect.catch((cause) => fail('write', `write ${path} failed`, path, cause)));\n\t});\n"],"mappings":";;;;;;;;AAWA,IAAa,6BAAb,cAAgD,OAAO,kBAA8C,CACpG,8BACA;;;;;;;;CAQC,MAAM,OAAO,SAAS;EAAC;EAAQ;EAAS;EAAU;EAAQ,CAAC;CAC3D,QAAQ,OAAO;CACf,MAAM,OAAO;CACb,OAAO,OAAO,SAAS,OAAO,OAAO;CACrC,CACD,CAAC;AAEF,MAAM,QACL,MACA,QACA,MACA,UAEA,OAAO,KAAK,IAAI,2BAA2B;CAAE;CAAM;CAAQ;CAAM;CAAO,CAAC,CAAC;AAE3E,MAAa,6BACZ,SAEA,OAAO,IAAI,aAAa;AAevB,QAAO,OAAO,cAAcA,eAAqB,OAV9B,cAAc,QAHb,OADF,WAAW,YAE3B,eAAe,KAAK,CACpB,KAAK,OAAO,OAAO,UAAU,KAAK,QAAQ,QAAQ,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC,EAC1C;EACtC,QAAQ;EACR,UAAU,UACT,IAAI,2BAA2B;GAC9B,MAAM;GACN,QAAQ,GAAG,KAAK;GAChB;GACA,OAAO,MAAM;GACb,CAAC;EACH,CAAC,EACoD;EACrD,QAAQ;EACR,UAAU,UACT,IAAI,2BAA2B;GAC9B,MAAM;GACN,QAAQ,GAAG,KAAK;GAChB;GACA,OAAO,MAAM;GACb,CAAC;EACH,CAAC;EACD;AAEH,MAAa,8BACZ,MACA,QAEA,OAAO,IAAI,aAAa;AAEvB,SAAO,OADW,WAAW,YAE3B,gBAAgB,MAAM,GAAG,KAAK,UAAU,KAAK,MAAM,EAAE,CAAC,IAAI,CAC1D,KAAK,OAAO,OAAO,UAAU,KAAK,SAAS,SAAS,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;EAClF"}
@@ -1,3 +1,4 @@
1
+ import { makePhaseFailer } from "./phase-error.mjs";
1
2
  import { Effect, FileSystem, Schema } from "effect";
2
3
  //#region src/orchestrators/snapshot/wipe.ts
3
4
  var WipePhaseError = class extends Schema.TaggedErrorClass()("SnapshotWipePhaseError", {
@@ -10,11 +11,54 @@ var WipePhaseError = class extends Schema.TaggedErrorClass()("SnapshotWipePhaseE
10
11
  detail: Schema.String,
11
12
  cause: Schema.optional(Schema.Defect)
12
13
  }) {};
13
- const failPhase = (phase, detail) => (cause) => Effect.fail(new WipePhaseError({
14
- phase,
15
- detail,
16
- cause
17
- }));
14
+ const failPhase = makePhaseFailer(WipePhaseError);
15
+ const resolvePreservePolicy = (inputs) => ({
16
+ keepSnapshots: inputs.keepSnapshots ?? true,
17
+ keepCache: inputs.keepCache ?? false
18
+ });
19
+ /** True when a direct child of `stackRoot` is PRESERVED (not removed)
20
+ * by a wipe under `policy`. `snapshots/` survives by default; `cache/`
21
+ * survives only when explicitly requested. Every other child — state,
22
+ * cross-process artifacts, per-plugin runtime trees — is removed. */
23
+ const isPreservedChild = (name, policy) => policy.keepSnapshots && name === "snapshots" || policy.keepCache && name === "cache";
24
+ /**
25
+ * Enumerate the concrete targets a wipe of `inputs.labelMatch` would
26
+ * remove, WITHOUT removing anything. Read-only: lists matching
27
+ * containers via the runtime adapter and reads the stack-root directory
28
+ * to classify each child against the SAME preserve predicate `runWipe`
29
+ * uses. Backs `devstack wipe --dry-run`.
30
+ */
31
+ const planWipe = (inputs) => Effect.gen(function* () {
32
+ const fs = yield* FileSystem.FileSystem;
33
+ yield* Effect.annotateCurrentSpan({
34
+ "devstack.snapshot.phase": "wipe-plan",
35
+ "devstack.app": inputs.labelMatch.app,
36
+ "devstack.stack": inputs.labelMatch.stack
37
+ });
38
+ const containers = (yield* inputs.runtime.inspectByLabels(inputs.labelMatch).pipe(Effect.catch(failPhase("sweep-containers", `container inspect failed`)))).map((h) => h.name).sort();
39
+ const policy = resolvePreservePolicy(inputs);
40
+ const children = yield* fs.readDirectory(inputs.stackRoot).pipe(Effect.catch(() => Effect.succeed([])));
41
+ const onDiskPaths = [];
42
+ const preserved = [];
43
+ for (const name of [...children].sort()) {
44
+ if (isPreservedChild(name, policy)) {
45
+ preserved.push(name);
46
+ continue;
47
+ }
48
+ onDiskPaths.push(`${inputs.stackRoot}/${name}`);
49
+ }
50
+ return {
51
+ app: inputs.labelMatch.app,
52
+ stack: inputs.labelMatch.stack,
53
+ containers,
54
+ networkLabelMatch: inputs.labelMatch,
55
+ volumeLabelMatch: inputs.labelMatch,
56
+ stateFile: inputs.stateFilePath,
57
+ stackRoot: inputs.stackRoot,
58
+ onDiskPaths,
59
+ preserved
60
+ };
61
+ }).pipe(Effect.withSpan("orchestrator.snapshot.wipe.plan"));
18
62
  /**
19
63
  * Tear down a stack's live footprint. Snapshots survive by default
20
64
  * (architecture § wipe).
@@ -24,6 +68,9 @@ const failPhase = (phase, detail) => (cause) => Effect.fail(new WipePhaseError({
24
68
  * 2. Remove managed networks and volumes by the same label filter.
25
69
  * 3. Remove state.json.
26
70
  * 4. Remove the runtime tree EXCEPT the snapshot catalog by default.
71
+ * 5. Remove the now-empty stack root when nothing survived (no
72
+ * preserved child remains) so wipe doesn't leak an empty
73
+ * `stacks/<stack>/` directory.
27
74
  */
28
75
  const runWipe = (inputs) => Effect.gen(function* () {
29
76
  const fs = yield* FileSystem.FileSystem;
@@ -36,24 +83,34 @@ const runWipe = (inputs) => Effect.gen(function* () {
36
83
  yield* inputs.runtime.removeManagedNetworks(inputs.labelMatch).pipe(Effect.catch(failPhase("sweep-networks-volumes", `network sweep failed`)));
37
84
  yield* inputs.runtime.removeManagedVolumes(inputs.labelMatch).pipe(Effect.catch(failPhase("sweep-networks-volumes", `volume sweep failed`)));
38
85
  yield* fs.remove(inputs.stateFilePath, { force: true }).pipe(Effect.catch(failPhase("remove-state", `remove state.json failed`)));
86
+ const policy = resolvePreservePolicy(inputs);
39
87
  const children = yield* fs.readDirectory(inputs.stackRoot).pipe(Effect.catch(() => Effect.succeed([])));
40
- const keepSnapshots = inputs.keepSnapshots ?? true;
41
- const keepCache = inputs.keepCache ?? false;
88
+ let preservedCount = 0;
42
89
  for (const name of children) {
43
- if (keepSnapshots && name === "snapshots") continue;
44
- if (keepCache && name === "cache") continue;
90
+ if (isPreservedChild(name, policy)) {
91
+ preservedCount += 1;
92
+ continue;
93
+ }
45
94
  yield* fs.remove(`${inputs.stackRoot}/${name}`, {
46
95
  recursive: true,
47
96
  force: true
48
97
  }).pipe(Effect.catch(failPhase("remove-runtime-tree", `remove ${name} failed`)));
49
98
  }
99
+ if (preservedCount === 0 && children.length > 0) yield* fs.remove(inputs.stackRoot, {
100
+ recursive: true,
101
+ force: true
102
+ }).pipe(Effect.ignore);
50
103
  }).pipe(Effect.withSpan("orchestrator.snapshot.wipe"));
51
104
  /** Centralized constant — the canonical snapshot-catalog directory
52
105
  * name. Used by both `runWipe` (to preserve) and the substrate's
53
106
  * path resolver (to compose `snapshotDir`). Distilled §17 calls out
54
107
  * the "centralize the snapshots-dir-name constant" opportunity. */
55
108
  const SNAPSHOTS_DIR_NAME = "snapshots";
109
+ /** Canonical stack-local artifact-cache directory name. `runWipe`
110
+ * preserves it only under `keepCache`; otherwise it is removed so a
111
+ * reset re-proves on-chain artifacts against the next chain. */
112
+ const CACHE_DIR_NAME = "cache";
56
113
  //#endregion
57
- export { SNAPSHOTS_DIR_NAME, runWipe };
114
+ export { CACHE_DIR_NAME, SNAPSHOTS_DIR_NAME, WipePhaseError, planWipe, runWipe };
58
115
 
59
116
  //# sourceMappingURL=wipe.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"wipe.mjs","names":[],"sources":["../../../src/orchestrators/snapshot/wipe.ts"],"sourcesContent":["// Wipe — label-scoped teardown.\n//\n// Architecture § Snapshot responsibilities:\n// \"Provide a wipe operation scoped to one `(app, stack)` that tears\n// down containers, networks, volumes, and per-stack on-disk state,\n// with snapshots surviving by default.\"\n//\n// Label-scoped: enumeration uses partial `ContainerLabelTuple` filters\n// (just `{ app, stack }`); the orchestrator does NOT reach for plugin\n// names. The runtime adapter sweeps containers/networks/volumes\n// matching the label set.\n\nimport { Effect, FileSystem, Schema } from 'effect';\n\nimport type { ContainerLabelTuple } from '../../contracts/snapshotable.ts';\nimport type { ContainerRuntime } from '../../contracts/container-runtime.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\nexport class WipePhaseError extends Schema.TaggedErrorClass<WipePhaseError>()(\n\t'SnapshotWipePhaseError',\n\t{\n\t\tphase: Schema.Literals([\n\t\t\t'sweep-containers',\n\t\t\t'sweep-networks-volumes',\n\t\t\t'remove-state',\n\t\t\t'remove-runtime-tree',\n\t\t]),\n\t\tdetail: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\nconst failPhase =\n\t(\n\t\tphase: WipePhaseError['phase'],\n\t\tdetail: string,\n\t): ((cause: unknown) => Effect.Effect<never, WipePhaseError>) =>\n\t(cause) =>\n\t\tEffect.fail(new WipePhaseError({ phase, detail, cause }));\n\n// -----------------------------------------------------------------------------\n// Inputs\n// -----------------------------------------------------------------------------\n\nexport interface WipeInputs {\n\treadonly labelMatch: Pick<ContainerLabelTuple, 'app' | 'stack'>;\n\treadonly stackRoot: string;\n\treadonly stateFilePath: string;\n\treadonly runtime: ContainerRuntime;\n\t/** Preserve the snapshot catalog (default behavior). When false,\n\t * the catalog is removed alongside the runtime tree. */\n\treadonly keepSnapshots?: boolean;\n\t/** Preserve stack-local artifact caches. Defaults to false; wipe\n\t * should force on-chain artifacts to re-prove against the next\n\t * chain instead of carrying local ids across a reset. */\n\treadonly keepCache?: boolean;\n}\n\n// -----------------------------------------------------------------------------\n// Wipe\n// -----------------------------------------------------------------------------\n\n/**\n * Tear down a stack's live footprint. Snapshots survive by default\n * (architecture § wipe).\n *\n * Order:\n * 1. Force-remove managed containers by `{ app, stack }` labels.\n * 2. Remove managed networks and volumes by the same label filter.\n * 3. Remove state.json.\n * 4. Remove the runtime tree EXCEPT the snapshot catalog by default.\n */\nexport const runWipe = (\n\tinputs: WipeInputs,\n): Effect.Effect<void, WipePhaseError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t'devstack.snapshot.phase': 'wipe',\n\t\t\t'devstack.app': inputs.labelMatch.app,\n\t\t\t'devstack.stack': inputs.labelMatch.stack,\n\t\t});\n\n\t\t// 1. Containers.\n\t\tyield* inputs.runtime\n\t\t\t.removeManagedContainers(inputs.labelMatch)\n\t\t\t.pipe(Effect.catch(failPhase('sweep-containers', `container sweep failed`)));\n\n\t\t// 2. Networks + volumes.\n\t\tyield* inputs.runtime\n\t\t\t.removeManagedNetworks(inputs.labelMatch)\n\t\t\t.pipe(Effect.catch(failPhase('sweep-networks-volumes', `network sweep failed`)));\n\t\tyield* inputs.runtime\n\t\t\t.removeManagedVolumes(inputs.labelMatch)\n\t\t\t.pipe(Effect.catch(failPhase('sweep-networks-volumes', `volume sweep failed`)));\n\n\t\t// 3. Remove state.json.\n\t\tyield* fs\n\t\t\t.remove(inputs.stateFilePath, { force: true })\n\t\t\t.pipe(Effect.catch(failPhase('remove-state', `remove state.json failed`)));\n\n\t\t// 4. Remove the runtime tree — but PRESERVE snapshots by default.\n\t\t// Strategy: enumerate the stack root and remove each child\n\t\t// whose name is not `snapshots/` (when preserved). Stack-local\n\t\t// artifact caches are state and are removed unless explicitly\n\t\t// requested otherwise.\n\t\tconst children = yield* fs\n\t\t\t.readDirectory(inputs.stackRoot)\n\t\t\t.pipe(Effect.catch(() => Effect.succeed([] as ReadonlyArray<string>)));\n\t\tconst keepSnapshots = inputs.keepSnapshots ?? true;\n\t\tconst keepCache = inputs.keepCache ?? false;\n\t\tfor (const name of children) {\n\t\t\tif (keepSnapshots && name === 'snapshots') continue;\n\t\t\tif (keepCache && name === 'cache') continue;\n\t\t\tyield* fs\n\t\t\t\t.remove(`${inputs.stackRoot}/${name}`, { recursive: true, force: true })\n\t\t\t\t.pipe(Effect.catch(failPhase('remove-runtime-tree', `remove ${name} failed`)));\n\t\t}\n\t}).pipe(Effect.withSpan('orchestrator.snapshot.wipe'));\n\n/** Centralized constant — the canonical snapshot-catalog directory\n * name. Used by both `runWipe` (to preserve) and the substrate's\n * path resolver (to compose `snapshotDir`). Distilled §17 calls out\n * the \"centralize the snapshots-dir-name constant\" opportunity. */\nexport const SNAPSHOTS_DIR_NAME = 'snapshots';\n"],"mappings":";;AAqBA,IAAa,iBAAb,cAAoC,OAAO,kBAAkC,CAC5E,0BACA;CACC,OAAO,OAAO,SAAS;EACtB;EACA;EACA;EACA;EACA,CAAC;CACF,QAAQ,OAAO;CACf,OAAO,OAAO,SAAS,OAAO,OAAO;CACrC,CACD,CAAC;AAEF,MAAM,aAEJ,OACA,YAEA,UACA,OAAO,KAAK,IAAI,eAAe;CAAE;CAAO;CAAQ;CAAO,CAAC,CAAC;;;;;;;;;;;AAkC3D,MAAa,WACZ,WAEA,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;AAC7B,QAAO,OAAO,oBAAoB;EACjC,2BAA2B;EAC3B,gBAAgB,OAAO,WAAW;EAClC,kBAAkB,OAAO,WAAW;EACpC,CAAC;AAGF,QAAO,OAAO,QACZ,wBAAwB,OAAO,WAAW,CAC1C,KAAK,OAAO,MAAM,UAAU,oBAAoB,yBAAyB,CAAC,CAAC;AAG7E,QAAO,OAAO,QACZ,sBAAsB,OAAO,WAAW,CACxC,KAAK,OAAO,MAAM,UAAU,0BAA0B,uBAAuB,CAAC,CAAC;AACjF,QAAO,OAAO,QACZ,qBAAqB,OAAO,WAAW,CACvC,KAAK,OAAO,MAAM,UAAU,0BAA0B,sBAAsB,CAAC,CAAC;AAGhF,QAAO,GACL,OAAO,OAAO,eAAe,EAAE,OAAO,MAAM,CAAC,CAC7C,KAAK,OAAO,MAAM,UAAU,gBAAgB,2BAA2B,CAAC,CAAC;CAO3E,MAAM,WAAW,OAAO,GACtB,cAAc,OAAO,UAAU,CAC/B,KAAK,OAAO,YAAY,OAAO,QAAQ,EAAE,CAA0B,CAAC,CAAC;CACvE,MAAM,gBAAgB,OAAO,iBAAiB;CAC9C,MAAM,YAAY,OAAO,aAAa;AACtC,MAAK,MAAM,QAAQ,UAAU;AAC5B,MAAI,iBAAiB,SAAS,YAAa;AAC3C,MAAI,aAAa,SAAS,QAAS;AACnC,SAAO,GACL,OAAO,GAAG,OAAO,UAAU,GAAG,QAAQ;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC,CACvE,KAAK,OAAO,MAAM,UAAU,uBAAuB,UAAU,KAAK,SAAS,CAAC,CAAC;;EAE/E,CAAC,KAAK,OAAO,SAAS,6BAA6B,CAAC;;;;;AAMvD,MAAa,qBAAqB"}
1
+ {"version":3,"file":"wipe.mjs","names":[],"sources":["../../../src/orchestrators/snapshot/wipe.ts"],"sourcesContent":["// Wipe — label-scoped teardown.\n//\n// Architecture § Snapshot responsibilities:\n// \"Provide a wipe operation scoped to one `(app, stack)` that tears\n// down containers, networks, volumes, and per-stack on-disk state,\n// with snapshots surviving by default.\"\n//\n// Label-scoped: enumeration uses partial `ContainerLabelTuple` filters\n// (just `{ app, stack }`); the orchestrator does NOT reach for plugin\n// names. The runtime adapter sweeps containers/networks/volumes\n// matching the label set.\n\nimport { Effect, FileSystem, Schema } from 'effect';\n\nimport type { ContainerLabelTuple } from '../../contracts/snapshotable.ts';\nimport type { ContainerRuntime } from '../../contracts/container-runtime.ts';\nimport { makePhaseFailer } from './phase-error.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\nexport class WipePhaseError extends Schema.TaggedErrorClass<WipePhaseError>()(\n\t'SnapshotWipePhaseError',\n\t{\n\t\tphase: Schema.Literals([\n\t\t\t'sweep-containers',\n\t\t\t'sweep-networks-volumes',\n\t\t\t'remove-state',\n\t\t\t'remove-runtime-tree',\n\t\t]),\n\t\tdetail: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\nconst failPhase = makePhaseFailer(WipePhaseError);\n\n// -----------------------------------------------------------------------------\n// Inputs\n// -----------------------------------------------------------------------------\n\nexport interface WipeInputs {\n\treadonly labelMatch: Pick<ContainerLabelTuple, 'app' | 'stack'>;\n\treadonly stackRoot: string;\n\treadonly stateFilePath: string;\n\treadonly runtime: ContainerRuntime;\n\t/** Preserve the snapshot catalog (default behavior). When false,\n\t * the catalog is removed alongside the runtime tree. */\n\treadonly keepSnapshots?: boolean;\n\t/** Preserve stack-local artifact caches. Defaults to false; wipe\n\t * should force on-chain artifacts to re-prove against the next\n\t * chain instead of carrying local ids across a reset. */\n\treadonly keepCache?: boolean;\n}\n\n// -----------------------------------------------------------------------------\n// Preserve predicate — the single source of truth for \"what survives a\n// wipe on disk\". Both `runWipe` (the destructive pass) and `planWipe`\n// (the dry-run enumeration) consult this so the preview can never drift\n// from what the real wipe removes.\n// -----------------------------------------------------------------------------\n\ninterface PreservePolicy {\n\treadonly keepSnapshots: boolean;\n\treadonly keepCache: boolean;\n}\n\nconst resolvePreservePolicy = (inputs: WipeInputs): PreservePolicy => ({\n\tkeepSnapshots: inputs.keepSnapshots ?? true,\n\tkeepCache: inputs.keepCache ?? false,\n});\n\n/** True when a direct child of `stackRoot` is PRESERVED (not removed)\n * by a wipe under `policy`. `snapshots/` survives by default; `cache/`\n * survives only when explicitly requested. Every other child — state,\n * cross-process artifacts, per-plugin runtime trees — is removed. */\nconst isPreservedChild = (name: string, policy: PreservePolicy): boolean =>\n\t(policy.keepSnapshots && name === SNAPSHOTS_DIR_NAME) ||\n\t(policy.keepCache && name === CACHE_DIR_NAME);\n\n// -----------------------------------------------------------------------------\n// Dry-run enumeration\n// -----------------------------------------------------------------------------\n\n/** Concrete teardown targets a wipe of one `(app, stack)` would remove.\n * Produced by `planWipe` WITHOUT mutating anything so `devstack wipe\n * --dry-run` can show the operator exactly what a real wipe deletes. */\nexport interface WipeTargets {\n\treadonly app: string;\n\treadonly stack: string;\n\t/** Managed container NAMES that match the `{ app, stack }` label\n\t * filter — enumerated via the runtime adapter so the preview lists\n\t * the exact containers a real wipe force-removes. */\n\treadonly containers: ReadonlyArray<string>;\n\t/** Networks/volumes are removed by the SAME `{ app, stack }` label\n\t * filter the runtime adapter sweeps on. The contract exposes no\n\t * by-label LIST for these, so the plan reports the selector that\n\t * scopes the removal rather than a (daemon-round-trip) name list. */\n\treadonly networkLabelMatch: Pick<ContainerLabelTuple, 'app' | 'stack'>;\n\treadonly volumeLabelMatch: Pick<ContainerLabelTuple, 'app' | 'stack'>;\n\t/** Absolute path of the per-stack `state.json` that is removed. */\n\treadonly stateFile: string;\n\t/** Absolute path of the per-stack runtime root. Its non-preserved\n\t * children (see `onDiskPaths`) are removed; the directory itself is\n\t * reaped too when nothing survives. */\n\treadonly stackRoot: string;\n\t/** Absolute paths of the `stackRoot` children a real wipe removes —\n\t * everything except the preserved `snapshots/` (and `cache/` unless\n\t * `keepCache`). Empty when the stack root does not exist yet. */\n\treadonly onDiskPaths: ReadonlyArray<string>;\n\t/** Direct children PRESERVED on disk (`snapshots/`, optionally\n\t * `cache/`) — surfaced so the preview is explicit about survivors. */\n\treadonly preserved: ReadonlyArray<string>;\n}\n\n/**\n * Enumerate the concrete targets a wipe of `inputs.labelMatch` would\n * remove, WITHOUT removing anything. Read-only: lists matching\n * containers via the runtime adapter and reads the stack-root directory\n * to classify each child against the SAME preserve predicate `runWipe`\n * uses. Backs `devstack wipe --dry-run`.\n */\nexport const planWipe = (\n\tinputs: WipeInputs,\n): Effect.Effect<WipeTargets, WipePhaseError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t'devstack.snapshot.phase': 'wipe-plan',\n\t\t\t'devstack.app': inputs.labelMatch.app,\n\t\t\t'devstack.stack': inputs.labelMatch.stack,\n\t\t});\n\n\t\tconst handles = yield* inputs.runtime\n\t\t\t.inspectByLabels(inputs.labelMatch as ContainerLabelTuple)\n\t\t\t.pipe(Effect.catch(failPhase('sweep-containers', `container inspect failed`)));\n\t\tconst containers = handles.map((h) => h.name).sort();\n\n\t\tconst policy = resolvePreservePolicy(inputs);\n\t\tconst children = yield* fs\n\t\t\t.readDirectory(inputs.stackRoot)\n\t\t\t.pipe(Effect.catch(() => Effect.succeed([] as ReadonlyArray<string>)));\n\t\tconst onDiskPaths: Array<string> = [];\n\t\tconst preserved: Array<string> = [];\n\t\tfor (const name of [...children].sort()) {\n\t\t\tif (isPreservedChild(name, policy)) {\n\t\t\t\tpreserved.push(name);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tonDiskPaths.push(`${inputs.stackRoot}/${name}`);\n\t\t}\n\n\t\treturn {\n\t\t\tapp: inputs.labelMatch.app,\n\t\t\tstack: inputs.labelMatch.stack,\n\t\t\tcontainers,\n\t\t\tnetworkLabelMatch: inputs.labelMatch,\n\t\t\tvolumeLabelMatch: inputs.labelMatch,\n\t\t\tstateFile: inputs.stateFilePath,\n\t\t\tstackRoot: inputs.stackRoot,\n\t\t\tonDiskPaths,\n\t\t\tpreserved,\n\t\t} satisfies WipeTargets;\n\t}).pipe(Effect.withSpan('orchestrator.snapshot.wipe.plan'));\n\n// -----------------------------------------------------------------------------\n// Wipe\n// -----------------------------------------------------------------------------\n\n/**\n * Tear down a stack's live footprint. Snapshots survive by default\n * (architecture § wipe).\n *\n * Order:\n * 1. Force-remove managed containers by `{ app, stack }` labels.\n * 2. Remove managed networks and volumes by the same label filter.\n * 3. Remove state.json.\n * 4. Remove the runtime tree EXCEPT the snapshot catalog by default.\n * 5. Remove the now-empty stack root when nothing survived (no\n * preserved child remains) so wipe doesn't leak an empty\n * `stacks/<stack>/` directory.\n */\nexport const runWipe = (\n\tinputs: WipeInputs,\n): Effect.Effect<void, WipePhaseError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t'devstack.snapshot.phase': 'wipe',\n\t\t\t'devstack.app': inputs.labelMatch.app,\n\t\t\t'devstack.stack': inputs.labelMatch.stack,\n\t\t});\n\n\t\t// 1. Containers.\n\t\tyield* inputs.runtime\n\t\t\t.removeManagedContainers(inputs.labelMatch)\n\t\t\t.pipe(Effect.catch(failPhase('sweep-containers', `container sweep failed`)));\n\n\t\t// 2. Networks + volumes.\n\t\tyield* inputs.runtime\n\t\t\t.removeManagedNetworks(inputs.labelMatch)\n\t\t\t.pipe(Effect.catch(failPhase('sweep-networks-volumes', `network sweep failed`)));\n\t\tyield* inputs.runtime\n\t\t\t.removeManagedVolumes(inputs.labelMatch)\n\t\t\t.pipe(Effect.catch(failPhase('sweep-networks-volumes', `volume sweep failed`)));\n\n\t\t// 3. Remove state.json.\n\t\tyield* fs\n\t\t\t.remove(inputs.stateFilePath, { force: true })\n\t\t\t.pipe(Effect.catch(failPhase('remove-state', `remove state.json failed`)));\n\n\t\t// 4. Remove the runtime tree — but PRESERVE snapshots by default.\n\t\t// Strategy: enumerate the stack root and remove each child the\n\t\t// preserve predicate does NOT keep (`snapshots/` by default;\n\t\t// `cache/` only when `keepCache`). Stack-local artifact caches\n\t\t// are state and are removed unless explicitly requested.\n\t\tconst policy = resolvePreservePolicy(inputs);\n\t\tconst children = yield* fs\n\t\t\t.readDirectory(inputs.stackRoot)\n\t\t\t.pipe(Effect.catch(() => Effect.succeed([] as ReadonlyArray<string>)));\n\t\tlet preservedCount = 0;\n\t\tfor (const name of children) {\n\t\t\tif (isPreservedChild(name, policy)) {\n\t\t\t\tpreservedCount += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tyield* fs\n\t\t\t\t.remove(`${inputs.stackRoot}/${name}`, { recursive: true, force: true })\n\t\t\t\t.pipe(Effect.catch(failPhase('remove-runtime-tree', `remove ${name} failed`)));\n\t\t}\n\n\t\t// 5. Reap the now-empty stack root. When NOTHING was preserved\n\t\t// (no `snapshots/`/`cache/` survivor) every child was removed in\n\t\t// step 4 — any child whose removal FAILED would have raised a\n\t\t// `remove-runtime-tree` error and aborted before here — so the\n\t\t// directory is empty at this point. Leaving it behind leaks an\n\t\t// empty `stacks/<stack>/` shell that `prune --list` would show as\n\t\t// a bare group. `recursive` is required to remove a directory at\n\t\t// all (a plain `remove` raises EISDIR even for an empty dir); it\n\t\t// is safe here precisely BECAUSE the `preservedCount === 0` guard\n\t\t// means no preserved subtree exists to be recursively swept.\n\t\t// Best-effort: a (racing) re-created child just leaves the dir.\n\t\tif (preservedCount === 0 && children.length > 0) {\n\t\t\tyield* fs.remove(inputs.stackRoot, { recursive: true, force: true }).pipe(Effect.ignore);\n\t\t}\n\t}).pipe(Effect.withSpan('orchestrator.snapshot.wipe'));\n\n/** Centralized constant — the canonical snapshot-catalog directory\n * name. Used by both `runWipe` (to preserve) and the substrate's\n * path resolver (to compose `snapshotDir`). Distilled §17 calls out\n * the \"centralize the snapshots-dir-name constant\" opportunity. */\nexport const SNAPSHOTS_DIR_NAME = 'snapshots';\n\n/** Canonical stack-local artifact-cache directory name. `runWipe`\n * preserves it only under `keepCache`; otherwise it is removed so a\n * reset re-proves on-chain artifacts against the next chain. */\nexport const CACHE_DIR_NAME = 'cache';\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Effect } from "effect";
1
+ import { defineSimpleConstExport } from "../internal/codegen-helpers.mjs";
2
2
  //#region src/plugins/account/codegen.ts
3
3
  /** Construct the Codegenable contribution. One emit per account.
4
4
  *
@@ -6,15 +6,16 @@ import { Effect } from "effect";
6
6
  * accounts in a single stack get co-located but typed-distinct
7
7
  * emissions; the codegen orchestrator can fold them into a single
8
8
  * `accounts` namespace at staging time. */
9
- const makeAccountCodegen = (parts) => ({
10
- kind: "codegenable",
9
+ const makeAccountCodegen = (parts) => defineSimpleConstExport({
11
10
  emitterName: `account/${parts.name}`,
12
11
  outputPath: `accounts/${parts.name}.ts`,
13
- sensitive: false,
14
- emit: (ctx) => Effect.sync(() => {
15
- ctx.exportConst(parts.name, parts.resolved);
16
- return ctx.done();
17
- })
12
+ exportName: parts.name,
13
+ value: parts.resolved,
14
+ aggregate: {
15
+ kind: "account",
16
+ bucket: "accounts.ts",
17
+ project: (exported) => exported
18
+ }
18
19
  });
19
20
  //#endregion
20
21
  export { makeAccountCodegen };