@mysten-incubation/devstack 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (838) hide show
  1. package/README.md +8 -4
  2. package/dashboard-ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  3. package/dashboard-ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  4. package/dashboard-ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  5. package/dashboard-ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  6. package/dashboard-ui/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
  7. package/dashboard-ui/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
  8. package/dashboard-ui/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
  9. package/dashboard-ui/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
  10. package/dashboard-ui/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
  11. package/dashboard-ui/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
  12. package/dashboard-ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  13. package/dashboard-ui/assets/index-Bmi1UtAg.js +1356 -0
  14. package/dashboard-ui/assets/index-D5EShVt4.js +3 -0
  15. package/dashboard-ui/assets/index-Deml9drg.css +1 -0
  16. package/dashboard-ui/index.html +13 -0
  17. package/dist/api/define-capabilities.d.mts +14 -3
  18. package/dist/api/define-capabilities.mjs +18 -1
  19. package/dist/api/define-capabilities.mjs.map +1 -1
  20. package/dist/api/define-devstack.d.mts +1 -1
  21. package/dist/api/define-devstack.mjs +6 -26
  22. package/dist/api/define-devstack.mjs.map +1 -1
  23. package/dist/api/define-plugin.d.mts +1 -0
  24. package/dist/api/define-plugin.mjs +2 -0
  25. package/dist/api/inference-network.d.mts +132 -0
  26. package/dist/api/inference-network.mjs +105 -16
  27. package/dist/api/inference-network.mjs.map +1 -1
  28. package/dist/api/plugin-errors.d.mts +7 -0
  29. package/dist/api/run-stack.d.mts +20 -9
  30. package/dist/api/run-stack.mjs +56 -29
  31. package/dist/api/run-stack.mjs.map +1 -1
  32. package/dist/build-integrations/playwright/config.d.mts +6 -4
  33. package/dist/build-integrations/playwright/config.mjs +5 -3
  34. package/dist/build-integrations/playwright/config.mjs.map +1 -1
  35. package/dist/build-integrations/playwright/errors.d.mts +1 -1
  36. package/dist/build-integrations/playwright/global-setup.d.mts +43 -17
  37. package/dist/build-integrations/playwright/global-setup.mjs +250 -13
  38. package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
  39. package/dist/build-integrations/playwright/index.d.mts +5 -3
  40. package/dist/build-integrations/playwright/index.mjs +4 -2
  41. package/dist/build-integrations/playwright/stack-context.d.mts +0 -1
  42. package/dist/build-integrations/playwright/stack-context.mjs +24 -63
  43. package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
  44. package/dist/build-integrations/playwright/wallet-context.d.mts +7 -10
  45. package/dist/build-integrations/playwright/wallet-context.mjs +8 -13
  46. package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
  47. package/dist/build-integrations/runtime/cold-start-url.d.mts +5 -0
  48. package/dist/build-integrations/runtime/cold-start-url.mjs +4 -3
  49. package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
  50. package/dist/build-integrations/runtime/conventional-routes.d.mts +51 -0
  51. package/dist/build-integrations/runtime/conventional-routes.mjs +92 -0
  52. package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -0
  53. package/dist/build-integrations/runtime/discover.d.mts +38 -30
  54. package/dist/build-integrations/runtime/discover.mjs +73 -70
  55. package/dist/build-integrations/runtime/discover.mjs.map +1 -1
  56. package/dist/build-integrations/runtime/endpoint-registry.d.mts +3 -1
  57. package/dist/build-integrations/runtime/endpoint-registry.mjs +3 -1
  58. package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
  59. package/dist/build-integrations/runtime/index.d.mts +7 -2
  60. package/dist/build-integrations/runtime/index.mjs +9 -2
  61. package/dist/build-integrations/runtime/manifest-types.d.mts +2 -0
  62. package/dist/build-integrations/runtime/manifest-types.mjs +3 -0
  63. package/dist/build-integrations/runtime/playwright-stack-context-slot.d.mts +29 -0
  64. package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs +10 -0
  65. package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs.map +1 -0
  66. package/dist/build-integrations/runtime/read-stack-context.d.mts +1 -1
  67. package/dist/build-integrations/runtime/read-stack-context.mjs +10 -4
  68. package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
  69. package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +48 -0
  70. package/dist/build-integrations/runtime/resolve-discovery-env.mjs +42 -0
  71. package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -0
  72. package/dist/build-integrations/runtime/stack-context.d.mts +1 -1
  73. package/dist/build-integrations/runtime/wallet-paths.mjs +2 -0
  74. package/dist/build-integrations/vite/index.d.mts +51 -0
  75. package/dist/build-integrations/vite/index.mjs +73 -0
  76. package/dist/build-integrations/vite/index.mjs.map +1 -0
  77. package/dist/build-integrations/vitest/env.d.mts +4 -3
  78. package/dist/build-integrations/vitest/env.mjs +6 -4
  79. package/dist/build-integrations/vitest/env.mjs.map +1 -1
  80. package/dist/build-integrations/vitest/errors.d.mts +25 -4
  81. package/dist/build-integrations/vitest/errors.mjs +15 -2
  82. package/dist/build-integrations/vitest/errors.mjs.map +1 -1
  83. package/dist/build-integrations/vitest/index.d.mts +2 -2
  84. package/dist/build-integrations/vitest/index.mjs +3 -3
  85. package/dist/build-integrations/vitest/setup.d.mts +4 -2
  86. package/dist/build-integrations/vitest/setup.mjs +2 -2
  87. package/dist/build-integrations/vitest/setup.mjs.map +1 -1
  88. package/dist/build-integrations/vitest/stack-context.d.mts +0 -1
  89. package/dist/build-integrations/vitest/stack-context.mjs +33 -13
  90. package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
  91. package/dist/cli/bail.mjs +23 -0
  92. package/dist/cli/bail.mjs.map +1 -0
  93. package/dist/{surfaces/cli/commands → cli}/doctor-probes.mjs +26 -16
  94. package/dist/cli/doctor-probes.mjs.map +1 -0
  95. package/dist/cli/main.d.mts +15 -1
  96. package/dist/cli/main.mjs +166 -482
  97. package/dist/cli/main.mjs.map +1 -1
  98. package/dist/cli/prune-direct.mjs +31 -201
  99. package/dist/cli/prune-direct.mjs.map +1 -1
  100. package/dist/cli/snapshot-reader.mjs +14 -11
  101. package/dist/cli/snapshot-reader.mjs.map +1 -1
  102. package/dist/cli/up-lifecycle.mjs +4 -1
  103. package/dist/cli/up-lifecycle.mjs.map +1 -1
  104. package/dist/cli/wirings/apply.mjs +95 -0
  105. package/dist/cli/wirings/apply.mjs.map +1 -0
  106. package/dist/cli/wirings/build-verb-layers.mjs +42 -0
  107. package/dist/cli/wirings/build-verb-layers.mjs.map +1 -0
  108. package/dist/cli/wirings/config-loader.mjs +69 -0
  109. package/dist/cli/wirings/config-loader.mjs.map +1 -0
  110. package/dist/cli/wirings/engine-command.mjs +30 -0
  111. package/dist/cli/wirings/engine-command.mjs.map +1 -0
  112. package/dist/cli/wirings/identity.mjs +72 -0
  113. package/dist/cli/wirings/identity.mjs.map +1 -0
  114. package/dist/cli/wirings/provide-file-system.mjs +7 -0
  115. package/dist/cli/wirings/provide-file-system.mjs.map +1 -0
  116. package/dist/cli/wirings/snapshot.mjs +177 -0
  117. package/dist/cli/wirings/snapshot.mjs.map +1 -0
  118. package/dist/cli/wirings/up.mjs +287 -0
  119. package/dist/cli/wirings/up.mjs.map +1 -0
  120. package/dist/cli/wirings/wipe.mjs +40 -0
  121. package/dist/cli/wirings/wipe.mjs.map +1 -0
  122. package/dist/contracts/capability-decl.d.mts +0 -2
  123. package/dist/contracts/codegenable.d.mts +43 -0
  124. package/dist/contracts/container-runtime.d.mts +26 -1
  125. package/dist/contracts/faucet-strategy.d.mts +25 -0
  126. package/dist/contracts/funding-strategy.d.mts +42 -0
  127. package/dist/contracts/plugin-expander.mjs +60 -0
  128. package/dist/contracts/plugin-expander.mjs.map +1 -0
  129. package/dist/contracts/projection.d.mts +5 -3
  130. package/dist/contracts/renderer.d.mts +23 -0
  131. package/dist/contracts/routable.d.mts +14 -1
  132. package/dist/contracts/snapshotable.d.mts +25 -3
  133. package/dist/contracts/wallet-protocol.d.mts +52 -0
  134. package/dist/contracts/wallet-protocol.mjs +52 -0
  135. package/dist/contracts/wallet-protocol.mjs.map +1 -0
  136. package/dist/index.d.mts +26 -25
  137. package/dist/index.mjs +20 -13
  138. package/dist/{runtime → orchestrators}/built-in-plugin-layers.mjs +10 -5
  139. package/dist/orchestrators/built-in-plugin-layers.mjs.map +1 -0
  140. package/dist/orchestrators/codegen/bindings.d.mts +1 -2
  141. package/dist/orchestrators/codegen/bindings.mjs +52 -186
  142. package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
  143. package/dist/orchestrators/codegen/emit.mjs +19 -1
  144. package/dist/orchestrators/codegen/emit.mjs.map +1 -1
  145. package/dist/orchestrators/codegen/errors.mjs +8 -3
  146. package/dist/orchestrators/codegen/errors.mjs.map +1 -1
  147. package/dist/orchestrators/codegen/format.mjs +47 -39
  148. package/dist/orchestrators/codegen/format.mjs.map +1 -1
  149. package/dist/orchestrators/codegen/gitignore.mjs +3 -1
  150. package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
  151. package/dist/orchestrators/codegen/output-location.mjs +41 -0
  152. package/dist/orchestrators/codegen/output-location.mjs.map +1 -0
  153. package/dist/orchestrators/codegen/paths.d.mts +1 -1
  154. package/dist/orchestrators/codegen/paths.mjs +40 -14
  155. package/dist/orchestrators/codegen/paths.mjs.map +1 -1
  156. package/dist/orchestrators/codegen/permissions.mjs +3 -1
  157. package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
  158. package/dist/orchestrators/codegen/service.mjs +157 -105
  159. package/dist/orchestrators/codegen/service.mjs.map +1 -1
  160. package/dist/orchestrators/lifecycle-prune/errors.mjs +31 -0
  161. package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -0
  162. package/dist/orchestrators/lifecycle-prune/index.mjs +350 -0
  163. package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -0
  164. package/dist/orchestrators/router/cleanup.mjs +26 -27
  165. package/dist/orchestrators/router/cleanup.mjs.map +1 -1
  166. package/dist/orchestrators/router/entrypoints.mjs +2 -2
  167. package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
  168. package/dist/orchestrators/router/file-provider.mjs +14 -5
  169. package/dist/orchestrators/router/file-provider.mjs.map +1 -1
  170. package/dist/orchestrators/router/hostname.mjs +19 -55
  171. package/dist/orchestrators/router/hostname.mjs.map +1 -1
  172. package/dist/orchestrators/router/index.mjs +11 -0
  173. package/dist/orchestrators/router/profile.mjs +2 -2
  174. package/dist/orchestrators/router/sentinels.mjs +26 -0
  175. package/dist/orchestrators/router/sentinels.mjs.map +1 -0
  176. package/dist/orchestrators/router/service.mjs +40 -37
  177. package/dist/orchestrators/router/service.mjs.map +1 -1
  178. package/dist/orchestrators/router/traefik-container.mjs +27 -16
  179. package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
  180. package/dist/{substrate/runtime → orchestrators}/run.mjs +26 -21
  181. package/dist/orchestrators/run.mjs.map +1 -0
  182. package/dist/orchestrators/runtime-composition.d.mts +1 -1
  183. package/dist/orchestrators/runtime-composition.mjs +123 -39
  184. package/dist/orchestrators/runtime-composition.mjs.map +1 -1
  185. package/dist/orchestrators/snapshot/capture-command.d.mts +1 -0
  186. package/dist/orchestrators/snapshot/capture-command.mjs +25 -0
  187. package/dist/orchestrators/snapshot/capture-command.mjs.map +1 -0
  188. package/dist/orchestrators/snapshot/capture.mjs +42 -28
  189. package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
  190. package/dist/orchestrators/snapshot/descriptor.mjs +35 -10
  191. package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
  192. package/dist/orchestrators/snapshot/identity-guard.mjs +1 -1
  193. package/dist/orchestrators/snapshot/index.mjs +13 -0
  194. package/dist/orchestrators/snapshot/integrity.d.mts +1 -1
  195. package/dist/orchestrators/snapshot/integrity.mjs +73 -17
  196. package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
  197. package/dist/orchestrators/snapshot/pending-marker.d.mts +1 -0
  198. package/dist/orchestrators/snapshot/pending-marker.mjs +99 -0
  199. package/dist/orchestrators/snapshot/pending-marker.mjs.map +1 -0
  200. package/dist/orchestrators/snapshot/phase-error.mjs +23 -0
  201. package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -0
  202. package/dist/orchestrators/snapshot/prune.mjs +20 -55
  203. package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
  204. package/dist/orchestrators/snapshot/recover-pending.d.mts +1 -0
  205. package/dist/orchestrators/snapshot/recover-pending.mjs +223 -0
  206. package/dist/orchestrators/snapshot/recover-pending.mjs.map +1 -0
  207. package/dist/orchestrators/snapshot/restore.mjs +118 -84
  208. package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
  209. package/dist/orchestrators/snapshot/service.mjs +53 -56
  210. package/dist/orchestrators/snapshot/service.mjs.map +1 -1
  211. package/dist/orchestrators/snapshot/state-document.d.mts +1 -0
  212. package/dist/orchestrators/snapshot/state-document.mjs +47 -7
  213. package/dist/orchestrators/snapshot/state-document.mjs.map +1 -1
  214. package/dist/orchestrators/snapshot/wipe.mjs +67 -10
  215. package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
  216. package/dist/plugins/account/codegen.mjs +9 -8
  217. package/dist/plugins/account/codegen.mjs.map +1 -1
  218. package/dist/plugins/account/errors.d.mts +33 -6
  219. package/dist/plugins/account/errors.mjs.map +1 -1
  220. package/dist/plugins/account/funding.d.mts +29 -19
  221. package/dist/plugins/account/funding.mjs +84 -66
  222. package/dist/plugins/account/funding.mjs.map +1 -1
  223. package/dist/plugins/account/index.d.mts +2 -4
  224. package/dist/plugins/account/index.mjs +64 -31
  225. package/dist/plugins/account/index.mjs.map +1 -1
  226. package/dist/plugins/account/keypair.mjs +6 -9
  227. package/dist/plugins/account/keypair.mjs.map +1 -1
  228. package/dist/plugins/account/lease.mjs +1 -0
  229. package/dist/plugins/account/lease.mjs.map +1 -1
  230. package/dist/plugins/account/registry.d.mts +14 -1
  231. package/dist/plugins/account/registry.mjs +18 -18
  232. package/dist/plugins/account/registry.mjs.map +1 -1
  233. package/dist/plugins/account/service.d.mts +23 -4
  234. package/dist/plugins/account/service.mjs +83 -45
  235. package/dist/plugins/account/service.mjs.map +1 -1
  236. package/dist/plugins/account/snapshot.mjs +2 -5
  237. package/dist/plugins/account/snapshot.mjs.map +1 -1
  238. package/dist/plugins/account/spans.mjs +16 -0
  239. package/dist/plugins/account/spans.mjs.map +1 -0
  240. package/dist/plugins/account/variants/keystore.mjs +6 -8
  241. package/dist/plugins/account/variants/keystore.mjs.map +1 -1
  242. package/dist/plugins/action/build-context.d.mts +3 -2
  243. package/dist/plugins/action/errors.d.mts +16 -12
  244. package/dist/plugins/action/errors.mjs.map +1 -1
  245. package/dist/plugins/action/execute.mjs +65 -103
  246. package/dist/plugins/action/execute.mjs.map +1 -1
  247. package/dist/plugins/action/index.d.mts +0 -2
  248. package/dist/plugins/action/index.mjs +17 -13
  249. package/dist/plugins/action/index.mjs.map +1 -1
  250. package/dist/plugins/action/service.mjs +38 -35
  251. package/dist/plugins/action/service.mjs.map +1 -1
  252. package/dist/plugins/action/spans.mjs +11 -0
  253. package/dist/plugins/action/spans.mjs.map +1 -0
  254. package/dist/plugins/coin/codegen.mjs +9 -8
  255. package/dist/plugins/coin/codegen.mjs.map +1 -1
  256. package/dist/plugins/coin/discovery.mjs +8 -4
  257. package/dist/plugins/coin/discovery.mjs.map +1 -1
  258. package/dist/plugins/coin/index.d.mts +11 -29
  259. package/dist/plugins/coin/index.mjs +56 -27
  260. package/dist/plugins/coin/index.mjs.map +1 -1
  261. package/dist/plugins/coin/metadata.mjs +8 -15
  262. package/dist/plugins/coin/metadata.mjs.map +1 -1
  263. package/dist/plugins/coin/mint.d.mts +30 -11
  264. package/dist/plugins/coin/mint.mjs +78 -74
  265. package/dist/plugins/coin/mint.mjs.map +1 -1
  266. package/dist/plugins/coin/registry.mjs +4 -7
  267. package/dist/plugins/coin/registry.mjs.map +1 -1
  268. package/dist/plugins/coin/service.d.mts +39 -1
  269. package/dist/plugins/coin/service.mjs +11 -4
  270. package/dist/plugins/coin/service.mjs.map +1 -1
  271. package/dist/plugins/coin/spans.mjs +20 -0
  272. package/dist/plugins/coin/spans.mjs.map +1 -0
  273. package/dist/plugins/dashboard/domain.mjs +402 -0
  274. package/dist/plugins/dashboard/domain.mjs.map +1 -0
  275. package/dist/plugins/dashboard/index.d.mts +29 -0
  276. package/dist/plugins/dashboard/index.mjs +91 -0
  277. package/dist/plugins/dashboard/index.mjs.map +1 -0
  278. package/dist/plugins/dashboard/origin-policy.mjs +36 -0
  279. package/dist/plugins/dashboard/origin-policy.mjs.map +1 -0
  280. package/dist/plugins/dashboard/routable.mjs +27 -0
  281. package/dist/plugins/dashboard/routable.mjs.map +1 -0
  282. package/dist/plugins/dashboard/schema/builder.mjs +21 -0
  283. package/dist/plugins/dashboard/schema/builder.mjs.map +1 -0
  284. package/dist/plugins/dashboard/schema/enums.mjs +72 -0
  285. package/dist/plugins/dashboard/schema/enums.mjs.map +1 -0
  286. package/dist/plugins/dashboard/schema/root.mjs +351 -0
  287. package/dist/plugins/dashboard/schema/root.mjs.map +1 -0
  288. package/dist/plugins/dashboard/schema/types.mjs +508 -0
  289. package/dist/plugins/dashboard/schema/types.mjs.map +1 -0
  290. package/dist/plugins/dashboard/schema.mjs +11 -0
  291. package/dist/plugins/dashboard/schema.mjs.map +1 -0
  292. package/dist/plugins/dashboard/server.mjs +166 -0
  293. package/dist/plugins/dashboard/server.mjs.map +1 -0
  294. package/dist/plugins/deepbook/deploy.mjs +53 -115
  295. package/dist/plugins/deepbook/deploy.mjs.map +1 -1
  296. package/dist/plugins/deepbook/errors.mjs +1 -0
  297. package/dist/plugins/deepbook/errors.mjs.map +1 -1
  298. package/dist/plugins/deepbook/faucet-strategy.mjs +47 -34
  299. package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
  300. package/dist/plugins/deepbook/hash.mjs +9 -0
  301. package/dist/plugins/deepbook/hash.mjs.map +1 -0
  302. package/dist/plugins/deepbook/index.d.mts +1 -10
  303. package/dist/plugins/deepbook/index.mjs +25 -22
  304. package/dist/plugins/deepbook/index.mjs.map +1 -1
  305. package/dist/plugins/deepbook/pyth/index.mjs +26 -55
  306. package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
  307. package/dist/plugins/deepbook/snapshot.mjs +17 -29
  308. package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
  309. package/dist/plugins/deepbook/spans.mjs +18 -0
  310. package/dist/plugins/deepbook/spans.mjs.map +1 -0
  311. package/dist/plugins/deepbook/type-strings.mjs +65 -0
  312. package/dist/plugins/deepbook/type-strings.mjs.map +1 -0
  313. package/dist/plugins/deepbook/types.d.mts +2 -3
  314. package/dist/plugins/deepbook/types.mjs.map +1 -1
  315. package/dist/plugins/faucet/dispatcher.d.mts +21 -2
  316. package/dist/plugins/faucet/dispatcher.mjs +1 -2
  317. package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
  318. package/dist/plugins/faucet/errors.d.mts +11 -24
  319. package/dist/plugins/faucet/errors.mjs.map +1 -1
  320. package/dist/plugins/faucet/http.mjs +33 -14
  321. package/dist/plugins/faucet/http.mjs.map +1 -1
  322. package/dist/plugins/faucet/index.d.mts +15 -5
  323. package/dist/plugins/faucet/index.mjs +3 -0
  324. package/dist/plugins/faucet/index.mjs.map +1 -1
  325. package/dist/plugins/faucet/spans.mjs +12 -0
  326. package/dist/plugins/faucet/spans.mjs.map +1 -0
  327. package/dist/plugins/host-service/errors.d.mts +1 -1
  328. package/dist/plugins/host-service/index.mjs +34 -8
  329. package/dist/plugins/host-service/index.mjs.map +1 -1
  330. package/dist/plugins/host-service/routable.mjs +2 -1
  331. package/dist/plugins/host-service/routable.mjs.map +1 -1
  332. package/dist/plugins/host-service/service.d.mts +21 -0
  333. package/dist/plugins/host-service/service.mjs +136 -81
  334. package/dist/plugins/host-service/service.mjs.map +1 -1
  335. package/dist/plugins/internal/acquire-on-chain-artifact.mjs +23 -0
  336. package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -0
  337. package/dist/plugins/internal/codegen-helpers.mjs +18 -0
  338. package/dist/plugins/internal/codegen-helpers.mjs.map +1 -0
  339. package/dist/plugins/internal/funding-failure-error.mjs +32 -0
  340. package/dist/plugins/internal/funding-failure-error.mjs.map +1 -0
  341. package/dist/plugins/package/build.mjs +1 -1
  342. package/dist/plugins/package/build.mjs.map +1 -1
  343. package/dist/plugins/package/codegen.mjs +26 -0
  344. package/dist/plugins/package/codegen.mjs.map +1 -1
  345. package/dist/plugins/package/errors.d.mts +16 -6
  346. package/dist/plugins/package/errors.mjs.map +1 -1
  347. package/dist/plugins/package/index.d.mts +0 -4
  348. package/dist/plugins/package/index.mjs +27 -21
  349. package/dist/plugins/package/index.mjs.map +1 -1
  350. package/dist/plugins/package/mode-known.mjs +26 -12
  351. package/dist/plugins/package/mode-known.mjs.map +1 -1
  352. package/dist/plugins/package/mode-local.mjs +47 -41
  353. package/dist/plugins/package/mode-local.mjs.map +1 -1
  354. package/dist/plugins/package/publish-executor.mjs +55 -114
  355. package/dist/plugins/package/publish-executor.mjs.map +1 -1
  356. package/dist/plugins/package/registry.mjs +2 -1
  357. package/dist/plugins/package/registry.mjs.map +1 -1
  358. package/dist/plugins/package/service.mjs.map +1 -1
  359. package/dist/plugins/package/spans.mjs +14 -0
  360. package/dist/plugins/package/spans.mjs.map +1 -0
  361. package/dist/plugins/postgres/db-ensure.mjs +19 -12
  362. package/dist/plugins/postgres/db-ensure.mjs.map +1 -1
  363. package/dist/plugins/postgres/errors.d.mts +5 -0
  364. package/dist/plugins/postgres/errors.mjs.map +1 -1
  365. package/dist/plugins/postgres/index.mjs +10 -17
  366. package/dist/plugins/postgres/index.mjs.map +1 -1
  367. package/dist/plugins/postgres/service.mjs +58 -24
  368. package/dist/plugins/postgres/service.mjs.map +1 -1
  369. package/dist/plugins/postgres/snapshot.mjs.map +1 -1
  370. package/dist/plugins/postgres/spans.mjs +11 -0
  371. package/dist/plugins/postgres/spans.mjs.map +1 -0
  372. package/dist/plugins/router-entrypoints.mjs +4 -4
  373. package/dist/plugins/router-entrypoints.mjs.map +1 -1
  374. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +15 -13
  375. package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
  376. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +15 -19
  377. package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
  378. package/dist/plugins/seal/codegen.d.mts +2 -3
  379. package/dist/plugins/seal/codegen.mjs.map +1 -1
  380. package/dist/plugins/seal/config-render.mjs +35 -8
  381. package/dist/plugins/seal/config-render.mjs.map +1 -1
  382. package/dist/plugins/seal/deploy.mjs +31 -39
  383. package/dist/plugins/seal/deploy.mjs.map +1 -1
  384. package/dist/plugins/seal/errors.d.mts +1 -3
  385. package/dist/plugins/seal/errors.mjs +7 -1
  386. package/dist/plugins/seal/errors.mjs.map +1 -1
  387. package/dist/plugins/seal/index.d.mts +1 -6
  388. package/dist/plugins/seal/index.mjs +40 -14
  389. package/dist/plugins/seal/index.mjs.map +1 -1
  390. package/dist/plugins/seal/key-manager.d.mts +1 -9
  391. package/dist/plugins/seal/key-manager.mjs +2 -18
  392. package/dist/plugins/seal/key-manager.mjs.map +1 -1
  393. package/dist/plugins/seal/key-server.mjs +37 -36
  394. package/dist/plugins/seal/key-server.mjs.map +1 -1
  395. package/dist/plugins/seal/keygen.mjs +16 -17
  396. package/dist/plugins/seal/keygen.mjs.map +1 -1
  397. package/dist/plugins/seal/mode/fork-known.mjs +36 -14
  398. package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
  399. package/dist/plugins/seal/mode/live.d.mts +6 -4
  400. package/dist/plugins/seal/mode/live.mjs +14 -21
  401. package/dist/plugins/seal/mode/live.mjs.map +1 -1
  402. package/dist/plugins/seal/mode/local-keygen.mjs +57 -21
  403. package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
  404. package/dist/plugins/seal/registry-publish.mjs +1 -0
  405. package/dist/plugins/seal/registry-publish.mjs.map +1 -1
  406. package/dist/plugins/seal/routable.mjs.map +1 -1
  407. package/dist/plugins/seal/service.mjs.map +1 -1
  408. package/dist/plugins/seal/spans.mjs +18 -0
  409. package/dist/plugins/seal/spans.mjs.map +1 -0
  410. package/dist/plugins/sui/auto-tick.mjs +17 -23
  411. package/dist/plugins/sui/auto-tick.mjs.map +1 -1
  412. package/dist/plugins/sui/chain-build-container.mjs +18 -0
  413. package/dist/plugins/sui/chain-build-container.mjs.map +1 -0
  414. package/dist/plugins/sui/chain-probe.d.mts +5 -5
  415. package/dist/plugins/sui/chain-probe.mjs +28 -58
  416. package/dist/plugins/sui/chain-probe.mjs.map +1 -1
  417. package/dist/plugins/sui/codegen.mjs +26 -0
  418. package/dist/plugins/sui/codegen.mjs.map +1 -1
  419. package/dist/plugins/sui/errors.d.mts +2 -29
  420. package/dist/plugins/sui/errors.mjs +1 -3
  421. package/dist/plugins/sui/errors.mjs.map +1 -1
  422. package/dist/plugins/sui/fork-faucet-strategy.d.mts +1 -0
  423. package/dist/plugins/sui/fork-faucet-strategy.mjs +55 -0
  424. package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -0
  425. package/dist/plugins/sui/fork-orchestration.d.mts +1 -0
  426. package/dist/plugins/sui/fork-orchestration.mjs +170 -2
  427. package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
  428. package/dist/plugins/sui/fork-transaction.d.mts +2 -0
  429. package/dist/plugins/sui/fork-transaction.mjs +92 -28
  430. package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
  431. package/dist/plugins/sui/index.d.mts +33 -70
  432. package/dist/plugins/sui/index.mjs +58 -35
  433. package/dist/plugins/sui/index.mjs.map +1 -1
  434. package/dist/plugins/{faucet/strategies/sui-local.mjs → sui/local-faucet-strategy.mjs} +10 -7
  435. package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -0
  436. package/dist/plugins/sui/mode/external.mjs +7 -13
  437. package/dist/plugins/sui/mode/external.mjs.map +1 -1
  438. package/dist/plugins/sui/mode/fork.mjs +136 -59
  439. package/dist/plugins/sui/mode/fork.mjs.map +1 -1
  440. package/dist/plugins/sui/mode/live.mjs +10 -16
  441. package/dist/plugins/sui/mode/live.mjs.map +1 -1
  442. package/dist/plugins/sui/mode/local.mjs +37 -36
  443. package/dist/plugins/sui/mode/local.mjs.map +1 -1
  444. package/dist/plugins/sui/mode/shared-boot.mjs +38 -115
  445. package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
  446. package/dist/plugins/sui/mode/shared.d.mts +1 -5
  447. package/dist/plugins/sui/mode/shared.mjs.map +1 -1
  448. package/dist/plugins/sui/mode/spec.d.mts +18 -0
  449. package/dist/plugins/sui/move-summary-runner.mjs +207 -0
  450. package/dist/plugins/sui/move-summary-runner.mjs.map +1 -0
  451. package/dist/plugins/sui/network-resolver.d.mts +4 -3
  452. package/dist/plugins/sui/service.mjs.map +1 -1
  453. package/dist/plugins/sui/snapshot.mjs +3 -0
  454. package/dist/plugins/sui/snapshot.mjs.map +1 -1
  455. package/dist/plugins/sui/spans.mjs +17 -0
  456. package/dist/plugins/sui/spans.mjs.map +1 -0
  457. package/dist/plugins/wallet/codegen.d.mts +0 -1
  458. package/dist/plugins/wallet/codegen.mjs +8 -9
  459. package/dist/plugins/wallet/codegen.mjs.map +1 -1
  460. package/dist/plugins/wallet/index.d.mts +6 -8
  461. package/dist/plugins/wallet/index.mjs +46 -35
  462. package/dist/plugins/wallet/index.mjs.map +1 -1
  463. package/dist/plugins/wallet/origin-policy.mjs +12 -28
  464. package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
  465. package/dist/plugins/wallet/pairing.mjs +6 -4
  466. package/dist/plugins/wallet/pairing.mjs.map +1 -1
  467. package/dist/plugins/wallet/protocol.d.mts +1 -0
  468. package/dist/plugins/wallet/protocol.mjs +5 -44
  469. package/dist/plugins/wallet/protocol.mjs.map +1 -1
  470. package/dist/plugins/wallet/routable.mjs +2 -8
  471. package/dist/plugins/wallet/routable.mjs.map +1 -1
  472. package/dist/plugins/wallet/server.mjs +36 -44
  473. package/dist/plugins/wallet/server.mjs.map +1 -1
  474. package/dist/plugins/wallet/service.d.mts +1 -5
  475. package/dist/plugins/wallet/service.mjs +22 -18
  476. package/dist/plugins/wallet/service.mjs.map +1 -1
  477. package/dist/plugins/wallet/spans.mjs +22 -0
  478. package/dist/plugins/wallet/spans.mjs.map +1 -0
  479. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +33 -8
  480. package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
  481. package/dist/plugins/walrus/codegen.d.mts +3 -3
  482. package/dist/plugins/walrus/codegen.mjs.map +1 -1
  483. package/dist/plugins/walrus/deploy-paths.mjs +21 -5
  484. package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
  485. package/dist/plugins/walrus/deploy.mjs +67 -69
  486. package/dist/plugins/walrus/deploy.mjs.map +1 -1
  487. package/dist/plugins/walrus/errors.mjs +1 -0
  488. package/dist/plugins/walrus/errors.mjs.map +1 -1
  489. package/dist/plugins/walrus/faucet-strategy.mjs +12 -7
  490. package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
  491. package/dist/plugins/walrus/index.d.mts +2 -13
  492. package/dist/plugins/walrus/index.mjs +18 -23
  493. package/dist/plugins/walrus/index.mjs.map +1 -1
  494. package/dist/plugins/walrus/mode/known-deploy.mjs +7 -5
  495. package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
  496. package/dist/plugins/walrus/mode/local-cluster.mjs +26 -7
  497. package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
  498. package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
  499. package/dist/plugins/walrus/routable.mjs +7 -23
  500. package/dist/plugins/walrus/routable.mjs.map +1 -1
  501. package/dist/plugins/walrus/service.mjs.map +1 -1
  502. package/dist/plugins/walrus/spans.mjs +18 -0
  503. package/dist/plugins/walrus/spans.mjs.map +1 -0
  504. package/dist/plugins/walrus/storage-nodes.d.mts +6 -2
  505. package/dist/plugins/walrus/storage-nodes.mjs +15 -8
  506. package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
  507. package/dist/plugins/walrus/wal-swap.mjs +58 -15
  508. package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
  509. package/dist/primitives/artifact-publisher.d.mts +1 -1
  510. package/dist/primitives/artifact-publisher.mjs +15 -0
  511. package/dist/primitives/artifact-publisher.mjs.map +1 -0
  512. package/dist/runtime/docker/client.mjs +17 -4
  513. package/dist/runtime/docker/client.mjs.map +1 -1
  514. package/dist/runtime/docker/container.mjs +219 -132
  515. package/dist/runtime/docker/container.mjs.map +1 -1
  516. package/dist/runtime/docker/errors.mjs +0 -6
  517. package/dist/runtime/docker/errors.mjs.map +1 -1
  518. package/dist/runtime/docker/exec.mjs +21 -18
  519. package/dist/runtime/docker/exec.mjs.map +1 -1
  520. package/dist/runtime/docker/image.mjs +36 -13
  521. package/dist/runtime/docker/image.mjs.map +1 -1
  522. package/dist/runtime/docker/index.mjs +14 -0
  523. package/dist/runtime/docker/inspect-and-decode.mjs +51 -0
  524. package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -0
  525. package/dist/runtime/docker/inventory.mjs +14 -11
  526. package/dist/runtime/docker/inventory.mjs.map +1 -1
  527. package/dist/runtime/docker/labels.mjs +38 -5
  528. package/dist/runtime/docker/labels.mjs.map +1 -1
  529. package/dist/runtime/docker/logs.d.mts +1 -1
  530. package/dist/runtime/docker/logs.mjs.map +1 -1
  531. package/dist/runtime/docker/network.mjs +83 -47
  532. package/dist/runtime/docker/network.mjs.map +1 -1
  533. package/dist/runtime/docker/render-run-args.mjs +82 -0
  534. package/dist/runtime/docker/render-run-args.mjs.map +1 -0
  535. package/dist/runtime/docker/service.d.mts +1 -1
  536. package/dist/runtime/docker/service.mjs +12 -7
  537. package/dist/runtime/docker/service.mjs.map +1 -1
  538. package/dist/runtime/docker/sweep.mjs +118 -43
  539. package/dist/runtime/docker/sweep.mjs.map +1 -1
  540. package/dist/runtime/docker/volume.mjs +2 -0
  541. package/dist/runtime/docker/volume.mjs.map +1 -1
  542. package/dist/runtime/docker/wrap.mjs +30 -1
  543. package/dist/runtime/docker/wrap.mjs.map +1 -1
  544. package/dist/substrate/cross-process.mjs +14 -8
  545. package/dist/substrate/cross-process.mjs.map +1 -1
  546. package/dist/substrate/event-time.mjs +33 -0
  547. package/dist/substrate/event-time.mjs.map +1 -0
  548. package/dist/substrate/events.d.mts +28 -8
  549. package/dist/substrate/identity.d.mts +2 -1
  550. package/dist/substrate/manifest.d.mts +68 -5
  551. package/dist/substrate/manifest.mjs +34 -5
  552. package/dist/substrate/manifest.mjs.map +1 -1
  553. package/dist/substrate/network.d.mts +3 -5
  554. package/dist/substrate/options.d.mts +0 -2
  555. package/dist/substrate/plugin.d.mts +13 -0
  556. package/dist/substrate/plugin.mjs +4 -2
  557. package/dist/substrate/plugin.mjs.map +1 -1
  558. package/dist/substrate/projection.d.mts +35 -3
  559. package/dist/substrate/runtime/artifact-publisher/index.mjs +19 -9
  560. package/dist/substrate/runtime/artifact-publisher/index.mjs.map +1 -1
  561. package/dist/substrate/runtime/atomic-write.mjs +97 -32
  562. package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
  563. package/dist/substrate/runtime/cache/index.mjs +3 -0
  564. package/dist/substrate/runtime/cache/schema.mjs +2 -2
  565. package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
  566. package/dist/substrate/runtime/cache/service.mjs +9 -7
  567. package/dist/substrate/runtime/cache/service.mjs.map +1 -1
  568. package/dist/substrate/runtime/capability-sinks/index.d.mts +1 -1
  569. package/dist/substrate/runtime/capability-sinks/index.mjs +3 -0
  570. package/dist/substrate/runtime/capability-sinks/layer.mjs +1 -1
  571. package/dist/substrate/runtime/capability-sinks/service.d.mts +61 -2
  572. package/dist/substrate/runtime/capability-sinks/service.mjs +21 -26
  573. package/dist/substrate/runtime/capability-sinks/service.mjs.map +1 -1
  574. package/dist/substrate/runtime/config-validation.d.mts +1 -13
  575. package/dist/substrate/runtime/config-validation.mjs +2 -91
  576. package/dist/substrate/runtime/config-validation.mjs.map +1 -1
  577. package/dist/substrate/runtime/container-runtime.d.mts +1 -0
  578. package/dist/substrate/runtime/container-runtime.mjs +2 -0
  579. package/dist/substrate/runtime/control-plane/domain.mjs +101 -0
  580. package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -0
  581. package/dist/substrate/runtime/control-plane/service.mjs +7 -0
  582. package/dist/substrate/runtime/control-plane/service.mjs.map +1 -0
  583. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +46 -19
  584. package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
  585. package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs +30 -12
  586. package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
  587. package/dist/substrate/runtime/cross-process/command-channel/index.mjs +5 -0
  588. package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs +11 -2
  589. package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs.map +1 -1
  590. package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs +1 -1
  591. package/dist/substrate/runtime/cross-process/index.mjs +11 -0
  592. package/dist/substrate/runtime/cross-process/live-clock.mjs +34 -0
  593. package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -0
  594. package/dist/substrate/runtime/cross-process/liveness.mjs +52 -10
  595. package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
  596. package/dist/substrate/runtime/cross-process/lock.d.mts +1 -0
  597. package/dist/substrate/runtime/cross-process/lock.mjs +23 -0
  598. package/dist/substrate/runtime/cross-process/lock.mjs.map +1 -0
  599. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +64 -0
  600. package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -0
  601. package/dist/substrate/runtime/cross-process/roster.mjs +97 -83
  602. package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
  603. package/dist/substrate/runtime/cross-process/self-pid.mjs +8 -0
  604. package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -0
  605. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +24 -28
  606. package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +1 -1
  607. package/dist/substrate/runtime/cross-process/stack-lock.mjs +41 -20
  608. package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
  609. package/dist/substrate/runtime/current-plugin.mjs +2 -2
  610. package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
  611. package/dist/substrate/runtime/errors.mjs +12 -6
  612. package/dist/substrate/runtime/errors.mjs.map +1 -1
  613. package/dist/substrate/runtime/format-unknown-error.mjs +30 -0
  614. package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -0
  615. package/dist/substrate/runtime/host-bind-mount-owner.mjs +10 -0
  616. package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -0
  617. package/dist/substrate/runtime/host-gateway.mjs +20 -0
  618. package/dist/substrate/runtime/host-gateway.mjs.map +1 -0
  619. package/dist/substrate/runtime/host-tree-tar/index.mjs +61 -50
  620. package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
  621. package/dist/substrate/runtime/http-probe.d.mts +1 -1
  622. package/dist/substrate/runtime/index.mjs +14 -0
  623. package/dist/substrate/runtime/lease-broker/index.d.mts +1 -1
  624. package/dist/substrate/runtime/lease-broker/index.mjs +2 -0
  625. package/dist/substrate/runtime/lease-broker/service.d.mts +1 -64
  626. package/dist/substrate/runtime/lease-broker/service.mjs +1 -1
  627. package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
  628. package/dist/substrate/runtime/lifecycle/dep-graph.mjs +22 -3
  629. package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
  630. package/dist/substrate/runtime/lifecycle/index.mjs +9 -0
  631. package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +1 -1
  632. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +20 -3
  633. package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
  634. package/dist/substrate/runtime/lifecycle/selective-restart.mjs +1 -1
  635. package/dist/substrate/runtime/lifecycle/signals.mjs +5 -5
  636. package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
  637. package/dist/substrate/runtime/lifecycle/state-machine.mjs +1 -1
  638. package/dist/substrate/runtime/managed-container.d.mts +19 -1
  639. package/dist/substrate/runtime/managed-container.mjs +20 -1
  640. package/dist/substrate/runtime/managed-container.mjs.map +1 -1
  641. package/dist/substrate/runtime/manifest/index.mjs +3 -0
  642. package/dist/substrate/runtime/manifest/manifest.mjs +15 -11
  643. package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
  644. package/dist/substrate/runtime/mode-errors.mjs +20 -0
  645. package/dist/substrate/runtime/mode-errors.mjs.map +1 -0
  646. package/dist/substrate/runtime/observability/cascade-formatter.mjs +9 -13
  647. package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
  648. package/dist/substrate/runtime/observability/formatter-registry.mjs +19 -36
  649. package/dist/substrate/runtime/observability/formatter-registry.mjs.map +1 -1
  650. package/dist/substrate/runtime/observability/ignore-with-log.d.mts +1 -0
  651. package/dist/substrate/runtime/observability/ignore-with-log.mjs +17 -0
  652. package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -0
  653. package/dist/substrate/runtime/observability/index.d.mts +1 -1
  654. package/dist/substrate/runtime/observability/index.mjs +13 -0
  655. package/dist/substrate/runtime/observability/log-store.d.mts +1 -0
  656. package/dist/substrate/runtime/observability/log-store.mjs +175 -0
  657. package/dist/substrate/runtime/observability/log-store.mjs.map +1 -0
  658. package/dist/substrate/runtime/observability/logger.d.mts +2 -1
  659. package/dist/substrate/runtime/observability/logger.mjs +5 -9
  660. package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
  661. package/dist/substrate/runtime/observability/output-truncate.mjs +35 -0
  662. package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -0
  663. package/dist/substrate/runtime/observability/pretty-error.mjs +9 -12
  664. package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
  665. package/dist/substrate/runtime/observability/redaction.d.mts +2 -13
  666. package/dist/substrate/runtime/observability/redaction.mjs +8 -14
  667. package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
  668. package/dist/substrate/runtime/observability/span-store.d.mts +1 -0
  669. package/dist/substrate/runtime/observability/span-store.mjs +110 -0
  670. package/dist/substrate/runtime/observability/span-store.mjs.map +1 -0
  671. package/dist/substrate/runtime/observability/spans.mjs +14 -14
  672. package/dist/substrate/runtime/observability/spans.mjs.map +1 -1
  673. package/dist/substrate/runtime/observability/subprocess-capture.mjs +1 -1
  674. package/dist/substrate/runtime/passthrough-or-wrap.mjs +33 -0
  675. package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -0
  676. package/dist/substrate/runtime/paths.d.mts +1 -1
  677. package/dist/substrate/runtime/paths.mjs +4 -3
  678. package/dist/substrate/runtime/paths.mjs.map +1 -1
  679. package/dist/substrate/runtime/phase-preserving-produce.mjs +40 -0
  680. package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -0
  681. package/dist/substrate/runtime/port-broker/index.mjs +3 -0
  682. package/dist/substrate/runtime/port-broker/service.mjs +170 -126
  683. package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
  684. package/dist/substrate/runtime/post-acquire-tasks.mjs +1 -1
  685. package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
  686. package/dist/substrate/runtime/probes.d.mts +7 -5
  687. package/dist/substrate/runtime/probes.mjs +3 -1
  688. package/dist/substrate/runtime/probes.mjs.map +1 -1
  689. package/dist/substrate/runtime/projection/index.mjs +4 -0
  690. package/dist/substrate/runtime/projection/operational-endpoints.mjs +1 -2
  691. package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
  692. package/dist/substrate/runtime/projection/persisted.mjs +33 -15
  693. package/dist/substrate/runtime/projection/persisted.mjs.map +1 -1
  694. package/dist/substrate/runtime/projection/state-ref.mjs +26 -2
  695. package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
  696. package/dist/substrate/runtime/projection/update.mjs +88 -24
  697. package/dist/substrate/runtime/projection/update.mjs.map +1 -1
  698. package/dist/substrate/runtime/random-suffix.mjs +11 -0
  699. package/dist/substrate/runtime/random-suffix.mjs.map +1 -0
  700. package/dist/substrate/runtime/retry-policy.d.mts +59 -1
  701. package/dist/substrate/runtime/retry-policy.mjs +66 -1
  702. package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
  703. package/dist/substrate/runtime/routed-url.d.mts +1 -0
  704. package/dist/substrate/runtime/routed-url.mjs +79 -0
  705. package/dist/substrate/runtime/routed-url.mjs.map +1 -0
  706. package/dist/substrate/runtime/runtime-decode.d.mts +1 -1
  707. package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
  708. package/dist/substrate/runtime/scoped-http-server.mjs +2 -3
  709. package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
  710. package/dist/substrate/runtime/scoped-multimap/index.mjs +2 -0
  711. package/dist/substrate/runtime/scoped-multimap/service.mjs +52 -0
  712. package/dist/substrate/runtime/scoped-multimap/service.mjs.map +1 -0
  713. package/dist/substrate/runtime/scoped-ref-map/index.mjs +2 -0
  714. package/dist/substrate/runtime/scoped-ref-map/service.mjs +2 -2
  715. package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +1 -1
  716. package/dist/substrate/runtime/stage-and-swap/index.mjs +72 -4
  717. package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
  718. package/dist/substrate/runtime/state-store/index.d.mts +1 -0
  719. package/dist/substrate/runtime/state-store/index.mjs +3 -0
  720. package/dist/substrate/runtime/state-store/schema.d.mts +1 -0
  721. package/dist/substrate/runtime/state-store/schema.mjs +19 -14
  722. package/dist/substrate/runtime/state-store/schema.mjs.map +1 -1
  723. package/dist/substrate/runtime/state-store/service.d.mts +1 -0
  724. package/dist/substrate/runtime/state-store/service.mjs +145 -0
  725. package/dist/substrate/runtime/state-store/service.mjs.map +1 -0
  726. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.d.mts +1 -0
  727. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs +33 -0
  728. package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -0
  729. package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs +1 -1
  730. package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
  731. package/dist/substrate/runtime/strategy-registry/index.mjs +4 -0
  732. package/dist/substrate/runtime/strategy-registry/service.mjs +19 -33
  733. package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
  734. package/dist/substrate/runtime/subnet-broker.mjs +60 -0
  735. package/dist/substrate/runtime/subnet-broker.mjs.map +1 -0
  736. package/dist/substrate/runtime/sui-execute/index.d.mts +21 -1
  737. package/dist/substrate/runtime/sui-execute/index.mjs +84 -20
  738. package/dist/substrate/runtime/sui-execute/index.mjs.map +1 -1
  739. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.d.mts +1 -0
  740. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs +32 -0
  741. package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +1 -0
  742. package/dist/substrate/runtime/sui-ledger/object-ref.mjs +32 -0
  743. package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +1 -0
  744. package/dist/substrate/runtime/sui-move-build/index.mjs +93 -19
  745. package/dist/substrate/runtime/sui-move-build/index.mjs.map +1 -1
  746. package/dist/substrate/runtime/supervisor/acquire-node.mjs +181 -0
  747. package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -0
  748. package/dist/substrate/runtime/supervisor/background-tasks.mjs +144 -0
  749. package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -0
  750. package/dist/substrate/runtime/supervisor/command-loop.mjs +68 -0
  751. package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -0
  752. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +151 -0
  753. package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +1 -0
  754. package/dist/substrate/runtime/supervisor/errors.d.mts +1 -0
  755. package/dist/substrate/runtime/supervisor/errors.mjs +9 -0
  756. package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -0
  757. package/dist/substrate/runtime/supervisor/index.d.mts +1 -0
  758. package/dist/substrate/runtime/supervisor/index.mjs +3 -0
  759. package/dist/substrate/runtime/supervisor/shutdown.mjs +51 -0
  760. package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -0
  761. package/dist/substrate/runtime/supervisor/start-supervisor.d.mts +1 -0
  762. package/dist/substrate/runtime/supervisor/start-supervisor.mjs +250 -0
  763. package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -0
  764. package/dist/substrate/runtime/supervisor/state.d.mts +1 -0
  765. package/dist/substrate/runtime/supervisor/state.mjs +22 -0
  766. package/dist/substrate/runtime/supervisor/state.mjs.map +1 -0
  767. package/dist/substrate/runtime/supervisor/teardown.mjs +57 -0
  768. package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -0
  769. package/dist/substrate/runtime/supervisor/types.d.mts +1 -0
  770. package/dist/substrate/runtime/supervisor/wiring.mjs +133 -0
  771. package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -0
  772. package/dist/substrate/runtime/typed-env.mjs +8 -0
  773. package/dist/substrate/runtime/typed-env.mjs.map +1 -0
  774. package/dist/substrate/state-store.d.mts +1 -0
  775. package/dist/substrate/versioned-doc-schema.mjs +17 -0
  776. package/dist/substrate/versioned-doc-schema.mjs.map +1 -0
  777. package/dist/substrate/versioned-doc-sync.mjs +94 -0
  778. package/dist/substrate/versioned-doc-sync.mjs.map +1 -0
  779. package/dist/surfaces/cli/command-tree.mjs.map +1 -1
  780. package/dist/surfaces/cli/commands/index.mjs +9 -0
  781. package/dist/surfaces/cli/commands/prune-picker-entry.mjs +13 -0
  782. package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -0
  783. package/dist/surfaces/cli/commands/prune-picker.mjs +3 -2
  784. package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
  785. package/dist/surfaces/cli/commands/prune.mjs +34 -17
  786. package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
  787. package/dist/surfaces/cli/commands/status.mjs +2 -2
  788. package/dist/surfaces/cli/commands/status.mjs.map +1 -1
  789. package/dist/surfaces/cli/commands/supervisor-presence.mjs +5 -3
  790. package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
  791. package/dist/surfaces/cli/commands/wipe.mjs +38 -4
  792. package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
  793. package/dist/surfaces/cli/errors.mjs +1 -1
  794. package/dist/surfaces/cli/errors.mjs.map +1 -1
  795. package/dist/surfaces/cli/flags.mjs +0 -1
  796. package/dist/surfaces/cli/flags.mjs.map +1 -1
  797. package/dist/surfaces/cli/index.mjs +98 -18
  798. package/dist/surfaces/cli/index.mjs.map +1 -1
  799. package/dist/surfaces/cli/output.mjs +1 -0
  800. package/dist/surfaces/cli/output.mjs.map +1 -1
  801. package/dist/surfaces/cli/sysexits.mjs +2 -1
  802. package/dist/surfaces/cli/sysexits.mjs.map +1 -1
  803. package/dist/surfaces/tui/app.mjs +25 -3
  804. package/dist/surfaces/tui/app.mjs.map +1 -1
  805. package/dist/surfaces/tui/display-derivation.mjs +23 -74
  806. package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
  807. package/dist/surfaces/tui/event-log.mjs +35 -23
  808. package/dist/surfaces/tui/event-log.mjs.map +1 -1
  809. package/dist/surfaces/tui/input.mjs +5 -1
  810. package/dist/surfaces/tui/input.mjs.map +1 -1
  811. package/dist/surfaces/tui/plain-renderer.mjs +70 -34
  812. package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
  813. package/package.json +24 -4
  814. package/dist/contracts/liveness-classifier.d.mts +0 -19
  815. package/dist/contracts/network-resolver.d.mts +0 -15
  816. package/dist/contracts/network-resolver.mjs +0 -7
  817. package/dist/contracts/network-resolver.mjs.map +0 -1
  818. package/dist/orchestrators/codegen/extras.mjs +0 -16
  819. package/dist/orchestrators/codegen/extras.mjs.map +0 -1
  820. package/dist/plugins/deepbook/routable.mjs +0 -21
  821. package/dist/plugins/deepbook/routable.mjs.map +0 -1
  822. package/dist/plugins/faucet/service.d.mts +0 -20
  823. package/dist/plugins/faucet/strategies/sui-local.d.mts +0 -23
  824. package/dist/plugins/faucet/strategies/sui-local.mjs.map +0 -1
  825. package/dist/plugins/sui/seed-objects.d.mts +0 -18
  826. package/dist/plugins/sui/seed-objects.mjs +0 -25
  827. package/dist/plugins/sui/seed-objects.mjs.map +0 -1
  828. package/dist/runtime/built-in-plugin-layers.mjs.map +0 -1
  829. package/dist/substrate/runtime/context-helpers.mjs +0 -32
  830. package/dist/substrate/runtime/context-helpers.mjs.map +0 -1
  831. package/dist/substrate/runtime/run.mjs.map +0 -1
  832. package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs +0 -30
  833. package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs.map +0 -1
  834. package/dist/substrate/runtime/supervisor.d.mts +0 -1
  835. package/dist/substrate/runtime/supervisor.mjs +0 -831
  836. package/dist/substrate/runtime/supervisor.mjs.map +0 -1
  837. package/dist/surfaces/cli/commands/doctor-probes.mjs.map +0 -1
  838. /package/dist/{substrate/runtime/strategy-registry/faucet-capability-for.d.mts → plugins/faucet/http.d.mts} +0 -0
