@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,94 @@
1
+ import { atomicWriteJsonSync } from "./runtime/atomic-write.mjs";
2
+ import { decodeJsonText, decodeJsonTextSync } from "./runtime/runtime-decode.mjs";
3
+ import { Effect } from "effect";
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ //#region src/substrate/versioned-doc-sync.ts
6
+ /**
7
+ * Read + decode a versioned cross-process document from disk.
8
+ *
9
+ * - Missing file (`!existsSync`) → returns the caller-supplied
10
+ * `whenAbsent` default. This is the "no peer has ever written yet"
11
+ * path; the caller decides whether that's an empty document or a
12
+ * failure.
13
+ * - Read failure (any `node:fs` error other than ENOENT) → `mkIo`.
14
+ * - JSON-parse OR Schema-decode failure → `mkCorrupt` with the raw
15
+ * bytes echoed so callers can log the offending content.
16
+ *
17
+ * The decode runs through `decodeJsonText`, so the on-disk JSON is
18
+ * parsed and Schema-validated in one envelope. No atomicity, fsync, or
19
+ * lock-ordering concerns at the read site — this is pure read-and-
20
+ * validate.
21
+ */
22
+ const readVersionedDocumentSync = (path, schema, errors, whenAbsent) => Effect.gen(function* () {
23
+ if (!existsSync(path)) return whenAbsent;
24
+ const raw = yield* Effect.try({
25
+ try: () => readFileSync(path, "utf8"),
26
+ catch: (cause) => errors.mkIo({
27
+ path,
28
+ cause
29
+ })
30
+ });
31
+ return yield* decodeJsonText(schema, raw, {
32
+ source: path,
33
+ mkError: (issue) => errors.mkCorrupt({
34
+ path,
35
+ raw,
36
+ cause: issue.cause ?? issue
37
+ })
38
+ });
39
+ });
40
+ /**
41
+ * Sync parse-or-null variant of `readVersionedDocumentSync`. Decodes a
42
+ * raw JSON body against `schema`; returns `null` on parse or decode
43
+ * failure. Use this when a malformed body should be treated the same as
44
+ * a missing body (e.g. half-written reservation by a crashed creator,
45
+ * stack-lock body interrupted mid-write) — the call site swaps a
46
+ * try/catch around `decodeJsonTextSync` for one helper call.
47
+ *
48
+ * `source` is purely a diagnostic label for the synthesized error
49
+ * inside `decodeJsonTextSync`'s throw; it is consumed by the catch and
50
+ * discarded along with the error. The caller does not need to thread a
51
+ * typed error constructor — the contract here is "null means
52
+ * unreadable/malformed; the caller decides what to do next".
53
+ */
54
+ const parseVersionedDocumentBodyOrNull = (raw, schema, source) => {
55
+ try {
56
+ return decodeJsonTextSync(schema, raw, {
57
+ source,
58
+ mkError: (issue) => issue
59
+ });
60
+ } catch {
61
+ return null;
62
+ }
63
+ };
64
+ /**
65
+ * Write a versioned cross-process document atomically.
66
+ *
67
+ * Routes through the canonical `atomicWriteJsonSync` primitive — the
68
+ * NFS-safe mkdir-parent → O_EXCL temp → write → fsync → rename dance
69
+ * with one owner of the tempfile cleanup. The version stamp lives in
70
+ * the caller-supplied `value` (every versioned-doc schema carries a
71
+ * literal `version` field via `versionedDocSchema`); this helper does
72
+ * NOT inject a version — it just ensures the typed write envelope
73
+ * (Effect.try → mkIo) is consistent across cross-process sites.
74
+ *
75
+ * No Schema-encode step here: matching `atomicWriteJsonSync`'s contract,
76
+ * cross-process docs are `Schema.Struct` of primitives that round-trip
77
+ * cleanly through plain `JSON.stringify`. The `value` is typed as a
78
+ * generic `D` so the caller's document interface (`RosterDocument`,
79
+ * `ContainerClaimDocument`, ...) is what gets serialized.
80
+ *
81
+ * This is the SYNC surface — used by callers that hold `stack.lock`
82
+ * and must keep their critical section non-yielding.
83
+ */
84
+ const writeVersionedDocumentSync = (path, value, errors) => Effect.try({
85
+ try: () => atomicWriteJsonSync(path, value),
86
+ catch: (cause) => errors.mkIo({
87
+ path,
88
+ cause
89
+ })
90
+ });
91
+ //#endregion
92
+ export { parseVersionedDocumentBodyOrNull, readVersionedDocumentSync, writeVersionedDocumentSync };
93
+
94
+ //# sourceMappingURL=versioned-doc-sync.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versioned-doc-sync.mjs","names":[],"sources":["../../src/substrate/versioned-doc-sync.ts"],"sourcesContent":["// Versioned cross-process document — read / write envelope helpers.\n//\n// Three cross-process modules (`runtime/cross-process/{roster,\n// snapshot-reservation, stack-lock}.ts`) historically re-implemented the\n// same `Effect.try(readFileSync) → decodeJsonText → mkIoError|\n// mkCorruptError` envelope and the same `Effect.try(atomicWriteJsonSync)\n// → mkIoError` envelope, each with a different typed-error pair. This\n// file centralizes both envelopes around the versioned-doc schemas\n// constructed via `versionedDocSchema` (the sibling file). The caller\n// supplies the error constructors so its typed error channel is\n// preserved end-to-end; nothing about NFS-safe atomicity, fsync\n// ordering, or version-stamp handling moves out of the underlying\n// primitives.\n//\n// Lives at substrate L0+ — depends on `effect`, `node:fs`, the\n// runtime-decode helpers, and the canonical atomic-write primitive.\n// Kept as a sibling of `versioned-doc-schema.ts` rather than absorbed\n// into it so the schema constructor remains pure-Schema (callable from\n// any L0 declaration site such as `substrate/cross-process.ts`); only\n// callers that actually round-trip a versioned doc to disk pay the\n// fs/runtime-decode import cost.\n\nimport { existsSync, readFileSync } from 'node:fs';\n\nimport { Effect, Schema } from 'effect';\n\nimport { atomicWriteJsonSync } from './runtime/atomic-write.ts';\nimport { decodeJsonText, decodeJsonTextSync } from './runtime/runtime-decode.ts';\n\n/** Constructor pair the caller supplies so its typed error channel is\n * preserved through the envelope. `mkIo` wraps any node:fs / disk\n * failure (read OR write); `mkCorrupt` wraps any JSON-parse / Schema-\n * decode failure (read only). Both helpers thread the on-disk `path`\n * and — for `mkCorrupt` — the raw bytes that failed to decode so\n * diagnostics can echo what was actually on disk. */\nexport interface VersionedDocErrors<IoErr, CorruptErr> {\n\treadonly mkIo: (input: { readonly path: string; readonly cause: unknown }) => IoErr;\n\treadonly mkCorrupt: (input: {\n\t\treadonly path: string;\n\t\treadonly raw: string;\n\t\treadonly cause: unknown;\n\t}) => CorruptErr;\n}\n\n/** Constructor for the IO-only case (write path — no decode failure\n * mode). Split into its own interface so `writeVersionedDocumentSync`\n * callers don't have to supply a `mkCorrupt` they would never invoke. */\nexport interface VersionedDocIoError<IoErr> {\n\treadonly mkIo: (input: { readonly path: string; readonly cause: unknown }) => IoErr;\n}\n\n/**\n * Read + decode a versioned cross-process document from disk.\n *\n * - Missing file (`!existsSync`) → returns the caller-supplied\n * `whenAbsent` default. This is the \"no peer has ever written yet\"\n * path; the caller decides whether that's an empty document or a\n * failure.\n * - Read failure (any `node:fs` error other than ENOENT) → `mkIo`.\n * - JSON-parse OR Schema-decode failure → `mkCorrupt` with the raw\n * bytes echoed so callers can log the offending content.\n *\n * The decode runs through `decodeJsonText`, so the on-disk JSON is\n * parsed and Schema-validated in one envelope. No atomicity, fsync, or\n * lock-ordering concerns at the read site — this is pure read-and-\n * validate.\n */\nexport const readVersionedDocumentSync = <\n\tS extends Schema.Decoder<unknown>,\n\tIoErr,\n\tCorruptErr,\n>(\n\tpath: string,\n\tschema: S,\n\terrors: VersionedDocErrors<IoErr, CorruptErr>,\n\twhenAbsent: S['Type'],\n): Effect.Effect<S['Type'], IoErr | CorruptErr> =>\n\tEffect.gen(function* () {\n\t\tif (!existsSync(path)) return whenAbsent;\n\t\tconst raw = yield* Effect.try({\n\t\t\ttry: () => readFileSync(path, 'utf8'),\n\t\t\tcatch: (cause) => errors.mkIo({ path, cause }),\n\t\t});\n\t\treturn yield* decodeJsonText(schema, raw, {\n\t\t\tsource: path,\n\t\t\tmkError: (issue) => errors.mkCorrupt({ path, raw, cause: issue.cause ?? issue }),\n\t\t});\n\t});\n\n/**\n * Sync parse-or-null variant of `readVersionedDocumentSync`. Decodes a\n * raw JSON body against `schema`; returns `null` on parse or decode\n * failure. Use this when a malformed body should be treated the same as\n * a missing body (e.g. half-written reservation by a crashed creator,\n * stack-lock body interrupted mid-write) — the call site swaps a\n * try/catch around `decodeJsonTextSync` for one helper call.\n *\n * `source` is purely a diagnostic label for the synthesized error\n * inside `decodeJsonTextSync`'s throw; it is consumed by the catch and\n * discarded along with the error. The caller does not need to thread a\n * typed error constructor — the contract here is \"null means\n * unreadable/malformed; the caller decides what to do next\".\n */\nexport const parseVersionedDocumentBodyOrNull = <S extends Schema.Decoder<unknown>>(\n\traw: string,\n\tschema: S,\n\tsource: string,\n): S['Type'] | null => {\n\ttry {\n\t\treturn decodeJsonTextSync(schema, raw, {\n\t\t\tsource,\n\t\t\tmkError: (issue) => issue,\n\t\t});\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n/**\n * Write a versioned cross-process document atomically.\n *\n * Routes through the canonical `atomicWriteJsonSync` primitive — the\n * NFS-safe mkdir-parent → O_EXCL temp → write → fsync → rename dance\n * with one owner of the tempfile cleanup. The version stamp lives in\n * the caller-supplied `value` (every versioned-doc schema carries a\n * literal `version` field via `versionedDocSchema`); this helper does\n * NOT inject a version — it just ensures the typed write envelope\n * (Effect.try → mkIo) is consistent across cross-process sites.\n *\n * No Schema-encode step here: matching `atomicWriteJsonSync`'s contract,\n * cross-process docs are `Schema.Struct` of primitives that round-trip\n * cleanly through plain `JSON.stringify`. The `value` is typed as a\n * generic `D` so the caller's document interface (`RosterDocument`,\n * `ContainerClaimDocument`, ...) is what gets serialized.\n *\n * This is the SYNC surface — used by callers that hold `stack.lock`\n * and must keep their critical section non-yielding.\n */\nexport const writeVersionedDocumentSync = <D, IoErr>(\n\tpath: string,\n\tvalue: D,\n\terrors: VersionedDocIoError<IoErr>,\n): Effect.Effect<void, IoErr> =>\n\tEffect.try({\n\t\ttry: () => atomicWriteJsonSync(path, value),\n\t\tcatch: (cause) => errors.mkIo({ path, cause }),\n\t});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAmEA,MAAa,6BAKZ,MACA,QACA,QACA,eAEA,OAAO,IAAI,aAAa;AACvB,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO;CAC9B,MAAM,MAAM,OAAO,OAAO,IAAI;EAC7B,WAAW,aAAa,MAAM,OAAO;EACrC,QAAQ,UAAU,OAAO,KAAK;GAAE;GAAM;GAAO,CAAC;EAC9C,CAAC;AACF,QAAO,OAAO,eAAe,QAAQ,KAAK;EACzC,QAAQ;EACR,UAAU,UAAU,OAAO,UAAU;GAAE;GAAM;GAAK,OAAO,MAAM,SAAS;GAAO,CAAC;EAChF,CAAC;EACD;;;;;;;;;;;;;;;AAgBH,MAAa,oCACZ,KACA,QACA,WACsB;AACtB,KAAI;AACH,SAAO,mBAAmB,QAAQ,KAAK;GACtC;GACA,UAAU,UAAU;GACpB,CAAC;SACK;AACP,SAAO;;;;;;;;;;;;;;;;;;;;;;;AAwBT,MAAa,8BACZ,MACA,OACA,WAEA,OAAO,IAAI;CACV,WAAW,oBAAoB,MAAM,MAAM;CAC3C,QAAQ,UAAU,OAAO,KAAK;EAAE;EAAM;EAAO,CAAC;CAC9C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"command-tree.mjs","names":[],"sources":["../../../src/surfaces/cli/command-tree.ts"],"sourcesContent":["import { ENV_VARS } from './flags.ts';\nimport { exitCodeTable } from './sysexits.ts';\n\nexport type CommandLifecycle = 'attached' | 'live-aware' | 'one-shot' | 'offline';\n\nexport interface CommandOption {\n\treadonly name: string;\n\treadonly value?: string;\n\treadonly description: string;\n}\n\nexport interface CommandNode {\n\treadonly name: string;\n\treadonly summary: string;\n\treadonly usage: string;\n\treadonly lifecycle: CommandLifecycle;\n\treadonly sideEffects: 'none' | 'read' | 'write' | 'destructive';\n\treadonly requiresDocker: boolean;\n\treadonly description?: string;\n\treadonly arguments?: ReadonlyArray<string>;\n\treadonly options?: ReadonlyArray<CommandOption>;\n\treadonly subcommands?: ReadonlyArray<CommandNode>;\n}\n\nconst identityOptions = [\n\t{ name: 'json', description: 'Emit JSON envelope output.' },\n\t{ name: 'app', value: 'name', description: 'Override app name.' },\n\t{ name: 'stack', value: 'name', description: 'Override stack name.' },\n\t{ name: 'state-dir', value: 'path', description: 'Override state directory.' },\n\t{ name: 'verbose', description: 'Enable more verbose logging.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst configOptions = [\n\t...identityOptions,\n\t{ name: 'config', value: 'path', description: 'Override devstack.config.ts path.' },\n\t{ name: 'network', value: 'name', description: 'Override network before config import.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst confirmOptions = [\n\t...identityOptions,\n\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t{ name: 'no-input', description: 'Forbid prompts.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst globalMaintenanceOptions = [\n\t{ name: 'json', description: 'Emit JSON envelope output.' },\n\t{ name: 'state-dir', value: 'path', description: 'Override state directory.' },\n\t{ name: 'verbose', description: 'Enable more verbose logging.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst commands = [\n\t{\n\t\tname: 'up',\n\t\tsummary: 'Boot a stack and stay attached until interrupted.',\n\t\tusage: 'devstack up [options]',\n\t\tlifecycle: 'attached',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\tdescription: 'Starts the supervisor and renders the attached operator surface.',\n\t\toptions: [\n\t\t\t...configOptions,\n\t\t\t{\n\t\t\t\tname: 'renderer',\n\t\t\t\tvalue: 'tui|plain|silent',\n\t\t\t\tdescription: 'Select the attached renderer.',\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'apply',\n\t\tsummary: 'Reconcile through a live supervisor when present; otherwise run one-shot setup.',\n\t\tusage: 'devstack apply [options]',\n\t\tlifecycle: 'live-aware',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\toptions: configOptions,\n\t},\n\t{\n\t\tname: 'status',\n\t\tsummary: 'Show the persisted stack projection.',\n\t\tusage: 'devstack status [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: false,\n\t\toptions: identityOptions,\n\t},\n\t{\n\t\tname: 'doctor',\n\t\tsummary: 'Run host and stack preflight checks.',\n\t\tusage: 'devstack doctor [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: true,\n\t\toptions: identityOptions,\n\t},\n\t{\n\t\tname: 'config',\n\t\tsummary: 'Print resolved config inputs.',\n\t\tusage: 'devstack config [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: false,\n\t\toptions: configOptions,\n\t},\n\t{\n\t\tname: 'schema',\n\t\tsummary: 'Emit the CLI schema.',\n\t\tusage: 'devstack schema --json',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'none',\n\t\trequiresDocker: false,\n\t\toptions: [{ name: 'json', description: 'Emit JSON schema output.' }],\n\t},\n\t{\n\t\tname: 'snapshot',\n\t\tsummary: 'Capture, restore, list, or delete stack snapshots.',\n\t\tusage: 'devstack snapshot <command> [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\tsubcommands: [\n\t\t\t{\n\t\t\t\tname: 'save',\n\t\t\t\tsummary: 'Capture a snapshot through a one-shot stack boot.',\n\t\t\t\tusage: 'devstack snapshot save [name] [options]',\n\t\t\t\tlifecycle: 'one-shot',\n\t\t\t\tsideEffects: 'write',\n\t\t\t\trequiresDocker: true,\n\t\t\t\targuments: ['name'],\n\t\t\t\toptions: [\n\t\t\t\t\t...configOptions,\n\t\t\t\t\t{ name: 'name', value: 'name', description: 'Human-readable snapshot name.' },\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'restore',\n\t\t\t\tsummary: 'Restore a snapshot by name or id.',\n\t\t\t\tusage: 'devstack snapshot restore <name-or-id> [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'destructive',\n\t\t\t\trequiresDocker: true,\n\t\t\t\targuments: ['name-or-id'],\n\t\t\t\toptions: confirmOptions,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'list',\n\t\t\t\tsummary: 'List snapshots for the selected stack.',\n\t\t\t\tusage: 'devstack snapshot list [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'read',\n\t\t\t\trequiresDocker: false,\n\t\t\t\toptions: identityOptions,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'delete',\n\t\t\t\tsummary: 'Delete a snapshot by name or id.',\n\t\t\t\tusage: 'devstack snapshot delete <name-or-id> [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'destructive',\n\t\t\t\trequiresDocker: false,\n\t\t\t\targuments: ['name-or-id'],\n\t\t\t\toptions: confirmOptions,\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'prune',\n\t\tsummary: 'Inventory and prune devstack-labelled Docker resources.',\n\t\tusage: 'devstack prune [--list | --dry-run | --all --yes] [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'destructive',\n\t\trequiresDocker: true,\n\t\toptions: [\n\t\t\t...globalMaintenanceOptions,\n\t\t\t{ name: 'list', description: 'List resource groups without pruning.' },\n\t\t\t{ name: 'all', description: 'Prune every idle non-shared resource group.' },\n\t\t\t{ name: 'no-containers', description: 'Do not remove containers.' },\n\t\t\t{ name: 'no-networks', description: 'Do not remove networks.' },\n\t\t\t{ name: 'no-volumes', description: 'Do not remove volumes.' },\n\t\t\t{ name: 'include-images', description: 'Also remove devstack-labelled images.' },\n\t\t\t{ name: 'dry-run', description: 'Skip mutating effects.' },\n\t\t\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t\t\t{ name: 'no-input', description: 'Forbid prompts.' },\n\t\t],\n\t},\n\t{\n\t\tname: 'wipe',\n\t\tsummary: 'Destroy all state for the selected stack.',\n\t\tusage: 'devstack wipe [--dry-run] [--yes] [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'destructive',\n\t\trequiresDocker: true,\n\t\toptions: [\n\t\t\t...identityOptions,\n\t\t\t{ name: 'dry-run', description: 'Skip mutating effects.' },\n\t\t\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t\t\t{ name: 'no-input', description: 'Forbid prompts.' },\n\t\t],\n\t},\n] as const satisfies ReadonlyArray<CommandNode>;\n\nexport const COMMAND_TREE: CommandNode = {\n\tname: 'devstack',\n\tsummary: 'Sui development stack CLI.',\n\tusage: 'devstack <command> [options]',\n\tlifecycle: 'offline',\n\tsideEffects: 'none',\n\trequiresDocker: false,\n\tdescription: 'Boot, inspect, snapshot, and manage local Sui development stacks.',\n\tsubcommands: commands,\n};\n\nexport type Verb = (typeof commands)[number]['name'];\n\nexport const VERBS: ReadonlyArray<Verb> = commands.map((command) => command.name);\n\nexport const isVerb = (value: string): value is Verb =>\n\t(VERBS as ReadonlyArray<string>).includes(value);\n\nconst findSubcommand = (node: CommandNode, name: string): CommandNode | undefined =>\n\tnode.subcommands?.find((candidate) => candidate.name === name);\n\nexport const findCommandNode = (path: ReadonlyArray<string>): CommandNode | null => {\n\tlet current: CommandNode = COMMAND_TREE;\n\tfor (const segment of path) {\n\t\tconst next = findSubcommand(current, segment);\n\t\tif (next === undefined) return null;\n\t\tcurrent = next;\n\t}\n\treturn current;\n};\n\nconst formatOption = (option: CommandOption): string =>\n\t`--${option.name}${option.value ? ` <${option.value}>` : ''}`;\n\nconst pad = (values: ReadonlyArray<string>): number =>\n\tvalues.reduce((max, value) => Math.max(max, value.length), 0);\n\nconst pushCommandRows = (\n\tlines: Array<string>,\n\ttitle: string,\n\tcommandsToRender: ReadonlyArray<CommandNode>,\n) => {\n\tif (commandsToRender.length === 0) return;\n\tconst names = commandsToRender.map((command) => command.name);\n\tconst width = pad(names);\n\tlines.push('', `${title}:`);\n\tfor (const command of commandsToRender) {\n\t\tlines.push(` ${command.name.padEnd(width, ' ')} ${command.summary}`);\n\t}\n};\n\nconst pushOptionRows = (\n\tlines: Array<string>,\n\ttitle: string,\n\toptions: ReadonlyArray<CommandOption>,\n) => {\n\tif (options.length === 0) return;\n\tconst rendered = options.map(formatOption);\n\tconst width = pad(rendered);\n\tlines.push('', `${title}:`);\n\tfor (let i = 0; i < options.length; i += 1) {\n\t\tlines.push(` ${rendered[i]!.padEnd(width, ' ')} ${options[i]!.description}`);\n\t}\n};\n\nexport const formatCommandHelp = (path: ReadonlyArray<string>): string => {\n\tconst node = findCommandNode(path) ?? COMMAND_TREE;\n\tconst lines: Array<string> = [\n\t\tpath.length === 0\n\t\t\t? 'devstack - Sui development stack CLI'\n\t\t\t: `${path.join(' ')} - ${node.summary}`,\n\t\t'',\n\t\t`Usage: ${node.usage}`,\n\t];\n\tif (node.description !== undefined) lines.push('', node.description);\n\tif (node.arguments !== undefined && node.arguments.length > 0) {\n\t\tlines.push('', `Arguments: ${node.arguments.join(', ')}`);\n\t}\n\tpushCommandRows(lines, path.length === 0 ? 'Commands' : 'Subcommands', node.subcommands ?? []);\n\tpushOptionRows(lines, 'Options', node.options ?? []);\n\treturn lines.join('\\n');\n};\n\nexport const commandSchema = () => ({\n\tschemaVersion: 1,\n\tverbs: VERBS,\n\tcommands: COMMAND_TREE,\n\texitCodes: exitCodeTable,\n\tenvVars: Object.values(ENV_VARS),\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"command-tree.mjs","names":[],"sources":["../../../src/surfaces/cli/command-tree.ts"],"sourcesContent":["import { ENV_VARS } from './flags.ts';\nimport { exitCodeTable } from './sysexits.ts';\n\nexport type CommandLifecycle = 'attached' | 'live-aware' | 'one-shot' | 'offline';\n\nexport interface CommandOption {\n\treadonly name: string;\n\treadonly value?: string;\n\treadonly description: string;\n}\n\nexport interface CommandNode {\n\treadonly name: string;\n\treadonly summary: string;\n\treadonly usage: string;\n\treadonly lifecycle: CommandLifecycle;\n\treadonly sideEffects: 'none' | 'read' | 'write' | 'destructive';\n\treadonly requiresDocker: boolean;\n\treadonly description?: string;\n\treadonly arguments?: ReadonlyArray<string>;\n\treadonly options?: ReadonlyArray<CommandOption>;\n\treadonly subcommands?: ReadonlyArray<CommandNode>;\n}\n\nconst identityOptions = [\n\t{ name: 'json', description: 'Emit JSON envelope output.' },\n\t{ name: 'app', value: 'name', description: 'Override app name.' },\n\t{ name: 'stack', value: 'name', description: 'Override stack name.' },\n\t{ name: 'state-dir', value: 'path', description: 'Override state directory.' },\n\t{ name: 'verbose', description: 'Enable more verbose logging.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst configOptions = [\n\t...identityOptions,\n\t{ name: 'config', value: 'path', description: 'Override devstack.config.ts path.' },\n\t{ name: 'network', value: 'name', description: 'Override network before config import.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst confirmOptions = [\n\t...identityOptions,\n\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t{ name: 'no-input', description: 'Forbid prompts.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst globalMaintenanceOptions = [\n\t{ name: 'json', description: 'Emit JSON envelope output.' },\n\t{ name: 'state-dir', value: 'path', description: 'Override state directory.' },\n\t{ name: 'verbose', description: 'Enable more verbose logging.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst commands = [\n\t{\n\t\tname: 'up',\n\t\tsummary: 'Boot a stack and stay attached until interrupted.',\n\t\tusage: 'devstack up [options]',\n\t\tlifecycle: 'attached',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\tdescription: 'Starts the supervisor and renders the attached operator surface.',\n\t\toptions: [\n\t\t\t...configOptions,\n\t\t\t{\n\t\t\t\tname: 'renderer',\n\t\t\t\tvalue: 'tui|plain|silent',\n\t\t\t\tdescription: 'Select the attached renderer.',\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'apply',\n\t\tsummary: 'Reconcile through a live supervisor when present; otherwise run one-shot setup.',\n\t\tusage: 'devstack apply [options]',\n\t\tlifecycle: 'live-aware',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\toptions: configOptions,\n\t},\n\t{\n\t\tname: 'status',\n\t\tsummary: 'Show the persisted stack projection.',\n\t\tusage: 'devstack status [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: false,\n\t\toptions: identityOptions,\n\t},\n\t{\n\t\tname: 'doctor',\n\t\tsummary: 'Run host and stack preflight checks.',\n\t\tusage: 'devstack doctor [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: true,\n\t\toptions: identityOptions,\n\t},\n\t{\n\t\tname: 'config',\n\t\tsummary: 'Print resolved config inputs.',\n\t\tusage: 'devstack config [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: false,\n\t\toptions: configOptions,\n\t},\n\t{\n\t\tname: 'schema',\n\t\tsummary: 'Emit the CLI schema.',\n\t\tusage: 'devstack schema --json',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'none',\n\t\trequiresDocker: false,\n\t\toptions: [{ name: 'json', description: 'Emit JSON schema output.' }],\n\t},\n\t{\n\t\tname: 'snapshot',\n\t\tsummary: 'Capture, restore, list, or delete stack snapshots.',\n\t\tusage: 'devstack snapshot <command> [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\tsubcommands: [\n\t\t\t{\n\t\t\t\tname: 'save',\n\t\t\t\tsummary: 'Capture a snapshot through a one-shot stack boot.',\n\t\t\t\tusage: 'devstack snapshot save [name] [options]',\n\t\t\t\tlifecycle: 'one-shot',\n\t\t\t\tsideEffects: 'write',\n\t\t\t\trequiresDocker: true,\n\t\t\t\targuments: ['name'],\n\t\t\t\toptions: [\n\t\t\t\t\t...configOptions,\n\t\t\t\t\t{ name: 'name', value: 'name', description: 'Human-readable snapshot name.' },\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'restore',\n\t\t\t\tsummary: 'Restore a snapshot by name or id.',\n\t\t\t\tusage: 'devstack snapshot restore <name-or-id> [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'destructive',\n\t\t\t\trequiresDocker: true,\n\t\t\t\targuments: ['name-or-id'],\n\t\t\t\toptions: confirmOptions,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'list',\n\t\t\t\tsummary: 'List snapshots for the selected stack.',\n\t\t\t\tusage: 'devstack snapshot list [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'read',\n\t\t\t\trequiresDocker: false,\n\t\t\t\toptions: identityOptions,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'delete',\n\t\t\t\tsummary: 'Delete a snapshot by name or id.',\n\t\t\t\tusage: 'devstack snapshot delete <name-or-id> [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'destructive',\n\t\t\t\trequiresDocker: false,\n\t\t\t\targuments: ['name-or-id'],\n\t\t\t\toptions: confirmOptions,\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'prune',\n\t\tsummary: 'Inventory and prune devstack-labelled Docker resources.',\n\t\tusage: 'devstack prune [--list | --dry-run | --all --yes] [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'destructive',\n\t\trequiresDocker: true,\n\t\toptions: [\n\t\t\t...globalMaintenanceOptions,\n\t\t\t{ name: 'list', description: 'List resource groups without pruning.' },\n\t\t\t{ name: 'all', description: 'Prune every idle non-shared resource group.' },\n\t\t\t{ name: 'no-containers', description: 'Do not remove containers.' },\n\t\t\t{ name: 'no-networks', description: 'Do not remove networks.' },\n\t\t\t{ name: 'no-volumes', description: 'Do not remove volumes.' },\n\t\t\t{ name: 'include-images', description: 'Also remove devstack-labelled images.' },\n\t\t\t{ name: 'dry-run', description: 'Skip mutating effects.' },\n\t\t\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t\t\t{ name: 'no-input', description: 'Forbid prompts.' },\n\t\t],\n\t},\n\t{\n\t\tname: 'wipe',\n\t\tsummary: 'Destroy all state for the selected stack.',\n\t\tusage: 'devstack wipe [--dry-run] [--yes] [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'destructive',\n\t\trequiresDocker: true,\n\t\toptions: [\n\t\t\t...identityOptions,\n\t\t\t{ name: 'dry-run', description: 'Skip mutating effects.' },\n\t\t\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t\t\t{ name: 'no-input', description: 'Forbid prompts.' },\n\t\t],\n\t},\n] as const satisfies ReadonlyArray<CommandNode>;\n\nexport const COMMAND_TREE: CommandNode = {\n\tname: 'devstack',\n\tsummary: 'Sui development stack CLI.',\n\tusage: 'devstack <command> [options]',\n\tlifecycle: 'offline',\n\tsideEffects: 'none',\n\trequiresDocker: false,\n\tdescription: 'Boot, inspect, snapshot, and manage local Sui development stacks.',\n\tsubcommands: commands,\n};\n\nexport type Verb = (typeof commands)[number]['name'];\n\nexport const VERBS: ReadonlyArray<Verb> = commands.map((command) => command.name);\n\nexport const commandSchema = () => ({\n\tschemaVersion: 1,\n\tverbs: VERBS,\n\tcommands: COMMAND_TREE,\n\texitCodes: exitCodeTable,\n\tenvVars: Object.values(ENV_VARS),\n});\n"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import "./config.mjs";
2
+ import "./doctor.mjs";
3
+ import "./prune.mjs";
4
+ import "./confirm.mjs";
5
+ import "./snapshot.mjs";
6
+ import "./status.mjs";
7
+ import "./wipe.mjs";
8
+ import "./supervisor-presence.mjs";
9
+ export {};
@@ -0,0 +1,13 @@
1
+ import { Effect } from "effect";
2
+ //#region src/surfaces/cli/commands/prune-picker-entry.ts
3
+ const selectPruneTargets = (inventory, resources) => {
4
+ if (inventory.groups.length === 0) return Effect.succeed({
5
+ groupKeys: [],
6
+ resources
7
+ });
8
+ return Effect.promise(() => import("./prune-picker.mjs")).pipe(Effect.flatMap((mod) => mod.selectPruneTargets(inventory, resources)));
9
+ };
10
+ //#endregion
11
+ export { selectPruneTargets };
12
+
13
+ //# sourceMappingURL=prune-picker-entry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prune-picker-entry.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/prune-picker-entry.ts"],"sourcesContent":["// Lazy seam for the ink/react-based prune-picker UI.\n//\n// `cli/prune-direct.ts` is on every `devstack` CLI invocation path, so\n// the picker entry MUST NOT eagerly pull `ink` + `react` (~ several MB\n// of cold-start cost). Mirrors the `surfaces/tui/index.ts:dynamic\n// import('./mount-ink.tsx')` lazy-import pattern.\n\nimport { Effect } from 'effect';\n\nimport type { PruneInventory, PruneResourceScope, PruneTargetSelection } from './prune.ts';\n\nexport const selectPruneTargets = (\n\tinventory: PruneInventory,\n\tresources: PruneResourceScope,\n): Effect.Effect<PruneTargetSelection> => {\n\tif (inventory.groups.length === 0) return Effect.succeed({ groupKeys: [], resources });\n\treturn Effect.promise(() => import('./prune-picker.tsx')).pipe(\n\t\tEffect.flatMap((mod) => mod.selectPruneTargets(inventory, resources)),\n\t);\n};\n"],"mappings":";;AAWA,MAAa,sBACZ,WACA,cACyC;AACzC,KAAI,UAAU,OAAO,WAAW,EAAG,QAAO,OAAO,QAAQ;EAAE,WAAW,EAAE;EAAE;EAAW,CAAC;AACtF,QAAO,OAAO,cAAc,OAAO,sBAAsB,CAAC,KACzD,OAAO,SAAS,QAAQ,IAAI,mBAAmB,WAAW,UAAU,CAAC,CACrE"}
@@ -1,4 +1,4 @@
1
- import { defaultPruneSelection, groupResourceCountForResources, hasPruneResources, summarizePruneGroups, summarizePruneGroupsForResources } from "./prune.mjs";
1
+ import { appsWithLiveSiblings, defaultPruneSelection, groupResourceCountForResources, hasPruneResources, summarizePruneGroups, summarizePruneGroupsForResources } from "./prune.mjs";
2
2
  import { Effect } from "effect";
