@mysten-incubation/devstack 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +1 -1
  2. package/dashboard-ui/assets/{grpc-Clz0oOtc.js → grpc-CpkDu4SA.js} +1 -1
  3. package/dashboard-ui/assets/{index-Cmqv9kiU.js → index-jLPRmjst.js} +2 -2
  4. package/dashboard-ui/index.html +1 -1
  5. package/dist/api/inference-network.d.mts +5 -5
  6. package/dist/api/inference-network.mjs +5 -5
  7. package/dist/api/inference-network.mjs.map +1 -1
  8. package/dist/api/run-stack-internal.mjs +1 -3
  9. package/dist/api/run-stack-internal.mjs.map +1 -1
  10. package/dist/build-integrations/dapp-kit/index.d.mts +12 -0
  11. package/dist/build-integrations/dapp-kit/index.mjs +14 -1
  12. package/dist/build-integrations/dapp-kit/index.mjs.map +1 -1
  13. package/dist/build-integrations/playwright/errors.d.mts +1 -1
  14. package/dist/build-integrations/playwright/errors.mjs.map +1 -1
  15. package/dist/build-integrations/playwright/index.d.mts +2 -2
  16. package/dist/build-integrations/playwright/index.mjs +2 -2
  17. package/dist/build-integrations/playwright/wallet-context.d.mts +2 -1
  18. package/dist/build-integrations/playwright/wallet-context.mjs +141 -41
  19. package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
  20. package/dist/build-integrations/runtime/dapp-kit-slot.mjs +1 -1
  21. package/dist/build-integrations/runtime/dapp-kit-slot.mjs.map +1 -1
  22. package/dist/build-integrations/vite/index.d.mts +54 -42
  23. package/dist/build-integrations/vite/index.mjs +239 -106
  24. package/dist/build-integrations/vite/index.mjs.map +1 -1
  25. package/dist/build-integrations/vitest/global-setup.mjs +8 -8
  26. package/dist/build-integrations/vitest/global-setup.mjs.map +1 -1
  27. package/dist/cli/main.mjs +3 -2
  28. package/dist/cli/main.mjs.map +1 -1
  29. package/dist/cli/wirings/apply.mjs +0 -1
  30. package/dist/cli/wirings/apply.mjs.map +1 -1
  31. package/dist/cli/wirings/codegen.mjs +2 -3
  32. package/dist/cli/wirings/codegen.mjs.map +1 -1
  33. package/dist/cli/wirings/dump-deployment.mjs +141 -0
  34. package/dist/cli/wirings/dump-deployment.mjs.map +1 -0
  35. package/dist/cli/wirings/snapshot.mjs +0 -1
  36. package/dist/cli/wirings/snapshot.mjs.map +1 -1
  37. package/dist/contracts/codegenable.d.mts +22 -25
  38. package/dist/contracts/codegenable.mjs +35 -4
  39. package/dist/contracts/codegenable.mjs.map +1 -1
  40. package/dist/contracts/config-bindings.mjs +43 -18
  41. package/dist/contracts/config-bindings.mjs.map +1 -1
  42. package/dist/index.mjs +1 -1
  43. package/dist/orchestrators/boot.d.mts +0 -10
  44. package/dist/orchestrators/boot.mjs +19 -59
  45. package/dist/orchestrators/boot.mjs.map +1 -1
  46. package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
  47. package/dist/orchestrators/codegen/config-runtime.mjs +137 -112
  48. package/dist/orchestrators/codegen/config-runtime.mjs.map +1 -1
  49. package/dist/orchestrators/codegen/deployment-network-file.mjs +130 -0
  50. package/dist/orchestrators/codegen/deployment-network-file.mjs.map +1 -0
  51. package/dist/orchestrators/codegen/deployment-strict.mjs +77 -0
  52. package/dist/orchestrators/codegen/deployment-strict.mjs.map +1 -0
  53. package/dist/orchestrators/codegen/deployment.d.mts +63 -0
  54. package/dist/orchestrators/codegen/deployment.mjs +153 -0
  55. package/dist/orchestrators/codegen/deployment.mjs.map +1 -0
  56. package/dist/orchestrators/codegen/errors.mjs +7 -9
  57. package/dist/orchestrators/codegen/errors.mjs.map +1 -1
  58. package/dist/orchestrators/codegen/format.mjs +20 -1
  59. package/dist/orchestrators/codegen/format.mjs.map +1 -1
  60. package/dist/orchestrators/codegen/paths.mjs +1 -8
  61. package/dist/orchestrators/codegen/paths.mjs.map +1 -1
  62. package/dist/orchestrators/codegen/service.mjs +354 -141
  63. package/dist/orchestrators/codegen/service.mjs.map +1 -1
  64. package/dist/orchestrators/layers.mjs +0 -1
  65. package/dist/orchestrators/layers.mjs.map +1 -1
  66. package/dist/orchestrators/network-options.d.mts +37 -35
  67. package/dist/orchestrators/network-options.mjs +13 -10
  68. package/dist/orchestrators/network-options.mjs.map +1 -1
  69. package/dist/plugins/account/codegen.d.mts +3 -4
  70. package/dist/plugins/account/codegen.mjs +5 -7
  71. package/dist/plugins/account/codegen.mjs.map +1 -1
  72. package/dist/plugins/account/index.mjs +1 -3
  73. package/dist/plugins/account/index.mjs.map +1 -1
  74. package/dist/plugins/coin/codegen.mjs +5 -5
  75. package/dist/plugins/coin/codegen.mjs.map +1 -1
  76. package/dist/plugins/coin/index.mjs.map +1 -1
  77. package/dist/plugins/dashboard/schema/root.mjs +3 -1
  78. package/dist/plugins/dashboard/schema/root.mjs.map +1 -1
  79. package/dist/plugins/deepbook/codegen.mjs +4 -4
  80. package/dist/plugins/deepbook/codegen.mjs.map +1 -1
  81. package/dist/plugins/deepbook/index.mjs.map +1 -1
  82. package/dist/plugins/internal/codegen-helpers.mjs +0 -1
  83. package/dist/plugins/internal/codegen-helpers.mjs.map +1 -1
  84. package/dist/plugins/package/codegen.d.mts +1 -12
  85. package/dist/plugins/package/codegen.mjs +43 -25
  86. package/dist/plugins/package/codegen.mjs.map +1 -1
  87. package/dist/plugins/package/dep-resolution.mjs +1 -1
  88. package/dist/plugins/package/dep-resolution.mjs.map +1 -1
  89. package/dist/plugins/package/index.d.mts +14 -11
  90. package/dist/plugins/package/index.mjs +6 -7
  91. package/dist/plugins/package/index.mjs.map +1 -1
  92. package/dist/plugins/seal/codegen.mjs +3 -3
  93. package/dist/plugins/seal/codegen.mjs.map +1 -1
  94. package/dist/plugins/seal/index.mjs.map +1 -1
  95. package/dist/plugins/sui/codegen.mjs +5 -4
  96. package/dist/plugins/sui/codegen.mjs.map +1 -1
  97. package/dist/plugins/sui/index.mjs +1 -2
  98. package/dist/plugins/sui/index.mjs.map +1 -1
  99. package/dist/plugins/wallet/codegen.d.mts +8 -12
  100. package/dist/plugins/wallet/codegen.mjs +22 -15
  101. package/dist/plugins/wallet/codegen.mjs.map +1 -1
  102. package/dist/plugins/wallet/index.d.mts +4 -3
  103. package/dist/plugins/wallet/index.mjs +1 -1
  104. package/dist/plugins/wallet/index.mjs.map +1 -1
  105. package/dist/plugins/wallet/service.d.mts +2 -2
  106. package/dist/plugins/wallet/service.mjs +2 -3
  107. package/dist/plugins/wallet/service.mjs.map +1 -1
  108. package/dist/plugins/walrus/codegen.mjs +4 -4
  109. package/dist/plugins/walrus/codegen.mjs.map +1 -1
  110. package/dist/plugins/walrus/index.mjs +1 -1
  111. package/dist/plugins/walrus/index.mjs.map +1 -1
  112. package/dist/substrate/manifest.d.mts +17 -22
  113. package/dist/substrate/manifest.mjs +4 -6
  114. package/dist/substrate/manifest.mjs.map +1 -1
  115. package/dist/substrate/plugin-ctx.d.mts +6 -6
  116. package/dist/substrate/plugin-ctx.mjs.map +1 -1
  117. package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
  118. package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
  119. package/dist/substrate/runtime/paths.mjs +1 -1
  120. package/dist/surfaces/cli/command-tree.mjs +6 -6
  121. package/dist/surfaces/cli/command-tree.mjs.map +1 -1
  122. package/dist/surfaces/cli/index.mjs +6 -4
  123. package/dist/surfaces/cli/index.mjs.map +1 -1
  124. package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
  125. package/package.json +1 -1
  126. package/dist/cli/wirings/dump-ids.mjs +0 -86
  127. package/dist/cli/wirings/dump-ids.mjs.map +0 -1
  128. package/dist/orchestrators/codegen/id-config.d.mts +0 -1
  129. package/dist/orchestrators/codegen/id-config.mjs +0 -107
  130. package/dist/orchestrators/codegen/id-config.mjs.map +0 -1
  131. package/dist/orchestrators/codegen/output-location.mjs +0 -32
  132. package/dist/orchestrators/codegen/output-location.mjs.map +0 -1
@@ -21,12 +21,12 @@ interface PluginCtx {
21
21
  * so it cannot be a static spec field; that is exactly why it is a
22
22
  * buffered verb). Since codegen was decoupled from boot this is the
23
23
  * LIVE half ONLY: at boot the orchestrator projects the decl into the
24
- * loadable id-config (`assembleIdConfig`) and, for dev-only decls,
25
- * writes the `generated-extras` tree (`emitExtras`) it does NOT write
26
- * the committed `src/generated` tree. That committed, stack-free tree is
27
- * emitted separately by the `devstack codegen` verb from the plugin
28
- * spec's `staticCodegen` hook, derived from the SAME `ConfigBindingSet`.
29
- * Returns void (the orchestrator owns rendering). */
24
+ * loadable deployment (`assembleDeployment`) folding both typed fields
25
+ * and the values-only channels (dev accounts, the dev-wallet connection)
26
+ * — and does NOT write the committed `src/generated` tree. That committed,
27
+ * stack-free tree is emitted separately by the `devstack codegen` verb
28
+ * from the plugin spec's `staticCodegen` hook, derived from the SAME
29
+ * `ConfigBindingSet`. Returns void (the orchestrator owns rendering). */
30
30
  readonly codegen: <E extends string>(decl: CodegenableDecl<E>) => void;
