@mysten-incubation/devstack 0.0.1 → 0.1.0

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 +7 -3
  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 +40 -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
@@ -0,0 +1,350 @@
1
+ import { logDebugAndFallback } from "../../substrate/runtime/observability/ignore-with-log.mjs";
2
+ import { LivenessProbeScope, layerLivenessProbeScope } from "../../substrate/runtime/cross-process/liveness.mjs";
3
+ import { readRoster } from "../../substrate/runtime/cross-process/roster.mjs";
4
+ import "../../substrate/runtime/observability/index.mjs";
5
+ import { DockerSpawner, layerDockerHostDefault } from "../../runtime/docker/client.mjs";
6
+ import { LabelKey } from "../../runtime/docker/labels.mjs";
7
+ import { listDevstackContainers, listDevstackContainersByKind, listDevstackImages, listDevstackNetworks, listDevstackVolumes } from "../../runtime/docker/inventory.mjs";
8
+ import { removeDevstackContainers, removeDevstackContainersByKindAndName, removeDevstackImages, removeDevstackNetworksBestEffort, removeDevstackVolumes } from "../../runtime/docker/sweep.mjs";
9
+ import "../../runtime/docker/index.mjs";
10
+ import { ROUTER_KIND_LABEL_VALUE, ROUTER_SHARED_APP } from "../router/sentinels.mjs";
11
+ import "../../substrate/runtime/managed-container.mjs";
12
+ import "../../substrate/runtime/cross-process/index.mjs";
13
+ import { removeRouterProfileStateForDockerStack } from "../router/cleanup.mjs";
14
+ import { failPhase } from "./errors.mjs";
15
+ import { Effect, Layer } from "effect";
16
+ import { existsSync } from "node:fs";
17
+ import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
18
+ import { join } from "node:path";
19
+ import * as NodePath from "@effect/platform-node/NodePath";
20
+ import * as NodeChildProcessSpawner from "@effect/platform-node/NodeChildProcessSpawner";
21
+ import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
22
+ //#region src/orchestrators/lifecycle-prune/index.ts
23
+ const layerDockerSpawnerFromNode = Layer.effect(DockerSpawner, Effect.gen(function* () {
24
+ return yield* ChildProcessSpawner;
25
+ }));
26
+ const dockerLayer = Layer.merge(layerDockerHostDefault, layerDockerSpawnerFromNode.pipe(Layer.provideMerge(NodeChildProcessSpawner.layer.pipe(Layer.provideMerge(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer))))));
27
+ const emptyBucket = () => ({
28
+ containers: 0,
29
+ runningContainers: 0,
30
+ networks: 0,
31
+ volumes: 0,
32
+ images: 0
33
+ });
34
+ /** Public, parser-safe key for a `(app, stack)` group. Display-only —
35
+ * internal grouping uses the structural `{app, stack}` tuple via the
36
+ * `GroupBuckets` map below, so a slash inside `app` or `stack` cannot
37
+ * produce a wrong tuple. The separator is a forward slash for human-
38
+ * readable log/JSON output (`arena/main`); membership tests round-trip
39
+ * through this same constructor so callers never re-split the key —
40
+ * any potential `/`-in-app-or-stack ambiguity is resolved structurally
41
+ * by `GroupBuckets`, not by string-splitting the key. */
42
+ const GROUP_KEY_SEPARATOR = "/";
43
+ const lifecyclePruneGroupKey = (app, stack) => `${app}${GROUP_KEY_SEPARATOR}${stack}`;
44
+ /** Map keyed on `(app, stack)` tuples without string encoding. Tuple
45
+ * equality is achieved by interning each `(app, stack)` pair through
46
+ * a nested `app → stack → bucket` index, so the map is collision-
47
+ * free even when `app` or `stack` contains a separator character. */
48
+ var GroupBuckets = class {
49
+ buckets = /* @__PURE__ */ new Map();
50
+ get(identity) {
51
+ let perApp = this.buckets.get(identity.app);
52
+ if (perApp === void 0) {
53
+ perApp = /* @__PURE__ */ new Map();
54
+ this.buckets.set(identity.app, perApp);
55
+ }
56
+ let bucket = perApp.get(identity.stack);
57
+ if (bucket === void 0) {
58
+ bucket = emptyBucket();
59
+ perApp.set(identity.stack, bucket);
60
+ }
61
+ return bucket;
62
+ }
63
+ *entries() {
64
+ for (const [app, perApp] of this.buckets) for (const [stack, bucket] of perApp) yield {
65
+ app,
66
+ stack,
67
+ bucket
68
+ };
69
+ }
70
+ };
71
+ const readAppStack = (labels) => {
72
+ const app = labels[LabelKey.app];
73
+ const stack = labels[LabelKey.stack];
74
+ if (app === void 0 || app.length === 0) return null;
75
+ if (stack === void 0 || stack.length === 0) return null;
76
+ return {
77
+ app,
78
+ stack
79
+ };
80
+ };
81
+ const routerStackForContainer = (name) => ({
82
+ app: ROUTER_SHARED_APP,
83
+ stack: name
84
+ });
85
+ /** Read the live-PID set for a stack from its roster.
86
+ *
87
+ * Path policy: `<runtimeRoot>/stacks/<stack>/roster.json`. This is the
88
+ * same key the entire substrate uses — see `substrate/runtime/paths.ts`
89
+ * (`path.join(root, 'stacks', identity.stack)`) — so the path here
90
+ * carries no NEW cross-attribution risk beyond the substrate-wide
91
+ * decision to key per-stack-disk-state on `stack` alone. (Two stacks
92
+ * in DIFFERENT apps that pick the SAME stack name would already share
93
+ * every other on-disk artifact — `state.json`, `cache/`, `snapshots/`,
94
+ * `stack.lock`, `container-claims.json`. The Docker-level discriminator
95
+ * is the container/network/volume NAME, which IS `<app>-<stack>-…`
96
+ * composed inside each plugin; so cross-attribution at the daemon
97
+ * level is prevented by name uniqueness even when disk state collides.)
98
+ *
99
+ * This function deliberately omits `app` from the path so the
100
+ * prune sweeper agrees with every other consumer (`cli/main.ts`,
101
+ * `cli/wirings/identity.ts`, `cli/wirings/up.ts`, the roster module
102
+ * itself). Promoting `app` into the stack-state path is an
103
+ * architectural change that must update all of those call sites in
104
+ * lockstep — out of scope for this orchestrator. */
105
+ const livePidsForStack = (runtimeRoot, stack) => {
106
+ const rosterFile = join(runtimeRoot, "stacks", stack, "roster.json");
107
+ if (!existsSync(rosterFile)) return Effect.succeed([]);
108
+ return Effect.gen(function* () {
109
+ const doc = yield* readRoster(rosterFile).pipe(logDebugAndFallback(null, "lifecycle-prune: roster read failed; treating as empty", { rosterFile }));
110
+ if (doc === null) return [];
111
+ const probe = yield* LivenessProbeScope;
112
+ const pids = [];
113
+ for (const holder of doc.holders) if ((yield* probe.probeHolderLiveness(holder).pipe(logDebugAndFallback("alive", "lifecycle-prune: liveness check failed; assuming alive", { pid: holder.pid }))) === "alive") pids.push(holder.pid);
114
+ return pids;
115
+ }).pipe(Effect.provide(layerLivenessProbeScope));
116
+ };
117
+ /** True when the group is one of the two shared shapes the
118
+ * orchestrator knows about: `_per-app_` (cross-stack-per-app shared)
119
+ * or the router-singleton (`ROUTER_SHARED_APP`). Surfaces consult
120
+ * this instead of recomputing the predicate. */
121
+ const isSharedLifecyclePruneGroup = (app, stack) => stack === "_per-app_" || app === "devstack-router";
122
+ const sharedKindFor = (app, stack) => {
123
+ if (app === "devstack-router") return "router";
124
+ if (stack === "_per-app_") return "per-app-shared";
125
+ return null;
126
+ };
127
+ /** True when the group represents a router-singleton resource set.
128
+ * Exported so L4 surfaces can call this rather than re-implement the
129
+ * router-stack naming predicate. */
130
+ const isRouterLifecyclePruneGroup = (group) => group.app === "devstack-router" && group.stack.startsWith(`devstack-router-`);
131
+ const isRouterGroup = isRouterLifecyclePruneGroup;
132
+ const isSharedGroup = isSharedLifecyclePruneGroup;
133
+ /** Label-tuple match for the `removeDevstack*` sweepers — router-shared
134
+ * resources stamp `{app: ROUTER_SHARED_APP, stack: <profile-name>}`
135
+ * (see `traefik-container.ts:ensureNetwork`) and the inventory pass
136
+ * buckets them under `routerStackForContainer(container.name)`, so for
137
+ * router groups the tuple is `{app: ROUTER_SHARED_APP, stack: group.stack}`.
138
+ * For non-router groups it's the bucket's literal `{app, stack}`. The
139
+ * branch is structurally identical to the container-removal branch's
140
+ * router-specific dispatch via `removeDevstackContainersByKindAndName`,
141
+ * so dry-run inventory counts and real-run removal stay in lockstep
142
+ * for router resources. Exported so tests can pin the dry-run ↔
143
+ * real-run parity without standing up a Docker daemon. */
144
+ const lifecyclePruneRemovalMatchTuple = (group) => isRouterGroup(group) ? {
145
+ app: ROUTER_SHARED_APP,
146
+ stack: group.stack
147
+ } : {
148
+ app: group.app,
149
+ stack: group.stack
150
+ };
151
+ const matchTupleForGroup = lifecyclePruneRemovalMatchTuple;
152
+ const collectLifecyclePruneInventory = (options) => Effect.gen(function* () {
153
+ const [containers, routerContainers, networks, volumes, images] = yield* Effect.all([
154
+ listDevstackContainers(),
155
+ listDevstackContainersByKind(ROUTER_KIND_LABEL_VALUE),
156
+ listDevstackNetworks(),
157
+ listDevstackVolumes(),
158
+ listDevstackImages()
159
+ ], { concurrency: "unbounded" }).pipe(Effect.provide(dockerLayer), Effect.mapError(failPhase("inventory")));
160
+ const buckets = new GroupBuckets();
161
+ for (const container of containers) {
162
+ const identity = readAppStack(container.labels);
163
+ if (identity === null) continue;
164
+ const bucket = buckets.get(identity);
165
+ bucket.containers += 1;
166
+ if (container.state === "running") bucket.runningContainers += 1;
167
+ }
168
+ for (const container of routerContainers) {
169
+ const bucket = buckets.get(routerStackForContainer(container.name));
170
+ bucket.containers += 1;
171
+ if (container.state === "running") bucket.runningContainers += 1;
172
+ }
173
+ for (const network of networks) {
174
+ const identity = readAppStack(network.labels);
175
+ if (identity === null) continue;
176
+ buckets.get(identity).networks += 1;
177
+ }
178
+ for (const volume of volumes) {
179
+ const identity = readAppStack(volume.labels);
180
+ if (identity === null) continue;
181
+ buckets.get(identity).volumes += 1;
182
+ }
183
+ for (const image of images) {
184
+ const identity = readAppStack(image.labels);
185
+ if (identity === null) continue;
186
+ buckets.get(identity).images += 1;
187
+ }
188
+ const groups = [];
189
+ for (const entry of buckets.entries()) {
190
+ const { app, stack, bucket } = entry;
191
+ const livePids = app === "devstack-router" ? [] : yield* livePidsForStack(options.runtimeRoot, stack);
192
+ const live = livePids.length > 0 || bucket.runningContainers > 0;
193
+ groups.push({
194
+ key: lifecyclePruneGroupKey(app, stack),
195
+ app,
196
+ stack,
197
+ live,
198
+ livePids,
199
+ shared: isSharedGroup(app, stack),
200
+ sharedKind: sharedKindFor(app, stack),
201
+ autoPrunable: isRouterGroup({
202
+ app,
203
+ stack
204
+ }),
205
+ containers: bucket.containers,
206
+ runningContainers: bucket.runningContainers,
207
+ networks: bucket.networks,
208
+ volumes: bucket.volumes,
209
+ images: bucket.images
210
+ });
211
+ }
212
+ groups.sort((a, b) => {
213
+ if (a.app !== b.app) return a.app < b.app ? -1 : 1;
214
+ return a.stack < b.stack ? -1 : a.stack > b.stack ? 1 : 0;
215
+ });
216
+ return { groups };
217
+ }).pipe(Effect.withSpan("orchestrator.lifecycle-prune.inventory"));
218
+ const selectedGroups = (inventory, selection) => {
219
+ const selected = new Set(selection.groupKeys);
220
+ return inventory.groups.filter((group) => selected.has(group.key));
221
+ };
222
+ /** Defensive enforcement of the per-app-shared pinning rule for a
223
+ * caller-supplied selection. Resolves `selection.groupKeys` against
224
+ * `inventory` and drops any `per-app-shared` group whose app still has
225
+ * a live sibling — reusing the SAME predicate as
226
+ * `defaultLifecyclePruneSelection` so a selection built outside the
227
+ * default path (interactive picker, scripted, programmatic) can never
228
+ * remove a shared group a live sibling depends on. Exported so the
229
+ * invariant is unit-testable without a Docker daemon. */
230
+ const enforcePinnedLifecyclePruneSelection = (inventory, selection) => {
231
+ const pinned = lifecyclePruneAppsWithLiveSiblings(inventory);
232
+ return selectedGroups(inventory, selection).filter((group) => !(group.sharedKind === "per-app-shared" && pinned.has(group.app)));
233
+ };
234
+ /** Default resource scope for the lifecycle-prune orchestrator —
235
+ * containers + networks + volumes, never images. Surfaces consume
236
+ * this directly so the default never drifts between L3 and L4. */
237
+ const DEFAULT_LIFECYCLE_PRUNE_RESOURCES = {
238
+ containers: true,
239
+ networks: true,
240
+ volumes: true,
241
+ images: false
242
+ };
243
+ /** Apps with at least one live non-shared group — their `_per-app_`
244
+ * shared resources stay pinned because something under the app is
245
+ * still running. Exported so L4 surfaces consume the orchestrator's
246
+ * pinning predicate. */
247
+ const lifecyclePruneAppsWithLiveSiblings = (inventory) => {
248
+ const apps = /* @__PURE__ */ new Set();
249
+ for (const group of inventory.groups) if (!group.shared && group.live) apps.add(group.app);
250
+ return apps;
251
+ };
252
+ const groupHasResource = (group, resources) => resources.containers && group.containers > 0 || resources.networks && group.networks > 0 || resources.volumes && group.volumes > 0 || resources.images && group.images > 0;
253
+ /** Default selection: every non-live group whose shared shape is
254
+ * prunable in non-interactive flows. Surfaces and the live-supervisor
255
+ * command handler share this so the orchestrator owns the policy. */
256
+ const defaultLifecyclePruneSelection = (inventory, resources = DEFAULT_LIFECYCLE_PRUNE_RESOURCES) => {
257
+ const pinned = lifecyclePruneAppsWithLiveSiblings(inventory);
258
+ const keys = [];
259
+ for (const group of inventory.groups) {
260
+ if (group.live) continue;
261
+ if (!groupHasResource(group, resources)) continue;
262
+ if (group.sharedKind === "per-app-shared" && pinned.has(group.app)) continue;
263
+ if (group.shared && !group.autoPrunable && group.sharedKind !== "per-app-shared") continue;
264
+ keys.push(group.key);
265
+ }
266
+ return keys;
267
+ };
268
+ const runLifecyclePrune = (options, selection) => Effect.gen(function* () {
269
+ const groups = enforcePinnedLifecyclePruneSelection(yield* collectLifecyclePruneInventory(options), selection);
270
+ let skippedLiveGroups = 0;
271
+ let containersRemoved = 0;
272
+ let networksRemoved = 0;
273
+ let networksSkipped = 0;
274
+ let volumesRemoved = 0;
275
+ let imagesRemoved = 0;
276
+ const prunableGroups = [];
277
+ for (const group of groups) {
278
+ if (group.live) {
279
+ skippedLiveGroups += 1;
280
+ continue;
281
+ }
282
+ if (selection.dryRun) {
283
+ prunableGroups.push(group);
284
+ if (selection.resources.containers) containersRemoved += group.containers;
285
+ if (selection.resources.networks) networksRemoved += group.networks;
286
+ if (selection.resources.volumes) volumesRemoved += group.volumes;
287
+ if (selection.resources.images) imagesRemoved += group.images;
288
+ continue;
289
+ }
290
+ if (!isRouterGroup(group)) {
291
+ if ((yield* livePidsForStack(options.runtimeRoot, group.stack)).length > 0) {
292
+ skippedLiveGroups += 1;
293
+ continue;
294
+ }
295
+ }
296
+ prunableGroups.push(group);
297
+ }
298
+ const foreignNetworkHolders = [];
299
+ const staleNetworkEndpoints = [];
300
+ yield* Effect.gen(function* () {
301
+ if (!selection.dryRun && selection.resources.containers) for (const group of prunableGroups) if (isRouterGroup(group)) containersRemoved += yield* removeDevstackContainersByKindAndName(ROUTER_KIND_LABEL_VALUE, group.stack).pipe(Effect.mapError(failPhase("remove-containers")));
302
+ else {
303
+ const match = {
304
+ app: group.app,
305
+ stack: group.stack
306
+ };
307
+ containersRemoved += yield* removeDevstackContainers(match).pipe(Effect.mapError(failPhase("remove-containers")));
308
+ }
309
+ if (!selection.dryRun && selection.resources.networks) for (const group of prunableGroups) {
310
+ const result = yield* removeDevstackNetworksBestEffort(matchTupleForGroup(group)).pipe(Effect.mapError(failPhase("remove-networks")));
311
+ networksRemoved += result.removed;
312
+ networksSkipped += result.skippedInUse;
313
+ for (const holder of result.foreignHolders) foreignNetworkHolders.push(holder);
314
+ for (const ep of result.staleEndpoints) staleNetworkEndpoints.push(ep);
315
+ }
316
+ if (!selection.dryRun && selection.resources.volumes) for (const group of prunableGroups) {
317
+ const match = matchTupleForGroup(group);
318
+ volumesRemoved += yield* removeDevstackVolumes(match).pipe(Effect.mapError(failPhase("remove-volumes")));
319
+ }
320
+ if (!selection.dryRun && selection.resources.images) for (const group of prunableGroups) {
321
+ const match = matchTupleForGroup(group);
322
+ imagesRemoved += yield* removeDevstackImages(match).pipe(Effect.mapError(failPhase("remove-images")));
323
+ }
324
+ }).pipe(Effect.provide(dockerLayer));
325
+ if (!selection.dryRun) yield* Effect.gen(function* () {
326
+ for (const group of prunableGroups) {
327
+ if (!isRouterGroup(group)) continue;
328
+ yield* removeRouterProfileStateForDockerStack({
329
+ runtimeRoot: options.runtimeRoot,
330
+ routerStack: group.stack
331
+ });
332
+ }
333
+ }).pipe(Effect.provide(NodeFileSystem.layer), Effect.withSpan("orchestrator.lifecycle-prune.removeRouterProfileState"));
334
+ return {
335
+ inspectedGroups: groups.length,
336
+ selectedGroups: prunableGroups.length,
337
+ skippedLiveGroups,
338
+ containersRemoved,
339
+ networksRemoved,
340
+ networksSkipped,
341
+ volumesRemoved,
342
+ imagesRemoved,
343
+ foreignNetworkHolders,
344
+ staleNetworkEndpoints
345
+ };
346
+ }).pipe(Effect.withSpan("orchestrator.lifecycle-prune.run"));
347
+ //#endregion
348
+ export { DEFAULT_LIFECYCLE_PRUNE_RESOURCES, collectLifecyclePruneInventory, defaultLifecyclePruneSelection, lifecyclePruneAppsWithLiveSiblings, runLifecyclePrune };
349
+
350
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["joinPath"],"sources":["../../../src/orchestrators/lifecycle-prune/index.ts"],"sourcesContent":["// Lifecycle prune orchestrator — cross-stack Docker sweep.\n//\n// Architecture § L3 orchestrators. Walks the L1 docker inventory (the\n// reference `ContainerRuntime` adapter), groups labelled resources by\n// `(app, stack)`, consults the cross-process roster + claim ledger for\n// liveness, and dispatches removal through the sibling router cleanup\n// helper for router-shared resources.\n//\n// Name-blindness: this orchestrator never names a service. It walks\n// the engine label tuple (`LabelKey.app` / `LabelKey.stack`) which is\n// L1-substrate-level vocabulary and dispatches on the\n// `ROUTER_SHARED_APP` sentinel exported by the router orchestrator.\n//\n// Consumers: `cli/prune-direct.ts` (the L4-adjacent CLI infrastructure\n// for the `devstack prune` verb) calls `runLifecyclePrune` /\n// `collectLifecyclePruneInventory`. No L4 surface module imports L1\n// runtime adapters directly; that boundary lives here.\n\nimport { existsSync } from 'node:fs';\nimport { join as joinPath } from 'node:path';\n\nimport * as NodeChildProcessSpawner from '@effect/platform-node/NodeChildProcessSpawner';\nimport * as NodeFileSystem from '@effect/platform-node/NodeFileSystem';\nimport * as NodePath from '@effect/platform-node/NodePath';\nimport { Effect, Layer } from 'effect';\nimport { ChildProcessSpawner } from 'effect/unstable/process/ChildProcessSpawner';\n\nimport {\n\tDockerHost,\n\tDockerSpawner,\n\ttype ForeignNetworkHolder,\n\tLabelKey,\n\tlayerDockerHostDefault,\n\tlistDevstackContainers,\n\tlistDevstackContainersByKind,\n\tlistDevstackImages,\n\tlistDevstackNetworks,\n\tlistDevstackVolumes,\n\tremoveDevstackContainers,\n\tremoveDevstackContainersByKindAndName,\n\tremoveDevstackImages,\n\tremoveDevstackNetworksBestEffort,\n\tremoveDevstackVolumes,\n\ttype StaleNetworkEndpoint,\n} from '../../runtime/docker/index.ts';\nimport {\n\tlayerLivenessProbeScope,\n\tLivenessProbeScope,\n\treadRoster,\n} from '../../substrate/runtime/cross-process/index.ts';\nimport { PER_APP_SHARED_STACK } from '../../substrate/runtime/managed-container.ts';\nimport { logDebugAndFallback } from '../../substrate/runtime/observability/index.ts';\nimport { ROUTER_SHARED_APP, removeRouterProfileStateForDockerStack } from '../router/cleanup.ts';\n// Import the kind sentinel from its canonical source rather than the\n// `traefik-container.ts` re-export — the router-singleton label literals\n// live in `sentinels.ts`, and lifecycle-prune only needs the constant\n// (not the container ops), so depending on the sentinel module keeps the\n// import edge minimal and the source-of-truth direct.\nimport { ROUTER_KIND_LABEL_VALUE } from '../router/sentinels.ts';\nimport { failPhase, type LifecyclePruneError } from './errors.ts';\n\nexport { LifecyclePruneError, LifecyclePrunePhase } from './errors.ts';\n\n// -----------------------------------------------------------------------------\n// Public shapes — mirror `surfaces/cli/commands/prune.ts` field-for-field so\n// the verb dispatcher can consume the orchestrator output without an adapter.\n// -----------------------------------------------------------------------------\n\nexport interface LifecyclePruneOptions {\n\treadonly runtimeRoot: string;\n}\n\nexport interface LifecyclePruneGroup {\n\treadonly key: string;\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly live: boolean;\n\treadonly livePids: ReadonlyArray<number>;\n\treadonly shared: boolean;\n\t/** Discriminator for shared-resource groups. `'per-app-shared'`\n\t * groups (the `_per-app_` synthetic stack) stay pinned while any\n\t * sibling stack under the same app is live; `'router'` groups are\n\t * auto-prunable when no app pins them. `null` for normal groups. */\n\treadonly sharedKind: SharedGroupKind | null;\n\t/** True when the group represents a router-shared resource set that\n\t * is auto-prunable in non-interactive flows (`devstack prune --all`).\n\t * Computed by the orchestrator so surfaces never recompute the\n\t * router-stack naming predicate. */\n\treadonly autoPrunable: boolean;\n\treadonly containers: number;\n\treadonly runningContainers: number;\n\treadonly networks: number;\n\treadonly volumes: number;\n\treadonly images: number;\n}\n\n/** Kind of a shared-resource group. `'per-app-shared'` groups belong\n * to the `PER_APP_SHARED_STACK` synthetic stack under a real app and\n * stay pinned while a sibling stack is live. `'router'` groups are\n * router-singleton resources. */\nexport type SharedGroupKind = 'per-app-shared' | 'router';\n\nexport interface LifecyclePruneInventory {\n\treadonly groups: ReadonlyArray<LifecyclePruneGroup>;\n}\n\nexport interface LifecyclePruneResourceScope {\n\treadonly containers: boolean;\n\treadonly networks: boolean;\n\treadonly volumes: boolean;\n\treadonly images: boolean;\n}\n\nexport interface LifecyclePruneSelection {\n\treadonly groupKeys: ReadonlyArray<string>;\n\treadonly resources: LifecyclePruneResourceScope;\n\treadonly dryRun: boolean;\n}\n\nexport interface LifecyclePruneSummary {\n\treadonly inspectedGroups: number;\n\treadonly selectedGroups: number;\n\treadonly skippedLiveGroups: number;\n\treadonly containersRemoved: number;\n\treadonly networksRemoved: number;\n\treadonly networksSkipped: number;\n\treadonly volumesRemoved: number;\n\treadonly imagesRemoved: number;\n\t/** Non-devstack containers still holding networks that prune could\n\t * not remove. Empty when every network came down cleanly. */\n\treadonly foreignNetworkHolders: ReadonlyArray<ForeignNetworkHolder>;\n\t/** Endpoints Docker insists exist on a network but which no CLI/API\n\t * path can address — symptom of a Docker engine bug (the bridge\n\t * driver leaked endpoint metadata after a container was reaped).\n\t * Only a Docker daemon restart clears these. */\n\treadonly staleNetworkEndpoints: ReadonlyArray<StaleNetworkEndpoint>;\n}\n\n// -----------------------------------------------------------------------------\n// Internal helpers\n// -----------------------------------------------------------------------------\n\ntype ResourceBucket = {\n\tcontainers: number;\n\trunningContainers: number;\n\tnetworks: number;\n\tvolumes: number;\n\timages: number;\n};\n\nconst layerDockerSpawnerFromNode: Layer.Layer<DockerSpawner, never, ChildProcessSpawner> =\n\tLayer.effect(\n\t\tDockerSpawner,\n\t\tEffect.gen(function* () {\n\t\t\treturn yield* ChildProcessSpawner;\n\t\t}),\n\t);\n\nconst dockerLayer: Layer.Layer<DockerHost | DockerSpawner> = Layer.merge(\n\tlayerDockerHostDefault,\n\tlayerDockerSpawnerFromNode.pipe(\n\t\tLayer.provideMerge(\n\t\t\tNodeChildProcessSpawner.layer.pipe(\n\t\t\t\tLayer.provideMerge(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)),\n\t\t\t),\n\t\t),\n\t),\n);\n\nconst emptyBucket = (): ResourceBucket => ({\n\tcontainers: 0,\n\trunningContainers: 0,\n\tnetworks: 0,\n\tvolumes: 0,\n\timages: 0,\n});\n\n/** Public, parser-safe key for a `(app, stack)` group. Display-only —\n * internal grouping uses the structural `{app, stack}` tuple via the\n * `GroupBuckets` map below, so a slash inside `app` or `stack` cannot\n * produce a wrong tuple. The separator is a forward slash for human-\n * readable log/JSON output (`arena/main`); membership tests round-trip\n * through this same constructor so callers never re-split the key —\n * any potential `/`-in-app-or-stack ambiguity is resolved structurally\n * by `GroupBuckets`, not by string-splitting the key. */\nconst GROUP_KEY_SEPARATOR = '/';\n\nexport const lifecyclePruneGroupKey = (app: string, stack: string): string =>\n\t`${app}${GROUP_KEY_SEPARATOR}${stack}`;\n\n/** Map keyed on `(app, stack)` tuples without string encoding. Tuple\n * equality is achieved by interning each `(app, stack)` pair through\n * a nested `app → stack → bucket` index, so the map is collision-\n * free even when `app` or `stack` contains a separator character. */\nclass GroupBuckets {\n\tprivate readonly buckets = new Map<string, Map<string, ResourceBucket>>();\n\n\tget(identity: { readonly app: string; readonly stack: string }): ResourceBucket {\n\t\tlet perApp = this.buckets.get(identity.app);\n\t\tif (perApp === undefined) {\n\t\t\tperApp = new Map<string, ResourceBucket>();\n\t\t\tthis.buckets.set(identity.app, perApp);\n\t\t}\n\t\tlet bucket = perApp.get(identity.stack);\n\t\tif (bucket === undefined) {\n\t\t\tbucket = emptyBucket();\n\t\t\tperApp.set(identity.stack, bucket);\n\t\t}\n\t\treturn bucket;\n\t}\n\n\t*entries(): Iterable<{\n\t\treadonly app: string;\n\t\treadonly stack: string;\n\t\treadonly bucket: ResourceBucket;\n\t}> {\n\t\tfor (const [app, perApp] of this.buckets) {\n\t\t\tfor (const [stack, bucket] of perApp) {\n\t\t\t\tyield { app, stack, bucket };\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst readAppStack = (\n\tlabels: Readonly<Record<string, string>>,\n): { readonly app: string; readonly stack: string } | null => {\n\tconst app = labels[LabelKey.app];\n\tconst stack = labels[LabelKey.stack];\n\tif (app === undefined || app.length === 0) return null;\n\tif (stack === undefined || stack.length === 0) return null;\n\treturn { app, stack };\n};\n\nconst routerStackForContainer = (\n\tname: string,\n): { readonly app: string; readonly stack: string } => ({\n\tapp: ROUTER_SHARED_APP,\n\tstack: name,\n});\n\n/** Read the live-PID set for a stack from its roster.\n *\n * Path policy: `<runtimeRoot>/stacks/<stack>/roster.json`. This is the\n * same key the entire substrate uses — see `substrate/runtime/paths.ts`\n * (`path.join(root, 'stacks', identity.stack)`) — so the path here\n * carries no NEW cross-attribution risk beyond the substrate-wide\n * decision to key per-stack-disk-state on `stack` alone. (Two stacks\n * in DIFFERENT apps that pick the SAME stack name would already share\n * every other on-disk artifact — `state.json`, `cache/`, `snapshots/`,\n * `stack.lock`, `container-claims.json`. The Docker-level discriminator\n * is the container/network/volume NAME, which IS `<app>-<stack>-…`\n * composed inside each plugin; so cross-attribution at the daemon\n * level is prevented by name uniqueness even when disk state collides.)\n *\n * This function deliberately omits `app` from the path so the\n * prune sweeper agrees with every other consumer (`cli/main.ts`,\n * `cli/wirings/identity.ts`, `cli/wirings/up.ts`, the roster module\n * itself). Promoting `app` into the stack-state path is an\n * architectural change that must update all of those call sites in\n * lockstep — out of scope for this orchestrator. */\nconst livePidsForStack = (\n\truntimeRoot: string,\n\tstack: string,\n): Effect.Effect<ReadonlyArray<number>> => {\n\tconst rosterFile = joinPath(runtimeRoot, 'stacks', stack, 'roster.json');\n\tif (!existsSync(rosterFile)) return Effect.succeed([]);\n\t// Yield a fresh `LivenessProbeScope` so a recycled-PID corner case\n\t// (multiple holders sharing one pid in the same roster) forks the OS\n\t// liveness probe once per pid across this scan.\n\treturn Effect.gen(function* () {\n\t\tconst doc = yield* readRoster(rosterFile).pipe(\n\t\t\tlogDebugAndFallback(null, 'lifecycle-prune: roster read failed; treating as empty', {\n\t\t\t\trosterFile,\n\t\t\t}),\n\t\t);\n\t\tif (doc === null) return [];\n\t\tconst probe = yield* LivenessProbeScope;\n\t\tconst pids: Array<number> = [];\n\t\tfor (const holder of doc.holders) {\n\t\t\tconst live = yield* probe\n\t\t\t\t.probeHolderLiveness(holder)\n\t\t\t\t.pipe(\n\t\t\t\t\tlogDebugAndFallback(\n\t\t\t\t\t\t'alive' as const,\n\t\t\t\t\t\t'lifecycle-prune: liveness check failed; assuming alive',\n\t\t\t\t\t\t{ pid: holder.pid },\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\tif (live === 'alive') pids.push(holder.pid);\n\t\t}\n\t\treturn pids;\n\t}).pipe(Effect.provide(layerLivenessProbeScope));\n};\n\n/** True when the group is one of the two shared shapes the\n * orchestrator knows about: `_per-app_` (cross-stack-per-app shared)\n * or the router-singleton (`ROUTER_SHARED_APP`). Surfaces consult\n * this instead of recomputing the predicate. */\nexport const isSharedLifecyclePruneGroup = (app: string, stack: string): boolean =>\n\tstack === PER_APP_SHARED_STACK || app === ROUTER_SHARED_APP;\n\nconst sharedKindFor = (app: string, stack: string): SharedGroupKind | null => {\n\tif (app === ROUTER_SHARED_APP) return 'router';\n\tif (stack === PER_APP_SHARED_STACK) return 'per-app-shared';\n\treturn null;\n};\n\n/** True when the group represents a router-singleton resource set.\n * Exported so L4 surfaces can call this rather than re-implement the\n * router-stack naming predicate. */\nexport const isRouterLifecyclePruneGroup = (\n\tgroup: Pick<LifecyclePruneGroup, 'app' | 'stack'>,\n): boolean => group.app === ROUTER_SHARED_APP && group.stack.startsWith(`${ROUTER_SHARED_APP}-`);\n\n// Internal aliases for back-compat call sites in this file.\nconst isRouterGroup = isRouterLifecyclePruneGroup;\nconst isSharedGroup = isSharedLifecyclePruneGroup;\n\n/** Label-tuple match for the `removeDevstack*` sweepers — router-shared\n * resources stamp `{app: ROUTER_SHARED_APP, stack: <profile-name>}`\n * (see `traefik-container.ts:ensureNetwork`) and the inventory pass\n * buckets them under `routerStackForContainer(container.name)`, so for\n * router groups the tuple is `{app: ROUTER_SHARED_APP, stack: group.stack}`.\n * For non-router groups it's the bucket's literal `{app, stack}`. The\n * branch is structurally identical to the container-removal branch's\n * router-specific dispatch via `removeDevstackContainersByKindAndName`,\n * so dry-run inventory counts and real-run removal stay in lockstep\n * for router resources. Exported so tests can pin the dry-run ↔\n * real-run parity without standing up a Docker daemon. */\nexport const lifecyclePruneRemovalMatchTuple = (\n\tgroup: Pick<LifecyclePruneGroup, 'app' | 'stack'>,\n): { readonly app: string; readonly stack: string } =>\n\tisRouterGroup(group)\n\t\t? { app: ROUTER_SHARED_APP, stack: group.stack }\n\t\t: { app: group.app, stack: group.stack };\n\nconst matchTupleForGroup = lifecyclePruneRemovalMatchTuple;\n\n// -----------------------------------------------------------------------------\n// Orchestrator entry points\n// -----------------------------------------------------------------------------\n\nexport const collectLifecyclePruneInventory = (\n\toptions: LifecyclePruneOptions,\n): Effect.Effect<LifecyclePruneInventory, LifecyclePruneError> =>\n\tEffect.gen(function* () {\n\t\tconst [containers, routerContainers, networks, volumes, images] = yield* Effect.all(\n\t\t\t[\n\t\t\t\tlistDevstackContainers(),\n\t\t\t\tlistDevstackContainersByKind(ROUTER_KIND_LABEL_VALUE),\n\t\t\t\tlistDevstackNetworks(),\n\t\t\t\tlistDevstackVolumes(),\n\t\t\t\tlistDevstackImages(),\n\t\t\t],\n\t\t\t{ concurrency: 'unbounded' },\n\t\t).pipe(Effect.provide(dockerLayer), Effect.mapError(failPhase('inventory')));\n\n\t\tconst buckets = new GroupBuckets();\n\n\t\tfor (const container of containers) {\n\t\t\tconst identity = readAppStack(container.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tconst bucket = buckets.get(identity);\n\t\t\tbucket.containers += 1;\n\t\t\tif (container.state === 'running') bucket.runningContainers += 1;\n\t\t}\n\t\tfor (const container of routerContainers) {\n\t\t\tconst bucket = buckets.get(routerStackForContainer(container.name));\n\t\t\tbucket.containers += 1;\n\t\t\tif (container.state === 'running') bucket.runningContainers += 1;\n\t\t}\n\t\tfor (const network of networks) {\n\t\t\tconst identity = readAppStack(network.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).networks += 1;\n\t\t}\n\t\tfor (const volume of volumes) {\n\t\t\tconst identity = readAppStack(volume.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).volumes += 1;\n\t\t}\n\t\tfor (const image of images) {\n\t\t\tconst identity = readAppStack(image.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).images += 1;\n\t\t}\n\n\t\tconst groups: Array<LifecyclePruneGroup> = [];\n\t\tfor (const entry of buckets.entries()) {\n\t\t\tconst { app, stack, bucket } = entry;\n\t\t\tconst routerGroup = app === ROUTER_SHARED_APP;\n\t\t\tconst livePids = routerGroup ? [] : yield* livePidsForStack(options.runtimeRoot, stack);\n\t\t\t// Liveness is the OR of two independent signals so a live stack\n\t\t\t// is never mis-reported `live:false` (and so never swept by\n\t\t\t// `devstack prune --all`):\n\t\t\t//\n\t\t\t// 1. The per-stack roster carries a live holder (`livePids`\n\t\t\t// — the SAME `<root>/stacks/<stack>/roster.json` probe\n\t\t\t// `ensureNoLiveSupervisor` / `probeSupervisorPresence` use;\n\t\t\t// see `livePidsForStack`). Authoritative supervisor-presence\n\t\t\t// signal; also yields the PID detail.\n\t\t\t// 2. The Docker daemon reports a RUNNING container for the\n\t\t\t// group. A `devstack up` supervisor always has running\n\t\t\t// containers, so this catches a live stack even when the\n\t\t\t// roster probe transiently comes back empty — a missing/\n\t\t\t// stale roster file, a holder reaped by a peer sweep, or a\n\t\t\t// `ps -o lstart` start-time hiccup that harvests a live\n\t\t\t// holder as dead. Running containers are a daemon-\n\t\t\t// authoritative \"not idle\" signal, so this only ever ADDS\n\t\t\t// protection; it never reclassifies as idle a stack the\n\t\t\t// roster probe alone would have protected.\n\t\t\t//\n\t\t\t// The router branch already keyed liveness on\n\t\t\t// `runningContainers > 0` (router singletons carry no per-stack\n\t\t\t// roster); folding the same signal into normal groups makes the\n\t\t\t// classification uniform and closes the under-report.\n\t\t\tconst live = livePids.length > 0 || bucket.runningContainers > 0;\n\t\t\tgroups.push({\n\t\t\t\tkey: lifecyclePruneGroupKey(app, stack),\n\t\t\t\tapp,\n\t\t\t\tstack,\n\t\t\t\tlive,\n\t\t\t\tlivePids,\n\t\t\t\tshared: isSharedGroup(app, stack),\n\t\t\t\tsharedKind: sharedKindFor(app, stack),\n\t\t\t\tautoPrunable: isRouterGroup({ app, stack }),\n\t\t\t\tcontainers: bucket.containers,\n\t\t\t\trunningContainers: bucket.runningContainers,\n\t\t\t\tnetworks: bucket.networks,\n\t\t\t\tvolumes: bucket.volumes,\n\t\t\t\timages: bucket.images,\n\t\t\t});\n\t\t}\n\n\t\tgroups.sort((a, b) => {\n\t\t\tif (a.app !== b.app) return a.app < b.app ? -1 : 1;\n\t\t\treturn a.stack < b.stack ? -1 : a.stack > b.stack ? 1 : 0;\n\t\t});\n\n\t\treturn { groups };\n\t}).pipe(Effect.withSpan('orchestrator.lifecycle-prune.inventory'));\n\nconst selectedGroups = (\n\tinventory: LifecyclePruneInventory,\n\tselection: LifecyclePruneSelection,\n): ReadonlyArray<LifecyclePruneGroup> => {\n\tconst selected = new Set(selection.groupKeys);\n\treturn inventory.groups.filter((group) => selected.has(group.key));\n};\n\n/** Defensive enforcement of the per-app-shared pinning rule for a\n * caller-supplied selection. Resolves `selection.groupKeys` against\n * `inventory` and drops any `per-app-shared` group whose app still has\n * a live sibling — reusing the SAME predicate as\n * `defaultLifecyclePruneSelection` so a selection built outside the\n * default path (interactive picker, scripted, programmatic) can never\n * remove a shared group a live sibling depends on. Exported so the\n * invariant is unit-testable without a Docker daemon. */\nexport const enforcePinnedLifecyclePruneSelection = (\n\tinventory: LifecyclePruneInventory,\n\tselection: LifecyclePruneSelection,\n): ReadonlyArray<LifecyclePruneGroup> => {\n\tconst pinned = lifecyclePruneAppsWithLiveSiblings(inventory);\n\treturn selectedGroups(inventory, selection).filter(\n\t\t(group) => !(group.sharedKind === 'per-app-shared' && pinned.has(group.app)),\n\t);\n};\n\n/** Default resource scope for the lifecycle-prune orchestrator —\n * containers + networks + volumes, never images. Surfaces consume\n * this directly so the default never drifts between L3 and L4. */\nexport const DEFAULT_LIFECYCLE_PRUNE_RESOURCES: LifecyclePruneResourceScope = {\n\tcontainers: true,\n\tnetworks: true,\n\tvolumes: true,\n\timages: false,\n};\n\n/** Apps with at least one live non-shared group — their `_per-app_`\n * shared resources stay pinned because something under the app is\n * still running. Exported so L4 surfaces consume the orchestrator's\n * pinning predicate. */\nexport const lifecyclePruneAppsWithLiveSiblings = (\n\tinventory: LifecyclePruneInventory,\n): ReadonlySet<string> => {\n\tconst apps = new Set<string>();\n\tfor (const group of inventory.groups) {\n\t\tif (!group.shared && group.live) apps.add(group.app);\n\t}\n\treturn apps;\n};\n\nconst groupHasResource = (\n\tgroup: LifecyclePruneGroup,\n\tresources: LifecyclePruneResourceScope,\n): boolean =>\n\t(resources.containers && group.containers > 0) ||\n\t(resources.networks && group.networks > 0) ||\n\t(resources.volumes && group.volumes > 0) ||\n\t(resources.images && group.images > 0);\n\n/** Default selection: every non-live group whose shared shape is\n * prunable in non-interactive flows. Surfaces and the live-supervisor\n * command handler share this so the orchestrator owns the policy. */\nexport const defaultLifecyclePruneSelection = (\n\tinventory: LifecyclePruneInventory,\n\tresources: LifecyclePruneResourceScope = DEFAULT_LIFECYCLE_PRUNE_RESOURCES,\n): ReadonlyArray<string> => {\n\tconst pinned = lifecyclePruneAppsWithLiveSiblings(inventory);\n\tconst keys: string[] = [];\n\tfor (const group of inventory.groups) {\n\t\tif (group.live) continue;\n\t\tif (!groupHasResource(group, resources)) continue;\n\t\tif (group.sharedKind === 'per-app-shared' && pinned.has(group.app)) continue;\n\t\tif (group.shared && !group.autoPrunable && group.sharedKind !== 'per-app-shared') continue;\n\t\tkeys.push(group.key);\n\t}\n\treturn keys;\n};\n\nexport const runLifecyclePrune = (\n\toptions: LifecyclePruneOptions,\n\tselection: LifecyclePruneSelection,\n): Effect.Effect<LifecyclePruneSummary, LifecyclePruneError> =>\n\tEffect.gen(function* () {\n\t\tconst inventory = yield* collectLifecyclePruneInventory(options);\n\t\t// Defensive enforcement: a caller-supplied selection (interactive\n\t\t// picker, scripted `--all`, or a programmatic caller) must never\n\t\t// remove a `per-app-shared` group pinned by a live sibling. The\n\t\t// default selection already drops these (see\n\t\t// `defaultLifecyclePruneSelection`); re-applying the SAME predicate\n\t\t// here guarantees the rule holds even when the selection was built\n\t\t// elsewhere.\n\t\tconst groups = enforcePinnedLifecyclePruneSelection(inventory, selection);\n\t\tlet skippedLiveGroups = 0;\n\t\tlet containersRemoved = 0;\n\t\tlet networksRemoved = 0;\n\t\tlet networksSkipped = 0;\n\t\tlet volumesRemoved = 0;\n\t\tlet imagesRemoved = 0;\n\n\t\tconst prunableGroups: Array<LifecyclePruneGroup> = [];\n\t\tfor (const group of groups) {\n\t\t\tif (group.live) {\n\t\t\t\tskippedLiveGroups += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (selection.dryRun) {\n\t\t\t\tprunableGroups.push(group);\n\t\t\t\tif (selection.resources.containers) containersRemoved += group.containers;\n\t\t\t\tif (selection.resources.networks) networksRemoved += group.networks;\n\t\t\t\tif (selection.resources.volumes) volumesRemoved += group.volumes;\n\t\t\t\tif (selection.resources.images) imagesRemoved += group.images;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// TOCTOU close: `collectLifecyclePruneInventory` probed liveness\n\t\t\t// ONCE at the top of this run. A stack that booted in the window\n\t\t\t// between that snapshot and here (a concurrent `devstack up` of a\n\t\t\t// previously-dead stack) would have its containers/networks/volumes\n\t\t\t// removed out from under the freshly-started supervisor. Re-probe\n\t\t\t// each non-router victim's roster via the SAME helper the inventory\n\t\t\t// pass used (`livePidsForStack`) immediately before committing it to\n\t\t\t// removal, and skip any group that came alive since. Router groups\n\t\t\t// carry no per-stack roster (their inventory liveness is\n\t\t\t// `runningContainers`), so they keep the snapshot classification —\n\t\t\t// mirroring the `routerGroup` branch in the inventory pass.\n\t\t\tif (!isRouterGroup(group)) {\n\t\t\t\tconst livePids = yield* livePidsForStack(options.runtimeRoot, group.stack);\n\t\t\t\tif (livePids.length > 0) {\n\t\t\t\t\tskippedLiveGroups += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tprunableGroups.push(group);\n\t\t}\n\n\t\tconst foreignNetworkHolders: Array<ForeignNetworkHolder> = [];\n\t\tconst staleNetworkEndpoints: Array<StaleNetworkEndpoint> = [];\n\t\t// Provide `dockerLayer` ONCE around the whole removal section rather\n\t\t// than re-providing it per group per resource. The inventory pass\n\t\t// already provides it once for its parallel scan; the removal loops\n\t\t// mirror that so the layer's docker-host resolution is built a single\n\t\t// time, not rebuilt for every container/network/volume/image removal.\n\t\t// The per-call `Effect.mapError(failPhase(...))` stays inside each\n\t\t// loop so each removal still carries its phase-specific tag.\n\t\tyield* Effect.gen(function* () {\n\t\t\tif (!selection.dryRun && selection.resources.containers) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tif (isRouterGroup(group)) {\n\t\t\t\t\t\tcontainersRemoved += yield* removeDevstackContainersByKindAndName(\n\t\t\t\t\t\t\tROUTER_KIND_LABEL_VALUE,\n\t\t\t\t\t\t\tgroup.stack,\n\t\t\t\t\t\t).pipe(Effect.mapError(failPhase('remove-containers')));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst match = { app: group.app, stack: group.stack };\n\t\t\t\t\t\tcontainersRemoved += yield* removeDevstackContainers(match).pipe(\n\t\t\t\t\t\t\tEffect.mapError(failPhase('remove-containers')),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.networks) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\tconst result = yield* removeDevstackNetworksBestEffort(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-networks')),\n\t\t\t\t\t);\n\t\t\t\t\tnetworksRemoved += result.removed;\n\t\t\t\t\tnetworksSkipped += result.skippedInUse;\n\t\t\t\t\tfor (const holder of result.foreignHolders) foreignNetworkHolders.push(holder);\n\t\t\t\t\tfor (const ep of result.staleEndpoints) staleNetworkEndpoints.push(ep);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.volumes) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\tvolumesRemoved += yield* removeDevstackVolumes(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-volumes')),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.images) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\timagesRemoved += yield* removeDevstackImages(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-images')),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}).pipe(Effect.provide(dockerLayer));\n\n\t\tif (!selection.dryRun) {\n\t\t\tyield* Effect.gen(function* () {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tif (!isRouterGroup(group)) continue;\n\t\t\t\t\tyield* removeRouterProfileStateForDockerStack({\n\t\t\t\t\t\truntimeRoot: options.runtimeRoot,\n\t\t\t\t\t\trouterStack: group.stack,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}).pipe(\n\t\t\t\tEffect.provide(NodeFileSystem.layer),\n\t\t\t\tEffect.withSpan('orchestrator.lifecycle-prune.removeRouterProfileState'),\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tinspectedGroups: groups.length,\n\t\t\t// Groups actually selected for pruning — `groups.length` minus\n\t\t\t// the live ones skipped above, NOT the inspected total.\n\t\t\tselectedGroups: prunableGroups.length,\n\t\t\tskippedLiveGroups,\n\t\t\tcontainersRemoved,\n\t\t\tnetworksRemoved,\n\t\t\tnetworksSkipped,\n\t\t\tvolumesRemoved,\n\t\t\timagesRemoved,\n\t\t\tforeignNetworkHolders,\n\t\t\tstaleNetworkEndpoints,\n\t\t};\n\t}).pipe(Effect.withSpan('orchestrator.lifecycle-prune.run'));\n"],"mappings":"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"}
@@ -1,58 +1,57 @@
1
+ import { ROUTER_CONTAINER_NAME_PREFIX } from "./sentinels.mjs";
1
2
  import { dispatchFileIdFromFilename, parseDispatchRouteFile } from "./file-provider.mjs";