@@ -3,22 +3,56 @@ import { emitSuccess } from "../output.mjs";
3
3
  import { confirmDestructive } from "./confirm.mjs";
4
4
  import { Effect } from "effect";
5
5
  //#region src/surfaces/cli/commands/wipe.ts
6
+ /** Human-readable preview of what a real wipe would delete. Mirrors the
7
+ * orchestrator's teardown order (containers → networks/volumes →
8
+ * state.json → runtime tree) so the operator reads the plan in the same
9
+ * sequence the wipe executes. */
10
+ const dryRunLines = (targets) => {
11
+ const lines = [`[dry-run] would wipe ${targets.app}/${targets.stack}:`];
12
+ if (targets.containers.length > 0) {
13
+ lines.push(` containers (${targets.containers.length}):`);
14
+ for (const name of targets.containers) lines.push(` - ${name}`);
15
+ } else lines.push(" containers: (none running/created)");
16
+ lines.push(` networks: all managed (label devstack.app=${targets.networkLabelMatch.app},devstack.stack=${targets.networkLabelMatch.stack})`);
17
+ lines.push(` volumes: all managed (label devstack.app=${targets.volumeLabelMatch.app},devstack.stack=${targets.volumeLabelMatch.stack})`);
18
+ lines.push(` state file: ${targets.stateFile}`);
19
+ if (targets.onDiskPaths.length > 0) {
20
+ lines.push(` on-disk (${targets.onDiskPaths.length}):`);
21
+ for (const path of targets.onDiskPaths) lines.push(` - ${path}`);
22
+ } else lines.push(` on-disk: (stack root ${targets.stackRoot} has no removable state)`);
23
+ if (targets.preserved.length > 0) lines.push(` preserved: ${targets.preserved.join(", ")}`);
24
+ return lines;
25
+ };
6
26
  const runWipe = (deps, ctx) => Effect.gen(function* () {
7
27
  const started = Date.now();
8
28
  yield* confirmDestructive(deps.confirm, ctx, {
9
29
  verb: "wipe",
10
30
  prompt: "Wipe all devstack state for the selected stack?"
11
31
  });
12
- if (!ctx.flags.dryRun) yield* deps.wipe().pipe(Effect.catch((cause) => isCliError(cause) ? Effect.fail(cause) : Effect.fail(new CliInternalError({
32
+ if (ctx.flags.dryRun) {
33
+ const targets = deps.plan === void 0 ? null : yield* deps.plan().pipe(Effect.catch(() => Effect.succeed(null)));
34
+ yield* emitSuccess(ctx.io, ctx.flags.outputMode, {
35
+ command: "wipe",
36
+ elapsedMs: Date.now() - started,
37
+ dryRun: true,
38
+ data: {
39
+ dryRun: true,
40
+ ...targets === null ? {} : { targets }
41
+ },
42
+ humanLines: targets === null ? ["[dry-run] would wipe selected stack state"] : dryRunLines(targets)
43
+ });
44
+ return { exitCode: 0 };
45
+ }
46
+ yield* deps.wipe().pipe(Effect.catch((cause) => isCliError(cause) ? Effect.fail(cause) : Effect.fail(new CliInternalError({
13
47
  message: "wipe failed",
14
48
  cause
15
49
  }))));
16
50
  yield* emitSuccess(ctx.io, ctx.flags.outputMode, {
17
51
  command: "wipe",
18
52
  elapsedMs: Date.now() - started,
19
- dryRun: ctx.flags.dryRun,
20
- data: { dryRun: ctx.flags.dryRun },
21
- humanLines: ctx.flags.dryRun ? ["[dry-run] would wipe selected stack state"] : ["stack state wiped"]
53
+ dryRun: false,
54
+ data: { dryRun: false },
55
+ humanLines: ["stack state wiped"]
22
56
  });
23
57
  return { exitCode: 0 };
24
58
  }).pipe(Effect.withSpan("cli.wipe"));
@@ -1 +1 @@
1
- {"version":3,"file":"wipe.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/wipe.ts"],"sourcesContent":["// CLI verb: `devstack wipe` — destroy all state for a stack.\n//\n// Architecture (distilled/20-cli.md § Subcommands § Teardown):\n// \"wipe — destroy all state for a stack.\"\n//\n// `wipe` is the recovery flow `apply` points users at on\n// `SeedManifestMismatchError`. It is tier-2 destructive (requires\n// `--yes` or an interactive TTY confirmation) and refuses to run while\n// a supervisor is live for the target stack (writes to disk while\n// a peer is acquiring a container would race).\n//\n// Surface-equality: `wipe` publishes `wipe.requested`; the L3 snapshot\n// orchestrator consumes (it owns the `wipe.ts` body that rmdir's the\n// stack root + container labels).\n\nimport { Effect } from 'effect';\n\nimport { type CliError, CliInternalError, isCliError } from '../errors.ts';\nimport { emitSuccess } from '../output.ts';\nimport { confirmDestructive, type ConfirmPrompt } from './confirm.ts';\nimport type { CommandContext, CommandResult } from './index.ts';\n\nexport interface WipeDeps {\n\treadonly wipe: () => Effect.Effect<void, unknown>;\n\treadonly confirm: ConfirmPrompt;\n}\n\nexport const runWipe = (\n\tdeps: WipeDeps,\n\tctx: CommandContext,\n): Effect.Effect<CommandResult, CliError> =>\n\tEffect.gen(function* () {\n\t\tconst started = Date.now();\n\t\tyield* confirmDestructive(deps.confirm, ctx, {\n\t\t\tverb: 'wipe',\n\t\t\tprompt: 'Wipe all devstack state for the selected stack?',\n\t\t});\n\t\tif (!ctx.flags.dryRun) {\n\t\t\tyield* deps.wipe().pipe(\n\t\t\t\tEffect.catch((cause: unknown) =>\n\t\t\t\t\tisCliError(cause)\n\t\t\t\t\t\t? Effect.fail(cause)\n\t\t\t\t\t\t: Effect.fail(\n\t\t\t\t\t\t\t\tnew CliInternalError({\n\t\t\t\t\t\t\t\t\tmessage: 'wipe failed',\n\t\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\tcommand: 'wipe',\n\t\t\telapsedMs: Date.now() - started,\n\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\tdata: {\n\t\t\t\tdryRun: ctx.flags.dryRun,\n\t\t\t},\n\t\t\thumanLines: ctx.flags.dryRun\n\t\t\t\t? ['[dry-run] would wipe selected stack state']\n\t\t\t\t: ['stack state wiped'],\n\t\t});\n\t\treturn { exitCode: 0 } as CommandResult;\n\t}).pipe(Effect.withSpan('cli.wipe'));\n"],"mappings":";;;;;AA2BA,MAAa,WACZ,MACA,QAEA,OAAO,IAAI,aAAa;CACvB,MAAM,UAAU,KAAK,KAAK;AAC1B,QAAO,mBAAmB,KAAK,SAAS,KAAK;EAC5C,MAAM;EACN,QAAQ;EACR,CAAC;AACF,KAAI,CAAC,IAAI,MAAM,OACd,QAAO,KAAK,MAAM,CAAC,KAClB,OAAO,OAAO,UACb,WAAW,MAAM,GACd,OAAO,KAAK,MAAM,GAClB,OAAO,KACP,IAAI,iBAAiB;EACpB,SAAS;EACT;EACA,CAAC,CACF,CACH,CACD;AAEF,QAAO,YAAY,IAAI,IAAI,IAAI,MAAM,YAAY;EAChD,SAAS;EACT,WAAW,KAAK,KAAK,GAAG;EACxB,QAAQ,IAAI,MAAM;EAClB,MAAM,EACL,QAAQ,IAAI,MAAM,QAClB;EACD,YAAY,IAAI,MAAM,SACnB,CAAC,4CAA4C,GAC7C,CAAC,oBAAoB;EACxB,CAAC;AACF,QAAO,EAAE,UAAU,GAAG;EACrB,CAAC,KAAK,OAAO,SAAS,WAAW,CAAC"}
1
+ {"version":3,"file":"wipe.mjs","names":[],"sources":["../../../../src/surfaces/cli/commands/wipe.ts"],"sourcesContent":["// CLI verb: `devstack wipe` — destroy all state for a stack.\n//\n// Architecture (distilled/20-cli.md § Subcommands § Teardown):\n// \"wipe — destroy all state for a stack.\"\n//\n// `wipe` is the recovery flow `apply` points users at when stale\n// on-disk state needs a clean reset. It is tier-2 destructive (requires\n// `--yes` or an interactive TTY confirmation) and refuses to run while\n// a supervisor is live for the target stack (writes to disk while\n// a peer is acquiring a container would race).\n//\n// Surface-equality: `wipe` publishes `wipe.requested`; the L3 snapshot\n// orchestrator consumes (it owns the `wipe.ts` body that rmdir's the\n// stack root + container labels).\n\nimport { Effect } from 'effect';\n\nimport type { WipeTargets } from '../../../orchestrators/snapshot/index.ts';\nimport { type CliError, CliInternalError, isCliError } from '../errors.ts';\nimport { emitSuccess } from '../output.ts';\nimport { confirmDestructive, type ConfirmPrompt } from './confirm.ts';\nimport type { CommandContext, CommandResult } from './index.ts';\n\nexport type { WipeTargets } from '../../../orchestrators/snapshot/index.ts';\n\nexport interface WipeDeps {\n\treadonly wipe: () => Effect.Effect<void, unknown>;\n\t/** Read-only enumeration of the concrete targets a real wipe would\n\t * remove (containers, network/volume label scope, state file, the\n\t * on-disk stack tree). Drives `--dry-run`. Optional so a deps\n\t * builder that cannot enumerate (or a test fixture) still works —\n\t * the dry-run then falls back to a generic preview line. */\n\treadonly plan?: () => Effect.Effect<WipeTargets, unknown>;\n\treadonly confirm: ConfirmPrompt;\n}\n\n/** Human-readable preview of what a real wipe would delete. Mirrors the\n * orchestrator's teardown order (containers → networks/volumes →\n * state.json → runtime tree) so the operator reads the plan in the same\n * sequence the wipe executes. */\nconst dryRunLines = (targets: WipeTargets): ReadonlyArray<string> => {\n\tconst lines: Array<string> = [`[dry-run] would wipe ${targets.app}/${targets.stack}:`];\n\tif (targets.containers.length > 0) {\n\t\tlines.push(` containers (${targets.containers.length}):`);\n\t\tfor (const name of targets.containers) lines.push(` - ${name}`);\n\t} else {\n\t\tlines.push(' containers: (none running/created)');\n\t}\n\tlines.push(\n\t\t` networks: all managed (label devstack.app=${targets.networkLabelMatch.app},devstack.stack=${targets.networkLabelMatch.stack})`,\n\t);\n\tlines.push(\n\t\t` volumes: all managed (label devstack.app=${targets.volumeLabelMatch.app},devstack.stack=${targets.volumeLabelMatch.stack})`,\n\t);\n\tlines.push(` state file: ${targets.stateFile}`);\n\tif (targets.onDiskPaths.length > 0) {\n\t\tlines.push(` on-disk (${targets.onDiskPaths.length}):`);\n\t\tfor (const path of targets.onDiskPaths) lines.push(` - ${path}`);\n\t} else {\n\t\tlines.push(` on-disk: (stack root ${targets.stackRoot} has no removable state)`);\n\t}\n\tif (targets.preserved.length > 0) {\n\t\tlines.push(` preserved: ${targets.preserved.join(', ')}`);\n\t}\n\treturn lines;\n};\n\nexport const runWipe = (\n\tdeps: WipeDeps,\n\tctx: CommandContext,\n): Effect.Effect<CommandResult, CliError> =>\n\tEffect.gen(function* () {\n\t\tconst started = Date.now();\n\t\tyield* confirmDestructive(deps.confirm, ctx, {\n\t\t\tverb: 'wipe',\n\t\t\tprompt: 'Wipe all devstack state for the selected stack?',\n\t\t});\n\n\t\tif (ctx.flags.dryRun) {\n\t\t\t// Enumerate (read-only) so the preview lists the concrete\n\t\t\t// containers / networks / volumes / on-disk state a real wipe\n\t\t\t// would remove. A plan failure is non-fatal for a dry-run — the\n\t\t\t// worst case is a less-detailed preview — so it degrades to the\n\t\t\t// generic line rather than failing the command.\n\t\t\tconst targets =\n\t\t\t\tdeps.plan === undefined\n\t\t\t\t\t? null\n\t\t\t\t\t: yield* deps.plan().pipe(Effect.catch(() => Effect.succeed(null)));\n\t\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\t\tcommand: 'wipe',\n\t\t\t\telapsedMs: Date.now() - started,\n\t\t\t\tdryRun: true,\n\t\t\t\tdata: {\n\t\t\t\t\tdryRun: true,\n\t\t\t\t\t...(targets === null ? {} : { targets }),\n\t\t\t\t},\n\t\t\t\thumanLines:\n\t\t\t\t\ttargets === null ? ['[dry-run] would wipe selected stack state'] : dryRunLines(targets),\n\t\t\t});\n\t\t\treturn { exitCode: 0 } as CommandResult;\n\t\t}\n\n\t\tyield* deps.wipe().pipe(\n\t\t\tEffect.catch((cause: unknown) =>\n\t\t\t\tisCliError(cause)\n\t\t\t\t\t? Effect.fail(cause)\n\t\t\t\t\t: Effect.fail(\n\t\t\t\t\t\t\tnew CliInternalError({\n\t\t\t\t\t\t\t\tmessage: 'wipe failed',\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t),\n\t\t);\n\t\tyield* emitSuccess(ctx.io, ctx.flags.outputMode, {\n\t\t\tcommand: 'wipe',\n\t\t\telapsedMs: Date.now() - started,\n\t\t\tdryRun: false,\n\t\t\tdata: {\n\t\t\t\tdryRun: false,\n\t\t\t},\n\t\t\thumanLines: ['stack state wiped'],\n\t\t});\n\t\treturn { exitCode: 0 } as CommandResult;\n\t}).pipe(Effect.withSpan('cli.wipe'));\n"],"mappings":";;;;;;;;;AAwCA,MAAM,eAAe,YAAgD;CACpE,MAAM,QAAuB,CAAC,wBAAwB,QAAQ,IAAI,GAAG,QAAQ,MAAM,GAAG;AACtF,KAAI,QAAQ,WAAW,SAAS,GAAG;AAClC,QAAM,KAAK,iBAAiB,QAAQ,WAAW,OAAO,IAAI;AAC1D,OAAK,MAAM,QAAQ,QAAQ,WAAY,OAAM,KAAK,SAAS,OAAO;OAElE,OAAM,KAAK,uCAAuC;AAEnD,OAAM,KACL,+CAA+C,QAAQ,kBAAkB,IAAI,kBAAkB,QAAQ,kBAAkB,MAAM,GAC/H;AACD,OAAM,KACL,8CAA8C,QAAQ,iBAAiB,IAAI,kBAAkB,QAAQ,iBAAiB,MAAM,GAC5H;AACD,OAAM,KAAK,iBAAiB,QAAQ,YAAY;AAChD,KAAI,QAAQ,YAAY,SAAS,GAAG;AACnC,QAAM,KAAK,cAAc,QAAQ,YAAY,OAAO,IAAI;AACxD,OAAK,MAAM,QAAQ,QAAQ,YAAa,OAAM,KAAK,SAAS,OAAO;OAEnE,OAAM,KAAK,0BAA0B,QAAQ,UAAU,0BAA0B;AAElF,KAAI,QAAQ,UAAU,SAAS,EAC9B,OAAM,KAAK,gBAAgB,QAAQ,UAAU,KAAK,KAAK,GAAG;AAE3D,QAAO;;AAGR,MAAa,WACZ,MACA,QAEA,OAAO,IAAI,aAAa;CACvB,MAAM,UAAU,KAAK,KAAK;AAC1B,QAAO,mBAAmB,KAAK,SAAS,KAAK;EAC5C,MAAM;EACN,QAAQ;EACR,CAAC;AAEF,KAAI,IAAI,MAAM,QAAQ;EAMrB,MAAM,UACL,KAAK,SAAS,KAAA,IACX,OACA,OAAO,KAAK,MAAM,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC;AACrE,SAAO,YAAY,IAAI,IAAI,IAAI,MAAM,YAAY;GAChD,SAAS;GACT,WAAW,KAAK,KAAK,GAAG;GACxB,QAAQ;GACR,MAAM;IACL,QAAQ;IACR,GAAI,YAAY,OAAO,EAAE,GAAG,EAAE,SAAS;IACvC;GACD,YACC,YAAY,OAAO,CAAC,4CAA4C,GAAG,YAAY,QAAQ;GACxF,CAAC;AACF,SAAO,EAAE,UAAU,GAAG;;AAGvB,QAAO,KAAK,MAAM,CAAC,KAClB,OAAO,OAAO,UACb,WAAW,MAAM,GACd,OAAO,KAAK,MAAM,GAClB,OAAO,KACP,IAAI,iBAAiB;EACpB,SAAS;EACT;EACA,CAAC,CACF,CACH,CACD;AACD,QAAO,YAAY,IAAI,IAAI,IAAI,MAAM,YAAY;EAChD,SAAS;EACT,WAAW,KAAK,KAAK,GAAG;EACxB,QAAQ;EACR,MAAM,EACL,QAAQ,OACR;EACD,YAAY,CAAC,oBAAoB;EACjC,CAAC;AACF,QAAO,EAAE,UAAU,GAAG;EACrB,CAAC,KAAK,OAAO,SAAS,WAAW,CAAC"}
@@ -96,7 +96,7 @@ const hintFor = (error) => {
96
96
  case "CliConfirmDeclinedError":
97
97
  case "CliSupervisorLiveError":
98
98
  case "CliNoSupervisorError": return error.hint;
99
- case "CliConfigNotFoundError":
99
+ case "CliConfigNotFoundError": return "config file not found; run `devstack init` or pass `--config <path>`";
100
100
  case "CliConfigInvalidError":
101
101
  case "CliSnapshotNotFoundError":
102
102
  case "CliInternalError":
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":["XC"],"sources":["../../../src/surfaces/cli/errors.ts"],"sourcesContent":["// CLI surface — typed errors carried up to the top-level renderer.\n//\n// Architecture (distilled/20-cli.md § Edge cases) enumerates the\n// surface's failure modes. Each one MUST resolve to a stable sysexit\n// code; the error tag here carries that mapping so the dispatcher\n// doesn't sprinkle exit codes through every command.\n//\n// Why tagged errors and not plain `Effect.fail(string)`?\n// - `catchTag` in commands can pick out the precise tag without\n// stringly matching.\n// - The cascade formatter (substrate/runtime/observability/\n// cascade-formatter.ts) renders these natively via their `_tag`.\n// - The `schema --json` command enumerates the tag table.\n\nimport { Data } from 'effect';\n\nimport { type ExitCode, ExitCode as XC } from './sysexits.ts';\n\n// -----------------------------------------------------------------------------\n// Tagged errors\n// -----------------------------------------------------------------------------\n\n/** Argv parsing produced an error — unknown subcommand, malformed\n * flag value, missing required positional. */\nexport class CliUsageError extends Data.TaggedError('CliUsageError')<{\n\treadonly message: string;\n\treadonly hint?: string;\n}> {}\n\n/** The user's `devstack.config.ts` could not be located (path probe\n * exhausted) or could not be imported. */\nexport class CliConfigNotFoundError extends Data.TaggedError('CliConfigNotFoundError')<{\n\treadonly message: string;\n\treadonly searchedPaths?: ReadonlyArray<string>;\n}> {}\n\n/** The config imported successfully but its default export is not a\n * `Stack` value (failed brand check, missing required field). */\nexport class CliConfigInvalidError extends Data.TaggedError('CliConfigInvalidError')<{\n\treadonly message: string;\n\treadonly cause?: unknown;\n}> {}\n\n/** A required service (Docker daemon, network) is unavailable. */\nexport class CliUnavailableError extends Data.TaggedError('CliUnavailableError')<{\n\treadonly message: string;\n\treadonly service: string;\n\treadonly hint?: string;\n}> {}\n\n/** Snapshot name/id not found. Carries the reference the caller\n * used so the failure renderer can echo it back. */\nexport class CliSnapshotNotFoundError extends Data.TaggedError('CliSnapshotNotFoundError')<{\n\treadonly snapshotRef: string;\n}> {}\n\nexport class CliSnapshotAmbiguousError extends Data.TaggedError('CliSnapshotAmbiguousError')<{\n\treadonly snapshotRef: string;\n\treadonly matches: ReadonlyArray<string>;\n}> {}\n\n/** Destructive verb refused because `--yes` was absent and prompting\n * was forbidden (non-TTY stdin or `--no-input`). */\nexport class CliConfirmRequiredError extends Data.TaggedError('CliConfirmRequiredError')<{\n\treadonly verb: string;\n\treadonly hint?: string;\n}> {}\n\n/** Destructive verb was prompted interactively and the user declined. */\nexport class CliConfirmDeclinedError extends Data.TaggedError('CliConfirmDeclinedError')<{\n\treadonly verb: string;\n\treadonly hint?: string;\n}> {}\n\n/** Engine reports a live supervisor for the target stack; the verb\n * refused to mutate shared state. */\nexport class CliSupervisorLiveError extends Data.TaggedError('CliSupervisorLiveError')<{\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly hint?: string;\n}> {}\n\n/** The verb requires a live supervisor (a stack must be `up`) but\n * none is running for `(app, stack)`. Distinguished from\n * `CliSupervisorLiveError` (the inverse case where the verb refused\n * because one IS live). */\nexport class CliNoSupervisorError extends Data.TaggedError('CliNoSupervisorError')<{\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly hint?: string;\n}> {}\n\n/** Internal/unexpected failure. Wraps an arbitrary cause; the cascade\n * formatter renders the inner. */\nexport class CliInternalError extends Data.TaggedError('CliInternalError')<{\n\treadonly message: string;\n\treadonly cause?: unknown;\n}> {}\n\n/** Sentinel: subcommand already pretty-rendered its failure. The\n * top-level renderer recognizes this tag and skips re-rendering, but\n * still propagates `exitCode` to the OS exit code.\n *\n * Architecture § Learnings: \"Already-reported sentinel pattern\" — the\n * marker traverses the cause structure used by the Effect runtime, so\n * the top-level `Effect.catchAll` sees it. */\nexport class CliAlreadyReportedError extends Data.TaggedError('CliAlreadyReportedError')<{\n\treadonly exitCode: ExitCode;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Union + sysexit projection\n// -----------------------------------------------------------------------------\n\nexport type CliError =\n\t| CliUsageError\n\t| CliConfigNotFoundError\n\t| CliConfigInvalidError\n\t| CliUnavailableError\n\t| CliSnapshotNotFoundError\n\t| CliSnapshotAmbiguousError\n\t| CliConfirmRequiredError\n\t| CliConfirmDeclinedError\n\t| CliSupervisorLiveError\n\t| CliNoSupervisorError\n\t| CliInternalError\n\t| CliAlreadyReportedError;\n\nexport const isCliError = (value: unknown): value is CliError => {\n\tif (typeof value !== 'object' || value === null) return false;\n\tconst tag = (value as { readonly _tag?: unknown })._tag;\n\tswitch (tag) {\n\t\tcase 'CliUsageError':\n\t\tcase 'CliConfigNotFoundError':\n\t\tcase 'CliConfigInvalidError':\n\t\tcase 'CliUnavailableError':\n\t\tcase 'CliSnapshotNotFoundError':\n\t\tcase 'CliSnapshotAmbiguousError':\n\t\tcase 'CliConfirmRequiredError':\n\t\tcase 'CliConfirmDeclinedError':\n\t\tcase 'CliSupervisorLiveError':\n\t\tcase 'CliNoSupervisorError':\n\t\tcase 'CliInternalError':\n\t\tcase 'CliAlreadyReportedError':\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n};\n\n/** Project a tagged error to its sysexit code. The dispatcher reads\n * this to set `process.exitCode` — there is exactly one place in the\n * surface where tag → numeric mapping happens. */\nexport const exitCodeFor = (error: CliError): ExitCode => {\n\tswitch (error._tag) {\n\t\tcase 'CliUsageError':\n\t\t\treturn XC.USAGE;\n\t\tcase 'CliConfigNotFoundError':\n\t\t\treturn XC.NO_INPUT;\n\t\tcase 'CliConfigInvalidError':\n\t\t\treturn XC.CONFIG;\n\t\tcase 'CliUnavailableError':\n\t\t\treturn XC.UNAVAILABLE;\n\t\tcase 'CliSnapshotNotFoundError':\n\t\t\treturn XC.SNAPSHOT_NOT_FOUND;\n\t\tcase 'CliSnapshotAmbiguousError':\n\t\t\treturn XC.USAGE;\n\t\tcase 'CliConfirmRequiredError':\n\t\tcase 'CliConfirmDeclinedError':\n\t\t\treturn XC.CONFIRM_REQUIRED;\n\t\tcase 'CliSupervisorLiveError':\n\t\t\treturn XC.SUPERVISOR_LIVE;\n\t\tcase 'CliNoSupervisorError':\n\t\t\treturn XC.UNAVAILABLE;\n\t\tcase 'CliInternalError':\n\t\t\treturn XC.SOFTWARE;\n\t\tcase 'CliAlreadyReportedError':\n\t\t\treturn error.exitCode;\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = error;\n\t\t\tvoid _exhaustive;\n\t\t\treturn XC.SOFTWARE;\n\t\t}\n\t}\n};\n\n/** Short single-line summary for the envelope's `error.summary`\n * field. Cascade detail lives in `error.chain[]`. */\nexport const summaryFor = (error: CliError): string => {\n\tswitch (error._tag) {\n\t\tcase 'CliUsageError':\n\t\t\treturn error.message;\n\t\tcase 'CliConfigNotFoundError':\n\t\t\treturn error.message;\n\t\tcase 'CliConfigInvalidError':\n\t\t\treturn error.message;\n\t\tcase 'CliUnavailableError':\n\t\t\treturn `${error.service} unavailable: ${error.message}`;\n\t\tcase 'CliSnapshotNotFoundError':\n\t\t\treturn `snapshot not found: ${error.snapshotRef}`;\n\t\tcase 'CliSnapshotAmbiguousError':\n\t\t\treturn `snapshot reference is ambiguous: ${error.snapshotRef}`;\n\t\tcase 'CliConfirmRequiredError':\n\t\t\treturn `${error.verb} requires confirmation`;\n\t\tcase 'CliConfirmDeclinedError':\n\t\t\treturn `${error.verb} confirmation declined`;\n\t\tcase 'CliSupervisorLiveError':\n\t\t\treturn `supervisor live for ${error.app}/${error.stack}`;\n\t\tcase 'CliNoSupervisorError':\n\t\t\treturn `no supervisor running for ${error.app}/${error.stack}`;\n\t\tcase 'CliInternalError':\n\t\t\treturn error.message;\n\t\tcase 'CliAlreadyReportedError':\n\t\t\treturn '(already reported)';\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = error;\n\t\t\tvoid _exhaustive;\n\t\t\treturn '(unknown error)';\n\t\t}\n\t}\n};\n\n/** Optional hint for the envelope's `error.hint` field. */\nexport const hintFor = (error: CliError): string | undefined => {\n\tswitch (error._tag) {\n\t\tcase 'CliUsageError':\n\t\t\treturn error.hint;\n\t\tcase 'CliSnapshotAmbiguousError':\n\t\t\treturn `use one of these ids instead: ${error.matches.join(', ')}`;\n\t\tcase 'CliUnavailableError':\n\t\tcase 'CliConfirmRequiredError':\n\t\tcase 'CliConfirmDeclinedError':\n\t\tcase 'CliSupervisorLiveError':\n\t\tcase 'CliNoSupervisorError':\n\t\t\treturn error.hint;\n\t\tcase 'CliConfigNotFoundError':\n\t\tcase 'CliConfigInvalidError':\n\t\tcase 'CliSnapshotNotFoundError':\n\t\tcase 'CliInternalError':\n\t\tcase 'CliAlreadyReportedError':\n\t\t\treturn undefined;\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = error;\n\t\t\tvoid _exhaustive;\n\t\t\treturn undefined;\n\t\t}\n\t}\n};\n"],"mappings":";;;;;AAwBA,IAAa,gBAAb,cAAmC,KAAK,YAAY,gBAAgB,CAGjE;;;AAIH,IAAa,yBAAb,cAA4C,KAAK,YAAY,yBAAyB,CAGnF;;;AAIH,IAAa,wBAAb,cAA2C,KAAK,YAAY,wBAAwB,CAGjF;;AAGH,IAAa,sBAAb,cAAyC,KAAK,YAAY,sBAAsB,CAI7E;;;AAIH,IAAa,2BAAb,cAA8C,KAAK,YAAY,2BAA2B,CAEvF;AAEH,IAAa,4BAAb,cAA+C,KAAK,YAAY,4BAA4B,CAGzF;;;AAIH,IAAa,0BAAb,cAA6C,KAAK,YAAY,0BAA0B,CAGrF;;AAGH,IAAa,0BAAb,cAA6C,KAAK,YAAY,0BAA0B,CAGrF;;;AAIH,IAAa,yBAAb,cAA4C,KAAK,YAAY,yBAAyB,CAInF;AAMuC,KAAK,YAAY,uBAAuB;;;AAQlF,IAAa,mBAAb,cAAsC,KAAK,YAAY,mBAAmB,CAGvE;AAS0C,KAAK,YAAY,0BAA0B;AAsBxF,MAAa,cAAc,UAAsC;AAChE,KAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AAExD,SADa,MAAsC,MACnD;EACC,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,0BACJ,QAAO;EACR,QACC,QAAO;;;;;;AAOV,MAAa,eAAe,UAA8B;AACzD,SAAQ,MAAM,MAAd;EACC,KAAK,gBACJ,QAAOA,SAAG;EACX,KAAK,yBACJ,QAAOA,SAAG;EACX,KAAK,wBACJ,QAAOA,SAAG;EACX,KAAK,sBACJ,QAAOA,SAAG;EACX,KAAK,2BACJ,QAAOA,SAAG;EACX,KAAK,4BACJ,QAAOA,SAAG;EACX,KAAK;EACL,KAAK,0BACJ,QAAOA,SAAG;EACX,KAAK,yBACJ,QAAOA,SAAG;EACX,KAAK,uBACJ,QAAOA,SAAG;EACX,KAAK,mBACJ,QAAOA,SAAG;EACX,KAAK,0BACJ,QAAO,MAAM;EACd,QAGC,QAAOA,SAAG;;;;;AAOb,MAAa,cAAc,UAA4B;AACtD,SAAQ,MAAM,MAAd;EACC,KAAK,gBACJ,QAAO,MAAM;EACd,KAAK,yBACJ,QAAO,MAAM;EACd,KAAK,wBACJ,QAAO,MAAM;EACd,KAAK,sBACJ,QAAO,GAAG,MAAM,QAAQ,gBAAgB,MAAM;EAC/C,KAAK,2BACJ,QAAO,uBAAuB,MAAM;EACrC,KAAK,4BACJ,QAAO,oCAAoC,MAAM;EAClD,KAAK,0BACJ,QAAO,GAAG,MAAM,KAAK;EACtB,KAAK,0BACJ,QAAO,GAAG,MAAM,KAAK;EACtB,KAAK,yBACJ,QAAO,uBAAuB,MAAM,IAAI,GAAG,MAAM;EAClD,KAAK,uBACJ,QAAO,6BAA6B,MAAM,IAAI,GAAG,MAAM;EACxD,KAAK,mBACJ,QAAO,MAAM;EACd,KAAK,0BACJ,QAAO;EACR,QAGC,QAAO;;;;AAMV,MAAa,WAAW,UAAwC;AAC/D,SAAQ,MAAM,MAAd;EACC,KAAK,gBACJ,QAAO,MAAM;EACd,KAAK,4BACJ,QAAO,iCAAiC,MAAM,QAAQ,KAAK,KAAK;EACjE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,uBACJ,QAAO,MAAM;EACd,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,0BACJ;EACD,QAGC"}
1
+ {"version":3,"file":"errors.mjs","names":["XC"],"sources":["../../../src/surfaces/cli/errors.ts"],"sourcesContent":["// CLI surface — typed errors carried up to the top-level renderer.\n//\n// Architecture (distilled/20-cli.md § Edge cases) enumerates the\n// surface's failure modes. Each one MUST resolve to a stable sysexit\n// code; the error tag here carries that mapping so the dispatcher\n// doesn't sprinkle exit codes through every command.\n//\n// Why tagged errors and not plain `Effect.fail(string)`?\n// - `catchTag` in commands can pick out the precise tag without\n// stringly matching.\n// - The cascade formatter (substrate/runtime/observability/\n// cascade-formatter.ts) renders these natively via their `_tag`.\n// - The `schema --json` command enumerates the tag table.\n\nimport { Data } from 'effect';\n\nimport { type ExitCode, ExitCode as XC } from './sysexits.ts';\n\n// -----------------------------------------------------------------------------\n// Tagged errors\n// -----------------------------------------------------------------------------\n\n/** Argv parsing produced an error — unknown subcommand, malformed\n * flag value, missing required positional. */\nexport class CliUsageError extends Data.TaggedError('CliUsageError')<{\n\treadonly message: string;\n\treadonly hint?: string;\n}> {}\n\n/** The user's `devstack.config.ts` could not be located (path probe\n * exhausted) or could not be imported. */\nexport class CliConfigNotFoundError extends Data.TaggedError('CliConfigNotFoundError')<{\n\treadonly message: string;\n\treadonly searchedPaths?: ReadonlyArray<string>;\n}> {}\n\n/** The config imported successfully but its default export is not a\n * `Stack` value (failed brand check, missing required field). */\nexport class CliConfigInvalidError extends Data.TaggedError('CliConfigInvalidError')<{\n\treadonly message: string;\n\treadonly cause?: unknown;\n}> {}\n\n/** A required service (Docker daemon, network) is unavailable. */\nexport class CliUnavailableError extends Data.TaggedError('CliUnavailableError')<{\n\treadonly message: string;\n\treadonly service: string;\n\treadonly hint?: string;\n}> {}\n\n/** Snapshot name/id not found. Carries the reference the caller\n * used so the failure renderer can echo it back. */\nexport class CliSnapshotNotFoundError extends Data.TaggedError('CliSnapshotNotFoundError')<{\n\treadonly snapshotRef: string;\n}> {}\n\nexport class CliSnapshotAmbiguousError extends Data.TaggedError('CliSnapshotAmbiguousError')<{\n\treadonly snapshotRef: string;\n\treadonly matches: ReadonlyArray<string>;\n}> {}\n\n/** Destructive verb refused because `--yes` was absent and prompting\n * was forbidden (non-TTY stdin or `--no-input`). */\nexport class CliConfirmRequiredError extends Data.TaggedError('CliConfirmRequiredError')<{\n\treadonly verb: string;\n\treadonly hint?: string;\n}> {}\n\n/** Destructive verb was prompted interactively and the user declined. */\nexport class CliConfirmDeclinedError extends Data.TaggedError('CliConfirmDeclinedError')<{\n\treadonly verb: string;\n\treadonly hint?: string;\n}> {}\n\n/** Engine reports a live supervisor for the target stack; the verb\n * refused to mutate shared state. */\nexport class CliSupervisorLiveError extends Data.TaggedError('CliSupervisorLiveError')<{\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly hint?: string;\n}> {}\n\n/** The verb requires a live supervisor (a stack must be `up`) but\n * none is running for `(app, stack)`. Distinguished from\n * `CliSupervisorLiveError` (the inverse case where the verb refused\n * because one IS live). */\nexport class CliNoSupervisorError extends Data.TaggedError('CliNoSupervisorError')<{\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly hint?: string;\n}> {}\n\n/** Internal/unexpected failure. Wraps an arbitrary cause; the cascade\n * formatter renders the inner. */\nexport class CliInternalError extends Data.TaggedError('CliInternalError')<{\n\treadonly message: string;\n\treadonly cause?: unknown;\n}> {}\n\n/** Sentinel: subcommand already pretty-rendered its failure. The\n * top-level renderer recognizes this tag and skips re-rendering, but\n * still propagates `exitCode` to the OS exit code.\n *\n * Architecture § Learnings: \"Already-reported sentinel pattern\" — the\n * marker traverses the cause structure used by the Effect runtime, so\n * the top-level `Effect.catch` sees it. */\nexport class CliAlreadyReportedError extends Data.TaggedError('CliAlreadyReportedError')<{\n\treadonly exitCode: ExitCode;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Union + sysexit projection\n// -----------------------------------------------------------------------------\n\nexport type CliError =\n\t| CliUsageError\n\t| CliConfigNotFoundError\n\t| CliConfigInvalidError\n\t| CliUnavailableError\n\t| CliSnapshotNotFoundError\n\t| CliSnapshotAmbiguousError\n\t| CliConfirmRequiredError\n\t| CliConfirmDeclinedError\n\t| CliSupervisorLiveError\n\t| CliNoSupervisorError\n\t| CliInternalError\n\t| CliAlreadyReportedError;\n\nexport const isCliError = (value: unknown): value is CliError => {\n\tif (typeof value !== 'object' || value === null) return false;\n\tconst tag = (value as { readonly _tag?: unknown })._tag;\n\tswitch (tag) {\n\t\tcase 'CliUsageError':\n\t\tcase 'CliConfigNotFoundError':\n\t\tcase 'CliConfigInvalidError':\n\t\tcase 'CliUnavailableError':\n\t\tcase 'CliSnapshotNotFoundError':\n\t\tcase 'CliSnapshotAmbiguousError':\n\t\tcase 'CliConfirmRequiredError':\n\t\tcase 'CliConfirmDeclinedError':\n\t\tcase 'CliSupervisorLiveError':\n\t\tcase 'CliNoSupervisorError':\n\t\tcase 'CliInternalError':\n\t\tcase 'CliAlreadyReportedError':\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n};\n\n/** Project a tagged error to its sysexit code. The dispatcher reads\n * this to set `process.exitCode` — there is exactly one place in the\n * surface where tag → numeric mapping happens. */\nexport const exitCodeFor = (error: CliError): ExitCode => {\n\tswitch (error._tag) {\n\t\tcase 'CliUsageError':\n\t\t\treturn XC.USAGE;\n\t\tcase 'CliConfigNotFoundError':\n\t\t\treturn XC.NO_INPUT;\n\t\tcase 'CliConfigInvalidError':\n\t\t\treturn XC.CONFIG;\n\t\tcase 'CliUnavailableError':\n\t\t\treturn XC.UNAVAILABLE;\n\t\tcase 'CliSnapshotNotFoundError':\n\t\t\treturn XC.SNAPSHOT_NOT_FOUND;\n\t\tcase 'CliSnapshotAmbiguousError':\n\t\t\treturn XC.USAGE;\n\t\tcase 'CliConfirmRequiredError':\n\t\tcase 'CliConfirmDeclinedError':\n\t\t\treturn XC.CONFIRM_REQUIRED;\n\t\tcase 'CliSupervisorLiveError':\n\t\t\treturn XC.SUPERVISOR_LIVE;\n\t\tcase 'CliNoSupervisorError':\n\t\t\treturn XC.UNAVAILABLE;\n\t\tcase 'CliInternalError':\n\t\t\treturn XC.SOFTWARE;\n\t\tcase 'CliAlreadyReportedError':\n\t\t\treturn error.exitCode;\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = error;\n\t\t\tvoid _exhaustive;\n\t\t\treturn XC.SOFTWARE;\n\t\t}\n\t}\n};\n\n/** Short single-line summary for the envelope's `error.summary`\n * field. Cascade detail lives in `error.chain[]`. */\nexport const summaryFor = (error: CliError): string => {\n\tswitch (error._tag) {\n\t\tcase 'CliUsageError':\n\t\t\treturn error.message;\n\t\tcase 'CliConfigNotFoundError':\n\t\t\treturn error.message;\n\t\tcase 'CliConfigInvalidError':\n\t\t\treturn error.message;\n\t\tcase 'CliUnavailableError':\n\t\t\treturn `${error.service} unavailable: ${error.message}`;\n\t\tcase 'CliSnapshotNotFoundError':\n\t\t\treturn `snapshot not found: ${error.snapshotRef}`;\n\t\tcase 'CliSnapshotAmbiguousError':\n\t\t\treturn `snapshot reference is ambiguous: ${error.snapshotRef}`;\n\t\tcase 'CliConfirmRequiredError':\n\t\t\treturn `${error.verb} requires confirmation`;\n\t\tcase 'CliConfirmDeclinedError':\n\t\t\treturn `${error.verb} confirmation declined`;\n\t\tcase 'CliSupervisorLiveError':\n\t\t\treturn `supervisor live for ${error.app}/${error.stack}`;\n\t\tcase 'CliNoSupervisorError':\n\t\t\treturn `no supervisor running for ${error.app}/${error.stack}`;\n\t\tcase 'CliInternalError':\n\t\t\treturn error.message;\n\t\tcase 'CliAlreadyReportedError':\n\t\t\treturn '(already reported)';\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = error;\n\t\t\tvoid _exhaustive;\n\t\t\treturn '(unknown error)';\n\t\t}\n\t}\n};\n\n/** Optional hint for the envelope's `error.hint` field. */\nexport const hintFor = (error: CliError): string | undefined => {\n\tswitch (error._tag) {\n\t\tcase 'CliUsageError':\n\t\t\treturn error.hint;\n\t\tcase 'CliSnapshotAmbiguousError':\n\t\t\treturn `use one of these ids instead: ${error.matches.join(', ')}`;\n\t\tcase 'CliUnavailableError':\n\t\tcase 'CliConfirmRequiredError':\n\t\tcase 'CliConfirmDeclinedError':\n\t\tcase 'CliSupervisorLiveError':\n\t\tcase 'CliNoSupervisorError':\n\t\t\treturn error.hint;\n\t\tcase 'CliConfigNotFoundError':\n\t\t\t// Operator-facing hint: the most common cause is running a\n\t\t\t// devstack verb in a directory whose ancestors don't contain a\n\t\t\t// `devstack.config.ts`. Two actions resolve it — either\n\t\t\t// initialize one in the current tree, or point the loader at\n\t\t\t// an explicit path. We surface both so the envelope renderer\n\t\t\t// (JSON `error.hint`, human stderr line) can render one short\n\t\t\t// line instead of forcing the caller to remember the recipe.\n\t\t\treturn 'config file not found; run `devstack init` or pass `--config <path>`';\n\t\tcase 'CliConfigInvalidError':\n\t\tcase 'CliSnapshotNotFoundError':\n\t\tcase 'CliInternalError':\n\t\tcase 'CliAlreadyReportedError':\n\t\t\treturn undefined;\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = error;\n\t\t\tvoid _exhaustive;\n\t\t\treturn undefined;\n\t\t}\n\t}\n};\n"],"mappings":";;;;;AAwBA,IAAa,gBAAb,cAAmC,KAAK,YAAY,gBAAgB,CAGjE;;;AAIH,IAAa,yBAAb,cAA4C,KAAK,YAAY,yBAAyB,CAGnF;;;AAIH,IAAa,wBAAb,cAA2C,KAAK,YAAY,wBAAwB,CAGjF;;AAGH,IAAa,sBAAb,cAAyC,KAAK,YAAY,sBAAsB,CAI7E;;;AAIH,IAAa,2BAAb,cAA8C,KAAK,YAAY,2BAA2B,CAEvF;AAEH,IAAa,4BAAb,cAA+C,KAAK,YAAY,4BAA4B,CAGzF;;;AAIH,IAAa,0BAAb,cAA6C,KAAK,YAAY,0BAA0B,CAGrF;;AAGH,IAAa,0BAAb,cAA6C,KAAK,YAAY,0BAA0B,CAGrF;;;AAIH,IAAa,yBAAb,cAA4C,KAAK,YAAY,yBAAyB,CAInF;AAMuC,KAAK,YAAY,uBAAuB;;;AAQlF,IAAa,mBAAb,cAAsC,KAAK,YAAY,mBAAmB,CAGvE;AAS0C,KAAK,YAAY,0BAA0B;AAsBxF,MAAa,cAAc,UAAsC;AAChE,KAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AAExD,SADa,MAAsC,MACnD;EACC,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,0BACJ,QAAO;EACR,QACC,QAAO;;;;;;AAOV,MAAa,eAAe,UAA8B;AACzD,SAAQ,MAAM,MAAd;EACC,KAAK,gBACJ,QAAOA,SAAG;EACX,KAAK,yBACJ,QAAOA,SAAG;EACX,KAAK,wBACJ,QAAOA,SAAG;EACX,KAAK,sBACJ,QAAOA,SAAG;EACX,KAAK,2BACJ,QAAOA,SAAG;EACX,KAAK,4BACJ,QAAOA,SAAG;EACX,KAAK;EACL,KAAK,0BACJ,QAAOA,SAAG;EACX,KAAK,yBACJ,QAAOA,SAAG;EACX,KAAK,uBACJ,QAAOA,SAAG;EACX,KAAK,mBACJ,QAAOA,SAAG;EACX,KAAK,0BACJ,QAAO,MAAM;EACd,QAGC,QAAOA,SAAG;;;;;AAOb,MAAa,cAAc,UAA4B;AACtD,SAAQ,MAAM,MAAd;EACC,KAAK,gBACJ,QAAO,MAAM;EACd,KAAK,yBACJ,QAAO,MAAM;EACd,KAAK,wBACJ,QAAO,MAAM;EACd,KAAK,sBACJ,QAAO,GAAG,MAAM,QAAQ,gBAAgB,MAAM;EAC/C,KAAK,2BACJ,QAAO,uBAAuB,MAAM;EACrC,KAAK,4BACJ,QAAO,oCAAoC,MAAM;EAClD,KAAK,0BACJ,QAAO,GAAG,MAAM,KAAK;EACtB,KAAK,0BACJ,QAAO,GAAG,MAAM,KAAK;EACtB,KAAK,yBACJ,QAAO,uBAAuB,MAAM,IAAI,GAAG,MAAM;EAClD,KAAK,uBACJ,QAAO,6BAA6B,MAAM,IAAI,GAAG,MAAM;EACxD,KAAK,mBACJ,QAAO,MAAM;EACd,KAAK,0BACJ,QAAO;EACR,QAGC,QAAO;;;;AAMV,MAAa,WAAW,UAAwC;AAC/D,SAAQ,MAAM,MAAd;EACC,KAAK,gBACJ,QAAO,MAAM;EACd,KAAK,4BACJ,QAAO,iCAAiC,MAAM,QAAQ,KAAK,KAAK;EACjE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,uBACJ,QAAO,MAAM;EACd,KAAK,yBAQJ,QAAO;EACR,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,0BACJ;EACD,QAGC"}
@@ -10,7 +10,6 @@ const ENV_VARS = {
10
10
  STATE_DIR: "DEVSTACK_STATE_DIR",
11
11
  CONFIG_PATH: "DEVSTACK_CONFIG",
12
12
  NETWORK: "DEVSTACK_NETWORK",
13
- RENDERER: "DEVSTACK_RENDERER",
14
13
  NO_COLOR: "NO_COLOR"
15
14
  };
16
15
  /** Pop the next positional argument from `rest`. Returns undefined if
@@ -1 +1 @@
1
- {"version":3,"file":"flags.mjs","names":[],"sources":["../../../src/surfaces/cli/flags.ts"],"sourcesContent":["// CLI surface — shared flag types and subcommand helpers.\n//\n// Stricli owns argv parsing in `surfaces/cli/index.ts`. This file\n// keeps the resolved flag shape and the tiny helpers used by nested\n// command implementations that still receive a typed `rest` tail\n// (currently snapshot subcommands).\n\nimport { CliUsageError } from './errors.ts';\n\n// -----------------------------------------------------------------------------\n// Global flag definitions (shared across every verb)\n// -----------------------------------------------------------------------------\n\n/** Output mode resolved from `--json` (or `DEVSTACK_JSON=1`). */\nexport type OutputMode = 'human' | 'json';\n\n/** Renderer selected by the CLI. `tui` maps to the Ink-backed live\n * dashboard; `plain` is line-oriented stderr output; `silent` mounts no\n * visible renderer. */\nexport type CliRendererMode = 'tui' | 'plain' | 'silent';\n\n/** Confirmation policy resolved from `--yes` + `--no-input` + TTY\n * state of stdin. */\nexport interface ConfirmPolicy {\n\treadonly assumeYes: boolean;\n\treadonly forbidPrompt: boolean;\n\treadonly stdinIsTty: boolean;\n}\n\n/** Bundle of command-scoped flags after Stricli parsing plus env\n * fallback resolution. */\nexport interface GlobalFlags {\n\treadonly outputMode: OutputMode;\n\treadonly app: string | undefined;\n\treadonly stack: string | undefined;\n\treadonly stateDir: string | undefined;\n\treadonly configPath: string | undefined;\n\treadonly network: string | undefined;\n\treadonly renderer: CliRendererMode | undefined;\n\treadonly dryRun: boolean;\n\treadonly confirm: ConfirmPolicy;\n\t/** Legacy field kept in the internal shape; schema is now the\n\t * explicit `devstack schema --json` command. */\n\treadonly schemaEmit: boolean;\n\t/** Verbosity bump; primarily affects logger filter. */\n\treadonly verbose: boolean;\n\t/** Print help instead of running. */\n\treadonly help: boolean;\n\t/** Print version one-liner and exit. */\n\treadonly version: boolean;\n\t/** Argv tail after the global-flag pass; subcommand parsers slice\n\t * positional arguments and verb-specific flags out of this. */\n\treadonly rest: ReadonlyArray<string>;\n}\n\n/** Environment-variable names the CLI consults. Centralized so the\n * `schema --json` command can enumerate them. */\nexport const ENV_VARS = {\n\tJSON: 'DEVSTACK_JSON',\n\tNO_INPUT: 'DEVSTACK_NO_INPUT',\n\tAPP: 'DEVSTACK_APP',\n\tSTACK: 'DEVSTACK_STACK',\n\tSTATE_DIR: 'DEVSTACK_STATE_DIR',\n\tCONFIG_PATH: 'DEVSTACK_CONFIG',\n\tNETWORK: 'DEVSTACK_NETWORK',\n\tRENDERER: 'DEVSTACK_RENDERER',\n\tNO_COLOR: 'NO_COLOR',\n} as const;\n\nexport type EnvVarName = (typeof ENV_VARS)[keyof typeof ENV_VARS];\n\n// -----------------------------------------------------------------------------\n// Subcommand-flag helpers\n// -----------------------------------------------------------------------------\n\n/** Pop the next positional argument from `rest`. Returns undefined if\n * none remain. */\nexport const takePositional = (\n\trest: ReadonlyArray<string>,\n): { readonly head: string | undefined; readonly tail: ReadonlyArray<string> } => {\n\tconst idx = rest.findIndex((tok) => !tok.startsWith('-'));\n\tif (idx === -1) return { head: undefined, tail: rest };\n\tconst tail = [...rest.slice(0, idx), ...rest.slice(idx + 1)];\n\treturn { head: rest[idx], tail };\n};\n\n/** Look up a boolean flag in `rest` by name (long form only). Returns\n * the matched flag plus the remaining tokens. */\nexport const takeBoolFlag = (\n\trest: ReadonlyArray<string>,\n\tname: string,\n): { readonly present: boolean; readonly tail: ReadonlyArray<string> } => {\n\tconst flag = `--${name}`;\n\tconst idx = rest.indexOf(flag);\n\tif (idx === -1) return { present: false, tail: rest };\n\tconst tail = [...rest.slice(0, idx), ...rest.slice(idx + 1)];\n\treturn { present: true, tail };\n};\n\n/** Look up a value flag (`--name=val` or `--name val`). */\nexport const takeValueFlag = (\n\trest: ReadonlyArray<string>,\n\tname: string,\n): { readonly value: string | undefined; readonly tail: ReadonlyArray<string> } => {\n\tconst prefix = `--${name}=`;\n\tfor (let i = 0; i < rest.length; i++) {\n\t\tconst tok = rest[i]!;\n\t\tif (tok.startsWith(prefix)) {\n\t\t\treturn {\n\t\t\t\tvalue: tok.slice(prefix.length),\n\t\t\t\ttail: [...rest.slice(0, i), ...rest.slice(i + 1)],\n\t\t\t};\n\t\t}\n\t\tif (tok === `--${name}`) {\n\t\t\tconst next = rest[i + 1];\n\t\t\tif (next === undefined || next.startsWith('-')) {\n\t\t\t\tthrow new CliUsageError({ message: `flag --${name} requires a value` });\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tvalue: next,\n\t\t\t\ttail: [...rest.slice(0, i), ...rest.slice(i + 2)],\n\t\t\t};\n\t\t}\n\t}\n\treturn { value: undefined, tail: rest };\n};\n"],"mappings":";;;;AAyDA,MAAa,WAAW;CACvB,MAAM;CACN,UAAU;CACV,KAAK;CACL,OAAO;CACP,WAAW;CACX,aAAa;CACb,SAAS;CACT,UAAU;CACV,UAAU;CACV;;;AAUD,MAAa,kBACZ,SACiF;CACjF,MAAM,MAAM,KAAK,WAAW,QAAQ,CAAC,IAAI,WAAW,IAAI,CAAC;AACzD,KAAI,QAAQ,GAAI,QAAO;EAAE,MAAM,KAAA;EAAW,MAAM;EAAM;CACtD,MAAM,OAAO,CAAC,GAAG,KAAK,MAAM,GAAG,IAAI,EAAE,GAAG,KAAK,MAAM,MAAM,EAAE,CAAC;AAC5D,QAAO;EAAE,MAAM,KAAK;EAAM;EAAM;;;AAiBjC,MAAa,iBACZ,MACA,SACkF;CAClF,MAAM,SAAS,KAAK,KAAK;AACzB,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACrC,MAAM,MAAM,KAAK;AACjB,MAAI,IAAI,WAAW,OAAO,CACzB,QAAO;GACN,OAAO,IAAI,MAAM,OAAO,OAAO;GAC/B,MAAM,CAAC,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,GAAG,KAAK,MAAM,IAAI,EAAE,CAAC;GACjD;AAEF,MAAI,QAAQ,KAAK,QAAQ;GACxB,MAAM,OAAO,KAAK,IAAI;AACtB,OAAI,SAAS,KAAA,KAAa,KAAK,WAAW,IAAI,CAC7C,OAAM,IAAI,cAAc,EAAE,SAAS,UAAU,KAAK,oBAAoB,CAAC;AAExE,UAAO;IACN,OAAO;IACP,MAAM,CAAC,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,GAAG,KAAK,MAAM,IAAI,EAAE,CAAC;IACjD;;;AAGH,QAAO;EAAE,OAAO,KAAA;EAAW,MAAM;EAAM"}
1
+ {"version":3,"file":"flags.mjs","names":[],"sources":["../../../src/surfaces/cli/flags.ts"],"sourcesContent":["// CLI surface — shared flag types and subcommand helpers.\n//\n// Stricli owns argv parsing in `surfaces/cli/index.ts`. This file\n// keeps the resolved flag shape and the tiny helpers used by nested\n// command implementations that still receive a typed `rest` tail\n// (currently snapshot subcommands).\n\nimport { CliUsageError } from './errors.ts';\n\n// -----------------------------------------------------------------------------\n// Global flag definitions (shared across every verb)\n// -----------------------------------------------------------------------------\n\n/** Output mode resolved from `--json` (or `DEVSTACK_JSON=1`). */\nexport type OutputMode = 'human' | 'json';\n\n/** Renderer selected by the CLI. `tui` maps to the Ink-backed live\n * dashboard; `plain` is line-oriented stderr output; `silent` mounts no\n * visible renderer. */\nexport type CliRendererMode = 'tui' | 'plain' | 'silent';\n\n/** Confirmation policy resolved from `--yes` + `--no-input` + TTY\n * state of stdin. */\nexport interface ConfirmPolicy {\n\treadonly assumeYes: boolean;\n\treadonly forbidPrompt: boolean;\n\treadonly stdinIsTty: boolean;\n}\n\n/** Bundle of command-scoped flags after Stricli parsing plus env\n * fallback resolution. */\nexport interface GlobalFlags {\n\treadonly outputMode: OutputMode;\n\treadonly app: string | undefined;\n\treadonly stack: string | undefined;\n\treadonly stateDir: string | undefined;\n\treadonly configPath: string | undefined;\n\treadonly network: string | undefined;\n\treadonly renderer: CliRendererMode | undefined;\n\treadonly dryRun: boolean;\n\treadonly confirm: ConfirmPolicy;\n\t/** Verbosity bump; primarily affects logger filter. */\n\treadonly verbose: boolean;\n\t/** Argv tail after the global-flag pass; subcommand parsers slice\n\t * positional arguments and verb-specific flags out of this. */\n\treadonly rest: ReadonlyArray<string>;\n}\n\n/** Environment-variable names the CLI consults. Centralized so the\n * `schema --json` command can enumerate them. */\nexport const ENV_VARS = {\n\tJSON: 'DEVSTACK_JSON',\n\tNO_INPUT: 'DEVSTACK_NO_INPUT',\n\tAPP: 'DEVSTACK_APP',\n\tSTACK: 'DEVSTACK_STACK',\n\tSTATE_DIR: 'DEVSTACK_STATE_DIR',\n\tCONFIG_PATH: 'DEVSTACK_CONFIG',\n\tNETWORK: 'DEVSTACK_NETWORK',\n\tNO_COLOR: 'NO_COLOR',\n} as const;\n\nexport type EnvVarName = (typeof ENV_VARS)[keyof typeof ENV_VARS];\n\n// -----------------------------------------------------------------------------\n// Subcommand-flag helpers\n// -----------------------------------------------------------------------------\n\n/** Pop the next positional argument from `rest`. Returns undefined if\n * none remain. */\nexport const takePositional = (\n\trest: ReadonlyArray<string>,\n): { readonly head: string | undefined; readonly tail: ReadonlyArray<string> } => {\n\tconst idx = rest.findIndex((tok) => !tok.startsWith('-'));\n\tif (idx === -1) return { head: undefined, tail: rest };\n\tconst tail = [...rest.slice(0, idx), ...rest.slice(idx + 1)];\n\treturn { head: rest[idx], tail };\n};\n\n/** Look up a value flag (`--name=val` or `--name val`). */\nexport const takeValueFlag = (\n\trest: ReadonlyArray<string>,\n\tname: string,\n): { readonly value: string | undefined; readonly tail: ReadonlyArray<string> } => {\n\tconst prefix = `--${name}=`;\n\tfor (let i = 0; i < rest.length; i++) {\n\t\tconst tok = rest[i]!;\n\t\tif (tok.startsWith(prefix)) {\n\t\t\treturn {\n\t\t\t\tvalue: tok.slice(prefix.length),\n\t\t\t\ttail: [...rest.slice(0, i), ...rest.slice(i + 1)],\n\t\t\t};\n\t\t}\n\t\tif (tok === `--${name}`) {\n\t\t\tconst next = rest[i + 1];\n\t\t\tif (next === undefined || next.startsWith('-')) {\n\t\t\t\tthrow new CliUsageError({ message: `flag --${name} requires a value` });\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tvalue: next,\n\t\t\t\ttail: [...rest.slice(0, i), ...rest.slice(i + 2)],\n\t\t\t};\n\t\t}\n\t}\n\treturn { value: undefined, tail: rest };\n};\n"],"mappings":";;;;AAkDA,MAAa,WAAW;CACvB,MAAM;CACN,UAAU;CACV,KAAK;CACL,OAAO;CACP,WAAW;CACX,aAAa;CACb,SAAS;CACT,UAAU;CACV;;;AAUD,MAAa,kBACZ,SACiF;CACjF,MAAM,MAAM,KAAK,WAAW,QAAQ,CAAC,IAAI,WAAW,IAAI,CAAC;AACzD,KAAI,QAAQ,GAAI,QAAO;EAAE,MAAM,KAAA;EAAW,MAAM;EAAM;CACtD,MAAM,OAAO,CAAC,GAAG,KAAK,MAAM,GAAG,IAAI,EAAE,GAAG,KAAK,MAAM,MAAM,EAAE,CAAC;AAC5D,QAAO;EAAE,MAAM,KAAK;EAAM;EAAM;;;AAIjC,MAAa,iBACZ,MACA,SACkF;CAClF,MAAM,SAAS,KAAK,KAAK;AACzB,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACrC,MAAM,MAAM,KAAK;AACjB,MAAI,IAAI,WAAW,OAAO,CACzB,QAAO;GACN,OAAO,IAAI,MAAM,OAAO,OAAO;GAC/B,MAAM,CAAC,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,GAAG,KAAK,MAAM,IAAI,EAAE,CAAC;GACjD;AAEF,MAAI,QAAQ,KAAK,QAAQ;GACxB,MAAM,OAAO,KAAK,IAAI;AACtB,OAAI,SAAS,KAAA,KAAa,KAAK,WAAW,IAAI,CAC7C,OAAM,IAAI,cAAc,EAAE,SAAS,UAAU,KAAK,oBAAoB,CAAC;AAExE,UAAO;IACN,OAAO;IACP,MAAM,CAAC,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,GAAG,KAAK,MAAM,IAAI,EAAE,CAAC;IACjD;;;AAGH,QAAO;EAAE,OAAO,KAAA;EAAW,MAAM;EAAM"}
@@ -1,19 +1,27 @@
1
1
  import { parseDevstackNetworkName } from "../../api/inference-network.mjs";
2
- import "./sysexits.mjs";
2
+ import { ExitCode } from "./sysexits.mjs";
3
3
  import { CliInternalError, CliUsageError, exitCodeFor } from "./errors.mjs";
4
4
  import { ENV_VARS } from "./flags.mjs";
5
5
  import { commandSchema } from "./command-tree.mjs";
6
6
  import "./envelope.mjs";
7
- import { emitFailure, nodeProcessIO } from "./output.mjs";
7
+ import { emitFailure, emitSuccess, nodeProcessIO } from "./output.mjs";
8
8
  import { runConfig } from "./commands/config.mjs";
9
9
  import { runDoctor } from "./commands/doctor.mjs";
10
10
  import { runPrune } from "./commands/prune.mjs";
11
11
  import { runSnapshot } from "./commands/snapshot.mjs";
12
12
  import { runStatus } from "./commands/status.mjs";
13
13
  import { runWipe } from "./commands/wipe.mjs";
14
+ import "./commands/index.mjs";
14
15
  import { Effect } from "effect";
16
+ import { readFileSync } from "node:fs";
15
17
  import { buildApplication, buildChoiceParser, buildCommand, buildRouteMap, run, text_en } from "@stricli/core";
16
18
  //#region src/surfaces/cli/index.ts
19
+ const readPackageVersion = () => {
20
+ const raw = readFileSync(new URL("../../../package.json", import.meta.url), "utf8");
21
+ const pkg = JSON.parse(raw);
22
+ if (typeof pkg.version !== "string") throw new Error("devstack package.json is missing a string version");
23
+ return pkg.version;
24
+ };
17
25
  const makeBufferedProcess = (env) => {
18
26
  const stdoutBuffer = [];
19
27
  const stderrBuffer = [];
@@ -138,18 +146,46 @@ const makeGlobalFlags = (ctx, flags, rest) => {
138
146
  forbidPrompt: flags.noInput === true || ctx.env[ENV_VARS.NO_INPUT] === "1",
139
147
  stdinIsTty: ctx.stdinIsTty
140
148
  },
141
- schemaEmit: false,
142
149
  verbose: flags.verbose === true,
143
- help: false,
144
- version: false,
145
150
  rest
146
151
  };
147
152
  };
148
- const setNetworkEnv = (flags) => flags.network === void 0 ? Effect.void : Effect.sync(() => {
149
- process.env[ENV_VARS.NETWORK] = flags.network;
150
- });
153
+ /**
154
+ * Bridge the CLI `--network` flag through `process.env` so config-load-time
155
+ * factory reads pick it up. This indirection is deliberate, not a leak.
156
+ *
157
+ * Why the mutation exists:
158
+ * The `deepbook()` factory (`plugins/deepbook/index.ts`) defaults its mode
159
+ * by reading `process.env.DEVSTACK_NETWORK` at config import time — before
160
+ * any flag value has reached the orchestrator. To make `--network` affect
161
+ * the same default, we must mutate the env BEFORE the user's
162
+ * `devstack.config.ts` is loaded. The chain is:
163
+ * `--network=<net>` flag → setNetworkEnv → `process.env.DEVSTACK_NETWORK`
164
+ * → `deepbook()` factory's env read at import.
165
+ *
166
+ * Why save/restore (and a scoped finalizer) matters:
167
+ * The CLI is also invoked from tests and embedded harnesses inside a single
168
+ * process. An unscoped mutation leaks across invocations: a test that runs
169
+ * `dispatch(['up','--network=testnet'])` followed by `dispatch(['up'])`
170
+ * would see the second call inherit `testnet` from the first. The
171
+ * finalizer restores the prior value (or deletes the key if it was unset)
172
+ * on success, failure, AND interrupt — `Effect.addFinalizer` guarantees
173
+ * the cleanup runs regardless of how the scope closes.
174
+ */
175
+ const setNetworkEnv = (flags) => {
176
+ if (flags.network === void 0) return Effect.void;
177
+ const next = flags.network;
178
+ return Effect.gen(function* () {
179
+ const prior = process.env[ENV_VARS.NETWORK];
180
+ process.env[ENV_VARS.NETWORK] = next;
181
+ yield* Effect.addFinalizer(() => Effect.sync(() => {
182
+ if (prior === void 0) delete process.env[ENV_VARS.NETWORK];
183
+ else process.env[ENV_VARS.NETWORK] = prior;
184
+ }));
185
+ });
186
+ };
151
187
  const runCommandEffect = async (ctx, command, flags, effect) => {
152
- const program = setNetworkEnv(flags).pipe(Effect.andThen(effect), Effect.catch((error) => emitFailure(ctx.io, flags.outputMode, {
188
+ const program = Effect.scoped(setNetworkEnv(flags).pipe(Effect.andThen(effect), Effect.catch((error) => emitFailure(ctx.io, flags.outputMode, {
153
189
  command,
154
190
  elapsedMs: 0,
155
191
  error
@@ -158,7 +194,7 @@ const runCommandEffect = async (ctx, command, flags, effect) => {
158
194
  elapsedMs: 0,
159
195
  error: new CliInternalError({ message: "unexpected internal failure" }),
160
196
  cause
161
- }).pipe(Effect.as({ exitCode: 70 }))));
197
+ }).pipe(Effect.as({ exitCode: ExitCode.SOFTWARE })))));
162
198
  const result = await Effect.runPromise(program);
163
199
  if (ctx.io.lastExitCode() === null) await Effect.runPromise(ctx.io.setExitCode(result.exitCode));
164
200
  };
@@ -251,11 +287,18 @@ const app = buildApplication(buildRouteMap({
251
287
  parameters: { flags: { json: identityFlagParams.json } },
252
288
  docs: { brief: "Emit the CLI schema" },
253
289
  func: function(flags) {
254
- const mode = outputModeFrom(flags, this.env);
255
- return Effect.runPromise(this.io.writeStdout(JSON.stringify({
256
- ...commandSchema(),
257
- outputMode: mode
258
- })).pipe(Effect.andThen(this.io.setExitCode(0))));
290
+ return runWithFlags(this, "schema", flags, [], (global) => {
291
+ const data = {
292
+ ...commandSchema(),
293
+ outputMode: global.outputMode
294
+ };
295
+ return emitSuccess(this.io, global.outputMode, {
296
+ command: "schema",
297
+ elapsedMs: 0,
298
+ data,
299
+ humanLines: [JSON.stringify(data, null, 2)]
300
+ }).pipe(Effect.as({ exitCode: 0 }));
301
+ });
259
302
  }
260
303
  }),
261
304
  snapshot: buildRouteMap({
@@ -356,7 +399,7 @@ const app = buildApplication(buildRouteMap({
356
399
  docs: { brief: "Sui development stack CLI" }
357
400
  }), {
358
401
  name: "devstack",
359
- versionInfo: { currentVersion: "0.0.0" },
402
+ versionInfo: { currentVersion: readPackageVersion() },
360
403
  scanner: { caseStyle: "allow-kebab-for-camel" },
361
404
  documentation: {
362
405
  caseStyle: "convert-camel-to-kebab",
@@ -369,10 +412,47 @@ const app = buildApplication(buildRouteMap({
369
412
  }) }
370
413
  });
371
414
  const jsonRequested = (argv, env) => env[ENV_VARS.JSON] === "1" || argv.includes("--json");
415
+ /**
416
+ * Project a `BufferedProcess.exitCode` to a sysexit code AT THE
417
+ * STRICLI-PARSE BOUNDARY ONLY.
418
+ *
419
+ * Contract: `BufferedProcess.exitCode` is mutated ONLY by Stricli's
420
+ * argv-parser when an argv parse step fails (unknown subcommand,
421
+ * malformed flag value, missing required positional). Verbs route
422
+ * their own outcomes through `ctx.io.setExitCode`, which marks
423
+ * `io.touched()` and short-circuits this projection in `dispatch`
424
+ * before `flushBufferedProcess` is reached.
425
+ *
426
+ * The historic implementation collapsed ANY non-zero `code` to
427
+ * `USAGE` (64), but that was wrong: it could only ever observe a
428
+ * Stricli parse failure (the only writer to this field), so the
429
+ * `USAGE` mapping holds by construction. Kept as a named function
430
+ * so the invariant is documented at the call site.
431
+ */
372
432
  const normalizeStricliExitCode = (code) => {
373
- if (typeof code === "number" && code !== 0) return 64;
374
- return 0;
433
+ if (typeof code === "number" && code !== 0) return ExitCode.USAGE;
434
+ return ExitCode.OK;
375
435
  };
436
+ /**
437
+ * Bridge Stricli's synchronous `StricliProcess.{stdout,stderr}.write`
438
+ * shape to our async (Effect-based) `CliIO` surface.
439
+ *
440
+ * The indirection is load-bearing for the JSON-envelope contract:
441
+ * Stricli writes argv-parse errors to stderr the moment the parser
442
+ * trips, but `--json` mode demands the failure be EMITTED as a
443
+ * structured envelope on stdout (not raw text on stderr) with exit
444
+ * code `EX_USAGE`. We can't wire Stricli's `stderr.write` directly to
445
+ * `nodeProcessIO.writeStderr` because:
446
+ *
447
+ * 1. The stderr bytes are the raw parser error text; in `--json`
448
+ * mode we need to transform them into a failure envelope.
449
+ * 2. We don't know whether the verb handler "touched" the IO
450
+ * (rendered its own envelope) until after Stricli returns.
451
+ *
452
+ * Buffering lets us delay the decision until both signals are
453
+ * available. Tests substitute a `BufferedProcess` whose buffers are
454
+ * later inspected, mirroring the prod flush behavior.
455
+ */
376
456
  const flushBufferedProcess = (process, io, argv, env) => Effect.gen(function* () {
377
457
  const exitCode = normalizeStricliExitCode(process.exitCode);
378
458
  const stdout = process.stdoutBuffer.join("");
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["runStricli"],"sources":["../../../src/surfaces/cli/index.ts"],"sourcesContent":["// CLI surface — top-level entry point.\n//\n// The public CLI is intentionally small: `up` is the attached operator\n// surface, `apply` is live-aware reconcile (publish to `up` when it is\n// running, one-shot otherwise), and every other command is offline/direct.\n\nimport {\n\tbuildApplication,\n\tbuildChoiceParser,\n\tbuildCommand,\n\tbuildRouteMap,\n\trun as runStricli,\n\ttext_en,\n\ttype CommandContext as StricliCommandContext,\n\ttype StricliProcess,\n} from '@stricli/core';\nimport { Effect } from 'effect';\n\nimport { commandSchema } from './command-tree.ts';\nimport { type CliError, CliInternalError, CliUsageError, exitCodeFor } from './errors.ts';\nimport { type CliRendererMode, ENV_VARS, type GlobalFlags, type OutputMode } from './flags.ts';\nimport { parseDevstackNetworkName } from '../../api/inference-network.ts';\nimport { type CliIO, emitFailure, nodeProcessIO } from './output.ts';\nimport {\n\ttype CommandResult,\n\ttype ConfigDeps,\n\ttype DoctorDeps,\n\ttype PruneDeps,\n\ttype PruneResourceScope,\n\ttype SnapshotDeps,\n\ttype StatusDeps,\n\ttype WipeDeps,\n\trunConfig,\n\trunDoctor,\n\trunPrune,\n\trunSnapshot,\n\trunStatus,\n\trunWipe,\n} from './commands/index.ts';\n\n// -----------------------------------------------------------------------------\n// Deps bundle\n// -----------------------------------------------------------------------------\n\nexport interface LifecycleCommandDeps {\n\treadonly run: (flags: GlobalFlags) => Effect.Effect<CommandResult, CliError>;\n}\n\nexport interface CliDeps {\n\treadonly up: LifecycleCommandDeps;\n\treadonly apply: LifecycleCommandDeps;\n\treadonly status: StatusDeps;\n\treadonly snapshot: SnapshotDeps;\n\treadonly prune: PruneDeps;\n\treadonly doctor: DoctorDeps;\n\treadonly config: ConfigDeps;\n\treadonly wipe: WipeDeps;\n}\n\nexport interface DispatchEnv {\n\treadonly argv: ReadonlyArray<string>;\n\treadonly env: Readonly<Record<string, string | undefined>>;\n\treadonly stdinIsTty: boolean;\n\treadonly io?: CliIO;\n}\n\n// -----------------------------------------------------------------------------\n// Stricli context + buffered process\n// -----------------------------------------------------------------------------\n\ninterface BufferedProcess extends StricliProcess {\n\treadonly stdoutBuffer: Array<string>;\n\treadonly stderrBuffer: Array<string>;\n\texitCode?: number | string | null;\n}\n\ninterface TrackedIO extends CliIO {\n\treadonly touched: () => boolean;\n\treadonly lastExitCode: () => number | null;\n}\n\ninterface DevstackCliContext extends StricliCommandContext {\n\treadonly deps: CliDeps;\n\treadonly env: Readonly<Record<string, string | undefined>>;\n\treadonly stdinIsTty: boolean;\n\treadonly io: TrackedIO;\n\treadonly process: BufferedProcess;\n}\n\nconst makeBufferedProcess = (\n\tenv: Readonly<Record<string, string | undefined>>,\n): BufferedProcess => {\n\tconst stdoutBuffer: Array<string> = [];\n\tconst stderrBuffer: Array<string> = [];\n\treturn {\n\t\tstdoutBuffer,\n\t\tstderrBuffer,\n\t\tenv,\n\t\tstdout: {\n\t\t\twrite: (str) => {\n\t\t\t\tstdoutBuffer.push(str);\n\t\t\t},\n\t\t},\n\t\tstderr: {\n\t\t\twrite: (str) => {\n\t\t\t\tstderrBuffer.push(str);\n\t\t\t},\n\t\t},\n\t};\n};\n\nconst trackIO = (io: CliIO): TrackedIO => {\n\tlet touched = false;\n\tlet lastExitCode: number | null = null;\n\treturn {\n\t\ttouched: () => touched,\n\t\tlastExitCode: () => lastExitCode,\n\t\twriteStdout: (line) =>\n\t\t\tio.writeStdout(line).pipe(\n\t\t\t\tEffect.tap(() =>\n\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\ttouched = true;\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\twriteStderr: (line) =>\n\t\t\tio.writeStderr(line).pipe(\n\t\t\t\tEffect.tap(() =>\n\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\ttouched = true;\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\tsetExitCode: (code) =>\n\t\t\tio.setExitCode(code).pipe(\n\t\t\t\tEffect.tap(() =>\n\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\ttouched = true;\n\t\t\t\t\t\tlastExitCode = code;\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// Flag models\n// -----------------------------------------------------------------------------\n\ninterface IdentityFlags {\n\treadonly json?: boolean;\n\treadonly app?: string;\n\treadonly stack?: string;\n\treadonly stateDir?: string;\n\treadonly verbose?: boolean;\n}\n\ninterface ConfigFlags extends IdentityFlags {\n\treadonly config?: string;\n\treadonly network?: string;\n}\n\ninterface UpFlags extends ConfigFlags {\n\treadonly renderer?: CliRendererMode;\n}\n\ninterface DestructiveFlags extends IdentityFlags {\n\treadonly dryRun?: boolean;\n\treadonly yes?: boolean;\n\treadonly noInput?: boolean;\n}\n\ninterface ConfirmFlags extends IdentityFlags {\n\treadonly yes?: boolean;\n\treadonly noInput?: boolean;\n}\n\ninterface PruneFlags {\n\treadonly json?: boolean;\n\treadonly stateDir?: string;\n\treadonly verbose?: boolean;\n\treadonly dryRun?: boolean;\n\treadonly yes?: boolean;\n\treadonly noInput?: boolean;\n\treadonly list?: boolean;\n\treadonly all?: boolean;\n\treadonly noContainers?: boolean;\n\treadonly noNetworks?: boolean;\n\treadonly noVolumes?: boolean;\n\treadonly includeImages?: boolean;\n}\n\ninterface SnapshotSaveFlags extends ConfigFlags {\n\treadonly name?: string;\n}\n\nconst textParser = (input: string): string => input;\n\nconst stringFlag = (brief: string, placeholder: string) =>\n\t({\n\t\tkind: 'parsed',\n\t\tparse: textParser,\n\t\toptional: true,\n\t\tplaceholder,\n\t\tbrief,\n\t}) as const;\n\nconst boolFlag = (brief: string) =>\n\t({\n\t\tkind: 'boolean',\n\t\toptional: true,\n\t\tbrief,\n\t}) as const;\n\nconst identityFlagParams = {\n\tjson: boolFlag('Emit JSON envelope output'),\n\tapp: stringFlag('Override app name', 'name'),\n\tstack: stringFlag('Override stack name', 'name'),\n\tstateDir: stringFlag('Override state directory', 'path'),\n\tverbose: boolFlag('Enable more verbose logging'),\n} as const;\n\nconst configFlagParams = {\n\t...identityFlagParams,\n\tconfig: stringFlag('Override devstack.config.ts path', 'path'),\n\tnetwork: stringFlag('Override network before config import', 'name'),\n} as const;\n\nconst destructiveFlagParams = {\n\t...identityFlagParams,\n\tdryRun: boolFlag('Skip mutating effects'),\n\tyes: boolFlag('Assume yes on prompts'),\n\tnoInput: boolFlag('Forbid prompts'),\n} as const;\n\nconst globalMaintenanceFlagParams = {\n\tjson: identityFlagParams.json,\n\tstateDir: identityFlagParams.stateDir,\n\tverbose: identityFlagParams.verbose,\n} as const;\n\nconst confirmFlagParams = {\n\t...identityFlagParams,\n\tyes: boolFlag('Assume yes on prompts'),\n\tnoInput: boolFlag('Forbid prompts'),\n} as const;\n\nconst pruneFlagParams = {\n\t...globalMaintenanceFlagParams,\n\tdryRun: destructiveFlagParams.dryRun,\n\tyes: destructiveFlagParams.yes,\n\tnoInput: destructiveFlagParams.noInput,\n\tlist: boolFlag('List devstack-labelled Docker resources without pruning'),\n\tall: boolFlag('Prune every idle non-shared resource group'),\n\tnoContainers: boolFlag('Do not remove containers'),\n\tnoNetworks: boolFlag('Do not remove networks'),\n\tnoVolumes: boolFlag('Do not remove volumes'),\n\tincludeImages: boolFlag('Also remove devstack-labelled images for selected groups'),\n} as const;\n\nconst rendererParser = buildChoiceParser(['tui', 'plain', 'silent'] as const);\n\nconst outputModeFrom = (\n\tflags: Pick<IdentityFlags, 'json'>,\n\tenv: Readonly<Record<string, string | undefined>>,\n): OutputMode => (flags.json === true || env[ENV_VARS.JSON] === '1' ? 'json' : 'human');\n\nconst pruneResourcesFromFlags = (flags: PruneFlags): PruneResourceScope => ({\n\tcontainers: flags.noContainers !== true,\n\tnetworks: flags.noNetworks !== true,\n\tvolumes: flags.noVolumes !== true,\n\timages: flags.includeImages === true,\n});\n\nconst optionalEnv = (\n\tvalue: string | undefined,\n\tenv: Readonly<Record<string, string | undefined>>,\n\tkey: string,\n): string | undefined => value ?? env[key];\n\nconst makeGlobalFlags = (\n\tctx: DevstackCliContext,\n\tflags: IdentityFlags & Partial<ConfigFlags & UpFlags & DestructiveFlags>,\n\trest: ReadonlyArray<string>,\n): GlobalFlags => {\n\tconst networkRaw = optionalEnv(flags.network, ctx.env, ENV_VARS.NETWORK);\n\tlet network: string | undefined;\n\tif (networkRaw !== undefined) {\n\t\ttry {\n\t\t\tnetwork = parseDevstackNetworkName(\n\t\t\t\tnetworkRaw,\n\t\t\t\tflags.network === undefined ? ENV_VARS.NETWORK : '--network',\n\t\t\t);\n\t\t} catch (cause) {\n\t\t\tthrow cause instanceof Error\n\t\t\t\t? new CliUsageError({ message: cause.message })\n\t\t\t\t: new CliInternalError({ message: 'failed to parse network flag', cause });\n\t\t}\n\t}\n\treturn {\n\t\toutputMode: outputModeFrom(flags, ctx.env),\n\t\tapp: optionalEnv(flags.app, ctx.env, ENV_VARS.APP),\n\t\tstack: optionalEnv(flags.stack, ctx.env, ENV_VARS.STACK),\n\t\tstateDir: optionalEnv(flags.stateDir, ctx.env, ENV_VARS.STATE_DIR),\n\t\tconfigPath: optionalEnv(flags.config, ctx.env, ENV_VARS.CONFIG_PATH),\n\t\tnetwork,\n\t\trenderer: flags.renderer,\n\t\tdryRun: flags.dryRun === true,\n\t\tconfirm: {\n\t\t\tassumeYes: flags.yes === true,\n\t\t\tforbidPrompt: flags.noInput === true || ctx.env[ENV_VARS.NO_INPUT] === '1',\n\t\t\tstdinIsTty: ctx.stdinIsTty,\n\t\t},\n\t\tschemaEmit: false,\n\t\tverbose: flags.verbose === true,\n\t\thelp: false,\n\t\tversion: false,\n\t\trest,\n\t};\n};\n\nconst setNetworkEnv = (flags: GlobalFlags): Effect.Effect<void> =>\n\tflags.network === undefined\n\t\t? Effect.void\n\t\t: Effect.sync(() => {\n\t\t\t\tprocess.env[ENV_VARS.NETWORK] = flags.network;\n\t\t\t});\n\n// -----------------------------------------------------------------------------\n// Command execution helpers\n// -----------------------------------------------------------------------------\n\nconst runCommandEffect = async (\n\tctx: DevstackCliContext,\n\tcommand: string,\n\tflags: GlobalFlags,\n\teffect: Effect.Effect<CommandResult, CliError>,\n): Promise<void> => {\n\tconst program = setNetworkEnv(flags).pipe(\n\t\tEffect.andThen(effect),\n\t\tEffect.catch((error: CliError) =>\n\t\t\temitFailure(ctx.io, flags.outputMode, {\n\t\t\t\tcommand,\n\t\t\t\telapsedMs: 0,\n\t\t\t\terror,\n\t\t\t}).pipe(Effect.as({ exitCode: exitCodeFor(error) })),\n\t\t),\n\t\tEffect.catchCause((cause) =>\n\t\t\temitFailure(ctx.io, flags.outputMode, {\n\t\t\t\tcommand,\n\t\t\t\telapsedMs: 0,\n\t\t\t\terror: new CliInternalError({ message: 'unexpected internal failure' }),\n\t\t\t\tcause,\n\t\t\t}).pipe(Effect.as({ exitCode: 70 })),\n\t\t),\n\t);\n\tconst result = await Effect.runPromise(program);\n\tif (ctx.io.lastExitCode() === null) {\n\t\tawait Effect.runPromise(ctx.io.setExitCode(result.exitCode));\n\t}\n};\n\nconst runWithFlags = async (\n\tctx: DevstackCliContext,\n\tcommand: string,\n\trawFlags: IdentityFlags & Partial<ConfigFlags & UpFlags & DestructiveFlags>,\n\trest: ReadonlyArray<string>,\n\teffect: (flags: GlobalFlags) => Effect.Effect<CommandResult, CliError>,\n): Promise<void> => {\n\tlet flags: GlobalFlags;\n\ttry {\n\t\tflags = makeGlobalFlags(ctx, rawFlags, rest);\n\t} catch (cause) {\n\t\tconst error =\n\t\t\tcause instanceof CliUsageError\n\t\t\t\t? cause\n\t\t\t\t: new CliInternalError({ message: 'failed to resolve CLI flags', cause });\n\t\tconst mode = outputModeFrom(rawFlags, ctx.env);\n\t\tawait Effect.runPromise(\n\t\t\temitFailure(ctx.io, mode, {\n\t\t\t\tcommand,\n\t\t\t\telapsedMs: 0,\n\t\t\t\terror,\n\t\t\t}),\n\t\t);\n\t\treturn;\n\t}\n\tawait runCommandEffect(ctx, command, flags, effect(flags));\n};\n\nconst requiredPositional = (placeholder: string, brief: string) =>\n\t({\n\t\tparse: textParser,\n\t\tplaceholder,\n\t\tbrief,\n\t}) as const;\n\nconst optionalPositional = (placeholder: string, brief: string) =>\n\t({\n\t\tparse: textParser,\n\t\tplaceholder,\n\t\tbrief,\n\t\toptional: true as const,\n\t}) as const;\n\n// -----------------------------------------------------------------------------\n// Commands\n// -----------------------------------------------------------------------------\n\nconst upCommand = buildCommand<UpFlags, [], DevstackCliContext>({\n\tparameters: {\n\t\tflags: {\n\t\t\t...configFlagParams,\n\t\t\trenderer: {\n\t\t\t\tkind: 'parsed',\n\t\t\t\tparse: rendererParser,\n\t\t\t\toptional: true,\n\t\t\t\tplaceholder: 'tui|plain|silent',\n\t\t\t\tbrief: 'Select the attached renderer',\n\t\t\t},\n\t\t},\n\t},\n\tdocs: {\n\t\tbrief: 'Boot a stack and stay attached until interrupted',\n\t},\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'up', flags, [], (global) => this.deps.up.run(global));\n\t},\n});\n\nconst applyCommand = buildCommand<ConfigFlags, [], DevstackCliContext>({\n\tparameters: { flags: configFlagParams },\n\tdocs: {\n\t\tbrief: 'Reconcile a live stack or run one-shot setup',\n\t},\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'apply', flags, [], (global) => this.deps.apply.run(global));\n\t},\n});\n\nconst statusCommand = buildCommand<IdentityFlags, [], DevstackCliContext>({\n\tparameters: { flags: identityFlagParams },\n\tdocs: { brief: 'Show the persisted stack projection' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'status', flags, [], (global) =>\n\t\t\trunStatus(this.deps.status, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst doctorCommand = buildCommand<IdentityFlags, [], DevstackCliContext>({\n\tparameters: { flags: identityFlagParams },\n\tdocs: { brief: 'Run host and stack preflight checks' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'doctor', flags, [], (global) =>\n\t\t\trunDoctor(this.deps.doctor, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst configCommand = buildCommand<ConfigFlags, [], DevstackCliContext>({\n\tparameters: { flags: configFlagParams },\n\tdocs: { brief: 'Print resolved config inputs' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'config', flags, [], (global) =>\n\t\t\trunConfig(this.deps.config, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst schemaCommand = buildCommand<Pick<IdentityFlags, 'json'>, [], DevstackCliContext>({\n\tparameters: { flags: { json: identityFlagParams.json } },\n\tdocs: { brief: 'Emit the CLI schema' },\n\tfunc: function (flags) {\n\t\tconst mode = outputModeFrom(flags, this.env);\n\t\treturn Effect.runPromise(\n\t\t\tthis.io\n\t\t\t\t.writeStdout(JSON.stringify({ ...commandSchema(), outputMode: mode }))\n\t\t\t\t.pipe(Effect.andThen(this.io.setExitCode(0))),\n\t\t);\n\t},\n});\n\nconst snapshotSaveCommand = buildCommand<\n\tSnapshotSaveFlags,\n\t[string | undefined],\n\tDevstackCliContext\n>({\n\tparameters: {\n\t\tflags: {\n\t\t\t...configFlagParams,\n\t\t\tname: stringFlag('Human-readable snapshot name', 'name'),\n\t\t},\n\t\tpositional: { kind: 'tuple', parameters: [optionalPositional('name', 'snapshot name')] },\n\t},\n\tdocs: { brief: 'Capture a snapshot' },\n\tfunc: function (flags, snapshotName) {\n\t\tconst rest = [\n\t\t\t'save',\n\t\t\t...(snapshotName === undefined ? [] : [snapshotName]),\n\t\t\t...(flags.name === undefined ? [] : ['--name', flags.name]),\n\t\t];\n\t\treturn runWithFlags(this, 'snapshot save', flags, rest, (global) =>\n\t\t\trunSnapshot(this.deps.snapshot, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst snapshotRestoreCommand = buildCommand<ConfirmFlags, [string], DevstackCliContext>({\n\tparameters: {\n\t\tflags: confirmFlagParams,\n\t\tpositional: {\n\t\t\tkind: 'tuple',\n\t\t\tparameters: [requiredPositional('name-or-id', 'snapshot name or id')],\n\t\t},\n\t},\n\tdocs: { brief: 'Restore a snapshot' },\n\tfunc: function (flags, snapshotRef) {\n\t\treturn runWithFlags(this, 'snapshot restore', flags, ['restore', snapshotRef], (global) =>\n\t\t\trunSnapshot(this.deps.snapshot, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst snapshotListCommand = buildCommand<IdentityFlags, [], DevstackCliContext>({\n\tparameters: { flags: identityFlagParams },\n\tdocs: { brief: 'List snapshots' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'snapshot list', flags, ['list'], (global) =>\n\t\t\trunSnapshot(this.deps.snapshot, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst snapshotDeleteCommand = buildCommand<ConfirmFlags, [string], DevstackCliContext>({\n\tparameters: {\n\t\tflags: confirmFlagParams,\n\t\tpositional: {\n\t\t\tkind: 'tuple',\n\t\t\tparameters: [requiredPositional('name-or-id', 'snapshot name or id')],\n\t\t},\n\t},\n\tdocs: { brief: 'Delete a snapshot' },\n\tfunc: function (flags, snapshotRef) {\n\t\treturn runWithFlags(this, 'snapshot delete', flags, ['delete', snapshotRef], (global) =>\n\t\t\trunSnapshot(this.deps.snapshot, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst snapshotCommands = buildRouteMap({\n\troutes: {\n\t\tsave: snapshotSaveCommand,\n\t\trestore: snapshotRestoreCommand,\n\t\tlist: snapshotListCommand,\n\t\tdelete: snapshotDeleteCommand,\n\t},\n\tdocs: { brief: 'Capture, restore, list, or delete stack snapshots' },\n});\n\nconst pruneCommand = buildCommand<PruneFlags, [], DevstackCliContext>({\n\tparameters: { flags: pruneFlagParams },\n\tdocs: { brief: 'Inventory and prune devstack-labelled Docker resources' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'prune', flags, [], (global) =>\n\t\t\trunPrune(\n\t\t\t\tthis.deps.prune,\n\t\t\t\t{ flags: global, io: this.io },\n\t\t\t\t{\n\t\t\t\t\tmode: flags.list === true ? 'list' : flags.all === true ? 'all' : 'auto',\n\t\t\t\t\tresources: pruneResourcesFromFlags(flags),\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\t},\n});\n\nconst wipeCommand = buildCommand<DestructiveFlags, [], DevstackCliContext>({\n\tparameters: { flags: destructiveFlagParams },\n\tdocs: { brief: 'Destroy all state for the selected stack' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'wipe', flags, [], (global) =>\n\t\t\trunWipe(this.deps.wipe, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst root = buildRouteMap({\n\troutes: {\n\t\tup: upCommand,\n\t\tapply: applyCommand,\n\t\tstatus: statusCommand,\n\t\tdoctor: doctorCommand,\n\t\tconfig: configCommand,\n\t\tschema: schemaCommand,\n\t\tsnapshot: snapshotCommands,\n\t\tprune: pruneCommand,\n\t\twipe: wipeCommand,\n\t},\n\tdocs: {\n\t\tbrief: 'Sui development stack CLI',\n\t},\n});\n\nconst app = buildApplication(root, {\n\tname: 'devstack',\n\tversionInfo: { currentVersion: '0.0.0' },\n\tscanner: { caseStyle: 'allow-kebab-for-camel' },\n\tdocumentation: {\n\t\tcaseStyle: 'convert-camel-to-kebab',\n\t\tdisableAnsiColor: true,\n\t\tonlyRequiredInUsageLine: true,\n\t},\n\tlocalization: {\n\t\tloadText: () => ({\n\t\t\t...text_en,\n\t\t\texceptionWhileParsingArguments: (error) =>\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t}),\n\t},\n});\n\nconst jsonRequested = (\n\targv: ReadonlyArray<string>,\n\tenv: Readonly<Record<string, string | undefined>>,\n): boolean => env[ENV_VARS.JSON] === '1' || argv.includes('--json');\n\nconst normalizeStricliExitCode = (code: number | string | null | undefined): number => {\n\tif (typeof code === 'number' && code !== 0) return 64;\n\treturn 0;\n};\n\nconst flushBufferedProcess = (\n\tprocess: BufferedProcess,\n\tio: CliIO,\n\targv: ReadonlyArray<string>,\n\tenv: Readonly<Record<string, string | undefined>>,\n): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tconst exitCode = normalizeStricliExitCode(process.exitCode);\n\t\tconst stdout = process.stdoutBuffer.join('');\n\t\tconst stderr = process.stderrBuffer.join('').trim();\n\t\tif (exitCode !== 0 && jsonRequested(argv, env)) {\n\t\t\tyield* emitFailure(io, 'json', {\n\t\t\t\tcommand: '(parse)',\n\t\t\t\telapsedMs: 0,\n\t\t\t\terror: new CliUsageError({\n\t\t\t\t\tmessage: stderr.length > 0 ? stderr : 'invalid command line',\n\t\t\t\t}),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (stdout.length > 0)\n\t\t\tyield* io.writeStdout(stdout.endsWith('\\n') ? stdout.slice(0, -1) : stdout);\n\t\tif (stderr.length > 0) yield* io.writeStderr(stderr);\n\t\tyield* io.setExitCode(exitCode);\n\t});\n\nexport const dispatch = (deps: CliDeps, dispatchEnv: DispatchEnv): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tconst io = trackIO(dispatchEnv.io ?? nodeProcessIO);\n\t\tconst process = makeBufferedProcess(dispatchEnv.env);\n\t\tconst ctx: DevstackCliContext = {\n\t\t\tprocess,\n\t\t\tdeps,\n\t\t\tenv: dispatchEnv.env,\n\t\t\tstdinIsTty: dispatchEnv.stdinIsTty,\n\t\t\tio,\n\t\t};\n\t\tyield* Effect.tryPromise({\n\t\t\ttry: () => runStricli(app, dispatchEnv.argv, ctx),\n\t\t\tcatch: (cause) => new CliInternalError({ message: 'CLI dispatcher failed', cause }),\n\t\t}).pipe(\n\t\t\tEffect.catch((error: CliError) =>\n\t\t\t\temitFailure(io, jsonRequested(dispatchEnv.argv, dispatchEnv.env) ? 'json' : 'human', {\n\t\t\t\t\tcommand: '(dispatch)',\n\t\t\t\t\telapsedMs: 0,\n\t\t\t\t\terror,\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tif (!io.touched()) {\n\t\t\tyield* flushBufferedProcess(process, io, dispatchEnv.argv, dispatchEnv.env);\n\t\t}\n\t});\n\n// -----------------------------------------------------------------------------\n// Re-exports\n// -----------------------------------------------------------------------------\n\nexport type { CliIO } from './output.ts';\nexport type { GlobalFlags } from './flags.ts';\nexport {\n\tCOMMAND_TREE,\n\tcommandSchema,\n\tformatCommandHelp,\n\tVERBS,\n\ttype Verb,\n} from './command-tree.ts';\nexport type { Envelope, EnvelopeError } from './envelope.ts';\nexport {\n\tENVELOPE_SCHEMA_VERSION,\n\tfailureEnvelope,\n\tstreamingEvent,\n\tsuccessEnvelope,\n\ttype StreamingEvent,\n} from './envelope.ts';\nexport { ExitCode, exitCodeName } from './sysexits.ts';\nexport {\n\ttype CliError,\n\tCliAlreadyReportedError,\n\tCliConfigInvalidError,\n\tCliConfigNotFoundError,\n\tCliConfirmDeclinedError,\n\tCliConfirmRequiredError,\n\tCliInternalError,\n\tCliSnapshotNotFoundError,\n\tCliSupervisorLiveError,\n\tCliUnavailableError,\n\tCliUsageError,\n} from './errors.ts';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["runStricli"],"sources":["../../../src/surfaces/cli/index.ts"],"sourcesContent":["// CLI surface — top-level entry point.\n//\n// The public CLI is intentionally small: `up` is the attached operator\n// surface, `apply` is live-aware reconcile (publish to `up` when it is\n// running, one-shot otherwise), and every other command is offline/direct.\n\nimport {\n\tbuildApplication,\n\tbuildChoiceParser,\n\tbuildCommand,\n\tbuildRouteMap,\n\trun as runStricli,\n\ttext_en,\n\ttype CommandContext as StricliCommandContext,\n\ttype StricliProcess,\n} from '@stricli/core';\nimport { Effect, type Scope } from 'effect';\nimport { readFileSync } from 'node:fs';\n\nimport { commandSchema } from './command-tree.ts';\nimport { type CliError, CliInternalError, CliUsageError, exitCodeFor } from './errors.ts';\nimport { type CliRendererMode, ENV_VARS, type GlobalFlags, type OutputMode } from './flags.ts';\nimport { ExitCode } from './sysexits.ts';\nimport { parseDevstackNetworkName } from '../../api/inference-network.ts';\nimport { type CliIO, emitFailure, emitSuccess, nodeProcessIO } from './output.ts';\nimport {\n\ttype CommandResult,\n\ttype ConfigDeps,\n\ttype DoctorDeps,\n\ttype PruneDeps,\n\ttype PruneResourceScope,\n\ttype SnapshotDeps,\n\ttype StatusDeps,\n\ttype WipeDeps,\n\trunConfig,\n\trunDoctor,\n\trunPrune,\n\trunSnapshot,\n\trunStatus,\n\trunWipe,\n} from './commands/index.ts';\n\nconst readPackageVersion = (): string => {\n\tconst raw = readFileSync(new URL('../../../package.json', import.meta.url), 'utf8');\n\tconst pkg = JSON.parse(raw) as { readonly version?: unknown };\n\tif (typeof pkg.version !== 'string') {\n\t\tthrow new Error('devstack package.json is missing a string version');\n\t}\n\treturn pkg.version;\n};\n\n// -----------------------------------------------------------------------------\n// Deps bundle\n// -----------------------------------------------------------------------------\n\nexport interface LifecycleCommandDeps {\n\treadonly run: (flags: GlobalFlags) => Effect.Effect<CommandResult, CliError>;\n}\n\nexport interface CliDeps {\n\treadonly up: LifecycleCommandDeps;\n\treadonly apply: LifecycleCommandDeps;\n\treadonly status: StatusDeps;\n\treadonly snapshot: SnapshotDeps;\n\treadonly prune: PruneDeps;\n\treadonly doctor: DoctorDeps;\n\treadonly config: ConfigDeps;\n\treadonly wipe: WipeDeps;\n}\n\nexport interface DispatchEnv {\n\treadonly argv: ReadonlyArray<string>;\n\treadonly env: Readonly<Record<string, string | undefined>>;\n\treadonly stdinIsTty: boolean;\n\treadonly io?: CliIO;\n}\n\n// -----------------------------------------------------------------------------\n// Stricli context + buffered process\n// -----------------------------------------------------------------------------\n\ninterface BufferedProcess extends StricliProcess {\n\treadonly stdoutBuffer: Array<string>;\n\treadonly stderrBuffer: Array<string>;\n\texitCode?: number | string | null;\n}\n\ninterface TrackedIO extends CliIO {\n\treadonly touched: () => boolean;\n\treadonly lastExitCode: () => number | null;\n}\n\ninterface DevstackCliContext extends StricliCommandContext {\n\treadonly deps: CliDeps;\n\treadonly env: Readonly<Record<string, string | undefined>>;\n\treadonly stdinIsTty: boolean;\n\treadonly io: TrackedIO;\n\treadonly process: BufferedProcess;\n}\n\nconst makeBufferedProcess = (\n\tenv: Readonly<Record<string, string | undefined>>,\n): BufferedProcess => {\n\tconst stdoutBuffer: Array<string> = [];\n\tconst stderrBuffer: Array<string> = [];\n\treturn {\n\t\tstdoutBuffer,\n\t\tstderrBuffer,\n\t\tenv,\n\t\tstdout: {\n\t\t\twrite: (str) => {\n\t\t\t\tstdoutBuffer.push(str);\n\t\t\t},\n\t\t},\n\t\tstderr: {\n\t\t\twrite: (str) => {\n\t\t\t\tstderrBuffer.push(str);\n\t\t\t},\n\t\t},\n\t};\n};\n\nconst trackIO = (io: CliIO): TrackedIO => {\n\tlet touched = false;\n\tlet lastExitCode: number | null = null;\n\treturn {\n\t\ttouched: () => touched,\n\t\tlastExitCode: () => lastExitCode,\n\t\twriteStdout: (line) =>\n\t\t\tio.writeStdout(line).pipe(\n\t\t\t\tEffect.tap(() =>\n\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\ttouched = true;\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\twriteStderr: (line) =>\n\t\t\tio.writeStderr(line).pipe(\n\t\t\t\tEffect.tap(() =>\n\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\ttouched = true;\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\tsetExitCode: (code) =>\n\t\t\tio.setExitCode(code).pipe(\n\t\t\t\tEffect.tap(() =>\n\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\ttouched = true;\n\t\t\t\t\t\tlastExitCode = code;\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// Flag models\n// -----------------------------------------------------------------------------\n\ninterface IdentityFlags {\n\treadonly json?: boolean;\n\treadonly app?: string;\n\treadonly stack?: string;\n\treadonly stateDir?: string;\n\treadonly verbose?: boolean;\n}\n\ninterface ConfigFlags extends IdentityFlags {\n\treadonly config?: string;\n\treadonly network?: string;\n}\n\ninterface UpFlags extends ConfigFlags {\n\treadonly renderer?: CliRendererMode;\n}\n\ninterface DestructiveFlags extends IdentityFlags {\n\treadonly dryRun?: boolean;\n\treadonly yes?: boolean;\n\treadonly noInput?: boolean;\n}\n\ninterface ConfirmFlags extends IdentityFlags {\n\treadonly yes?: boolean;\n\treadonly noInput?: boolean;\n}\n\ninterface PruneFlags {\n\treadonly json?: boolean;\n\treadonly stateDir?: string;\n\treadonly verbose?: boolean;\n\treadonly dryRun?: boolean;\n\treadonly yes?: boolean;\n\treadonly noInput?: boolean;\n\treadonly list?: boolean;\n\treadonly all?: boolean;\n\treadonly noContainers?: boolean;\n\treadonly noNetworks?: boolean;\n\treadonly noVolumes?: boolean;\n\treadonly includeImages?: boolean;\n}\n\ninterface SnapshotSaveFlags extends ConfigFlags {\n\treadonly name?: string;\n}\n\nconst textParser = (input: string): string => input;\n\nconst stringFlag = (brief: string, placeholder: string) =>\n\t({\n\t\tkind: 'parsed',\n\t\tparse: textParser,\n\t\toptional: true,\n\t\tplaceholder,\n\t\tbrief,\n\t}) as const;\n\nconst boolFlag = (brief: string) =>\n\t({\n\t\tkind: 'boolean',\n\t\toptional: true,\n\t\tbrief,\n\t}) as const;\n\nconst identityFlagParams = {\n\tjson: boolFlag('Emit JSON envelope output'),\n\tapp: stringFlag('Override app name', 'name'),\n\tstack: stringFlag('Override stack name', 'name'),\n\tstateDir: stringFlag('Override state directory', 'path'),\n\tverbose: boolFlag('Enable more verbose logging'),\n} as const;\n\nconst configFlagParams = {\n\t...identityFlagParams,\n\tconfig: stringFlag('Override devstack.config.ts path', 'path'),\n\tnetwork: stringFlag('Override network before config import', 'name'),\n} as const;\n\nconst destructiveFlagParams = {\n\t...identityFlagParams,\n\tdryRun: boolFlag('Skip mutating effects'),\n\tyes: boolFlag('Assume yes on prompts'),\n\tnoInput: boolFlag('Forbid prompts'),\n} as const;\n\nconst globalMaintenanceFlagParams = {\n\tjson: identityFlagParams.json,\n\tstateDir: identityFlagParams.stateDir,\n\tverbose: identityFlagParams.verbose,\n} as const;\n\nconst confirmFlagParams = {\n\t...identityFlagParams,\n\tyes: boolFlag('Assume yes on prompts'),\n\tnoInput: boolFlag('Forbid prompts'),\n} as const;\n\nconst pruneFlagParams = {\n\t...globalMaintenanceFlagParams,\n\tdryRun: destructiveFlagParams.dryRun,\n\tyes: destructiveFlagParams.yes,\n\tnoInput: destructiveFlagParams.noInput,\n\tlist: boolFlag('List devstack-labelled Docker resources without pruning'),\n\tall: boolFlag('Prune every idle non-shared resource group'),\n\tnoContainers: boolFlag('Do not remove containers'),\n\tnoNetworks: boolFlag('Do not remove networks'),\n\tnoVolumes: boolFlag('Do not remove volumes'),\n\tincludeImages: boolFlag('Also remove devstack-labelled images for selected groups'),\n} as const;\n\nconst rendererParser = buildChoiceParser(['tui', 'plain', 'silent'] as const);\n\nconst outputModeFrom = (\n\tflags: Pick<IdentityFlags, 'json'>,\n\tenv: Readonly<Record<string, string | undefined>>,\n): OutputMode => (flags.json === true || env[ENV_VARS.JSON] === '1' ? 'json' : 'human');\n\nconst pruneResourcesFromFlags = (flags: PruneFlags): PruneResourceScope => ({\n\tcontainers: flags.noContainers !== true,\n\tnetworks: flags.noNetworks !== true,\n\tvolumes: flags.noVolumes !== true,\n\timages: flags.includeImages === true,\n});\n\nconst optionalEnv = (\n\tvalue: string | undefined,\n\tenv: Readonly<Record<string, string | undefined>>,\n\tkey: string,\n): string | undefined => value ?? env[key];\n\nconst makeGlobalFlags = (\n\tctx: DevstackCliContext,\n\tflags: IdentityFlags & Partial<ConfigFlags & UpFlags & DestructiveFlags>,\n\trest: ReadonlyArray<string>,\n): GlobalFlags => {\n\tconst networkRaw = optionalEnv(flags.network, ctx.env, ENV_VARS.NETWORK);\n\tlet network: string | undefined;\n\tif (networkRaw !== undefined) {\n\t\ttry {\n\t\t\tnetwork = parseDevstackNetworkName(\n\t\t\t\tnetworkRaw,\n\t\t\t\tflags.network === undefined ? ENV_VARS.NETWORK : '--network',\n\t\t\t);\n\t\t} catch (cause) {\n\t\t\tthrow cause instanceof Error\n\t\t\t\t? new CliUsageError({ message: cause.message })\n\t\t\t\t: new CliInternalError({ message: 'failed to parse network flag', cause });\n\t\t}\n\t}\n\treturn {\n\t\toutputMode: outputModeFrom(flags, ctx.env),\n\t\tapp: optionalEnv(flags.app, ctx.env, ENV_VARS.APP),\n\t\tstack: optionalEnv(flags.stack, ctx.env, ENV_VARS.STACK),\n\t\tstateDir: optionalEnv(flags.stateDir, ctx.env, ENV_VARS.STATE_DIR),\n\t\tconfigPath: optionalEnv(flags.config, ctx.env, ENV_VARS.CONFIG_PATH),\n\t\tnetwork,\n\t\trenderer: flags.renderer,\n\t\tdryRun: flags.dryRun === true,\n\t\tconfirm: {\n\t\t\tassumeYes: flags.yes === true,\n\t\t\tforbidPrompt: flags.noInput === true || ctx.env[ENV_VARS.NO_INPUT] === '1',\n\t\t\tstdinIsTty: ctx.stdinIsTty,\n\t\t},\n\t\tverbose: flags.verbose === true,\n\t\trest,\n\t};\n};\n\n/**\n * Bridge the CLI `--network` flag through `process.env` so config-load-time\n * factory reads pick it up. This indirection is deliberate, not a leak.\n *\n * Why the mutation exists:\n * The `deepbook()` factory (`plugins/deepbook/index.ts`) defaults its mode\n * by reading `process.env.DEVSTACK_NETWORK` at config import time — before\n * any flag value has reached the orchestrator. To make `--network` affect\n * the same default, we must mutate the env BEFORE the user's\n * `devstack.config.ts` is loaded. The chain is:\n * `--network=<net>` flag → setNetworkEnv → `process.env.DEVSTACK_NETWORK`\n * → `deepbook()` factory's env read at import.\n *\n * Why save/restore (and a scoped finalizer) matters:\n * The CLI is also invoked from tests and embedded harnesses inside a single\n * process. An unscoped mutation leaks across invocations: a test that runs\n * `dispatch(['up','--network=testnet'])` followed by `dispatch(['up'])`\n * would see the second call inherit `testnet` from the first. The\n * finalizer restores the prior value (or deletes the key if it was unset)\n * on success, failure, AND interrupt — `Effect.addFinalizer` guarantees\n * the cleanup runs regardless of how the scope closes.\n */\nconst setNetworkEnv = (flags: GlobalFlags): Effect.Effect<void, never, Scope.Scope> => {\n\tif (flags.network === undefined) return Effect.void;\n\tconst next = flags.network;\n\treturn Effect.gen(function* () {\n\t\tconst prior = process.env[ENV_VARS.NETWORK];\n\t\tprocess.env[ENV_VARS.NETWORK] = next;\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.sync(() => {\n\t\t\t\tif (prior === undefined) {\n\t\t\t\t\tdelete process.env[ENV_VARS.NETWORK];\n\t\t\t\t} else {\n\t\t\t\t\tprocess.env[ENV_VARS.NETWORK] = prior;\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t});\n};\n\n// -----------------------------------------------------------------------------\n// Command execution helpers\n// -----------------------------------------------------------------------------\n\nconst runCommandEffect = async (\n\tctx: DevstackCliContext,\n\tcommand: string,\n\tflags: GlobalFlags,\n\teffect: Effect.Effect<CommandResult, CliError>,\n): Promise<void> => {\n\t// `setNetworkEnv` registers a `process.env[ENV_VARS.NETWORK]` restore as a\n\t// scope finalizer; the outer `Effect.scoped` closes that scope after the\n\t// command completes (success, failure, or interrupt), preventing env leaks\n\t// between concurrent CLI invocations in the same process.\n\tconst program = Effect.scoped(\n\t\tsetNetworkEnv(flags).pipe(\n\t\t\tEffect.andThen(effect),\n\t\t\tEffect.catch((error: CliError) =>\n\t\t\t\temitFailure(ctx.io, flags.outputMode, {\n\t\t\t\t\tcommand,\n\t\t\t\t\telapsedMs: 0,\n\t\t\t\t\terror,\n\t\t\t\t}).pipe(Effect.as({ exitCode: exitCodeFor(error) })),\n\t\t\t),\n\t\t\tEffect.catchCause((cause) =>\n\t\t\t\temitFailure(ctx.io, flags.outputMode, {\n\t\t\t\t\tcommand,\n\t\t\t\t\telapsedMs: 0,\n\t\t\t\t\terror: new CliInternalError({ message: 'unexpected internal failure' }),\n\t\t\t\t\tcause,\n\t\t\t\t}).pipe(Effect.as({ exitCode: ExitCode.SOFTWARE })),\n\t\t\t),\n\t\t),\n\t);\n\tconst result = await Effect.runPromise(program);\n\tif (ctx.io.lastExitCode() === null) {\n\t\tawait Effect.runPromise(ctx.io.setExitCode(result.exitCode));\n\t}\n};\n\nconst runWithFlags = async (\n\tctx: DevstackCliContext,\n\tcommand: string,\n\trawFlags: IdentityFlags & Partial<ConfigFlags & UpFlags & DestructiveFlags>,\n\trest: ReadonlyArray<string>,\n\teffect: (flags: GlobalFlags) => Effect.Effect<CommandResult, CliError>,\n): Promise<void> => {\n\tlet flags: GlobalFlags;\n\ttry {\n\t\tflags = makeGlobalFlags(ctx, rawFlags, rest);\n\t} catch (cause) {\n\t\tconst error =\n\t\t\tcause instanceof CliUsageError\n\t\t\t\t? cause\n\t\t\t\t: new CliInternalError({ message: 'failed to resolve CLI flags', cause });\n\t\tconst mode = outputModeFrom(rawFlags, ctx.env);\n\t\tawait Effect.runPromise(\n\t\t\temitFailure(ctx.io, mode, {\n\t\t\t\tcommand,\n\t\t\t\telapsedMs: 0,\n\t\t\t\terror,\n\t\t\t}),\n\t\t);\n\t\treturn;\n\t}\n\tawait runCommandEffect(ctx, command, flags, effect(flags));\n};\n\nconst requiredPositional = (placeholder: string, brief: string) =>\n\t({\n\t\tparse: textParser,\n\t\tplaceholder,\n\t\tbrief,\n\t}) as const;\n\nconst optionalPositional = (placeholder: string, brief: string) =>\n\t({\n\t\tparse: textParser,\n\t\tplaceholder,\n\t\tbrief,\n\t\toptional: true as const,\n\t}) as const;\n\n// -----------------------------------------------------------------------------\n// Commands\n// -----------------------------------------------------------------------------\n\nconst upCommand = buildCommand<UpFlags, [], DevstackCliContext>({\n\tparameters: {\n\t\tflags: {\n\t\t\t...configFlagParams,\n\t\t\trenderer: {\n\t\t\t\tkind: 'parsed',\n\t\t\t\tparse: rendererParser,\n\t\t\t\toptional: true,\n\t\t\t\tplaceholder: 'tui|plain|silent',\n\t\t\t\tbrief: 'Select the attached renderer',\n\t\t\t},\n\t\t},\n\t},\n\tdocs: {\n\t\tbrief: 'Boot a stack and stay attached until interrupted',\n\t},\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'up', flags, [], (global) => this.deps.up.run(global));\n\t},\n});\n\nconst applyCommand = buildCommand<ConfigFlags, [], DevstackCliContext>({\n\tparameters: { flags: configFlagParams },\n\tdocs: {\n\t\tbrief: 'Reconcile a live stack or run one-shot setup',\n\t},\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'apply', flags, [], (global) => this.deps.apply.run(global));\n\t},\n});\n\nconst statusCommand = buildCommand<IdentityFlags, [], DevstackCliContext>({\n\tparameters: { flags: identityFlagParams },\n\tdocs: { brief: 'Show the persisted stack projection' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'status', flags, [], (global) =>\n\t\t\trunStatus(this.deps.status, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst doctorCommand = buildCommand<IdentityFlags, [], DevstackCliContext>({\n\tparameters: { flags: identityFlagParams },\n\tdocs: { brief: 'Run host and stack preflight checks' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'doctor', flags, [], (global) =>\n\t\t\trunDoctor(this.deps.doctor, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst configCommand = buildCommand<ConfigFlags, [], DevstackCliContext>({\n\tparameters: { flags: configFlagParams },\n\tdocs: { brief: 'Print resolved config inputs' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'config', flags, [], (global) =>\n\t\t\trunConfig(this.deps.config, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst schemaCommand = buildCommand<Pick<IdentityFlags, 'json'>, [], DevstackCliContext>({\n\tparameters: { flags: { json: identityFlagParams.json } },\n\tdocs: { brief: 'Emit the CLI schema' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'schema', flags, [], (global) => {\n\t\t\tconst data = { ...commandSchema(), outputMode: global.outputMode };\n\t\t\treturn emitSuccess(this.io, global.outputMode, {\n\t\t\t\tcommand: 'schema',\n\t\t\t\telapsedMs: 0,\n\t\t\t\tdata,\n\t\t\t\thumanLines: [JSON.stringify(data, null, 2)],\n\t\t\t}).pipe(Effect.as({ exitCode: 0 }));\n\t\t});\n\t},\n});\n\nconst snapshotSaveCommand = buildCommand<\n\tSnapshotSaveFlags,\n\t[string | undefined],\n\tDevstackCliContext\n>({\n\tparameters: {\n\t\tflags: {\n\t\t\t...configFlagParams,\n\t\t\tname: stringFlag('Human-readable snapshot name', 'name'),\n\t\t},\n\t\tpositional: { kind: 'tuple', parameters: [optionalPositional('name', 'snapshot name')] },\n\t},\n\tdocs: { brief: 'Capture a snapshot' },\n\tfunc: function (flags, snapshotName) {\n\t\tconst rest = [\n\t\t\t'save',\n\t\t\t...(snapshotName === undefined ? [] : [snapshotName]),\n\t\t\t...(flags.name === undefined ? [] : ['--name', flags.name]),\n\t\t];\n\t\treturn runWithFlags(this, 'snapshot save', flags, rest, (global) =>\n\t\t\trunSnapshot(this.deps.snapshot, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst snapshotRestoreCommand = buildCommand<ConfirmFlags, [string], DevstackCliContext>({\n\tparameters: {\n\t\tflags: confirmFlagParams,\n\t\tpositional: {\n\t\t\tkind: 'tuple',\n\t\t\tparameters: [requiredPositional('name-or-id', 'snapshot name or id')],\n\t\t},\n\t},\n\tdocs: { brief: 'Restore a snapshot' },\n\tfunc: function (flags, snapshotRef) {\n\t\treturn runWithFlags(this, 'snapshot restore', flags, ['restore', snapshotRef], (global) =>\n\t\t\trunSnapshot(this.deps.snapshot, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst snapshotListCommand = buildCommand<IdentityFlags, [], DevstackCliContext>({\n\tparameters: { flags: identityFlagParams },\n\tdocs: { brief: 'List snapshots' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'snapshot list', flags, ['list'], (global) =>\n\t\t\trunSnapshot(this.deps.snapshot, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst snapshotDeleteCommand = buildCommand<ConfirmFlags, [string], DevstackCliContext>({\n\tparameters: {\n\t\tflags: confirmFlagParams,\n\t\tpositional: {\n\t\t\tkind: 'tuple',\n\t\t\tparameters: [requiredPositional('name-or-id', 'snapshot name or id')],\n\t\t},\n\t},\n\tdocs: { brief: 'Delete a snapshot' },\n\tfunc: function (flags, snapshotRef) {\n\t\treturn runWithFlags(this, 'snapshot delete', flags, ['delete', snapshotRef], (global) =>\n\t\t\trunSnapshot(this.deps.snapshot, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst snapshotCommands = buildRouteMap({\n\troutes: {\n\t\tsave: snapshotSaveCommand,\n\t\trestore: snapshotRestoreCommand,\n\t\tlist: snapshotListCommand,\n\t\tdelete: snapshotDeleteCommand,\n\t},\n\tdocs: { brief: 'Capture, restore, list, or delete stack snapshots' },\n});\n\nconst pruneCommand = buildCommand<PruneFlags, [], DevstackCliContext>({\n\tparameters: { flags: pruneFlagParams },\n\tdocs: { brief: 'Inventory and prune devstack-labelled Docker resources' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'prune', flags, [], (global) =>\n\t\t\trunPrune(\n\t\t\t\tthis.deps.prune,\n\t\t\t\t{ flags: global, io: this.io },\n\t\t\t\t{\n\t\t\t\t\tmode: flags.list === true ? 'list' : flags.all === true ? 'all' : 'auto',\n\t\t\t\t\tresources: pruneResourcesFromFlags(flags),\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\t},\n});\n\nconst wipeCommand = buildCommand<DestructiveFlags, [], DevstackCliContext>({\n\tparameters: { flags: destructiveFlagParams },\n\tdocs: { brief: 'Destroy all state for the selected stack' },\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'wipe', flags, [], (global) =>\n\t\t\trunWipe(this.deps.wipe, { flags: global, io: this.io }),\n\t\t);\n\t},\n});\n\nconst root = buildRouteMap({\n\troutes: {\n\t\tup: upCommand,\n\t\tapply: applyCommand,\n\t\tstatus: statusCommand,\n\t\tdoctor: doctorCommand,\n\t\tconfig: configCommand,\n\t\tschema: schemaCommand,\n\t\tsnapshot: snapshotCommands,\n\t\tprune: pruneCommand,\n\t\twipe: wipeCommand,\n\t},\n\tdocs: {\n\t\tbrief: 'Sui development stack CLI',\n\t},\n});\n\nconst app = buildApplication(root, {\n\tname: 'devstack',\n\tversionInfo: { currentVersion: readPackageVersion() },\n\tscanner: { caseStyle: 'allow-kebab-for-camel' },\n\tdocumentation: {\n\t\tcaseStyle: 'convert-camel-to-kebab',\n\t\tdisableAnsiColor: true,\n\t\tonlyRequiredInUsageLine: true,\n\t},\n\tlocalization: {\n\t\tloadText: () => ({\n\t\t\t...text_en,\n\t\t\texceptionWhileParsingArguments: (error) =>\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t}),\n\t},\n});\n\nconst jsonRequested = (\n\targv: ReadonlyArray<string>,\n\tenv: Readonly<Record<string, string | undefined>>,\n): boolean => env[ENV_VARS.JSON] === '1' || argv.includes('--json');\n\n/**\n * Project a `BufferedProcess.exitCode` to a sysexit code AT THE\n * STRICLI-PARSE BOUNDARY ONLY.\n *\n * Contract: `BufferedProcess.exitCode` is mutated ONLY by Stricli's\n * argv-parser when an argv parse step fails (unknown subcommand,\n * malformed flag value, missing required positional). Verbs route\n * their own outcomes through `ctx.io.setExitCode`, which marks\n * `io.touched()` and short-circuits this projection in `dispatch`\n * before `flushBufferedProcess` is reached.\n *\n * The historic implementation collapsed ANY non-zero `code` to\n * `USAGE` (64), but that was wrong: it could only ever observe a\n * Stricli parse failure (the only writer to this field), so the\n * `USAGE` mapping holds by construction. Kept as a named function\n * so the invariant is documented at the call site.\n */\nconst normalizeStricliExitCode = (code: number | string | null | undefined): number => {\n\tif (typeof code === 'number' && code !== 0) return ExitCode.USAGE;\n\treturn ExitCode.OK;\n};\n\n/**\n * Bridge Stricli's synchronous `StricliProcess.{stdout,stderr}.write`\n * shape to our async (Effect-based) `CliIO` surface.\n *\n * The indirection is load-bearing for the JSON-envelope contract:\n * Stricli writes argv-parse errors to stderr the moment the parser\n * trips, but `--json` mode demands the failure be EMITTED as a\n * structured envelope on stdout (not raw text on stderr) with exit\n * code `EX_USAGE`. We can't wire Stricli's `stderr.write` directly to\n * `nodeProcessIO.writeStderr` because:\n *\n * 1. The stderr bytes are the raw parser error text; in `--json`\n * mode we need to transform them into a failure envelope.\n * 2. We don't know whether the verb handler \"touched\" the IO\n * (rendered its own envelope) until after Stricli returns.\n *\n * Buffering lets us delay the decision until both signals are\n * available. Tests substitute a `BufferedProcess` whose buffers are\n * later inspected, mirroring the prod flush behavior.\n */\nconst flushBufferedProcess = (\n\tprocess: BufferedProcess,\n\tio: CliIO,\n\targv: ReadonlyArray<string>,\n\tenv: Readonly<Record<string, string | undefined>>,\n): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tconst exitCode = normalizeStricliExitCode(process.exitCode);\n\t\tconst stdout = process.stdoutBuffer.join('');\n\t\tconst stderr = process.stderrBuffer.join('').trim();\n\t\tif (exitCode !== 0 && jsonRequested(argv, env)) {\n\t\t\tyield* emitFailure(io, 'json', {\n\t\t\t\tcommand: '(parse)',\n\t\t\t\telapsedMs: 0,\n\t\t\t\terror: new CliUsageError({\n\t\t\t\t\tmessage: stderr.length > 0 ? stderr : 'invalid command line',\n\t\t\t\t}),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (stdout.length > 0)\n\t\t\tyield* io.writeStdout(stdout.endsWith('\\n') ? stdout.slice(0, -1) : stdout);\n\t\tif (stderr.length > 0) yield* io.writeStderr(stderr);\n\t\tyield* io.setExitCode(exitCode);\n\t});\n\nexport const dispatch = (deps: CliDeps, dispatchEnv: DispatchEnv): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tconst io = trackIO(dispatchEnv.io ?? nodeProcessIO);\n\t\tconst process = makeBufferedProcess(dispatchEnv.env);\n\t\tconst ctx: DevstackCliContext = {\n\t\t\tprocess,\n\t\t\tdeps,\n\t\t\tenv: dispatchEnv.env,\n\t\t\tstdinIsTty: dispatchEnv.stdinIsTty,\n\t\t\tio,\n\t\t};\n\t\tyield* Effect.tryPromise({\n\t\t\ttry: () => runStricli(app, dispatchEnv.argv, ctx),\n\t\t\tcatch: (cause) => new CliInternalError({ message: 'CLI dispatcher failed', cause }),\n\t\t}).pipe(\n\t\t\tEffect.catch((error: CliError) =>\n\t\t\t\temitFailure(io, jsonRequested(dispatchEnv.argv, dispatchEnv.env) ? 'json' : 'human', {\n\t\t\t\t\tcommand: '(dispatch)',\n\t\t\t\t\telapsedMs: 0,\n\t\t\t\t\terror,\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tif (!io.touched()) {\n\t\t\tyield* flushBufferedProcess(process, io, dispatchEnv.argv, dispatchEnv.env);\n\t\t}\n\t});\n\n// -----------------------------------------------------------------------------\n// Re-exports\n// -----------------------------------------------------------------------------\n\nexport type { CliIO } from './output.ts';\nexport type { GlobalFlags } from './flags.ts';\nexport { COMMAND_TREE, commandSchema, VERBS, type Verb } from './command-tree.ts';\nexport type { Envelope, EnvelopeError } from './envelope.ts';\nexport {\n\tENVELOPE_SCHEMA_VERSION,\n\tfailureEnvelope,\n\tstreamingEvent,\n\tsuccessEnvelope,\n\ttype StreamingEvent,\n} from './envelope.ts';\nexport { ExitCode, exitCodeName } from './sysexits.ts';\nexport {\n\ttype CliError,\n\tCliAlreadyReportedError,\n\tCliConfigInvalidError,\n\tCliConfigNotFoundError,\n\tCliConfirmDeclinedError,\n\tCliConfirmRequiredError,\n\tCliInternalError,\n\tCliSnapshotNotFoundError,\n\tCliSupervisorLiveError,\n\tCliUnavailableError,\n\tCliUsageError,\n} from './errors.ts';\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { formatCause, formatValue } from "../../substrate/runtime/observability/cascade-formatter.mjs";
2
+ import "./sysexits.mjs";
2
3
  import { exitCodeFor, hintFor, summaryFor } from "./errors.mjs";
3
4
  import { failureEnvelope, successEnvelope } from "./envelope.mjs";
4
5
  import { Effect } from "effect";
@@ -1 +1 @@
1
- {"version":3,"file":"output.mjs","names":[],"sources":["../../../src/surfaces/cli/output.ts"],"sourcesContent":["// CLI surface — output rendering (human / JSON envelope / structured\n// pretty error).\n//\n// Architecture (distilled/20-cli.md § Output formats):\n// - JSON envelope on stdout EXACTLY ONCE per command in `--json`\n// mode; stderr never receives the envelope.\n// - Human text on stdout otherwise.\n// - Pretty error tree on stderr only on failure — and only if the\n// subcommand hasn't already rendered (the `CliAlreadyReportedError`\n// sentinel suppresses).\n//\n// This module owns all stdout/stderr writes for the CLI. Subcommands\n// build envelope params; this module serializes and emits.\n\nimport { Effect } from 'effect';\nimport type { Cause } from 'effect';\n\nimport {\n\tformatCause,\n\tformatValue,\n} from '../../substrate/runtime/observability/cascade-formatter.ts';\nimport {\n\ttype Envelope,\n\tfailureEnvelope,\n\ttype FailureParams,\n\ttype StreamingEvent,\n\tsuccessEnvelope,\n\ttype SuccessParams,\n} from './envelope.ts';\nimport {\n\ttype CliError,\n\tCliAlreadyReportedError,\n\texitCodeFor,\n\thintFor,\n\tsummaryFor,\n} from './errors.ts';\nimport type { OutputMode } from './flags.ts';\nimport { type ExitCode } from './sysexits.ts';\n\n// -----------------------------------------------------------------------------\n// I/O service abstraction\n// -----------------------------------------------------------------------------\n\n/** Surface-level IO. Tests substitute a buffered impl. */\nexport interface CliIO {\n\treadonly writeStdout: (line: string) => Effect.Effect<void>;\n\treadonly writeStderr: (line: string) => Effect.Effect<void>;\n\treadonly setExitCode: (code: number) => Effect.Effect<void>;\n}\n\n/** Default IO bound to the Node process. */\nexport const nodeProcessIO: CliIO = {\n\twriteStdout: (line) =>\n\t\tEffect.sync(() => {\n\t\t\tprocess.stdout.write(line.endsWith('\\n') ? line : `${line}\\n`);\n\t\t}),\n\twriteStderr: (line) =>\n\t\tEffect.sync(() => {\n\t\t\tprocess.stderr.write(line.endsWith('\\n') ? line : `${line}\\n`);\n\t\t}),\n\tsetExitCode: (code) =>\n\t\tEffect.sync(() => {\n\t\t\tprocess.exitCode = code;\n\t\t}),\n};\n\n// -----------------------------------------------------------------------------\n// Stable JSON serialization\n// -----------------------------------------------------------------------------\n\n/**\n * `JSON.stringify` strips `undefined` from objects but NOT from\n * arrays (replaces with `null`). The envelope builders already omit\n * absent keys, but we add a guard so a future builder bug doesn't\n * leak `undefined` into automation output.\n *\n * Architecture: \"Absent fields are OMITTED from serialized output (no\n * `undefined`).\" This function enforces that promise.\n */\nexport const serializeEnvelope = (env: Envelope<unknown> | StreamingEvent<unknown>): string => {\n\tconst cleaned = stripUndefined(env);\n\treturn JSON.stringify(cleaned);\n};\n\nconst stripUndefined = (value: unknown): unknown => {\n\tif (value === undefined) return undefined;\n\tif (value === null) return null;\n\tif (Array.isArray(value)) return value.map(stripUndefined);\n\tif (typeof value !== 'object') return value;\n\tconst out: Record<string, unknown> = {};\n\tfor (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n\t\tif (v === undefined) continue;\n\t\tout[k] = stripUndefined(v);\n\t}\n\treturn out;\n};\n\n// -----------------------------------------------------------------------------\n// Success path\n// -----------------------------------------------------------------------------\n\n/**\n * Emit a success envelope OR a human-mode line block. Sets exit code\n * 0 unless a streaming success path supplies its own process code.\n *\n * In JSON mode: one envelope on stdout, nothing on stderr.\n * In human mode: each `humanLines` entry on stdout; hints rendered\n * after; no envelope.\n */\nexport const emitSuccess = <Data>(\n\tio: CliIO,\n\tmode: OutputMode,\n\tparams: SuccessParams<Data> & {\n\t\treadonly humanLines?: ReadonlyArray<string>;\n\t\t/** Override the OS exit code for commands whose transport\n\t\t * succeeded but whose payload carries a process-style status. */\n\t\treadonly exitCode?: number;\n\t},\n): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tif (mode === 'json') {\n\t\t\tconst env = successEnvelope({\n\t\t\t\tcommand: params.command,\n\t\t\t\telapsedMs: params.elapsedMs,\n\t\t\t\tdata: params.data,\n\t\t\t\thints: params.hints,\n\t\t\t\tdryRun: params.dryRun,\n\t\t\t});\n\t\t\tyield* io.writeStdout(serializeEnvelope(env));\n\t\t} else {\n\t\t\tfor (const line of params.humanLines ?? []) {\n\t\t\t\tyield* io.writeStdout(line);\n\t\t\t}\n\t\t\tfor (const hint of params.hints ?? []) {\n\t\t\t\tyield* io.writeStdout(`hint: ${hint}`);\n\t\t\t}\n\t\t}\n\t\tyield* io.setExitCode(params.exitCode ?? 0);\n\t});\n\n// -----------------------------------------------------------------------------\n// Failure path\n// -----------------------------------------------------------------------------\n\n/**\n * Emit a failure for a `CliError`. Threads the sysexit code through\n * to `process.exitCode`. Honors the already-reported sentinel.\n *\n * Architecture invariant: \"Top-level error rendering must not\n * double-print when a subcommand already rendered.\" — we still set the\n * exit code but skip the envelope/stderr write.\n */\nexport const emitFailure = (\n\tio: CliIO,\n\tmode: OutputMode,\n\tparams: {\n\t\treadonly command: string;\n\t\treadonly elapsedMs: number;\n\t\treadonly error: CliError;\n\t\treadonly cause?: Cause.Cause<unknown>;\n\t\treadonly dryRun?: boolean;\n\t},\n): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tconst exitCode = exitCodeFor(params.error);\n\t\tif (params.error._tag === 'CliAlreadyReportedError') {\n\t\t\tyield* io.setExitCode(exitCode);\n\t\t\treturn;\n\t\t}\n\t\tyield* renderFailure(io, mode, {\n\t\t\tcommand: params.command,\n\t\t\telapsedMs: params.elapsedMs,\n\t\t\texitCode,\n\t\t\tsummary: summaryFor(params.error),\n\t\t\thint: hintFor(params.error),\n\t\t\tcause: params.cause,\n\t\t\terrorCause:\n\t\t\t\t'cause' in params.error ? (params.error as { readonly cause?: unknown }).cause : undefined,\n\t\t\tdryRun: params.dryRun,\n\t\t});\n\t\tyield* io.setExitCode(exitCode);\n\t});\n\ninterface RenderFailureParams {\n\treadonly command: string;\n\treadonly elapsedMs: number;\n\treadonly exitCode: ExitCode;\n\treadonly summary: string;\n\treadonly hint?: string;\n\treadonly cause?: Cause.Cause<unknown>;\n\treadonly errorCause?: unknown;\n\treadonly dryRun?: boolean;\n}\n\nconst renderFailure = (io: CliIO, mode: OutputMode, p: RenderFailureParams): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tconst rawChain =\n\t\t\tp.cause !== undefined\n\t\t\t\t? formatCause(p.cause)\n\t\t\t\t: p.errorCause !== undefined\n\t\t\t\t\t? formatValue(p.errorCause)\n\t\t\t\t\t: undefined;\n\t\tconst chain = rawChain?.split('\\n').filter((l) => l.length > 0);\n\n\t\tif (mode === 'json') {\n\t\t\tconst fp: FailureParams = {\n\t\t\t\tcommand: p.command,\n\t\t\t\telapsedMs: p.elapsedMs,\n\t\t\t\texitCode: p.exitCode,\n\t\t\t\tsummary: p.summary,\n\t\t\t\thint: p.hint,\n\t\t\t\tchain,\n\t\t\t\tdryRun: p.dryRun,\n\t\t\t};\n\t\t\tyield* io.writeStdout(serializeEnvelope(failureEnvelope(fp)));\n\t\t\treturn;\n\t\t}\n\n\t\t// Human mode: one-line summary on stderr, then optional hint and\n\t\t// the cascade chain (if present), all on stderr.\n\t\tyield* io.writeStderr(`error: ${p.summary}`);\n\t\tif (p.hint !== undefined) {\n\t\t\tyield* io.writeStderr(`hint: ${p.hint}`);\n\t\t}\n\t\tif (chain !== undefined && chain.length > 0) {\n\t\t\tfor (const line of chain) {\n\t\t\t\tyield* io.writeStderr(` ${line}`);\n\t\t\t}\n\t\t}\n\t});\n\n// -----------------------------------------------------------------------------\n// Already-reported helper\n// -----------------------------------------------------------------------------\n\n/** A subcommand that pretty-renders its own failure (e.g. an\n * interactive picker that already drew an error panel) returns this\n * sentinel so the top-level renderer skips re-rendering but still\n * propagates the right exit code. */\nexport const alreadyReported = (exitCode: ExitCode): CliAlreadyReportedError =>\n\tnew CliAlreadyReportedError({ exitCode });\n"],"mappings":";;;;;;AAmDA,MAAa,gBAAuB;CACnC,cAAc,SACb,OAAO,WAAW;AACjB,UAAQ,OAAO,MAAM,KAAK,SAAS,KAAK,GAAG,OAAO,GAAG,KAAK,IAAI;GAC7D;CACH,cAAc,SACb,OAAO,WAAW;AACjB,UAAQ,OAAO,MAAM,KAAK,SAAS,KAAK,GAAG,OAAO,GAAG,KAAK,IAAI;GAC7D;CACH,cAAc,SACb,OAAO,WAAW;AACjB,UAAQ,WAAW;GAClB;CACH;;;;;;;;;;AAeD,MAAa,qBAAqB,QAA6D;CAC9F,MAAM,UAAU,eAAe,IAAI;AACnC,QAAO,KAAK,UAAU,QAAQ;;AAG/B,MAAM,kBAAkB,UAA4B;AACnD,KAAI,UAAU,KAAA,EAAW,QAAO,KAAA;AAChC,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,MAAM,IAAI,eAAe;AAC1D,KAAI,OAAO,UAAU,SAAU,QAAO;CACtC,MAAM,MAA+B,EAAE;AACvC,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAiC,EAAE;AACtE,MAAI,MAAM,KAAA,EAAW;AACrB,MAAI,KAAK,eAAe,EAAE;;AAE3B,QAAO;;;;;;;;;;AAeR,MAAa,eACZ,IACA,MACA,WAOA,OAAO,IAAI,aAAa;AACvB,KAAI,SAAS,QAAQ;EACpB,MAAM,MAAM,gBAAgB;GAC3B,SAAS,OAAO;GAChB,WAAW,OAAO;GAClB,MAAM,OAAO;GACb,OAAO,OAAO;GACd,QAAQ,OAAO;GACf,CAAC;AACF,SAAO,GAAG,YAAY,kBAAkB,IAAI,CAAC;QACvC;AACN,OAAK,MAAM,QAAQ,OAAO,cAAc,EAAE,CACzC,QAAO,GAAG,YAAY,KAAK;AAE5B,OAAK,MAAM,QAAQ,OAAO,SAAS,EAAE,CACpC,QAAO,GAAG,YAAY,SAAS,OAAO;;AAGxC,QAAO,GAAG,YAAY,OAAO,YAAY,EAAE;EAC1C;;;;;;;;;AAcH,MAAa,eACZ,IACA,MACA,WAQA,OAAO,IAAI,aAAa;CACvB,MAAM,WAAW,YAAY,OAAO,MAAM;AAC1C,KAAI,OAAO,MAAM,SAAS,2BAA2B;AACpD,SAAO,GAAG,YAAY,SAAS;AAC/B;;AAED,QAAO,cAAc,IAAI,MAAM;EAC9B,SAAS,OAAO;EAChB,WAAW,OAAO;EAClB;EACA,SAAS,WAAW,OAAO,MAAM;EACjC,MAAM,QAAQ,OAAO,MAAM;EAC3B,OAAO,OAAO;EACd,YACC,WAAW,OAAO,QAAS,OAAO,MAAuC,QAAQ,KAAA;EAClF,QAAQ,OAAO;EACf,CAAC;AACF,QAAO,GAAG,YAAY,SAAS;EAC9B;AAaH,MAAM,iBAAiB,IAAW,MAAkB,MACnD,OAAO,IAAI,aAAa;CAOvB,MAAM,SALL,EAAE,UAAU,KAAA,IACT,YAAY,EAAE,MAAM,GACpB,EAAE,eAAe,KAAA,IAChB,YAAY,EAAE,WAAW,GACzB,KAAA,IACmB,MAAM,KAAK,CAAC,QAAQ,MAAM,EAAE,SAAS,EAAE;AAE/D,KAAI,SAAS,QAAQ;EACpB,MAAM,KAAoB;GACzB,SAAS,EAAE;GACX,WAAW,EAAE;GACb,UAAU,EAAE;GACZ,SAAS,EAAE;GACX,MAAM,EAAE;GACR;GACA,QAAQ,EAAE;GACV;AACD,SAAO,GAAG,YAAY,kBAAkB,gBAAgB,GAAG,CAAC,CAAC;AAC7D;;AAKD,QAAO,GAAG,YAAY,UAAU,EAAE,UAAU;AAC5C,KAAI,EAAE,SAAS,KAAA,EACd,QAAO,GAAG,YAAY,SAAS,EAAE,OAAO;AAEzC,KAAI,UAAU,KAAA,KAAa,MAAM,SAAS,EACzC,MAAK,MAAM,QAAQ,MAClB,QAAO,GAAG,YAAY,KAAK,OAAO;EAGnC"}
1
+ {"version":3,"file":"output.mjs","names":[],"sources":["../../../src/surfaces/cli/output.ts"],"sourcesContent":["// CLI surface — output rendering (human / JSON envelope / structured\n// pretty error).\n//\n// Architecture (distilled/20-cli.md § Output formats):\n// - JSON envelope on stdout EXACTLY ONCE per command in `--json`\n// mode; stderr never receives the envelope.\n// - Human text on stdout otherwise.\n// - Pretty error tree on stderr only on failure — and only if the\n// subcommand hasn't already rendered (the `CliAlreadyReportedError`\n// sentinel suppresses).\n//\n// This module owns all stdout/stderr writes for the CLI. Subcommands\n// build envelope params; this module serializes and emits.\n\nimport { Effect } from 'effect';\nimport type { Cause } from 'effect';\n\nimport {\n\tformatCause,\n\tformatValue,\n} from '../../substrate/runtime/observability/cascade-formatter.ts';\nimport {\n\ttype Envelope,\n\tfailureEnvelope,\n\ttype FailureParams,\n\ttype StreamingEvent,\n\tsuccessEnvelope,\n\ttype SuccessParams,\n} from './envelope.ts';\nimport {\n\ttype CliError,\n\tCliAlreadyReportedError,\n\texitCodeFor,\n\thintFor,\n\tsummaryFor,\n} from './errors.ts';\nimport type { OutputMode } from './flags.ts';\nimport { type ExitCode } from './sysexits.ts';\n\n// -----------------------------------------------------------------------------\n// I/O service abstraction\n// -----------------------------------------------------------------------------\n\n/** Surface-level IO. Tests substitute a buffered impl. */\nexport interface CliIO {\n\treadonly writeStdout: (line: string) => Effect.Effect<void>;\n\treadonly writeStderr: (line: string) => Effect.Effect<void>;\n\treadonly setExitCode: (code: number) => Effect.Effect<void>;\n}\n\n/** Default IO bound to the Node process. */\nexport const nodeProcessIO: CliIO = {\n\twriteStdout: (line) =>\n\t\tEffect.sync(() => {\n\t\t\tprocess.stdout.write(line.endsWith('\\n') ? line : `${line}\\n`);\n\t\t}),\n\twriteStderr: (line) =>\n\t\tEffect.sync(() => {\n\t\t\tprocess.stderr.write(line.endsWith('\\n') ? line : `${line}\\n`);\n\t\t}),\n\tsetExitCode: (code) =>\n\t\tEffect.sync(() => {\n\t\t\tprocess.exitCode = code;\n\t\t}),\n};\n\n// -----------------------------------------------------------------------------\n// Stable JSON serialization\n// -----------------------------------------------------------------------------\n\n/**\n * `JSON.stringify` strips `undefined` from objects but NOT from\n * arrays (replaces with `null`). The envelope builders already omit\n * absent keys, but we add a guard so a future builder bug doesn't\n * leak `undefined` into automation output.\n *\n * Architecture: \"Absent fields are OMITTED from serialized output (no\n * `undefined`).\" This function enforces that promise.\n */\nexport const serializeEnvelope = (env: Envelope<unknown> | StreamingEvent<unknown>): string => {\n\tconst cleaned = stripUndefined(env);\n\treturn JSON.stringify(cleaned);\n};\n\nconst stripUndefined = (value: unknown): unknown => {\n\tif (value === undefined) return undefined;\n\tif (value === null) return null;\n\tif (Array.isArray(value)) return value.map(stripUndefined);\n\tif (typeof value !== 'object') return value;\n\tconst out: Record<string, unknown> = {};\n\tfor (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n\t\tif (v === undefined) continue;\n\t\tout[k] = stripUndefined(v);\n\t}\n\treturn out;\n};\n\n// -----------------------------------------------------------------------------\n// Success path\n// -----------------------------------------------------------------------------\n\n/**\n * Emit a success envelope OR a human-mode line block. Sets exit code\n * 0 unless a streaming success path supplies its own process code.\n *\n * In JSON mode: one envelope on stdout, nothing on stderr.\n * In human mode: each `humanLines` entry on stdout; hints rendered\n * after; no envelope.\n */\nexport const emitSuccess = <Data>(\n\tio: CliIO,\n\tmode: OutputMode,\n\tparams: SuccessParams<Data> & {\n\t\treadonly humanLines?: ReadonlyArray<string>;\n\t\t/** Override the OS exit code for commands whose transport\n\t\t * succeeded but whose payload carries a process-style status. */\n\t\treadonly exitCode?: number;\n\t},\n): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tif (mode === 'json') {\n\t\t\tconst env = successEnvelope({\n\t\t\t\tcommand: params.command,\n\t\t\t\telapsedMs: params.elapsedMs,\n\t\t\t\tdata: params.data,\n\t\t\t\thints: params.hints,\n\t\t\t\tdryRun: params.dryRun,\n\t\t\t});\n\t\t\tyield* io.writeStdout(serializeEnvelope(env));\n\t\t} else {\n\t\t\tfor (const line of params.humanLines ?? []) {\n\t\t\t\tyield* io.writeStdout(line);\n\t\t\t}\n\t\t\tfor (const hint of params.hints ?? []) {\n\t\t\t\tyield* io.writeStdout(`hint: ${hint}`);\n\t\t\t}\n\t\t}\n\t\tyield* io.setExitCode(params.exitCode ?? 0);\n\t});\n\n// -----------------------------------------------------------------------------\n// Failure path\n// -----------------------------------------------------------------------------\n\n/**\n * Emit a failure for a `CliError`. Threads the sysexit code through\n * to `process.exitCode`. Honors the already-reported sentinel.\n *\n * Architecture invariant: \"Top-level error rendering must not\n * double-print when a subcommand already rendered.\" — we still set the\n * exit code but skip the envelope/stderr write.\n */\nexport const emitFailure = (\n\tio: CliIO,\n\tmode: OutputMode,\n\tparams: {\n\t\treadonly command: string;\n\t\treadonly elapsedMs: number;\n\t\treadonly error: CliError;\n\t\treadonly cause?: Cause.Cause<unknown>;\n\t\treadonly dryRun?: boolean;\n\t},\n): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tconst exitCode = exitCodeFor(params.error);\n\t\tif (params.error._tag === 'CliAlreadyReportedError') {\n\t\t\tyield* io.setExitCode(exitCode);\n\t\t\treturn;\n\t\t}\n\t\tyield* renderFailure(io, mode, {\n\t\t\tcommand: params.command,\n\t\t\telapsedMs: params.elapsedMs,\n\t\t\texitCode,\n\t\t\tsummary: summaryFor(params.error),\n\t\t\thint: hintFor(params.error),\n\t\t\tcause: params.cause,\n\t\t\terrorCause:\n\t\t\t\t'cause' in params.error ? (params.error as { readonly cause?: unknown }).cause : undefined,\n\t\t\tdryRun: params.dryRun,\n\t\t});\n\t\tyield* io.setExitCode(exitCode);\n\t});\n\ninterface RenderFailureParams {\n\treadonly command: string;\n\treadonly elapsedMs: number;\n\treadonly exitCode: ExitCode;\n\treadonly summary: string;\n\treadonly hint?: string;\n\treadonly cause?: Cause.Cause<unknown>;\n\treadonly errorCause?: unknown;\n\treadonly dryRun?: boolean;\n}\n\nconst renderFailure = (io: CliIO, mode: OutputMode, p: RenderFailureParams): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tconst rawChain =\n\t\t\tp.cause !== undefined\n\t\t\t\t? formatCause(p.cause)\n\t\t\t\t: p.errorCause !== undefined\n\t\t\t\t\t? formatValue(p.errorCause)\n\t\t\t\t\t: undefined;\n\t\tconst chain = rawChain?.split('\\n').filter((l) => l.length > 0);\n\n\t\tif (mode === 'json') {\n\t\t\tconst fp: FailureParams = {\n\t\t\t\tcommand: p.command,\n\t\t\t\telapsedMs: p.elapsedMs,\n\t\t\t\texitCode: p.exitCode,\n\t\t\t\tsummary: p.summary,\n\t\t\t\thint: p.hint,\n\t\t\t\tchain,\n\t\t\t\tdryRun: p.dryRun,\n\t\t\t};\n\t\t\tyield* io.writeStdout(serializeEnvelope(failureEnvelope(fp)));\n\t\t\treturn;\n\t\t}\n\n\t\t// Human mode: one-line summary on stderr, then optional hint and\n\t\t// the cascade chain (if present), all on stderr.\n\t\tyield* io.writeStderr(`error: ${p.summary}`);\n\t\tif (p.hint !== undefined) {\n\t\t\tyield* io.writeStderr(`hint: ${p.hint}`);\n\t\t}\n\t\tif (chain !== undefined && chain.length > 0) {\n\t\t\tfor (const line of chain) {\n\t\t\t\tyield* io.writeStderr(` ${line}`);\n\t\t\t}\n\t\t}\n\t});\n\n// -----------------------------------------------------------------------------\n// Already-reported helper\n// -----------------------------------------------------------------------------\n\n/** A subcommand that pretty-renders its own failure (e.g. an\n * interactive picker that already drew an error panel) returns this\n * sentinel so the top-level renderer skips re-rendering but still\n * propagates the right exit code. */\nexport const alreadyReported = (exitCode: ExitCode): CliAlreadyReportedError =>\n\tnew CliAlreadyReportedError({ exitCode });\n"],"mappings":"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"}
@@ -26,7 +26,7 @@ const ExitCode = {
26
26
  /** Cannot open input (config not found, snapshot file missing). */
27
27
  NO_INPUT: 66,
28
28
  /** Internal software error / unhandled defect. Default for
29
- * defects that escape Effect.catchAll. */
29
+ * defects that escape Effect.catch. */
30
30
  SOFTWARE: 70,
31
31
  /** Service required by the CLI is unavailable (Docker daemon
32
32
  * unreachable, network down). */
@@ -59,6 +59,7 @@ const exitCodeName = (code) => {
59
59
  case ExitCode.CANT_CREATE: return "CANT_CREATE";
60
60
  case ExitCode.TEMP_FAIL: return "TEMP_FAIL";
61
61
  case ExitCode.CONFIG: return "CONFIG";
62
+ default: throw new Error(`Unknown ExitCode: ${String(code)}`);
62
63
  }
63
64
  };
64
65
  const exitCodeTable = [