31
31
  /** Buffered: declare a routable endpoint for the router orchestrator.
32
32
  * Replayed in the supervisor frame after a successful `start`.
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-ctx.mjs","names":[],"sources":["../../src/substrate/plugin-ctx.ts"],"sourcesContent":["// PluginCtx — the minimal, closed, typed plugin-authoring surface.\n//\n// A plugin contributes to the stack through inline typed verbs it calls\n// from `start`. The plugin reaches this `ctx` by `yield* PluginContext` (an Effect\n// service tag, declared at the bottom of this file) — exactly like any\n// other substrate service it `yield*`s (`ContainerRuntimeService`,\n// `IdentityContext`, …). The supervisor PROVIDES a freshly-built\n// per-plugin ctx into `start`'s requirement (R) channel via\n// `Effect.provideService(start(deps), PluginContext, ctx)`. Delivering\n// ctx through the requirement channel — rather than as a 2nd positional\n// `start` argument — keeps `start` SINGLE-arg, which restores automatic\n// contextual typing of `deps` (no per-plugin `deps:` annotations), while\n// `ctx` stays always-present (the supervisor never omits it).\n//\n// -----------------------------------------------------------------------------\n// INV-5 — the closed 5-key set\n// -----------------------------------------------------------------------------\n//\n// `PluginCtx` has EXACTLY these 5 readonly keys and no more:\n//\n// codegen · endpoint · snapshotExtra · publish · provides\n//\n// This is a HARD invariant (pinned by `plugin-ctx-keyset.test-d.ts`).\n// Growing the set re-builds a god-object surface. The rule for what may\n// live here:\n//\n// - `codegen` / `endpoint` / `snapshotExtra` / `publish` are the four\n// BUFFERED declarative verbs. Their backing services\n// (CodegenOrchestratorService, RouterService +\n// ManifestEndpointRegistryService, SnapshotOrchestratorService, the\n// projection sink) are SUPERVISOR-FRAME-ONLY — they are absent from\n// plugin scope. A verb call therefore cannot run its backing\n// service inline; it pushes a typed buffer entry that the supervisor\n// REPLAYS in its own frame after the plugin's `start` succeeds.\n// - `provides` is the strategy bus writer (the faucet pattern\n// generalized): a plugin contributes to a sibling's\n// capability-keyed registry without a dep-graph edge. Siblings READ\n// it with `yield* StrategyRegistryService` directly (no ctx verb).\n//\n// What stays OUT of `ctx`, and WHY: every other substrate service a\n// plugin needs is already in plugin scope and is reached with\n// `yield* Service` directly — `CacheService` (whose `.publish` is the\n// folded-in artifact-publisher cycle),\n// `ContainerRuntimeService`, `IdentityContext`,\n// `PortBrokerService`, `LeaseBrokerService`, etc. The infra\n// orchestrators that are NOT in plugin scope\n// (Snapshot/Codegen/Router/ManifestEndpoint) are precisely the four\n// buffered verbs above — and infra contributions stay `yield*`-shaped\n// declarations, NEVER imperative `ctx.tx`-style Sui leaks. Adding a\n// service to `ctx` that a plugin could already `yield*` is the drift\n// INV-5 forbids.\n\nimport { Context } from 'effect';\n\nimport type { CodegenableDecl } from '../contracts/codegenable.ts';\nimport type { ProjectionDecl } from '../contracts/projection.ts';\nimport type { RoutableDecl } from '../contracts/routable.ts';\nimport type { SnapshotableDecl } from '../contracts/snapshotable.ts';\nimport type { StrategyContributorDecl } from '../contracts/strategy-contributor.ts';\n\n/**\n * The CLOSED union of contribution decls a plugin emits via the four\n * buffered verbs + `provides`. Discriminated by `kind`. A plugin's `start`\n * emits these inline — either by direct `ctx.*` verb calls or, for a\n * conditional/variadic set, through the shared {@link emitContributions}\n * router below. The supervisor's static dispatch switches over exactly\n * these five kinds.\n */\nexport type Contribution =\n\t| CodegenableDecl<string>\n\t| RoutableDecl\n\t| SnapshotableDecl\n\t| ProjectionDecl\n\t| StrategyContributorDecl<string, unknown>;\n\n/**\n * The ONE shared contribution router. A plugin's `start` builds an ordered\n * (possibly conditional/variadic) list of {@link Contribution} decls and\n * feeds it here; this routes each decl to its matching `ctx` verb by its\n * `kind` discriminant, IN LIST ORDER:\n *\n * snapshotable → `ctx.snapshotExtra`\n * codegenable → `ctx.codegen`\n * routable → `ctx.endpoint`\n * projection → `ctx.publish`\n * strategy-contributor → `ctx.provides`\n *\n * This is the single exhaustive `switch` over the closed `Contribution`\n * union — replacing the per-plugin `build*`/`emit*` wrapper pairs that each\n * re-implemented this same routing. The supervisor mirrors this dispatch\n * when it REPLAYS the buffered decls (`acquire-node.ts`); the verbs simply\n * buffer here. For a fixed, unconditional set of decls a plugin may prefer\n * direct `ctx.*` calls — but any conditional or variadic emission should\n * route through this helper so the routing lives in exactly one place.\n *\n * Exhaustive: the `default` arm narrows `decl` to `never`, so adding a\n * sixth `Contribution` kind is a compile error here until handled.\n */\nexport const emitContributions = (ctx: PluginCtx, decls: ReadonlyArray<Contribution>): void => {\n\tfor (const decl of decls) {\n\t\tswitch (decl.kind) {\n\t\t\tcase 'snapshotable':\n\t\t\t\tctx.snapshotExtra(decl);\n\t\t\t\tbreak;\n\t\t\tcase 'codegenable':\n\t\t\t\tctx.codegen(decl);\n\t\t\t\tbreak;\n\t\t\tcase 'routable':\n\t\t\t\tctx.endpoint(decl);\n\t\t\t\tbreak;\n\t\t\tcase 'projection':\n\t\t\t\tctx.publish(decl);\n\t\t\t\tbreak;\n\t\t\tcase 'strategy-contributor':\n\t\t\t\tctx.provides(decl);\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tconst _exhaustive: never = decl;\n\t\t\t\tvoid _exhaustive;\n\t\t\t}\n\t\t}\n\t}\n};\n\n/**\n * The minimal typed plugin-authoring context. A plugin reaches it from\n * `start` with `const ctx = yield* PluginContext` (the service tag\n * below); the supervisor provides a per-plugin instance into `start`'s\n * requirement channel.\n *\n * EXACTLY 5 keys (INV-5 — see file header). Do not add a 6th.\n */\nexport interface PluginCtx {\n\t/** Buffered: contribute the plugin's LIVE codegen decl to the codegen\n\t * orchestrator. Replayed in the supervisor frame after a successful\n\t * `start` (it carries post-acquire runtime state — resolved ids, rpc —\n\t * so it cannot be a static spec field; that is exactly why it is a\n\t * buffered verb). Since codegen was decoupled from boot this is the\n\t * LIVE half ONLY: at boot the orchestrator projects the decl into the\n\t * loadable id-config (`assembleIdConfig`) and, for dev-only decls,\n\t * writes the `generated-extras` tree (`emitExtras`) — it does NOT write\n\t * the committed `src/generated` tree. That committed, stack-free tree is\n\t * emitted separately by the `devstack codegen` verb from the plugin\n\t * spec's `staticCodegen` hook, derived from the SAME `ConfigBindingSet`.\n\t * Returns void (the orchestrator owns rendering). */\n\treadonly codegen: <E extends string>(decl: CodegenableDecl<E>) => void;\n\t/** Buffered: declare a routable endpoint for the router orchestrator.\n\t * Replayed in the supervisor frame after a successful `start`.\n\t * Returns VOID — no URL is echoed back to the plugin (the router\n\t * mints and publishes the endpoint event itself). */\n\treadonly endpoint: (decl: RoutableDecl) => void;\n\t/** Buffered: declare extra snapshot subtrees / managed containers /\n\t * identity-guard hooks for the snapshot orchestrator. Replayed in\n\t * the supervisor frame after a successful `start`. */\n\treadonly snapshotExtra: (decl: SnapshotableDecl) => void;\n\t/** Buffered: publish a name-blind projection event through the\n\t * projection sink. Replayed in the supervisor frame after a\n\t * successful `start`. */\n\treadonly publish: (decl: ProjectionDecl) => void;\n\t/** Strategy bus (write): contribute a strategy under a capability\n\t * key for a sibling to read via `StrategyRegistryService`. Registers\n\t * on the scope-local StrategyRegistry, publishes `strategy.registered`,\n\t * and arms a finalizer publishing `strategy.unregistered`. */\n\treadonly provides: <K extends string, S>(decl: StrategyContributorDecl<K, S>) => void;\n}\n\n/**\n * Effect service tag carrying the per-plugin {@link PluginCtx}.\n *\n * A plugin's `start` body reaches its ctx with `const ctx = yield*\n * PluginContext`. The supervisor builds a fresh per-plugin ctx (+ its\n * replay buffer) in `runtime/supervisor/acquire-node.ts` and PROVIDES it\n * into `start`'s requirement channel\n * (`Effect.provideService(start(deps), PluginContext, ctx)`), so the tag\n * is always satisfied for code the supervisor runs.\n *\n * This ambient requirement lives only in `start`'s R-channel; it does NOT\n * surface in the public `Plugin<Id, Value, Needs>` contract (`Needs` is\n * `dependsOn` only — see `plugin.ts`). Declaring it as a service rather\n * than a `start` argument is what keeps `start` single-arg, restoring\n * automatic `deps` inference.\n */\nexport class PluginContext extends Context.Service<PluginContext, PluginCtx>()(\n\t'@devstack/substrate/PluginContext',\n) {}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAkGA,MAAa,qBAAqB,KAAgB,UAA6C;CAC9F,KAAK,MAAM,QAAQ,OAClB,QAAQ,KAAK,MAAb;EACC,KAAK;GACJ,IAAI,cAAc,IAAI;GACtB;EACD,KAAK;GACJ,IAAI,QAAQ,IAAI;GAChB;EACD,KAAK;GACJ,IAAI,SAAS,IAAI;GACjB;EACD,KAAK;GACJ,IAAI,QAAQ,IAAI;GAChB;EACD,KAAK;GACJ,IAAI,SAAS,IAAI;GACjB;EACD;CAID;AAEF;;;;;;;;;;;;;;;;;AA4DA,IAAa,gBAAb,cAAmC,QAAQ,QAAkC,CAAC,CAC7E,mCACD,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"plugin-ctx.mjs","names":[],"sources":["../../src/substrate/plugin-ctx.ts"],"sourcesContent":["// PluginCtx — the minimal, closed, typed plugin-authoring surface.\n//\n// A plugin contributes to the stack through inline typed verbs it calls\n// from `start`. The plugin reaches this `ctx` by `yield* PluginContext` (an Effect\n// service tag, declared at the bottom of this file) — exactly like any\n// other substrate service it `yield*`s (`ContainerRuntimeService`,\n// `IdentityContext`, …). The supervisor PROVIDES a freshly-built\n// per-plugin ctx into `start`'s requirement (R) channel via\n// `Effect.provideService(start(deps), PluginContext, ctx)`. Delivering\n// ctx through the requirement channel — rather than as a 2nd positional\n// `start` argument — keeps `start` SINGLE-arg, which restores automatic\n// contextual typing of `deps` (no per-plugin `deps:` annotations), while\n// `ctx` stays always-present (the supervisor never omits it).\n//\n// -----------------------------------------------------------------------------\n// INV-5 — the closed 5-key set\n// -----------------------------------------------------------------------------\n//\n// `PluginCtx` has EXACTLY these 5 readonly keys and no more:\n//\n// codegen · endpoint · snapshotExtra · publish · provides\n//\n// This is a HARD invariant (pinned by `plugin-ctx-keyset.test-d.ts`).\n// Growing the set re-builds a god-object surface. The rule for what may\n// live here:\n//\n// - `codegen` / `endpoint` / `snapshotExtra` / `publish` are the four\n// BUFFERED declarative verbs. Their backing services\n// (CodegenOrchestratorService, RouterService +\n// ManifestEndpointRegistryService, SnapshotOrchestratorService, the\n// projection sink) are SUPERVISOR-FRAME-ONLY — they are absent from\n// plugin scope. A verb call therefore cannot run its backing\n// service inline; it pushes a typed buffer entry that the supervisor\n// REPLAYS in its own frame after the plugin's `start` succeeds.\n// - `provides` is the strategy bus writer (the faucet pattern\n// generalized): a plugin contributes to a sibling's\n// capability-keyed registry without a dep-graph edge. Siblings READ\n// it with `yield* StrategyRegistryService` directly (no ctx verb).\n//\n// What stays OUT of `ctx`, and WHY: every other substrate service a\n// plugin needs is already in plugin scope and is reached with\n// `yield* Service` directly — `CacheService` (whose `.publish` is the\n// folded-in artifact-publisher cycle),\n// `ContainerRuntimeService`, `IdentityContext`,\n// `PortBrokerService`, `LeaseBrokerService`, etc. The infra\n// orchestrators that are NOT in plugin scope\n// (Snapshot/Codegen/Router/ManifestEndpoint) are precisely the four\n// buffered verbs above — and infra contributions stay `yield*`-shaped\n// declarations, NEVER imperative `ctx.tx`-style Sui leaks. Adding a\n// service to `ctx` that a plugin could already `yield*` is the drift\n// INV-5 forbids.\n\nimport { Context } from 'effect';\n\nimport type { CodegenableDecl } from '../contracts/codegenable.ts';\nimport type { ProjectionDecl } from '../contracts/projection.ts';\nimport type { RoutableDecl } from '../contracts/routable.ts';\nimport type { SnapshotableDecl } from '../contracts/snapshotable.ts';\nimport type { StrategyContributorDecl } from '../contracts/strategy-contributor.ts';\n\n/**\n * The CLOSED union of contribution decls a plugin emits via the four\n * buffered verbs + `provides`. Discriminated by `kind`. A plugin's `start`\n * emits these inline — either by direct `ctx.*` verb calls or, for a\n * conditional/variadic set, through the shared {@link emitContributions}\n * router below. The supervisor's static dispatch switches over exactly\n * these five kinds.\n */\nexport type Contribution =\n\t| CodegenableDecl<string>\n\t| RoutableDecl\n\t| SnapshotableDecl\n\t| ProjectionDecl\n\t| StrategyContributorDecl<string, unknown>;\n\n/**\n * The ONE shared contribution router. A plugin's `start` builds an ordered\n * (possibly conditional/variadic) list of {@link Contribution} decls and\n * feeds it here; this routes each decl to its matching `ctx` verb by its\n * `kind` discriminant, IN LIST ORDER:\n *\n * snapshotable → `ctx.snapshotExtra`\n * codegenable → `ctx.codegen`\n * routable → `ctx.endpoint`\n * projection → `ctx.publish`\n * strategy-contributor → `ctx.provides`\n *\n * This is the single exhaustive `switch` over the closed `Contribution`\n * union — replacing the per-plugin `build*`/`emit*` wrapper pairs that each\n * re-implemented this same routing. The supervisor mirrors this dispatch\n * when it REPLAYS the buffered decls (`acquire-node.ts`); the verbs simply\n * buffer here. For a fixed, unconditional set of decls a plugin may prefer\n * direct `ctx.*` calls — but any conditional or variadic emission should\n * route through this helper so the routing lives in exactly one place.\n *\n * Exhaustive: the `default` arm narrows `decl` to `never`, so adding a\n * sixth `Contribution` kind is a compile error here until handled.\n */\nexport const emitContributions = (ctx: PluginCtx, decls: ReadonlyArray<Contribution>): void => {\n\tfor (const decl of decls) {\n\t\tswitch (decl.kind) {\n\t\t\tcase 'snapshotable':\n\t\t\t\tctx.snapshotExtra(decl);\n\t\t\t\tbreak;\n\t\t\tcase 'codegenable':\n\t\t\t\tctx.codegen(decl);\n\t\t\t\tbreak;\n\t\t\tcase 'routable':\n\t\t\t\tctx.endpoint(decl);\n\t\t\t\tbreak;\n\t\t\tcase 'projection':\n\t\t\t\tctx.publish(decl);\n\t\t\t\tbreak;\n\t\t\tcase 'strategy-contributor':\n\t\t\t\tctx.provides(decl);\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tconst _exhaustive: never = decl;\n\t\t\t\tvoid _exhaustive;\n\t\t\t}\n\t\t}\n\t}\n};\n\n/**\n * The minimal typed plugin-authoring context. A plugin reaches it from\n * `start` with `const ctx = yield* PluginContext` (the service tag\n * below); the supervisor provides a per-plugin instance into `start`'s\n * requirement channel.\n *\n * EXACTLY 5 keys (INV-5 — see file header). Do not add a 6th.\n */\nexport interface PluginCtx {\n\t/** Buffered: contribute the plugin's LIVE codegen decl to the codegen\n\t * orchestrator. Replayed in the supervisor frame after a successful\n\t * `start` (it carries post-acquire runtime state — resolved ids, rpc —\n\t * so it cannot be a static spec field; that is exactly why it is a\n\t * buffered verb). Since codegen was decoupled from boot this is the\n\t * LIVE half ONLY: at boot the orchestrator projects the decl into the\n\t * loadable deployment (`assembleDeployment`) folding both typed fields\n\t * and the values-only channels (dev accounts, the dev-wallet connection)\n\t * and does NOT write the committed `src/generated` tree. That committed,\n\t * stack-free tree is emitted separately by the `devstack codegen` verb\n\t * from the plugin spec's `staticCodegen` hook, derived from the SAME\n\t * `ConfigBindingSet`. Returns void (the orchestrator owns rendering). */\n\treadonly codegen: <E extends string>(decl: CodegenableDecl<E>) => void;\n\t/** Buffered: declare a routable endpoint for the router orchestrator.\n\t * Replayed in the supervisor frame after a successful `start`.\n\t * Returns VOID — no URL is echoed back to the plugin (the router\n\t * mints and publishes the endpoint event itself). */\n\treadonly endpoint: (decl: RoutableDecl) => void;\n\t/** Buffered: declare extra snapshot subtrees / managed containers /\n\t * identity-guard hooks for the snapshot orchestrator. Replayed in\n\t * the supervisor frame after a successful `start`. */\n\treadonly snapshotExtra: (decl: SnapshotableDecl) => void;\n\t/** Buffered: publish a name-blind projection event through the\n\t * projection sink. Replayed in the supervisor frame after a\n\t * successful `start`. */\n\treadonly publish: (decl: ProjectionDecl) => void;\n\t/** Strategy bus (write): contribute a strategy under a capability\n\t * key for a sibling to read via `StrategyRegistryService`. Registers\n\t * on the scope-local StrategyRegistry, publishes `strategy.registered`,\n\t * and arms a finalizer publishing `strategy.unregistered`. */\n\treadonly provides: <K extends string, S>(decl: StrategyContributorDecl<K, S>) => void;\n}\n\n/**\n * Effect service tag carrying the per-plugin {@link PluginCtx}.\n *\n * A plugin's `start` body reaches its ctx with `const ctx = yield*\n * PluginContext`. The supervisor builds a fresh per-plugin ctx (+ its\n * replay buffer) in `runtime/supervisor/acquire-node.ts` and PROVIDES it\n * into `start`'s requirement channel\n * (`Effect.provideService(start(deps), PluginContext, ctx)`), so the tag\n * is always satisfied for code the supervisor runs.\n *\n * This ambient requirement lives only in `start`'s R-channel; it does NOT\n * surface in the public `Plugin<Id, Value, Needs>` contract (`Needs` is\n * `dependsOn` only — see `plugin.ts`). Declaring it as a service rather\n * than a `start` argument is what keeps `start` single-arg, restoring\n * automatic `deps` inference.\n */\nexport class PluginContext extends Context.Service<PluginContext, PluginCtx>()(\n\t'@devstack/substrate/PluginContext',\n) {}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAkGA,MAAa,qBAAqB,KAAgB,UAA6C;CAC9F,KAAK,MAAM,QAAQ,OAClB,QAAQ,KAAK,MAAb;EACC,KAAK;GACJ,IAAI,cAAc,IAAI;GACtB;EACD,KAAK;GACJ,IAAI,QAAQ,IAAI;GAChB;EACD,KAAK;GACJ,IAAI,SAAS,IAAI;GACjB;EACD,KAAK;GACJ,IAAI,QAAQ,IAAI;GAChB;EACD,KAAK;GACJ,IAAI,SAAS,IAAI;GACjB;EACD;CAID;AAEF;;;;;;;;;;;;;;;;;AA4DA,IAAa,gBAAb,cAAmC,QAAQ,QAAkC,CAAC,CAC7E,mCACD,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"liveness.mjs","names":["nodeHostname"],"sources":["../../../../src/substrate/runtime/cross-process/liveness.ts"],"sourcesContent":["// PID + start-time liveness check.\n//\n// Architecture § Cross-process safety protocol § Claim protocol step 3:\n// \"process exists AND its start-time (as read from `/proc/<pid>/stat` on\n// Linux, `ps -o lstart` on macOS, or equivalent) matches the recorded\n// `startTime`.\"\n//\n// Devstack is single-host: two checks combine. `kill(pid, 0)`\n// determines pid-in-use; `ps -o lstart` confirms it's the SAME process\n// (defending against pid reuse on long-uptime machines).\n//\n// This module is the ONLY place in the substrate that calls into\n// `process.kill` and shells out for start times. Roster/snapshot\n// reservation/stack-lock all consult it through the typed predicates.\n\nimport { execFileSync } from 'node:child_process';\nimport { hostname as nodeHostname } from 'node:os';\n\nimport { Context, Effect, Layer } from 'effect';\n\nimport type { RosterHolder } from '../../cross-process.ts';\nimport { selfPid } from './self-pid.ts';\n\n/** Cheap \"send signal 0\" pid liveness. ESRCH → dead; EPERM → alive\n * (foreign-user pid on shared dev hosts). Any other errno is\n * conservatively treated as dead so we don't refuse cleanup on exotic\n * platforms. */\nexport const isPidAlive = (pid: number): boolean => {\n\tif (!Number.isFinite(pid) || pid <= 0) return false;\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\treturn code === 'EPERM';\n\t}\n};\n\n/** Per-sweep cache mapping `pid → start-time stamp` (or `null` when the\n * pid is gone/unprobable). Callers that issue many probes in a single\n * sweep (roster step-3 stale-eviction, container-claim ledger prune,\n * etc.) instantiate one cache per pass and thread it through every\n * probe call so the same pid forks `ps`/`tasklist` AT MOST once per\n * sweep. Single-shot callers omit the cache and get the no-cache\n * behavior.\n *\n * Discriminator is `Map.has(pid)` — a cached `null` is a real result\n * (the pid is missing) and skipping it would re-fork pointlessly. */\nexport type LivenessCache = Map<number, number | null>;\n\n/** Best-effort start-time stamp for `pid`. Returns `null` when the\n * process is gone OR the platform can't produce a time. The stamp\n * itself is opaque text — its only contract is bytewise equality with\n * a previously-recorded sibling stamp.\n *\n * Architecture § Cross-process safety protocol §3 — `ps -o lstart` on\n * macOS/Linux, `tasklist` confirmation on Windows.\n *\n * Pass `cache` to reuse a previously-probed result across a sweep\n * pass — same `pid` only forks the underlying utility once. */\nexport const processStartTime = (pid: number, cache?: LivenessCache): number | null => {\n\tif (!Number.isFinite(pid) || pid <= 0) return null;\n\t// Our own pid's startTime never changes during the process\n\t// lifetime — cache the first SUCCESSFUL probe forever. Eliminates\n\t// the `ps` spawn under high in-process contention (e.g. N fibers\n\t// fighting over a single cross-process lock all probing each\n\t// other's shared pid). Without this cache, 8 concurrent fibers\n\t// each fork `ps -o lstart` with a 2s timeout, compounding into\n\t// seconds of latency that exhaust the claim budget (review fix\n\t// phase 22f reclaim-stress reproducer caught it).\n\t//\n\t// Only a non-null result is memoized: a `null` here means the\n\t// probe transiently FAILED (spawn hiccup / 2s timeout under load),\n\t// not that the pid is gone — it's our own pid, which is alive by\n\t// definition. Caching that null forever would surrender this\n\t// process's PID-reuse protection (it would write `startTime: null`\n\t// to disk for its whole lifetime) on a single flaky fork. Leaving\n\t// the cache UNSET re-probes on the next call; an occasional extra\n\t// `ps` on a previously-failing probe is negligible versus losing\n\t// reuse protection for the lifetime.\n\tif (pid === selfPid()) {\n\t\tif (ownStartTimeCache !== UNSET) return ownStartTimeCache;\n\t\tconst probed = probeStartTimeUncached(pid);\n\t\tif (probed !== null) ownStartTimeCache = probed;\n\t\treturn probed;\n\t}\n\tif (cache?.has(pid)) {\n\t\t// Map.get is `T | undefined` — but `has` is true, so the value\n\t\t// is one of the cached results (a `number` or `null`).\n\t\treturn cache.get(pid) ?? null;\n\t}\n\tconst probed = probeStartTimeUncached(pid);\n\tcache?.set(pid, probed);\n\treturn probed;\n};\n\nconst UNSET: unique symbol = Symbol('UNSET');\n// Only a successful (non-null) self-pid probe is ever stored here; a\n// failed probe leaves this UNSET so the next call re-probes.\nlet ownStartTimeCache: number | typeof UNSET = UNSET;\n\n/** Inner probe — always forks the platform utility. Split out so the\n * cache branch in `processStartTime` stays a single read/write. */\nconst probeStartTimeUncached = (pid: number): number | null => {\n\tif (process.platform === 'win32') {\n\t\ttry {\n\t\t\tconst out = execFileSync('tasklist', ['/fi', `PID eq ${pid}`, '/fo', 'csv', '/nh'], {\n\t\t\t\tencoding: 'utf8',\n\t\t\t\ttimeout: 2000,\n\t\t\t\tstdio: ['ignore', 'pipe', 'ignore'],\n\t\t\t});\n\t\t\t// Windows path: just hash the line. Windows PID reuse on uptime\n\t\t\t// is a known v1 trade-off; the architecture says the protocol\n\t\t\t// stays POSIX-first.\n\t\t\treturn out.trim().startsWith('\"') ? hashStartTimeStamp(out.trim()) : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\ttry {\n\t\tconst out = execFileSync('ps', ['-o', 'lstart=', '-p', String(pid)], {\n\t\t\tencoding: 'utf8',\n\t\t\ttimeout: 2000,\n\t\t\tstdio: ['ignore', 'pipe', 'ignore'],\n\t\t});\n\t\tconst trimmed = out.trim();\n\t\treturn trimmed.length > 0 ? hashStartTimeStamp(trimmed) : null;\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n/** Stable hash of a start-time string. Reduces the textual `ps -o\n * lstart=` output to a single number so the roster carries a fixed-\n * width integer (the architecture's `startTime` field is `number`).\n *\n * FNV-1a 32-bit; collisions are negligible at the (pid, host, second)\n * granularity we ever compare. */\nconst hashStartTimeStamp = (stamp: string): number => {\n\tlet h = 2166136261;\n\tfor (let i = 0; i < stamp.length; i++) {\n\t\th ^= stamp.charCodeAt(i);\n\t\th = (h * 16777619) >>> 0;\n\t}\n\t// Stay inside Number.MAX_SAFE_INTEGER's positive 32-bit range so the\n\t// number round-trips through JSON without precision loss.\n\treturn h >>> 0;\n};\n\n/** Liveness probe for a roster holder. Used by the claim-protocol\n * sweep AND the stack-lock one-shot orphan check.\n *\n * Discipline (devstack is single-host): pid must be live AND start-time\n * must match the recorded stamp.\n *\n * Returns `'alive' | 'dead'`. Never throws. Effect-wrapped so callers\n * compose under spans. */\nexport const checkHolderLiveness = Effect.fn('cross-process.liveness.checkHolderLiveness')(\n\tfunction* (holder: RosterHolder, cache?: LivenessCache) {\n\t\tif (!isPidAlive(holder.pid)) return 'dead' as const;\n\t\tconst probedStart = processStartTime(holder.pid, cache);\n\t\t// pid alive but no stamp probable → conservative: ALIVE\n\t\t// (we have nothing to dispute the recorded startTime with).\n\t\tif (probedStart === null) return 'alive' as const;\n\t\t// Holder recorded a `null` startTime (writer's platform couldn't\n\t\t// probe at the time). The (pid, hostname) pair carries the\n\t\t// identity; same conservative policy as the probedStart-null\n\t\t// branch. Mismatching a real probed stamp against a recorded\n\t\t// `null` would otherwise harvest live holders as \"dead\".\n\t\tif (holder.startTime === null) return 'alive' as const;\n\t\treturn probedStart === holder.startTime ? ('alive' as const) : ('dead' as const);\n\t},\n);\n\n/** Build a holder snapshot for THIS process. The intent defaults to\n * `'normal'`; the snapshot bounce flips it to `'snapshot'`\n * under the stack lock and back when the bounce completes.\n *\n * A `null` `startTime` propagates verbatim — readers (`isOwnEntry`\n * in `roster.ts`, `checkHolderLiveness` above) honor the null-\n * conservative branch. Writing `0` for \"unprobable\" would cause a\n * false-dead harvest: a subsequent probe yielding a real stamp would\n * mismatch the recorded `0` and the process would fail to recognize\n * its own entry. */\nexport const ownHolder = (intent: 'normal' | 'snapshot' = 'normal'): RosterHolder => {\n\tconst pid = selfPid();\n\tconst startTime = processStartTime(pid);\n\treturn {\n\t\tpid,\n\t\tstartTime,\n\t\thostname: nodeHostname(),\n\t\tclaimedAt: Date.now(),\n\t\theartbeatAt: Date.now(),\n\t\tintent,\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// LivenessProbeScope — per-sweep cache, lifted out of the Map parameter.\n// -----------------------------------------------------------------------------\n//\n// The bare `LivenessCache = Map<pid, stamp | null>` was threaded through\n// `roster.sweepStaleHolders` as an explicit parameter, which is fine for\n// one site but doesn't scale to the other sweep loops (dispatch routes,\n// doctor probes, lifecycle-prune). Promoting the cache into a service\n// lets a sweep loop `yield* LivenessProbeScope` once and call the probe\n// methods without re-passing the Map. The optional-Map params on\n// `processStartTime` / `checkHolderLiveness` stay so unmigrated callers\n// can keep working from outside an Effect.\n\n/** Methods a per-sweep scope exposes — the captured cache backs both\n * so the same `pid` forks `ps`/`tasklist` at most once per scope. */\nexport interface LivenessProbeScopeShape {\n\treadonly probeStartTime: (pid: number) => number | null;\n\treadonly probeHolderLiveness: (holder: RosterHolder) => Effect.Effect<'alive' | 'dead'>;\n}\n\n/** A per-sweep liveness scope. Callers `yield* LivenessProbeScope`\n * once per sweep (typically inside an `Effect.scoped` block that\n * wraps the loop) and call `probeStartTime` / `probeHolderLiveness`\n * on each holder without threading the Map manually. */\nexport class LivenessProbeScope extends Context.Service<\n\tLivenessProbeScope,\n\tLivenessProbeScopeShape\n>()('@devstack/substrate/cross-process/LivenessProbeScope') {}\n\n/** Construct a fresh `LivenessProbeScope` whose underlying cache is\n * private to this layer — every yielding scope gets its own Map. */\nexport const layerLivenessProbeScope: Layer.Layer<LivenessProbeScope> = Layer.effect(\n\tLivenessProbeScope,\n\tEffect.sync(() => {\n\t\tconst cache: LivenessCache = new Map();\n\t\treturn LivenessProbeScope.of({\n\t\t\tprobeStartTime: (pid) => processStartTime(pid, cache),\n\t\t\tprobeHolderLiveness: (holder) => checkHolderLiveness(holder, cache),\n\t\t});\n\t}),\n);\n"],"mappings":";;;;;;;;;AA2BA,MAAa,cAAc,QAAyB;CACnD,IAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,GAAG,OAAO;CAC9C,IAAI;EACH,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACR,SAAS,KAAK;EAEb,OADc,IAA8B,SAC5B;CACjB;AACD;;;;;;;;;;;AAwBA,MAAa,oBAAoB,KAAa,UAAyC;CACtF,IAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,GAAG,OAAO;CAmB9C,IAAI,QAAQ,QAAQ,GAAG;EACtB,IAAI,sBAAsB,OAAO,OAAO;EACxC,MAAM,SAAS,uBAAuB,GAAG;EACzC,IAAI,WAAW,MAAM,oBAAoB;EACzC,OAAO;CACR;CACA,IAAI,OAAO,IAAI,GAAG,GAGjB,OAAO,MAAM,IAAI,GAAG,KAAK;CAE1B,MAAM,SAAS,uBAAuB,GAAG;CACzC,OAAO,IAAI,KAAK,MAAM;CACtB,OAAO;AACR;AAEA,MAAM,QAAuB,OAAO,OAAO;AAG3C,IAAI,oBAA2C;;;AAI/C,MAAM,0BAA0B,QAA+B;CAC9D,IAAI,QAAQ,aAAa,SACxB,IAAI;EACH,MAAM,MAAM,aAAa,YAAY;GAAC;GAAO,UAAU;GAAO;GAAO;GAAO;EAAK,GAAG;GACnF,UAAU;GACV,SAAS;GACT,OAAO;IAAC;IAAU;IAAQ;GAAQ;EACnC,CAAC;EAID,OAAO,IAAI,KAAK,CAAC,CAAC,WAAW,IAAG,IAAI,mBAAmB,IAAI,KAAK,CAAC,IAAI;CACtE,QAAQ;EACP,OAAO;CACR;CAED,IAAI;EAMH,MAAM,UALM,aAAa,MAAM;GAAC;GAAM;GAAW;GAAM,OAAO,GAAG;EAAC,GAAG;GACpE,UAAU;GACV,SAAS;GACT,OAAO;IAAC;IAAU;IAAQ;GAAQ;EACnC,CACkB,CAAC,CAAC,KAAK;EACzB,OAAO,QAAQ,SAAS,IAAI,mBAAmB,OAAO,IAAI;CAC3D,QAAQ;EACP,OAAO;CACR;AACD;;;;;;;AAQA,MAAM,sBAAsB,UAA0B;CACrD,IAAI,IAAI;CACR,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACtC,KAAK,MAAM,WAAW,CAAC;EACvB,IAAK,IAAI,aAAc;CACxB;CAGA,OAAO,MAAM;AACd;;;;;;;;;AAUA,MAAa,sBAAsB,OAAO,GAAG,4CAA4C,CAAC,CACzF,WAAW,QAAsB,OAAuB;CACvD,IAAI,CAAC,WAAW,OAAO,GAAG,GAAG,OAAO;CACpC,MAAM,cAAc,iBAAiB,OAAO,KAAK,KAAK;CAGtD,IAAI,gBAAgB,MAAM,OAAO;CAMjC,IAAI,OAAO,cAAc,MAAM,OAAO;CACtC,OAAO,gBAAgB,OAAO,YAAa,UAAqB;AACjE,CACD;;;;;;;;;;;AAYA,MAAa,aAAa,SAAgC,aAA2B;CACpF,MAAM,MAAM,QAAQ;CAEpB,OAAO;EACN;EACA,WAHiB,iBAAiB,GAG1B;EACR,UAAUA,SAAa;EACvB,WAAW,KAAK,IAAI;EACpB,aAAa,KAAK,IAAI;EACtB;CACD;AACD;;;;;AA0BA,IAAa,qBAAb,cAAwC,QAAQ,QAG9C,CAAC,CAAC,sDAAsD,CAAC,CAAC,CAAC;;;AAI7D,MAAa,0BAA2D,MAAM,OAC7E,oBACA,OAAO,WAAW;CACjB,MAAM,wBAAuB,IAAI,IAAI;CACrC,OAAO,mBAAmB,GAAG;EAC5B,iBAAiB,QAAQ,iBAAiB,KAAK,KAAK;EACpD,sBAAsB,WAAW,oBAAoB,QAAQ,KAAK;CACnE,CAAC;AACF,CAAC,CACF"}
1
+ {"version":3,"file":"liveness.mjs","names":["nodeHostname"],"sources":["../../../../src/substrate/runtime/cross-process/liveness.ts"],"sourcesContent":["// PID + start-time liveness check.\n//\n// Architecture § Cross-process safety protocol § Claim protocol step 3:\n// \"process exists AND its start-time (as read from `/proc/<pid>/stat` on\n// Linux, `ps -o lstart` on macOS, or equivalent) matches the recorded\n// `startTime`.\"\n//\n// Devstack is single-host: two checks combine. `kill(pid, 0)`\n// determines pid-in-use; `ps -o lstart` confirms it's the SAME process\n// (defending against pid reuse on long-uptime machines).\n//\n// This module is the ONLY place in the substrate that calls into\n// `process.kill` and shells out for start times. Roster/snapshot\n// reservation/stack-lock all consult it through the typed predicates.\n\nimport { execFileSync } from 'node:child_process';\nimport { hostname as nodeHostname } from 'node:os';\n\nimport { Context, Effect, Layer } from 'effect';\n\nimport type { RosterHolder } from '../../cross-process.ts';\nimport { selfPid } from './self-pid.ts';\n\n/** Cheap \"send signal 0\" pid liveness. ESRCH → dead; EPERM → alive\n * (foreign-user pid on shared dev hosts). Any other errno is\n * conservatively treated as dead so we don't refuse cleanup on exotic\n * platforms. */\nexport const isPidAlive = (pid: number): boolean => {\n\tif (!Number.isFinite(pid) || pid <= 0) return false;\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\treturn code === 'EPERM';\n\t}\n};\n\n/** Per-sweep cache mapping `pid → start-time stamp` (or `null` when the\n * pid is gone/unprobable). Callers that issue many probes in a single\n * sweep (roster step-3 stale-eviction, container-claim ledger prune,\n * etc.) instantiate one cache per pass and thread it through every\n * probe call so the same pid forks `ps`/`tasklist` AT MOST once per\n * sweep. Single-shot callers omit the cache and get the no-cache\n * behavior.\n *\n * Discriminator is `Map.has(pid)` — a cached `null` is a real result\n * (the pid is missing) and skipping it would re-fork pointlessly. */\nexport type LivenessCache = Map<number, number | null>;\n\n/** Best-effort start-time stamp for `pid`. Returns `null` when the\n * process is gone OR the platform can't produce a time. The stamp\n * itself is opaque text — its only contract is bytewise equality with\n * a previously-recorded sibling stamp.\n *\n * Architecture § Cross-process safety protocol §3 — `ps -o lstart` on\n * macOS/Linux, `tasklist` confirmation on Windows.\n *\n * Pass `cache` to reuse a previously-probed result across a sweep\n * pass — same `pid` only forks the underlying utility once. */\nexport const processStartTime = (pid: number, cache?: LivenessCache): number | null => {\n\tif (!Number.isFinite(pid) || pid <= 0) return null;\n\t// Our own pid's startTime never changes during the process\n\t// lifetime — cache the first SUCCESSFUL probe forever. Eliminates\n\t// the `ps` spawn under high in-process contention (e.g. N fibers\n\t// fighting over a single cross-process lock all probing each\n\t// other's shared pid). Without this cache, 8 concurrent fibers\n\t// each fork `ps -o lstart` with a 2s timeout, compounding into\n\t// seconds of latency that exhaust the claim budget (review fix\n\t// phase 22f reclaim-stress reproducer caught it).\n\t//\n\t// Only a non-null result is memoized: a `null` here means the\n\t// probe transiently FAILED (spawn hiccup / 2s timeout under load),\n\t// not that the pid is gone — it's our own pid, which is alive by\n\t// definition. Caching that null forever would surrender this\n\t// process's PID-reuse protection (it would write `startTime: null`\n\t// to disk for its whole lifetime) on a single flaky fork. Leaving\n\t// the cache UNSET re-probes on the next call; an occasional extra\n\t// `ps` on a previously-failing probe is negligible versus losing\n\t// reuse protection for the lifetime.\n\tif (pid === selfPid()) {\n\t\tif (ownStartTimeCache !== UNSET) return ownStartTimeCache;\n\t\tconst probed = probeStartTimeUncached(pid);\n\t\tif (probed !== null) ownStartTimeCache = probed;\n\t\treturn probed;\n\t}\n\tif (cache?.has(pid)) {\n\t\t// Map.get is `T | undefined` — but `has` is true, so the value\n\t\t// is one of the cached results (a `number` or `null`).\n\t\treturn cache.get(pid) ?? null;\n\t}\n\tconst probed = probeStartTimeUncached(pid);\n\tcache?.set(pid, probed);\n\treturn probed;\n};\n\nconst UNSET: unique symbol = Symbol('UNSET');\n// Only a successful (non-null) self-pid probe is ever stored here; a\n// failed probe leaves this UNSET so the next call re-probes.\nlet ownStartTimeCache: number | typeof UNSET = UNSET;\n\n/** Inner probe — always forks the platform utility. Split out so the\n * cache branch in `processStartTime` stays a single read/write. */\nconst probeStartTimeUncached = (pid: number): number | null => {\n\tif (process.platform === 'win32') {\n\t\ttry {\n\t\t\tconst out = execFileSync('tasklist', ['/fi', `PID eq ${pid}`, '/fo', 'csv', '/nh'], {\n\t\t\t\tencoding: 'utf8',\n\t\t\t\ttimeout: 2000,\n\t\t\t\tstdio: ['ignore', 'pipe', 'ignore'],\n\t\t\t});\n\t\t\t// Windows path: just hash the line. Windows PID reuse on uptime\n\t\t\t// is a known v1 trade-off; the architecture says the protocol\n\t\t\t// stays POSIX-first.\n\t\t\treturn out.trim().startsWith('\"') ? hashStartTimeStamp(out.trim()) : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\ttry {\n\t\tconst out = execFileSync('ps', ['-o', 'lstart=', '-p', String(pid)], {\n\t\t\tencoding: 'utf8',\n\t\t\ttimeout: 2000,\n\t\t\tstdio: ['ignore', 'pipe', 'ignore'],\n\t\t});\n\t\tconst trimmed = out.trim();\n\t\treturn trimmed.length > 0 ? hashStartTimeStamp(trimmed) : null;\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n/** Stable hash of a start-time string. Reduces the textual `ps -o\n * lstart=` output to a single number so the roster carries a fixed-\n * width integer (the architecture's `startTime` field is `number`).\n *\n * FNV-1a 32-bit; collisions are negligible at the (pid, host, second)\n * granularity we ever compare. */\nconst hashStartTimeStamp = (stamp: string): number => {\n\tlet h = 2166136261;\n\tfor (let i = 0; i < stamp.length; i++) {\n\t\th ^= stamp.charCodeAt(i);\n\t\th = (h * 16777619) >>> 0;\n\t}\n\t// Stay inside Number.MAX_SAFE_INTEGER's positive 32-bit range so the\n\t// number round-trips through JSON without precision loss.\n\treturn h >>> 0;\n};\n\n/** Liveness probe for a roster holder. Used by the claim-protocol\n * sweep AND the stack-lock one-shot orphan check.\n *\n * Discipline (devstack is single-host): pid must be live AND start-time\n * must match the recorded stamp.\n *\n * Returns `'alive' | 'dead'`. Never throws. Effect-wrapped so callers\n * compose under spans. */\nexport const checkHolderLiveness = Effect.fn('cross-process.liveness.checkHolderLiveness')(\n\t// eslint-disable-next-line require-yield -- Effect.fn requires a generator; this probe is pure synchronous logic wrapped only for span composition, so it has no yield\n\tfunction* (holder: RosterHolder, cache?: LivenessCache) {\n\t\tif (!isPidAlive(holder.pid)) return 'dead' as const;\n\t\tconst probedStart = processStartTime(holder.pid, cache);\n\t\t// pid alive but no stamp probable → conservative: ALIVE\n\t\t// (we have nothing to dispute the recorded startTime with).\n\t\tif (probedStart === null) return 'alive' as const;\n\t\t// Holder recorded a `null` startTime (writer's platform couldn't\n\t\t// probe at the time). The (pid, hostname) pair carries the\n\t\t// identity; same conservative policy as the probedStart-null\n\t\t// branch. Mismatching a real probed stamp against a recorded\n\t\t// `null` would otherwise harvest live holders as \"dead\".\n\t\tif (holder.startTime === null) return 'alive' as const;\n\t\treturn probedStart === holder.startTime ? ('alive' as const) : ('dead' as const);\n\t},\n);\n\n/** Build a holder snapshot for THIS process. The intent defaults to\n * `'normal'`; the snapshot bounce flips it to `'snapshot'`\n * under the stack lock and back when the bounce completes.\n *\n * A `null` `startTime` propagates verbatim — readers (`isOwnEntry`\n * in `roster.ts`, `checkHolderLiveness` above) honor the null-\n * conservative branch. Writing `0` for \"unprobable\" would cause a\n * false-dead harvest: a subsequent probe yielding a real stamp would\n * mismatch the recorded `0` and the process would fail to recognize\n * its own entry. */\nexport const ownHolder = (intent: 'normal' | 'snapshot' = 'normal'): RosterHolder => {\n\tconst pid = selfPid();\n\tconst startTime = processStartTime(pid);\n\treturn {\n\t\tpid,\n\t\tstartTime,\n\t\thostname: nodeHostname(),\n\t\tclaimedAt: Date.now(),\n\t\theartbeatAt: Date.now(),\n\t\tintent,\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// LivenessProbeScope — per-sweep cache, lifted out of the Map parameter.\n// -----------------------------------------------------------------------------\n//\n// The bare `LivenessCache = Map<pid, stamp | null>` was threaded through\n// `roster.sweepStaleHolders` as an explicit parameter, which is fine for\n// one site but doesn't scale to the other sweep loops (dispatch routes,\n// doctor probes, lifecycle-prune). Promoting the cache into a service\n// lets a sweep loop `yield* LivenessProbeScope` once and call the probe\n// methods without re-passing the Map. The optional-Map params on\n// `processStartTime` / `checkHolderLiveness` stay so unmigrated callers\n// can keep working from outside an Effect.\n\n/** Methods a per-sweep scope exposes — the captured cache backs both\n * so the same `pid` forks `ps`/`tasklist` at most once per scope. */\nexport interface LivenessProbeScopeShape {\n\treadonly probeStartTime: (pid: number) => number | null;\n\treadonly probeHolderLiveness: (holder: RosterHolder) => Effect.Effect<'alive' | 'dead'>;\n}\n\n/** A per-sweep liveness scope. Callers `yield* LivenessProbeScope`\n * once per sweep (typically inside an `Effect.scoped` block that\n * wraps the loop) and call `probeStartTime` / `probeHolderLiveness`\n * on each holder without threading the Map manually. */\nexport class LivenessProbeScope extends Context.Service<\n\tLivenessProbeScope,\n\tLivenessProbeScopeShape\n>()('@devstack/substrate/cross-process/LivenessProbeScope') {}\n\n/** Construct a fresh `LivenessProbeScope` whose underlying cache is\n * private to this layer — every yielding scope gets its own Map. */\nexport const layerLivenessProbeScope: Layer.Layer<LivenessProbeScope> = Layer.effect(\n\tLivenessProbeScope,\n\tEffect.sync(() => {\n\t\tconst cache: LivenessCache = new Map();\n\t\treturn LivenessProbeScope.of({\n\t\t\tprobeStartTime: (pid) => processStartTime(pid, cache),\n\t\t\tprobeHolderLiveness: (holder) => checkHolderLiveness(holder, cache),\n\t\t});\n\t}),\n);\n"],"mappings":";;;;;;;;;AA2BA,MAAa,cAAc,QAAyB;CACnD,IAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,GAAG,OAAO;CAC9C,IAAI;EACH,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACR,SAAS,KAAK;EAEb,OADc,IAA8B,SAC5B;CACjB;AACD;;;;;;;;;;;AAwBA,MAAa,oBAAoB,KAAa,UAAyC;CACtF,IAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,GAAG,OAAO;CAmB9C,IAAI,QAAQ,QAAQ,GAAG;EACtB,IAAI,sBAAsB,OAAO,OAAO;EACxC,MAAM,SAAS,uBAAuB,GAAG;EACzC,IAAI,WAAW,MAAM,oBAAoB;EACzC,OAAO;CACR;CACA,IAAI,OAAO,IAAI,GAAG,GAGjB,OAAO,MAAM,IAAI,GAAG,KAAK;CAE1B,MAAM,SAAS,uBAAuB,GAAG;CACzC,OAAO,IAAI,KAAK,MAAM;CACtB,OAAO;AACR;AAEA,MAAM,QAAuB,OAAO,OAAO;AAG3C,IAAI,oBAA2C;;;AAI/C,MAAM,0BAA0B,QAA+B;CAC9D,IAAI,QAAQ,aAAa,SACxB,IAAI;EACH,MAAM,MAAM,aAAa,YAAY;GAAC;GAAO,UAAU;GAAO;GAAO;GAAO;EAAK,GAAG;GACnF,UAAU;GACV,SAAS;GACT,OAAO;IAAC;IAAU;IAAQ;GAAQ;EACnC,CAAC;EAID,OAAO,IAAI,KAAK,CAAC,CAAC,WAAW,IAAG,IAAI,mBAAmB,IAAI,KAAK,CAAC,IAAI;CACtE,QAAQ;EACP,OAAO;CACR;CAED,IAAI;EAMH,MAAM,UALM,aAAa,MAAM;GAAC;GAAM;GAAW;GAAM,OAAO,GAAG;EAAC,GAAG;GACpE,UAAU;GACV,SAAS;GACT,OAAO;IAAC;IAAU;IAAQ;GAAQ;EACnC,CACkB,CAAC,CAAC,KAAK;EACzB,OAAO,QAAQ,SAAS,IAAI,mBAAmB,OAAO,IAAI;CAC3D,QAAQ;EACP,OAAO;CACR;AACD;;;;;;;AAQA,MAAM,sBAAsB,UAA0B;CACrD,IAAI,IAAI;CACR,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACtC,KAAK,MAAM,WAAW,CAAC;EACvB,IAAK,IAAI,aAAc;CACxB;CAGA,OAAO,MAAM;AACd;;;;;;;;;AAUA,MAAa,sBAAsB,OAAO,GAAG,4CAA4C,CAAC,CAEzF,WAAW,QAAsB,OAAuB;CACvD,IAAI,CAAC,WAAW,OAAO,GAAG,GAAG,OAAO;CACpC,MAAM,cAAc,iBAAiB,OAAO,KAAK,KAAK;CAGtD,IAAI,gBAAgB,MAAM,OAAO;CAMjC,IAAI,OAAO,cAAc,MAAM,OAAO;CACtC,OAAO,gBAAgB,OAAO,YAAa,UAAqB;AACjE,CACD;;;;;;;;;;;AAYA,MAAa,aAAa,SAAgC,aAA2B;CACpF,MAAM,MAAM,QAAQ;CAEpB,OAAO;EACN;EACA,WAHiB,iBAAiB,GAG1B;EACR,UAAUA,SAAa;EACvB,WAAW,KAAK,IAAI;EACpB,aAAa,KAAK,IAAI;EACtB;CACD;AACD;;;;;AA0BA,IAAa,qBAAb,cAAwC,QAAQ,QAG9C,CAAC,CAAC,sDAAsD,CAAC,CAAC,CAAC;;;AAI7D,MAAa,0BAA2D,MAAM,OAC7E,oBACA,OAAO,WAAW;CACjB,MAAM,wBAAuB,IAAI,IAAI;CACrC,OAAO,mBAAmB,GAAG;EAC5B,iBAAiB,QAAQ,iBAAiB,KAAK,KAAK;EACpD,sBAAsB,WAAW,oBAAoB,QAAQ,KAAK;CACnE,CAAC;AACF,CAAC,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.mjs","names":[],"sources":["../../../../src/substrate/runtime/manifest/manifest.ts"],"sourcesContent":["// Manifest emitter — endpoint-keyed envelope, atomic write.\n//\n// Architecture § Manifest data model. The envelope is L0 (`identity`\n// tuple, `manifestVersion`, `endpoints` lookup, `extras`).\n//\n// The emitter:\n// - Walks endpoint entries to build the flat `endpoints` lookup\n// keyed by `endpointKey`. Entries carry the declared endpoint\n// `name` for build-integration lookup.\n// - Writes atomically via tempfile + fsync + rename. The atomic-\n// write primitive lives in L0 (per architecture § Collapsed:\n// \"Three tempfile+rename impls → one atomic-write primitive\").\n// - Validates the envelope against `ManifestEnvelopeSchema` on read.\n// - Pins `manifestVersion` so future-proofing migrations have a\n// compatibility seam. The pinned version is `1`.\n//\n// Discipline: zero service names. The schema validates the envelope\n// shape, not the per-plugin contents.\n\nimport { Effect } from 'effect';\nimport { Data } from 'effect';\nimport { FileSystem } from 'effect';\n\nimport type { EndpointKey, PluginKey } from '../../brand.ts';\nimport {\n\ttype EndpointEntry,\n\ttype ManifestCodegen,\n\ttype ManifestExtras,\n\ttype ManifestEnvelope,\n\tManifestEnvelopeSchema,\n} from '../../manifest.ts';\nimport { atomicWriteFile } from '../atomic-write.ts';\nimport { decodeJsonText } from '../runtime-decode.ts';\n\n// -----------------------------------------------------------------------------\n// Pinned schema version\n// -----------------------------------------------------------------------------\n\n/**\n * Schema version pinned at the envelope level. Bump only on a\n * breaking shape change to the envelope (NOT to a per-plugin service\n * slice — those carry their own version via Codegenable contribution).\n *\n * Reader policy:\n * - Equal version: accept.\n * - Older version: refuse (advise user to re-run `up`).\n * - Newer version: refuse (build integration is out of date).\n */\nexport const CURRENT_MANIFEST_VERSION = 1 as const;\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** One error class for every manifest IO failure. */\nexport class ManifestError extends Data.TaggedError('ManifestError')<{\n\treadonly reason:\n\t\t| 'write-failed'\n\t\t| 'read-failed'\n\t\t| 'decode-failed'\n\t\t| 'version-mismatch'\n\t\t| 'duplicate-endpoint';\n\treadonly path: string;\n\treadonly detail?: string;\n\treadonly cause?: unknown;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Public writer interface\n// -----------------------------------------------------------------------------\n\nexport interface WriteManifestInput {\n\treadonly identity: ManifestEnvelope['identity'];\n\treadonly endpoints?: ReadonlyArray<EndpointEntry>;\n\treadonly extras?: ManifestExtras;\n\t/** Per-stack codegen metadata (the dev-only `extrasDir` + live\n\t * `idsFile`). Optional + additive — omitting it produces an envelope\n\t * without the `codegen` key, identical to a pre-field manifest. */\n\treadonly codegen?: ManifestCodegen;\n}\n\n/**\n * Build the envelope from endpoint entries + extras. Pure; the writer\n * calls this and then atomically writes the JSON.\n *\n * Endpoint-key collisions error: every endpoint has a unique\n * `(pluginKey, dispatchId)` digest by construction; a duplicate means\n * a substrate bug.\n */\nexport const buildEnvelope = (\n\tinput: WriteManifestInput,\n): Effect.Effect<ManifestEnvelope, ManifestError> =>\n\tEffect.gen(function* () {\n\t\tconst endpoints: Record<string, EndpointEntry> = {};\n\t\tconst extras: Record<string, unknown> = { ...input.extras };\n\n\t\tconst addEndpoint = (ep: EndpointEntry): Effect.Effect<void, ManifestError> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ek = ep.endpointKey as string;\n\t\t\t\tif (ek in endpoints) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew ManifestError({\n\t\t\t\t\t\t\treason: 'duplicate-endpoint',\n\t\t\t\t\t\t\tpath: '(in-memory envelope)',\n\t\t\t\t\t\t\tdetail: `endpointKey ${ek} contributed twice`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tendpoints[ek] = ep;\n\t\t\t});\n\n\t\tfor (const ep of input.endpoints ?? []) {\n\t\t\tyield* addEndpoint(ep);\n\t\t}\n\n\t\treturn {\n\t\t\tidentity: input.identity,\n\t\t\tmanifestVersion: CURRENT_MANIFEST_VERSION,\n\t\t\tendpoints,\n\t\t\textras,\n\t\t\t// Spread only when present so an omitted `codegen` yields the\n\t\t\t// exact same envelope (and serialized bytes) as a pre-field\n\t\t\t// manifest — additive, no churn for stacks that don't record it.\n\t\t\t...(input.codegen !== undefined ? { codegen: input.codegen } : {}),\n\t\t};\n\t});\n\n// -----------------------------------------------------------------------------\n// Atomic write — delegates to the canonical primitive.\n// -----------------------------------------------------------------------------\n\n/**\n * Serialize and write the envelope at `path` atomically. Routes\n * through the canonical `atomicWriteFile` primitive (mkdir-parent →\n * O_EXCL temp → write → fsync → rename), so the manifest writer\n * shares ONE owner of the tempfile dance with the cache.\n */\nexport const writeManifest = (\n\tenvelope: ManifestEnvelope,\n\tpath: string,\n): Effect.Effect<void, ManifestError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst json = serializeEnvelope(envelope);\n\t\tconst bytes = new TextEncoder().encode(json);\n\t\tyield* atomicWriteFile(path, bytes).pipe(\n\t\t\tEffect.mapError(\n\t\t\t\t(cause) =>\n\t\t\t\t\tnew ManifestError({\n\t\t\t\t\t\treason: 'write-failed',\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tdetail: `atomic write failed at stage ${cause.stage}`,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t});\n\nconst serializeEnvelope = (envelope: ManifestEnvelope): string =>\n\t// Stable key ordering, deterministic re-emit. `JSON.stringify` with\n\t// 2-space indent is reproducible byte-for-byte given the same\n\t// input object.\n\tJSON.stringify(envelope, null, 2);\n\n// -----------------------------------------------------------------------------\n// Read + decode\n// -----------------------------------------------------------------------------\n\n/**\n * Read and decode the manifest at `path`. Validates against\n * `ManifestEnvelopeSchema` and the pinned `CURRENT_MANIFEST_VERSION`.\n *\n * Used by:\n * - Build integrations (Vitest, Playwright, generated app code) — consumes\n * `envelope.endpoints` lookup.\n *\n * The reader is read-only; no mutation, no IO beyond the single read.\n */\nexport const readManifest = (\n\tpath: string,\n): Effect.Effect<ManifestEnvelope, ManifestError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst text = yield* fs\n\t\t\t.readFileString(path)\n\t\t\t.pipe(Effect.mapError((cause) => new ManifestError({ reason: 'read-failed', path, cause })));\n\t\tconst decoded = yield* decodeJsonText(ManifestEnvelopeSchema, text, {\n\t\t\tsource: path,\n\t\t\tmkError: (issue) =>\n\t\t\t\tnew ManifestError({ reason: 'decode-failed', path, cause: issue.cause ?? issue }),\n\t\t});\n\t\tif (decoded.manifestVersion !== CURRENT_MANIFEST_VERSION) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew ManifestError({\n\t\t\t\t\treason: 'version-mismatch',\n\t\t\t\t\tpath,\n\t\t\t\t\tdetail: `expected ${CURRENT_MANIFEST_VERSION}, got ${decoded.manifestVersion}`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn decoded as ManifestEnvelope;\n\t});\n\n// -----------------------------------------------------------------------------\n// Re-export the envelope + endpoint types so downstream substrate\n// modules (codegen, build integrations) don't reach into substrate/\n// directly.\n// -----------------------------------------------------------------------------\n\nexport type { EndpointEntry, EndpointKey, ManifestCodegen, ManifestEnvelope, PluginKey };\nexport { ManifestEnvelopeSchema };\n"],"mappings":";;;;;AAuDA,IAAa,gBAAb,cAAmC,KAAK,YAAY,eAAe,CAAC,CAUjE,CAAC;;;;;;;;;AAwBJ,MAAa,iBACZ,UAEA,OAAO,IAAI,aAAa;CACvB,MAAM,YAA2C,CAAC;CAClD,MAAM,SAAkC,EAAE,GAAG,MAAM,OAAO;CAE1D,MAAM,eAAe,OACpB,OAAO,IAAI,aAAa;EACvB,MAAM,KAAK,GAAG;EACd,IAAI,MAAM,WACT,OAAO,OAAO,OAAO,KACpB,IAAI,cAAc;GACjB,QAAQ;GACR,MAAM;GACN,QAAQ,eAAe,GAAG;EAC3B,CAAC,CACF;EAED,UAAU,MAAM;CACjB,CAAC;CAEF,KAAK,MAAM,MAAM,MAAM,aAAa,CAAC,GACpC,OAAO,YAAY,EAAE;CAGtB,OAAO;EACN,UAAU,MAAM;EAChB,iBAAA;EACA;EACA;EAIA,GAAI,MAAM,YAAY,KAAA,IAAY,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;CACjE;AACD,CAAC;;;;;;;AAYF,MAAa,iBACZ,UACA,SAEA,OAAO,IAAI,aAAa;CACvB,MAAM,OAAO,kBAAkB,QAAQ;CAEvC,OAAO,gBAAgB,MADT,IAAI,YAAY,CAAC,CAAC,OAAO,IACN,CAAC,CAAC,CAAC,KACnC,OAAO,UACL,UACA,IAAI,cAAc;EACjB,QAAQ;EACR;EACA,QAAQ,gCAAgC,MAAM;EAC9C;CACD,CAAC,CACH,CACD;AACD,CAAC;AAEF,MAAM,qBAAqB,aAI1B,KAAK,UAAU,UAAU,MAAM,CAAC"}
1
+ {"version":3,"file":"manifest.mjs","names":[],"sources":["../../../../src/substrate/runtime/manifest/manifest.ts"],"sourcesContent":["// Manifest emitter — endpoint-keyed envelope, atomic write.\n//\n// Architecture § Manifest data model. The envelope is L0 (`identity`\n// tuple, `manifestVersion`, `endpoints` lookup, `extras`).\n//\n// The emitter:\n// - Walks endpoint entries to build the flat `endpoints` lookup\n// keyed by `endpointKey`. Entries carry the declared endpoint\n// `name` for build-integration lookup.\n// - Writes atomically via tempfile + fsync + rename. The atomic-\n// write primitive lives in L0 (per architecture § Collapsed:\n// \"Three tempfile+rename impls → one atomic-write primitive\").\n// - Validates the envelope against `ManifestEnvelopeSchema` on read.\n// - Pins `manifestVersion` so future-proofing migrations have a\n// compatibility seam. The pinned version is `1`.\n//\n// Discipline: zero service names. The schema validates the envelope\n// shape, not the per-plugin contents.\n\nimport { Effect } from 'effect';\nimport { Data } from 'effect';\nimport { FileSystem } from 'effect';\n\nimport type { EndpointKey, PluginKey } from '../../brand.ts';\nimport {\n\ttype EndpointEntry,\n\ttype ManifestCodegen,\n\ttype ManifestExtras,\n\ttype ManifestEnvelope,\n\tManifestEnvelopeSchema,\n} from '../../manifest.ts';\nimport { atomicWriteFile } from '../atomic-write.ts';\nimport { decodeJsonText } from '../runtime-decode.ts';\n\n// -----------------------------------------------------------------------------\n// Pinned schema version\n// -----------------------------------------------------------------------------\n\n/**\n * Schema version pinned at the envelope level. Bump only on a\n * breaking shape change to the envelope (NOT to a per-plugin service\n * slice — those carry their own version via Codegenable contribution).\n *\n * Reader policy:\n * - Equal version: accept.\n * - Older version: refuse (advise user to re-run `up`).\n * - Newer version: refuse (build integration is out of date).\n */\nexport const CURRENT_MANIFEST_VERSION = 1 as const;\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** One error class for every manifest IO failure. */\nexport class ManifestError extends Data.TaggedError('ManifestError')<{\n\treadonly reason:\n\t\t| 'write-failed'\n\t\t| 'read-failed'\n\t\t| 'decode-failed'\n\t\t| 'version-mismatch'\n\t\t| 'duplicate-endpoint';\n\treadonly path: string;\n\treadonly detail?: string;\n\treadonly cause?: unknown;\n}> {}\n\n// -----------------------------------------------------------------------------\n// Public writer interface\n// -----------------------------------------------------------------------------\n\nexport interface WriteManifestInput {\n\treadonly identity: ManifestEnvelope['identity'];\n\treadonly endpoints?: ReadonlyArray<EndpointEntry>;\n\treadonly extras?: ManifestExtras;\n\t/** Per-stack codegen metadata (the live `deploymentFile`). Optional +\n\t * additive — omitting it produces an envelope without the `codegen`\n\t * key, identical to a pre-field manifest. */\n\treadonly codegen?: ManifestCodegen;\n}\n\n/**\n * Build the envelope from endpoint entries + extras. Pure; the writer\n * calls this and then atomically writes the JSON.\n *\n * Endpoint-key collisions error: every endpoint has a unique\n * `(pluginKey, dispatchId)` digest by construction; a duplicate means\n * a substrate bug.\n */\nexport const buildEnvelope = (\n\tinput: WriteManifestInput,\n): Effect.Effect<ManifestEnvelope, ManifestError> =>\n\tEffect.gen(function* () {\n\t\tconst endpoints: Record<string, EndpointEntry> = {};\n\t\tconst extras: Record<string, unknown> = { ...input.extras };\n\n\t\tconst addEndpoint = (ep: EndpointEntry): Effect.Effect<void, ManifestError> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ek = ep.endpointKey as string;\n\t\t\t\tif (ek in endpoints) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew ManifestError({\n\t\t\t\t\t\t\treason: 'duplicate-endpoint',\n\t\t\t\t\t\t\tpath: '(in-memory envelope)',\n\t\t\t\t\t\t\tdetail: `endpointKey ${ek} contributed twice`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tendpoints[ek] = ep;\n\t\t\t});\n\n\t\tfor (const ep of input.endpoints ?? []) {\n\t\t\tyield* addEndpoint(ep);\n\t\t}\n\n\t\treturn {\n\t\t\tidentity: input.identity,\n\t\t\tmanifestVersion: CURRENT_MANIFEST_VERSION,\n\t\t\tendpoints,\n\t\t\textras,\n\t\t\t// Spread only when present so an omitted `codegen` yields the\n\t\t\t// exact same envelope (and serialized bytes) as a pre-field\n\t\t\t// manifest — additive, no churn for stacks that don't record it.\n\t\t\t...(input.codegen !== undefined ? { codegen: input.codegen } : {}),\n\t\t};\n\t});\n\n// -----------------------------------------------------------------------------\n// Atomic write — delegates to the canonical primitive.\n// -----------------------------------------------------------------------------\n\n/**\n * Serialize and write the envelope at `path` atomically. Routes\n * through the canonical `atomicWriteFile` primitive (mkdir-parent →\n * O_EXCL temp → write → fsync → rename), so the manifest writer\n * shares ONE owner of the tempfile dance with the cache.\n */\nexport const writeManifest = (\n\tenvelope: ManifestEnvelope,\n\tpath: string,\n): Effect.Effect<void, ManifestError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst json = serializeEnvelope(envelope);\n\t\tconst bytes = new TextEncoder().encode(json);\n\t\tyield* atomicWriteFile(path, bytes).pipe(\n\t\t\tEffect.mapError(\n\t\t\t\t(cause) =>\n\t\t\t\t\tnew ManifestError({\n\t\t\t\t\t\treason: 'write-failed',\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\tdetail: `atomic write failed at stage ${cause.stage}`,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t});\n\nconst serializeEnvelope = (envelope: ManifestEnvelope): string =>\n\t// Stable key ordering, deterministic re-emit. `JSON.stringify` with\n\t// 2-space indent is reproducible byte-for-byte given the same\n\t// input object.\n\tJSON.stringify(envelope, null, 2);\n\n// -----------------------------------------------------------------------------\n// Read + decode\n// -----------------------------------------------------------------------------\n\n/**\n * Read and decode the manifest at `path`. Validates against\n * `ManifestEnvelopeSchema` and the pinned `CURRENT_MANIFEST_VERSION`.\n *\n * Used by:\n * - Build integrations (Vitest, Playwright, generated app code) — consumes\n * `envelope.endpoints` lookup.\n *\n * The reader is read-only; no mutation, no IO beyond the single read.\n */\nexport const readManifest = (\n\tpath: string,\n): Effect.Effect<ManifestEnvelope, ManifestError, FileSystem.FileSystem> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem;\n\t\tconst text = yield* fs\n\t\t\t.readFileString(path)\n\t\t\t.pipe(Effect.mapError((cause) => new ManifestError({ reason: 'read-failed', path, cause })));\n\t\tconst decoded = yield* decodeJsonText(ManifestEnvelopeSchema, text, {\n\t\t\tsource: path,\n\t\t\tmkError: (issue) =>\n\t\t\t\tnew ManifestError({ reason: 'decode-failed', path, cause: issue.cause ?? issue }),\n\t\t});\n\t\tif (decoded.manifestVersion !== CURRENT_MANIFEST_VERSION) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew ManifestError({\n\t\t\t\t\treason: 'version-mismatch',\n\t\t\t\t\tpath,\n\t\t\t\t\tdetail: `expected ${CURRENT_MANIFEST_VERSION}, got ${decoded.manifestVersion}`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn decoded as ManifestEnvelope;\n\t});\n\n// -----------------------------------------------------------------------------\n// Re-export the envelope + endpoint types so downstream substrate\n// modules (codegen, build integrations) don't reach into substrate/\n// directly.\n// -----------------------------------------------------------------------------\n\nexport type { EndpointEntry, EndpointKey, ManifestCodegen, ManifestEnvelope, PluginKey };\nexport { ManifestEnvelopeSchema };\n"],"mappings":";;;;;AAuDA,IAAa,gBAAb,cAAmC,KAAK,YAAY,eAAe,CAAC,CAUjE,CAAC;;;;;;;;;AAwBJ,MAAa,iBACZ,UAEA,OAAO,IAAI,aAAa;CACvB,MAAM,YAA2C,CAAC;CAClD,MAAM,SAAkC,EAAE,GAAG,MAAM,OAAO;CAE1D,MAAM,eAAe,OACpB,OAAO,IAAI,aAAa;EACvB,MAAM,KAAK,GAAG;EACd,IAAI,MAAM,WACT,OAAO,OAAO,OAAO,KACpB,IAAI,cAAc;GACjB,QAAQ;GACR,MAAM;GACN,QAAQ,eAAe,GAAG;EAC3B,CAAC,CACF;EAED,UAAU,MAAM;CACjB,CAAC;CAEF,KAAK,MAAM,MAAM,MAAM,aAAa,CAAC,GACpC,OAAO,YAAY,EAAE;CAGtB,OAAO;EACN,UAAU,MAAM;EAChB,iBAAA;EACA;EACA;EAIA,GAAI,MAAM,YAAY,KAAA,IAAY,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;CACjE;AACD,CAAC;;;;;;;AAYF,MAAa,iBACZ,UACA,SAEA,OAAO,IAAI,aAAa;CACvB,MAAM,OAAO,kBAAkB,QAAQ;CAEvC,OAAO,gBAAgB,MADT,IAAI,YAAY,CAAC,CAAC,OAAO,IACN,CAAC,CAAC,CAAC,KACnC,OAAO,UACL,UACA,IAAI,cAAc;EACjB,QAAQ;EACR;EACA,QAAQ,gCAAgC,MAAM;EAC9C;CACD,CAAC,CACH,CACD;AACD,CAAC;AAEF,MAAM,qBAAqB,aAI1B,KAAK,UAAU,UAAU,MAAM,CAAC"}
@@ -75,6 +75,6 @@ const layerStackPaths = Layer.effect(StackPathsService, Effect.gen(function* ()
75
75
  });
76
76
  }));