3
3
  import { Box, Text, render, useApp, useInput } from "ink";
4
4
  import React, { useEffect, useMemo, useState } from "react";
@@ -47,7 +47,8 @@ const PrunePicker = ({ inventory, resources: initialResources, onSubmit, onQuit
47
47
  inventory,
48
48
  rows
49
49
  ]);
50
- const selectable = (row) => !row.live;
50
+ const pinned = useMemo(() => appsWithLiveSiblings(inventory), [inventory]);
51
+ const selectable = (row) => !row.live && !(row.sharedKind === "per-app-shared" && pinned.has(row.app));
51
52
  const move = (dir) => {
52
53
  if (rows.length === 0) return;
53
54
  let next = cursor;
@@ -1 +1 @@
1
- {"version":3,"file":"prune-picker.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/prune-picker.tsx"],"sourcesContent":["import { Box, Text, render, useApp, useInput } from 'ink';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { Effect } from 'effect';\n\nimport {\n\tdefaultPruneSelection,\n\tgroupResourceCountForResources,\n\thasPruneResources,\n\tsummarizePruneGroups,\n\tsummarizePruneGroupsForResources,\n\ttype PruneGroup,\n\ttype PruneInventory,\n\ttype PruneResourceScope,\n\ttype PruneTargetSelection,\n} from './prune.ts';\n\nexport const selectPruneTargets = (\n\tinventory: PruneInventory,\n\tresources: PruneResourceScope,\n): Effect.Effect<PruneTargetSelection> => {\n\tif (inventory.groups.length === 0) return Effect.succeed({ groupKeys: [], resources });\n\treturn Effect.callback<PruneTargetSelection>((resume) => {\n\t\tconst instance = renderPrunePicker({\n\t\t\tinventory,\n\t\t\tresources,\n\t\t\tonSubmit: (selection) => {\n\t\t\t\tinstance.unmount();\n\t\t\t\tresume(Effect.succeed(selection));\n\t\t\t},\n\t\t\tonQuit: () => {\n\t\t\t\tinstance.unmount();\n\t\t\t\tresume(Effect.succeed({ groupKeys: [], resources }));\n\t\t\t},\n\t\t});\n\t});\n};\n\nconst renderPrunePicker = (props: PrunePickerProps) => {\n\treturn render(React.createElement(PrunePicker, props), { exitOnCtrlC: false });\n};\n\ninterface PrunePickerProps {\n\treadonly inventory: PruneInventory;\n\treadonly resources: PruneResourceScope;\n\treadonly onSubmit: (selection: PruneTargetSelection) => void;\n\treadonly onQuit: () => void;\n}\n\nconst PrunePicker = ({\n\tinventory,\n\tresources: initialResources,\n\tonSubmit,\n\tonQuit,\n}: PrunePickerProps): React.JSX.Element => {\n\tconst app = useApp();\n\tconst rows = inventory.groups;\n\tconst [cursor, setCursor] = useState(0);\n\tconst [resources, setResources] = useState<PruneResourceScope>(initialResources);\n\tconst [selected, setSelected] = useState<ReadonlySet<string>>(\n\t\t() => new Set(defaultPruneSelection(inventory, initialResources)),\n\t);\n\tconst [confirming, setConfirming] = useState(false);\n\n\tuseEffect(() => {\n\t\tconst first = rows.findIndex((row) => !row.live);\n\t\tsetCursor(first >= 0 ? first : 0);\n\t\tsetResources(initialResources);\n\t\tsetSelected(new Set(defaultPruneSelection(inventory, initialResources)));\n\t}, [initialResources, inventory, rows]);\n\n\tconst selectable = (row: PruneGroup): boolean => !row.live;\n\n\tconst move = (dir: 1 | -1): void => {\n\t\tif (rows.length === 0) return;\n\t\tlet next = cursor;\n\t\tfor (let i = 0; i < rows.length; i += 1) {\n\t\t\tnext = (next + dir + rows.length) % rows.length;\n\t\t\tconst row = rows[next];\n\t\t\tif (row !== undefined && selectable(row)) {\n\t\t\t\tsetCursor(next);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t};\n\n\tconst toggle = (): void => {\n\t\tconst row = rows[cursor];\n\t\tif (row === undefined || !selectable(row)) return;\n\t\tconst next = new Set(selected);\n\t\tif (next.has(row.key)) next.delete(row.key);\n\t\telse next.add(row.key);\n\t\tsetSelected(next);\n\t};\n\n\tconst selectAll = (): void => {\n\t\tsetSelected(\n\t\t\tnew Set(\n\t\t\t\trows\n\t\t\t\t\t.filter((row) => selectable(row) && groupResourceCountForResources(row, resources) > 0)\n\t\t\t\t\t.map((row) => row.key),\n\t\t\t),\n\t\t);\n\t};\n\n\tconst toggleResource = (name: keyof PruneResourceScope): void => {\n\t\tsetResources((current) => ({ ...current, [name]: !current[name] }));\n\t};\n\n\tuseInput((input, key) => {\n\t\tif (confirming) {\n\t\t\tif (input === 'y' || input === 'Y') {\n\t\t\t\tapp.exit();\n\t\t\t\tonSubmit({ groupKeys: [...selected], resources });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (input === 'n' || input === 'N' || key.escape || key.return) {\n\t\t\t\tsetConfirming(false);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (input === 'q' || input === 'Q' || (key.ctrl && input === 'c')) {\n\t\t\tapp.exit();\n\t\t\tonQuit();\n\t\t\treturn;\n\t\t}\n\t\tif (key.upArrow || input === 'k') {\n\t\t\tmove(-1);\n\t\t\treturn;\n\t\t}\n\t\tif (key.downArrow || input === 'j') {\n\t\t\tmove(1);\n\t\t\treturn;\n\t\t}\n\t\tif (input === ' ') {\n\t\t\ttoggle();\n\t\t\treturn;\n\t\t}\n\t\tif (input === '1') {\n\t\t\ttoggleResource('containers');\n\t\t\treturn;\n\t\t}\n\t\tif (input === '2') {\n\t\t\ttoggleResource('networks');\n\t\t\treturn;\n\t\t}\n\t\tif (input === '3') {\n\t\t\ttoggleResource('volumes');\n\t\t\treturn;\n\t\t}\n\t\tif (input === '4' || input === 'i' || input === 'I') {\n\t\t\ttoggleResource('images');\n\t\t\treturn;\n\t\t}\n\t\tif (input === 'a' || input === 'A') {\n\t\t\tselectAll();\n\t\t\treturn;\n\t\t}\n\t\tif (input === 'n' || input === 'N') {\n\t\t\tsetSelected(new Set());\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\tkey.return &&\n\t\t\tselected.size > 0 &&\n\t\t\thasPruneResources(resources) &&\n\t\t\tselectedResourceCount(rows, selected, resources) > 0\n\t\t) {\n\t\t\tsetConfirming(true);\n\t\t}\n\t});\n\n\tconst selectedRows = useMemo(() => rows.filter((row) => selected.has(row.key)), [rows, selected]);\n\tconst selectedTotals = useMemo(\n\t\t() => summarizePruneGroupsForResources(selectedRows, resources),\n\t\t[selectedRows, resources],\n\t);\n\tconst idleTotals = summarizePruneGroupsForResources(\n\t\trows.filter((row) => !row.live),\n\t\tresources,\n\t);\n\tconst allIdleTotals = summarizePruneGroups(rows.filter((row) => !row.live));\n\tconst effectiveSelectedCount =\n\t\tselectedTotals.containers +\n\t\tselectedTotals.networks +\n\t\tselectedTotals.volumes +\n\t\tselectedTotals.images;\n\n\treturn (\n\t\t<Box flexDirection=\"column\">\n\t\t\t<Box paddingX={1} borderStyle=\"round\" borderColor=\"gray\">\n\t\t\t\t<Text bold>devstack prune</Text>\n\t\t\t\t<Text dimColor>\n\t\t\t\t\t{' '}\n\t\t\t\t\t{rows.length} group(s), {idleTotals.containers} container(s), {idleTotals.networks}{' '}\n\t\t\t\t\tnetwork(s), {idleTotals.volumes} volume(s), {idleTotals.images} image(s)\n\t\t\t\t</Text>\n\t\t\t</Box>\n\t\t\t<ResourceScopeRow resources={resources} totals={allIdleTotals} />\n\t\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t\t{rows.map((row, index) => (\n\t\t\t\t\t<PruneRow\n\t\t\t\t\t\tkey={row.key}\n\t\t\t\t\t\trow={row}\n\t\t\t\t\t\tresources={resources}\n\t\t\t\t\t\tfocused={index === cursor}\n\t\t\t\t\t\tchecked={selected.has(row.key)}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</Box>\n\t\t\t{confirming ? (\n\t\t\t\t<Box paddingX={1} marginTop={1} flexDirection=\"column\">\n\t\t\t\t\t<Text>\n\t\t\t\t\t\tWill remove {selectedRows.length} group(s): {selectedTotals.containers} container(s),{' '}\n\t\t\t\t\t\t{selectedTotals.networks} network(s), {selectedTotals.volumes} volume(s),{' '}\n\t\t\t\t\t\t{selectedTotals.images} image(s).\n\t\t\t\t\t</Text>\n\t\t\t\t\t<Text color=\"yellow\">Proceed? [y/N]</Text>\n\t\t\t\t</Box>\n\t\t\t) : (\n\t\t\t\t<Box paddingX={1} marginTop={1} flexDirection=\"column\">\n\t\t\t\t\t{!hasPruneResources(resources) ? (\n\t\t\t\t\t\t<Text color=\"yellow\">Enable at least one resource type.</Text>\n\t\t\t\t\t) : selected.size > 0 && effectiveSelectedCount === 0 ? (\n\t\t\t\t\t\t<Text color=\"yellow\">Selected groups have no enabled resource types.</Text>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<Text dimColor>\n\t\t\t\t\t\t[space] toggle row [1-4] resources [a] all [n] none [enter] prune selected [q]uit\n\t\t\t\t\t</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t</Box>\n\t);\n};\n\nconst selectedResourceCount = (\n\trows: ReadonlyArray<PruneGroup>,\n\tselected: ReadonlySet<string>,\n\tresources: PruneResourceScope,\n): number =>\n\trows\n\t\t.filter((row) => selected.has(row.key))\n\t\t.reduce((total, row) => total + groupResourceCountForResources(row, resources), 0);\n\nconst ResourceScopeRow = ({\n\tresources,\n\ttotals,\n}: {\n\treadonly resources: PruneResourceScope;\n\treadonly totals: ReturnType<typeof summarizePruneGroups>;\n}): React.JSX.Element => (\n\t<Box paddingX={1}>\n\t\t<Text dimColor>resources </Text>\n\t\t<ResourceToggle\n\t\t\tactive={resources.containers}\n\t\t\tindex=\"1\"\n\t\t\tlabel=\"containers\"\n\t\t\tcount={totals.containers}\n\t\t/>\n\t\t<Text> </Text>\n\t\t<ResourceToggle\n\t\t\tactive={resources.networks}\n\t\t\tindex=\"2\"\n\t\t\tlabel=\"networks\"\n\t\t\tcount={totals.networks}\n\t\t/>\n\t\t<Text> </Text>\n\t\t<ResourceToggle active={resources.volumes} index=\"3\" label=\"volumes\" count={totals.volumes} />\n\t\t<Text> </Text>\n\t\t<ResourceToggle active={resources.images} index=\"4\" label=\"images\" count={totals.images} />\n\t</Box>\n);\n\nconst ResourceToggle = ({\n\tactive,\n\tindex,\n\tlabel,\n\tcount,\n}: {\n\treadonly active: boolean;\n\treadonly index: string;\n\treadonly label: string;\n\treadonly count: number;\n}): React.JSX.Element => (\n\t<Text color={active ? 'green' : 'gray'}>\n\t\t{index}:{active ? '[x]' : '[ ]'} {label}({count})\n\t</Text>\n);\n\nconst PruneRow = ({\n\trow,\n\tresources,\n\tfocused,\n\tchecked,\n}: {\n\treadonly row: PruneGroup;\n\treadonly resources: PruneResourceScope;\n\treadonly focused: boolean;\n\treadonly checked: boolean;\n}): React.JSX.Element => {\n\tconst cursor = focused ? '>' : ' ';\n\tconst box = row.live ? '[-]' : checked ? '[x]' : '[ ]';\n\tconst state = row.live ? `live pid ${row.livePids.join(',')}` : row.shared ? 'shared' : 'idle';\n\tconst color = row.live ? 'red' : focused ? 'cyan' : row.shared ? 'yellow' : undefined;\n\tconst running = row.runningContainers > 0 ? `, ${row.runningContainers} running` : '';\n\treturn (\n\t\t<Box>\n\t\t\t<Text color={color} bold={focused || row.shared}>\n\t\t\t\t{cursor} {box} {row.app}/{row.stack}{' '}\n\t\t\t</Text>\n\t\t\t<Text dimColor>\n\t\t\t\t{state}\n\t\t\t\t{running}{' '}\n\t\t\t</Text>\n\t\t\t<ResourceCount active={resources.containers} count={row.containers} label=\"ctr\" />\n\t\t\t<Text dimColor>, </Text>\n\t\t\t<ResourceCount active={resources.networks} count={row.networks} label=\"net\" />\n\t\t\t<Text dimColor>, </Text>\n\t\t\t<ResourceCount active={resources.volumes} count={row.volumes} label=\"vol\" />\n\t\t\t<Text dimColor>, </Text>\n\t\t\t<ResourceCount active={resources.images} count={row.images} label=\"img\" />\n\t\t</Box>\n\t);\n};\n\nconst ResourceCount = ({\n\tactive,\n\tcount,\n\tlabel,\n}: {\n\treadonly active: boolean;\n\treadonly count: number;\n\treadonly label: string;\n}): React.JSX.Element => (\n\t<Text color={active ? undefined : 'gray'}>\n\t\t{count} {label}\n\t</Text>\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"prune-picker.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/prune-picker.tsx"],"sourcesContent":["import { Box, Text, render, useApp, useInput } from 'ink';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { Effect } from 'effect';\n\nimport {\n\tappsWithLiveSiblings,\n\tdefaultPruneSelection,\n\tgroupResourceCountForResources,\n\thasPruneResources,\n\tsummarizePruneGroups,\n\tsummarizePruneGroupsForResources,\n\ttype PruneGroup,\n\ttype PruneInventory,\n\ttype PruneResourceScope,\n\ttype PruneTargetSelection,\n} from './prune.ts';\n\nexport const selectPruneTargets = (\n\tinventory: PruneInventory,\n\tresources: PruneResourceScope,\n): Effect.Effect<PruneTargetSelection> => {\n\tif (inventory.groups.length === 0) return Effect.succeed({ groupKeys: [], resources });\n\treturn Effect.callback<PruneTargetSelection>((resume) => {\n\t\tconst instance = renderPrunePicker({\n\t\t\tinventory,\n\t\t\tresources,\n\t\t\tonSubmit: (selection) => {\n\t\t\t\tinstance.unmount();\n\t\t\t\tresume(Effect.succeed(selection));\n\t\t\t},\n\t\t\tonQuit: () => {\n\t\t\t\tinstance.unmount();\n\t\t\t\tresume(Effect.succeed({ groupKeys: [], resources }));\n\t\t\t},\n\t\t});\n\t});\n};\n\nconst renderPrunePicker = (props: PrunePickerProps) => {\n\treturn render(React.createElement(PrunePicker, props), { exitOnCtrlC: false });\n};\n\ninterface PrunePickerProps {\n\treadonly inventory: PruneInventory;\n\treadonly resources: PruneResourceScope;\n\treadonly onSubmit: (selection: PruneTargetSelection) => void;\n\treadonly onQuit: () => void;\n}\n\nconst PrunePicker = ({\n\tinventory,\n\tresources: initialResources,\n\tonSubmit,\n\tonQuit,\n}: PrunePickerProps): React.JSX.Element => {\n\tconst app = useApp();\n\tconst rows = inventory.groups;\n\tconst [cursor, setCursor] = useState(0);\n\tconst [resources, setResources] = useState<PruneResourceScope>(initialResources);\n\tconst [selected, setSelected] = useState<ReadonlySet<string>>(\n\t\t() => new Set(defaultPruneSelection(inventory, initialResources)),\n\t);\n\tconst [confirming, setConfirming] = useState(false);\n\n\tuseEffect(() => {\n\t\tconst first = rows.findIndex((row) => !row.live);\n\t\tsetCursor(first >= 0 ? first : 0);\n\t\tsetResources(initialResources);\n\t\tsetSelected(new Set(defaultPruneSelection(inventory, initialResources)));\n\t}, [initialResources, inventory, rows]);\n\n\t// Apps with a live non-shared sibling pin their `per-app-shared`\n\t// group (shared chain-build cache &c.). The default `selected` set\n\t// already excludes these via `defaultPruneSelection`; mirroring the\n\t// predicate here keeps manual toggle / select-all from re-adding a\n\t// pinned shared group, matching the orchestrator's enforcement.\n\tconst pinned = useMemo(() => appsWithLiveSiblings(inventory), [inventory]);\n\n\tconst selectable = (row: PruneGroup): boolean =>\n\t\t!row.live && !(row.sharedKind === 'per-app-shared' && pinned.has(row.app));\n\n\tconst move = (dir: 1 | -1): void => {\n\t\tif (rows.length === 0) return;\n\t\tlet next = cursor;\n\t\tfor (let i = 0; i < rows.length; i += 1) {\n\t\t\tnext = (next + dir + rows.length) % rows.length;\n\t\t\tconst row = rows[next];\n\t\t\tif (row !== undefined && selectable(row)) {\n\t\t\t\tsetCursor(next);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t};\n\n\tconst toggle = (): void => {\n\t\tconst row = rows[cursor];\n\t\tif (row === undefined || !selectable(row)) return;\n\t\tconst next = new Set(selected);\n\t\tif (next.has(row.key)) next.delete(row.key);\n\t\telse next.add(row.key);\n\t\tsetSelected(next);\n\t};\n\n\tconst selectAll = (): void => {\n\t\tsetSelected(\n\t\t\tnew Set(\n\t\t\t\trows\n\t\t\t\t\t.filter((row) => selectable(row) && groupResourceCountForResources(row, resources) > 0)\n\t\t\t\t\t.map((row) => row.key),\n\t\t\t),\n\t\t);\n\t};\n\n\tconst toggleResource = (name: keyof PruneResourceScope): void => {\n\t\tsetResources((current) => ({ ...current, [name]: !current[name] }));\n\t};\n\n\tuseInput((input, key) => {\n\t\tif (confirming) {\n\t\t\tif (input === 'y' || input === 'Y') {\n\t\t\t\tapp.exit();\n\t\t\t\tonSubmit({ groupKeys: [...selected], resources });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (input === 'n' || input === 'N' || key.escape || key.return) {\n\t\t\t\tsetConfirming(false);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (input === 'q' || input === 'Q' || (key.ctrl && input === 'c')) {\n\t\t\tapp.exit();\n\t\t\tonQuit();\n\t\t\treturn;\n\t\t}\n\t\tif (key.upArrow || input === 'k') {\n\t\t\tmove(-1);\n\t\t\treturn;\n\t\t}\n\t\tif (key.downArrow || input === 'j') {\n\t\t\tmove(1);\n\t\t\treturn;\n\t\t}\n\t\tif (input === ' ') {\n\t\t\ttoggle();\n\t\t\treturn;\n\t\t}\n\t\tif (input === '1') {\n\t\t\ttoggleResource('containers');\n\t\t\treturn;\n\t\t}\n\t\tif (input === '2') {\n\t\t\ttoggleResource('networks');\n\t\t\treturn;\n\t\t}\n\t\tif (input === '3') {\n\t\t\ttoggleResource('volumes');\n\t\t\treturn;\n\t\t}\n\t\tif (input === '4' || input === 'i' || input === 'I') {\n\t\t\ttoggleResource('images');\n\t\t\treturn;\n\t\t}\n\t\tif (input === 'a' || input === 'A') {\n\t\t\tselectAll();\n\t\t\treturn;\n\t\t}\n\t\tif (input === 'n' || input === 'N') {\n\t\t\tsetSelected(new Set());\n\t\t\treturn;\n\t\t}\n\t\tif (\n\t\t\tkey.return &&\n\t\t\tselected.size > 0 &&\n\t\t\thasPruneResources(resources) &&\n\t\t\tselectedResourceCount(rows, selected, resources) > 0\n\t\t) {\n\t\t\tsetConfirming(true);\n\t\t}\n\t});\n\n\tconst selectedRows = useMemo(() => rows.filter((row) => selected.has(row.key)), [rows, selected]);\n\tconst selectedTotals = useMemo(\n\t\t() => summarizePruneGroupsForResources(selectedRows, resources),\n\t\t[selectedRows, resources],\n\t);\n\tconst idleTotals = summarizePruneGroupsForResources(\n\t\trows.filter((row) => !row.live),\n\t\tresources,\n\t);\n\tconst allIdleTotals = summarizePruneGroups(rows.filter((row) => !row.live));\n\tconst effectiveSelectedCount =\n\t\tselectedTotals.containers +\n\t\tselectedTotals.networks +\n\t\tselectedTotals.volumes +\n\t\tselectedTotals.images;\n\n\treturn (\n\t\t<Box flexDirection=\"column\">\n\t\t\t<Box paddingX={1} borderStyle=\"round\" borderColor=\"gray\">\n\t\t\t\t<Text bold>devstack prune</Text>\n\t\t\t\t<Text dimColor>\n\t\t\t\t\t{' '}\n\t\t\t\t\t{rows.length} group(s), {idleTotals.containers} container(s), {idleTotals.networks}{' '}\n\t\t\t\t\tnetwork(s), {idleTotals.volumes} volume(s), {idleTotals.images} image(s)\n\t\t\t\t</Text>\n\t\t\t</Box>\n\t\t\t<ResourceScopeRow resources={resources} totals={allIdleTotals} />\n\t\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t\t{rows.map((row, index) => (\n\t\t\t\t\t<PruneRow\n\t\t\t\t\t\tkey={row.key}\n\t\t\t\t\t\trow={row}\n\t\t\t\t\t\tresources={resources}\n\t\t\t\t\t\tfocused={index === cursor}\n\t\t\t\t\t\tchecked={selected.has(row.key)}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</Box>\n\t\t\t{confirming ? (\n\t\t\t\t<Box paddingX={1} marginTop={1} flexDirection=\"column\">\n\t\t\t\t\t<Text>\n\t\t\t\t\t\tWill remove {selectedRows.length} group(s): {selectedTotals.containers} container(s),{' '}\n\t\t\t\t\t\t{selectedTotals.networks} network(s), {selectedTotals.volumes} volume(s),{' '}\n\t\t\t\t\t\t{selectedTotals.images} image(s).\n\t\t\t\t\t</Text>\n\t\t\t\t\t<Text color=\"yellow\">Proceed? [y/N]</Text>\n\t\t\t\t</Box>\n\t\t\t) : (\n\t\t\t\t<Box paddingX={1} marginTop={1} flexDirection=\"column\">\n\t\t\t\t\t{!hasPruneResources(resources) ? (\n\t\t\t\t\t\t<Text color=\"yellow\">Enable at least one resource type.</Text>\n\t\t\t\t\t) : selected.size > 0 && effectiveSelectedCount === 0 ? (\n\t\t\t\t\t\t<Text color=\"yellow\">Selected groups have no enabled resource types.</Text>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<Text dimColor>\n\t\t\t\t\t\t[space] toggle row [1-4] resources [a] all [n] none [enter] prune selected [q]uit\n\t\t\t\t\t</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t</Box>\n\t);\n};\n\nconst selectedResourceCount = (\n\trows: ReadonlyArray<PruneGroup>,\n\tselected: ReadonlySet<string>,\n\tresources: PruneResourceScope,\n): number =>\n\trows\n\t\t.filter((row) => selected.has(row.key))\n\t\t.reduce((total, row) => total + groupResourceCountForResources(row, resources), 0);\n\nconst ResourceScopeRow = ({\n\tresources,\n\ttotals,\n}: {\n\treadonly resources: PruneResourceScope;\n\treadonly totals: ReturnType<typeof summarizePruneGroups>;\n}): React.JSX.Element => (\n\t<Box paddingX={1}>\n\t\t<Text dimColor>resources </Text>\n\t\t<ResourceToggle\n\t\t\tactive={resources.containers}\n\t\t\tindex=\"1\"\n\t\t\tlabel=\"containers\"\n\t\t\tcount={totals.containers}\n\t\t/>\n\t\t<Text> </Text>\n\t\t<ResourceToggle\n\t\t\tactive={resources.networks}\n\t\t\tindex=\"2\"\n\t\t\tlabel=\"networks\"\n\t\t\tcount={totals.networks}\n\t\t/>\n\t\t<Text> </Text>\n\t\t<ResourceToggle active={resources.volumes} index=\"3\" label=\"volumes\" count={totals.volumes} />\n\t\t<Text> </Text>\n\t\t<ResourceToggle active={resources.images} index=\"4\" label=\"images\" count={totals.images} />\n\t</Box>\n);\n\nconst ResourceToggle = ({\n\tactive,\n\tindex,\n\tlabel,\n\tcount,\n}: {\n\treadonly active: boolean;\n\treadonly index: string;\n\treadonly label: string;\n\treadonly count: number;\n}): React.JSX.Element => (\n\t<Text color={active ? 'green' : 'gray'}>\n\t\t{index}:{active ? '[x]' : '[ ]'} {label}({count})\n\t</Text>\n);\n\nconst PruneRow = ({\n\trow,\n\tresources,\n\tfocused,\n\tchecked,\n}: {\n\treadonly row: PruneGroup;\n\treadonly resources: PruneResourceScope;\n\treadonly focused: boolean;\n\treadonly checked: boolean;\n}): React.JSX.Element => {\n\tconst cursor = focused ? '>' : ' ';\n\tconst box = row.live ? '[-]' : checked ? '[x]' : '[ ]';\n\tconst state = row.live ? `live pid ${row.livePids.join(',')}` : row.shared ? 'shared' : 'idle';\n\tconst color = row.live ? 'red' : focused ? 'cyan' : row.shared ? 'yellow' : undefined;\n\tconst running = row.runningContainers > 0 ? `, ${row.runningContainers} running` : '';\n\treturn (\n\t\t<Box>\n\t\t\t<Text color={color} bold={focused || row.shared}>\n\t\t\t\t{cursor} {box} {row.app}/{row.stack}{' '}\n\t\t\t</Text>\n\t\t\t<Text dimColor>\n\t\t\t\t{state}\n\t\t\t\t{running}{' '}\n\t\t\t</Text>\n\t\t\t<ResourceCount active={resources.containers} count={row.containers} label=\"ctr\" />\n\t\t\t<Text dimColor>, </Text>\n\t\t\t<ResourceCount active={resources.networks} count={row.networks} label=\"net\" />\n\t\t\t<Text dimColor>, </Text>\n\t\t\t<ResourceCount active={resources.volumes} count={row.volumes} label=\"vol\" />\n\t\t\t<Text dimColor>, </Text>\n\t\t\t<ResourceCount active={resources.images} count={row.images} label=\"img\" />\n\t\t</Box>\n\t);\n};\n\nconst ResourceCount = ({\n\tactive,\n\tcount,\n\tlabel,\n}: {\n\treadonly active: boolean;\n\treadonly count: number;\n\treadonly label: string;\n}): React.JSX.Element => (\n\t<Text color={active ? undefined : 'gray'}>\n\t\t{count} {label}\n\t</Text>\n);\n"],"mappings":"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"}
@@ -1,14 +1,11 @@
1
1
  import { CliConfirmRequiredError, CliInternalError, CliUsageError, isCliError } from "../errors.mjs";
2
2
  import { emitSuccess } from "../output.mjs";
3
- import "../../../orchestrators/router/cleanup.mjs";
3
+ import { DEFAULT_LIFECYCLE_PRUNE_RESOURCES, defaultLifecyclePruneSelection, lifecyclePruneAppsWithLiveSiblings } from "../../../orchestrators/lifecycle-prune/index.mjs";
4
4
  import { Effect } from "effect";
5
5
  //#region src/surfaces/cli/commands/prune.ts
6
- const DEFAULT_PRUNE_RESOURCES = {
7
- containers: true,
8
- networks: true,
9
- volumes: true,
10
- images: false
11
- };
6
+ /** L4 mirror of the orchestrator's default resource scope. Re-export
7
+ * rather than redefine so the default never drifts between layers. */
8
+ const DEFAULT_PRUNE_RESOURCES = DEFAULT_LIFECYCLE_PRUNE_RESOURCES;
12
9
  const summarizePruneGroups = (groups) => {
13
10
  let liveGroups = 0;
14
11
  let sharedGroups = 0;
@@ -50,8 +47,16 @@ const summarizePruneGroupsForResources = (groups, resources) => {
50
47
  };
51
48
  const hasPruneResources = (resources) => resources.containers || resources.networks || resources.volumes || resources.images;
52
49
  const groupResourceCountForResources = (group, resources) => (resources.containers ? group.containers : 0) + (resources.networks ? group.networks : 0) + (resources.volumes ? group.volumes : 0) + (resources.images ? group.images : 0);
53
- const isAutoPrunableSharedGroup = (group) => group.app === "devstack-router" && group.stack.startsWith("devstack-router-");
54
- const defaultPruneSelection = (inventory, resources = DEFAULT_PRUNE_RESOURCES) => inventory.groups.filter((group) => !group.live && (!group.shared || isAutoPrunableSharedGroup(group)) && groupResourceCountForResources(group, resources) > 0).map((group) => group.key);
50
+ /** L4 mirror defers to the orchestrator's pinning predicate so the
51
+ * shared-resource rule lives in exactly one place. Exported so the
52
+ * interactive picker enforces the same per-app-shared pinning rule
53
+ * the default selection already obeys (manual toggle / select-all must
54
+ * not add a `per-app-shared` group pinned by a live sibling). */
55
+ const appsWithLiveSiblings = (inventory) => lifecyclePruneAppsWithLiveSiblings(inventory);
56
+ /** Delegates to the orchestrator's `defaultLifecyclePruneSelection`.
57
+ * The L4 shape mirrors the L3 shape field-for-field; the surface
58
+ * never reimplements the shared/per-app/router policy. */
59
+ const defaultPruneSelection = (inventory, resources = DEFAULT_PRUNE_RESOURCES) => defaultLifecyclePruneSelection(inventory, resources);
55
60
  const requireBulkConfirm = (verb, ctx) => {
56
61
  if (ctx.flags.dryRun) return Effect.void;
57
62
  if (ctx.flags.confirm.assumeYes) return Effect.void;
@@ -67,20 +72,30 @@ const requireInteractive = (ctx) => {
67
72
  hint: "use --list, --dry-run, or --all --yes in non-interactive shells"
68
73
  }));
69
74
  };
70
- const formatGroupLine = (group) => {
71
- const state = group.live && group.livePids.length > 0 ? `live pid ${group.livePids.join(",")}` : group.live ? "live" : group.shared ? "shared" : "idle";
75
+ const formatGroupLine = (group, pinnedApps) => {
76
+ const state = group.live && group.livePids.length > 0 ? `live pid ${group.livePids.join(",")}` : group.live ? "live" : group.sharedKind === "per-app-shared" ? pinnedApps.has(group.app) ? "shared (pinned by live sibling)" : "shared (auto)" : group.shared ? group.autoPrunable ? "shared (auto)" : "shared" : "idle";
72
77
  const running = group.runningContainers > 0 ? `, ${group.runningContainers} running` : "";
73
78
  const images = group.images > 0 ? `, ${group.images} image(s)` : "";
74
79
  return ` ${group.app}/${group.stack} ${state} ${group.containers} container(s)${running}, ${group.networks} network(s), ${group.volumes} volume(s)${images}`;
75
80
  };
76
81
  const inventoryLines = (inventory) => {
77
82
  if (inventory.groups.length === 0) return ["(no devstack-labelled Docker resources)"];
78
- return [`devstack prune inventory: ${inventory.totals.groups} group(s), ${inventory.totals.containers} container(s), ${inventory.totals.networks} network(s), ${inventory.totals.volumes} volume(s), ${inventory.totals.images} image(s)`, ...inventory.groups.map(formatGroupLine)];
83
+ const pinned = appsWithLiveSiblings(inventory);
84
+ return [`devstack prune inventory: ${inventory.totals.groups} group(s), ${inventory.totals.containers} container(s), ${inventory.totals.networks} network(s), ${inventory.totals.volumes} volume(s), ${inventory.totals.images} image(s)`, ...inventory.groups.map((g) => formatGroupLine(g, pinned))];
79
85
  };
80
- const completedLine = (summary, dryRun) => {
86
+ const completedLines = (summary, dryRun) => {
81
87
  const prefix = dryRun ? "[dry-run] would prune" : "prune completed";
82
88
  const skippedNetworks = summary.networksSkipped > 0 ? `, ${summary.networksSkipped} network(s) still in use` : "";
83
- return `${prefix}: ${summary.selectedGroups} group(s), ${summary.containersRemoved} container(s), ${summary.networksRemoved} network(s), ${summary.volumesRemoved} volume(s), ${summary.imagesRemoved} image(s), ${summary.skippedLiveGroups} live group(s) skipped${skippedNetworks}`;
89
+ const lines = [`${prefix}: ${summary.selectedGroups} group(s), ${summary.containersRemoved} container(s), ${summary.networksRemoved} network(s), ${summary.volumesRemoved} volume(s), ${summary.imagesRemoved} image(s), ${summary.skippedLiveGroups} live group(s) skipped${skippedNetworks}`];
90
+ if (summary.foreignNetworkHolders.length > 0) {
91
+ lines.push("foreign network holders:");
92
+ for (const h of summary.foreignNetworkHolders) lines.push(` ${h.network} held by ${h.container.name} (${h.container.id.slice(0, 12)})`);
93
+ }
94
+ if (summary.staleNetworkEndpoints.length > 0) {
95
+ lines.push("stale endpoints (Docker engine bug — restart Docker Desktop to reclaim these networks):");
96
+ for (const ep of summary.staleNetworkEndpoints) lines.push(` ${ep.network}: phantom endpoint "${ep.name}" (${ep.id.slice(0, 12)})`);
97
+ }
98
+ return lines;
84
99
  };
85
100
  const mapUnknownPruneError = (cause) => isCliError(cause) ? Effect.fail(cause) : Effect.fail(new CliInternalError({
86
101
  message: "prune failed",
@@ -144,7 +159,9 @@ const runPrune = (deps, ctx, options = {
144
159
  networksRemoved: 0,
145
160
  networksSkipped: 0,
146
161
  volumesRemoved: 0,
147
- imagesRemoved: 0
162
+ imagesRemoved: 0,
163
+ foreignNetworkHolders: [],
164
+ staleNetworkEndpoints: []
148
165
  }
149
166
  }
150
167
  },
@@ -168,11 +185,11 @@ const runPrune = (deps, ctx, options = {
168
185
  selection: selected,
169
186
  outcome
170
187
  },
171
- humanLines: [completedLine(outcome.summary, ctx.flags.dryRun)]
188
+ humanLines: completedLines(outcome.summary, ctx.flags.dryRun)
172
189
  });
173
190
  return { exitCode: 0 };
174
191
  }).pipe(Effect.withSpan("cli.prune"));
175
192
  //#endregion
176
- export { defaultPruneSelection, groupResourceCountForResources, hasPruneResources, runPrune, summarizePruneGroups, summarizePruneGroupsForResources };
193
+ export { DEFAULT_PRUNE_RESOURCES, appsWithLiveSiblings, defaultPruneSelection, groupResourceCountForResources, hasPruneResources, runPrune, summarizePruneGroups, summarizePruneGroupsForResources };
177
194
 
178
195
  //# sourceMappingURL=prune.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"prune.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/prune.ts"],"sourcesContent":["// CLI verb: `devstack prune` — cross-stack Docker cleanup.\n//\n// The rewrite initially wired this command to snapshot-catalog prune\n// only. That path can report success while leaving ordinary stale\n// containers, networks, and volumes untouched. This command owns the\n// operator surface; production injects a Docker-label inventory and\n// remover that understand both current labels and pre-rewrite labels.\n\nimport { Effect } from 'effect';\n\nimport {\n\tROUTER_CONTAINER_NAME_PREFIX,\n\tROUTER_SHARED_APP,\n} from '../../../orchestrators/router/cleanup.ts';\nimport {\n\ttype CliError,\n\tCliConfirmRequiredError,\n\tCliInternalError,\n\tCliUsageError,\n\tisCliError,\n} from '../errors.ts';\nimport { emitSuccess } from '../output.ts';\nimport type { CommandContext, CommandResult } from './index.ts';\n\nexport interface PruneGroup {\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\treadonly containers: number;\n\treadonly runningContainers: number;\n\treadonly networks: number;\n\treadonly volumes: number;\n\treadonly images: number;\n}\n\nexport interface PruneTotals {\n\treadonly groups: number;\n\treadonly liveGroups: number;\n\treadonly sharedGroups: number;\n\treadonly containers: number;\n\treadonly runningContainers: number;\n\treadonly networks: number;\n\treadonly volumes: number;\n\treadonly images: number;\n}\n\nexport interface PruneInventory {\n\treadonly groups: ReadonlyArray<PruneGroup>;\n\treadonly totals: PruneTotals;\n}\n\nexport interface PruneResourceScope {\n\treadonly containers: boolean;\n\treadonly networks: boolean;\n\treadonly volumes: boolean;\n\treadonly images: boolean;\n}\n\nexport interface PruneTargetSelection {\n\treadonly groupKeys: ReadonlyArray<string>;\n\treadonly resources: PruneResourceScope;\n}\n\nexport interface PruneSelection extends PruneTargetSelection {\n\treadonly dryRun: boolean;\n}\n\nexport interface PruneSummary {\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}\n\nexport type PruneOutcome = { readonly kind: 'completed'; readonly summary: PruneSummary };\n\nexport interface PruneDeps {\n\treadonly inventory: () => Effect.Effect<PruneInventory, unknown>;\n\treadonly prune: (selection: PruneSelection) => Effect.Effect<PruneOutcome, unknown>;\n\treadonly select: (\n\t\tinventory: PruneInventory,\n\t\tresources: PruneResourceScope,\n\t) => Effect.Effect<PruneTargetSelection, unknown>;\n}\n\nexport interface PruneRunOptions {\n\treadonly mode: 'auto' | 'list' | 'all';\n\treadonly resources: PruneResourceScope;\n}\n\nexport const DEFAULT_PRUNE_RESOURCES: PruneResourceScope = {\n\tcontainers: true,\n\tnetworks: true,\n\tvolumes: true,\n\timages: false,\n};\n\nexport const summarizePruneGroups = (groups: ReadonlyArray<PruneGroup>): PruneTotals => {\n\tlet liveGroups = 0;\n\tlet sharedGroups = 0;\n\tlet containers = 0;\n\tlet runningContainers = 0;\n\tlet networks = 0;\n\tlet volumes = 0;\n\tlet images = 0;\n\tfor (const group of groups) {\n\t\tif (group.live) liveGroups += 1;\n\t\tif (group.shared) sharedGroups += 1;\n\t\tcontainers += group.containers;\n\t\trunningContainers += group.runningContainers;\n\t\tnetworks += group.networks;\n\t\tvolumes += group.volumes;\n\t\timages += group.images;\n\t}\n\treturn {\n\t\tgroups: groups.length,\n\t\tliveGroups,\n\t\tsharedGroups,\n\t\tcontainers,\n\t\trunningContainers,\n\t\tnetworks,\n\t\tvolumes,\n\t\timages,\n\t};\n};\n\nexport const summarizePruneGroupsForResources = (\n\tgroups: ReadonlyArray<PruneGroup>,\n\tresources: PruneResourceScope,\n): PruneTotals => {\n\tconst totals = summarizePruneGroups(groups);\n\treturn {\n\t\t...totals,\n\t\tcontainers: resources.containers ? totals.containers : 0,\n\t\trunningContainers: resources.containers ? totals.runningContainers : 0,\n\t\tnetworks: resources.networks ? totals.networks : 0,\n\t\tvolumes: resources.volumes ? totals.volumes : 0,\n\t\timages: resources.images ? totals.images : 0,\n\t};\n};\n\nexport const hasPruneResources = (resources: PruneResourceScope): boolean =>\n\tresources.containers || resources.networks || resources.volumes || resources.images;\n\nexport const groupResourceCountForResources = (\n\tgroup: PruneGroup,\n\tresources: PruneResourceScope,\n): number =>\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\nconst isAutoPrunableSharedGroup = (group: PruneGroup): boolean =>\n\tgroup.app === ROUTER_SHARED_APP && group.stack.startsWith(ROUTER_CONTAINER_NAME_PREFIX);\n\nexport const defaultPruneSelection = (\n\tinventory: PruneInventory,\n\tresources: PruneResourceScope = DEFAULT_PRUNE_RESOURCES,\n): ReadonlyArray<string> =>\n\tinventory.groups\n\t\t.filter(\n\t\t\t(group) =>\n\t\t\t\t!group.live &&\n\t\t\t\t(!group.shared || isAutoPrunableSharedGroup(group)) &&\n\t\t\t\tgroupResourceCountForResources(group, resources) > 0,\n\t\t)\n\t\t.map((group) => group.key);\n\nconst requireBulkConfirm = (verb: string, ctx: CommandContext): Effect.Effect<void, CliError> => {\n\tif (ctx.flags.dryRun) return Effect.void;\n\tif (ctx.flags.confirm.assumeYes) return Effect.void;\n\treturn Effect.fail(\n\t\tnew CliConfirmRequiredError({\n\t\t\tverb,\n\t\t\thint: 'rerun with --yes for non-interactive bulk prune, or omit --all in a TTY to pick rows interactively',\n\t\t}),\n\t);\n};\n\nconst requireInteractive = (ctx: CommandContext): Effect.Effect<void, CliError> => {\n\tif (ctx.flags.confirm.stdinIsTty && !ctx.flags.confirm.forbidPrompt) return Effect.void;\n\treturn Effect.fail(\n\t\tnew CliConfirmRequiredError({\n\t\t\tverb: 'prune',\n\t\t\thint: 'use --list, --dry-run, or --all --yes in non-interactive shells',\n\t\t}),\n\t);\n};\n\nconst formatGroupLine = (group: PruneGroup): string => {\n\tconst state =\n\t\tgroup.live && group.livePids.length > 0\n\t\t\t? `live pid ${group.livePids.join(',')}`\n\t\t\t: group.live\n\t\t\t\t? 'live'\n\t\t\t\t: group.shared\n\t\t\t\t\t? 'shared'\n\t\t\t\t\t: 'idle';\n\tconst running = group.runningContainers > 0 ? `, ${group.runningContainers} running` : '';\n\tconst images = group.images > 0 ? `, ${group.images} image(s)` : '';\n\treturn ` ${group.app}/${group.stack} ${state} ${group.containers} container(s)${running}, ${group.networks} network(s), ${group.volumes} volume(s)${images}`;\n};\n\nconst inventoryLines = (inventory: PruneInventory): ReadonlyArray<string> => {\n\tif (inventory.groups.length === 0) return ['(no devstack-labelled Docker resources)'];\n\treturn [\n\t\t`devstack prune inventory: ${inventory.totals.groups} group(s), ${inventory.totals.containers} container(s), ${inventory.totals.networks} network(s), ${inventory.totals.volumes} volume(s), ${inventory.totals.images} image(s)`,\n\t\t...inventory.groups.map(formatGroupLine),\n\t];\n};\n\nconst completedLine = (summary: PruneSummary, dryRun: boolean): string => {\n\tconst prefix = dryRun ? '[dry-run] would prune' : 'prune completed';\n\tconst skippedNetworks =\n\t\tsummary.networksSkipped > 0 ? `, ${summary.networksSkipped} network(s) still in use` : '';\n\treturn `${prefix}: ${summary.selectedGroups} group(s), ${summary.containersRemoved} container(s), ${summary.networksRemoved} network(s), ${summary.volumesRemoved} volume(s), ${summary.imagesRemoved} image(s), ${summary.skippedLiveGroups} live group(s) skipped${skippedNetworks}`;\n};\n\nconst mapUnknownPruneError = (cause: unknown): Effect.Effect<never, CliError> =>\n\tisCliError(cause)\n\t\t? Effect.fail(cause)\n\t\t: Effect.fail(new CliInternalError({ message: 'prune failed', cause }));\n\nexport const runPrune = (\n\tdeps: PruneDeps,\n\tctx: CommandContext,\n\toptions: PruneRunOptions = { mode: 'auto', resources: DEFAULT_PRUNE_RESOURCES },\n): Effect.Effect<CommandResult, CliError> =>\n\tEffect.gen(function* () {\n\t\tconst started = Date.now();\n\t\tconst inventory = yield* deps.inventory().pipe(Effect.catch(mapUnknownPruneError));\n\n\t\tif (options.mode === 'list') {\n\t\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\t\tcommand: 'prune',\n\t\t\t\telapsedMs: Date.now() - started,\n\t\t\t\tdryRun: true,\n\t\t\t\tdata: { mode: 'list' as const, inventory },\n\t\t\t\thumanLines: inventoryLines(inventory),\n\t\t\t});\n\t\t\treturn { exitCode: 0 } as CommandResult;\n\t\t}\n\n\t\tif (!hasPruneResources(options.resources)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew CliUsageError({\n\t\t\t\t\tmessage: 'prune requires at least one resource type',\n\t\t\t\t\thint: 'enable containers, networks, volumes, or images',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tlet selected: PruneTargetSelection;\n\t\tif (ctx.flags.dryRun || options.mode === 'all' || ctx.flags.confirm.assumeYes) {\n\t\t\tyield* requireBulkConfirm('prune', ctx);\n\t\t\tselected = {\n\t\t\t\tgroupKeys: defaultPruneSelection(inventory, options.resources),\n\t\t\t\tresources: options.resources,\n\t\t\t};\n\t\t} else {\n\t\t\tyield* requireInteractive(ctx);\n\t\t\tselected = yield* deps\n\t\t\t\t.select(inventory, options.resources)\n\t\t\t\t.pipe(Effect.catch(mapUnknownPruneError));\n\t\t}\n\n\t\tif (!hasPruneResources(selected.resources)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew CliUsageError({\n\t\t\t\t\tmessage: 'prune requires at least one selected resource type',\n\t\t\t\t\thint: 'toggle at least one resource type before confirming',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (selected.groupKeys.length === 0) {\n\t\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\t\tcommand: 'prune',\n\t\t\t\telapsedMs: Date.now() - started,\n\t\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\t\tdata: {\n\t\t\t\t\tmode: options.mode,\n\t\t\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\t\t\tinventory,\n\t\t\t\t\tselection: selected,\n\t\t\t\t\toutcome: {\n\t\t\t\t\t\tkind: 'completed' as const,\n\t\t\t\t\t\tsummary: {\n\t\t\t\t\t\t\tinspectedGroups: inventory.groups.length,\n\t\t\t\t\t\t\tselectedGroups: 0,\n\t\t\t\t\t\t\tskippedLiveGroups: inventory.totals.liveGroups,\n\t\t\t\t\t\t\tcontainersRemoved: 0,\n\t\t\t\t\t\t\tnetworksRemoved: 0,\n\t\t\t\t\t\t\tnetworksSkipped: 0,\n\t\t\t\t\t\t\tvolumesRemoved: 0,\n\t\t\t\t\t\t\timagesRemoved: 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\thumanLines: ['prune: no idle resource groups selected'],\n\t\t\t});\n\t\t\treturn { exitCode: 0 } as CommandResult;\n\t\t}\n\n\t\tconst outcome = yield* deps\n\t\t\t.prune({\n\t\t\t\tgroupKeys: selected.groupKeys,\n\t\t\t\tresources: selected.resources,\n\t\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\t})\n\t\t\t.pipe(Effect.catch(mapUnknownPruneError));\n\n\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\tcommand: 'prune',\n\t\t\telapsedMs: Date.now() - started,\n\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\tdata: {\n\t\t\t\tmode: options.mode,\n\t\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\t\tinventory,\n\t\t\t\tselection: selected,\n\t\t\t\toutcome,\n\t\t\t},\n\t\t\thumanLines: [completedLine(outcome.summary, ctx.flags.dryRun)],\n\t\t});\n\t\treturn { exitCode: 0 } as CommandResult;\n\t}).pipe(Effect.withSpan('cli.prune'));\n"],"mappings":"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"}
1
+ {"version":3,"file":"prune.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/prune.ts"],"sourcesContent":["// CLI verb: `devstack prune` — cross-stack Docker cleanup.\n//\n// The rewrite initially wired this command to snapshot-catalog prune\n// only. That path can report success while leaving ordinary stale\n// containers, networks, and volumes untouched. This command owns the\n// operator surface; production injects a Docker-label inventory and\n// remover that understand both current labels and pre-rewrite labels.\n\nimport { Effect } from 'effect';\n\nimport {\n\tdefaultLifecyclePruneSelection,\n\tDEFAULT_LIFECYCLE_PRUNE_RESOURCES,\n\tlifecyclePruneAppsWithLiveSiblings,\n\ttype SharedGroupKind,\n} from '../../../orchestrators/lifecycle-prune/index.ts';\nimport {\n\ttype CliError,\n\tCliConfirmRequiredError,\n\tCliInternalError,\n\tCliUsageError,\n\tisCliError,\n} from '../errors.ts';\nimport { emitSuccess } from '../output.ts';\nimport type { CommandContext, CommandResult } from './index.ts';\n\nexport interface PruneGroup {\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 groups — mirrored from the L3 orchestrator\n\t * shape so surfaces never re-derive the `_per-app_` / router-singleton\n\t * predicate. `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 lifecycle-prune orchestrator; surfaces never\n\t * recompute the 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\nexport interface PruneTotals {\n\treadonly groups: number;\n\treadonly liveGroups: number;\n\treadonly sharedGroups: number;\n\treadonly containers: number;\n\treadonly runningContainers: number;\n\treadonly networks: number;\n\treadonly volumes: number;\n\treadonly images: number;\n}\n\nexport interface PruneInventory {\n\treadonly groups: ReadonlyArray<PruneGroup>;\n\treadonly totals: PruneTotals;\n}\n\nexport interface PruneResourceScope {\n\treadonly containers: boolean;\n\treadonly networks: boolean;\n\treadonly volumes: boolean;\n\treadonly images: boolean;\n}\n\nexport interface PruneTargetSelection {\n\treadonly groupKeys: ReadonlyArray<string>;\n\treadonly resources: PruneResourceScope;\n}\n\nexport interface PruneSelection extends PruneTargetSelection {\n\treadonly dryRun: boolean;\n}\n\n/** One non-devstack container still holding a network that prune\n * could not remove. Surfaced so the operator can investigate the\n * external holder (typically a test fixture, a sibling project's\n * container, or a manually-attached debugging container). */\nexport interface PruneForeignNetworkHolder {\n\treadonly network: string;\n\treadonly container: { readonly id: string; readonly name: string };\n}\n\n/** Endpoint Docker still tracks on a network even though the owning\n * container is gone and no CLI/API path can remove it. Docker engine\n * bug — only a daemon restart reclaims the network. */\nexport interface PruneStaleNetworkEndpoint {\n\treadonly network: string;\n\treadonly name: string;\n\treadonly id: string;\n}\n\nexport interface PruneSummary {\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/** Foreign holders surviving network removal. Empty when every\n\t * network came down cleanly or no network removal was attempted. */\n\treadonly foreignNetworkHolders: ReadonlyArray<PruneForeignNetworkHolder>;\n\t/** Stale phantom endpoints requiring a Docker daemon restart to\n\t * reclaim. Empty in the happy path. */\n\treadonly staleNetworkEndpoints: ReadonlyArray<PruneStaleNetworkEndpoint>;\n}\n\nexport type PruneOutcome = { readonly kind: 'completed'; readonly summary: PruneSummary };\n\nexport interface PruneDeps {\n\treadonly inventory: () => Effect.Effect<PruneInventory, unknown>;\n\treadonly prune: (selection: PruneSelection) => Effect.Effect<PruneOutcome, unknown>;\n\treadonly select: (\n\t\tinventory: PruneInventory,\n\t\tresources: PruneResourceScope,\n\t) => Effect.Effect<PruneTargetSelection, unknown>;\n}\n\nexport interface PruneRunOptions {\n\treadonly mode: 'auto' | 'list' | 'all';\n\treadonly resources: PruneResourceScope;\n}\n\n/** L4 mirror of the orchestrator's default resource scope. Re-export\n * rather than redefine so the default never drifts between layers. */\nexport const DEFAULT_PRUNE_RESOURCES: PruneResourceScope = DEFAULT_LIFECYCLE_PRUNE_RESOURCES;\n\nexport const summarizePruneGroups = (groups: ReadonlyArray<PruneGroup>): PruneTotals => {\n\tlet liveGroups = 0;\n\tlet sharedGroups = 0;\n\tlet containers = 0;\n\tlet runningContainers = 0;\n\tlet networks = 0;\n\tlet volumes = 0;\n\tlet images = 0;\n\tfor (const group of groups) {\n\t\tif (group.live) liveGroups += 1;\n\t\tif (group.shared) sharedGroups += 1;\n\t\tcontainers += group.containers;\n\t\trunningContainers += group.runningContainers;\n\t\tnetworks += group.networks;\n\t\tvolumes += group.volumes;\n\t\timages += group.images;\n\t}\n\treturn {\n\t\tgroups: groups.length,\n\t\tliveGroups,\n\t\tsharedGroups,\n\t\tcontainers,\n\t\trunningContainers,\n\t\tnetworks,\n\t\tvolumes,\n\t\timages,\n\t};\n};\n\nexport const summarizePruneGroupsForResources = (\n\tgroups: ReadonlyArray<PruneGroup>,\n\tresources: PruneResourceScope,\n): PruneTotals => {\n\tconst totals = summarizePruneGroups(groups);\n\treturn {\n\t\t...totals,\n\t\tcontainers: resources.containers ? totals.containers : 0,\n\t\trunningContainers: resources.containers ? totals.runningContainers : 0,\n\t\tnetworks: resources.networks ? totals.networks : 0,\n\t\tvolumes: resources.volumes ? totals.volumes : 0,\n\t\timages: resources.images ? totals.images : 0,\n\t};\n};\n\nexport const hasPruneResources = (resources: PruneResourceScope): boolean =>\n\tresources.containers || resources.networks || resources.volumes || resources.images;\n\nexport const groupResourceCountForResources = (\n\tgroup: PruneGroup,\n\tresources: PruneResourceScope,\n): number =>\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/** L4 mirror — defers to the orchestrator's pinning predicate so the\n * shared-resource rule lives in exactly one place. Exported so the\n * interactive picker enforces the same per-app-shared pinning rule\n * the default selection already obeys (manual toggle / select-all must\n * not add a `per-app-shared` group pinned by a live sibling). */\nexport const appsWithLiveSiblings = (inventory: PruneInventory): ReadonlySet<string> =>\n\tlifecyclePruneAppsWithLiveSiblings(inventory);\n\n/** Delegates to the orchestrator's `defaultLifecyclePruneSelection`.\n * The L4 shape mirrors the L3 shape field-for-field; the surface\n * never reimplements the shared/per-app/router policy. */\nexport const defaultPruneSelection = (\n\tinventory: PruneInventory,\n\tresources: PruneResourceScope = DEFAULT_PRUNE_RESOURCES,\n): ReadonlyArray<string> => defaultLifecyclePruneSelection(inventory, resources);\n\nconst requireBulkConfirm = (verb: string, ctx: CommandContext): Effect.Effect<void, CliError> => {\n\tif (ctx.flags.dryRun) return Effect.void;\n\tif (ctx.flags.confirm.assumeYes) return Effect.void;\n\treturn Effect.fail(\n\t\tnew CliConfirmRequiredError({\n\t\t\tverb,\n\t\t\thint: 'rerun with --yes for non-interactive bulk prune, or omit --all in a TTY to pick rows interactively',\n\t\t}),\n\t);\n};\n\nconst requireInteractive = (ctx: CommandContext): Effect.Effect<void, CliError> => {\n\tif (ctx.flags.confirm.stdinIsTty && !ctx.flags.confirm.forbidPrompt) return Effect.void;\n\treturn Effect.fail(\n\t\tnew CliConfirmRequiredError({\n\t\t\tverb: 'prune',\n\t\t\thint: 'use --list, --dry-run, or --all --yes in non-interactive shells',\n\t\t}),\n\t);\n};\n\nconst formatGroupLine = (group: PruneGroup, pinnedApps: ReadonlySet<string>): string => {\n\tconst state =\n\t\tgroup.live && group.livePids.length > 0\n\t\t\t? `live pid ${group.livePids.join(',')}`\n\t\t\t: group.live\n\t\t\t\t? 'live'\n\t\t\t\t: group.sharedKind === 'per-app-shared'\n\t\t\t\t\t? pinnedApps.has(group.app)\n\t\t\t\t\t\t? 'shared (pinned by live sibling)'\n\t\t\t\t\t\t: 'shared (auto)'\n\t\t\t\t\t: group.shared\n\t\t\t\t\t\t? group.autoPrunable\n\t\t\t\t\t\t\t? 'shared (auto)'\n\t\t\t\t\t\t\t: 'shared'\n\t\t\t\t\t\t: 'idle';\n\tconst running = group.runningContainers > 0 ? `, ${group.runningContainers} running` : '';\n\tconst images = group.images > 0 ? `, ${group.images} image(s)` : '';\n\treturn ` ${group.app}/${group.stack} ${state} ${group.containers} container(s)${running}, ${group.networks} network(s), ${group.volumes} volume(s)${images}`;\n};\n\nconst inventoryLines = (inventory: PruneInventory): ReadonlyArray<string> => {\n\tif (inventory.groups.length === 0) return ['(no devstack-labelled Docker resources)'];\n\tconst pinned = appsWithLiveSiblings(inventory);\n\treturn [\n\t\t`devstack prune inventory: ${inventory.totals.groups} group(s), ${inventory.totals.containers} container(s), ${inventory.totals.networks} network(s), ${inventory.totals.volumes} volume(s), ${inventory.totals.images} image(s)`,\n\t\t...inventory.groups.map((g) => formatGroupLine(g, pinned)),\n\t];\n};\n\nconst completedLines = (summary: PruneSummary, dryRun: boolean): ReadonlyArray<string> => {\n\tconst prefix = dryRun ? '[dry-run] would prune' : 'prune completed';\n\tconst skippedNetworks =\n\t\tsummary.networksSkipped > 0 ? `, ${summary.networksSkipped} network(s) still in use` : '';\n\tconst head = `${prefix}: ${summary.selectedGroups} group(s), ${summary.containersRemoved} container(s), ${summary.networksRemoved} network(s), ${summary.volumesRemoved} volume(s), ${summary.imagesRemoved} image(s), ${summary.skippedLiveGroups} live group(s) skipped${skippedNetworks}`;\n\tconst lines: Array<string> = [head];\n\tif (summary.foreignNetworkHolders.length > 0) {\n\t\tlines.push('foreign network holders:');\n\t\tfor (const h of summary.foreignNetworkHolders) {\n\t\t\tlines.push(` ${h.network} held by ${h.container.name} (${h.container.id.slice(0, 12)})`);\n\t\t}\n\t}\n\tif (summary.staleNetworkEndpoints.length > 0) {\n\t\tlines.push(\n\t\t\t'stale endpoints (Docker engine bug — restart Docker Desktop to reclaim these networks):',\n\t\t);\n\t\tfor (const ep of summary.staleNetworkEndpoints) {\n\t\t\tlines.push(` ${ep.network}: phantom endpoint \"${ep.name}\" (${ep.id.slice(0, 12)})`);\n\t\t}\n\t}\n\treturn lines;\n};\n\nconst mapUnknownPruneError = (cause: unknown): Effect.Effect<never, CliError> =>\n\tisCliError(cause)\n\t\t? Effect.fail(cause)\n\t\t: Effect.fail(new CliInternalError({ message: 'prune failed', cause }));\n\nexport const runPrune = (\n\tdeps: PruneDeps,\n\tctx: CommandContext,\n\toptions: PruneRunOptions = { mode: 'auto', resources: DEFAULT_PRUNE_RESOURCES },\n): Effect.Effect<CommandResult, CliError> =>\n\tEffect.gen(function* () {\n\t\tconst started = Date.now();\n\t\tconst inventory = yield* deps.inventory().pipe(Effect.catch(mapUnknownPruneError));\n\n\t\tif (options.mode === 'list') {\n\t\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\t\tcommand: 'prune',\n\t\t\t\telapsedMs: Date.now() - started,\n\t\t\t\tdryRun: true,\n\t\t\t\tdata: { mode: 'list' as const, inventory },\n\t\t\t\thumanLines: inventoryLines(inventory),\n\t\t\t});\n\t\t\treturn { exitCode: 0 } as CommandResult;\n\t\t}\n\n\t\tif (!hasPruneResources(options.resources)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew CliUsageError({\n\t\t\t\t\tmessage: 'prune requires at least one resource type',\n\t\t\t\t\thint: 'enable containers, networks, volumes, or images',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tlet selected: PruneTargetSelection;\n\t\tif (ctx.flags.dryRun || options.mode === 'all' || ctx.flags.confirm.assumeYes) {\n\t\t\tyield* requireBulkConfirm('prune', ctx);\n\t\t\tselected = {\n\t\t\t\tgroupKeys: defaultPruneSelection(inventory, options.resources),\n\t\t\t\tresources: options.resources,\n\t\t\t};\n\t\t} else {\n\t\t\tyield* requireInteractive(ctx);\n\t\t\tselected = yield* deps\n\t\t\t\t.select(inventory, options.resources)\n\t\t\t\t.pipe(Effect.catch(mapUnknownPruneError));\n\t\t}\n\n\t\tif (!hasPruneResources(selected.resources)) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew CliUsageError({\n\t\t\t\t\tmessage: 'prune requires at least one selected resource type',\n\t\t\t\t\thint: 'toggle at least one resource type before confirming',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (selected.groupKeys.length === 0) {\n\t\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\t\tcommand: 'prune',\n\t\t\t\telapsedMs: Date.now() - started,\n\t\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\t\tdata: {\n\t\t\t\t\tmode: options.mode,\n\t\t\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\t\t\tinventory,\n\t\t\t\t\tselection: selected,\n\t\t\t\t\toutcome: {\n\t\t\t\t\t\tkind: 'completed' as const,\n\t\t\t\t\t\tsummary: {\n\t\t\t\t\t\t\tinspectedGroups: inventory.groups.length,\n\t\t\t\t\t\t\tselectedGroups: 0,\n\t\t\t\t\t\t\tskippedLiveGroups: inventory.totals.liveGroups,\n\t\t\t\t\t\t\tcontainersRemoved: 0,\n\t\t\t\t\t\t\tnetworksRemoved: 0,\n\t\t\t\t\t\t\tnetworksSkipped: 0,\n\t\t\t\t\t\t\tvolumesRemoved: 0,\n\t\t\t\t\t\t\timagesRemoved: 0,\n\t\t\t\t\t\t\tforeignNetworkHolders: [],\n\t\t\t\t\t\t\tstaleNetworkEndpoints: [],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\thumanLines: ['prune: no idle resource groups selected'],\n\t\t\t});\n\t\t\treturn { exitCode: 0 } as CommandResult;\n\t\t}\n\n\t\tconst outcome = yield* deps\n\t\t\t.prune({\n\t\t\t\tgroupKeys: selected.groupKeys,\n\t\t\t\tresources: selected.resources,\n\t\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\t})\n\t\t\t.pipe(Effect.catch(mapUnknownPruneError));\n\n\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\tcommand: 'prune',\n\t\t\telapsedMs: Date.now() - started,\n\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\tdata: {\n\t\t\t\tmode: options.mode,\n\t\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\t\tinventory,\n\t\t\t\tselection: selected,\n\t\t\t\toutcome,\n\t\t\t},\n\t\t\thumanLines: completedLines(outcome.summary, ctx.flags.dryRun),\n\t\t});\n\t\treturn { exitCode: 0 } as CommandResult;\n\t}).pipe(Effect.withSpan('cli.prune'));\n"],"mappings":"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"}
@@ -66,7 +66,7 @@ const runStatus = (deps, ctx) => Effect.gen(function* () {
66
66
  const started = Date.now();
67
67
  const app = ctx.flags.app ?? "";
68
68
  const stack = ctx.flags.stack ?? "";
69
- const data = buildStatusPayload(yield* deps.reader.readState(app, stack));
69
+ const data = buildStatusPayload(yield* deps.reader.readState());
70
70
  const elapsedMs = Date.now() - started;
71
71
  const humanLines = [];
72
72
  if (!data.present) humanLines.push(`status: no state present for ${app || "(no app)"} / ${stack || "(no stack)"}`);
@@ -90,6 +90,6 @@ const runStatus = (deps, ctx) => Effect.gen(function* () {
90
90
  return { exitCode: 0 };
91
91
  }).pipe(Effect.withSpan("cli.status"));
92
92
  //#endregion
93
- export { runStatus };
93
+ export { buildStatusPayload, runStatus };
94
94
 
95
95
  //# sourceMappingURL=status.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"status.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/status.ts"],"sourcesContent":["// CLI verb: `devstack status` — observational read of stack state.\n//\n// Architecture (distilled/20-cli.md § Subcommands § Lifecycle):\n// \"status — observational read of stack state, manifest, and (if\n// present) fork meta.\"\n//\n// Surface invariant: status MUST tolerate missing/malformed state\n// files. It describes what IS, not what should be. The architecture\n// explicitly calls this out — throwing on a stack that hasn't been\n// brought up would be a regression.\n\nimport { Effect } from 'effect';\n\nimport type { SubscribableState } from '../../../substrate/projection.ts';\nimport type { CliError } from '../errors.ts';\nimport { emitSuccess } from '../output.ts';\nimport type { CommandContext, CommandResult } from './index.ts';\n\n/** Pure projection: `SubscribableState` → JSON-safe status blob.\n * Never leaks substrate types. */\nexport const buildStatusPayload = (state: SubscribableState | null) => {\n\tif (state === null) {\n\t\treturn {\n\t\t\tpresent: false as const,\n\t\t\tidentity: null,\n\t\t\tcycle: null,\n\t\t\trowCount: 0,\n\t\t\tendpointCount: 0,\n\t\t\taccountCount: 0,\n\t\t\tpackageCount: 0,\n\t\t\terrorCount: 0,\n\t\t};\n\t}\n\treturn {\n\t\tpresent: true as const,\n\t\tidentity: { ...state.identity },\n\t\tcycle: { ...state.cycle },\n\t\trowCount: state.rows.length,\n\t\tendpointCount: state.endpoints.length,\n\t\taccountCount: state.accounts.length,\n\t\tpackageCount: state.packages.length,\n\t\terrorCount: state.errors.length,\n\t\trows: state.rows.map((r) => ({\n\t\t\tkey: r.key as string,\n\t\t\trole: r.role,\n\t\t\tstatus: r.status,\n\t\t\tphase: r.phase,\n\t\t\tlastErrorTag: r.lastError?.tag ?? null,\n\t\t})),\n\t\tendpoints: state.endpoints.map((e) => ({\n\t\t\tendpointKey: e.endpointKey as string,\n\t\t\tname: e.name,\n\t\t\turl: e.displayUrl ?? e.url,\n\t\t})),\n\t\taccounts: state.accounts.map((account) => ({\n\t\t\tkey: account.key,\n\t\t\trowKey: account.rowKey,\n\t\t\tname: account.name,\n\t\t\taddress: account.address,\n\t\t\tscheme: account.scheme,\n\t\t\tsource: account.source,\n\t\t\tfunding: { ...account.funding },\n\t\t\twalletVisible: account.walletVisible,\n\t\t})),\n\t\tpackages: state.packages.map((pkg) => ({\n\t\t\tkey: pkg.key,\n\t\t\trowKey: pkg.rowKey,\n\t\t\tname: pkg.name,\n\t\t\tkind: pkg.kind,\n\t\t\tpackageId: pkg.packageId,\n\t\t\tupgradeCapId: pkg.upgradeCapId,\n\t\t\tmvrPlaceholder: pkg.mvrPlaceholder,\n\t\t\tsourcePath: pkg.sourcePath,\n\t\t})),\n\t};\n};\n\n/**\n * Status reader interface. The dispatcher wires this to the\n * substrate's projection ref via a thin reader; tests pass a stub.\n * The CLI never calls engine methods directly — it reads the same\n * projection any peer surface (TUI, programmable API) sees.\n */\nexport interface StatusReader {\n\t/** Resolve current projection for `(app, stack)`. Returns `null`\n\t * if no state has been written yet — that is a normal, tolerated\n\t * state for a freshly-defined stack. */\n\treadonly readState: (app: string, stack: string) => Effect.Effect<SubscribableState | null>;\n}\n\nexport interface StatusDeps {\n\treadonly reader: StatusReader;\n}\n\n/**\n * Run `devstack status`. Tolerant of missing state. Always succeeds\n * (returns ok=true) unless the projection read itself raises (which\n * the reader contractually swallows for missing files).\n */\nexport const runStatus = (\n\tdeps: StatusDeps,\n\tctx: CommandContext,\n): Effect.Effect<CommandResult, CliError> =>\n\tEffect.gen(function* () {\n\t\tconst started = Date.now();\n\t\t// Resolve `(app, stack)` from flags. The dispatcher seeds these\n\t\t// from the precedence chain; if both are absent we still render\n\t\t// (the projection reader returns `null`).\n\t\tconst app = ctx.flags.app ?? '';\n\t\tconst stack = ctx.flags.stack ?? '';\n\t\tconst state = yield* deps.reader.readState(app, stack);\n\t\tconst data = buildStatusPayload(state);\n\t\tconst elapsedMs = Date.now() - started;\n\n\t\tconst humanLines: Array<string> = [];\n\t\tif (!data.present) {\n\t\t\thumanLines.push(\n\t\t\t\t`status: no state present for ${app || '(no app)'} / ${stack || '(no stack)'}`,\n\t\t\t);\n\t\t} else {\n\t\t\thumanLines.push(`app: ${data.identity!.app}`);\n\t\t\thumanLines.push(`stack: ${data.identity!.stack}`);\n\t\t\thumanLines.push(`network: ${data.identity!.network}`);\n\t\t\thumanLines.push(`cycle: #${data.cycle!.id} ${data.cycle!.phase}`);\n\t\t\thumanLines.push(`rows: ${data.rowCount}`);\n\t\t\thumanLines.push(`endpoints: ${data.endpointCount}`);\n\t\t\thumanLines.push(`accounts: ${data.accountCount}`);\n\t\t\thumanLines.push(`packages: ${data.packageCount}`);\n\t\t\thumanLines.push(`errors: ${data.errorCount}`);\n\t\t}\n\n\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\tcommand: 'status',\n\t\t\telapsedMs,\n\t\t\tdata,\n\t\t\thumanLines,\n\t\t});\n\t\treturn { exitCode: 0 } as CommandResult;\n\t}).pipe(Effect.withSpan('cli.status'));\n"],"mappings":";;;;;AAoBA,MAAa,sBAAsB,UAAoC;AACtE,KAAI,UAAU,KACb,QAAO;EACN,SAAS;EACT,UAAU;EACV,OAAO;EACP,UAAU;EACV,eAAe;EACf,cAAc;EACd,cAAc;EACd,YAAY;EACZ;AAEF,QAAO;EACN,SAAS;EACT,UAAU,EAAE,GAAG,MAAM,UAAU;EAC/B,OAAO,EAAE,GAAG,MAAM,OAAO;EACzB,UAAU,MAAM,KAAK;EACrB,eAAe,MAAM,UAAU;EAC/B,cAAc,MAAM,SAAS;EAC7B,cAAc,MAAM,SAAS;EAC7B,YAAY,MAAM,OAAO;EACzB,MAAM,MAAM,KAAK,KAAK,OAAO;GAC5B,KAAK,EAAE;GACP,MAAM,EAAE;GACR,QAAQ,EAAE;GACV,OAAO,EAAE;GACT,cAAc,EAAE,WAAW,OAAO;GAClC,EAAE;EACH,WAAW,MAAM,UAAU,KAAK,OAAO;GACtC,aAAa,EAAE;GACf,MAAM,EAAE;GACR,KAAK,EAAE,cAAc,EAAE;GACvB,EAAE;EACH,UAAU,MAAM,SAAS,KAAK,aAAa;GAC1C,KAAK,QAAQ;GACb,QAAQ,QAAQ;GAChB,MAAM,QAAQ;GACd,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,SAAS,EAAE,GAAG,QAAQ,SAAS;GAC/B,eAAe,QAAQ;GACvB,EAAE;EACH,UAAU,MAAM,SAAS,KAAK,SAAS;GACtC,KAAK,IAAI;GACT,QAAQ,IAAI;GACZ,MAAM,IAAI;GACV,MAAM,IAAI;GACV,WAAW,IAAI;GACf,cAAc,IAAI;GAClB,gBAAgB,IAAI;GACpB,YAAY,IAAI;GAChB,EAAE;EACH;;;;;;;AAyBF,MAAa,aACZ,MACA,QAEA,OAAO,IAAI,aAAa;CACvB,MAAM,UAAU,KAAK,KAAK;CAI1B,MAAM,MAAM,IAAI,MAAM,OAAO;CAC7B,MAAM,QAAQ,IAAI,MAAM,SAAS;CAEjC,MAAM,OAAO,mBAAmB,OADX,KAAK,OAAO,UAAU,KAAK,MAAM,CAChB;CACtC,MAAM,YAAY,KAAK,KAAK,GAAG;CAE/B,MAAM,aAA4B,EAAE;AACpC,KAAI,CAAC,KAAK,QACT,YAAW,KACV,gCAAgC,OAAO,WAAW,KAAK,SAAS,eAChE;MACK;AACN,aAAW,KAAK,YAAY,KAAK,SAAU,MAAM;AACjD,aAAW,KAAK,YAAY,KAAK,SAAU,QAAQ;AACnD,aAAW,KAAK,YAAY,KAAK,SAAU,UAAU;AACrD,aAAW,KAAK,aAAa,KAAK,MAAO,GAAG,GAAG,KAAK,MAAO,QAAQ;AACnE,aAAW,KAAK,YAAY,KAAK,WAAW;AAC5C,aAAW,KAAK,cAAc,KAAK,gBAAgB;AACnD,aAAW,KAAK,aAAa,KAAK,eAAe;AACjD,aAAW,KAAK,aAAa,KAAK,eAAe;AACjD,aAAW,KAAK,YAAY,KAAK,aAAa;;AAG/C,QAAO,YAAY,IAAI,IAAI,IAAI,MAAM,YAAY;EAChD,SAAS;EACT;EACA;EACA;EACA,CAAC;AACF,QAAO,EAAE,UAAU,GAAG;EACrB,CAAC,KAAK,OAAO,SAAS,aAAa,CAAC"}
1
+ {"version":3,"file":"status.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/status.ts"],"sourcesContent":["// CLI verb: `devstack status` — observational read of stack state.\n//\n// Architecture (distilled/20-cli.md § Subcommands § Lifecycle):\n// \"status — observational read of stack state, manifest, and (if\n// present) fork meta.\"\n//\n// Surface invariant: status MUST tolerate missing/malformed state\n// files. It describes what IS, not what should be. The architecture\n// explicitly calls this out — throwing on a stack that hasn't been\n// brought up would be a regression.\n\nimport { Effect } from 'effect';\n\nimport type { SubscribableState } from '../../../substrate/projection.ts';\nimport type { CliError } from '../errors.ts';\nimport { emitSuccess } from '../output.ts';\nimport type { CommandContext, CommandResult } from './index.ts';\n\n/** Pure projection: `SubscribableState` → JSON-safe status blob.\n * Never leaks substrate types. */\nexport const buildStatusPayload = (state: SubscribableState | null) => {\n\tif (state === null) {\n\t\treturn {\n\t\t\tpresent: false as const,\n\t\t\tidentity: null,\n\t\t\tcycle: null,\n\t\t\trowCount: 0,\n\t\t\tendpointCount: 0,\n\t\t\taccountCount: 0,\n\t\t\tpackageCount: 0,\n\t\t\terrorCount: 0,\n\t\t};\n\t}\n\treturn {\n\t\tpresent: true as const,\n\t\tidentity: { ...state.identity },\n\t\tcycle: { ...state.cycle },\n\t\trowCount: state.rows.length,\n\t\tendpointCount: state.endpoints.length,\n\t\taccountCount: state.accounts.length,\n\t\tpackageCount: state.packages.length,\n\t\terrorCount: state.errors.length,\n\t\trows: state.rows.map((r) => ({\n\t\t\tkey: r.key as string,\n\t\t\trole: r.role,\n\t\t\tstatus: r.status,\n\t\t\tphase: r.phase,\n\t\t\tlastErrorTag: r.lastError?.tag ?? null,\n\t\t})),\n\t\tendpoints: state.endpoints.map((e) => ({\n\t\t\tendpointKey: e.endpointKey as string,\n\t\t\tname: e.name,\n\t\t\turl: e.displayUrl ?? e.url,\n\t\t})),\n\t\taccounts: state.accounts.map((account) => ({\n\t\t\tkey: account.key,\n\t\t\trowKey: account.rowKey,\n\t\t\tname: account.name,\n\t\t\taddress: account.address,\n\t\t\tscheme: account.scheme,\n\t\t\tsource: account.source,\n\t\t\tfunding: { ...account.funding },\n\t\t\twalletVisible: account.walletVisible,\n\t\t})),\n\t\tpackages: state.packages.map((pkg) => ({\n\t\t\tkey: pkg.key,\n\t\t\trowKey: pkg.rowKey,\n\t\t\tname: pkg.name,\n\t\t\tkind: pkg.kind,\n\t\t\tpackageId: pkg.packageId,\n\t\t\tupgradeCapId: pkg.upgradeCapId,\n\t\t\tmvrPlaceholder: pkg.mvrPlaceholder,\n\t\t\tsourcePath: pkg.sourcePath,\n\t\t})),\n\t};\n};\n\n/**\n * Status reader interface. The dispatcher wires this to the\n * substrate's projection ref via a thin reader; tests pass a stub.\n * The CLI never calls engine methods directly — it reads the same\n * projection any peer surface (TUI, programmable API) sees.\n */\nexport interface StatusReader {\n\t/** Resolve the current projection. Identity is resolved from argv\n\t * upstream and baked into the reader at wiring time, so this takes no\n\t * `(app, stack)` params. Returns `null` if no state has been written\n\t * yet — a normal, tolerated state for a freshly-defined stack. */\n\treadonly readState: () => Effect.Effect<SubscribableState | null>;\n}\n\nexport interface StatusDeps {\n\treadonly reader: StatusReader;\n}\n\n/**\n * Run `devstack status`. Tolerant of missing state. Always succeeds\n * (returns ok=true) unless the projection read itself raises (which\n * the reader contractually swallows for missing files).\n */\nexport const runStatus = (\n\tdeps: StatusDeps,\n\tctx: CommandContext,\n): Effect.Effect<CommandResult, CliError> =>\n\tEffect.gen(function* () {\n\t\tconst started = Date.now();\n\t\t// `(app, stack)` are read ONLY for the human-readable status line\n\t\t// label below. The reader's identity is resolved from argv upstream\n\t\t// and baked in at wiring time, so it takes no params.\n\t\tconst app = ctx.flags.app ?? '';\n\t\tconst stack = ctx.flags.stack ?? '';\n\t\tconst state = yield* deps.reader.readState();\n\t\tconst data = buildStatusPayload(state);\n\t\tconst elapsedMs = Date.now() - started;\n\n\t\tconst humanLines: Array<string> = [];\n\t\tif (!data.present) {\n\t\t\thumanLines.push(\n\t\t\t\t`status: no state present for ${app || '(no app)'} / ${stack || '(no stack)'}`,\n\t\t\t);\n\t\t} else {\n\t\t\thumanLines.push(`app: ${data.identity!.app}`);\n\t\t\thumanLines.push(`stack: ${data.identity!.stack}`);\n\t\t\thumanLines.push(`network: ${data.identity!.network}`);\n\t\t\thumanLines.push(`cycle: #${data.cycle!.id} ${data.cycle!.phase}`);\n\t\t\thumanLines.push(`rows: ${data.rowCount}`);\n\t\t\thumanLines.push(`endpoints: ${data.endpointCount}`);\n\t\t\thumanLines.push(`accounts: ${data.accountCount}`);\n\t\t\thumanLines.push(`packages: ${data.packageCount}`);\n\t\t\thumanLines.push(`errors: ${data.errorCount}`);\n\t\t}\n\n\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\tcommand: 'status',\n\t\t\telapsedMs,\n\t\t\tdata,\n\t\t\thumanLines,\n\t\t});\n\t\treturn { exitCode: 0 } as CommandResult;\n\t}).pipe(Effect.withSpan('cli.status'));\n"],"mappings":";;;;;AAoBA,MAAa,sBAAsB,UAAoC;AACtE,KAAI,UAAU,KACb,QAAO;EACN,SAAS;EACT,UAAU;EACV,OAAO;EACP,UAAU;EACV,eAAe;EACf,cAAc;EACd,cAAc;EACd,YAAY;EACZ;AAEF,QAAO;EACN,SAAS;EACT,UAAU,EAAE,GAAG,MAAM,UAAU;EAC/B,OAAO,EAAE,GAAG,MAAM,OAAO;EACzB,UAAU,MAAM,KAAK;EACrB,eAAe,MAAM,UAAU;EAC/B,cAAc,MAAM,SAAS;EAC7B,cAAc,MAAM,SAAS;EAC7B,YAAY,MAAM,OAAO;EACzB,MAAM,MAAM,KAAK,KAAK,OAAO;GAC5B,KAAK,EAAE;GACP,MAAM,EAAE;GACR,QAAQ,EAAE;GACV,OAAO,EAAE;GACT,cAAc,EAAE,WAAW,OAAO;GAClC,EAAE;EACH,WAAW,MAAM,UAAU,KAAK,OAAO;GACtC,aAAa,EAAE;GACf,MAAM,EAAE;GACR,KAAK,EAAE,cAAc,EAAE;GACvB,EAAE;EACH,UAAU,MAAM,SAAS,KAAK,aAAa;GAC1C,KAAK,QAAQ;GACb,QAAQ,QAAQ;GAChB,MAAM,QAAQ;GACd,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,SAAS,EAAE,GAAG,QAAQ,SAAS;GAC/B,eAAe,QAAQ;GACvB,EAAE;EACH,UAAU,MAAM,SAAS,KAAK,SAAS;GACtC,KAAK,IAAI;GACT,QAAQ,IAAI;GACZ,MAAM,IAAI;GACV,MAAM,IAAI;GACV,WAAW,IAAI;GACf,cAAc,IAAI;GAClB,gBAAgB,IAAI;GACpB,YAAY,IAAI;GAChB,EAAE;EACH;;;;;;;AA0BF,MAAa,aACZ,MACA,QAEA,OAAO,IAAI,aAAa;CACvB,MAAM,UAAU,KAAK,KAAK;CAI1B,MAAM,MAAM,IAAI,MAAM,OAAO;CAC7B,MAAM,QAAQ,IAAI,MAAM,SAAS;CAEjC,MAAM,OAAO,mBAAmB,OADX,KAAK,OAAO,WAAW,CACN;CACtC,MAAM,YAAY,KAAK,KAAK,GAAG;CAE/B,MAAM,aAA4B,EAAE;AACpC,KAAI,CAAC,KAAK,QACT,YAAW,KACV,gCAAgC,OAAO,WAAW,KAAK,SAAS,eAChE;MACK;AACN,aAAW,KAAK,YAAY,KAAK,SAAU,MAAM;AACjD,aAAW,KAAK,YAAY,KAAK,SAAU,QAAQ;AACnD,aAAW,KAAK,YAAY,KAAK,SAAU,UAAU;AACrD,aAAW,KAAK,aAAa,KAAK,MAAO,GAAG,GAAG,KAAK,MAAO,QAAQ;AACnE,aAAW,KAAK,YAAY,KAAK,WAAW;AAC5C,aAAW,KAAK,cAAc,KAAK,gBAAgB;AACnD,aAAW,KAAK,aAAa,KAAK,eAAe;AACjD,aAAW,KAAK,aAAa,KAAK,eAAe;AACjD,aAAW,KAAK,YAAY,KAAK,aAAa;;AAG/C,QAAO,YAAY,IAAI,IAAI,IAAI,MAAM,YAAY;EAChD,SAAS;EACT;EACA;EACA;EACA,CAAC;AACF,QAAO,EAAE,UAAU,GAAG;EACrB,CAAC,KAAK,OAAO,SAAS,aAAa,CAAC"}
@@ -1,5 +1,6 @@
1
- import { checkHolderLiveness } from "../../../substrate/runtime/cross-process/liveness.mjs";
1
+ import { LivenessProbeScope, layerLivenessProbeScope } from "../../../substrate/runtime/cross-process/liveness.mjs";
2
2
  import { readRoster } from "../../../substrate/runtime/cross-process/roster.mjs";
3
+ import "../../../substrate/runtime/cross-process/index.mjs";
3
4
  import { Effect } from "effect";
4
5
  //#region src/surfaces/cli/commands/supervisor-presence.ts
5
6
  /**
@@ -16,7 +17,8 @@ const probeSupervisorPresence = (rosterFile) => Effect.gen(function* () {
16
17
  version: 1,
17
18
  holders: []
18
19
  })));
19
- for (const holder of doc.holders) if ((yield* checkHolderLiveness(holder).pipe(Effect.catch(() => Effect.succeed("alive")))) === "alive") return {
20
+ const probe = yield* LivenessProbeScope;
21
+ for (const holder of doc.holders) if ((yield* probe.probeHolderLiveness(holder).pipe(Effect.catch(() => Effect.succeed("alive")))) === "alive") return {
20
22
  live: true,
21
23
  pid: holder.pid,
22
24
  hostname: holder.hostname
@@ -26,7 +28,7 @@ const probeSupervisorPresence = (rosterFile) => Effect.gen(function* () {
26
28
  pid: null,
27
29
  hostname: null
28
30
  };
29
- });
31
+ }).pipe(Effect.provide(layerLivenessProbeScope));
30
32
  //#endregion
31
33
  export { probeSupervisorPresence };
32
34
 
@@ -1 +1 @@
1
- {"version":3,"file":"supervisor-presence.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/supervisor-presence.ts"],"sourcesContent":["// Detect whether a supervisor is currently live for a given stack.\n//\n// The CLI uses this to decide whether a verb should publish via the\n// cross-process command channel (live supervisor: publish + await ack)\n// or use its direct/offline fallback.\n//\n// Detection reads the existing `roster.json` (architecture § Cross-\n// process safety protocol § Roster) and walks holders. A same-host\n// holder with a live PID + matching start-time → supervisor live.\n// Foreign-host holders are conservatively treated as live (NFS-safe\n// default; matches `liveness.ts` discipline).\n\nimport { Effect } from 'effect';\n\nimport {\n\tcheckHolderLiveness,\n\treadRoster,\n\ttype RosterError,\n} from '../../../substrate/runtime/cross-process/index.ts';\n\nexport interface SupervisorPresence {\n\treadonly live: boolean;\n\treadonly pid: number | null;\n\treadonly hostname: string | null;\n}\n\n/**\n * Read `rosterFile` and report whether at least one live holder\n * remains. Returns `{ live: false, pid: null }` when the roster is\n * missing OR all holders are dead-by-liveness-check.\n *\n * Tolerates corrupt rosters: a malformed file is treated as \"no\n * live supervisor\" — the worst case is the CLI refusing a publish\n * the user could have buffered.\n */\nexport const probeSupervisorPresence = (\n\trosterFile: string,\n): Effect.Effect<SupervisorPresence, RosterError> =>\n\tEffect.gen(function* () {\n\t\tconst doc = yield* readRoster(rosterFile).pipe(\n\t\t\tEffect.catchTag('RosterCorruptError', () =>\n\t\t\t\tEffect.succeed({ version: 1 as const, holders: [] }),\n\t\t\t),\n\t\t);\n\t\tfor (const holder of doc.holders) {\n\t\t\tconst liveness = yield* checkHolderLiveness(holder).pipe(\n\t\t\t\tEffect.catch(() => Effect.succeed('alive' as const)),\n\t\t\t);\n\t\t\tif (liveness === 'alive') {\n\t\t\t\treturn { live: true, pid: holder.pid, hostname: holder.hostname };\n\t\t\t}\n\t\t}\n\t\treturn { live: false, pid: null, hostname: null };\n\t});\n"],"mappings":";;;;;;;;;;;;;AAmCA,MAAa,2BACZ,eAEA,OAAO,IAAI,aAAa;CACvB,MAAM,MAAM,OAAO,WAAW,WAAW,CAAC,KACzC,OAAO,SAAS,4BACf,OAAO,QAAQ;EAAE,SAAS;EAAY,SAAS,EAAE;EAAE,CAAC,CACpD,CACD;AACD,MAAK,MAAM,UAAU,IAAI,QAIxB,MAAI,OAHoB,oBAAoB,OAAO,CAAC,KACnD,OAAO,YAAY,OAAO,QAAQ,QAAiB,CAAC,CACpD,MACgB,QAChB,QAAO;EAAE,MAAM;EAAM,KAAK,OAAO;EAAK,UAAU,OAAO;EAAU;AAGnE,QAAO;EAAE,MAAM;EAAO,KAAK;EAAM,UAAU;EAAM;EAChD"}
1
+ {"version":3,"file":"supervisor-presence.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/supervisor-presence.ts"],"sourcesContent":["// Detect whether a supervisor is currently live for a given stack.\n//\n// The CLI uses this to decide whether a verb should publish via the\n// cross-process command channel (live supervisor: publish + await ack)\n// or use its direct/offline fallback.\n//\n// Detection reads the existing `roster.json` (architecture § Cross-\n// process safety protocol § Roster) and walks holders. A same-host\n// holder with a live PID + matching start-time → supervisor live.\n// Foreign-host holders are conservatively treated as live (NFS-safe\n// default; matches `liveness.ts` discipline).\n\nimport { Effect } from 'effect';\n\nimport {\n\tlayerLivenessProbeScope,\n\tLivenessProbeScope,\n\treadRoster,\n\ttype RosterError,\n} from '../../../substrate/runtime/cross-process/index.ts';\n\nexport interface SupervisorPresence {\n\treadonly live: boolean;\n\treadonly pid: number | null;\n\treadonly hostname: string | null;\n}\n\n/**\n * Read `rosterFile` and report whether at least one live holder\n * remains. Returns `{ live: false, pid: null }` when the roster is\n * missing OR all holders are dead-by-liveness-check.\n *\n * Tolerates corrupt rosters: a malformed file is treated as \"no\n * live supervisor\" — the worst case is the CLI refusing a publish\n * the user could have buffered.\n */\nexport const probeSupervisorPresence = (\n\trosterFile: string,\n): Effect.Effect<SupervisorPresence, RosterError> =>\n\t// Yield a fresh `LivenessProbeScope` so a corrupted-roster edge case\n\t// with multiple holders sharing one pid forks `ps`/`tasklist` once.\n\tEffect.gen(function* () {\n\t\tconst doc = yield* readRoster(rosterFile).pipe(\n\t\t\tEffect.catchTag('RosterCorruptError', () =>\n\t\t\t\tEffect.succeed({ version: 1 as const, holders: [] }),\n\t\t\t),\n\t\t);\n\t\tconst probe = yield* LivenessProbeScope;\n\t\tfor (const holder of doc.holders) {\n\t\t\tconst liveness = yield* probe\n\t\t\t\t.probeHolderLiveness(holder)\n\t\t\t\t.pipe(Effect.catch(() => Effect.succeed('alive' as const)));\n\t\t\tif (liveness === 'alive') {\n\t\t\t\treturn { live: true, pid: holder.pid, hostname: holder.hostname };\n\t\t\t}\n\t\t}\n\t\treturn { live: false, pid: null, hostname: null };\n\t}).pipe(Effect.provide(layerLivenessProbeScope));\n"],"mappings":";;;;;;;;;;;;;;AAoCA,MAAa,2BACZ,eAIA,OAAO,IAAI,aAAa;CACvB,MAAM,MAAM,OAAO,WAAW,WAAW,CAAC,KACzC,OAAO,SAAS,4BACf,OAAO,QAAQ;EAAE,SAAS;EAAY,SAAS,EAAE;EAAE,CAAC,CACpD,CACD;CACD,MAAM,QAAQ,OAAO;AACrB,MAAK,MAAM,UAAU,IAAI,QAIxB,MAAI,OAHoB,MACtB,oBAAoB,OAAO,CAC3B,KAAK,OAAO,YAAY,OAAO,QAAQ,QAAiB,CAAC,CAAC,MAC3C,QAChB,QAAO;EAAE,MAAM;EAAM,KAAK,OAAO;EAAK,UAAU,OAAO;EAAU;AAGnE,QAAO;EAAE,MAAM;EAAO,KAAK;EAAM,UAAU;EAAM;EAChD,CAAC,KAAK,OAAO,QAAQ,wBAAwB,CAAC"}