2
- import { Effect } from "effect";
3
- import { existsSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
3
+ import { Effect, FileSystem } from "effect";
4
4
  import { join } from "node:path";
5
5
  //#region src/orchestrators/router/cleanup.ts
6
- const ROUTER_SHARED_APP = "devstack-router";
7
- const ROUTER_CONTAINER_NAME_PREFIX = "devstack-router-";
8
6
  const routerStateRoot = (runtimeRoot) => join(runtimeRoot, "router");
9
- const safeDirectories = (dir) => {
10
- try {
11
- return readdirSync(dir).filter((entry) => statSync(join(dir, entry)).isDirectory());
12
- } catch {
13
- return [];
7
+ const safeDirectories = (fs, dir) => Effect.gen(function* () {
8
+ const entries = yield* fs.readDirectory(dir).pipe(Effect.catch(() => Effect.succeed([])));
9
+ const directories = [];
10
+ for (const entry of entries) {
11
+ const stat = yield* fs.stat(join(dir, entry)).pipe(Effect.catch(() => Effect.succeed(null)));
12
+ if (stat !== null && stat.type === "Directory") directories.push(entry);
14
13
  }
15
- };
16
- const removeRouterDispatchFilesForStack = (inputs) => Effect.sync(() => {
14
+ return directories;
15
+ });
16
+ const removeRouterDispatchFilesForStack = (inputs) => Effect.gen(function* () {
17
+ const fs = yield* FileSystem.FileSystem;
17
18
  let removed = 0;
18
- for (const profileDir of safeDirectories(routerStateRoot(inputs.runtimeRoot))) {
19
+ for (const profileDir of yield* safeDirectories(fs, routerStateRoot(inputs.runtimeRoot))) {
19
20
  const dispatchDir = join(routerStateRoot(inputs.runtimeRoot), profileDir, "dispatch");
20
- if (!existsSync(dispatchDir)) continue;
21
- for (const filename of readdirSync(dispatchDir)) {
21
+ if (!(yield* fs.exists(dispatchDir).pipe(Effect.catch(() => Effect.succeed(false))))) continue;
22
+ const filenames = yield* fs.readDirectory(dispatchDir).pipe(Effect.catch(() => Effect.succeed([])));
23
+ for (const filename of filenames) {
22
24
  const dispatchFileId = dispatchFileIdFromFilename(filename);
23
25
  if (dispatchFileId === null) continue;
24
26
  const file = join(dispatchDir, filename);
25
- let body;
26
- try {
27
- body = readFileSync(file, "utf8");
28
- } catch {
29
- continue;
30
- }
27
+ const body = yield* fs.readFileString(file).pipe(Effect.catch(() => Effect.succeed(null)));
28
+ if (body === null) continue;
31
29
  const parsed = parseDispatchRouteFile(body, dispatchFileId);
32
30
  if (parsed._tag === "valid" && parsed.route.lease?.app === inputs.app && parsed.route.lease.stack === inputs.stack) {
33
- rmSync(file, { force: true });
31
+ yield* fs.remove(file, { force: true }).pipe(Effect.ignore);
34
32
  removed += 1;
35
33
  }
36
34
  }
37
35
  }
38
36
  return removed;
39
37
  });
40
- const removeRouterProfileStateForDockerStack = (inputs) => Effect.sync(() => {
38
+ const removeRouterProfileStateForDockerStack = (inputs) => Effect.gen(function* () {
41
39
  if (!inputs.routerStack.startsWith("devstack-router-")) return 0;
42
- const fingerprint = inputs.routerStack.slice(16);
40
+ const fingerprint = inputs.routerStack.slice(ROUTER_CONTAINER_NAME_PREFIX.length);
43
41
  if (fingerprint.length === 0) return 0;
42
+ const fs = yield* FileSystem.FileSystem;
44
43
  let removed = 0;
45
- for (const profileDir of safeDirectories(routerStateRoot(inputs.runtimeRoot))) {
44
+ for (const profileDir of yield* safeDirectories(fs, routerStateRoot(inputs.runtimeRoot))) {
46
45
  if (!profileDir.endsWith(`-${fingerprint}`)) continue;
47
- rmSync(join(routerStateRoot(inputs.runtimeRoot), profileDir), {
46
+ yield* fs.remove(join(routerStateRoot(inputs.runtimeRoot), profileDir), {
48
47
  recursive: true,
49
48
  force: true
50
- });
49
+ }).pipe(Effect.ignore);
51
50
  removed += 1;
52
51
  }
53
52
  return removed;
54
53
  });
55
54
  //#endregion
56
- export { ROUTER_CONTAINER_NAME_PREFIX, ROUTER_SHARED_APP, removeRouterDispatchFilesForStack, removeRouterProfileStateForDockerStack };
55
+ export { removeRouterDispatchFilesForStack, removeRouterProfileStateForDockerStack };
57
56
 
58
57
  //# sourceMappingURL=cleanup.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.mjs","names":[],"sources":["../../../src/orchestrators/router/cleanup.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { Effect } from 'effect';\n\nimport { dispatchFileIdFromFilename, parseDispatchRouteFile } from './file-provider.ts';\n\nexport const ROUTER_SHARED_APP = 'devstack-router';\nexport const ROUTER_CONTAINER_NAME_PREFIX = 'devstack-router-';\n\nconst routerStateRoot = (runtimeRoot: string): string => join(runtimeRoot, 'router');\n\nconst safeDirectories = (dir: string): ReadonlyArray<string> => {\n\ttry {\n\t\treturn readdirSync(dir).filter((entry) => statSync(join(dir, entry)).isDirectory());\n\t} catch {\n\t\treturn [];\n\t}\n};\n\nexport const removeRouterDispatchFilesForStack = (inputs: {\n\treadonly runtimeRoot: string;\n\treadonly app: string;\n\treadonly stack: string;\n}): Effect.Effect<number> =>\n\tEffect.sync(() => {\n\t\tlet removed = 0;\n\t\tfor (const profileDir of safeDirectories(routerStateRoot(inputs.runtimeRoot))) {\n\t\t\tconst dispatchDir = join(routerStateRoot(inputs.runtimeRoot), profileDir, 'dispatch');\n\t\t\tif (!existsSync(dispatchDir)) continue;\n\t\t\tfor (const filename of readdirSync(dispatchDir)) {\n\t\t\t\tconst dispatchFileId = dispatchFileIdFromFilename(filename);\n\t\t\t\tif (dispatchFileId === null) continue;\n\t\t\t\tconst file = join(dispatchDir, filename);\n\t\t\t\tlet body: string;\n\t\t\t\ttry {\n\t\t\t\t\tbody = readFileSync(file, 'utf8');\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst parsed = parseDispatchRouteFile(body, dispatchFileId);\n\t\t\t\tif (\n\t\t\t\t\tparsed._tag === 'valid' &&\n\t\t\t\t\tparsed.route.lease?.app === inputs.app &&\n\t\t\t\t\tparsed.route.lease.stack === inputs.stack\n\t\t\t\t) {\n\t\t\t\t\trmSync(file, { force: true });\n\t\t\t\t\tremoved += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn removed;\n\t});\n\nexport const removeRouterProfileStateForDockerStack = (inputs: {\n\treadonly runtimeRoot: string;\n\treadonly routerStack: string;\n}): Effect.Effect<number> =>\n\tEffect.sync(() => {\n\t\tif (!inputs.routerStack.startsWith(ROUTER_CONTAINER_NAME_PREFIX)) return 0;\n\t\tconst fingerprint = inputs.routerStack.slice(ROUTER_CONTAINER_NAME_PREFIX.length);\n\t\tif (fingerprint.length === 0) return 0;\n\n\t\tlet removed = 0;\n\t\tfor (const profileDir of safeDirectories(routerStateRoot(inputs.runtimeRoot))) {\n\t\t\tif (!profileDir.endsWith(`-${fingerprint}`)) continue;\n\t\t\trmSync(join(routerStateRoot(inputs.runtimeRoot), profileDir), {\n\t\t\t\trecursive: true,\n\t\t\t\tforce: true,\n\t\t\t});\n\t\t\tremoved += 1;\n\t\t}\n\t\treturn removed;\n\t});\n"],"mappings":";;;;;AAOA,MAAa,oBAAoB;AACjC,MAAa,+BAA+B;AAE5C,MAAM,mBAAmB,gBAAgC,KAAK,aAAa,SAAS;AAEpF,MAAM,mBAAmB,QAAuC;AAC/D,KAAI;AACH,SAAO,YAAY,IAAI,CAAC,QAAQ,UAAU,SAAS,KAAK,KAAK,MAAM,CAAC,CAAC,aAAa,CAAC;SAC5E;AACP,SAAO,EAAE;;;AAIX,MAAa,qCAAqC,WAKjD,OAAO,WAAW;CACjB,IAAI,UAAU;AACd,MAAK,MAAM,cAAc,gBAAgB,gBAAgB,OAAO,YAAY,CAAC,EAAE;EAC9E,MAAM,cAAc,KAAK,gBAAgB,OAAO,YAAY,EAAE,YAAY,WAAW;AACrF,MAAI,CAAC,WAAW,YAAY,CAAE;AAC9B,OAAK,MAAM,YAAY,YAAY,YAAY,EAAE;GAChD,MAAM,iBAAiB,2BAA2B,SAAS;AAC3D,OAAI,mBAAmB,KAAM;GAC7B,MAAM,OAAO,KAAK,aAAa,SAAS;GACxC,IAAI;AACJ,OAAI;AACH,WAAO,aAAa,MAAM,OAAO;WAC1B;AACP;;GAED,MAAM,SAAS,uBAAuB,MAAM,eAAe;AAC3D,OACC,OAAO,SAAS,WAChB,OAAO,MAAM,OAAO,QAAQ,OAAO,OACnC,OAAO,MAAM,MAAM,UAAU,OAAO,OACnC;AACD,WAAO,MAAM,EAAE,OAAO,MAAM,CAAC;AAC7B,eAAW;;;;AAId,QAAO;EACN;AAEH,MAAa,0CAA0C,WAItD,OAAO,WAAW;AACjB,KAAI,CAAC,OAAO,YAAY,WAAA,mBAAwC,CAAE,QAAO;CACzE,MAAM,cAAc,OAAO,YAAY,MAAM,GAAoC;AACjF,KAAI,YAAY,WAAW,EAAG,QAAO;CAErC,IAAI,UAAU;AACd,MAAK,MAAM,cAAc,gBAAgB,gBAAgB,OAAO,YAAY,CAAC,EAAE;AAC9E,MAAI,CAAC,WAAW,SAAS,IAAI,cAAc,CAAE;AAC7C,SAAO,KAAK,gBAAgB,OAAO,YAAY,EAAE,WAAW,EAAE;GAC7D,WAAW;GACX,OAAO;GACP,CAAC;AACF,aAAW;;AAEZ,QAAO;EACN"}
1
+ {"version":3,"file":"cleanup.mjs","names":[],"sources":["../../../src/orchestrators/router/cleanup.ts"],"sourcesContent":["import { join } from 'node:path';\n\nimport { Effect, FileSystem } from 'effect';\n\nimport { dispatchFileIdFromFilename, parseDispatchRouteFile } from './file-provider.ts';\nimport { ROUTER_CONTAINER_NAME_PREFIX } from './sentinels.ts';\n\nexport { ROUTER_CONTAINER_NAME_PREFIX, ROUTER_SHARED_APP } from './sentinels.ts';\n\nconst routerStateRoot = (runtimeRoot: string): string => join(runtimeRoot, 'router');\n\nconst safeDirectories = (\n\tfs: FileSystem.FileSystem,\n\tdir: string,\n): Effect.Effect<ReadonlyArray<string>> =>\n\tEffect.gen(function* () {\n\t\tconst entries = yield* fs\n\t\t\t.readDirectory(dir)\n\t\t\t.pipe(Effect.catch(() => Effect.succeed<ReadonlyArray<string>>([])));\n\t\tconst directories: Array<string> = [];\n\t\tfor (const entry of entries) {\n\t\t\tconst stat = yield* fs.stat(join(dir, entry)).pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\tif (stat !== null && stat.type === 'Directory') {\n\t\t\t\tdirectories.push(entry);\n\t\t\t}\n\t\t}\n\t\treturn directories;\n\t});\n\nexport const removeRouterDispatchFilesForStack = (inputs: {\n\treadonly runtimeRoot: string;\n\treadonly app: string;\n\treadonly stack: string;\n}): Effect.Effect<number, never, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tlet removed = 0;\n\t\tfor (const profileDir of yield* safeDirectories(fs, routerStateRoot(inputs.runtimeRoot))) {\n\t\t\tconst dispatchDir = join(routerStateRoot(inputs.runtimeRoot), profileDir, 'dispatch');\n\t\t\tconst exists = yield* fs.exists(dispatchDir).pipe(Effect.catch(() => Effect.succeed(false)));\n\t\t\tif (!exists) continue;\n\t\t\tconst filenames = yield* fs\n\t\t\t\t.readDirectory(dispatchDir)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed<ReadonlyArray<string>>([])));\n\t\t\tfor (const filename of filenames) {\n\t\t\t\tconst dispatchFileId = dispatchFileIdFromFilename(filename);\n\t\t\t\tif (dispatchFileId === null) continue;\n\t\t\t\tconst file = join(dispatchDir, filename);\n\t\t\t\tconst body = yield* fs.readFileString(file).pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\t\tif (body === null) continue;\n\t\t\t\tconst parsed = parseDispatchRouteFile(body, dispatchFileId);\n\t\t\t\tif (\n\t\t\t\t\tparsed._tag === 'valid' &&\n\t\t\t\t\tparsed.route.lease?.app === inputs.app &&\n\t\t\t\t\tparsed.route.lease.stack === inputs.stack\n\t\t\t\t) {\n\t\t\t\t\tyield* fs.remove(file, { force: true }).pipe(Effect.ignore);\n\t\t\t\t\tremoved += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn removed;\n\t});\n\nexport const removeRouterProfileStateForDockerStack = (inputs: {\n\treadonly runtimeRoot: string;\n\treadonly routerStack: string;\n}): Effect.Effect<number, never, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tif (!inputs.routerStack.startsWith(ROUTER_CONTAINER_NAME_PREFIX)) return 0;\n\t\tconst fingerprint = inputs.routerStack.slice(ROUTER_CONTAINER_NAME_PREFIX.length);\n\t\tif (fingerprint.length === 0) return 0;\n\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tlet removed = 0;\n\t\tfor (const profileDir of yield* safeDirectories(fs, routerStateRoot(inputs.runtimeRoot))) {\n\t\t\tif (!profileDir.endsWith(`-${fingerprint}`)) continue;\n\t\t\tyield* fs\n\t\t\t\t.remove(join(routerStateRoot(inputs.runtimeRoot), profileDir), {\n\t\t\t\t\trecursive: true,\n\t\t\t\t\tforce: true,\n\t\t\t\t})\n\t\t\t\t.pipe(Effect.ignore);\n\t\t\tremoved += 1;\n\t\t}\n\t\treturn removed;\n\t});\n"],"mappings":";;;;;AASA,MAAM,mBAAmB,gBAAgC,KAAK,aAAa,SAAS;AAEpF,MAAM,mBACL,IACA,QAEA,OAAO,IAAI,aAAa;CACvB,MAAM,UAAU,OAAO,GACrB,cAAc,IAAI,CAClB,KAAK,OAAO,YAAY,OAAO,QAA+B,EAAE,CAAC,CAAC,CAAC;CACrE,MAAM,cAA6B,EAAE;AACrC,MAAK,MAAM,SAAS,SAAS;EAC5B,MAAM,OAAO,OAAO,GAAG,KAAK,KAAK,KAAK,MAAM,CAAC,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC;AAC5F,MAAI,SAAS,QAAQ,KAAK,SAAS,YAClC,aAAY,KAAK,MAAM;;AAGzB,QAAO;EACN;AAEH,MAAa,qCAAqC,WAKjD,OAAO,IAAI,aAAa;CACvB,MAAM,KAAK,OAAO,WAAW;CAC7B,IAAI,UAAU;AACd,MAAK,MAAM,cAAc,OAAO,gBAAgB,IAAI,gBAAgB,OAAO,YAAY,CAAC,EAAE;EACzF,MAAM,cAAc,KAAK,gBAAgB,OAAO,YAAY,EAAE,YAAY,WAAW;AAErF,MAAI,EAAC,OADiB,GAAG,OAAO,YAAY,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,CAAC,EAC/E;EACb,MAAM,YAAY,OAAO,GACvB,cAAc,YAAY,CAC1B,KAAK,OAAO,YAAY,OAAO,QAA+B,EAAE,CAAC,CAAC,CAAC;AACrE,OAAK,MAAM,YAAY,WAAW;GACjC,MAAM,iBAAiB,2BAA2B,SAAS;AAC3D,OAAI,mBAAmB,KAAM;GAC7B,MAAM,OAAO,KAAK,aAAa,SAAS;GACxC,MAAM,OAAO,OAAO,GAAG,eAAe,KAAK,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC;AAC1F,OAAI,SAAS,KAAM;GACnB,MAAM,SAAS,uBAAuB,MAAM,eAAe;AAC3D,OACC,OAAO,SAAS,WAChB,OAAO,MAAM,OAAO,QAAQ,OAAO,OACnC,OAAO,MAAM,MAAM,UAAU,OAAO,OACnC;AACD,WAAO,GAAG,OAAO,MAAM,EAAE,OAAO,MAAM,CAAC,CAAC,KAAK,OAAO,OAAO;AAC3D,eAAW;;;;AAId,QAAO;EACN;AAEH,MAAa,0CAA0C,WAItD,OAAO,IAAI,aAAa;AACvB,KAAI,CAAC,OAAO,YAAY,WAAA,mBAAwC,CAAE,QAAO;CACzE,MAAM,cAAc,OAAO,YAAY,MAAM,6BAA6B,OAAO;AACjF,KAAI,YAAY,WAAW,EAAG,QAAO;CAErC,MAAM,KAAK,OAAO,WAAW;CAC7B,IAAI,UAAU;AACd,MAAK,MAAM,cAAc,OAAO,gBAAgB,IAAI,gBAAgB,OAAO,YAAY,CAAC,EAAE;AACzF,MAAI,CAAC,WAAW,SAAS,IAAI,cAAc,CAAE;AAC7C,SAAO,GACL,OAAO,KAAK,gBAAgB,OAAO,YAAY,EAAE,WAAW,EAAE;GAC9D,WAAW;GACX,OAAO;GACP,CAAC,CACD,KAAK,OAAO,OAAO;AACrB,aAAW;;AAEZ,QAAO;EACN"}
@@ -1,7 +1,7 @@
1
1
  import { EntrypointConflict, UnknownEntrypoint } from "./errors.mjs";
2
2
  import { Context, Effect, Layer } from "effect";
3
3
  //#region src/orchestrators/router/entrypoints.ts
4
- var EntrypointRegistry = class extends Context.Service()("@devstack-rewrite/orchestrators/router/EntrypointRegistry") {};
4
+ var EntrypointRegistry = class extends Context.Service()("@devstack/orchestrators/router/EntrypointRegistry") {};
5
5
  const entrypointFamily = (protocol) => protocol === "tcp" ? "tcp" : "http";
6
6
  /** Build a registry from a literal seed. Idempotent on identical
7
7
  * `(name, port, protocol)` triples; throws synchronously on conflict
@@ -75,6 +75,6 @@ const makeEntrypointRegistry = (seed) => {
75
75
  * registry — they pick names from it via their Routable contributions. */
76
76
  const layerEntrypointRegistry = (seed) => Layer.succeed(EntrypointRegistry)(makeEntrypointRegistry(seed));
77
77
  //#endregion
78
- export { EntrypointRegistry, layerEntrypointRegistry };
78
+ export { EntrypointRegistry, layerEntrypointRegistry, makeEntrypointRegistry };
79
79
 
80
80
  //# sourceMappingURL=entrypoints.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"entrypoints.mjs","names":[],"sources":["../../../src/orchestrators/router/entrypoints.ts"],"sourcesContent":["// Entrypoint registry — a process-global allowlist of named host\n// ports the Traefik container will listen on.\n//\n// Architecture invariants:\n// #6 — Routables select an entrypoint by *name* from a closed set\n// populated at module-load time. The set is read ONCE at\n// router launch.\n// #12 — Every entrypoint is plain HTTP / h2c / TCP; no TLS surface.\n// TCP entrypoints bind a host port and dispatch raw bytes to a\n// single backend (postgres, redis, …); they cannot share the\n// port across parallel stacks because TCP has no virtual-host\n// concept. HTTP/h2c entrypoints DO share ports via Host-header\n// dispatch.\n//\n// The registry is intentionally a typed Context service (Effect v4\n// idiom) rather than a module-mutable map: that lets tests build a\n// fresh registry per scenario, lets the orchestrator carry the\n// registration set forward immutably, and removes the\n// `ATTACHED_FOLLOWERS`-style hidden globals the architecture's\n// \"What's NOT in it\" section forbids.\n//\n// Construction surface:\n// `makeEntrypointRegistry(...)` — pure constructor; throws synchronously\n// on conflict registration (architecture invariant #6 is \"throw at\n// registration time\", not at acquire time).\n//\n// Lookup surface:\n// `EntrypointRegistry.byName(name)` — typed yieldable that fails\n// `UnknownEntrypoint` if the caller spliced an unregistered name.\n\nimport { Context, Effect, Layer } from 'effect';\n\nimport type { EntrypointDecl } from '../../contracts/routable.ts';\nimport { EntrypointConflict, UnknownEntrypoint } from './errors.ts';\n\n/** A named entrypoint — a host port the Traefik container binds and\n * listens on. `http`/`h2c` entrypoints support Host-header dispatch\n * (one port → many backends). `tcp` entrypoints are point-to-point\n * (one port → one backend) — siblings dial them directly. */\nexport type Entrypoint = EntrypointDecl;\n\n/** Registry shape. Closed map from declared name → canonical listener.\n * HTTP-family declarations may share a port; Traefik still needs one\n * listener name for that port, so aliases resolve to the first\n * registered listener. */\nexport interface EntrypointRegistryShape {\n\treadonly byName: (name: string) => Effect.Effect<Entrypoint, UnknownEntrypoint>;\n\treadonly all: () => ReadonlyArray<Entrypoint>;\n}\n\nexport class EntrypointRegistry extends Context.Service<\n\tEntrypointRegistry,\n\tEntrypointRegistryShape\n>()('@devstack-rewrite/orchestrators/router/EntrypointRegistry') {}\n\nconst entrypointFamily = (protocol: Entrypoint['protocol']): 'http' | 'tcp' =>\n\tprotocol === 'tcp' ? 'tcp' : 'http';\n\n/** Build a registry from a literal seed. Idempotent on identical\n * `(name, port, protocol)` triples; throws synchronously on conflict\n * per architecture invariant #6. The reason this is synchronous\n * rather than yielded: registration is module-load wiring, not a\n * runtime effect — the failure mode is \"two callers wired the same\n * name to different ports\", which is a build-time bug.\n *\n * Traefik cannot bind multiple entrypoints to the same container port.\n * For HTTP-family aliases on one port, keep every declared name\n * lookupable but resolve those aliases to the first listener registered\n * for the port. */\nexport const makeEntrypointRegistry = (\n\tseed: ReadonlyArray<Entrypoint>,\n): EntrypointRegistryShape => {\n\tconst declaredByName = new Map<string, Entrypoint>();\n\tconst lookupByName = new Map<string, Entrypoint>();\n\tconst listenerByPort = new Map<number, Entrypoint>();\n\tfor (const e of seed) {\n\t\tconst existing = declaredByName.get(e.name);\n\t\tif (existing) {\n\t\t\tif (existing.port === e.port && existing.protocol === e.protocol) continue;\n\t\t\tthrow new EntrypointConflict({\n\t\t\t\tname: e.name,\n\t\t\t\texisting: { port: existing.port, protocol: existing.protocol },\n\t\t\t\tattempted: { port: e.port, protocol: e.protocol },\n\t\t\t});\n\t\t}\n\t\tdeclaredByName.set(e.name, e);\n\n\t\tconst listener = listenerByPort.get(e.port);\n\t\tif (listener) {\n\t\t\tconst listenerFamily = entrypointFamily(listener.protocol);\n\t\t\tconst attemptedFamily = entrypointFamily(e.protocol);\n\t\t\tif (listenerFamily === 'tcp' || listenerFamily !== attemptedFamily) {\n\t\t\t\tthrow new EntrypointConflict({\n\t\t\t\t\tname: e.name,\n\t\t\t\t\texisting: { port: listener.port, protocol: listener.protocol },\n\t\t\t\t\tattempted: { port: e.port, protocol: e.protocol },\n\t\t\t\t});\n\t\t\t}\n\t\t\tlookupByName.set(e.name, listener);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlistenerByPort.set(e.port, e);\n\t\tlookupByName.set(e.name, e);\n\t}\n\tconst frozenListeners: ReadonlyArray<Entrypoint> = Array.from(listenerByPort.values());\n\tconst knownNames = Array.from(declaredByName.keys());\n\treturn {\n\t\tbyName: (name) => {\n\t\t\tconst hit = lookupByName.get(name);\n\t\t\tif (hit) return Effect.succeed(hit);\n\t\t\treturn Effect.fail(\n\t\t\t\tnew UnknownEntrypoint({\n\t\t\t\t\tname,\n\t\t\t\t\tknown: knownNames,\n\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t\tall: () => frozenListeners,\n\t};\n};\n\n/** Layer that pins the registry from a literal seed. The orchestrator\n * takes this as a substrate dependency; plugins do NOT mutate the\n * registry — they pick names from it via their Routable contributions. */\nexport const layerEntrypointRegistry = (\n\tseed: ReadonlyArray<Entrypoint>,\n): Layer.Layer<EntrypointRegistry> =>\n\tLayer.succeed(EntrypointRegistry)(makeEntrypointRegistry(seed));\n"],"mappings":";;;AAkDA,IAAa,qBAAb,cAAwC,QAAQ,SAG7C,CAAC,4DAA4D,CAAC;AAEjE,MAAM,oBAAoB,aACzB,aAAa,QAAQ,QAAQ;;;;;;;;;;;;AAa9B,MAAa,0BACZ,SAC6B;CAC7B,MAAM,iCAAiB,IAAI,KAAyB;CACpD,MAAM,+BAAe,IAAI,KAAyB;CAClD,MAAM,iCAAiB,IAAI,KAAyB;AACpD,MAAK,MAAM,KAAK,MAAM;EACrB,MAAM,WAAW,eAAe,IAAI,EAAE,KAAK;AAC3C,MAAI,UAAU;AACb,OAAI,SAAS,SAAS,EAAE,QAAQ,SAAS,aAAa,EAAE,SAAU;AAClE,SAAM,IAAI,mBAAmB;IAC5B,MAAM,EAAE;IACR,UAAU;KAAE,MAAM,SAAS;KAAM,UAAU,SAAS;KAAU;IAC9D,WAAW;KAAE,MAAM,EAAE;KAAM,UAAU,EAAE;KAAU;IACjD,CAAC;;AAEH,iBAAe,IAAI,EAAE,MAAM,EAAE;EAE7B,MAAM,WAAW,eAAe,IAAI,EAAE,KAAK;AAC3C,MAAI,UAAU;GACb,MAAM,iBAAiB,iBAAiB,SAAS,SAAS;GAC1D,MAAM,kBAAkB,iBAAiB,EAAE,SAAS;AACpD,OAAI,mBAAmB,SAAS,mBAAmB,gBAClD,OAAM,IAAI,mBAAmB;IAC5B,MAAM,EAAE;IACR,UAAU;KAAE,MAAM,SAAS;KAAM,UAAU,SAAS;KAAU;IAC9D,WAAW;KAAE,MAAM,EAAE;KAAM,UAAU,EAAE;KAAU;IACjD,CAAC;AAEH,gBAAa,IAAI,EAAE,MAAM,SAAS;AAClC;;AAGD,iBAAe,IAAI,EAAE,MAAM,EAAE;AAC7B,eAAa,IAAI,EAAE,MAAM,EAAE;;CAE5B,MAAM,kBAA6C,MAAM,KAAK,eAAe,QAAQ,CAAC;CACtF,MAAM,aAAa,MAAM,KAAK,eAAe,MAAM,CAAC;AACpD,QAAO;EACN,SAAS,SAAS;GACjB,MAAM,MAAM,aAAa,IAAI,KAAK;AAClC,OAAI,IAAK,QAAO,OAAO,QAAQ,IAAI;AACnC,UAAO,OAAO,KACb,IAAI,kBAAkB;IACrB;IACA,OAAO;IACP,CAAC,CACF;;EAEF,WAAW;EACX;;;;;AAMF,MAAa,2BACZ,SAEA,MAAM,QAAQ,mBAAmB,CAAC,uBAAuB,KAAK,CAAC"}
1
+ {"version":3,"file":"entrypoints.mjs","names":[],"sources":["../../../src/orchestrators/router/entrypoints.ts"],"sourcesContent":["// Entrypoint registry — a process-global allowlist of named host\n// ports the Traefik container will listen on.\n//\n// Architecture invariants:\n// #6 — Routables select an entrypoint by *name* from a closed set\n// populated at module-load time. The set is read ONCE at\n// router launch.\n// #12 — Every entrypoint is plain HTTP / h2c / TCP; no TLS surface.\n// TCP entrypoints bind a host port and dispatch raw bytes to a\n// single backend (postgres, redis, …); they cannot share the\n// port across parallel stacks because TCP has no virtual-host\n// concept. HTTP/h2c entrypoints DO share ports via Host-header\n// dispatch.\n//\n// The registry is intentionally a typed Context service (Effect v4\n// idiom) rather than a module-mutable map: that lets tests build a\n// fresh registry per scenario, lets the orchestrator carry the\n// registration set forward immutably, and removes the\n// `ATTACHED_FOLLOWERS`-style hidden globals the architecture's\n// \"What's NOT in it\" section forbids.\n//\n// Construction surface:\n// `makeEntrypointRegistry(...)` — pure constructor; throws synchronously\n// on conflict registration (architecture invariant #6 is \"throw at\n// registration time\", not at acquire time).\n//\n// Lookup surface:\n// `EntrypointRegistry.byName(name)` — typed yieldable that fails\n// `UnknownEntrypoint` if the caller spliced an unregistered name.\n\nimport { Context, Effect, Layer } from 'effect';\n\nimport type { EntrypointDecl } from '../../contracts/routable.ts';\nimport { EntrypointConflict, UnknownEntrypoint } from './errors.ts';\n\n/** A named entrypoint — a host port the Traefik container binds and\n * listens on. `http`/`h2c` entrypoints support Host-header dispatch\n * (one port → many backends). `tcp` entrypoints are point-to-point\n * (one port → one backend) — siblings dial them directly. */\nexport type Entrypoint = EntrypointDecl;\n\n/** Registry shape. Closed map from declared name → canonical listener.\n * HTTP-family declarations may share a port; Traefik still needs one\n * listener name for that port, so aliases resolve to the first\n * registered listener. */\nexport interface EntrypointRegistryShape {\n\treadonly byName: (name: string) => Effect.Effect<Entrypoint, UnknownEntrypoint>;\n\treadonly all: () => ReadonlyArray<Entrypoint>;\n}\n\nexport class EntrypointRegistry extends Context.Service<\n\tEntrypointRegistry,\n\tEntrypointRegistryShape\n>()('@devstack/orchestrators/router/EntrypointRegistry') {}\n\nconst entrypointFamily = (protocol: Entrypoint['protocol']): 'http' | 'tcp' =>\n\tprotocol === 'tcp' ? 'tcp' : 'http';\n\n/** Build a registry from a literal seed. Idempotent on identical\n * `(name, port, protocol)` triples; throws synchronously on conflict\n * per architecture invariant #6. The reason this is synchronous\n * rather than yielded: registration is module-load wiring, not a\n * runtime effect — the failure mode is \"two callers wired the same\n * name to different ports\", which is a build-time bug.\n *\n * Traefik cannot bind multiple entrypoints to the same container port.\n * For HTTP-family aliases on one port, keep every declared name\n * lookupable but resolve those aliases to the first listener registered\n * for the port. */\nexport const makeEntrypointRegistry = (\n\tseed: ReadonlyArray<Entrypoint>,\n): EntrypointRegistryShape => {\n\tconst declaredByName = new Map<string, Entrypoint>();\n\tconst lookupByName = new Map<string, Entrypoint>();\n\tconst listenerByPort = new Map<number, Entrypoint>();\n\tfor (const e of seed) {\n\t\tconst existing = declaredByName.get(e.name);\n\t\tif (existing) {\n\t\t\tif (existing.port === e.port && existing.protocol === e.protocol) continue;\n\t\t\tthrow new EntrypointConflict({\n\t\t\t\tname: e.name,\n\t\t\t\texisting: { port: existing.port, protocol: existing.protocol },\n\t\t\t\tattempted: { port: e.port, protocol: e.protocol },\n\t\t\t});\n\t\t}\n\t\tdeclaredByName.set(e.name, e);\n\n\t\tconst listener = listenerByPort.get(e.port);\n\t\tif (listener) {\n\t\t\tconst listenerFamily = entrypointFamily(listener.protocol);\n\t\t\tconst attemptedFamily = entrypointFamily(e.protocol);\n\t\t\tif (listenerFamily === 'tcp' || listenerFamily !== attemptedFamily) {\n\t\t\t\tthrow new EntrypointConflict({\n\t\t\t\t\tname: e.name,\n\t\t\t\t\texisting: { port: listener.port, protocol: listener.protocol },\n\t\t\t\t\tattempted: { port: e.port, protocol: e.protocol },\n\t\t\t\t});\n\t\t\t}\n\t\t\tlookupByName.set(e.name, listener);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlistenerByPort.set(e.port, e);\n\t\tlookupByName.set(e.name, e);\n\t}\n\tconst frozenListeners: ReadonlyArray<Entrypoint> = Array.from(listenerByPort.values());\n\tconst knownNames = Array.from(declaredByName.keys());\n\treturn {\n\t\tbyName: (name) => {\n\t\t\tconst hit = lookupByName.get(name);\n\t\t\tif (hit) return Effect.succeed(hit);\n\t\t\treturn Effect.fail(\n\t\t\t\tnew UnknownEntrypoint({\n\t\t\t\t\tname,\n\t\t\t\t\tknown: knownNames,\n\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t\tall: () => frozenListeners,\n\t};\n};\n\n/** Layer that pins the registry from a literal seed. The orchestrator\n * takes this as a substrate dependency; plugins do NOT mutate the\n * registry — they pick names from it via their Routable contributions. */\nexport const layerEntrypointRegistry = (\n\tseed: ReadonlyArray<Entrypoint>,\n): Layer.Layer<EntrypointRegistry> =>\n\tLayer.succeed(EntrypointRegistry)(makeEntrypointRegistry(seed));\n"],"mappings":";;;AAkDA,IAAa,qBAAb,cAAwC,QAAQ,SAG7C,CAAC,oDAAoD,CAAC;AAEzD,MAAM,oBAAoB,aACzB,aAAa,QAAQ,QAAQ;;;;;;;;;;;;AAa9B,MAAa,0BACZ,SAC6B;CAC7B,MAAM,iCAAiB,IAAI,KAAyB;CACpD,MAAM,+BAAe,IAAI,KAAyB;CAClD,MAAM,iCAAiB,IAAI,KAAyB;AACpD,MAAK,MAAM,KAAK,MAAM;EACrB,MAAM,WAAW,eAAe,IAAI,EAAE,KAAK;AAC3C,MAAI,UAAU;AACb,OAAI,SAAS,SAAS,EAAE,QAAQ,SAAS,aAAa,EAAE,SAAU;AAClE,SAAM,IAAI,mBAAmB;IAC5B,MAAM,EAAE;IACR,UAAU;KAAE,MAAM,SAAS;KAAM,UAAU,SAAS;KAAU;IAC9D,WAAW;KAAE,MAAM,EAAE;KAAM,UAAU,EAAE;KAAU;IACjD,CAAC;;AAEH,iBAAe,IAAI,EAAE,MAAM,EAAE;EAE7B,MAAM,WAAW,eAAe,IAAI,EAAE,KAAK;AAC3C,MAAI,UAAU;GACb,MAAM,iBAAiB,iBAAiB,SAAS,SAAS;GAC1D,MAAM,kBAAkB,iBAAiB,EAAE,SAAS;AACpD,OAAI,mBAAmB,SAAS,mBAAmB,gBAClD,OAAM,IAAI,mBAAmB;IAC5B,MAAM,EAAE;IACR,UAAU;KAAE,MAAM,SAAS;KAAM,UAAU,SAAS;KAAU;IAC9D,WAAW;KAAE,MAAM,EAAE;KAAM,UAAU,EAAE;KAAU;IACjD,CAAC;AAEH,gBAAa,IAAI,EAAE,MAAM,SAAS;AAClC;;AAGD,iBAAe,IAAI,EAAE,MAAM,EAAE;AAC7B,eAAa,IAAI,EAAE,MAAM,EAAE;;CAE5B,MAAM,kBAA6C,MAAM,KAAK,eAAe,QAAQ,CAAC;CACtF,MAAM,aAAa,MAAM,KAAK,eAAe,MAAM,CAAC;AACpD,QAAO;EACN,SAAS,SAAS;GACjB,MAAM,MAAM,aAAa,IAAI,KAAK;AAClC,OAAI,IAAK,QAAO,OAAO,QAAQ,IAAI;AACnC,UAAO,OAAO,KACb,IAAI,kBAAkB;IACrB;IACA,OAAO;IACP,CAAC,CACF;;EAEF,WAAW;EACX;;;;;AAMF,MAAa,2BACZ,SAEA,MAAM,QAAQ,mBAAmB,CAAC,uBAAuB,KAAK,CAAC"}
@@ -1,13 +1,21 @@
1
1
  import { RouteCollision, RouterValidationError } from "./errors.mjs";
2
- import { dispatchFileId, renderUrl, routerHostname } from "./hostname.mjs";
3
2
  import { CORS_MIDDLEWARE_NAME } from "./cors.mjs";
3
+ import { renderUrl } from "../../substrate/runtime/routed-url.mjs";
4
+ import { dispatchFileId, routerHostname } from "./hostname.mjs";
4
5
  import { Effect } from "effect";
5
6
  //#region src/orchestrators/router/file-provider.ts
6
7
  const ROUTE_READINESS_HEADER = "X-Devstack-Route-Id";
8
+ /** Dispatch-file filename prefix. `10-` sorts behind the shared CORS
9
+ * middleware (`00-`) so Traefik's file-provider loads the middleware
10
+ * before any router that references it. Single source of truth for
11
+ * the file-write side (`dispatchFilename`) and the file-read side
12
+ * (`dispatchFileIdFromFilename`) — keep them in lockstep here. */
13
+ const DISPATCH_FILENAME_PREFIX = "10-";
7
14
  /** Filename within the dispatch directory for a given file-id. The
8
- * `10-` prefix sorts behind the shared CORS middleware (`00-`) so
9
- * Traefik picks up the middleware before any router referencing it. */
10
- const dispatchFilename = (fileId) => `10-${fileId}.yml`;
15
+ * prefix (`DISPATCH_FILENAME_PREFIX`) sorts behind the shared CORS
16
+ * middleware (`00-`) so Traefik picks up the middleware before any
17
+ * router referencing it. */
18
+ const dispatchFilename = (fileId) => `${DISPATCH_FILENAME_PREFIX}${fileId}.yml`;
11
19
  /** Resolve a single Routable into a ResolvedRoute. Pure-ish: the only
12
20
  * effectful bit is the upstream resolver (which talks to docker /
13
21
  * the port broker) and the entrypoint lookup.
@@ -143,7 +151,8 @@ const renderTcpRouteYaml = (route, lease) => {
143
151
  ].join("\n");
144
152
  };
145
153
  const dispatchFileIdFromFilename = (filename) => {
146
- return /^10-(.+)\.yml$/.exec(filename)?.[1] ?? null;
154
+ const escapedPrefix = DISPATCH_FILENAME_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
155
+ return new RegExp(`^${escapedPrefix}(.+)\\.yml$`).exec(filename)?.[1] ?? null;
147
156
  };
148
157
  const commentValue = (body, key) => {
149
158
  const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");