77
77
  //#endregion
78
- export { IdentityContext, RuntimeRoot, StackPathsService, layerIdentity, layerRuntimeRoot, layerStackPaths, stackSubpath };
78
+ export { IdentityContext, RuntimeRoot, StackPathsService, layerIdentity, layerRuntimeRoot, layerStackPaths };
79
79
 
80
80
  //# sourceMappingURL=paths.mjs.map
@@ -100,7 +100,7 @@ const commands = [
100
100
  },
101
101
  {
102
102
  name: "apply",
103
- summary: "Re-emit the per-stack ids file and dev extras from a live or one-shot stack.",
103
+ summary: "Re-emit the per-stack deployment file from a live or one-shot stack.",
104
104
  usage: "devstack apply [options]",
105
105
  lifecycle: "live-aware",
106
106
  sideEffects: "write",
@@ -118,17 +118,17 @@ const commands = [
118
118
  options: configOptions
119
119
  },
120
120
  {
121
- name: "dump-ids",
122
- summary: "Emit the stack id-config (devstack-ids.json) for a real-network deploy.",
123
- usage: "devstack dump-ids [options]",
121
+ name: "dump-deployment",
122
+ summary: "Emit the stack deployment for a real-network deploy.",
123
+ usage: "devstack dump-deployment [options]",
124
124
  lifecycle: "live-aware",
125
125
  sideEffects: "write",
126
126
  requiresDocker: true,
127
- description: "Emits the stack devstack-ids.json id-config to --out (or stdout). Reads the existing file when a supervisor is live; otherwise runs a one-shot boot to produce it, then tears down. The supported way to obtain a committed id-config file for a real-network deploy.",
127
+ description: "Emits the stack deployment.json deployment to --out (or stdout). Reads the existing file when a supervisor is live; otherwise runs a one-shot boot to produce it, then tears down. With --network <net>, instead emits a typed deployments/<net>.ts (satisfies AppNetworkDeployment) from that network entry. The supported way to obtain a committed deployment file for a real-network deploy.",
128
128
  options: [...configOptions, {
129
129
  name: "out",
130
130
  value: "path",
131
- description: "Write the id-config JSON to this file instead of stdout."
131
+ description: "Write the deployment JSON to this file instead of stdout."
132
132
  }]
133
133
  },
134
134
  {
@@ -1 +1 @@
1
- {"version":3,"file":"command-tree.mjs","names":[],"sources":["../../../src/surfaces/cli/command-tree.ts"],"sourcesContent":["import { ENV_VARS } from './flags.ts';\nimport { exitCodeTable } from './sysexits.ts';\n\nexport type CommandLifecycle = 'attached' | 'live-aware' | 'one-shot' | 'offline';\n\nexport interface CommandOption {\n\treadonly name: string;\n\treadonly value?: string;\n\treadonly description: string;\n}\n\nexport interface CommandNode {\n\treadonly name: string;\n\treadonly summary: string;\n\treadonly usage: string;\n\treadonly lifecycle: CommandLifecycle;\n\treadonly sideEffects: 'none' | 'read' | 'write' | 'destructive';\n\treadonly requiresDocker: boolean;\n\treadonly description?: string;\n\treadonly arguments?: ReadonlyArray<string>;\n\treadonly options?: ReadonlyArray<CommandOption>;\n\treadonly subcommands?: ReadonlyArray<CommandNode>;\n}\n\nconst identityOptions = [\n\t{ name: 'json', description: 'Emit JSON envelope output.' },\n\t{ name: 'app', value: 'name', description: 'Override app name.' },\n\t{ name: 'stack', value: 'name', description: 'Override stack name.' },\n\t{ name: 'state-dir', value: 'path', description: 'Override state directory.' },\n\t{ name: 'verbose', description: 'Enable more verbose logging.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst configOptions = [\n\t...identityOptions,\n\t{ name: 'config', value: 'path', description: 'Override devstack.config.ts path.' },\n\t{ name: 'network', value: 'name', description: 'Override network before config import.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst confirmOptions = [\n\t...identityOptions,\n\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t{ name: 'no-input', description: 'Forbid prompts.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst globalMaintenanceOptions = [\n\t{ name: 'json', description: 'Emit JSON envelope output.' },\n\t{ name: 'state-dir', value: 'path', description: 'Override state directory.' },\n\t{ name: 'verbose', description: 'Enable more verbose logging.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst commands = [\n\t{\n\t\tname: 'up',\n\t\tsummary: 'Boot a stack and stay attached until interrupted.',\n\t\tusage: 'devstack up [options]',\n\t\tlifecycle: 'attached',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\tdescription: 'Starts the supervisor and renders the attached operator surface.',\n\t\toptions: [\n\t\t\t...configOptions,\n\t\t\t{\n\t\t\t\tname: 'renderer',\n\t\t\t\tvalue: 'tui|plain|silent',\n\t\t\t\tdescription: 'Select the attached renderer.',\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'from-snapshot',\n\t\t\t\tvalue: 'name-or-id',\n\t\t\t\tdescription: 'Start by restoring a snapshot before the initial acquire.',\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'snapshot-cache',\n\t\t\t\tvalue: 'name',\n\t\t\t\tdescription: 'Use a named snapshot as a startup cache and refresh it when stale.',\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'snapshot-stale',\n\t\t\t\tvalue: 'warn|block|clean-start',\n\t\t\t\tdescription: 'Policy when --from-snapshot inputs differ from the current stack.',\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'apply',\n\t\tsummary: 'Re-emit the per-stack ids file and dev extras from a live or one-shot stack.',\n\t\tusage: 'devstack apply [options]',\n\t\tlifecycle: 'live-aware',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\toptions: configOptions,\n\t},\n\t{\n\t\tname: 'codegen',\n\t\tsummary: 'Regenerate committed bindings from Move source (no stack boot).',\n\t\tusage: 'devstack codegen [options]',\n\t\tlifecycle: 'one-shot',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: false,\n\t\tdescription:\n\t\t\t'Move-compiles local package sources and emits the committed src/generated tree (type bindings + sentinel-id config stubs) without booting a stack.',\n\t\toptions: configOptions,\n\t},\n\t{\n\t\tname: 'dump-ids',\n\t\tsummary: 'Emit the stack id-config (devstack-ids.json) for a real-network deploy.',\n\t\tusage: 'devstack dump-ids [options]',\n\t\tlifecycle: 'live-aware',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\tdescription:\n\t\t\t'Emits the stack devstack-ids.json id-config to --out (or stdout). Reads the existing file when a supervisor is live; otherwise runs a one-shot boot to produce it, then tears down. The supported way to obtain a committed id-config file for a real-network deploy.',\n\t\toptions: [\n\t\t\t...configOptions,\n\t\t\t{\n\t\t\t\tname: 'out',\n\t\t\t\tvalue: 'path',\n\t\t\t\tdescription: 'Write the id-config JSON to this file instead of stdout.',\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'status',\n\t\tsummary: 'Show the current stack projection (offline: from the manifest).',\n\t\tusage: 'devstack status [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: false,\n\t\toptions: identityOptions,\n\t},\n\t{\n\t\tname: 'doctor',\n\t\tsummary: 'Run host and stack preflight checks.',\n\t\tusage: 'devstack doctor [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: true,\n\t\toptions: identityOptions,\n\t},\n\t{\n\t\tname: 'config',\n\t\tsummary: 'Print resolved config inputs.',\n\t\tusage: 'devstack config [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: false,\n\t\toptions: configOptions,\n\t},\n\t{\n\t\tname: 'schema',\n\t\tsummary: 'Emit the CLI schema.',\n\t\tusage: 'devstack schema --json',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'none',\n\t\trequiresDocker: false,\n\t\toptions: [{ name: 'json', description: 'Emit JSON schema output.' }],\n\t},\n\t{\n\t\tname: 'snapshot',\n\t\tsummary: 'Capture, restore, list, or delete stack snapshots.',\n\t\tusage: 'devstack snapshot <command> [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\tsubcommands: [\n\t\t\t{\n\t\t\t\tname: 'save',\n\t\t\t\tsummary: 'Capture a snapshot through a one-shot stack boot.',\n\t\t\t\tusage: 'devstack snapshot save [name] [options]',\n\t\t\t\tlifecycle: 'one-shot',\n\t\t\t\tsideEffects: 'write',\n\t\t\t\trequiresDocker: true,\n\t\t\t\targuments: ['name'],\n\t\t\t\toptions: [\n\t\t\t\t\t...configOptions,\n\t\t\t\t\t{ name: 'name', value: 'name', description: 'Human-readable snapshot name.' },\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'restore',\n\t\t\t\tsummary: 'Restore a snapshot by name or id.',\n\t\t\t\tusage: 'devstack snapshot restore <name-or-id> [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'destructive',\n\t\t\t\trequiresDocker: true,\n\t\t\t\targuments: ['name-or-id'],\n\t\t\t\toptions: confirmOptions,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'list',\n\t\t\t\tsummary: 'List snapshots for the selected stack.',\n\t\t\t\tusage: 'devstack snapshot list [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'read',\n\t\t\t\trequiresDocker: false,\n\t\t\t\toptions: identityOptions,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'delete',\n\t\t\t\tsummary: 'Delete a snapshot by name or id.',\n\t\t\t\tusage: 'devstack snapshot delete <name-or-id> [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'destructive',\n\t\t\t\trequiresDocker: false,\n\t\t\t\targuments: ['name-or-id'],\n\t\t\t\toptions: confirmOptions,\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'prune',\n\t\tsummary: 'Inventory and prune devstack-labelled Docker resources.',\n\t\tusage: 'devstack prune [--list | --dry-run | --all --yes] [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'destructive',\n\t\trequiresDocker: true,\n\t\toptions: [\n\t\t\t...globalMaintenanceOptions,\n\t\t\t{ name: 'list', description: 'List resource groups without pruning.' },\n\t\t\t{ name: 'all', description: 'Prune every idle non-shared resource group.' },\n\t\t\t{ name: 'no-containers', description: 'Do not remove containers.' },\n\t\t\t{ name: 'no-networks', description: 'Do not remove networks.' },\n\t\t\t{ name: 'no-volumes', description: 'Do not remove volumes.' },\n\t\t\t{ name: 'include-images', description: 'Also remove devstack-labelled images.' },\n\t\t\t{ name: 'dry-run', description: 'Skip mutating effects.' },\n\t\t\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t\t\t{ name: 'no-input', description: 'Forbid prompts.' },\n\t\t],\n\t},\n\t{\n\t\tname: 'wipe',\n\t\tsummary: 'Destroy all state for the selected stack.',\n\t\tusage: 'devstack wipe [--dry-run] [--yes] [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'destructive',\n\t\trequiresDocker: true,\n\t\toptions: [\n\t\t\t...identityOptions,\n\t\t\t{ name: 'dry-run', description: 'Skip mutating effects.' },\n\t\t\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t\t\t{ name: 'no-input', description: 'Forbid prompts.' },\n\t\t],\n\t},\n] as const satisfies ReadonlyArray<CommandNode>;\n\nexport const COMMAND_TREE: CommandNode = {\n\tname: 'devstack',\n\tsummary: 'Sui development stack CLI.',\n\tusage: 'devstack <command> [options]',\n\tlifecycle: 'offline',\n\tsideEffects: 'none',\n\trequiresDocker: false,\n\tdescription: 'Boot, inspect, snapshot, and manage local Sui development stacks.',\n\tsubcommands: commands,\n};\n\nexport type Verb = (typeof commands)[number]['name'];\n\nexport const VERBS: ReadonlyArray<Verb> = commands.map((command) => command.name);\n\nexport const commandSchema = () => ({\n\tschemaVersion: 1,\n\tverbs: VERBS,\n\tcommands: COMMAND_TREE,\n\texitCodes: exitCodeTable,\n\tenvVars: Object.values(ENV_VARS),\n});\n"],"mappings":";;;AAwBA,MAAM,kBAAkB;CACvB;EAAE,MAAM;EAAQ,aAAa;CAA6B;CAC1D;EAAE,MAAM;EAAO,OAAO;EAAQ,aAAa;CAAqB;CAChE;EAAE,MAAM;EAAS,OAAO;EAAQ,aAAa;CAAuB;CACpE;EAAE,MAAM;EAAa,OAAO;EAAQ,aAAa;CAA4B;CAC7E;EAAE,MAAM;EAAW,aAAa;CAA+B;AAChE;AAEA,MAAM,gBAAgB;CACrB,GAAG;CACH;EAAE,MAAM;EAAU,OAAO;EAAQ,aAAa;CAAoC;CAClF;EAAE,MAAM;EAAW,OAAO;EAAQ,aAAa;CAAyC;AACzF;AAEA,MAAM,iBAAiB;CACtB,GAAG;CACH;EAAE,MAAM;EAAO,aAAa;CAAyB;CACrD;EAAE,MAAM;EAAY,aAAa;CAAkB;AACpD;AAEA,MAAM,2BAA2B;CAChC;EAAE,MAAM;EAAQ,aAAa;CAA6B;CAC1D;EAAE,MAAM;EAAa,OAAO;EAAQ,aAAa;CAA4B;CAC7E;EAAE,MAAM;EAAW,aAAa;CAA+B;AAChE;AAEA,MAAM,WAAW;CAChB;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,aAAa;EACb,SAAS;GACR,GAAG;GACH;IACC,MAAM;IACN,OAAO;IACP,aAAa;GACd;GACA;IACC,MAAM;IACN,OAAO;IACP,aAAa;GACd;GACA;IACC,MAAM;IACN,OAAO;IACP,aAAa;GACd;GACA;IACC,MAAM;IACN,OAAO;IACP,aAAa;GACd;EACD;CACD;CACA;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,SAAS;CACV;CACA;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,aACC;EACD,SAAS;CACV;CACA;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,aACC;EACD,SAAS,CACR,GAAG,eACH;GACC,MAAM;GACN,OAAO;GACP,aAAa;EACd,CACD;CACD;CACA;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,SAAS;CACV;CACA;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,SAAS;CACV;CACA;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,SAAS;CACV;CACA;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,SAAS,CAAC;GAAE,MAAM;GAAQ,aAAa;EAA2B,CAAC;CACpE;CACA;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,aAAa;GACZ;IACC,MAAM;IACN,SAAS;IACT,OAAO;IACP,WAAW;IACX,aAAa;IACb,gBAAgB;IAChB,WAAW,CAAC,MAAM;IAClB,SAAS,CACR,GAAG,eACH;KAAE,MAAM;KAAQ,OAAO;KAAQ,aAAa;IAAgC,CAC7E;GACD;GACA;IACC,MAAM;IACN,SAAS;IACT,OAAO;IACP,WAAW;IACX,aAAa;IACb,gBAAgB;IAChB,WAAW,CAAC,YAAY;IACxB,SAAS;GACV;GACA;IACC,MAAM;IACN,SAAS;IACT,OAAO;IACP,WAAW;IACX,aAAa;IACb,gBAAgB;IAChB,SAAS;GACV;GACA;IACC,MAAM;IACN,SAAS;IACT,OAAO;IACP,WAAW;IACX,aAAa;IACb,gBAAgB;IAChB,WAAW,CAAC,YAAY;IACxB,SAAS;GACV;EACD;CACD;CACA;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,SAAS;GACR,GAAG;GACH;IAAE,MAAM;IAAQ,aAAa;GAAwC;GACrE;IAAE,MAAM;IAAO,aAAa;GAA8C;GAC1E;IAAE,MAAM;IAAiB,aAAa;GAA4B;GAClE;IAAE,MAAM;IAAe,aAAa;GAA0B;GAC9D;IAAE,MAAM;IAAc,aAAa;GAAyB;GAC5D;IAAE,MAAM;IAAkB,aAAa;GAAwC;GAC/E;IAAE,MAAM;IAAW,aAAa;GAAyB;GACzD;IAAE,MAAM;IAAO,aAAa;GAAyB;GACrD;IAAE,MAAM;IAAY,aAAa;GAAkB;EACpD;CACD;CACA;EACC,MAAM;EACN,SAAS;EACT,OAAO;EACP,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,SAAS;GACR,GAAG;GACH;IAAE,MAAM;IAAW,aAAa;GAAyB;GACzD;IAAE,MAAM;IAAO,aAAa;GAAyB;GACrD;IAAE,MAAM;IAAY,aAAa;GAAkB;EACpD;CACD;AACD;AAEA,MAAa,eAA4B;CACxC,MAAM;CACN,SAAS;CACT,OAAO;CACP,WAAW;CACX,aAAa;CACb,gBAAgB;CAChB,aAAa;CACb,aAAa;AACd;AAIA,MAAa,QAA6B,SAAS,KAAK,YAAY,QAAQ,IAAI;AAEhF,MAAa,uBAAuB;CACnC,eAAe;CACf,OAAO;CACP,UAAU;CACV,WAAW;CACX,SAAS,OAAO,OAAO,QAAQ;AAChC"}
1
+ {"version":3,"file":"command-tree.mjs","names":[],"sources":["../../../src/surfaces/cli/command-tree.ts"],"sourcesContent":["import { ENV_VARS } from './flags.ts';\nimport { exitCodeTable } from './sysexits.ts';\n\nexport type CommandLifecycle = 'attached' | 'live-aware' | 'one-shot' | 'offline';\n\nexport interface CommandOption {\n\treadonly name: string;\n\treadonly value?: string;\n\treadonly description: string;\n}\n\nexport interface CommandNode {\n\treadonly name: string;\n\treadonly summary: string;\n\treadonly usage: string;\n\treadonly lifecycle: CommandLifecycle;\n\treadonly sideEffects: 'none' | 'read' | 'write' | 'destructive';\n\treadonly requiresDocker: boolean;\n\treadonly description?: string;\n\treadonly arguments?: ReadonlyArray<string>;\n\treadonly options?: ReadonlyArray<CommandOption>;\n\treadonly subcommands?: ReadonlyArray<CommandNode>;\n}\n\nconst identityOptions = [\n\t{ name: 'json', description: 'Emit JSON envelope output.' },\n\t{ name: 'app', value: 'name', description: 'Override app name.' },\n\t{ name: 'stack', value: 'name', description: 'Override stack name.' },\n\t{ name: 'state-dir', value: 'path', description: 'Override state directory.' },\n\t{ name: 'verbose', description: 'Enable more verbose logging.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst configOptions = [\n\t...identityOptions,\n\t{ name: 'config', value: 'path', description: 'Override devstack.config.ts path.' },\n\t{ name: 'network', value: 'name', description: 'Override network before config import.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst confirmOptions = [\n\t...identityOptions,\n\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t{ name: 'no-input', description: 'Forbid prompts.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst globalMaintenanceOptions = [\n\t{ name: 'json', description: 'Emit JSON envelope output.' },\n\t{ name: 'state-dir', value: 'path', description: 'Override state directory.' },\n\t{ name: 'verbose', description: 'Enable more verbose logging.' },\n] as const satisfies ReadonlyArray<CommandOption>;\n\nconst commands = [\n\t{\n\t\tname: 'up',\n\t\tsummary: 'Boot a stack and stay attached until interrupted.',\n\t\tusage: 'devstack up [options]',\n\t\tlifecycle: 'attached',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\tdescription: 'Starts the supervisor and renders the attached operator surface.',\n\t\toptions: [\n\t\t\t...configOptions,\n\t\t\t{\n\t\t\t\tname: 'renderer',\n\t\t\t\tvalue: 'tui|plain|silent',\n\t\t\t\tdescription: 'Select the attached renderer.',\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'from-snapshot',\n\t\t\t\tvalue: 'name-or-id',\n\t\t\t\tdescription: 'Start by restoring a snapshot before the initial acquire.',\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'snapshot-cache',\n\t\t\t\tvalue: 'name',\n\t\t\t\tdescription: 'Use a named snapshot as a startup cache and refresh it when stale.',\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'snapshot-stale',\n\t\t\t\tvalue: 'warn|block|clean-start',\n\t\t\t\tdescription: 'Policy when --from-snapshot inputs differ from the current stack.',\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'apply',\n\t\tsummary: 'Re-emit the per-stack deployment file from a live or one-shot stack.',\n\t\tusage: 'devstack apply [options]',\n\t\tlifecycle: 'live-aware',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\toptions: configOptions,\n\t},\n\t{\n\t\tname: 'codegen',\n\t\tsummary: 'Regenerate committed bindings from Move source (no stack boot).',\n\t\tusage: 'devstack codegen [options]',\n\t\tlifecycle: 'one-shot',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: false,\n\t\tdescription:\n\t\t\t'Move-compiles local package sources and emits the committed src/generated tree (type bindings + sentinel-id config stubs) without booting a stack.',\n\t\toptions: configOptions,\n\t},\n\t{\n\t\tname: 'dump-deployment',\n\t\tsummary: 'Emit the stack deployment for a real-network deploy.',\n\t\tusage: 'devstack dump-deployment [options]',\n\t\tlifecycle: 'live-aware',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\tdescription:\n\t\t\t'Emits the stack deployment.json deployment to --out (or stdout). Reads the existing file when a supervisor is live; otherwise runs a one-shot boot to produce it, then tears down. With --network <net>, instead emits a typed deployments/<net>.ts (satisfies AppNetworkDeployment) from that network entry. The supported way to obtain a committed deployment file for a real-network deploy.',\n\t\toptions: [\n\t\t\t...configOptions,\n\t\t\t{\n\t\t\t\tname: 'out',\n\t\t\t\tvalue: 'path',\n\t\t\t\tdescription: 'Write the deployment JSON to this file instead of stdout.',\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'status',\n\t\tsummary: 'Show the current stack projection (offline: from the manifest).',\n\t\tusage: 'devstack status [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: false,\n\t\toptions: identityOptions,\n\t},\n\t{\n\t\tname: 'doctor',\n\t\tsummary: 'Run host and stack preflight checks.',\n\t\tusage: 'devstack doctor [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: true,\n\t\toptions: identityOptions,\n\t},\n\t{\n\t\tname: 'config',\n\t\tsummary: 'Print resolved config inputs.',\n\t\tusage: 'devstack config [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'read',\n\t\trequiresDocker: false,\n\t\toptions: configOptions,\n\t},\n\t{\n\t\tname: 'schema',\n\t\tsummary: 'Emit the CLI schema.',\n\t\tusage: 'devstack schema --json',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'none',\n\t\trequiresDocker: false,\n\t\toptions: [{ name: 'json', description: 'Emit JSON schema output.' }],\n\t},\n\t{\n\t\tname: 'snapshot',\n\t\tsummary: 'Capture, restore, list, or delete stack snapshots.',\n\t\tusage: 'devstack snapshot <command> [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'write',\n\t\trequiresDocker: true,\n\t\tsubcommands: [\n\t\t\t{\n\t\t\t\tname: 'save',\n\t\t\t\tsummary: 'Capture a snapshot through a one-shot stack boot.',\n\t\t\t\tusage: 'devstack snapshot save [name] [options]',\n\t\t\t\tlifecycle: 'one-shot',\n\t\t\t\tsideEffects: 'write',\n\t\t\t\trequiresDocker: true,\n\t\t\t\targuments: ['name'],\n\t\t\t\toptions: [\n\t\t\t\t\t...configOptions,\n\t\t\t\t\t{ name: 'name', value: 'name', description: 'Human-readable snapshot name.' },\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'restore',\n\t\t\t\tsummary: 'Restore a snapshot by name or id.',\n\t\t\t\tusage: 'devstack snapshot restore <name-or-id> [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'destructive',\n\t\t\t\trequiresDocker: true,\n\t\t\t\targuments: ['name-or-id'],\n\t\t\t\toptions: confirmOptions,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'list',\n\t\t\t\tsummary: 'List snapshots for the selected stack.',\n\t\t\t\tusage: 'devstack snapshot list [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'read',\n\t\t\t\trequiresDocker: false,\n\t\t\t\toptions: identityOptions,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'delete',\n\t\t\t\tsummary: 'Delete a snapshot by name or id.',\n\t\t\t\tusage: 'devstack snapshot delete <name-or-id> [options]',\n\t\t\t\tlifecycle: 'offline',\n\t\t\t\tsideEffects: 'destructive',\n\t\t\t\trequiresDocker: false,\n\t\t\t\targuments: ['name-or-id'],\n\t\t\t\toptions: confirmOptions,\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'prune',\n\t\tsummary: 'Inventory and prune devstack-labelled Docker resources.',\n\t\tusage: 'devstack prune [--list | --dry-run | --all --yes] [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'destructive',\n\t\trequiresDocker: true,\n\t\toptions: [\n\t\t\t...globalMaintenanceOptions,\n\t\t\t{ name: 'list', description: 'List resource groups without pruning.' },\n\t\t\t{ name: 'all', description: 'Prune every idle non-shared resource group.' },\n\t\t\t{ name: 'no-containers', description: 'Do not remove containers.' },\n\t\t\t{ name: 'no-networks', description: 'Do not remove networks.' },\n\t\t\t{ name: 'no-volumes', description: 'Do not remove volumes.' },\n\t\t\t{ name: 'include-images', description: 'Also remove devstack-labelled images.' },\n\t\t\t{ name: 'dry-run', description: 'Skip mutating effects.' },\n\t\t\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t\t\t{ name: 'no-input', description: 'Forbid prompts.' },\n\t\t],\n\t},\n\t{\n\t\tname: 'wipe',\n\t\tsummary: 'Destroy all state for the selected stack.',\n\t\tusage: 'devstack wipe [--dry-run] [--yes] [options]',\n\t\tlifecycle: 'offline',\n\t\tsideEffects: 'destructive',\n\t\trequiresDocker: true,\n\t\toptions: [\n\t\t\t...identityOptions,\n\t\t\t{ name: 'dry-run', description: 'Skip mutating effects.' },\n\t\t\t{ name: 'yes', description: 'Assume yes on prompts.' },\n\t\t\t{ name: 'no-input', description: 'Forbid prompts.' },\n\t\t],\n\t},\n] as const satisfies ReadonlyArray<CommandNode>;\n\nexport const COMMAND_TREE: CommandNode = {\n\tname: 'devstack',\n\tsummary: 'Sui development stack CLI.',\n\tusage: 'devstack <command> [options]',\n\tlifecycle: 'offline',\n\tsideEffects: 'none',\n\trequiresDocker: false,\n\tdescription: 'Boot, inspect, snapshot, and manage local Sui development stacks.',\n\tsubcommands: commands,\n};\n\nexport type Verb = (typeof commands)[number]['name'];\n\nexport const VERBS: ReadonlyArray<Verb> = commands.map((command) => command.name);\n\nexport const commandSchema = () => ({\n\tschemaVersion: 1,\n\tverbs: VERBS,\n\tcommands: COMMAND_TREE,\n\texitCodes: exitCodeTable,\n\tenvVars: Object.values(ENV_VARS),\n});\n"],"mappings":"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"}
@@ -271,16 +271,18 @@ const app = buildApplication(buildRouteMap({
271
271
  return runWithFlags(this, "codegen", flags, [], (global) => this.deps.codegen.run({ configPath: global.configPath }));
272
272
  }
273
273
  }),
274
- dumpIds: buildCommand({
274
+ dumpDeployment: buildCommand({
275
275
  parameters: { flags: {
276
276
  ...configFlagParams,
277
- out: stringFlag("Write the id-config JSON to this file instead of stdout", "path")
277
+ out: stringFlag("Write the deployment JSON to this file instead of stdout", "path"),
278
+ network: stringFlag("Emit a typed deployments/<network>.ts (satisfies AppNetworkDeployment) instead of the raw envelope", "name")
278
279
  } },
279
- docs: { brief: "Emit the stack id-config (devstack-ids.json) for a real-network deploy" },
280
+ docs: { brief: "Emit the stack deployment (deployment.json) for a real-network deploy" },
280
281
  func: function(flags) {
281
- return runWithFlags(this, "dump-ids", flags, [], (global) => this.deps.dumpIds.run({
282
+ return runWithFlags(this, "dump-deployment", flags, [], (global) => this.deps.dumpDeployment.run({
282
283
  configPath: global.configPath,
283
284
  out: flags.out,
285
+ network: flags.network,
284
286
  io: this.io,
285
287
  outputMode: global.outputMode
286
288
  }));
@@ -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, type Scope } from 'effect';\n\nimport { commandSchema } from './command-tree.ts';\nimport { type CliError, CliInternalError, CliUsageError, exitCodeFor } from './errors.ts';\nimport {\n\ttype CliRendererMode,\n\tENV_VARS,\n\ttype GlobalFlags,\n\ttype OutputMode,\n\ttype SnapshotStalePolicy,\n} 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';\nimport { readDevstackVersion } from '../../cli/wirings/read-devstack-version.ts';\n\n// -----------------------------------------------------------------------------\n// Deps bundle\n// -----------------------------------------------------------------------------\n\nexport interface LifecycleCommandDeps {\n\treadonly run: (flags: GlobalFlags) => Effect.Effect<CommandResult, CliError>;\n}\n\n/** The stack-free `codegen` verb takes only a config path — it boots no\n * stack, so it needs no renderer/snapshot/identity flags. */\nexport interface CodegenCommandDeps {\n\treadonly run: (flags: {\n\t\treadonly configPath: string | undefined;\n\t}) => Effect.Effect<CommandResult, CliError>;\n}\n\n/** `dump-ids` emits the stack's `devstack-ids.json` id-config. It owns\n * its own output (file via `--out`, else stdout), so it receives `io`\n * and the resolved `outputMode` alongside the config path + destination. */\nexport interface DumpIdsCommandDeps {\n\treadonly run: (flags: {\n\t\treadonly configPath: string | undefined;\n\t\treadonly out: string | undefined;\n\t\treadonly io: CliIO;\n\t\treadonly outputMode: OutputMode;\n\t}) => Effect.Effect<CommandResult, CliError>;\n}\n\nexport interface CliDeps {\n\treadonly up: LifecycleCommandDeps;\n\treadonly apply: LifecycleCommandDeps;\n\treadonly codegen: CodegenCommandDeps;\n\treadonly dumpIds: DumpIdsCommandDeps;\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\treadonly fromSnapshot?: string;\n\treadonly snapshotCache?: string;\n\treadonly snapshotStale?: SnapshotStalePolicy;\n}\n\ninterface DumpIdsFlags extends ConfigFlags {\n\treadonly out?: string;\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);\nconst snapshotStaleParser = buildChoiceParser(['warn', 'block', 'clean-start'] 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\tfromSnapshot: flags.fromSnapshot,\n\t\tsnapshotCache: flags.snapshotCache,\n\t\tsnapshotStalePolicy: flags.snapshotStale,\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\tfromSnapshot: stringFlag('Start by restoring a named snapshot before acquire', 'name-or-id'),\n\t\t\tsnapshotCache: stringFlag(\n\t\t\t\t'Use a named snapshot as a startup cache and refresh it when stale',\n\t\t\t\t'name',\n\t\t\t),\n\t\t\tsnapshotStale: {\n\t\t\t\tkind: 'parsed',\n\t\t\t\tparse: snapshotStaleParser,\n\t\t\t\toptional: true,\n\t\t\t\tplaceholder: 'warn|block|clean-start',\n\t\t\t\tbrief: 'Policy when --from-snapshot inputs differ from the current stack',\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 codegenCommand = buildCommand<ConfigFlags, [], DevstackCliContext>({\n\tparameters: { flags: configFlagParams },\n\tdocs: {\n\t\tbrief: 'Regenerate committed bindings from Move source (no stack boot)',\n\t},\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'codegen', flags, [], (global) =>\n\t\t\tthis.deps.codegen.run({ configPath: global.configPath }),\n\t\t);\n\t},\n});\n\nconst dumpIdsCommand = buildCommand<DumpIdsFlags, [], DevstackCliContext>({\n\tparameters: {\n\t\tflags: {\n\t\t\t...configFlagParams,\n\t\t\tout: stringFlag('Write the id-config JSON to this file instead of stdout', 'path'),\n\t\t},\n\t},\n\tdocs: {\n\t\tbrief: 'Emit the stack id-config (devstack-ids.json) for a real-network deploy',\n\t},\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'dump-ids', flags, [], (global) =>\n\t\t\tthis.deps.dumpIds.run({\n\t\t\t\tconfigPath: global.configPath,\n\t\t\t\tout: flags.out,\n\t\t\t\tio: this.io,\n\t\t\t\toutputMode: global.outputMode,\n\t\t\t}),\n\t\t);\n\t},\n});\n\nconst statusCommand = buildCommand<IdentityFlags, [], DevstackCliContext>({\n\tparameters: { flags: identityFlagParams },\n\tdocs: { brief: 'Show the current stack projection (offline: from the manifest)' },\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\tcodegen: codegenCommand,\n\t\tdumpIds: dumpIdsCommand,\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: readDevstackVersion() },\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 * A non-zero value here means Stricli rejected argv before any verb\n * ran, so mapping to `USAGE` holds by construction. Kept as a named\n * function 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
+ {"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';\n\nimport { commandSchema } from './command-tree.ts';\nimport { type CliError, CliInternalError, CliUsageError, exitCodeFor } from './errors.ts';\nimport {\n\ttype CliRendererMode,\n\tENV_VARS,\n\ttype GlobalFlags,\n\ttype OutputMode,\n\ttype SnapshotStalePolicy,\n} 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';\nimport { readDevstackVersion } from '../../cli/wirings/read-devstack-version.ts';\n\n// -----------------------------------------------------------------------------\n// Deps bundle\n// -----------------------------------------------------------------------------\n\nexport interface LifecycleCommandDeps {\n\treadonly run: (flags: GlobalFlags) => Effect.Effect<CommandResult, CliError>;\n}\n\n/** The stack-free `codegen` verb takes only a config path — it boots no\n * stack, so it needs no renderer/snapshot/identity flags. */\nexport interface CodegenCommandDeps {\n\treadonly run: (flags: {\n\t\treadonly configPath: string | undefined;\n\t}) => Effect.Effect<CommandResult, CliError>;\n}\n\n/** `dump-deployment` emits the stack's `deployment.json` deployment. It owns\n * its own output (file via `--out`, else stdout), so it receives `io`\n * and the resolved `outputMode` alongside the config path + destination. */\nexport interface DumpDeploymentCommandDeps {\n\treadonly run: (flags: {\n\t\treadonly configPath: string | undefined;\n\t\treadonly out: string | undefined;\n\t\treadonly network: string | undefined;\n\t\treadonly io: CliIO;\n\t\treadonly outputMode: OutputMode;\n\t}) => Effect.Effect<CommandResult, CliError>;\n}\n\nexport interface CliDeps {\n\treadonly up: LifecycleCommandDeps;\n\treadonly apply: LifecycleCommandDeps;\n\treadonly codegen: CodegenCommandDeps;\n\treadonly dumpDeployment: DumpDeploymentCommandDeps;\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\treadonly fromSnapshot?: string;\n\treadonly snapshotCache?: string;\n\treadonly snapshotStale?: SnapshotStalePolicy;\n}\n\ninterface DumpDeploymentFlags extends ConfigFlags {\n\treadonly out?: string;\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);\nconst snapshotStaleParser = buildChoiceParser(['warn', 'block', 'clean-start'] 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\tfromSnapshot: flags.fromSnapshot,\n\t\tsnapshotCache: flags.snapshotCache,\n\t\tsnapshotStalePolicy: flags.snapshotStale,\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\tfromSnapshot: stringFlag('Start by restoring a named snapshot before acquire', 'name-or-id'),\n\t\t\tsnapshotCache: stringFlag(\n\t\t\t\t'Use a named snapshot as a startup cache and refresh it when stale',\n\t\t\t\t'name',\n\t\t\t),\n\t\t\tsnapshotStale: {\n\t\t\t\tkind: 'parsed',\n\t\t\t\tparse: snapshotStaleParser,\n\t\t\t\toptional: true,\n\t\t\t\tplaceholder: 'warn|block|clean-start',\n\t\t\t\tbrief: 'Policy when --from-snapshot inputs differ from the current stack',\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 codegenCommand = buildCommand<ConfigFlags, [], DevstackCliContext>({\n\tparameters: { flags: configFlagParams },\n\tdocs: {\n\t\tbrief: 'Regenerate committed bindings from Move source (no stack boot)',\n\t},\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'codegen', flags, [], (global) =>\n\t\t\tthis.deps.codegen.run({ configPath: global.configPath }),\n\t\t);\n\t},\n});\n\nconst dumpDeploymentCommand = buildCommand<DumpDeploymentFlags, [], DevstackCliContext>({\n\tparameters: {\n\t\tflags: {\n\t\t\t...configFlagParams,\n\t\t\tout: stringFlag('Write the deployment JSON to this file instead of stdout', 'path'),\n\t\t\tnetwork: stringFlag(\n\t\t\t\t'Emit a typed deployments/<network>.ts (satisfies AppNetworkDeployment) instead of the raw envelope',\n\t\t\t\t'name',\n\t\t\t),\n\t\t},\n\t},\n\tdocs: {\n\t\tbrief: 'Emit the stack deployment (deployment.json) for a real-network deploy',\n\t},\n\tfunc: function (flags) {\n\t\treturn runWithFlags(this, 'dump-deployment', flags, [], (global) =>\n\t\t\tthis.deps.dumpDeployment.run({\n\t\t\t\tconfigPath: global.configPath,\n\t\t\t\tout: flags.out,\n\t\t\t\tnetwork: flags.network,\n\t\t\t\tio: this.io,\n\t\t\t\toutputMode: global.outputMode,\n\t\t\t}),\n\t\t);\n\t},\n});\n\nconst statusCommand = buildCommand<IdentityFlags, [], DevstackCliContext>({\n\tparameters: { flags: identityFlagParams },\n\tdocs: { brief: 'Show the current stack projection (offline: from the manifest)' },\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\tcodegen: codegenCommand,\n\t\tdumpDeployment: dumpDeploymentCommand,\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: readDevstackVersion() },\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 * A non-zero value here means Stricli rejected argv before any verb\n * ran, so mapping to `USAGE` holds by construction. Kept as a named\n * function 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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"}