@mysten-incubation/devstack 0.1.1 → 0.2.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.
- package/README.md +6 -3
- package/dashboard-ui/assets/grpc-a4usE3Nk.js +3 -0
- package/dashboard-ui/assets/index-B82Bh84P.css +1 -0
- package/dashboard-ui/assets/index-CRYJ4pru.js +1277 -0
- package/dashboard-ui/index.html +2 -2
- package/dist/api/define-capabilities.d.mts +1 -20
- package/dist/api/define-capabilities.mjs +15 -22
- package/dist/api/define-capabilities.mjs.map +1 -1
- package/dist/api/define-devstack-with.d.mts +7 -1
- package/dist/api/define-devstack-with.mjs.map +1 -1
- package/dist/api/define-devstack.mjs.map +1 -1
- package/dist/api/define-plugin.d.mts +2 -1
- package/dist/api/inference-network.d.mts +31 -17
- package/dist/api/inference-network.mjs +40 -43
- package/dist/api/inference-network.mjs.map +1 -1
- package/dist/api/load-config.mjs +88 -0
- package/dist/api/load-config.mjs.map +1 -0
- package/dist/api/mode-narrowed-factory.d.mts +1 -1
- package/dist/api/mode-narrowed-factory.mjs.map +1 -1
- package/dist/api/run-stack-internal.mjs +202 -0
- package/dist/api/run-stack-internal.mjs.map +1 -0
- package/dist/api/run-stack.d.mts +82 -31
- package/dist/api/run-stack.mjs +22 -141
- package/dist/api/run-stack.mjs.map +1 -1
- package/dist/build-integrations/dapp-kit/index.d.mts +61 -0
- package/dist/build-integrations/dapp-kit/index.mjs +58 -0
- package/dist/build-integrations/dapp-kit/index.mjs.map +1 -0
- package/dist/build-integrations/playwright/config.d.mts +1 -30
- package/dist/build-integrations/playwright/config.mjs +1 -27
- package/dist/build-integrations/playwright/config.mjs.map +1 -1
- package/dist/build-integrations/playwright/errors.d.mts +4 -7
- package/dist/build-integrations/playwright/errors.mjs.map +1 -1
- package/dist/build-integrations/playwright/global-setup.d.mts +42 -71
- package/dist/build-integrations/playwright/global-setup.mjs +142 -218
- package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
- package/dist/build-integrations/playwright/index.d.mts +2 -2
- package/dist/build-integrations/playwright/index.mjs +2 -2
- package/dist/build-integrations/playwright/stack-context.d.mts +3 -1
- package/dist/build-integrations/playwright/stack-context.mjs +4 -2
- package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
- package/dist/build-integrations/playwright/wallet-context.mjs +44 -19
- package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
- package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -1
- package/dist/build-integrations/runtime/dapp-kit-slot.mjs.map +1 -1
- package/dist/build-integrations/runtime/discover.d.mts +6 -4
- package/dist/build-integrations/runtime/discover.mjs +15 -4
- package/dist/build-integrations/runtime/discover.mjs.map +1 -1
- package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
- package/dist/build-integrations/runtime/errors.mjs.map +1 -1
- package/dist/build-integrations/runtime/read-stack-context.mjs +3 -3
- package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +23 -7
- package/dist/build-integrations/runtime/resolve-discovery-env.mjs +25 -12
- package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -1
- package/dist/build-integrations/runtime/stack-context.d.mts +2 -2
- package/dist/build-integrations/vite/index.d.mts +84 -19
- package/dist/build-integrations/vite/index.mjs +203 -28
- package/dist/build-integrations/vite/index.mjs.map +1 -1
- package/dist/build-integrations/vitest/config.d.mts +20 -2
- package/dist/build-integrations/vitest/config.mjs +16 -4
- package/dist/build-integrations/vitest/config.mjs.map +1 -1
- package/dist/build-integrations/vitest/env.d.mts +18 -10
- package/dist/build-integrations/vitest/env.mjs +11 -10
- package/dist/build-integrations/vitest/env.mjs.map +1 -1
- package/dist/build-integrations/vitest/errors.d.mts +7 -11
- package/dist/build-integrations/vitest/errors.mjs +3 -4
- package/dist/build-integrations/vitest/errors.mjs.map +1 -1
- package/dist/build-integrations/vitest/global-setup.d.mts +35 -0
- package/dist/build-integrations/vitest/global-setup.mjs +156 -0
- package/dist/build-integrations/vitest/global-setup.mjs.map +1 -0
- package/dist/build-integrations/vitest/index.d.mts +3 -2
- package/dist/build-integrations/vitest/index.mjs +3 -2
- package/dist/build-integrations/vitest/setup.mjs +2 -2
- package/dist/build-integrations/vitest/setup.mjs.map +1 -1
- package/dist/build-integrations/vitest/stack-context.d.mts +5 -3
- package/dist/build-integrations/vitest/stack-context.mjs +3 -2
- package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
- package/dist/cli/bail.mjs.map +1 -1
- package/dist/cli/doctor-probes.mjs +1 -3
- package/dist/cli/doctor-probes.mjs.map +1 -1
- package/dist/cli/main.d.mts +23 -1
- package/dist/cli/main.mjs +63 -8
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/prune-direct.mjs.map +1 -1
- package/dist/cli/snapshot-reader.mjs.map +1 -1
- package/dist/cli/up-lifecycle.mjs.map +1 -1
- package/dist/cli/wirings/apply.mjs +28 -21
- package/dist/cli/wirings/apply.mjs.map +1 -1
- package/dist/cli/wirings/codegen.mjs +108 -0
- package/dist/cli/wirings/codegen.mjs.map +1 -0
- package/dist/cli/wirings/config-loader.mjs +12 -62
- package/dist/cli/wirings/config-loader.mjs.map +1 -1
- package/dist/cli/wirings/dump-ids.mjs +86 -0
- package/dist/cli/wirings/dump-ids.mjs.map +1 -0
- package/dist/cli/wirings/engine-command.mjs +1 -1
- package/dist/cli/wirings/engine-command.mjs.map +1 -1
- package/dist/cli/wirings/identity.mjs +2 -2
- package/dist/cli/wirings/identity.mjs.map +1 -1
- package/dist/cli/wirings/provide-file-system.mjs.map +1 -1
- package/dist/cli/wirings/read-devstack-version.mjs +17 -0
- package/dist/cli/wirings/read-devstack-version.mjs.map +1 -0
- package/dist/cli/wirings/snapshot.mjs +27 -34
- package/dist/cli/wirings/snapshot.mjs.map +1 -1
- package/dist/cli/wirings/up-ipc.mjs +147 -0
- package/dist/cli/wirings/up-ipc.mjs.map +1 -0
- package/dist/cli/wirings/up.mjs +218 -198
- package/dist/cli/wirings/up.mjs.map +1 -1
- package/dist/cli/wirings/wipe.mjs +1 -1
- package/dist/cli/wirings/wipe.mjs.map +1 -1
- package/dist/contracts/chain-probe.d.mts +1 -1
- package/dist/contracts/chain-probe.mjs +1 -1
- package/dist/contracts/chain-probe.mjs.map +1 -1
- package/dist/contracts/codegenable.d.mts +60 -6
- package/dist/contracts/codegenable.mjs +28 -0
- package/dist/contracts/codegenable.mjs.map +1 -0
- package/dist/contracts/config-bindings.mjs +162 -0
- package/dist/contracts/config-bindings.mjs.map +1 -0
- package/dist/contracts/container-runtime.d.mts +23 -17
- package/dist/contracts/plugin-expander.mjs.map +1 -1
- package/dist/contracts/snapshotable.d.mts +5 -7
- package/dist/contracts/wallet-protocol.d.mts +2 -5
- package/dist/contracts/wallet-protocol.mjs +2 -5
- package/dist/contracts/wallet-protocol.mjs.map +1 -1
- package/dist/index.d.mts +21 -26
- package/dist/index.mjs +8 -9
- package/dist/orchestrators/boot.d.mts +28 -0
- package/dist/orchestrators/boot.mjs +440 -0
- package/dist/orchestrators/boot.mjs.map +1 -0
- package/dist/orchestrators/codegen/bindings.d.mts +13 -1
- package/dist/orchestrators/codegen/bindings.mjs +23 -9
- package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
- package/dist/orchestrators/codegen/config-runtime.mjs +208 -0
- package/dist/orchestrators/codegen/config-runtime.mjs.map +1 -0
- package/dist/orchestrators/codegen/emit.mjs +1 -1
- package/dist/orchestrators/codegen/emit.mjs.map +1 -1
- package/dist/orchestrators/codegen/errors.mjs +21 -1
- package/dist/orchestrators/codegen/errors.mjs.map +1 -1
- package/dist/orchestrators/codegen/format.mjs +2 -0
- package/dist/orchestrators/codegen/format.mjs.map +1 -1
- package/dist/orchestrators/codegen/gitignore.mjs +30 -11
- package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
- package/dist/orchestrators/codegen/id-config.mjs +107 -0
- package/dist/orchestrators/codegen/id-config.mjs.map +1 -0
- package/dist/orchestrators/codegen/output-location.mjs +23 -32
- package/dist/orchestrators/codegen/output-location.mjs.map +1 -1
- package/dist/orchestrators/codegen/paths.mjs +8 -1
- package/dist/orchestrators/codegen/paths.mjs.map +1 -1
- package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
- package/dist/orchestrators/codegen/service.mjs +286 -58
- package/dist/orchestrators/codegen/service.mjs.map +1 -1
- package/dist/orchestrators/layers.mjs +31 -0
- package/dist/orchestrators/layers.mjs.map +1 -0
- package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -1
- package/dist/orchestrators/lifecycle-prune/index.mjs +8 -10
- package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -1
- package/dist/orchestrators/network-options.d.mts +52 -0
- package/dist/orchestrators/network-options.mjs +43 -0
- package/dist/orchestrators/network-options.mjs.map +1 -0
- package/dist/orchestrators/router/cleanup.mjs.map +1 -1
- package/dist/orchestrators/router/cors.mjs.map +1 -1
- package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
- package/dist/orchestrators/router/errors.mjs.map +1 -1
- package/dist/orchestrators/router/file-provider.mjs.map +1 -1
- package/dist/orchestrators/router/hostname.mjs.map +1 -1
- package/dist/orchestrators/router/profile.mjs +1 -1
- package/dist/orchestrators/router/profile.mjs.map +1 -1
- package/dist/orchestrators/router/service.d.mts +2 -2
- package/dist/orchestrators/router/service.mjs +12 -27
- package/dist/orchestrators/router/service.mjs.map +1 -1
- package/dist/orchestrators/router/traefik-container.mjs +1 -1
- package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
- package/dist/orchestrators/snapshot/capture.mjs +171 -195
- package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
- package/dist/orchestrators/snapshot/descriptor.d.mts +1 -1
- package/dist/orchestrators/snapshot/descriptor.mjs +53 -11
- package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
- package/dist/orchestrators/snapshot/identity-guard.mjs +3 -5
- package/dist/orchestrators/snapshot/identity-guard.mjs.map +1 -1
- package/dist/orchestrators/snapshot/image-bundle-tags.mjs +32 -101
- package/dist/orchestrators/snapshot/image-bundle-tags.mjs.map +1 -1
- package/dist/orchestrators/snapshot/index.mjs +1 -4
- package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
- package/dist/orchestrators/snapshot/interrupted-restore.mjs +116 -0
- package/dist/orchestrators/snapshot/interrupted-restore.mjs.map +1 -0
- package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -1
- package/dist/orchestrators/snapshot/prune.mjs +40 -31
- package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
- package/dist/orchestrators/snapshot/restore.mjs +221 -196
- package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
- package/dist/orchestrators/snapshot/service.mjs +72 -62
- package/dist/orchestrators/snapshot/service.mjs.map +1 -1
- package/dist/orchestrators/snapshot/wipe.mjs +67 -56
- package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
- package/dist/plugins/account/codegen.mjs +3 -0
- package/dist/plugins/account/codegen.mjs.map +1 -1
- package/dist/plugins/account/errors.d.mts +2 -2
- package/dist/plugins/account/errors.mjs +1 -4
- package/dist/plugins/account/errors.mjs.map +1 -1
- package/dist/plugins/account/funding.mjs +3 -27
- package/dist/plugins/account/funding.mjs.map +1 -1
- package/dist/plugins/account/index.d.mts +3 -8
- package/dist/plugins/account/index.mjs +71 -35
- package/dist/plugins/account/index.mjs.map +1 -1
- package/dist/plugins/account/keypair.d.mts +8 -7
- package/dist/plugins/account/keypair.mjs +5 -18
- package/dist/plugins/account/keypair.mjs.map +1 -1
- package/dist/plugins/account/lease.mjs.map +1 -1
- package/dist/plugins/account/registry.d.mts +1 -46
- package/dist/plugins/account/registry.mjs.map +1 -1
- package/dist/plugins/account/service.d.mts +43 -34
- package/dist/plugins/account/service.mjs +9 -54
- package/dist/plugins/account/service.mjs.map +1 -1
- package/dist/plugins/account/snapshot.mjs.map +1 -1
- package/dist/plugins/account/variants/ephemeral.mjs.map +1 -1
- package/dist/plugins/account/variants/impersonate.mjs.map +1 -1
- package/dist/plugins/account/variants/signer.mjs.map +1 -1
- package/dist/plugins/action/discriminator.mjs.map +1 -1
- package/dist/plugins/action/errors.mjs +1 -4
- package/dist/plugins/action/errors.mjs.map +1 -1
- package/dist/plugins/action/execute.mjs +3 -4
- package/dist/plugins/action/execute.mjs.map +1 -1
- package/dist/plugins/action/index.d.mts +2 -2
- package/dist/plugins/action/index.mjs +7 -11
- package/dist/plugins/action/index.mjs.map +1 -1
- package/dist/plugins/action/service.d.mts +5 -7
- package/dist/plugins/action/service.mjs +2 -16
- package/dist/plugins/action/service.mjs.map +1 -1
- package/dist/plugins/coin/address-resolution.mjs.map +1 -1
- package/dist/plugins/coin/codegen.mjs +97 -16
- package/dist/plugins/coin/codegen.mjs.map +1 -1
- package/dist/plugins/coin/discovery.mjs.map +1 -1
- package/dist/plugins/coin/errors.mjs +1 -4
- package/dist/plugins/coin/errors.mjs.map +1 -1
- package/dist/plugins/coin/index.d.mts +4 -34
- package/dist/plugins/coin/index.mjs +47 -28
- package/dist/plugins/coin/index.mjs.map +1 -1
- package/dist/plugins/coin/metadata.d.mts +1 -0
- package/dist/plugins/coin/metadata.mjs +8 -9
- package/dist/plugins/coin/metadata.mjs.map +1 -1
- package/dist/plugins/coin/mint.mjs +7 -21
- package/dist/plugins/coin/mint.mjs.map +1 -1
- package/dist/plugins/coin/registry.mjs +33 -20
- package/dist/plugins/coin/registry.mjs.map +1 -1
- package/dist/plugins/coin/service.d.mts +1 -1
- package/dist/plugins/coin/service.mjs +1 -8
- package/dist/plugins/coin/service.mjs.map +1 -1
- package/dist/plugins/coin/snapshot.mjs.map +1 -1
- package/dist/plugins/coin/type-strings.mjs.map +1 -1
- package/dist/plugins/dashboard/domain.mjs +106 -167
- package/dist/plugins/dashboard/domain.mjs.map +1 -1
- package/dist/plugins/dashboard/index.mjs +13 -13
- package/dist/plugins/dashboard/index.mjs.map +1 -1
- package/dist/plugins/dashboard/origin-policy.mjs +3 -3
- package/dist/plugins/dashboard/origin-policy.mjs.map +1 -1
- package/dist/plugins/dashboard/routable.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/builder.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/enums.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/root.mjs +23 -44
- package/dist/plugins/dashboard/schema/root.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/types.mjs +37 -91
- package/dist/plugins/dashboard/schema/types.mjs.map +1 -1
- package/dist/plugins/dashboard/schema.mjs.map +1 -1
- package/dist/plugins/dashboard/server.mjs.map +1 -1
- package/dist/plugins/deepbook/codegen.d.mts +12 -11
- package/dist/plugins/deepbook/codegen.mjs +108 -11
- package/dist/plugins/deepbook/codegen.mjs.map +1 -1
- package/dist/plugins/deepbook/deploy.mjs +7 -14
- package/dist/plugins/deepbook/deploy.mjs.map +1 -1
- package/dist/plugins/deepbook/errors.mjs +4 -2
- package/dist/plugins/deepbook/errors.mjs.map +1 -1
- package/dist/plugins/deepbook/faucet-strategy.mjs +4 -10
- package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
- package/dist/plugins/deepbook/hash.mjs.map +1 -1
- package/dist/plugins/deepbook/index.d.mts +38 -34
- package/dist/plugins/deepbook/index.mjs +133 -64
- package/dist/plugins/deepbook/index.mjs.map +1 -1
- package/dist/plugins/deepbook/plugin-key.mjs.map +1 -1
- package/dist/plugins/deepbook/pyth/index.mjs +4 -8
- package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
- package/dist/plugins/deepbook/snapshot.mjs +0 -1
- package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
- package/dist/plugins/deepbook/type-strings.mjs.map +1 -1
- package/dist/plugins/deepbook/types.d.mts +2 -1
- package/dist/plugins/deepbook/types.mjs.map +1 -1
- package/dist/plugins/faucet/dispatcher.d.mts +3 -3
- package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
- package/dist/plugins/faucet/errors.mjs.map +1 -1
- package/dist/plugins/faucet/http.mjs +2 -10
- package/dist/plugins/faucet/http.mjs.map +1 -1
- package/dist/plugins/faucet/index.mjs +0 -1
- package/dist/plugins/faucet/index.mjs.map +1 -1
- package/dist/plugins/host-service/errors.d.mts +1 -3
- package/dist/plugins/host-service/errors.mjs +1 -2
- package/dist/plugins/host-service/errors.mjs.map +1 -1
- package/dist/plugins/host-service/index.d.mts +1 -2
- package/dist/plugins/host-service/index.mjs +11 -11
- package/dist/plugins/host-service/index.mjs.map +1 -1
- package/dist/plugins/host-service/routable.mjs.map +1 -1
- package/dist/plugins/host-service/service.mjs +13 -13
- package/dist/plugins/host-service/service.mjs.map +1 -1
- package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -1
- package/dist/plugins/internal/codegen-helpers.mjs +2 -0
- package/dist/plugins/internal/codegen-helpers.mjs.map +1 -1
- package/dist/plugins/internal/funding-failure-error.mjs.map +1 -1
- package/dist/plugins/{postgres → internal/postgres-sidecar}/connection.mjs +1 -1
- package/dist/plugins/internal/postgres-sidecar/connection.mjs.map +1 -0
- package/dist/plugins/{postgres → internal/postgres-sidecar}/db-ensure.mjs +30 -16
- package/dist/plugins/internal/postgres-sidecar/db-ensure.mjs.map +1 -0
- package/dist/plugins/internal/postgres-sidecar/errors.mjs +21 -0
- package/dist/plugins/internal/postgres-sidecar/errors.mjs.map +1 -0
- package/dist/{substrate/runtime/scoped-multimap → plugins/internal/postgres-sidecar}/index.mjs +1 -0
- package/dist/plugins/internal/postgres-sidecar/service.mjs +121 -0
- package/dist/plugins/internal/postgres-sidecar/service.mjs.map +1 -0
- package/dist/plugins/package/build.mjs +2 -2
- package/dist/plugins/package/build.mjs.map +1 -1
- package/dist/plugins/package/codegen.d.mts +7 -14
- package/dist/plugins/package/codegen.mjs +187 -59
- package/dist/plugins/package/codegen.mjs.map +1 -1
- package/dist/plugins/package/dep-resolution.mjs +51 -3
- package/dist/plugins/package/dep-resolution.mjs.map +1 -1
- package/dist/plugins/package/errors.mjs +1 -4
- package/dist/plugins/package/errors.mjs.map +1 -1
- package/dist/plugins/package/git-source.d.mts +18 -0
- package/dist/plugins/package/git-source.mjs +119 -0
- package/dist/plugins/package/git-source.mjs.map +1 -0
- package/dist/plugins/package/index.d.mts +28 -19
- package/dist/plugins/package/index.mjs +107 -32
- package/dist/plugins/package/index.mjs.map +1 -1
- package/dist/plugins/package/mode-known.mjs +2 -2
- package/dist/plugins/package/mode-known.mjs.map +1 -1
- package/dist/plugins/package/mode-local.mjs +18 -35
- package/dist/plugins/package/mode-local.mjs.map +1 -1
- package/dist/plugins/package/publish-executor.mjs +13 -8
- package/dist/plugins/package/publish-executor.mjs.map +1 -1
- package/dist/plugins/package/publish-output.d.mts +1 -11
- package/dist/plugins/package/publish-output.mjs +1 -9
- package/dist/plugins/package/publish-output.mjs.map +1 -1
- package/dist/plugins/package/registry.d.mts +1 -1
- package/dist/plugins/package/registry.mjs +26 -14
- package/dist/plugins/package/registry.mjs.map +1 -1
- package/dist/plugins/package/service.mjs.map +1 -1
- package/dist/plugins/package/snapshot.mjs.map +1 -1
- package/dist/plugins/router-entrypoints.mjs +1 -3
- package/dist/plugins/router-entrypoints.mjs.map +1 -1
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +1 -2
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +1 -6
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
- package/dist/plugins/seal/codegen.d.mts +1 -5
- package/dist/plugins/seal/codegen.mjs +83 -12
- package/dist/plugins/seal/codegen.mjs.map +1 -1
- package/dist/plugins/seal/config-render.mjs +2 -6
- package/dist/plugins/seal/config-render.mjs.map +1 -1
- package/dist/plugins/seal/deploy.mjs +18 -29
- package/dist/plugins/seal/deploy.mjs.map +1 -1
- package/dist/plugins/seal/errors.d.mts +4 -5
- package/dist/plugins/seal/errors.mjs +1 -8
- package/dist/plugins/seal/errors.mjs.map +1 -1
- package/dist/plugins/seal/index.d.mts +7 -28
- package/dist/plugins/seal/index.mjs +69 -47
- package/dist/plugins/seal/index.mjs.map +1 -1
- package/dist/plugins/seal/key-manager.mjs.map +1 -1
- package/dist/plugins/seal/key-server.mjs +1 -6
- package/dist/plugins/seal/key-server.mjs.map +1 -1
- package/dist/plugins/seal/keygen.mjs +2 -3
- package/dist/plugins/seal/keygen.mjs.map +1 -1
- package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
- package/dist/plugins/seal/mode/live.mjs +1 -1
- package/dist/plugins/seal/mode/live.mjs.map +1 -1
- package/dist/plugins/seal/mode/local-keygen.mjs +4 -10
- package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
- package/dist/plugins/seal/plugin-key.mjs.map +1 -1
- package/dist/plugins/seal/registry-publish.mjs.map +1 -1
- package/dist/plugins/seal/routable.mjs.map +1 -1
- package/dist/plugins/seal/service.mjs.map +1 -1
- package/dist/plugins/seal/snapshot.mjs +1 -2
- package/dist/plugins/seal/snapshot.mjs.map +1 -1
- package/dist/plugins/sui/auto-tick.mjs +5 -5
- package/dist/plugins/sui/auto-tick.mjs.map +1 -1
- package/dist/plugins/sui/chain-build-container.mjs +2 -2
- package/dist/plugins/sui/chain-build-container.mjs.map +1 -1
- package/dist/plugins/sui/chain-probe.mjs +4 -4
- package/dist/plugins/sui/chain-probe.mjs.map +1 -1
- package/dist/plugins/sui/codegen.d.mts +9 -7
- package/dist/plugins/sui/codegen.mjs +49 -46
- package/dist/plugins/sui/codegen.mjs.map +1 -1
- package/dist/plugins/sui/errors.d.mts +1 -1
- package/dist/plugins/sui/errors.mjs +1 -9
- package/dist/plugins/sui/errors.mjs.map +1 -1
- package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/index.d.mts +1 -1
- package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/index.mjs +3 -7
- package/dist/plugins/sui/exec/index.mjs.map +1 -0
- package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/sign-and-dispatch.mjs +1 -1
- package/dist/plugins/sui/exec/sign-and-dispatch.mjs.map +1 -0
- package/dist/plugins/sui/fork-faucet-strategy.mjs +2 -6
- package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -1
- package/dist/plugins/sui/fork-orchestration.mjs +13 -23
- package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
- package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
- package/dist/plugins/sui/index.d.mts +34 -83
- package/dist/plugins/sui/index.mjs +200 -68
- package/dist/plugins/sui/index.mjs.map +1 -1
- package/dist/{substrate/runtime/sui-ledger → plugins/sui/ledger}/object-ref.mjs +1 -1
- package/dist/plugins/sui/ledger/object-ref.mjs.map +1 -0
- package/dist/plugins/sui/local-faucet-strategy.mjs +1 -5
- package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -1
- package/dist/plugins/sui/log-attrs.mjs +11 -0
- package/dist/plugins/sui/log-attrs.mjs.map +1 -0
- package/dist/plugins/sui/mode/external.mjs +4 -12
- package/dist/plugins/sui/mode/external.mjs.map +1 -1
- package/dist/plugins/sui/mode/fork.mjs +19 -16
- package/dist/plugins/sui/mode/fork.mjs.map +1 -1
- package/dist/plugins/sui/mode/live.mjs +4 -17
- package/dist/plugins/sui/mode/live.mjs.map +1 -1
- package/dist/plugins/sui/mode/local.d.mts +1 -0
- package/dist/plugins/sui/mode/local.mjs +203 -61
- package/dist/plugins/sui/mode/local.mjs.map +1 -1
- package/dist/plugins/sui/mode/shared-boot.mjs +18 -23
- package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
- package/dist/plugins/sui/mode/shared.d.mts +6 -7
- package/dist/plugins/sui/mode/shared.mjs.map +1 -1
- package/dist/plugins/sui/mode/spec.d.mts +23 -6
- package/dist/{substrate/runtime/sui-move-build → plugins/sui/move}/index.mjs +28 -30
- package/dist/plugins/sui/move/index.mjs.map +1 -0
- package/dist/plugins/sui/move-summary-runner.mjs +8 -4
- package/dist/plugins/sui/move-summary-runner.mjs.map +1 -1
- package/dist/{substrate/network.d.mts → plugins/sui/network-config.d.mts} +11 -10
- package/dist/plugins/sui/network-resolver.d.mts +6 -6
- package/dist/plugins/sui/routable.mjs.map +1 -1
- package/dist/plugins/sui/service.mjs +10 -6
- package/dist/plugins/sui/service.mjs.map +1 -1
- package/dist/plugins/sui/snapshot.mjs +10 -5
- package/dist/plugins/sui/snapshot.mjs.map +1 -1
- package/dist/plugins/wallet/codegen.d.mts +8 -6
- package/dist/plugins/wallet/codegen.mjs +4 -10
- package/dist/plugins/wallet/codegen.mjs.map +1 -1
- package/dist/plugins/wallet/errors.mjs +1 -4
- package/dist/plugins/wallet/errors.mjs.map +1 -1
- package/dist/plugins/wallet/index.d.mts +3 -6
- package/dist/plugins/wallet/index.mjs +16 -21
- package/dist/plugins/wallet/index.mjs.map +1 -1
- package/dist/plugins/wallet/origin-policy.mjs +3 -3
- package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
- package/dist/plugins/wallet/pairing.mjs +6 -24
- package/dist/plugins/wallet/pairing.mjs.map +1 -1
- package/dist/plugins/wallet/protocol.mjs.map +1 -1
- package/dist/plugins/wallet/routable.mjs.map +1 -1
- package/dist/plugins/wallet/server.mjs +17 -24
- package/dist/plugins/wallet/server.mjs.map +1 -1
- package/dist/plugins/wallet/service.d.mts +2 -2
- package/dist/plugins/wallet/service.mjs +4 -15
- package/dist/plugins/wallet/service.mjs.map +1 -1
- package/dist/plugins/wallet/snapshot.mjs.map +1 -1
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +1 -5
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
- package/dist/plugins/walrus/codegen.d.mts +2 -13
- package/dist/plugins/walrus/codegen.mjs +93 -28
- package/dist/plugins/walrus/codegen.mjs.map +1 -1
- package/dist/plugins/walrus/deploy-paths.mjs +2 -3
- package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
- package/dist/plugins/walrus/deploy.mjs +26 -19
- package/dist/plugins/walrus/deploy.mjs.map +1 -1
- package/dist/plugins/walrus/errors.mjs +1 -8
- package/dist/plugins/walrus/errors.mjs.map +1 -1
- package/dist/plugins/walrus/faucet-strategy.mjs +15 -1
- package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
- package/dist/plugins/walrus/index.d.mts +13 -44
- package/dist/plugins/walrus/index.mjs +119 -122
- package/dist/plugins/walrus/index.mjs.map +1 -1
- package/dist/plugins/walrus/mode/known-deploy.mjs +6 -6
- package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
- package/dist/plugins/walrus/mode/local-cluster.d.mts +2 -2
- package/dist/plugins/walrus/mode/local-cluster.mjs +2 -10
- package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
- package/dist/plugins/walrus/plugin-key.mjs.map +1 -1
- package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
- package/dist/plugins/walrus/routable.mjs.map +1 -1
- package/dist/plugins/walrus/service.mjs.map +1 -1
- package/dist/plugins/walrus/snapshot.mjs +3 -3
- package/dist/plugins/walrus/snapshot.mjs.map +1 -1
- package/dist/plugins/walrus/storage-nodes.mjs +53 -14
- package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
- package/dist/plugins/walrus/wal-swap.mjs +4 -8
- package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
- package/dist/primitives/artifact-publisher.mjs.map +1 -1
- package/dist/primitives/cache.d.mts +1 -1
- package/dist/runtime/docker/client.mjs +47 -3
- package/dist/runtime/docker/client.mjs.map +1 -1
- package/dist/runtime/docker/container.mjs +37 -57
- package/dist/runtime/docker/container.mjs.map +1 -1
- package/dist/runtime/docker/errors.mjs.map +1 -1
- package/dist/runtime/docker/exec.mjs +2 -2
- package/dist/runtime/docker/exec.mjs.map +1 -1
- package/dist/runtime/docker/image.mjs +10 -11
- package/dist/runtime/docker/image.mjs.map +1 -1
- package/dist/runtime/docker/index.mjs +0 -1
- package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -1
- package/dist/runtime/docker/inventory.mjs +9 -9
- package/dist/runtime/docker/inventory.mjs.map +1 -1
- package/dist/runtime/docker/labels.mjs +12 -1
- package/dist/runtime/docker/labels.mjs.map +1 -1
- package/dist/runtime/docker/network.mjs +6 -6
- package/dist/runtime/docker/network.mjs.map +1 -1
- package/dist/runtime/docker/render-run-args.mjs.map +1 -1
- package/dist/runtime/docker/service.mjs +31 -45
- package/dist/runtime/docker/service.mjs.map +1 -1
- package/dist/runtime/docker/sweep.mjs +14 -64
- package/dist/runtime/docker/sweep.mjs.map +1 -1
- package/dist/runtime/docker/volume.mjs +1 -1
- package/dist/runtime/docker/volume.mjs.map +1 -1
- package/dist/runtime/docker/wrap.mjs +14 -2
- package/dist/runtime/docker/wrap.mjs.map +1 -1
- package/dist/substrate/brand.d.mts +2 -5
- package/dist/substrate/brand.mjs +3 -2
- package/dist/substrate/brand.mjs.map +1 -1
- package/dist/substrate/cross-process.mjs +2 -16
- package/dist/substrate/cross-process.mjs.map +1 -1
- package/dist/substrate/event-time.mjs +0 -2
- package/dist/substrate/event-time.mjs.map +1 -1
- package/dist/substrate/events.d.mts +46 -16
- package/dist/substrate/identity.d.mts +11 -5
- package/dist/substrate/manifest.d.mts +33 -26
- package/dist/substrate/manifest.mjs +8 -3
- package/dist/substrate/manifest.mjs.map +1 -1
- package/dist/substrate/options.d.mts +33 -4
- package/dist/substrate/plugin-ctx.d.mts +70 -0
- package/dist/substrate/plugin-ctx.mjs +66 -0
- package/dist/substrate/plugin-ctx.mjs.map +1 -0
- package/dist/substrate/plugin.d.mts +46 -38
- package/dist/substrate/plugin.mjs +13 -9
- package/dist/substrate/plugin.mjs.map +1 -1
- package/dist/substrate/projection.d.mts +1 -1
- package/dist/substrate/runtime/atomic-write.mjs +3 -4
- package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
- package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
- package/dist/substrate/runtime/cache/service.mjs +50 -20
- package/dist/substrate/runtime/cache/service.mjs.map +1 -1
- package/dist/substrate/runtime/config-validation.mjs.map +1 -1
- package/dist/substrate/runtime/control-plane/domain.mjs +34 -12
- package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -1
- package/dist/substrate/runtime/control-plane/service.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +14 -17
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/runtime-control-lock.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/index.mjs +0 -2
- package/dist/substrate/runtime/cross-process/live-clock.mjs +1 -1
- package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/liveness.mjs +11 -18
- package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +2 -2
- package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/roster.mjs +17 -127
- package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/stack-lock.mjs +29 -28
- package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
- package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
- package/dist/substrate/runtime/errors.mjs +1 -13
- package/dist/substrate/runtime/errors.mjs.map +1 -1
- package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -1
- package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -1
- package/dist/substrate/runtime/host-gateway.mjs.map +1 -1
- package/dist/substrate/runtime/host-tree-tar/index.mjs +15 -163
- package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
- package/dist/substrate/runtime/http-probe.d.mts +1 -3
- package/dist/substrate/runtime/http-probe.mjs.map +1 -1
- package/dist/substrate/runtime/index.mjs +1 -6
- package/dist/substrate/runtime/lease-broker/service.mjs +5 -23
- package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs +3 -3
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/graph-input-id.d.mts +1 -0
- package/dist/substrate/runtime/lifecycle/graph-input-id.mjs +133 -0
- package/dist/substrate/runtime/lifecycle/graph-input-id.mjs.map +1 -0
- package/dist/substrate/runtime/lifecycle/index.mjs +2 -2
- package/dist/substrate/runtime/lifecycle/plugin-registry.d.mts +1 -1
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +22 -1
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/ready-gate.mjs +2 -15
- package/dist/substrate/runtime/lifecycle/ready-gate.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/selective-restart.mjs +29 -25
- package/dist/substrate/runtime/lifecycle/selective-restart.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/signals.mjs +1 -1
- package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/state-machine.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/watch-attribution.d.mts +1 -1
- package/dist/substrate/runtime/lifecycle/watch-attribution.mjs +0 -1
- package/dist/substrate/runtime/lifecycle/watch-attribution.mjs.map +1 -1
- package/dist/substrate/runtime/managed-container.d.mts +5 -1
- package/dist/substrate/runtime/managed-container.mjs +8 -10
- package/dist/substrate/runtime/managed-container.mjs.map +1 -1
- package/dist/substrate/runtime/manifest/manifest.mjs +8 -30
- package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
- package/dist/substrate/runtime/mode-errors.d.mts +1 -2
- package/dist/substrate/runtime/mode-errors.mjs.map +1 -1
- package/dist/substrate/runtime/observability/cascade-formatter.mjs +1 -11
- package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
- package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -1
- package/dist/substrate/runtime/observability/index.d.mts +1 -1
- package/dist/substrate/runtime/observability/index.mjs +4 -6
- package/dist/substrate/runtime/observability/log-attrs.mjs +46 -0
- package/dist/substrate/runtime/observability/log-attrs.mjs.map +1 -0
- package/dist/substrate/runtime/observability/log-store.mjs.map +1 -1
- package/dist/substrate/runtime/observability/logger.mjs +3 -3
- package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
- package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -1
- package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
- package/dist/substrate/runtime/observability/process-lines.mjs.map +1 -1
- package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
- package/dist/substrate/runtime/observability/subprocess-capture.mjs +2 -2
- package/dist/substrate/runtime/observability/subprocess-capture.mjs.map +1 -1
- package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -1
- package/dist/substrate/runtime/paths.mjs +27 -9
- package/dist/substrate/runtime/paths.mjs.map +1 -1
- package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -1
- package/dist/substrate/runtime/port-broker/service.mjs +10 -27
- package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
- package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
- package/dist/substrate/runtime/probes.d.mts +2 -3
- package/dist/substrate/runtime/probes.mjs.map +1 -1
- package/dist/substrate/runtime/process-supervisor.mjs.map +1 -1
- package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
- package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
- package/dist/substrate/runtime/projection/update.mjs +87 -11
- package/dist/substrate/runtime/projection/update.mjs.map +1 -1
- package/dist/substrate/runtime/random-suffix.mjs.map +1 -1
- package/dist/substrate/runtime/reconcile/fs-plan.mjs +146 -0
- package/dist/substrate/runtime/reconcile/fs-plan.mjs.map +1 -0
- package/dist/substrate/runtime/reconcile/graph.mjs +19 -0
- package/dist/substrate/runtime/reconcile/graph.mjs.map +1 -0
- package/dist/substrate/runtime/reconcile/index.mjs +5 -0
- package/dist/substrate/runtime/reconcile/label.mjs +41 -0
- package/dist/substrate/runtime/reconcile/label.mjs.map +1 -0
- package/dist/substrate/runtime/reconcile/spec.mjs +15 -0
- package/dist/substrate/runtime/reconcile/spec.mjs.map +1 -0
- package/dist/substrate/runtime/retry-policy.d.mts +2 -3
- package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
- package/dist/substrate/runtime/routed-url.mjs.map +1 -1
- package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
- package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
- package/dist/substrate/runtime/stage-and-swap/index.mjs +49 -12
- package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -1
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
- package/dist/substrate/runtime/strategy-registry/service.mjs +29 -17
- package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
- package/dist/substrate/runtime/subnet-broker.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/acquire-node.mjs +188 -56
- package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/background-tasks.mjs +79 -65
- package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/command-loop.mjs +82 -23
- package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/contribution-dispatcher.mjs +16 -0
- package/dist/substrate/runtime/supervisor/contribution-dispatcher.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/errors.mjs +11 -2
- package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/index.mjs +2 -0
- package/dist/substrate/runtime/supervisor/shutdown.mjs +6 -6
- package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs +60 -70
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/state.d.mts +1 -1
- package/dist/substrate/runtime/supervisor/state.mjs +1 -1
- package/dist/substrate/runtime/supervisor/state.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/teardown.mjs +10 -9
- package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/wiring.mjs +6 -20
- package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -1
- package/dist/substrate/runtime/tar/reader.mjs +216 -0
- package/dist/substrate/runtime/tar/reader.mjs.map +1 -0
- package/dist/substrate/runtime/typed-env.mjs.map +1 -1
- package/dist/substrate/versioned-doc-schema.mjs.map +1 -1
- package/dist/substrate/versioned-doc-sync.mjs +5 -5
- package/dist/substrate/versioned-doc-sync.mjs.map +1 -1
- package/dist/surfaces/cli/command-tree.mjs +49 -7
- package/dist/surfaces/cli/command-tree.mjs.map +1 -1
- package/dist/surfaces/cli/commands/config.mjs +1 -1
- package/dist/surfaces/cli/commands/config.mjs.map +1 -1
- package/dist/surfaces/cli/commands/confirm-node.mjs.map +1 -1
- package/dist/surfaces/cli/commands/confirm.mjs.map +1 -1
- package/dist/surfaces/cli/commands/doctor.mjs +1 -1
- package/dist/surfaces/cli/commands/doctor.mjs.map +1 -1
- package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -1
- package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
- package/dist/surfaces/cli/commands/prune.mjs +1 -1
- package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
- package/dist/surfaces/cli/commands/snapshot.mjs +1 -1
- package/dist/surfaces/cli/commands/snapshot.mjs.map +1 -1
- package/dist/surfaces/cli/commands/status.mjs +1 -1
- package/dist/surfaces/cli/commands/status.mjs.map +1 -1
- package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
- package/dist/surfaces/cli/commands/wipe.mjs +3 -4
- package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
- package/dist/surfaces/cli/envelope.mjs.map +1 -1
- package/dist/surfaces/cli/errors.mjs.map +1 -1
- package/dist/surfaces/cli/flags.mjs.map +1 -1
- package/dist/surfaces/cli/index.mjs +45 -14
- package/dist/surfaces/cli/index.mjs.map +1 -1
- package/dist/surfaces/cli/output.mjs.map +1 -1
- package/dist/surfaces/cli/sysexits.mjs.map +1 -1
- package/dist/surfaces/tui/app.mjs +0 -21
- package/dist/surfaces/tui/app.mjs.map +1 -1
- package/dist/surfaces/tui/dashboard.mjs +0 -48
- package/dist/surfaces/tui/dashboard.mjs.map +1 -1
- package/dist/surfaces/tui/display-derivation.mjs +80 -16
- package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
- package/dist/surfaces/tui/errors.mjs.map +1 -1
- package/dist/surfaces/tui/event-log.mjs +0 -9
- package/dist/surfaces/tui/event-log.mjs.map +1 -1
- package/dist/surfaces/tui/heartbeat.mjs.map +1 -1
- package/dist/surfaces/tui/index.mjs +2 -2
- package/dist/surfaces/tui/index.mjs.map +1 -1
- package/dist/surfaces/tui/input.mjs.map +1 -1
- package/dist/surfaces/tui/mode-detect.mjs.map +1 -1
- package/dist/surfaces/tui/mount-ink.mjs.map +1 -1
- package/dist/surfaces/tui/plain-renderer.mjs +26 -19
- package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
- package/dist/surfaces/tui/resource-table.mjs +8 -10
- package/dist/surfaces/tui/resource-table.mjs.map +1 -1
- package/images/postgres/Dockerfile +0 -1
- package/images/sui/Dockerfile +27 -54
- package/images/sui/entrypoint.sh +17 -179
- package/package.json +23 -15
- package/dashboard-ui/assets/index-Bmi1UtAg.js +0 -1356
- package/dashboard-ui/assets/index-D5EShVt4.js +0 -3
- package/dashboard-ui/assets/index-Deml9drg.css +0 -1
- package/dist/api/plugin-errors.d.mts +0 -7
- package/dist/api/plugin-errors.mjs +0 -10
- package/dist/api/plugin-errors.mjs.map +0 -1
- package/dist/cli/wirings/build-verb-layers.mjs +0 -42
- package/dist/cli/wirings/build-verb-layers.mjs.map +0 -1
- package/dist/contracts/capability-decl.d.mts +0 -40
- package/dist/orchestrators/built-in-plugin-layers.mjs +0 -54
- package/dist/orchestrators/built-in-plugin-layers.mjs.map +0 -1
- package/dist/orchestrators/run.mjs +0 -91
- package/dist/orchestrators/run.mjs.map +0 -1
- package/dist/orchestrators/runtime-composition.d.mts +0 -10
- package/dist/orchestrators/runtime-composition.mjs +0 -252
- package/dist/orchestrators/runtime-composition.mjs.map +0 -1
- package/dist/orchestrators/snapshot/capture-command.d.mts +0 -1
- package/dist/orchestrators/snapshot/capture-command.mjs +0 -25
- package/dist/orchestrators/snapshot/capture-command.mjs.map +0 -1
- package/dist/orchestrators/snapshot/pending-marker.mjs +0 -99
- package/dist/orchestrators/snapshot/pending-marker.mjs.map +0 -1
- package/dist/orchestrators/snapshot/recover-pending.mjs +0 -223
- package/dist/orchestrators/snapshot/recover-pending.mjs.map +0 -1
- package/dist/orchestrators/snapshot/state-document.d.mts +0 -1
- package/dist/orchestrators/snapshot/state-document.mjs +0 -58
- package/dist/orchestrators/snapshot/state-document.mjs.map +0 -1
- package/dist/plugins/account/spans.mjs +0 -16
- package/dist/plugins/account/spans.mjs.map +0 -1
- package/dist/plugins/account/variants/env.mjs +0 -24
- package/dist/plugins/account/variants/env.mjs.map +0 -1
- package/dist/plugins/account/variants/inline.mjs +0 -13
- package/dist/plugins/account/variants/inline.mjs.map +0 -1
- package/dist/plugins/account/variants/keystore.mjs +0 -91
- package/dist/plugins/account/variants/keystore.mjs.map +0 -1
- package/dist/plugins/action/spans.mjs +0 -11
- package/dist/plugins/action/spans.mjs.map +0 -1
- package/dist/plugins/coin/spans.mjs +0 -20
- package/dist/plugins/coin/spans.mjs.map +0 -1
- package/dist/plugins/deepbook/spans.mjs +0 -18
- package/dist/plugins/deepbook/spans.mjs.map +0 -1
- package/dist/plugins/faucet/spans.mjs +0 -12
- package/dist/plugins/faucet/spans.mjs.map +0 -1
- package/dist/plugins/package/spans.mjs +0 -14
- package/dist/plugins/package/spans.mjs.map +0 -1
- package/dist/plugins/postgres/codegen.mjs +0 -42
- package/dist/plugins/postgres/codegen.mjs.map +0 -1
- package/dist/plugins/postgres/connection.d.mts +0 -33
- package/dist/plugins/postgres/connection.mjs.map +0 -1
- package/dist/plugins/postgres/db-ensure.mjs.map +0 -1
- package/dist/plugins/postgres/errors.d.mts +0 -60
- package/dist/plugins/postgres/errors.mjs +0 -29
- package/dist/plugins/postgres/errors.mjs.map +0 -1
- package/dist/plugins/postgres/index.d.mts +0 -37
- package/dist/plugins/postgres/index.mjs +0 -68
- package/dist/plugins/postgres/index.mjs.map +0 -1
- package/dist/plugins/postgres/routable.mjs +0 -33
- package/dist/plugins/postgres/routable.mjs.map +0 -1
- package/dist/plugins/postgres/service.d.mts +0 -50
- package/dist/plugins/postgres/service.mjs +0 -179
- package/dist/plugins/postgres/service.mjs.map +0 -1
- package/dist/plugins/postgres/snapshot.mjs +0 -32
- package/dist/plugins/postgres/snapshot.mjs.map +0 -1
- package/dist/plugins/postgres/spans.mjs +0 -11
- package/dist/plugins/postgres/spans.mjs.map +0 -1
- package/dist/plugins/seal/spans.mjs +0 -18
- package/dist/plugins/seal/spans.mjs.map +0 -1
- package/dist/plugins/sui/spans.mjs +0 -17
- package/dist/plugins/sui/spans.mjs.map +0 -1
- package/dist/plugins/wallet/spans.mjs +0 -22
- package/dist/plugins/wallet/spans.mjs.map +0 -1
- package/dist/plugins/walrus/registry-publish.d.mts +0 -24
- package/dist/plugins/walrus/spans.mjs +0 -18
- package/dist/plugins/walrus/spans.mjs.map +0 -1
- package/dist/runtime/docker/logs.d.mts +0 -1
- package/dist/runtime/docker/logs.mjs +0 -34
- package/dist/runtime/docker/logs.mjs.map +0 -1
- package/dist/substrate/runtime/artifact-publisher/index.mjs +0 -86
- package/dist/substrate/runtime/artifact-publisher/index.mjs.map +0 -1
- package/dist/substrate/runtime/capability-sinks/index.d.mts +0 -1
- package/dist/substrate/runtime/capability-sinks/index.mjs +0 -3
- package/dist/substrate/runtime/capability-sinks/layer.d.mts +0 -1
- package/dist/substrate/runtime/capability-sinks/layer.mjs +0 -31
- package/dist/substrate/runtime/capability-sinks/layer.mjs.map +0 -1
- package/dist/substrate/runtime/capability-sinks/service.d.mts +0 -95
- package/dist/substrate/runtime/capability-sinks/service.mjs +0 -69
- package/dist/substrate/runtime/capability-sinks/service.mjs.map +0 -1
- package/dist/substrate/runtime/cross-process/lock.d.mts +0 -1
- package/dist/substrate/runtime/cross-process/lock.mjs +0 -23
- package/dist/substrate/runtime/cross-process/lock.mjs.map +0 -1
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +0 -113
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +0 -1
- package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs +0 -31
- package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +0 -1
- package/dist/substrate/runtime/observability/formatter-registry.d.mts +0 -1
- package/dist/substrate/runtime/observability/formatter-registry.mjs +0 -48
- package/dist/substrate/runtime/observability/formatter-registry.mjs.map +0 -1
- package/dist/substrate/runtime/observability/span-store.d.mts +0 -1
- package/dist/substrate/runtime/observability/span-store.mjs +0 -110
- package/dist/substrate/runtime/observability/span-store.mjs.map +0 -1
- package/dist/substrate/runtime/observability/spans.d.mts +0 -1
- package/dist/substrate/runtime/observability/spans.mjs +0 -87
- package/dist/substrate/runtime/observability/spans.mjs.map +0 -1
- package/dist/substrate/runtime/projection/index.mjs +0 -4
- package/dist/substrate/runtime/projection/persisted.mjs +0 -213
- package/dist/substrate/runtime/projection/persisted.mjs.map +0 -1
- package/dist/substrate/runtime/scoped-multimap/service.mjs +0 -52
- package/dist/substrate/runtime/scoped-multimap/service.mjs.map +0 -1
- package/dist/substrate/runtime/scoped-ref-map/index.mjs +0 -2
- package/dist/substrate/runtime/scoped-ref-map/service.mjs +0 -83
- package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +0 -1
- package/dist/substrate/runtime/state-store/index.mjs +0 -3
- package/dist/substrate/runtime/state-store/schema.d.mts +0 -1
- package/dist/substrate/runtime/state-store/schema.mjs +0 -41
- package/dist/substrate/runtime/state-store/schema.mjs.map +0 -1
- package/dist/substrate/runtime/state-store/service.d.mts +0 -1
- package/dist/substrate/runtime/state-store/service.mjs +0 -145
- package/dist/substrate/runtime/state-store/service.mjs.map +0 -1
- package/dist/substrate/runtime/sui-execute/index.mjs.map +0 -1
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +0 -1
- package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +0 -1
- package/dist/substrate/runtime/sui-move-build/index.mjs.map +0 -1
- package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +0 -151
- package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +0 -1
- package/dist/substrate/state-store.d.mts +0 -1
- /package/dist/orchestrators/{snapshot/pending-marker.d.mts → codegen/id-config.d.mts} +0 -0
- /package/dist/orchestrators/snapshot/{recover-pending.d.mts → interrupted-restore.d.mts} +0 -0
- /package/dist/{substrate/runtime/sui-execute → plugins/sui/exec}/sign-and-dispatch.d.mts +0 -0
- /package/dist/plugins/{postgres/snapshot.d.mts → sui/ledger/object-ref.d.mts} +0 -0
- /package/dist/{substrate/runtime/sui-move-build → plugins/sui/move}/index.d.mts +0 -0
- /package/dist/substrate/runtime/{state-store/index.d.mts → reconcile/graph.d.mts} +0 -0
- /package/dist/substrate/runtime/{cross-process/snapshot-reservation.d.mts → supervisor/contribution-dispatcher.d.mts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.mjs","names":[],"sources":["../../../src/orchestrators/codegen/service.ts"],"sourcesContent":["// Codegen orchestrator — main service.\n//\n// Architecture §6 (Codegenable):\n// \"Codegen is a surface (L4) that walks the plugin-emitted\n// `Codegenable` capability decls.\"\n//\n// Hard boundary (distilled-doc §\"Hard boundary\"):\n// \"Apps consume codegen output, NOT devstack.\" Every runtime value\n// the app needs flows through codegen-emitted TS files. The\n// orchestrator owns the only legitimate channel from stack state\n// to app code.\n//\n// Lifecycle (distilled-doc §\"Lifecycle states\"):\n// - at-up: per supervisor cycle, run all emitters serially,\n// with per-file atomic/idempotent writes inside a\n// cycle-level stage-and-swap (rollback on any\n// per-file failure leaves the user-visible tree\n// unchanged).\n// - on-change: re-run as part of the new cycle when the\n// supervisor restarts.\n// - on-demand: same emit pipeline for snapshot resume.\n// - idempotency: per-file no-touch on unchanged content.\n//\n// Pipeline (one cycle):\n// 1. Collect `Codegenable` contributions from the active stack\n// members (the supervisor walks `member.capabilities`).\n// 2. Validate uniqueness: emitter name (literal) is globally\n// unique; output path is globally unique.\n// 3. Run each emitter serially (distilled-doc § \"Serial within a\n// cycle\"). Each emit writes exports through a per-file context.\n// 4. Render each collected file emission to a TS source string.\n// 5. Emit each file with an atomic write + per-file no-touch\n// idempotency.\n// 6. Run the Move-bindings emitter against the collected\n// `package`-emitted contributions.\n// 7. Write the `.gitignore`.\n//\n// What this module does NOT do:\n// - Construct plugin-level resolved blobs (plugins pass them at\n// factory-build time).\n// - Decode the manifest envelope (see `manifest-bridge.ts`).\n// - Watch files. Re-emit is driven by the supervisor cycle (and\n// on-demand by the CLI); the app's own toolchain (Vite/HMR)\n// watches the emitted output tree.\n// - Walk the user's Move-source mtimes (see `bindings.ts`).\n\nimport { Context, Effect, FileSystem, Layer, Order, Ref, Scope } from 'effect';\nimport { dirname } from 'node:path';\n\nimport type {\n\tCodegenableDecl,\n\tCodegenEmitDone,\n\tCodegenEmitContext,\n} from '../../contracts/codegenable.ts';\nimport { acquireStackLock } from '../../substrate/runtime/cross-process/stack-lock.ts';\nimport { mintRandomSuffix } from '../../substrate/runtime/random-suffix.ts';\nimport { stageAndSwap, StageAndSwapError } from '../../substrate/runtime/stage-and-swap/index.ts';\n\nimport {\n\temitBindings,\n\ttype EmitBindingsResult,\n\tMoveCodegenService,\n\tMoveSummaryRunnerService,\n\ttype PackageBindings,\n} from './bindings.ts';\nimport { emitOne } from './emit.ts';\nimport {\n\tCodegenEmitFailed,\n\tCodegenEmitterCollision,\n\tCodegenPathConflict,\n\tCodegenWriteFailed,\n\ttype CodegenError,\n} from './errors.ts';\nimport { renderFile } from './format.ts';\nimport { writeGitignore } from './gitignore.ts';\nimport { CodegenPathsService, type CodegenPaths } from './paths.ts';\nimport { dirModeFor, modeFor, NON_SENSITIVE_DIR_MODE } from './permissions.ts';\n\n// -----------------------------------------------------------------------------\n// Public types\n// -----------------------------------------------------------------------------\n\n/** A type alias the orchestrator uses internally to avoid restating\n * the wide-erased `CodegenableDecl` generic in every signature. */\nexport type Codegenable = CodegenableDecl<string>;\n\nexport interface RunEmitCycleInput {\n\t/** All Codegenable contributions, as collected from the active\n\t * stack members' `capabilities` tuples. The orchestrator does\n\t * not look at plugins — only at the decl set. */\n\treadonly contributions: ReadonlyArray<Codegenable>;\n\t/** Optional: import-extension for bindings emission. Default `.ts`. */\n\treadonly bindingsImportExtension?: '.ts' | '.js' | '';\n}\n\nexport interface RunEmitCycleResult {\n\treadonly filesWritten: ReadonlyArray<string>;\n\treadonly filesUnchanged: ReadonlyArray<string>;\n\treadonly filesChmod: ReadonlyArray<string>;\n\treadonly bindings: EmitBindingsResult | null;\n}\n\nconst buildParentModeResolver = (\n\tpaths: CodegenPaths,\n\tdecls: ReadonlyArray<Codegenable>,\n): Effect.Effect<(absolutePath: string) => number, CodegenPathConflict> =>\n\tEffect.gen(function* () {\n\t\tconst byParent = new Map<string, Array<Pick<Codegenable, 'sensitive'>>>();\n\t\tfor (const decl of decls) {\n\t\t\tconst parent = dirname(yield* paths.resolve(decl.outputPath));\n\t\t\tconst current = byParent.get(parent);\n\t\t\tif (current === undefined) {\n\t\t\t\tbyParent.set(parent, [decl]);\n\t\t\t} else {\n\t\t\t\tcurrent.push(decl);\n\t\t\t}\n\t\t}\n\t\tconst modes = new Map<string, number>();\n\t\tfor (const [parent, parentDecls] of byParent) {\n\t\t\tmodes.set(parent, dirModeFor(parentDecls));\n\t\t}\n\t\treturn (absolutePath: string) => modes.get(dirname(absolutePath)) ?? NON_SENSITIVE_DIR_MODE;\n\t});\n\n// -----------------------------------------------------------------------------\n// Main entry — one cycle of the codegen pipeline\n// -----------------------------------------------------------------------------\n\n/**\n * Per-cycle lock acquire timeout. Codegen cycles can be file-system\n * heavy (multi-emitter, Move-bindings compilation), so we allow more\n * than the substrate's default 5s for `stack.lock` — a custom CLI\n * caller that hits a supervisor mid-cycle should wait, not error.\n * Mirrors `MOVE_BUILD_LOCK_TIMEOUT_MS` (5 minutes).\n */\nconst CODEGEN_CYCLE_LOCK_TIMEOUT_MS = 5 * 60_000;\n\nexport const runEmitCycle = (\n\tinput: RunEmitCycleInput,\n): Effect.Effect<\n\tRunEmitCycleResult,\n\tCodegenError,\n\tFileSystem.FileSystem | CodegenPathsService | MoveSummaryRunnerService | MoveCodegenService\n> =>\n\t// Per-process lock. The supervisor's serialized post-acquire path\n\t// is fine for the normal lifecycle, but custom callers (CLI direct\n\t// invocations, future watcher hooks) can call `runCycle`\n\t// concurrently. With `stageAndSwap` writing under shared\n\t// `<outputDir>.staging.<cycleId>` and `<outputDir>.bak.<cycleId>`\n\t// siblings — and with the pre-seed `fs.copy` reading from the\n\t// shared `outputDir` — two overlapping cycles can stage from a\n\t// half-published tree of the other. The lock serializes them.\n\t//\n\t// Dedicated `codegenLockFile` (NOT the substrate `stack.lock`):\n\t// codegen cycles can run for many seconds when Move bindings\n\t// compile, and the substrate's `stack.lock` is reserved for short\n\t// critical sections (roster mutations, snapshot reservation). A\n\t// dedicated lock isolates codegen contention from those subsystems.\n\tEffect.scoped(\n\t\tEffect.gen(function* () {\n\t\t\tconst paths = yield* CodegenPathsService;\n\t\t\tyield* acquireStackLock(paths.codegenLockFile, CODEGEN_CYCLE_LOCK_TIMEOUT_MS).pipe(\n\t\t\t\tEffect.mapError(\n\t\t\t\t\t(cause) =>\n\t\t\t\t\t\tnew CodegenWriteFailed({\n\t\t\t\t\t\t\toutputPath: paths.codegenLockFile,\n\t\t\t\t\t\t\tstage: 'write',\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn yield* runEmitCycleLocked(input);\n\t\t}),\n\t);\n\nconst runEmitCycleLocked = (\n\tinput: RunEmitCycleInput,\n): Effect.Effect<\n\tRunEmitCycleResult,\n\tCodegenError,\n\tFileSystem.FileSystem | CodegenPathsService | MoveSummaryRunnerService | MoveCodegenService\n> =>\n\tEffect.gen(function* () {\n\t\tconst paths = yield* CodegenPathsService;\n\t\t// Yield the Move-codegen services here (outside the\n\t\t// stage-and-swap build) so the build's R-channel collapses to\n\t\t// just `FileSystem.FileSystem` — the substrate `stageAndSwap`\n\t\t// primitive constrains `build`'s requirements to\n\t\t// `FileSystem.FileSystem`.\n\t\tconst moveRunner = yield* MoveSummaryRunnerService;\n\t\tconst moveCodegen = yield* MoveCodegenService;\n\n\t\t// Pre-flight contribution-set validation. Detected BEFORE the\n\t\t// stage-and-swap so a programming-bug rejection (duplicate\n\t\t// emitterName / outputPath collision) never opens an empty\n\t\t// staging dir on disk. `package` is the one exception:\n\t\t// multiple instances (one per Package) legitimately share the\n\t\t// `emitterName` literal by design.\n\t\tyield* validateUniqueness(input.contributions);\n\t\tyield* validateAggregatePathAvailability(input.contributions);\n\n\t\t// Cycle-level atomicity: substrate stage-and-swap. The build\n\t\t// populates `<outputDir>.staging.<cycleId>/`; on success the\n\t\t// substrate renames it into place; on any failure the previous\n\t\t// tree (if any) is restored byte-for-byte. Without this wrapper\n\t\t// a mid-cycle emit failure would leave `src/generated/`\n\t\t// half-rewritten — see STYLE_GUIDE §19.\n\t\t//\n\t\t// `preserveOnPreseed: true` — substrate clones the current\n\t\t// target into staging before `build` runs so the per-file\n\t\t// no-touch idempotency (and gitignore user-block preservation)\n\t\t// sees the right baseline. Files this cycle rewrites are\n\t\t// overwritten in staging; files this cycle does NOT touch\n\t\t// survive into the next target verbatim with their original\n\t\t// mtimes (HMR watchers stay quiet for unchanged outputs).\n\t\t//\n\t\t// Cycle id is a random suffix. STYLE_GUIDE §17 mandates 8\n\t\t// hex chars for external-facing identifiers; this is a\n\t\t// deliberate 16-char carve-out because the value only\n\t\t// appears in transient staging-directory names\n\t\t// (`.staging.<id>` / `.bak.<id>`) that the substrate rm's\n\t\t// after publish. The extra entropy is defense-in-depth for\n\t\t// the race-window where two concurrent emit cycles under a\n\t\t// custom-CLI caller could mint overlapping staging dirs\n\t\t// against the same shared `outputDir`; a collision there\n\t\t// would corrupt a half-built tree, not just clash an\n\t\t// operator-visible name.\n\t\tconst cycleId = mintRandomSuffix(16);\n\t\tconst stagingPaths = paths.withRoot(`${paths.outputDir}.staging.${cycleId}`);\n\n\t\treturn yield* stageAndSwap({\n\t\t\ttargetPath: paths.outputDir,\n\t\t\tidSuffix: cycleId,\n\t\t\tpreserveOnPreseed: true,\n\t\t\tbuild: Effect.gen(function* () {\n\t\t\t\tconst inner = yield* runEmitCycleInner(input, stagingPaths).pipe(\n\t\t\t\t\tEffect.provideService(MoveSummaryRunnerService, moveRunner),\n\t\t\t\t\tEffect.provideService(MoveCodegenService, moveCodegen),\n\t\t\t\t);\n\t\t\t\t// Rewrite paths so callers see the final user-visible\n\t\t\t\t// `outputDir` location, not the staging directory that\n\t\t\t\t// only exists for the duration of the build.\n\t\t\t\treturn rewriteResultPaths(inner, stagingPaths.outputDir, paths.outputDir);\n\t\t\t}),\n\t\t}).pipe(\n\t\t\tEffect.mapError((e): CodegenError => {\n\t\t\t\tif (e instanceof StageAndSwapError) {\n\t\t\t\t\treturn new CodegenWriteFailed({\n\t\t\t\t\t\toutputPath: paths.outputDir,\n\t\t\t\t\t\tstage: 'rename',\n\t\t\t\t\t\tcause: e,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn e;\n\t\t\t}),\n\t\t\tEffect.withSpan('codegen.runEmitCycle', {\n\t\t\t\tattributes: {\n\t\t\t\t\t'codegen.contributions': input.contributions.length,\n\t\t\t\t\t'codegen.cycleId': cycleId,\n\t\t\t\t\t'codegen.stagingDir': stagingPaths.outputDir,\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t});\n\n/**\n * Project a staging-rooted `RunEmitCycleResult` back into the user-\n * visible `outputDir` namespace. The build effect writes through\n * `stagingDir` paths; callers (and tests) expect to see the final\n * post-rename locations.\n */\nconst rewriteResultPaths = (\n\tresult: RunEmitCycleResult,\n\tstagingDir: string,\n\toutputDir: string,\n): RunEmitCycleResult => {\n\tconst stripped = stagingDir.replace(/\\/+$/, '');\n\tconst target = outputDir.replace(/\\/+$/, '');\n\tconst rewrite = (p: string): string =>\n\t\tp.startsWith(stripped) ? `${target}${p.slice(stripped.length)}` : p;\n\treturn {\n\t\tfilesWritten: result.filesWritten.map(rewrite),\n\t\tfilesUnchanged: result.filesUnchanged.map(rewrite),\n\t\tfilesChmod: result.filesChmod.map(rewrite),\n\t\tbindings:\n\t\t\tresult.bindings === null\n\t\t\t\t? null\n\t\t\t\t: {\n\t\t\t\t\t\t...result.bindings,\n\t\t\t\t\t\tfilesWritten: result.bindings.filesWritten.map(rewrite),\n\t\t\t\t\t},\n\t};\n};\n\n/**\n * The body of one emit cycle. Pulled out of `runEmitCycle` so the\n * stage-and-swap wrapper can drive it against a redirected\n * `CodegenPathsService` (the staging tree). Validation runs BEFORE\n * this function so callers know the contribution set is well-formed.\n */\nconst runEmitCycleInner = (\n\tinput: RunEmitCycleInput,\n\tpaths: CodegenPaths,\n): Effect.Effect<\n\tRunEmitCycleResult,\n\tCodegenError,\n\tFileSystem.FileSystem | MoveSummaryRunnerService | MoveCodegenService\n> =>\n\tEffect.gen(function* () {\n\t\tconst fileEmitters: Array<Codegenable> = [...input.contributions];\n\n\t\tconst filesWritten: Array<string> = [];\n\t\tconst filesUnchanged: Array<string> = [];\n\t\tconst filesChmod: Array<string> = [];\n\t\t// Aggregate buckets keyed by plugin-supplied bucket name. The\n\t\t// orchestrator treats bucket names as opaque tags chosen by\n\t\t// the contributor; it never branches on plugin identity. See\n\t\t// `CodegenableDecl.aggregate` (contracts/codegenable.ts).\n\t\tconst aggregates = new Map<string, Record<string, unknown>>();\n\t\tconst packageContribs: Array<PackageBindings> = [];\n\t\tconst sortedDecls = [...fileEmitters].sort(\n\t\t\tOrder.mapInput(Order.String, (d: Codegenable) => d.outputPath),\n\t\t);\n\t\tconst parentModeFor = yield* buildParentModeResolver(paths, fileEmitters);\n\t\tfor (const decl of sortedDecls) {\n\t\t\tconst emission = yield* runEmitter(decl);\n\t\t\tconst exported = emission.exports;\n\t\t\tif (decl.aggregate !== undefined) {\n\t\t\t\tconst projected = decl.aggregate.project(exported);\n\t\t\t\tif (projected !== null) {\n\t\t\t\t\tconst bucket = aggregates.get(decl.aggregate.bucket) ?? {};\n\t\t\t\t\tObject.assign(bucket, projected);\n\t\t\t\t\taggregates.set(decl.aggregate.bucket, bucket);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Move-bindings collection: any export whose shape matches\n\t\t\t// the orchestrator's `PackageBindings` consumer contract is\n\t\t\t// forwarded to `emitBindings`. Runs against the raw `exported`\n\t\t\t// map (not via `aggregate.project`) so direct `codegenable(...)`\n\t\t\t// contributions — which carry no `aggregate` — are picked up\n\t\t\t// too. The orchestrator validates the shape it consumes; it\n\t\t\t// does NOT name the plugin that produced it.\n\t\t\tfor (const value of Object.values(exported)) {\n\t\t\t\tif (isPackageBindings(value)) {\n\t\t\t\t\tpackageContribs.push(value);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst rendered = renderFile({\n\t\t\t\temitterName: decl.emitterName,\n\t\t\t\toutputPath: decl.outputPath,\n\t\t\t\tsensitive: decl.sensitive === true,\n\t\t\t\texports: exported,\n\t\t\t\timports: emission.imports,\n\t\t\t});\n\t\t\tif (!rendered.ok) {\n\t\t\t\treturn yield* Effect.fail(rendered.error);\n\t\t\t}\n\t\t\tconst abs = yield* paths.resolve(decl.outputPath);\n\t\t\tconst outcome = yield* emitOne({\n\t\t\t\tpath: abs,\n\t\t\t\tcontent: rendered.text,\n\t\t\t\tmode: modeFor(decl),\n\t\t\t\tparentMode: parentModeFor(abs),\n\t\t\t});\n\t\t\tswitch (outcome.outcome) {\n\t\t\t\tcase 'wrote':\n\t\t\t\t\tfilesWritten.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'unchanged':\n\t\t\t\t\tfilesUnchanged.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'chmod-only':\n\t\t\t\t\tfilesChmod.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfor (const aggregate of buildAggregateFiles(aggregates)) {\n\t\t\tconst rendered = renderFile({\n\t\t\t\temitterName: aggregate.emitterName,\n\t\t\t\toutputPath: aggregate.outputPath,\n\t\t\t\tsensitive: false,\n\t\t\t\texports: aggregate.exports,\n\t\t\t});\n\t\t\tif (!rendered.ok) {\n\t\t\t\treturn yield* Effect.fail(rendered.error);\n\t\t\t}\n\t\t\tconst abs = yield* paths.resolve(aggregate.outputPath);\n\t\t\tconst outcome = yield* emitOne({\n\t\t\t\tpath: abs,\n\t\t\t\tcontent: rendered.text,\n\t\t\t\tmode: 0o644,\n\t\t\t\tparentMode: parentModeFor(abs),\n\t\t\t});\n\t\t\tswitch (outcome.outcome) {\n\t\t\t\tcase 'wrote':\n\t\t\t\t\tfilesWritten.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'unchanged':\n\t\t\t\t\tfilesUnchanged.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'chmod-only':\n\t\t\t\t\tfilesChmod.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tlet bindings: EmitBindingsResult | null = null;\n\t\tif (packageContribs.length > 0) {\n\t\t\tbindings = yield* emitBindings({\n\t\t\t\tbindingsDir: paths.bindingsDir,\n\t\t\t\tpackages: packageContribs,\n\t\t\t\timportExtension: input.bindingsImportExtension,\n\t\t\t});\n\t\t} else {\n\t\t\tyield* Effect.logInfo(\n\t\t\t\t'codegen: no package contributions; skipping Move-to-TS bindings step.',\n\t\t\t);\n\t\t}\n\n\t\tconst sensitivePaths = fileEmitters\n\t\t\t.filter((d) => d.sensitive === true)\n\t\t\t.map((d) => d.outputPath);\n\t\tyield* writeGitignore({\n\t\t\tpath: paths.gitignoreFile,\n\t\t\tsensitivePaths,\n\t\t\tparentMode: parentModeFor(paths.gitignoreFile),\n\t\t});\n\n\t\treturn {\n\t\t\tfilesWritten,\n\t\t\tfilesUnchanged,\n\t\t\tfilesChmod,\n\t\t\tbindings,\n\t\t};\n\t});\n\n// -----------------------------------------------------------------------------\n// Internals\n// -----------------------------------------------------------------------------\n\ninterface CodegenEmission {\n\treadonly exports: { readonly [key: string]: unknown };\n\treadonly imports: ReadonlyArray<string>;\n}\n\nconst runEmitter = (decl: Codegenable): Effect.Effect<CodegenEmission, CodegenEmitFailed> =>\n\tEffect.gen(function* () {\n\t\tconst exports: Record<string, unknown> = {};\n\t\tconst imports: Array<string> = [];\n\t\tconst done: CodegenEmitDone = { _tag: 'CodegenEmitDone' };\n\t\tconst ctx: CodegenEmitContext = {\n\t\t\texportConst: (name, value) => {\n\t\t\t\texports[name] = value;\n\t\t\t},\n\t\t\timportStatement: (statement) => {\n\t\t\t\timports.push(statement);\n\t\t\t},\n\t\t\tdone: () => done,\n\t\t};\n\t\tyield* decl.emit(ctx).pipe(\n\t\t\tEffect.mapError(\n\t\t\t\t(cause) =>\n\t\t\t\t\tnew CodegenEmitFailed({\n\t\t\t\t\t\temitterName: decl.emitterName,\n\t\t\t\t\t\toutputPath: decl.outputPath,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\treturn { exports, imports };\n\t});\n\n/**\n * Uniqueness check: emitter name (literal) must be unique unless\n * the decl opts into repetition via `allowEmitterNameRepetition`\n * (used by per-item plugins like Package, which emit one decl per\n * published package under a shared emitter name). Output paths\n * must be unique across ALL emitters.\n */\nconst validateUniqueness = (\n\tdecls: ReadonlyArray<Codegenable>,\n): Effect.Effect<void, CodegenPathConflict | CodegenEmitterCollision> =>\n\tEffect.gen(function* () {\n\t\tconst byPath = new Map<string, Array<string>>();\n\t\tconst byName = new Map<string, Array<string>>();\n\t\tfor (const d of decls) {\n\t\t\tconst ps = byPath.get(d.outputPath) ?? [];\n\t\t\tps.push(d.emitterName);\n\t\t\tbyPath.set(d.outputPath, ps);\n\t\t\tif (d.allowEmitterNameRepetition === true) continue;\n\t\t\tconst ns = byName.get(d.emitterName) ?? [];\n\t\t\tns.push(d.outputPath);\n\t\t\tbyName.set(d.emitterName, ns);\n\t\t}\n\t\tfor (const [path, emitters] of byPath) {\n\t\t\tif (emitters.length > 1) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew CodegenPathConflict({\n\t\t\t\t\t\tkind: 'duplicate',\n\t\t\t\t\t\toutputPath: path,\n\t\t\t\t\t\temitters,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tfor (const [name, outputPaths] of byName) {\n\t\t\tif (outputPaths.length > 1) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew CodegenEmitterCollision({\n\t\t\t\t\t\temitterName: name,\n\t\t\t\t\t\toutputPaths,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n/**\n * Reject contribution sets that would have an aggregate bucket\n * collide with a per-decl `outputPath`. The orchestrator only knows\n * bucket names because plugins declared them via `aggregate.bucket`;\n * it does not enumerate or recognize plugin identities here.\n */\nconst validateAggregatePathAvailability = (\n\tdecls: ReadonlyArray<Codegenable>,\n): Effect.Effect<void, CodegenPathConflict> =>\n\tEffect.gen(function* () {\n\t\tconst aggregatePaths = new Set<string>();\n\t\tfor (const decl of decls) {\n\t\t\tif (decl.aggregate !== undefined) aggregatePaths.add(decl.aggregate.bucket);\n\t\t}\n\t\tfor (const path of aggregatePaths) {\n\t\t\tconst colliding = decls.filter((decl) => decl.outputPath === path);\n\t\t\tif (colliding.length > 0) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew CodegenPathConflict({\n\t\t\t\t\t\tkind: 'duplicate',\n\t\t\t\t\t\toutputPath: path,\n\t\t\t\t\t\temitters: [...colliding.map((decl) => decl.emitterName), `aggregate/${path}`],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\ninterface AggregateFile {\n\treadonly emitterName: string;\n\treadonly outputPath: string;\n\treadonly exports: { readonly [key: string]: unknown };\n}\n\n/**\n * Synthesize one `AggregateFile` per non-empty bucket. The exports\n * map is keyed by the bucket's stem (e.g. `accounts.ts` → `accounts`)\n * so the rendered file exports `export const <stem> = { ... }`. The\n * orchestrator picks the export key from the bucket filename; the\n * stem itself is not a plugin identifier — it is the filename\n * without the `.ts` extension, derived mechanically.\n */\nconst buildAggregateFiles = (\n\tbuckets: ReadonlyMap<string, Record<string, unknown>>,\n): ReadonlyArray<AggregateFile> => {\n\tconst files: Array<AggregateFile> = [];\n\tconst sortedEntries = [...buckets.entries()].sort(([a], [b]) => a.localeCompare(b));\n\tfor (const [bucket, contents] of sortedEntries) {\n\t\tif (Object.keys(contents).length === 0) continue;\n\t\tconst stem = bucketStem(bucket);\n\t\tfiles.push({\n\t\t\temitterName: `aggregate/${stem}`,\n\t\t\toutputPath: bucket,\n\t\t\texports: { [stem]: contents },\n\t\t});\n\t}\n\treturn files;\n};\n\nconst bucketStem = (bucket: string): string => bucket.replace(/\\.ts$/, '').replace(/^.*\\//, '');\n\n/**\n * Consumer-side shape guard: does this aggregated value look like a\n * Move-bindings contribution that `emitBindings` knows how to\n * consume? This is structural validation of the orchestrator's own\n * input contract, NOT a plugin-name match. Any plugin whose\n * `aggregate.project` returns objects with this shape will be\n * forwarded to the Move bindings emitter.\n */\nconst isPackageBindings = (v: unknown): v is PackageBindings =>\n\ttypeof v === 'object' &&\n\tv !== null &&\n\t'name' in v &&\n\t'packageId' in v &&\n\t'mvrPlaceholder' in v &&\n\t'sourcePath' in v;\n\n// -----------------------------------------------------------------------------\n// Service surface — registration API + emit-cycle trigger\n// -----------------------------------------------------------------------------\n\n/**\n * The codegen orchestrator's Context-bound service. The substrate's\n * supervisor calls `registerContribution(pluginKey, decl)` once per\n * `CodegenableDecl` on each plugin's `capabilities` tuple, scope-bound\n * to that plugin's acquire scope. `runCycle()` walks the registered\n * set and runs one full emit pipeline.\n */\nexport interface CodegenOrchestrator {\n\t/** Register a `CodegenableDecl` from a plugin. Scope-bound — when\n\t * the caller's scope (the plugin's acquire scope) closes, the\n\t * registration is reaped. */\n\treadonly registerContribution: (\n\t\tpluginKey: string,\n\t\tdecl: Codegenable,\n\t) => Effect.Effect<void, never, Scope.Scope>;\n\n\t/** Run one emit cycle against the currently-registered set.\n\t * `extraContributions` are merged into the active set (callers\n\t * that have a one-off decl not tied to a plugin scope).\n\t * `bindingsImportExtension` mirrors `runEmitCycle`. */\n\treadonly runCycle: (args?: {\n\t\treadonly extraContributions?: ReadonlyArray<Codegenable>;\n\t\treadonly bindingsImportExtension?: '.ts' | '.js' | '';\n\t}) => Effect.Effect<\n\t\tRunEmitCycleResult,\n\t\tCodegenError,\n\t\tFileSystem.FileSystem | CodegenPathsService | MoveSummaryRunnerService | MoveCodegenService\n\t>;\n}\n\nexport class CodegenOrchestratorService extends Context.Service<\n\tCodegenOrchestratorService,\n\tCodegenOrchestrator\n>()('@devstack/orchestrators/Codegen') {}\n\ninterface RegisteredCodegenEntry {\n\treadonly pluginKey: string;\n\treadonly decl: Codegenable;\n\treadonly seq: number;\n}\n\n/** Layer-wired codegen orchestrator. No upstream requirements at boot\n * time — the per-cycle effect carries the `FileSystem` + Move-codegen\n * service requirements through to the caller. */\nexport const layerCodegenOrchestrator: Layer.Layer<CodegenOrchestratorService> = Layer.effect(\n\tCodegenOrchestratorService,\n\tEffect.gen(function* () {\n\t\tconst contributionsRef = yield* Ref.make<ReadonlyArray<RegisteredCodegenEntry>>([]);\n\t\tconst seqRef = yield* Ref.make(0);\n\n\t\tconst registerContribution: CodegenOrchestrator['registerContribution'] = (pluginKey, decl) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst seq = yield* Ref.updateAndGet(seqRef, (n) => n + 1);\n\t\t\t\tconst entry: RegisteredCodegenEntry = { pluginKey, decl, seq };\n\t\t\t\tyield* Ref.update(contributionsRef, (xs) => [...xs, entry]);\n\t\t\t\tyield* Effect.addFinalizer(() =>\n\t\t\t\t\tRef.update(contributionsRef, (xs) => xs.filter((e) => e.seq !== seq)),\n\t\t\t\t);\n\t\t\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t\t\t'codegen.contribution.plugin': pluginKey,\n\t\t\t\t\t'codegen.contribution.emitter': decl.emitterName,\n\t\t\t\t\t'codegen.contribution.outputPath': decl.outputPath,\n\t\t\t\t});\n\t\t\t}).pipe(Effect.withSpan('orchestrator.codegen.registerContribution')) as Effect.Effect<\n\t\t\t\tvoid,\n\t\t\t\tnever,\n\t\t\t\tScope.Scope\n\t\t\t>;\n\n\t\tconst runCycle: CodegenOrchestrator['runCycle'] = (args) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst registered = (yield* Ref.get(contributionsRef)).map((e) => e.decl);\n\t\t\t\tconst merged: ReadonlyArray<Codegenable> = args?.extraContributions\n\t\t\t\t\t? [...registered, ...args.extraContributions]\n\t\t\t\t\t: registered;\n\t\t\t\treturn yield* runEmitCycle({\n\t\t\t\t\tcontributions: merged,\n\t\t\t\t\tbindingsImportExtension: args?.bindingsImportExtension,\n\t\t\t\t});\n\t\t\t}).pipe(Effect.withSpan('orchestrator.codegen.runCycle'));\n\n\t\treturn CodegenOrchestratorService.of({\n\t\t\tregisterContribution,\n\t\t\trunCycle,\n\t\t});\n\t}),\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.mjs","names":[],"sources":["../../../src/orchestrators/codegen/service.ts"],"sourcesContent":["// Codegen orchestrator — main service.\n//\n// Architecture §6 (Codegenable):\n// \"Codegen is a surface (L4) that walks the plugin-emitted\n// `Codegenable` capability decls.\"\n//\n// Hard boundary (distilled-doc §\"Hard boundary\"):\n// \"Apps consume codegen output, NOT devstack.\" Every runtime value\n// the app needs flows through codegen-emitted TS files. The\n// orchestrator owns the only legitimate channel from stack state\n// to app code.\n//\n// Lifecycle (distilled-doc §\"Lifecycle states\"):\n// - at-up: per supervisor cycle, run all emitters serially,\n// with per-file atomic/idempotent writes inside a\n// cycle-level stage-and-swap (rollback on any\n// per-file failure leaves the user-visible tree\n// unchanged).\n// - on-change: re-run as part of the new cycle when the\n// supervisor restarts.\n// - on-demand: same emit pipeline for snapshot resume.\n// - idempotency: per-file no-touch on unchanged content.\n//\n// Pipeline (one cycle):\n// 1. Collect `Codegenable` contributions from the active stack\n// members (the supervisor walks `member.capabilities`).\n// 2. Validate uniqueness: emitter name (literal) is globally\n// unique; output path is globally unique.\n// 3. Run each emitter serially (distilled-doc § \"Serial within a\n// cycle\"). Each emit writes exports through a per-file context.\n// 4. Render each collected file emission to a TS source string.\n// 5. Emit each file with an atomic write + per-file no-touch\n// idempotency.\n// 6. Run the Move-bindings emitter against the collected\n// `package`-emitted contributions.\n// 7. Write the `.gitignore`.\n//\n// What this module does NOT do:\n// - Construct plugin-level resolved blobs (plugins pass them at\n// factory-build time).\n// - Decode the manifest envelope (plugins pass resolved blobs at\n// factory-build time; the orchestrator never re-reads the envelope).\n// - Watch files. Re-emit is driven by the supervisor cycle (and\n// on-demand by the CLI); the app's own toolchain (Vite/HMR)\n// watches the emitted output tree.\n// - Walk the user's Move-source mtimes (see `bindings.ts`).\n\nimport { Context, Effect, FileSystem, Layer, Order, Ref, Scope } from 'effect';\nimport { dirname } from 'node:path';\n\nimport {\n\tisRawExpr,\n\ttype CodegenableDecl,\n\ttype CodegenEmitDone,\n\ttype CodegenEmitContext,\n\ttype OutputLocation,\n} from '../../contracts/codegenable.ts';\nimport { CONFIG_RUNTIME_OUTPUT_PATH, CONFIG_RUNTIME_SOURCE } from './config-runtime.ts';\nimport { acquireStackLock } from '../../substrate/runtime/cross-process/stack-lock.ts';\nimport { mintRandomSuffix } from '../../substrate/runtime/random-suffix.ts';\nimport { stageAndSwap, StageAndSwapError } from '../../substrate/runtime/stage-and-swap/index.ts';\n\nimport {\n\temitBindings,\n\ttype EmitBindingsResult,\n\tisPackageBindings,\n\tMoveCodegenService,\n\tMoveSummaryRunnerService,\n\ttype PackageBindings,\n} from './bindings.ts';\nimport { emitOne } from './emit.ts';\nimport {\n\tCodegenAggregateConflict,\n\tCodegenEmitFailed,\n\tCodegenEmitterCollision,\n\tCodegenPathConflict,\n\tCodegenWriteFailed,\n\ttype CodegenError,\n} from './errors.ts';\nimport { renderFile } from './format.ts';\nimport { writeGitignore } from './gitignore.ts';\nimport { UNRESOLVED_ID } from './id-config.ts';\nimport type { IdConfig, IdConfigNetwork, IdConfigPackage, IdConfigValues } from './id-config.ts';\nimport { CodegenPathsService, type CodegenPaths } from './paths.ts';\nimport { dirModeFor, modeFor, NON_SENSITIVE_DIR_MODE } from './permissions.ts';\n\n// -----------------------------------------------------------------------------\n// Public types\n// -----------------------------------------------------------------------------\n\n/** A type alias the orchestrator uses internally to avoid restating\n * the wide-erased `CodegenableDecl` generic in every signature. */\nexport type Codegenable = CodegenableDecl<string>;\n\nexport interface RunEmitCycleInput {\n\t/** All Codegenable contributions, as collected from the active\n\t * stack members' `capabilities` tuples. The orchestrator does\n\t * not look at plugins — only at the decl set. */\n\treadonly contributions: ReadonlyArray<Codegenable>;\n\t/** Optional: import-extension for bindings emission. Default `.ts`. */\n\treadonly bindingsImportExtension?: '.ts' | '.js' | '';\n\t/** When `true`, this is the COMMITTED projection tree (`src/generated`,\n\t * written by the stack-free `codegen` verb): the `.gitignore` TRACKS\n\t * the stubs (bindings, config, config-runtime) so `tsc`/`vite build`\n\t * work on a fresh clone, ignoring only `sensitivePaths`. When\n\t * `false`/omitted, the ephemeral tree is blanket-ignored. */\n\treadonly trackTree?: boolean;\n}\n\nexport interface RunEmitCycleResult {\n\treadonly filesWritten: ReadonlyArray<string>;\n\treadonly filesUnchanged: ReadonlyArray<string>;\n\treadonly filesChmod: ReadonlyArray<string>;\n\treadonly bindings: EmitBindingsResult | null;\n}\n\n/** Resolve a decl/aggregate's absolute output path against the tree\n * selected by its `outputLocation`. `'generated-extras'` routes\n * through `paths.resolveExtras` (the gitignored dev tree); everything\n * else through `paths.resolve` (the staging-and-swapped runtime tree). */\nconst resolveAt = (\n\tpaths: CodegenPaths,\n\tlocation: OutputLocation,\n\toutputPath: string,\n): Effect.Effect<string, CodegenPathConflict> =>\n\tlocation === 'generated-extras' ? paths.resolveExtras(outputPath) : paths.resolve(outputPath);\n\nconst declLocation = (decl: Pick<Codegenable, 'outputLocation'>): OutputLocation =>\n\tdecl.outputLocation ?? 'generated';\n\n/** A decl belongs to the `emitExtras` flush IFF it writes ONLY into the\n * dev-only `generated-extras` tree. INVARIANT: `emitExtras` must NEVER\n * touch the committed `src/generated` tree (it runs without stage-and-swap),\n * so a decl that would emit any file into `generated` is excluded.\n *\n * - `aggregateOnly` decls write only their aggregate file: include iff the\n * aggregate's location is `generated-extras` (e.g. each account folding\n * into the gitignored `accounts.ts`).\n * - standalone decls write a per-decl file (and possibly an aggregate):\n * include iff that standalone file lands in `generated-extras` (e.g. the\n * wallet's `dev-wallet.ts`). A standalone-in-`generated` decl is excluded\n * even if its aggregate targets `generated-extras`. */\nconst isExtrasDecl = (decl: Codegenable): boolean =>\n\tdecl.aggregateOnly === true\n\t\t? (decl.aggregate?.outputLocation ?? 'generated') === 'generated-extras'\n\t\t: declLocation(decl) === 'generated-extras';\n\nconst buildParentModeResolver = (\n\tpaths: CodegenPaths,\n\tentries: ReadonlyArray<{\n\t\treadonly outputPath: string;\n\t\treadonly location: OutputLocation;\n\t\treadonly sensitive: boolean;\n\t}>,\n): Effect.Effect<(absolutePath: string) => number, CodegenPathConflict> =>\n\tEffect.gen(function* () {\n\t\tconst byParent = new Map<string, Array<{ readonly sensitive?: boolean }>>();\n\t\tfor (const entry of entries) {\n\t\t\tconst parent = dirname(yield* resolveAt(paths, entry.location, entry.outputPath));\n\t\t\tconst current = byParent.get(parent);\n\t\t\tif (current === undefined) {\n\t\t\t\tbyParent.set(parent, [{ sensitive: entry.sensitive }]);\n\t\t\t} else {\n\t\t\t\tcurrent.push({ sensitive: entry.sensitive });\n\t\t\t}\n\t\t}\n\t\tconst modes = new Map<string, number>();\n\t\tfor (const [parent, parentDecls] of byParent) {\n\t\t\tmodes.set(parent, dirModeFor(parentDecls));\n\t\t}\n\t\treturn (absolutePath: string) => modes.get(dirname(absolutePath)) ?? NON_SENSITIVE_DIR_MODE;\n\t});\n\n// -----------------------------------------------------------------------------\n// Main entry — one cycle of the codegen pipeline\n// -----------------------------------------------------------------------------\n\n/**\n * Per-cycle lock acquire timeout. Codegen cycles can be file-system\n * heavy (multi-emitter, Move-bindings compilation), so we allow more\n * than the substrate's default 5s for `stack.lock` — a custom CLI\n * caller that hits a supervisor mid-cycle should wait, not error.\n * Mirrors `MOVE_BUILD_LOCK_TIMEOUT_MS` (5 minutes).\n */\nconst CODEGEN_CYCLE_LOCK_TIMEOUT_MS = 5 * 60_000;\n\nexport const runEmitCycle = (\n\tinput: RunEmitCycleInput,\n): Effect.Effect<\n\tRunEmitCycleResult,\n\tCodegenError,\n\tFileSystem.FileSystem | CodegenPathsService | MoveSummaryRunnerService | MoveCodegenService\n> =>\n\t// Per-process lock. The supervisor's serialized post-acquire path\n\t// is fine for the normal lifecycle, but custom callers (CLI direct\n\t// invocations, future watcher hooks) can call `runEmitCycle`\n\t// concurrently. With `stageAndSwap` writing under shared\n\t// `<outputDir>.staging.<cycleId>` and `<outputDir>.bak.<cycleId>`\n\t// siblings — and with the pre-seed `fs.copy` reading from the\n\t// shared `outputDir` — two overlapping cycles can stage from a\n\t// half-published tree of the other. The lock serializes them.\n\t//\n\t// Dedicated `codegenLockFile` (NOT the substrate `stack.lock`):\n\t// codegen cycles can run for many seconds when Move bindings\n\t// compile, and the substrate's `stack.lock` is reserved for short\n\t// critical sections (roster mutations, the snapshot bounce). A\n\t// dedicated lock isolates codegen contention from those subsystems.\n\tEffect.scoped(\n\t\tEffect.gen(function* () {\n\t\t\tconst paths = yield* CodegenPathsService;\n\t\t\tyield* acquireStackLock(paths.codegenLockFile, CODEGEN_CYCLE_LOCK_TIMEOUT_MS).pipe(\n\t\t\t\tEffect.mapError(\n\t\t\t\t\t(cause) =>\n\t\t\t\t\t\tnew CodegenWriteFailed({\n\t\t\t\t\t\t\toutputPath: paths.codegenLockFile,\n\t\t\t\t\t\t\tstage: 'write',\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn yield* runEmitCycleLocked(input);\n\t\t}),\n\t);\n\nconst runEmitCycleLocked = (\n\tinput: RunEmitCycleInput,\n): Effect.Effect<\n\tRunEmitCycleResult,\n\tCodegenError,\n\tFileSystem.FileSystem | CodegenPathsService | MoveSummaryRunnerService | MoveCodegenService\n> =>\n\tEffect.gen(function* () {\n\t\tconst paths = yield* CodegenPathsService;\n\t\t// Yield the Move-codegen services here (outside the\n\t\t// stage-and-swap build) so the build's R-channel collapses to\n\t\t// just `FileSystem.FileSystem` — the substrate `stageAndSwap`\n\t\t// primitive constrains `build`'s requirements to\n\t\t// `FileSystem.FileSystem`.\n\t\tconst moveRunner = yield* MoveSummaryRunnerService;\n\t\tconst moveCodegen = yield* MoveCodegenService;\n\n\t\t// Pre-flight contribution-set validation. Detected BEFORE the\n\t\t// stage-and-swap so a programming-bug rejection (duplicate\n\t\t// emitterName / outputPath collision) never opens an empty\n\t\t// staging dir on disk. `package` is the one exception:\n\t\t// multiple instances (one per Package) legitimately share the\n\t\t// `emitterName` literal by design.\n\t\tyield* validateUniqueness(input.contributions);\n\t\tyield* validateAggregatePathAvailability(input.contributions);\n\n\t\t// Cycle-level atomicity: substrate stage-and-swap. The build\n\t\t// populates `<outputDir>.staging.<cycleId>/`; on success the\n\t\t// substrate renames it into place; on any failure the previous\n\t\t// tree (if any) is restored byte-for-byte. Without this wrapper\n\t\t// a mid-cycle emit failure would leave `src/generated/`\n\t\t// half-rewritten — see STYLE_GUIDE §19.\n\t\t//\n\t\t// `preserveOnPreseed: true` — substrate clones the current\n\t\t// target into staging before `build` runs so the per-file\n\t\t// no-touch idempotency (and gitignore user-block preservation)\n\t\t// sees the right baseline. Files this cycle rewrites are\n\t\t// overwritten in staging; files this cycle does NOT touch\n\t\t// survive into the next target verbatim with their original\n\t\t// mtimes (HMR watchers stay quiet for unchanged outputs).\n\t\t//\n\t\t// Cycle id is a random suffix. STYLE_GUIDE §17 mandates 8\n\t\t// hex chars for external-facing identifiers; this is a\n\t\t// deliberate 16-char carve-out because the value only\n\t\t// appears in transient staging-directory names\n\t\t// (`.staging.<id>` / `.bak.<id>`) that the substrate rm's\n\t\t// after publish. The extra entropy is defense-in-depth for\n\t\t// the race-window where two concurrent emit cycles under a\n\t\t// custom-CLI caller could mint overlapping staging dirs\n\t\t// against the same shared `outputDir`; a collision there\n\t\t// would corrupt a half-built tree, not just clash an\n\t\t// operator-visible name.\n\t\tconst cycleId = mintRandomSuffix(16);\n\t\tconst stagingPaths = paths.withRoot(`${paths.outputDir}.staging.${cycleId}`);\n\n\t\treturn yield* stageAndSwap({\n\t\t\ttargetPath: paths.outputDir,\n\t\t\tidSuffix: cycleId,\n\t\t\tpreserveOnPreseed: true,\n\t\t\tbuild: Effect.gen(function* () {\n\t\t\t\tconst inner = yield* runEmitCycleInner(input, stagingPaths).pipe(\n\t\t\t\t\tEffect.provideService(MoveSummaryRunnerService, moveRunner),\n\t\t\t\t\tEffect.provideService(MoveCodegenService, moveCodegen),\n\t\t\t\t);\n\t\t\t\t// Rewrite paths so callers see the final user-visible\n\t\t\t\t// `outputDir` location, not the staging directory that\n\t\t\t\t// only exists for the duration of the build.\n\t\t\t\treturn rewriteResultPaths(inner, stagingPaths.outputDir, paths.outputDir);\n\t\t\t}),\n\t\t}).pipe(\n\t\t\tEffect.mapError((e): CodegenError => {\n\t\t\t\tif (e instanceof StageAndSwapError) {\n\t\t\t\t\treturn new CodegenWriteFailed({\n\t\t\t\t\t\toutputPath: paths.outputDir,\n\t\t\t\t\t\tstage: 'rename',\n\t\t\t\t\t\tcause: e,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn e;\n\t\t\t}),\n\t\t);\n\t});\n\n/**\n * Project a staging-rooted `RunEmitCycleResult` back into the user-\n * visible `outputDir` namespace. The build effect writes through\n * `stagingDir` paths; callers (and tests) expect to see the final\n * post-rename locations.\n */\nconst rewriteResultPaths = (\n\tresult: RunEmitCycleResult,\n\tstagingDir: string,\n\toutputDir: string,\n): RunEmitCycleResult => {\n\tconst stripped = stagingDir.replace(/\\/+$/, '');\n\tconst target = outputDir.replace(/\\/+$/, '');\n\tconst rewrite = (p: string): string =>\n\t\tp.startsWith(stripped) ? `${target}${p.slice(stripped.length)}` : p;\n\treturn {\n\t\tfilesWritten: result.filesWritten.map(rewrite),\n\t\tfilesUnchanged: result.filesUnchanged.map(rewrite),\n\t\tfilesChmod: result.filesChmod.map(rewrite),\n\t\tbindings:\n\t\t\tresult.bindings === null\n\t\t\t\t? null\n\t\t\t\t: {\n\t\t\t\t\t\t...result.bindings,\n\t\t\t\t\t\tfilesWritten: result.bindings.filesWritten.map(rewrite),\n\t\t\t\t\t},\n\t};\n};\n\n/**\n * The body of one emit cycle. Pulled out of `runEmitCycle` so the\n * stage-and-swap wrapper can drive it against a redirected\n * `CodegenPathsService` (the staging tree). Validation runs BEFORE\n * this function so callers know the contribution set is well-formed.\n */\nconst runEmitCycleInner = (\n\tinput: RunEmitCycleInput,\n\tpaths: CodegenPaths,\n): Effect.Effect<\n\tRunEmitCycleResult,\n\tCodegenError,\n\tFileSystem.FileSystem | MoveSummaryRunnerService | MoveCodegenService\n> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.logInfo(`codegen: emitting projection (trackTree=${input.trackTree === true}).`);\n\t\tconst fileEmitters: Array<Codegenable> = [...input.contributions];\n\n\t\tconst filesWritten: Array<string> = [];\n\t\tconst filesUnchanged: Array<string> = [];\n\t\tconst filesChmod: Array<string> = [];\n\t\t// Aggregate buckets keyed by plugin-supplied bucket name. The\n\t\t// orchestrator treats bucket names as opaque tags chosen by\n\t\t// the contributor; it never branches on plugin identity. See\n\t\t// `CodegenableDecl.aggregate` (contracts/codegenable.ts).\n\t\tconst aggregates = new Map<string, Record<string, unknown>>();\n\t\t// Per-bucket location + sensitivity, read from the first decl\n\t\t// the orchestrator sees contributing to the bucket. Drives where\n\t\t// the synthesized aggregate file lands and its file mode. Every\n\t\t// later contributor to the same bucket MUST agree (enforced\n\t\t// below) — a silent disagreement could misroute a sensitive\n\t\t// aggregate into the committed `generated` tree.\n\t\tconst aggregateMeta = new Map<string, AggregateMeta>();\n\t\tconst packageContribs: Array<PackageBindings> = [];\n\t\tconst sortedDecls = [...fileEmitters].sort(\n\t\t\tOrder.mapInput(Order.String, (d: Codegenable) => d.outputPath),\n\t\t);\n\t\t// Parent-mode resolver must see every path that will be written —\n\t\t// standalone decls AND synthesized aggregates — across BOTH trees.\n\t\t// Aggregate-only decls do not write a standalone file, so they are\n\t\t// excluded from the standalone-path set here.\n\t\tconst parentModeFor = yield* buildParentModeResolver(\n\t\t\tpaths,\n\t\t\tfileEmitters\n\t\t\t\t.filter((d) => d.aggregateOnly !== true)\n\t\t\t\t.map((d) => ({\n\t\t\t\t\toutputPath: d.outputPath,\n\t\t\t\t\tlocation: declLocation(d),\n\t\t\t\t\tsensitive: d.sensitive === true,\n\t\t\t\t})),\n\t\t);\n\t\tfor (const decl of sortedDecls) {\n\t\t\tconst emission = yield* runEmitter(decl);\n\t\t\tconst exported = emission.exports;\n\t\t\tif (decl.aggregate !== undefined) {\n\t\t\t\tconst projected = decl.aggregate.project(exported);\n\t\t\t\tif (projected !== null) {\n\t\t\t\t\tconst bucket = aggregates.get(decl.aggregate.bucket) ?? {};\n\t\t\t\t\tdeepMerge(bucket, projected);\n\t\t\t\t\taggregates.set(decl.aggregate.bucket, bucket);\n\t\t\t\t\t// First-contributor-wins for routing/sensitivity, but a\n\t\t\t\t\t// LATER contributor that disagrees is a hard error — the\n\t\t\t\t\t// `AggregateContribution` contract requires all\n\t\t\t\t\t// contributors to a bucket to agree, and a silent\n\t\t\t\t\t// mismatch could misroute a sensitive aggregate into the\n\t\t\t\t\t// committed `generated` tree (secret leak).\n\t\t\t\t\tconst declLoc = decl.aggregate.outputLocation ?? 'generated';\n\t\t\t\t\tconst declSensitive = decl.aggregate.sensitive === true;\n\t\t\t\t\tconst established = aggregateMeta.get(decl.aggregate.bucket);\n\t\t\t\t\tif (established === undefined) {\n\t\t\t\t\t\taggregateMeta.set(decl.aggregate.bucket, {\n\t\t\t\t\t\t\tlocation: declLoc,\n\t\t\t\t\t\t\tsensitive: declSensitive,\n\t\t\t\t\t\t\testablishedBy: decl.emitterName,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (established.location !== declLoc) {\n\t\t\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\t\t\tnew CodegenAggregateConflict({\n\t\t\t\t\t\t\t\t\tbucket: decl.aggregate.bucket,\n\t\t\t\t\t\t\t\t\tfield: 'outputLocation',\n\t\t\t\t\t\t\t\t\testablished: established.location,\n\t\t\t\t\t\t\t\t\tconflicting: declLoc,\n\t\t\t\t\t\t\t\t\temitters: [established.establishedBy, decl.emitterName],\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (established.sensitive !== declSensitive) {\n\t\t\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\t\t\tnew CodegenAggregateConflict({\n\t\t\t\t\t\t\t\t\tbucket: decl.aggregate.bucket,\n\t\t\t\t\t\t\t\t\tfield: 'sensitive',\n\t\t\t\t\t\t\t\t\testablished: String(established.sensitive),\n\t\t\t\t\t\t\t\t\tconflicting: String(declSensitive),\n\t\t\t\t\t\t\t\t\temitters: [established.establishedBy, decl.emitterName],\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Move-bindings collection: any export whose shape matches\n\t\t\t// the orchestrator's `PackageBindings` consumer contract is\n\t\t\t// forwarded to `emitBindings`. Runs against the raw `exported`\n\t\t\t// map (not via `aggregate.project`) so direct `codegenable(...)`\n\t\t\t// contributions — which carry no `aggregate` — are picked up\n\t\t\t// too. The orchestrator validates the shape it consumes; it\n\t\t\t// does NOT name the plugin that produced it.\n\t\t\tfor (const value of Object.values(exported)) {\n\t\t\t\tif (isPackageBindings(value)) {\n\t\t\t\t\tpackageContribs.push(value);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Aggregate-only decls contribute solely to their bucket; the\n\t\t\t// standalone per-decl file is skipped (the combined aggregate\n\t\t\t// is the only app-facing surface).\n\t\t\tif (decl.aggregateOnly === true) continue;\n\t\t\tconst rendered = renderFile({\n\t\t\t\temitterName: decl.emitterName,\n\t\t\t\toutputPath: decl.outputPath,\n\t\t\t\tsensitive: decl.sensitive === true,\n\t\t\t\texports: exported,\n\t\t\t\timports: emission.imports,\n\t\t\t});\n\t\t\tif (!rendered.ok) {\n\t\t\t\treturn yield* Effect.fail(rendered.error);\n\t\t\t}\n\t\t\tconst abs = yield* resolveAt(paths, declLocation(decl), decl.outputPath);\n\t\t\tconst outcome = yield* emitOne({\n\t\t\t\tpath: abs,\n\t\t\t\tcontent: rendered.text,\n\t\t\t\tmode: modeFor(decl),\n\t\t\t\tparentMode: parentModeFor(abs),\n\t\t\t});\n\t\t\tswitch (outcome.outcome) {\n\t\t\t\tcase 'wrote':\n\t\t\t\t\tfilesWritten.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'unchanged':\n\t\t\t\t\tfilesUnchanged.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'chmod-only':\n\t\t\t\t\tfilesChmod.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tconst aggregateFiles = buildAggregateFiles(aggregates, aggregateMeta);\n\t\t// True once any aggregate references a `config-runtime.ts` resolver\n\t\t// (the committed `config.ts`): then we ALSO emit the fixed\n\t\t// `config-runtime.ts` resolver and import the referenced resolvers into\n\t\t// that file.\n\t\tlet needsConfigRuntime = false;\n\t\tfor (const aggregate of aggregateFiles) {\n\t\t\tconst resolvers = resolversUsedBy(aggregate.exports);\n\t\t\tif (resolvers.length > 0) needsConfigRuntime = true;\n\t\t\tconst rendered = renderFile({\n\t\t\t\temitterName: aggregate.emitterName,\n\t\t\t\toutputPath: aggregate.outputPath,\n\t\t\t\tsensitive: aggregate.sensitive,\n\t\t\t\texports: aggregate.exports,\n\t\t\t\t// The committed `config.ts` resolves ids/network at runtime —\n\t\t\t\t// import exactly the resolvers it references (no unused imports;\n\t\t\t\t// oxlint is pinned and flags them). `.js` specifier (ESM/TS-\n\t\t\t\t// resolved) mirrors the bindings' import style.\n\t\t\t\t...(resolvers.length > 0\n\t\t\t\t\t? { imports: [`import { ${resolvers.join(', ')} } from './config-runtime.js';`] }\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t\tif (!rendered.ok) {\n\t\t\t\treturn yield* Effect.fail(rendered.error);\n\t\t\t}\n\t\t\tconst abs = yield* resolveAt(paths, aggregate.location, aggregate.outputPath);\n\t\t\tconst outcome = yield* emitOne({\n\t\t\t\tpath: abs,\n\t\t\t\tcontent: rendered.text,\n\t\t\t\tmode: aggregate.sensitive ? 0o600 : 0o644,\n\t\t\t\tparentMode: parentModeFor(abs),\n\t\t\t});\n\t\t\tswitch (outcome.outcome) {\n\t\t\t\tcase 'wrote':\n\t\t\t\t\tfilesWritten.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'unchanged':\n\t\t\t\t\tfilesUnchanged.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'chmod-only':\n\t\t\t\t\tfilesChmod.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Emit the FIXED `config-runtime.ts` resolver (a constant string, NOT\n\t\t// routed through the literal renderer) when `config.ts` resolves ids\n\t\t// at runtime. It reads the injected `__DEVSTACK_IDS__` global and\n\t\t// THROWS `DevstackConfigMissingError` on an unresolved id.\n\t\tif (needsConfigRuntime) {\n\t\t\tconst abs = yield* paths.resolve(CONFIG_RUNTIME_OUTPUT_PATH);\n\t\t\tconst outcome = yield* emitOne({\n\t\t\t\tpath: abs,\n\t\t\t\tcontent: CONFIG_RUNTIME_SOURCE,\n\t\t\t\tmode: 0o644,\n\t\t\t\tparentMode: parentModeFor(abs),\n\t\t\t});\n\t\t\tswitch (outcome.outcome) {\n\t\t\t\tcase 'wrote':\n\t\t\t\t\tfilesWritten.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'unchanged':\n\t\t\t\t\tfilesUnchanged.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'chmod-only':\n\t\t\t\t\tfilesChmod.push(abs);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tlet bindings: EmitBindingsResult | null = null;\n\t\tif (packageContribs.length > 0) {\n\t\t\tbindings = yield* emitBindings({\n\t\t\t\tbindingsDir: paths.bindingsDir,\n\t\t\t\tpackages: packageContribs,\n\t\t\t\timportExtension: input.bindingsImportExtension,\n\t\t\t});\n\t\t} else {\n\t\t\tyield* Effect.logInfo(\n\t\t\t\t'codegen: no package contributions; skipping Move-to-TS bindings step.',\n\t\t\t);\n\t\t}\n\n\t\t// `.gitignore` covers the runtime `generated/` tree only. Decls\n\t\t// routed to `generated-extras` live outside `outputDir` (that\n\t\t// whole tree is gitignored at the `.devstack/` level), and\n\t\t// aggregate-only decls never write a standalone file — both are\n\t\t// excluded here so the managed `.gitignore` only lists real\n\t\t// sensitive files in `outputDir`.\n\t\t//\n\t\t// Synthesized aggregate files are a SEPARATE source of sensitive\n\t\t// runtime paths: a sensitive bucket routed to `generated` writes a\n\t\t// real secret-bearing file in `outputDir` that the standalone-decl\n\t\t// scan above never sees (its contributors may all be\n\t\t// `aggregateOnly`). Without an explicit ignore line such a file\n\t\t// would rely solely on the blanket `*` rule — and a user `!<file>`\n\t\t// override in the preserved user block would then start tracking\n\t\t// the secret. Include them so each gets an explicit re-ignore line.\n\t\tconst sensitivePaths = [\n\t\t\t...fileEmitters\n\t\t\t\t.filter(\n\t\t\t\t\t(d) =>\n\t\t\t\t\t\td.sensitive === true && d.aggregateOnly !== true && declLocation(d) === 'generated',\n\t\t\t\t)\n\t\t\t\t.map((d) => d.outputPath),\n\t\t\t...aggregateFiles\n\t\t\t\t.filter((a) => a.sensitive && a.location === 'generated')\n\t\t\t\t.map((a) => a.outputPath),\n\t\t];\n\t\t// A committed projection (written by the stack-free `codegen` verb\n\t\t// into `src/generated`) is TRACKED: the stubs are committed so\n\t\t// `tsc`/`vite build` work on a fresh clone. Any ephemeral tree keeps\n\t\t// the blanket ignore.\n\t\tyield* writeGitignore({\n\t\t\tpath: paths.gitignoreFile,\n\t\t\tsensitivePaths,\n\t\t\tparentMode: parentModeFor(paths.gitignoreFile),\n\t\t\ttrackTree: input.trackTree === true,\n\t\t});\n\n\t\treturn {\n\t\t\tfilesWritten,\n\t\t\tfilesUnchanged,\n\t\t\tfilesChmod,\n\t\t\tbindings,\n\t\t};\n\t});\n\n// -----------------------------------------------------------------------------\n// Internals\n// -----------------------------------------------------------------------------\n\ninterface CodegenEmission {\n\treadonly exports: { readonly [key: string]: unknown };\n\treadonly imports: ReadonlyArray<string>;\n}\n\nconst runEmitter = (decl: Codegenable): Effect.Effect<CodegenEmission, CodegenEmitFailed> =>\n\tEffect.gen(function* () {\n\t\t// Annotate the emit span with the contributor's plugin-supplied\n\t\t// `kind` tag (declared on the decl's `aggregate`). The orchestrator\n\t\t// stays name-blind — it never reads the tag's VALUE or branches on it\n\t\t// (per the `AggregateContribution` contract); wiring it here makes the\n\t\t// otherwise-dead field observable so a trace attributes each emit to\n\t\t// the plugin family that produced it. `annotateCurrentSpan` only lands\n\t\t// inside an enclosing `withSpan` — the `codegen.emit` span this body is\n\t\t// wrapped in below.\n\t\tconst kind = decl.aggregate?.kind;\n\t\tif (kind !== undefined) {\n\t\t\tyield* Effect.annotateCurrentSpan('codegen.kind', kind);\n\t\t}\n\t\tconst exports: Record<string, unknown> = {};\n\t\tconst imports: Array<string> = [];\n\t\tconst done: CodegenEmitDone = { _tag: 'CodegenEmitDone' };\n\t\tconst ctx: CodegenEmitContext = {\n\t\t\texportConst: (name, value) => {\n\t\t\t\texports[name] = value;\n\t\t\t},\n\t\t\timportStatement: (statement) => {\n\t\t\t\timports.push(statement);\n\t\t\t},\n\t\t\tdone: () => done,\n\t\t};\n\t\tyield* decl.emit(ctx).pipe(\n\t\t\tEffect.mapError(\n\t\t\t\t(cause) =>\n\t\t\t\t\tnew CodegenEmitFailed({\n\t\t\t\t\t\temitterName: decl.emitterName,\n\t\t\t\t\t\toutputPath: decl.outputPath,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\treturn { exports, imports };\n\t}).pipe(Effect.withSpan('codegen.emit', { attributes: { 'codegen.emitter': decl.emitterName } }));\n\n/**\n * Uniqueness check: emitter name (literal) must be unique unless\n * the decl opts into repetition via `allowEmitterNameRepetition`\n * (used by per-item plugins like Package, which emit one decl per\n * published package under a shared emitter name). Output paths\n * must be unique across ALL emitters.\n */\nconst validateUniqueness = (\n\tdecls: ReadonlyArray<Codegenable>,\n): Effect.Effect<void, CodegenPathConflict | CodegenEmitterCollision> =>\n\tEffect.gen(function* () {\n\t\tconst byPath = new Map<string, Array<string>>();\n\t\tconst byName = new Map<string, Array<string>>();\n\t\tfor (const d of decls) {\n\t\t\t// Aggregate-only decls write no standalone file, so their\n\t\t\t// `outputPath` is a dead value — exclude them from the\n\t\t\t// path-uniqueness check (many `package` decls legitimately\n\t\t\t// share `config.ts`'s bucket but carry distinct dead\n\t\t\t// `package/<name>.ts` outputPaths that never hit disk).\n\t\t\tif (d.aggregateOnly !== true) {\n\t\t\t\t// Key by (location, path): the same relative path in the\n\t\t\t\t// `generated` vs `generated-extras` trees is two distinct\n\t\t\t\t// files, so `accounts.ts` may exist in both without a\n\t\t\t\t// false collision.\n\t\t\t\tconst pathKey = `${declLocation(d)} ${d.outputPath}`;\n\t\t\t\tconst ps = byPath.get(pathKey) ?? [];\n\t\t\t\tps.push(d.emitterName);\n\t\t\t\tbyPath.set(pathKey, ps);\n\t\t\t}\n\t\t\tif (d.allowEmitterNameRepetition === true) continue;\n\t\t\tconst ns = byName.get(d.emitterName) ?? [];\n\t\t\tns.push(d.outputPath);\n\t\t\tbyName.set(d.emitterName, ns);\n\t\t}\n\t\tfor (const [pathKey, emitters] of byPath) {\n\t\t\tif (emitters.length > 1) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew CodegenPathConflict({\n\t\t\t\t\t\tkind: 'duplicate',\n\t\t\t\t\t\t// Strip the `<location> ` prefix from the dedup key so\n\t\t\t\t\t\t// the error names the relative path the plugin declared.\n\t\t\t\t\t\toutputPath: pathKey.slice(pathKey.indexOf(' ') + 1),\n\t\t\t\t\t\temitters,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tfor (const [name, outputPaths] of byName) {\n\t\t\tif (outputPaths.length > 1) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew CodegenEmitterCollision({\n\t\t\t\t\t\temitterName: name,\n\t\t\t\t\t\toutputPaths,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n/**\n * Reject contribution sets that would have an aggregate bucket\n * collide with a per-decl `outputPath`. The orchestrator only knows\n * bucket names because plugins declared them via `aggregate.bucket`;\n * it does not enumerate or recognize plugin identities here.\n */\nconst validateAggregatePathAvailability = (\n\tdecls: ReadonlyArray<Codegenable>,\n): Effect.Effect<void, CodegenPathConflict> =>\n\tEffect.gen(function* () {\n\t\t// Aggregate buckets keyed by (location, bucket). A standalone\n\t\t// decl in the SAME tree that writes the bucket path would clash\n\t\t// with the synthesized aggregate; a decl in the OTHER tree (or\n\t\t// an aggregate-only decl, which writes no standalone file) does\n\t\t// not.\n\t\tconst aggregatePaths = new Map<string, string>();\n\t\tfor (const decl of decls) {\n\t\t\tif (decl.aggregate !== undefined) {\n\t\t\t\tconst location = decl.aggregate.outputLocation ?? 'generated';\n\t\t\t\taggregatePaths.set(`${location} ${decl.aggregate.bucket}`, decl.aggregate.bucket);\n\t\t\t}\n\t\t}\n\t\tfor (const [key, bucket] of aggregatePaths) {\n\t\t\tconst location = key.slice(0, key.indexOf(' '));\n\t\t\tconst colliding = decls.filter(\n\t\t\t\t(decl) =>\n\t\t\t\t\tdecl.aggregateOnly !== true &&\n\t\t\t\t\tdeclLocation(decl) === location &&\n\t\t\t\t\tdecl.outputPath === bucket,\n\t\t\t);\n\t\t\tif (colliding.length > 0) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew CodegenPathConflict({\n\t\t\t\t\t\tkind: 'duplicate',\n\t\t\t\t\t\toutputPath: bucket,\n\t\t\t\t\t\temitters: [...colliding.map((decl) => decl.emitterName), `aggregate/${bucket}`],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n/** Per-bucket location + sensitivity, captured from the first decl\n * the orchestrator sees contributing to a bucket. The orchestrator\n * stays name-blind; the plugin owns these on its\n * `AggregateContribution`. `establishedBy` records the emitter name\n * of that first contributor so a later disagreement can quote both\n * sides in `CodegenAggregateConflict`. */\ninterface AggregateMeta {\n\treadonly location: OutputLocation;\n\treadonly sensitive: boolean;\n\treadonly establishedBy: string;\n}\n\ninterface AggregateFile {\n\treadonly emitterName: string;\n\treadonly outputPath: string;\n\treadonly exports: { readonly [key: string]: unknown };\n\treadonly location: OutputLocation;\n\treadonly sensitive: boolean;\n}\n\n/**\n * Recursively merge `source` into `target`. Distinct buckets are\n * shallow records keyed by name, but a single bucket (e.g. `config.ts`)\n * accumulates contributions from MANY plugins into nested sub-records\n * (`networks.localnet` from sui, `packages.<name>` / `objects.<name>`\n * from each package). A shallow `Object.assign` would have the last\n * package's `{packages:{...}}` clobber the prior ones. Deep-merge so\n * sibling keys at every level coexist.\n *\n * Arrays and non-plain values overwrite (no element-wise merge — a\n * plugin that re-emits a bucket key owns its full value). Only plain\n * objects recurse.\n */\nconst isPlainObject = (v: unknown): v is Record<string, unknown> =>\n\ttypeof v === 'object' && v !== null && !Array.isArray(v);\n\nconst deepMerge = (\n\ttarget: Record<string, unknown>,\n\tsource: Readonly<Record<string, unknown>>,\n): void => {\n\tfor (const [key, value] of Object.entries(source)) {\n\t\tconst existing = target[key];\n\t\tif (isPlainObject(existing) && isPlainObject(value)) {\n\t\t\tdeepMerge(existing, value);\n\t\t} else {\n\t\t\t// `value` may be a decl-owned cached projection object (or array).\n\t\t\t// Storing it by reference would let a sibling decl that later\n\t\t\t// recurses into the same nested key mutate this decl's cache in\n\t\t\t// place, leaking state across emit cycles. Deep-clone any plain\n\t\t\t// object / array so the shared bucket never aliases decl state.\n\t\t\ttarget[key] = isPlainObject(value) || Array.isArray(value) ? structuredClone(value) : value;\n\t\t}\n\t}\n};\n\n/**\n * Synthesize one `AggregateFile` per non-empty bucket. The exports\n * map is keyed by the bucket's stem (e.g. `accounts.ts` → `accounts`)\n * so the rendered file exports `export const <stem> = { ... }`. The\n * orchestrator picks the export key from the bucket filename; the\n * stem itself is not a plugin identifier — it is the filename\n * without the `.ts` extension, derived mechanically. The bucket's\n * `location`/`sensitive` (from the first contributing decl) drive\n * which tree the file lands in and its mode.\n */\nconst buildAggregateFiles = (\n\tbuckets: ReadonlyMap<string, Record<string, unknown>>,\n\tmeta: ReadonlyMap<string, AggregateMeta>,\n): ReadonlyArray<AggregateFile> => {\n\tconst files: Array<AggregateFile> = [];\n\tconst sortedEntries = [...buckets.entries()].sort(([a], [b]) => a.localeCompare(b));\n\tfor (const [bucket, contents] of sortedEntries) {\n\t\tif (Object.keys(contents).length === 0) continue;\n\t\tconst stem = bucketStem(bucket);\n\t\tconst bucketMeta = meta.get(bucket) ?? {\n\t\t\tlocation: 'generated' as const,\n\t\t\tsensitive: false,\n\t\t\testablishedBy: `aggregate/${stem}`,\n\t\t};\n\t\tfiles.push({\n\t\t\temitterName: `aggregate/${stem}`,\n\t\t\toutputPath: bucket,\n\t\t\texports: { [stem]: contents },\n\t\t\tlocation: bucketMeta.location,\n\t\t\tsensitive: bucketMeta.sensitive,\n\t\t});\n\t}\n\treturn files;\n};\n\nconst bucketStem = (bucket: string): string => bucket.replace(/\\.ts$/, '').replace(/^.*\\//, '');\n\n/** The config-runtime resolver names a committed `config.ts` aggregate may\n * reference as raw expressions. Each is imported from `./config-runtime.js`\n * only when the aggregate actually calls it (oxlint flags unused imports). */\nconst CONFIG_RUNTIME_RESOLVERS = [\n\t'resolveId',\n\t'resolveNetwork',\n\t'resolveNetworks',\n\t'resolveValue',\n] as const;\ntype ConfigRuntimeResolver = (typeof CONFIG_RUNTIME_RESOLVERS)[number];\n\n/** Recursively collect which `config-runtime.ts` resolvers an exports map\n * references via raw expressions — i.e. the committed `config.ts` needs each\n * imported + the fixed `config-runtime.ts` emitted alongside it. */\nconst collectResolversInValue = (value: unknown, found: Set<ConfigRuntimeResolver>): void => {\n\tif (isRawExpr(value)) {\n\t\tfor (const name of CONFIG_RUNTIME_RESOLVERS) {\n\t\t\tif (value.expr.includes(`${name}(`)) found.add(name);\n\t\t}\n\t\treturn;\n\t}\n\tif (Array.isArray(value)) {\n\t\tfor (const v of value) collectResolversInValue(v, found);\n\t\treturn;\n\t}\n\tif (isPlainObject(value)) {\n\t\tfor (const v of Object.values(value)) collectResolversInValue(v, found);\n\t}\n};\n\n/** The ordered set of config-runtime resolvers an aggregate's exports use\n * (empty when none — the aggregate needs no resolver import). */\nconst resolversUsedBy = (exports: {\n\treadonly [key: string]: unknown;\n}): ReadonlyArray<ConfigRuntimeResolver> => {\n\tconst found = new Set<ConfigRuntimeResolver>();\n\tfor (const v of Object.values(exports)) collectResolversInValue(v, found);\n\treturn CONFIG_RUNTIME_RESOLVERS.filter((name) => found.has(name));\n};\n\n// -----------------------------------------------------------------------------\n// Service surface — registration API + emit-cycle trigger\n// -----------------------------------------------------------------------------\n\n/**\n * The codegen orchestrator's Context-bound service. The substrate's\n * supervisor calls `registerContribution(pluginKey, decl)` once per\n * `CodegenableDecl` on each plugin's `capabilities` tuple, scope-bound\n * to that plugin's acquire scope.\n *\n * Boot no longer runs codegen — the emit pipeline is the stack-free\n * `devstack codegen` verb (which calls `runEmitCycle` directly off the\n * config-derived `staticCodegen` decls). This service keeps the dispatcher\n * seam closed: every `codegenable` contribution still has a handler, so\n * plugins emit decls uniformly even though boot writes the id-config (not\n * the committed tree).\n */\nexport interface CodegenOrchestrator {\n\t/** Register a `CodegenableDecl` from a plugin. Scope-bound — when\n\t * the caller's scope (the plugin's acquire scope) closes, the\n\t * registration is reaped. */\n\treadonly registerContribution: (\n\t\tpluginKey: string,\n\t\tdecl: Codegenable,\n\t) => Effect.Effect<void, never, Scope.Scope>;\n\n\t/** Assemble the id-config from the currently-registered (live-resolved)\n\t * contributions. Boot calls this in its post-acquire hook to WRITE the\n\t * id-config file (the same `networks` / `packages` / `mvrOverrides`\n\t * data that fed `config.ts`, but as loadable JSON the Vite plugin\n\t * injects). `network` is the active network name (`ctx.identity.network`).\n\t * Pure projection over the registered decls — no I/O, no chain. */\n\treadonly assembleIdConfig: (network: string) => Effect.Effect<IdConfig, CodegenEmitFailed>;\n\n\t/** Flush ONLY the `generated-extras` contributions (the dev wallet's\n\t * `dev-wallet.ts` + the account plugin's `accounts.ts`) to the\n\t * gitignored `.devstack/stacks/<stack>/generated-extras` tree. These\n\t * are acquire-resolved (can't be statically derived by the `codegen`\n\t * verb), so boot writes them — but ONLY when the resolved network's\n\t * `devWallet` flag is on. The committed `src/generated` tree is NEVER\n\t * touched (no `generated`-located decl is emitted). Reuses the emit\n\t * renderer + aggregate logic; skips the stage-and-swap of the runtime\n\t * tree (extras live outside it). No-op (empty result) when nothing is\n\t * routed to `generated-extras`. */\n\treadonly emitExtras: () => Effect.Effect<\n\t\tRunEmitCycleResult,\n\t\tCodegenError,\n\t\tFileSystem.FileSystem | CodegenPathsService | MoveSummaryRunnerService | MoveCodegenService\n\t>;\n}\n\nexport class CodegenOrchestratorService extends Context.Service<\n\tCodegenOrchestratorService,\n\tCodegenOrchestrator\n>()('@devstack/orchestrators/Codegen') {}\n\n/**\n * Slice the deep-merged `config.ts` aggregate bucket into the loadable\n * `IdConfig` interchange shape. The bucket is the live codegen\n * accumulation (sui `networks`, per-package `packages`/`objects`/\n * `mvrOverrides`, account `accounts`); this picks the id-bearing fields\n * the Vite plugin injects. Reads are defensive — any missing slice\n * collapses to an empty record so a partial stack still writes a valid\n * (if sparse) id-config.\n */\nconst idConfigFromBucket = (\n\tbucket: Record<string, unknown>,\n\tnetwork: string,\n\tvalues: IdConfigValues,\n): IdConfig => {\n\tconst asRecord = (v: unknown): Record<string, unknown> => (isPlainObject(v) ? v : {});\n\tconst asString = (v: unknown): string | undefined => (typeof v === 'string' ? v : undefined);\n\n\tconst networks: Record<string, IdConfigNetwork> = {};\n\tfor (const [name, raw] of Object.entries(asRecord(bucket['networks']))) {\n\t\tconst entry = asRecord(raw);\n\t\tconst rpc = asString(entry['rpc']);\n\t\tif (rpc === undefined) continue;\n\t\tnetworks[name] = {\n\t\t\trpc,\n\t\t\t...(asString(entry['chainId']) !== undefined ? { chainId: asString(entry['chainId']) } : {}),\n\t\t\t...(entry['faucet'] !== undefined ? { faucet: asString(entry['faucet']) ?? null } : {}),\n\t\t\t...(entry['graphql'] !== undefined ? { graphql: asString(entry['graphql']) ?? null } : {}),\n\t\t};\n\t}\n\n\tconst packages: Record<string, IdConfigPackage> = {};\n\tfor (const [name, raw] of Object.entries(asRecord(bucket['packages']))) {\n\t\tconst entry = asRecord(raw);\n\t\t// The active-network id is `packageId` (convenience field the package\n\t\t// projection sets = `byNetwork[activeNetwork]`). An empty/missing\n\t\t// packageId maps to the UNRESOLVED_ID sentinel — an empty string would\n\t\t// slip past `isUnresolvedId` and ship as a real, resolved id.\n\t\tconst rawId = asString(entry['packageId']);\n\t\tconst id = rawId === undefined || rawId === '' ? UNRESOLVED_ID : rawId;\n\t\tconst objectsRaw = asRecord(entry['objects']);\n\t\tconst objects: Record<string, string> = {};\n\t\tfor (const [k, v] of Object.entries(objectsRaw)) {\n\t\t\tconst s = asString(v);\n\t\t\tif (s !== undefined) objects[k] = s;\n\t\t}\n\t\tpackages[name] = {\n\t\t\tid,\n\t\t\t...(Object.keys(objects).length > 0 ? { objects } : {}),\n\t\t};\n\t}\n\n\tconst accounts: Record<string, string> = {};\n\tfor (const [name, v] of Object.entries(asRecord(bucket['accounts']))) {\n\t\t// Account bindings are an object keyed by name; the injectable id is\n\t\t// the `address`. Tolerate a bare string too (a pinned known-config).\n\t\tconst address = asString(v) ?? asString(asRecord(v)['address']);\n\t\tif (address !== undefined) accounts[name] = address;\n\t}\n\n\tconst mvrOverrides: Record<string, string> = {};\n\tfor (const [mvr, v] of Object.entries(asRecord(bucket['mvrOverrides']))) {\n\t\tconst s = asString(v);\n\t\tif (s !== undefined) mvrOverrides[mvr] = s;\n\t}\n\n\t// The active `network` field MUST be a key present in `networks` — the\n\t// committed `config-runtime.ts` `resolveActiveNetwork()` does\n\t// `resolveNetworks()[network]`, and the Vite dev-wallet injection reads\n\t// `networks[network].rpc`. The `network` PARAM is the identity's network\n\t// name (e.g. `\"testnet-fork\"`), but the sui binding keys the `networks`\n\t// map by what it emitted (`\"localnet\"` for every mode). So PREFER the\n\t// network the binding stamped into the bucket (`bucket['network']`, which\n\t// matches the `networks` key); fall back to the param, then — if neither\n\t// is a known key but exactly one network exists — that sole key. This\n\t// keeps `network` in agreement with `networks` so resolution never\n\t// dereferences `undefined`.\n\tconst networkKeys = Object.keys(networks);\n\tconst bucketNetwork = asString(bucket['network']);\n\tconst activeNetwork =\n\t\tbucketNetwork !== undefined && networkKeys.includes(bucketNetwork)\n\t\t\t? bucketNetwork\n\t\t\t: networkKeys.includes(network)\n\t\t\t\t? network\n\t\t\t\t: networkKeys.length === 1\n\t\t\t\t\t? networkKeys[0]!\n\t\t\t\t\t: (bucketNetwork ?? network);\n\n\treturn {\n\t\tnetwork: activeNetwork,\n\t\tnetworks,\n\t\tpackages,\n\t\taccounts,\n\t\tmvrOverrides,\n\t\t...(Object.keys(values).length > 0 ? { values } : {}),\n\t};\n};\n\ninterface RegisteredCodegenEntry {\n\treadonly pluginKey: string;\n\treadonly decl: Codegenable;\n\treadonly seq: number;\n}\n\n/** Layer-wired codegen orchestrator. No upstream requirements at boot\n * time — the per-cycle effect carries the `FileSystem` + Move-codegen\n * service requirements through to the caller. */\nexport const layerCodegenOrchestrator: Layer.Layer<CodegenOrchestratorService> = Layer.effect(\n\tCodegenOrchestratorService,\n\tEffect.gen(function* () {\n\t\tconst contributionsRef = yield* Ref.make<ReadonlyArray<RegisteredCodegenEntry>>([]);\n\t\tconst seqRef = yield* Ref.make(0);\n\n\t\tconst registerContribution: CodegenOrchestrator['registerContribution'] = (pluginKey, decl) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst seq = yield* Ref.updateAndGet(seqRef, (n) => n + 1);\n\t\t\t\tconst entry: RegisteredCodegenEntry = { pluginKey, decl, seq };\n\t\t\t\tyield* Ref.update(contributionsRef, (xs) => [...xs, entry]);\n\t\t\t\tyield* Effect.addFinalizer(() =>\n\t\t\t\t\tRef.update(contributionsRef, (xs) => xs.filter((e) => e.seq !== seq)),\n\t\t\t\t);\n\t\t\t}) as Effect.Effect<void, never, Scope.Scope>;\n\n\t\tconst assembleIdConfig: CodegenOrchestrator['assembleIdConfig'] = (network) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst registered = (yield* Ref.get(contributionsRef)).map((e) => e.decl);\n\t\t\t\t// Deep-merge every contribution's `config.ts` aggregate\n\t\t\t\t// projection into ONE bucket — the SAME accumulation the live\n\t\t\t\t// codegen cycle performed (sui's `networks.<net>` + each\n\t\t\t\t// package's `packages.<name>` / `objects` / `mvrOverrides`,\n\t\t\t\t// the account plugin's `accounts`). The merged bucket carries\n\t\t\t\t// real (live-`acquire`-resolved) ids; we then slice it into the\n\t\t\t\t// loadable `IdConfig` shape.\n\t\t\t\tconst bucket: Record<string, unknown> = {};\n\t\t\t\t// `accounts.ts` is a SEPARATE aggregate bucket (the account\n\t\t\t\t// plugin routes it to `generated-extras`); fold its projection\n\t\t\t\t// under an `accounts` key so the id-config carries account\n\t\t\t\t// addresses alongside the `config.ts`-derived ids.\n\t\t\t\tconst accounts: Record<string, unknown> = {};\n\t\t\t\t// The generic resolver channel — `values[namespace][key]` —\n\t\t\t\t// accumulated from any LIVE config-binding aggregate that\n\t\t\t\t// declared `idConfigValues` (the plugin live JSON the typed\n\t\t\t\t// fields can't carry). Deep-merged so sibling namespaces /\n\t\t\t\t// keys from distinct plugins coexist.\n\t\t\t\tconst values: Record<string, unknown> = {};\n\t\t\t\tfor (const decl of registered) {\n\t\t\t\t\tif (decl.aggregate === undefined) continue;\n\t\t\t\t\t// The generic `values` channel is BUCKET-BLIND: any LIVE\n\t\t\t\t\t// config-binding aggregate (config.ts, coins.ts, deepbook.ts,\n\t\t\t\t\t// walrus.ts, seal.ts, ...) may declare `idConfigValues`. Fold\n\t\t\t\t\t// every contributor's so the committed-tree `resolveValue` calls\n\t\t\t\t\t// those buckets emit resolve at app build/dev time -- not just the\n\t\t\t\t\t// `config.ts` plugins'. Slicing the TYPED id-config fields\n\t\t\t\t\t// (`networks` / `packages` / `mvrOverrides`) stays scoped to the\n\t\t\t\t\t// `config.ts` bucket below.\n\t\t\t\t\tif (decl.aggregate.idConfigValues !== undefined) {\n\t\t\t\t\t\tdeepMerge(values, decl.aggregate.idConfigValues);\n\t\t\t\t\t}\n\t\t\t\t\tif (decl.aggregate.bucket === 'config.ts') {\n\t\t\t\t\t\tconst emission = yield* runEmitter(decl);\n\t\t\t\t\t\tconst projected = decl.aggregate.project(emission.exports);\n\t\t\t\t\t\tif (projected !== null) deepMerge(bucket, projected);\n\t\t\t\t\t} else if (decl.aggregate.bucket === 'accounts.ts') {\n\t\t\t\t\t\tconst emission = yield* runEmitter(decl);\n\t\t\t\t\t\tconst projected = decl.aggregate.project(emission.exports);\n\t\t\t\t\t\tif (projected !== null) deepMerge(accounts, projected);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbucket['accounts'] = accounts;\n\t\t\t\treturn idConfigFromBucket(bucket, network, values as IdConfigValues);\n\t\t\t});\n\n\t\tconst emitExtras: CodegenOrchestrator['emitExtras'] = () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst registered = (yield* Ref.get(contributionsRef)).map((e) => e.decl);\n\t\t\t\tconst extras = registered.filter(isExtrasDecl);\n\t\t\t\tif (extras.length === 0) {\n\t\t\t\t\t// Nothing routed to the dev tree (no wallet/accounts mounted).\n\t\t\t\t\treturn { filesWritten: [], filesUnchanged: [], filesChmod: [], bindings: null };\n\t\t\t\t}\n\t\t\t\t// Validate the extras-only set up front (mirrors `runEmitCycle`'s\n\t\t\t\t// pre-flight), then emit DIRECTLY against the real paths — no\n\t\t\t\t// stage-and-swap, since the extras tree lives outside the runtime\n\t\t\t\t// `outputDir`. Every `generated-extras` decl routes through\n\t\t\t\t// `paths.resolveExtras`; no `generated`-located decl is present,\n\t\t\t\t// so the committed `src/generated` tree is untouched.\n\t\t\t\tyield* validateUniqueness(extras);\n\t\t\t\tyield* validateAggregatePathAvailability(extras);\n\t\t\t\tconst paths = yield* CodegenPathsService;\n\t\t\t\t// Acquire the dedicated `codegenLockFile` for symmetry with\n\t\t\t\t// `runEmitCycle` — the extras emit writes into the shared stack\n\t\t\t\t// tree (`generated-extras`) and a concurrent codegen cycle could\n\t\t\t\t// otherwise interleave. Scoped so the lock releases when the emit\n\t\t\t\t// completes (or fails).\n\t\t\t\treturn yield* Effect.scoped(\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tyield* acquireStackLock(paths.codegenLockFile, CODEGEN_CYCLE_LOCK_TIMEOUT_MS).pipe(\n\t\t\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t\t\t(cause) =>\n\t\t\t\t\t\t\t\t\tnew CodegenWriteFailed({\n\t\t\t\t\t\t\t\t\t\toutputPath: paths.codegenLockFile,\n\t\t\t\t\t\t\t\t\t\tstage: 'write',\n\t\t\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn yield* runEmitCycleInner({ contributions: extras, trackTree: false }, paths);\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t});\n\n\t\treturn CodegenOrchestratorService.of({\n\t\t\tregisterContribution,\n\t\t\tassembleIdConfig,\n\t\t\temitExtras,\n\t\t});\n\t}),\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwHA,MAAM,aACL,OACA,UACA,eAEA,aAAa,qBAAqB,MAAM,cAAc,UAAU,IAAI,MAAM,QAAQ,UAAU;AAE7F,MAAM,gBAAgB,SACrB,KAAK,kBAAkB;;;;;;;;;;;;;AAcxB,MAAM,gBAAgB,SACrB,KAAK,kBAAkB,QACnB,KAAK,WAAW,kBAAkB,iBAAiB,qBACpD,aAAa,IAAI,MAAM;AAE3B,MAAM,2BACL,OACA,YAMA,OAAO,IAAI,aAAa;CACvB,MAAM,2BAAW,IAAI,IAAqD;CAC1E,KAAK,MAAM,SAAS,SAAS;EAC5B,MAAM,SAAS,QAAQ,OAAO,UAAU,OAAO,MAAM,UAAU,MAAM,UAAU,CAAC;EAChF,MAAM,UAAU,SAAS,IAAI,MAAM;EACnC,IAAI,YAAY,KAAA,GACf,SAAS,IAAI,QAAQ,CAAC,EAAE,WAAW,MAAM,UAAU,CAAC,CAAC;OAErD,QAAQ,KAAK,EAAE,WAAW,MAAM,UAAU,CAAC;CAE7C;CACA,MAAM,wBAAQ,IAAI,IAAoB;CACtC,KAAK,MAAM,CAAC,QAAQ,gBAAgB,UACnC,MAAM,IAAI,QAAQ,WAAW,WAAW,CAAC;CAE1C,QAAQ,iBAAyB,MAAM,IAAI,QAAQ,YAAY,CAAC,KAAA;AACjE,CAAC;;;;;;;;AAaF,MAAM,gCAAgC,IAAI;AAE1C,MAAa,gBACZ,UAoBA,OAAO,OACN,OAAO,IAAI,aAAa;CACvB,MAAM,QAAQ,OAAO;CACrB,OAAO,iBAAiB,MAAM,iBAAiB,6BAA6B,CAAC,CAAC,KAC7E,OAAO,UACL,UACA,IAAI,mBAAmB;EACtB,YAAY,MAAM;EAClB,OAAO;EACP;CACD,CAAC,CACH,CACD;CACA,OAAO,OAAO,mBAAmB,KAAK;AACvC,CAAC,CACF;AAED,MAAM,sBACL,UAMA,OAAO,IAAI,aAAa;CACvB,MAAM,QAAQ,OAAO;CAMrB,MAAM,aAAa,OAAO;CAC1B,MAAM,cAAc,OAAO;CAQ3B,OAAO,mBAAmB,MAAM,aAAa;CAC7C,OAAO,kCAAkC,MAAM,aAAa;CA4B5D,MAAM,UAAU,iBAAiB,EAAE;CACnC,MAAM,eAAe,MAAM,SAAS,GAAG,MAAM,UAAU,WAAW,SAAS;CAE3E,OAAO,OAAO,aAAa;EAC1B,YAAY,MAAM;EAClB,UAAU;EACV,mBAAmB;EACnB,OAAO,OAAO,IAAI,aAAa;GAQ9B,OAAO,mBAAmB,OAPL,kBAAkB,OAAO,YAAY,CAAC,CAAC,KAC3D,OAAO,eAAe,0BAA0B,UAAU,GAC1D,OAAO,eAAe,oBAAoB,WAAW,CACtD,GAIiC,aAAa,WAAW,MAAM,SAAS;EACzE,CAAC;CACF,CAAC,CAAC,CAAC,KACF,OAAO,UAAU,MAAoB;EACpC,IAAI,aAAa,mBAChB,OAAO,IAAI,mBAAmB;GAC7B,YAAY,MAAM;GAClB,OAAO;GACP,OAAO;EACR,CAAC;EAEF,OAAO;CACR,CAAC,CACF;AACD,CAAC;;;;;;;AAQF,MAAM,sBACL,QACA,YACA,cACwB;CACxB,MAAM,WAAW,WAAW,QAAQ,QAAQ,EAAE;CAC9C,MAAM,SAAS,UAAU,QAAQ,QAAQ,EAAE;CAC3C,MAAM,WAAW,MAChB,EAAE,WAAW,QAAQ,IAAI,GAAG,SAAS,EAAE,MAAM,SAAS,MAAM,MAAM;CACnE,OAAO;EACN,cAAc,OAAO,aAAa,IAAI,OAAO;EAC7C,gBAAgB,OAAO,eAAe,IAAI,OAAO;EACjD,YAAY,OAAO,WAAW,IAAI,OAAO;EACzC,UACC,OAAO,aAAa,OACjB,OACA;GACA,GAAG,OAAO;GACV,cAAc,OAAO,SAAS,aAAa,IAAI,OAAO;EACvD;CACJ;AACD;;;;;;;AAQA,MAAM,qBACL,OACA,UAMA,OAAO,IAAI,aAAa;CACvB,OAAO,OAAO,QAAQ,2CAA2C,MAAM,cAAc,KAAK,GAAG;CAC7F,MAAM,eAAmC,CAAC,GAAG,MAAM,aAAa;CAEhE,MAAM,eAA8B,CAAC;CACrC,MAAM,iBAAgC,CAAC;CACvC,MAAM,aAA4B,CAAC;CAKnC,MAAM,6BAAa,IAAI,IAAqC;CAO5D,MAAM,gCAAgB,IAAI,IAA2B;CACrD,MAAM,kBAA0C,CAAC;CACjD,MAAM,cAAc,CAAC,GAAG,YAAY,CAAC,CAAC,KACrC,MAAM,SAAS,MAAM,SAAS,MAAmB,EAAE,UAAU,CAC9D;CAKA,MAAM,gBAAgB,OAAO,wBAC5B,OACA,aACE,QAAQ,MAAM,EAAE,kBAAkB,IAAI,CAAC,CACvC,KAAK,OAAO;EACZ,YAAY,EAAE;EACd,UAAU,aAAa,CAAC;EACxB,WAAW,EAAE,cAAc;CAC5B,EAAE,CACJ;CACA,KAAK,MAAM,QAAQ,aAAa;EAC/B,MAAM,WAAW,OAAO,WAAW,IAAI;EACvC,MAAM,WAAW,SAAS;EAC1B,IAAI,KAAK,cAAc,KAAA,GAAW;GACjC,MAAM,YAAY,KAAK,UAAU,QAAQ,QAAQ;GACjD,IAAI,cAAc,MAAM;IACvB,MAAM,SAAS,WAAW,IAAI,KAAK,UAAU,MAAM,KAAK,CAAC;IACzD,UAAU,QAAQ,SAAS;IAC3B,WAAW,IAAI,KAAK,UAAU,QAAQ,MAAM;IAO5C,MAAM,UAAU,KAAK,UAAU,kBAAkB;IACjD,MAAM,gBAAgB,KAAK,UAAU,cAAc;IACnD,MAAM,cAAc,cAAc,IAAI,KAAK,UAAU,MAAM;IAC3D,IAAI,gBAAgB,KAAA,GACnB,cAAc,IAAI,KAAK,UAAU,QAAQ;KACxC,UAAU;KACV,WAAW;KACX,eAAe,KAAK;IACrB,CAAC;SACK;KACN,IAAI,YAAY,aAAa,SAC5B,OAAO,OAAO,OAAO,KACpB,IAAI,yBAAyB;MAC5B,QAAQ,KAAK,UAAU;MACvB,OAAO;MACP,aAAa,YAAY;MACzB,aAAa;MACb,UAAU,CAAC,YAAY,eAAe,KAAK,WAAW;KACvD,CAAC,CACF;KAED,IAAI,YAAY,cAAc,eAC7B,OAAO,OAAO,OAAO,KACpB,IAAI,yBAAyB;MAC5B,QAAQ,KAAK,UAAU;MACvB,OAAO;MACP,aAAa,OAAO,YAAY,SAAS;MACzC,aAAa,OAAO,aAAa;MACjC,UAAU,CAAC,YAAY,eAAe,KAAK,WAAW;KACvD,CAAC,CACF;IAEF;GACD;EACD;EAQA,KAAK,MAAM,SAAS,OAAO,OAAO,QAAQ,GACzC,IAAI,kBAAkB,KAAK,GAC1B,gBAAgB,KAAK,KAAK;EAM5B,IAAI,KAAK,kBAAkB,MAAM;EACjC,MAAM,WAAW,WAAW;GAC3B,aAAa,KAAK;GAClB,YAAY,KAAK;GACjB,WAAW,KAAK,cAAc;GAC9B,SAAS;GACT,SAAS,SAAS;EACnB,CAAC;EACD,IAAI,CAAC,SAAS,IACb,OAAO,OAAO,OAAO,KAAK,SAAS,KAAK;EAEzC,MAAM,MAAM,OAAO,UAAU,OAAO,aAAa,IAAI,GAAG,KAAK,UAAU;EAOvE,SAAQ,OANe,QAAQ;GAC9B,MAAM;GACN,SAAS,SAAS;GAClB,MAAM,QAAQ,IAAI;GAClB,YAAY,cAAc,GAAG;EAC9B,CAAC,EAAA,CACe,SAAhB;GACC,KAAK;IACJ,aAAa,KAAK,GAAG;IACrB;GACD,KAAK;IACJ,eAAe,KAAK,GAAG;IACvB;GACD,KAAK;IACJ,WAAW,KAAK,GAAG;IACnB;EACF;CACD;CAEA,MAAM,iBAAiB,oBAAoB,YAAY,aAAa;CAKpE,IAAI,qBAAqB;CACzB,KAAK,MAAM,aAAa,gBAAgB;EACvC,MAAM,YAAY,gBAAgB,UAAU,OAAO;EACnD,IAAI,UAAU,SAAS,GAAG,qBAAqB;EAC/C,MAAM,WAAW,WAAW;GAC3B,aAAa,UAAU;GACvB,YAAY,UAAU;GACtB,WAAW,UAAU;GACrB,SAAS,UAAU;GAKnB,GAAI,UAAU,SAAS,IACpB,EAAE,SAAS,CAAC,YAAY,UAAU,KAAK,IAAI,EAAE,+BAA+B,EAAE,IAC9E,CAAC;EACL,CAAC;EACD,IAAI,CAAC,SAAS,IACb,OAAO,OAAO,OAAO,KAAK,SAAS,KAAK;EAEzC,MAAM,MAAM,OAAO,UAAU,OAAO,UAAU,UAAU,UAAU,UAAU;EAO5E,SAAQ,OANe,QAAQ;GAC9B,MAAM;GACN,SAAS,SAAS;GAClB,MAAM,UAAU,YAAY,MAAQ;GACpC,YAAY,cAAc,GAAG;EAC9B,CAAC,EAAA,CACe,SAAhB;GACC,KAAK;IACJ,aAAa,KAAK,GAAG;IACrB;GACD,KAAK;IACJ,eAAe,KAAK,GAAG;IACvB;GACD,KAAK;IACJ,WAAW,KAAK,GAAG;IACnB;EACF;CACD;CAMA,IAAI,oBAAoB;EACvB,MAAM,MAAM,OAAO,MAAM,QAAQ,0BAA0B;EAO3D,SAAQ,OANe,QAAQ;GAC9B,MAAM;GACN,SAAS;GACT,MAAM;GACN,YAAY,cAAc,GAAG;EAC9B,CAAC,EAAA,CACe,SAAhB;GACC,KAAK;IACJ,aAAa,KAAK,GAAG;IACrB;GACD,KAAK;IACJ,eAAe,KAAK,GAAG;IACvB;GACD,KAAK;IACJ,WAAW,KAAK,GAAG;IACnB;EACF;CACD;CAEA,IAAI,WAAsC;CAC1C,IAAI,gBAAgB,SAAS,GAC5B,WAAW,OAAO,aAAa;EAC9B,aAAa,MAAM;EACnB,UAAU;EACV,iBAAiB,MAAM;CACxB,CAAC;MAED,OAAO,OAAO,QACb,uEACD;CAkBD,MAAM,iBAAiB,CACtB,GAAG,aACD,QACC,MACA,EAAE,cAAc,QAAQ,EAAE,kBAAkB,QAAQ,aAAa,CAAC,MAAM,WAC1E,CAAC,CACA,KAAK,MAAM,EAAE,UAAU,GACzB,GAAG,eACD,QAAQ,MAAM,EAAE,aAAa,EAAE,aAAa,WAAW,CAAC,CACxD,KAAK,MAAM,EAAE,UAAU,CAC1B;CAKA,OAAO,eAAe;EACrB,MAAM,MAAM;EACZ;EACA,YAAY,cAAc,MAAM,aAAa;EAC7C,WAAW,MAAM,cAAc;CAChC,CAAC;CAED,OAAO;EACN;EACA;EACA;EACA;CACD;AACD,CAAC;AAWF,MAAM,cAAc,SACnB,OAAO,IAAI,aAAa;CASvB,MAAM,OAAO,KAAK,WAAW;CAC7B,IAAI,SAAS,KAAA,GACZ,OAAO,OAAO,oBAAoB,gBAAgB,IAAI;CAEvD,MAAM,UAAmC,CAAC;CAC1C,MAAM,UAAyB,CAAC;CAChC,MAAM,OAAwB,EAAE,MAAM,kBAAkB;CAUxD,OAAO,KAAK,KAAK;EARhB,cAAc,MAAM,UAAU;GAC7B,QAAQ,QAAQ;EACjB;EACA,kBAAkB,cAAc;GAC/B,QAAQ,KAAK,SAAS;EACvB;EACA,YAAY;CAEM,CAAC,CAAC,CAAC,KACrB,OAAO,UACL,UACA,IAAI,kBAAkB;EACrB,aAAa,KAAK;EAClB,YAAY,KAAK;EACjB;CACD,CAAC,CACH,CACD;CACA,OAAO;EAAE;EAAS;CAAQ;AAC3B,CAAC,CAAC,CAAC,KAAK,OAAO,SAAS,gBAAgB,EAAE,YAAY,EAAE,mBAAmB,KAAK,YAAY,EAAE,CAAC,CAAC;;;;;;;;AASjG,MAAM,sBACL,UAEA,OAAO,IAAI,aAAa;CACvB,MAAM,yBAAS,IAAI,IAA2B;CAC9C,MAAM,yBAAS,IAAI,IAA2B;CAC9C,KAAK,MAAM,KAAK,OAAO;EAMtB,IAAI,EAAE,kBAAkB,MAAM;GAK7B,MAAM,UAAU,GAAG,aAAa,CAAC,EAAE,GAAG,EAAE;GACxC,MAAM,KAAK,OAAO,IAAI,OAAO,KAAK,CAAC;GACnC,GAAG,KAAK,EAAE,WAAW;GACrB,OAAO,IAAI,SAAS,EAAE;EACvB;EACA,IAAI,EAAE,+BAA+B,MAAM;EAC3C,MAAM,KAAK,OAAO,IAAI,EAAE,WAAW,KAAK,CAAC;EACzC,GAAG,KAAK,EAAE,UAAU;EACpB,OAAO,IAAI,EAAE,aAAa,EAAE;CAC7B;CACA,KAAK,MAAM,CAAC,SAAS,aAAa,QACjC,IAAI,SAAS,SAAS,GACrB,OAAO,OAAO,OAAO,KACpB,IAAI,oBAAoB;EACvB,MAAM;EAGN,YAAY,QAAQ,MAAM,QAAQ,QAAQ,GAAG,IAAI,CAAC;EAClD;CACD,CAAC,CACF;CAGF,KAAK,MAAM,CAAC,MAAM,gBAAgB,QACjC,IAAI,YAAY,SAAS,GACxB,OAAO,OAAO,OAAO,KACpB,IAAI,wBAAwB;EAC3B,aAAa;EACb;CACD,CAAC,CACF;AAGH,CAAC;;;;;;;AAQF,MAAM,qCACL,UAEA,OAAO,IAAI,aAAa;CAMvB,MAAM,iCAAiB,IAAI,IAAoB;CAC/C,KAAK,MAAM,QAAQ,OAClB,IAAI,KAAK,cAAc,KAAA,GAAW;EACjC,MAAM,WAAW,KAAK,UAAU,kBAAkB;EAClD,eAAe,IAAI,GAAG,SAAS,GAAG,KAAK,UAAU,UAAU,KAAK,UAAU,MAAM;CACjF;CAED,KAAK,MAAM,CAAC,KAAK,WAAW,gBAAgB;EAC3C,MAAM,WAAW,IAAI,MAAM,GAAG,IAAI,QAAQ,GAAG,CAAC;EAC9C,MAAM,YAAY,MAAM,QACtB,SACA,KAAK,kBAAkB,QACvB,aAAa,IAAI,MAAM,YACvB,KAAK,eAAe,MACtB;EACA,IAAI,UAAU,SAAS,GACtB,OAAO,OAAO,OAAO,KACpB,IAAI,oBAAoB;GACvB,MAAM;GACN,YAAY;GACZ,UAAU,CAAC,GAAG,UAAU,KAAK,SAAS,KAAK,WAAW,GAAG,aAAa,QAAQ;EAC/E,CAAC,CACF;CAEF;AACD,CAAC;;;;;;;;;;;;;;AAmCF,MAAM,iBAAiB,MACtB,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC;AAExD,MAAM,aACL,QACA,WACU;CACV,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAAG;EAClD,MAAM,WAAW,OAAO;EACxB,IAAI,cAAc,QAAQ,KAAK,cAAc,KAAK,GACjD,UAAU,UAAU,KAAK;OAOzB,OAAO,OAAO,cAAc,KAAK,KAAK,MAAM,QAAQ,KAAK,IAAI,gBAAgB,KAAK,IAAI;CAExF;AACD;;;;;;;;;;;AAYA,MAAM,uBACL,SACA,SACkC;CAClC,MAAM,QAA8B,CAAC;CACrC,MAAM,gBAAgB,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;CAClF,KAAK,MAAM,CAAC,QAAQ,aAAa,eAAe;EAC/C,IAAI,OAAO,KAAK,QAAQ,CAAC,CAAC,WAAW,GAAG;EACxC,MAAM,OAAO,WAAW,MAAM;EAC9B,MAAM,aAAa,KAAK,IAAI,MAAM,KAAK;GACtC,UAAU;GACV,WAAW;GACX,eAAe,aAAa;EAC7B;EACA,MAAM,KAAK;GACV,aAAa,aAAa;GAC1B,YAAY;GACZ,SAAS,GAAG,OAAO,SAAS;GAC5B,UAAU,WAAW;GACrB,WAAW,WAAW;EACvB,CAAC;CACF;CACA,OAAO;AACR;AAEA,MAAM,cAAc,WAA2B,OAAO,QAAQ,SAAS,EAAE,CAAC,CAAC,QAAQ,SAAS,EAAE;;;;AAK9F,MAAM,2BAA2B;CAChC;CACA;CACA;CACA;AACD;;;;AAMA,MAAM,2BAA2B,OAAgB,UAA4C;CAC5F,IAAI,UAAU,KAAK,GAAG;EACrB,KAAK,MAAM,QAAQ,0BAClB,IAAI,MAAM,KAAK,SAAS,GAAG,KAAK,EAAE,GAAG,MAAM,IAAI,IAAI;EAEpD;CACD;CACA,IAAI,MAAM,QAAQ,KAAK,GAAG;EACzB,KAAK,MAAM,KAAK,OAAO,wBAAwB,GAAG,KAAK;EACvD;CACD;CACA,IAAI,cAAc,KAAK,GACtB,KAAK,MAAM,KAAK,OAAO,OAAO,KAAK,GAAG,wBAAwB,GAAG,KAAK;AAExE;;;AAIA,MAAM,mBAAmB,YAEmB;CAC3C,MAAM,wBAAQ,IAAI,IAA2B;CAC7C,KAAK,MAAM,KAAK,OAAO,OAAO,OAAO,GAAG,wBAAwB,GAAG,KAAK;CACxE,OAAO,yBAAyB,QAAQ,SAAS,MAAM,IAAI,IAAI,CAAC;AACjE;AAqDA,IAAa,6BAAb,cAAgD,QAAQ,QAGtD,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC;;;;;;;;;;AAWxC,MAAM,sBACL,QACA,SACA,WACc;CACd,MAAM,YAAY,MAAyC,cAAc,CAAC,IAAI,IAAI,CAAC;CACnF,MAAM,YAAY,MAAoC,OAAO,MAAM,WAAW,IAAI,KAAA;CAElF,MAAM,WAA4C,CAAC;CACnD,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,SAAS,OAAO,WAAW,CAAC,GAAG;EACvE,MAAM,QAAQ,SAAS,GAAG;EAC1B,MAAM,MAAM,SAAS,MAAM,MAAM;EACjC,IAAI,QAAQ,KAAA,GAAW;EACvB,SAAS,QAAQ;GAChB;GACA,GAAI,SAAS,MAAM,UAAU,MAAM,KAAA,IAAY,EAAE,SAAS,SAAS,MAAM,UAAU,EAAE,IAAI,CAAC;GAC1F,GAAI,MAAM,cAAc,KAAA,IAAY,EAAE,QAAQ,SAAS,MAAM,SAAS,KAAK,KAAK,IAAI,CAAC;GACrF,GAAI,MAAM,eAAe,KAAA,IAAY,EAAE,SAAS,SAAS,MAAM,UAAU,KAAK,KAAK,IAAI,CAAC;EACzF;CACD;CAEA,MAAM,WAA4C,CAAC;CACnD,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,SAAS,OAAO,WAAW,CAAC,GAAG;EACvE,MAAM,QAAQ,SAAS,GAAG;EAK1B,MAAM,QAAQ,SAAS,MAAM,YAAY;EACzC,MAAM,KAAK,UAAU,KAAA,KAAa,UAAU,KAAK,gBAAgB;EACjE,MAAM,aAAa,SAAS,MAAM,UAAU;EAC5C,MAAM,UAAkC,CAAC;EACzC,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,UAAU,GAAG;GAChD,MAAM,IAAI,SAAS,CAAC;GACpB,IAAI,MAAM,KAAA,GAAW,QAAQ,KAAK;EACnC;EACA,SAAS,QAAQ;GAChB;GACA,GAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,IAAI,EAAE,QAAQ,IAAI,CAAC;EACtD;CACD;CAEA,MAAM,WAAmC,CAAC;CAC1C,KAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,SAAS,OAAO,WAAW,CAAC,GAAG;EAGrE,MAAM,UAAU,SAAS,CAAC,KAAK,SAAS,SAAS,CAAC,CAAC,CAAC,UAAU;EAC9D,IAAI,YAAY,KAAA,GAAW,SAAS,QAAQ;CAC7C;CAEA,MAAM,eAAuC,CAAC;CAC9C,KAAK,MAAM,CAAC,KAAK,MAAM,OAAO,QAAQ,SAAS,OAAO,eAAe,CAAC,GAAG;EACxE,MAAM,IAAI,SAAS,CAAC;EACpB,IAAI,MAAM,KAAA,GAAW,aAAa,OAAO;CAC1C;CAaA,MAAM,cAAc,OAAO,KAAK,QAAQ;CACxC,MAAM,gBAAgB,SAAS,OAAO,UAAU;CAUhD,OAAO;EACN,SATA,kBAAkB,KAAA,KAAa,YAAY,SAAS,aAAa,IAC9D,gBACA,YAAY,SAAS,OAAO,IAC3B,UACA,YAAY,WAAW,IACtB,YAAY,KACX,iBAAiB;EAIvB;EACA;EACA;EACA;EACA,GAAI,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,IAAI,EAAE,OAAO,IAAI,CAAC;CACpD;AACD;;;;AAWA,MAAa,2BAAoE,MAAM,OACtF,4BACA,OAAO,IAAI,aAAa;CACvB,MAAM,mBAAmB,OAAO,IAAI,KAA4C,CAAC,CAAC;CAClF,MAAM,SAAS,OAAO,IAAI,KAAK,CAAC;CAEhC,MAAM,wBAAqE,WAAW,SACrF,OAAO,IAAI,aAAa;EACvB,MAAM,MAAM,OAAO,IAAI,aAAa,SAAS,MAAM,IAAI,CAAC;EACxD,MAAM,QAAgC;GAAE;GAAW;GAAM;EAAI;EAC7D,OAAO,IAAI,OAAO,mBAAmB,OAAO,CAAC,GAAG,IAAI,KAAK,CAAC;EAC1D,OAAO,OAAO,mBACb,IAAI,OAAO,mBAAmB,OAAO,GAAG,QAAQ,MAAM,EAAE,QAAQ,GAAG,CAAC,CACrE;CACD,CAAC;CAEF,MAAM,oBAA6D,YAClE,OAAO,IAAI,aAAa;EACvB,MAAM,cAAc,OAAO,IAAI,IAAI,gBAAgB,EAAA,CAAG,KAAK,MAAM,EAAE,IAAI;EAQvE,MAAM,SAAkC,CAAC;EAKzC,MAAM,WAAoC,CAAC;EAM3C,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,QAAQ,YAAY;GAC9B,IAAI,KAAK,cAAc,KAAA,GAAW;GASlC,IAAI,KAAK,UAAU,mBAAmB,KAAA,GACrC,UAAU,QAAQ,KAAK,UAAU,cAAc;GAEhD,IAAI,KAAK,UAAU,WAAW,aAAa;IAC1C,MAAM,WAAW,OAAO,WAAW,IAAI;IACvC,MAAM,YAAY,KAAK,UAAU,QAAQ,SAAS,OAAO;IACzD,IAAI,cAAc,MAAM,UAAU,QAAQ,SAAS;GACpD,OAAO,IAAI,KAAK,UAAU,WAAW,eAAe;IACnD,MAAM,WAAW,OAAO,WAAW,IAAI;IACvC,MAAM,YAAY,KAAK,UAAU,QAAQ,SAAS,OAAO;IACzD,IAAI,cAAc,MAAM,UAAU,UAAU,SAAS;GACtD;EACD;EACA,OAAO,cAAc;EACrB,OAAO,mBAAmB,QAAQ,SAAS,MAAwB;CACpE,CAAC;CAEF,MAAM,mBACL,OAAO,IAAI,aAAa;EAEvB,MAAM,UADc,OAAO,IAAI,IAAI,gBAAgB,EAAA,CAAG,KAAK,MAAM,EAAE,IAC3C,CAAC,CAAC,OAAO,YAAY;EAC7C,IAAI,OAAO,WAAW,GAErB,OAAO;GAAE,cAAc,CAAC;GAAG,gBAAgB,CAAC;GAAG,YAAY,CAAC;GAAG,UAAU;EAAK;EAQ/E,OAAO,mBAAmB,MAAM;EAChC,OAAO,kCAAkC,MAAM;EAC/C,MAAM,QAAQ,OAAO;EAMrB,OAAO,OAAO,OAAO,OACpB,OAAO,IAAI,aAAa;GACvB,OAAO,iBAAiB,MAAM,iBAAiB,6BAA6B,CAAC,CAAC,KAC7E,OAAO,UACL,UACA,IAAI,mBAAmB;IACtB,YAAY,MAAM;IAClB,OAAO;IACP;GACD,CAAC,CACH,CACD;GACA,OAAO,OAAO,kBAAkB;IAAE,eAAe;IAAQ,WAAW;GAAM,GAAG,KAAK;EACnF,CAAC,CACF;CACD,CAAC;CAEF,OAAO,2BAA2B,GAAG;EACpC;EACA;EACA;CACD,CAAC;AACF,CAAC,CACF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { buildSubstrateLayers, layerProductionOrchestrators, resolveProductionCodegenOptions } from "./boot.mjs";
|
|
2
|
+
import { Layer } from "effect";
|
|
3
|
+
//#region src/orchestrators/layers.ts
|
|
4
|
+
/** Compose substrate + orchestrator Layers for a verb that knows its
|
|
5
|
+
* stack-config (i.e. has a `loaded` config and can pass codegen
|
|
6
|
+
* options). Use `buildDirectSnapshotLayers` when the verb does NOT
|
|
7
|
+
* load a config (e.g. restore/delete/wipe).
|
|
8
|
+
*
|
|
9
|
+
* Resolves the per-stack LIVE codegen output location HERE — the one
|
|
10
|
+
* boot seam where the EFFECTIVE stack (`String(identity.stack)`, already
|
|
11
|
+
* run through the explicit-`--stack` > `config.stackName` > inferred
|
|
12
|
+
* precedence ladder by `resolvedIdentityForStack`/`identityValueFor`
|
|
13
|
+
* upstream) is in scope. EVERY live run emits into
|
|
14
|
+
* `.devstack/stacks/<stack>/generated/` (gitignored) so the id-bearing
|
|
15
|
+
* tree never lands in committed source and two stacks never clobber; the
|
|
16
|
+
* committed `src/generated` tree is owned by the stack-free `codegen`
|
|
17
|
+
* verb. The resolved literal `outputDir`/`stackSubdir` flow into
|
|
18
|
+
* `layerProductionOrchestrators` unchanged — `paths.ts` keeps consuming
|
|
19
|
+
* a literal, minimal blast radius. */
|
|
20
|
+
const buildVerbLayers = (params) => layerProductionOrchestrators({ codegen: resolveProductionCodegenOptions({
|
|
21
|
+
appRoot: params.appRoot,
|
|
22
|
+
effectiveStack: String(params.identity.stack),
|
|
23
|
+
codegen: params.stack.options.codegen
|
|
24
|
+
}) }).pipe(Layer.provideMerge(buildSubstrateLayers(params.identity, params.runtimeRoot)));
|
|
25
|
+
/** Substrate + (default-codegen) orchestrator Layers for verbs that
|
|
26
|
+
* don't load a config. */
|
|
27
|
+
const buildDirectSnapshotLayers = (params) => layerProductionOrchestrators().pipe(Layer.provideMerge(buildSubstrateLayers(params.identity, params.runtimeRoot)));
|
|
28
|
+
//#endregion
|
|
29
|
+
export { buildDirectSnapshotLayers, buildVerbLayers };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=layers.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layers.mjs","names":[],"sources":["../../src/orchestrators/layers.ts"],"sourcesContent":["// Shared substrate + orchestrator Layer composition for stack boot seams.\n//\n// The seams that boot a substrate (CLI `up`, `apply --direct`, snapshot\n// capture --direct; and `api/run-stack.ts`) all build the same composition:\n// - `layerProductionOrchestrators({ codegen })` on top of\n// - `buildSubstrateLayers(identity, runtimeRoot)`.\n//\n// One helper each seam references, instead of rebuilding the composition\n// inline at every call site. Lives in `orchestrators/` so BOTH `api/` and\n// `cli/` can import it by going *down* the layering (neither can import the\n// other). This module imports only from `orchestrators/boot.ts` and\n// `substrate/` — never from `api/` or `cli/`, so there is no import cycle.\n\nimport { Layer } from 'effect';\n\nimport type { Identity } from '../substrate/identity.ts';\nimport {\n\tbuildSubstrateLayers,\n\tlayerProductionOrchestrators,\n\tresolveProductionCodegenOptions,\n} from './boot.ts';\nimport type { SupervisedStack } from '../substrate/runtime/index.ts';\n\n/** Compose substrate + orchestrator Layers for a verb that knows its\n * stack-config (i.e. has a `loaded` config and can pass codegen\n * options). Use `buildDirectSnapshotLayers` when the verb does NOT\n * load a config (e.g. restore/delete/wipe).\n *\n * Resolves the per-stack LIVE codegen output location HERE — the one\n * boot seam where the EFFECTIVE stack (`String(identity.stack)`, already\n * run through the explicit-`--stack` > `config.stackName` > inferred\n * precedence ladder by `resolvedIdentityForStack`/`identityValueFor`\n * upstream) is in scope. EVERY live run emits into\n * `.devstack/stacks/<stack>/generated/` (gitignored) so the id-bearing\n * tree never lands in committed source and two stacks never clobber; the\n * committed `src/generated` tree is owned by the stack-free `codegen`\n * verb. The resolved literal `outputDir`/`stackSubdir` flow into\n * `layerProductionOrchestrators` unchanged — `paths.ts` keeps consuming\n * a literal, minimal blast radius. */\nexport const buildVerbLayers = (params: {\n\treadonly identity: Identity;\n\treadonly stack: SupervisedStack;\n\treadonly appRoot: string;\n\treadonly runtimeRoot: string;\n}) =>\n\tlayerProductionOrchestrators({\n\t\tcodegen: resolveProductionCodegenOptions({\n\t\t\tappRoot: params.appRoot,\n\t\t\teffectiveStack: String(params.identity.stack),\n\t\t\tcodegen: params.stack.options.codegen,\n\t\t}),\n\t}).pipe(Layer.provideMerge(buildSubstrateLayers(params.identity, params.runtimeRoot)));\n\n/** Substrate + (default-codegen) orchestrator Layers for verbs that\n * don't load a config. */\nexport const buildDirectSnapshotLayers = (params: {\n\treadonly identity: Identity;\n\treadonly runtimeRoot: string;\n}) =>\n\tlayerProductionOrchestrators().pipe(\n\t\tLayer.provideMerge(buildSubstrateLayers(params.identity, params.runtimeRoot)),\n\t);\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuCA,MAAa,mBAAmB,WAM/B,6BAA6B,EAC5B,SAAS,gCAAgC;CACxC,SAAS,OAAO;CAChB,gBAAgB,OAAO,OAAO,SAAS,KAAK;CAC5C,SAAS,OAAO,MAAM,QAAQ;AAC/B,CAAC,EACF,CAAC,CAAC,CAAC,KAAK,MAAM,aAAa,qBAAqB,OAAO,UAAU,OAAO,WAAW,CAAC,CAAC;;;AAItF,MAAa,6BAA6B,WAIzC,6BAA6B,CAAC,CAAC,KAC9B,MAAM,aAAa,qBAAqB,OAAO,UAAU,OAAO,WAAW,CAAC,CAC7E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/orchestrators/lifecycle-prune/errors.ts"],"sourcesContent":["// Tagged failures for the lifecycle-prune orchestrator.\n//\n// Per STYLE_GUIDE §2.2: orchestrator-level failures use\n// `Schema.TaggedErrorClass`; never plain `Error`; never `unknown` at\n// the public surface. The orchestrator's underlying L1 docker calls\n// raise `DockerRuntimeError` (itself a union of 16+ tagged errors);\n// we project each underlying failure onto a single phase-tagged\n// envelope so CLI consumers can `Effect.catchTag('LifecyclePruneError')`\n// and branch on `phase` instead of inspecting the L1 union directly.\n\nimport { Schema } from 'effect';\n\nimport type { DockerRuntimeError } from '../../runtime/docker/errors.ts';\n\n/** Phase discriminator: which step of the cross-stack sweep failed.\n * `inventory` covers the parallel listDevstack* fan-out and the\n * per-group bucketing; the four `remove-*` phases cover the\n * resource-specific sweep loops in `runLifecyclePrune`. */\nexport const LifecyclePrunePhase = Schema.Literals([\n\t'inventory',\n\t'remove-containers',\n\t'remove-networks',\n\t'remove-volumes',\n\t'remove-images',\n]);\nexport type LifecyclePrunePhase = typeof LifecyclePrunePhase.Type;\n\nexport class LifecyclePruneError extends Schema.TaggedErrorClass<LifecyclePruneError>()(\n\t'LifecyclePruneError',\n\t{\n\t\tphase: LifecyclePrunePhase,\n\t\tdetail: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** Curry: `phase` → `(DockerRuntimeError) → LifecyclePruneError`.\n * Used as `Effect.mapError(failPhase('inventory'))` to project the L1\n * docker error union onto a single phase-tagged envelope while\n * preserving the underlying cause for diagnostics. */\nexport const failPhase =\n\t(phase: LifecyclePrunePhase) =>\n\t(cause: DockerRuntimeError): LifecyclePruneError =>\n\t\tnew LifecyclePruneError({\n\t\t\tphase,\n\t\t\tdetail: `${phase}: ${cause._tag}`,\n\t\t\tcause,\n\t\t});\n"],"mappings":";;;;;;AAkBA,MAAa,sBAAsB,OAAO,SAAS;CAClD;CACA;CACA;CACA;CACA;
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/orchestrators/lifecycle-prune/errors.ts"],"sourcesContent":["// Tagged failures for the lifecycle-prune orchestrator.\n//\n// Per STYLE_GUIDE §2.2: orchestrator-level failures use\n// `Schema.TaggedErrorClass`; never plain `Error`; never `unknown` at\n// the public surface. The orchestrator's underlying L1 docker calls\n// raise `DockerRuntimeError` (itself a union of 16+ tagged errors);\n// we project each underlying failure onto a single phase-tagged\n// envelope so CLI consumers can `Effect.catchTag('LifecyclePruneError')`\n// and branch on `phase` instead of inspecting the L1 union directly.\n\nimport { Schema } from 'effect';\n\nimport type { DockerRuntimeError } from '../../runtime/docker/errors.ts';\n\n/** Phase discriminator: which step of the cross-stack sweep failed.\n * `inventory` covers the parallel listDevstack* fan-out and the\n * per-group bucketing; the four `remove-*` phases cover the\n * resource-specific sweep loops in `runLifecyclePrune`. */\nexport const LifecyclePrunePhase = Schema.Literals([\n\t'inventory',\n\t'remove-containers',\n\t'remove-networks',\n\t'remove-volumes',\n\t'remove-images',\n]);\nexport type LifecyclePrunePhase = typeof LifecyclePrunePhase.Type;\n\nexport class LifecyclePruneError extends Schema.TaggedErrorClass<LifecyclePruneError>()(\n\t'LifecyclePruneError',\n\t{\n\t\tphase: LifecyclePrunePhase,\n\t\tdetail: Schema.String,\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** Curry: `phase` → `(DockerRuntimeError) → LifecyclePruneError`.\n * Used as `Effect.mapError(failPhase('inventory'))` to project the L1\n * docker error union onto a single phase-tagged envelope while\n * preserving the underlying cause for diagnostics. */\nexport const failPhase =\n\t(phase: LifecyclePrunePhase) =>\n\t(cause: DockerRuntimeError): LifecyclePruneError =>\n\t\tnew LifecyclePruneError({\n\t\t\tphase,\n\t\t\tdetail: `${phase}: ${cause._tag}`,\n\t\t\tcause,\n\t\t});\n"],"mappings":";;;;;;AAkBA,MAAa,sBAAsB,OAAO,SAAS;CAClD;CACA;CACA;CACA;CACA;AACD,CAAC;AAGD,IAAa,sBAAb,cAAyC,OAAO,iBAAsC,CAAC,CACtF,uBACA;CACC,OAAO;CACP,QAAQ,OAAO;CACf,OAAO,OAAO,SAAS,OAAO,MAAM;AACrC,CACD,CAAC,CAAC,CAAC;;;;;AAMH,MAAa,aACX,WACA,UACA,IAAI,oBAAoB;CACvB;CACA,QAAQ,GAAG,MAAM,IAAI,MAAM;CAC3B;AACD,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { logDebugAndFallback } from "../../substrate/runtime/observability/ignore-with-log.mjs";
|
|
2
1
|
import { LivenessProbeScope, layerLivenessProbeScope } from "../../substrate/runtime/cross-process/liveness.mjs";
|
|
3
2
|
import { readRoster } from "../../substrate/runtime/cross-process/roster.mjs";
|
|
3
|
+
import { logDebugAndFallback } from "../../substrate/runtime/observability/ignore-with-log.mjs";
|
|
4
4
|
import "../../substrate/runtime/observability/index.mjs";
|
|
5
5
|
import { DockerSpawner, layerDockerHostDefault } from "../../runtime/docker/client.mjs";
|
|
6
6
|
import { LabelKey } from "../../runtime/docker/labels.mjs";
|
|
@@ -14,8 +14,8 @@ import { removeRouterProfileStateForDockerStack } from "../router/cleanup.mjs";
|
|
|
14
14
|
import { failPhase } from "./errors.mjs";
|
|
15
15
|
import { Effect, Layer } from "effect";
|
|
16
16
|
import { existsSync } from "node:fs";
|
|
17
|
-
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
|
|
18
17
|
import { join } from "node:path";
|
|
18
|
+
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
|
|
19
19
|
import * as NodePath from "@effect/platform-node/NodePath";
|
|
20
20
|
import * as NodeChildProcessSpawner from "@effect/platform-node/NodeChildProcessSpawner";
|
|
21
21
|
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
@@ -91,7 +91,7 @@ const routerStackForContainer = (name) => ({
|
|
|
91
91
|
* decision to key per-stack-disk-state on `stack` alone. (Two stacks
|
|
92
92
|
* in DIFFERENT apps that pick the SAME stack name would already share
|
|
93
93
|
* every other on-disk artifact — `state.json`, `cache/`, `snapshots/`,
|
|
94
|
-
* `stack.lock
|
|
94
|
+
* `stack.lock`. The Docker-level discriminator
|
|
95
95
|
* is the container/network/volume NAME, which IS `<app>-<stack>-…`
|
|
96
96
|
* composed inside each plugin; so cross-attribution at the daemon
|
|
97
97
|
* level is prevented by name uniqueness even when disk state collides.)
|
|
@@ -116,9 +116,8 @@ const livePidsForStack = (runtimeRoot, stack) => {
|
|
|
116
116
|
};
|
|
117
117
|
/** True when the group is one of the two shared shapes the
|
|
118
118
|
* orchestrator knows about: `_per-app_` (cross-stack-per-app shared)
|
|
119
|
-
* or the router-singleton (`ROUTER_SHARED_APP`).
|
|
120
|
-
|
|
121
|
-
const isSharedLifecyclePruneGroup = (app, stack) => stack === "_per-app_" || app === "devstack-router";
|
|
119
|
+
* or the router-singleton (`ROUTER_SHARED_APP`). */
|
|
120
|
+
const isSharedGroup = (app, stack) => stack === "_per-app_" || app === "devstack-router";
|
|
122
121
|
const sharedKindFor = (app, stack) => {
|
|
123
122
|
if (app === "devstack-router") return "router";
|
|
124
123
|
if (stack === "_per-app_") return "per-app-shared";
|
|
@@ -129,7 +128,6 @@ const sharedKindFor = (app, stack) => {
|
|
|
129
128
|
* router-stack naming predicate. */
|
|
130
129
|
const isRouterLifecyclePruneGroup = (group) => group.app === "devstack-router" && group.stack.startsWith(`devstack-router-`);
|
|
131
130
|
const isRouterGroup = isRouterLifecyclePruneGroup;
|
|
132
|
-
const isSharedGroup = isSharedLifecyclePruneGroup;
|
|
133
131
|
/** Label-tuple match for the `removeDevstack*` sweepers — router-shared
|
|
134
132
|
* resources stamp `{app: ROUTER_SHARED_APP, stack: <profile-name>}`
|
|
135
133
|
* (see `traefik-container.ts:ensureNetwork`) and the inventory pass
|
|
@@ -214,7 +212,7 @@ const collectLifecyclePruneInventory = (options) => Effect.gen(function* () {
|
|
|
214
212
|
return a.stack < b.stack ? -1 : a.stack > b.stack ? 1 : 0;
|
|
215
213
|
});
|
|
216
214
|
return { groups };
|
|
217
|
-
})
|
|
215
|
+
});
|
|
218
216
|
const selectedGroups = (inventory, selection) => {
|
|
219
217
|
const selected = new Set(selection.groupKeys);
|
|
220
218
|
return inventory.groups.filter((group) => selected.has(group.key));
|
|
@@ -330,7 +328,7 @@ const runLifecyclePrune = (options, selection) => Effect.gen(function* () {
|
|
|
330
328
|
routerStack: group.stack
|
|
331
329
|
});
|
|
332
330
|
}
|
|
333
|
-
}).pipe(Effect.provide(NodeFileSystem.layer)
|
|
331
|
+
}).pipe(Effect.provide(NodeFileSystem.layer));
|
|
334
332
|
return {
|
|
335
333
|
inspectedGroups: groups.length,
|
|
336
334
|
selectedGroups: prunableGroups.length,
|
|
@@ -343,7 +341,7 @@ const runLifecyclePrune = (options, selection) => Effect.gen(function* () {
|
|
|
343
341
|
foreignNetworkHolders,
|
|
344
342
|
staleNetworkEndpoints
|
|
345
343
|
};
|
|
346
|
-
})
|
|
344
|
+
});
|
|
347
345
|
//#endregion
|
|
348
346
|
export { DEFAULT_LIFECYCLE_PRUNE_RESOURCES, collectLifecyclePruneInventory, defaultLifecyclePruneSelection, lifecyclePruneAppsWithLiveSiblings, runLifecyclePrune };
|
|
349
347
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["joinPath"],"sources":["../../../src/orchestrators/lifecycle-prune/index.ts"],"sourcesContent":["// Lifecycle prune orchestrator — cross-stack Docker sweep.\n//\n// Architecture § L3 orchestrators. Walks the L1 docker inventory (the\n// reference `ContainerRuntime` adapter), groups labelled resources by\n// `(app, stack)`, consults the cross-process roster + claim ledger for\n// liveness, and dispatches removal through the sibling router cleanup\n// helper for router-shared resources.\n//\n// Name-blindness: this orchestrator never names a service. It walks\n// the engine label tuple (`LabelKey.app` / `LabelKey.stack`) which is\n// L1-substrate-level vocabulary and dispatches on the\n// `ROUTER_SHARED_APP` sentinel exported by the router orchestrator.\n//\n// Consumers: `cli/prune-direct.ts` (the L4-adjacent CLI infrastructure\n// for the `devstack prune` verb) calls `runLifecyclePrune` /\n// `collectLifecyclePruneInventory`. No L4 surface module imports L1\n// runtime adapters directly; that boundary lives here.\n\nimport { existsSync } from 'node:fs';\nimport { join as joinPath } from 'node:path';\n\nimport * as NodeChildProcessSpawner from '@effect/platform-node/NodeChildProcessSpawner';\nimport * as NodeFileSystem from '@effect/platform-node/NodeFileSystem';\nimport * as NodePath from '@effect/platform-node/NodePath';\nimport { Effect, Layer } from 'effect';\nimport { ChildProcessSpawner } from 'effect/unstable/process/ChildProcessSpawner';\n\nimport {\n\tDockerHost,\n\tDockerSpawner,\n\ttype ForeignNetworkHolder,\n\tLabelKey,\n\tlayerDockerHostDefault,\n\tlistDevstackContainers,\n\tlistDevstackContainersByKind,\n\tlistDevstackImages,\n\tlistDevstackNetworks,\n\tlistDevstackVolumes,\n\tremoveDevstackContainers,\n\tremoveDevstackContainersByKindAndName,\n\tremoveDevstackImages,\n\tremoveDevstackNetworksBestEffort,\n\tremoveDevstackVolumes,\n\ttype StaleNetworkEndpoint,\n} from '../../runtime/docker/index.ts';\nimport {\n\tlayerLivenessProbeScope,\n\tLivenessProbeScope,\n\treadRoster,\n} from '../../substrate/runtime/cross-process/index.ts';\nimport { PER_APP_SHARED_STACK } from '../../substrate/runtime/managed-container.ts';\nimport { logDebugAndFallback } from '../../substrate/runtime/observability/index.ts';\nimport { ROUTER_SHARED_APP, removeRouterProfileStateForDockerStack } from '../router/cleanup.ts';\n// Import the kind sentinel from its canonical source rather than the\n// `traefik-container.ts` re-export — the router-singleton label literals\n// live in `sentinels.ts`, and lifecycle-prune only needs the constant\n// (not the container ops), so depending on the sentinel module keeps the\n// import edge minimal and the source-of-truth direct.\nimport { ROUTER_KIND_LABEL_VALUE } from '../router/sentinels.ts';\nimport { failPhase, type LifecyclePruneError } from './errors.ts';\n\nexport { LifecyclePruneError, LifecyclePrunePhase } from './errors.ts';\n\n// -----------------------------------------------------------------------------\n// Public shapes — mirror `surfaces/cli/commands/prune.ts` field-for-field so\n// the verb dispatcher can consume the orchestrator output without an adapter.\n// -----------------------------------------------------------------------------\n\nexport interface LifecyclePruneOptions {\n\treadonly runtimeRoot: string;\n}\n\nexport interface LifecyclePruneGroup {\n\treadonly key: string;\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly live: boolean;\n\treadonly livePids: ReadonlyArray<number>;\n\treadonly shared: boolean;\n\t/** Discriminator for shared-resource groups. `'per-app-shared'`\n\t * groups (the `_per-app_` synthetic stack) stay pinned while any\n\t * sibling stack under the same app is live; `'router'` groups are\n\t * auto-prunable when no app pins them. `null` for normal groups. */\n\treadonly sharedKind: SharedGroupKind | null;\n\t/** True when the group represents a router-shared resource set that\n\t * is auto-prunable in non-interactive flows (`devstack prune --all`).\n\t * Computed by the orchestrator so surfaces never recompute the\n\t * router-stack naming predicate. */\n\treadonly autoPrunable: boolean;\n\treadonly containers: number;\n\treadonly runningContainers: number;\n\treadonly networks: number;\n\treadonly volumes: number;\n\treadonly images: number;\n}\n\n/** Kind of a shared-resource group. `'per-app-shared'` groups belong\n * to the `PER_APP_SHARED_STACK` synthetic stack under a real app and\n * stay pinned while a sibling stack is live. `'router'` groups are\n * router-singleton resources. */\nexport type SharedGroupKind = 'per-app-shared' | 'router';\n\nexport interface LifecyclePruneInventory {\n\treadonly groups: ReadonlyArray<LifecyclePruneGroup>;\n}\n\nexport interface LifecyclePruneResourceScope {\n\treadonly containers: boolean;\n\treadonly networks: boolean;\n\treadonly volumes: boolean;\n\treadonly images: boolean;\n}\n\nexport interface LifecyclePruneSelection {\n\treadonly groupKeys: ReadonlyArray<string>;\n\treadonly resources: LifecyclePruneResourceScope;\n\treadonly dryRun: boolean;\n}\n\nexport interface LifecyclePruneSummary {\n\treadonly inspectedGroups: number;\n\treadonly selectedGroups: number;\n\treadonly skippedLiveGroups: number;\n\treadonly containersRemoved: number;\n\treadonly networksRemoved: number;\n\treadonly networksSkipped: number;\n\treadonly volumesRemoved: number;\n\treadonly imagesRemoved: number;\n\t/** Non-devstack containers still holding networks that prune could\n\t * not remove. Empty when every network came down cleanly. */\n\treadonly foreignNetworkHolders: ReadonlyArray<ForeignNetworkHolder>;\n\t/** Endpoints Docker insists exist on a network but which no CLI/API\n\t * path can address — symptom of a Docker engine bug (the bridge\n\t * driver leaked endpoint metadata after a container was reaped).\n\t * Only a Docker daemon restart clears these. */\n\treadonly staleNetworkEndpoints: ReadonlyArray<StaleNetworkEndpoint>;\n}\n\n// -----------------------------------------------------------------------------\n// Internal helpers\n// -----------------------------------------------------------------------------\n\ntype ResourceBucket = {\n\tcontainers: number;\n\trunningContainers: number;\n\tnetworks: number;\n\tvolumes: number;\n\timages: number;\n};\n\nconst layerDockerSpawnerFromNode: Layer.Layer<DockerSpawner, never, ChildProcessSpawner> =\n\tLayer.effect(\n\t\tDockerSpawner,\n\t\tEffect.gen(function* () {\n\t\t\treturn yield* ChildProcessSpawner;\n\t\t}),\n\t);\n\nconst dockerLayer: Layer.Layer<DockerHost | DockerSpawner> = Layer.merge(\n\tlayerDockerHostDefault,\n\tlayerDockerSpawnerFromNode.pipe(\n\t\tLayer.provideMerge(\n\t\t\tNodeChildProcessSpawner.layer.pipe(\n\t\t\t\tLayer.provideMerge(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)),\n\t\t\t),\n\t\t),\n\t),\n);\n\nconst emptyBucket = (): ResourceBucket => ({\n\tcontainers: 0,\n\trunningContainers: 0,\n\tnetworks: 0,\n\tvolumes: 0,\n\timages: 0,\n});\n\n/** Public, parser-safe key for a `(app, stack)` group. Display-only —\n * internal grouping uses the structural `{app, stack}` tuple via the\n * `GroupBuckets` map below, so a slash inside `app` or `stack` cannot\n * produce a wrong tuple. The separator is a forward slash for human-\n * readable log/JSON output (`arena/main`); membership tests round-trip\n * through this same constructor so callers never re-split the key —\n * any potential `/`-in-app-or-stack ambiguity is resolved structurally\n * by `GroupBuckets`, not by string-splitting the key. */\nconst GROUP_KEY_SEPARATOR = '/';\n\nexport const lifecyclePruneGroupKey = (app: string, stack: string): string =>\n\t`${app}${GROUP_KEY_SEPARATOR}${stack}`;\n\n/** Map keyed on `(app, stack)` tuples without string encoding. Tuple\n * equality is achieved by interning each `(app, stack)` pair through\n * a nested `app → stack → bucket` index, so the map is collision-\n * free even when `app` or `stack` contains a separator character. */\nclass GroupBuckets {\n\tprivate readonly buckets = new Map<string, Map<string, ResourceBucket>>();\n\n\tget(identity: { readonly app: string; readonly stack: string }): ResourceBucket {\n\t\tlet perApp = this.buckets.get(identity.app);\n\t\tif (perApp === undefined) {\n\t\t\tperApp = new Map<string, ResourceBucket>();\n\t\t\tthis.buckets.set(identity.app, perApp);\n\t\t}\n\t\tlet bucket = perApp.get(identity.stack);\n\t\tif (bucket === undefined) {\n\t\t\tbucket = emptyBucket();\n\t\t\tperApp.set(identity.stack, bucket);\n\t\t}\n\t\treturn bucket;\n\t}\n\n\t*entries(): Iterable<{\n\t\treadonly app: string;\n\t\treadonly stack: string;\n\t\treadonly bucket: ResourceBucket;\n\t}> {\n\t\tfor (const [app, perApp] of this.buckets) {\n\t\t\tfor (const [stack, bucket] of perApp) {\n\t\t\t\tyield { app, stack, bucket };\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst readAppStack = (\n\tlabels: Readonly<Record<string, string>>,\n): { readonly app: string; readonly stack: string } | null => {\n\tconst app = labels[LabelKey.app];\n\tconst stack = labels[LabelKey.stack];\n\tif (app === undefined || app.length === 0) return null;\n\tif (stack === undefined || stack.length === 0) return null;\n\treturn { app, stack };\n};\n\nconst routerStackForContainer = (\n\tname: string,\n): { readonly app: string; readonly stack: string } => ({\n\tapp: ROUTER_SHARED_APP,\n\tstack: name,\n});\n\n/** Read the live-PID set for a stack from its roster.\n *\n * Path policy: `<runtimeRoot>/stacks/<stack>/roster.json`. This is the\n * same key the entire substrate uses — see `substrate/runtime/paths.ts`\n * (`path.join(root, 'stacks', identity.stack)`) — so the path here\n * carries no NEW cross-attribution risk beyond the substrate-wide\n * decision to key per-stack-disk-state on `stack` alone. (Two stacks\n * in DIFFERENT apps that pick the SAME stack name would already share\n * every other on-disk artifact — `state.json`, `cache/`, `snapshots/`,\n * `stack.lock`, `container-claims.json`. The Docker-level discriminator\n * is the container/network/volume NAME, which IS `<app>-<stack>-…`\n * composed inside each plugin; so cross-attribution at the daemon\n * level is prevented by name uniqueness even when disk state collides.)\n *\n * This function deliberately omits `app` from the path so the\n * prune sweeper agrees with every other consumer (`cli/main.ts`,\n * `cli/wirings/identity.ts`, `cli/wirings/up.ts`, the roster module\n * itself). Promoting `app` into the stack-state path is an\n * architectural change that must update all of those call sites in\n * lockstep — out of scope for this orchestrator. */\nconst livePidsForStack = (\n\truntimeRoot: string,\n\tstack: string,\n): Effect.Effect<ReadonlyArray<number>> => {\n\tconst rosterFile = joinPath(runtimeRoot, 'stacks', stack, 'roster.json');\n\tif (!existsSync(rosterFile)) return Effect.succeed([]);\n\t// Yield a fresh `LivenessProbeScope` so a recycled-PID corner case\n\t// (multiple holders sharing one pid in the same roster) forks the OS\n\t// liveness probe once per pid across this scan.\n\treturn Effect.gen(function* () {\n\t\tconst doc = yield* readRoster(rosterFile).pipe(\n\t\t\tlogDebugAndFallback(null, 'lifecycle-prune: roster read failed; treating as empty', {\n\t\t\t\trosterFile,\n\t\t\t}),\n\t\t);\n\t\tif (doc === null) return [];\n\t\tconst probe = yield* LivenessProbeScope;\n\t\tconst pids: Array<number> = [];\n\t\tfor (const holder of doc.holders) {\n\t\t\tconst live = yield* probe\n\t\t\t\t.probeHolderLiveness(holder)\n\t\t\t\t.pipe(\n\t\t\t\t\tlogDebugAndFallback(\n\t\t\t\t\t\t'alive' as const,\n\t\t\t\t\t\t'lifecycle-prune: liveness check failed; assuming alive',\n\t\t\t\t\t\t{ pid: holder.pid },\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\tif (live === 'alive') pids.push(holder.pid);\n\t\t}\n\t\treturn pids;\n\t}).pipe(Effect.provide(layerLivenessProbeScope));\n};\n\n/** True when the group is one of the two shared shapes the\n * orchestrator knows about: `_per-app_` (cross-stack-per-app shared)\n * or the router-singleton (`ROUTER_SHARED_APP`). Surfaces consult\n * this instead of recomputing the predicate. */\nexport const isSharedLifecyclePruneGroup = (app: string, stack: string): boolean =>\n\tstack === PER_APP_SHARED_STACK || app === ROUTER_SHARED_APP;\n\nconst sharedKindFor = (app: string, stack: string): SharedGroupKind | null => {\n\tif (app === ROUTER_SHARED_APP) return 'router';\n\tif (stack === PER_APP_SHARED_STACK) return 'per-app-shared';\n\treturn null;\n};\n\n/** True when the group represents a router-singleton resource set.\n * Exported so L4 surfaces can call this rather than re-implement the\n * router-stack naming predicate. */\nexport const isRouterLifecyclePruneGroup = (\n\tgroup: Pick<LifecyclePruneGroup, 'app' | 'stack'>,\n): boolean => group.app === ROUTER_SHARED_APP && group.stack.startsWith(`${ROUTER_SHARED_APP}-`);\n\n// Internal aliases for back-compat call sites in this file.\nconst isRouterGroup = isRouterLifecyclePruneGroup;\nconst isSharedGroup = isSharedLifecyclePruneGroup;\n\n/** Label-tuple match for the `removeDevstack*` sweepers — router-shared\n * resources stamp `{app: ROUTER_SHARED_APP, stack: <profile-name>}`\n * (see `traefik-container.ts:ensureNetwork`) and the inventory pass\n * buckets them under `routerStackForContainer(container.name)`, so for\n * router groups the tuple is `{app: ROUTER_SHARED_APP, stack: group.stack}`.\n * For non-router groups it's the bucket's literal `{app, stack}`. The\n * branch is structurally identical to the container-removal branch's\n * router-specific dispatch via `removeDevstackContainersByKindAndName`,\n * so dry-run inventory counts and real-run removal stay in lockstep\n * for router resources. Exported so tests can pin the dry-run ↔\n * real-run parity without standing up a Docker daemon. */\nexport const lifecyclePruneRemovalMatchTuple = (\n\tgroup: Pick<LifecyclePruneGroup, 'app' | 'stack'>,\n): { readonly app: string; readonly stack: string } =>\n\tisRouterGroup(group)\n\t\t? { app: ROUTER_SHARED_APP, stack: group.stack }\n\t\t: { app: group.app, stack: group.stack };\n\nconst matchTupleForGroup = lifecyclePruneRemovalMatchTuple;\n\n// -----------------------------------------------------------------------------\n// Orchestrator entry points\n// -----------------------------------------------------------------------------\n\nexport const collectLifecyclePruneInventory = (\n\toptions: LifecyclePruneOptions,\n): Effect.Effect<LifecyclePruneInventory, LifecyclePruneError> =>\n\tEffect.gen(function* () {\n\t\tconst [containers, routerContainers, networks, volumes, images] = yield* Effect.all(\n\t\t\t[\n\t\t\t\tlistDevstackContainers(),\n\t\t\t\tlistDevstackContainersByKind(ROUTER_KIND_LABEL_VALUE),\n\t\t\t\tlistDevstackNetworks(),\n\t\t\t\tlistDevstackVolumes(),\n\t\t\t\tlistDevstackImages(),\n\t\t\t],\n\t\t\t{ concurrency: 'unbounded' },\n\t\t).pipe(Effect.provide(dockerLayer), Effect.mapError(failPhase('inventory')));\n\n\t\tconst buckets = new GroupBuckets();\n\n\t\tfor (const container of containers) {\n\t\t\tconst identity = readAppStack(container.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tconst bucket = buckets.get(identity);\n\t\t\tbucket.containers += 1;\n\t\t\tif (container.state === 'running') bucket.runningContainers += 1;\n\t\t}\n\t\tfor (const container of routerContainers) {\n\t\t\tconst bucket = buckets.get(routerStackForContainer(container.name));\n\t\t\tbucket.containers += 1;\n\t\t\tif (container.state === 'running') bucket.runningContainers += 1;\n\t\t}\n\t\tfor (const network of networks) {\n\t\t\tconst identity = readAppStack(network.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).networks += 1;\n\t\t}\n\t\tfor (const volume of volumes) {\n\t\t\tconst identity = readAppStack(volume.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).volumes += 1;\n\t\t}\n\t\tfor (const image of images) {\n\t\t\tconst identity = readAppStack(image.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).images += 1;\n\t\t}\n\n\t\tconst groups: Array<LifecyclePruneGroup> = [];\n\t\tfor (const entry of buckets.entries()) {\n\t\t\tconst { app, stack, bucket } = entry;\n\t\t\tconst routerGroup = app === ROUTER_SHARED_APP;\n\t\t\tconst livePids = routerGroup ? [] : yield* livePidsForStack(options.runtimeRoot, stack);\n\t\t\t// Liveness is the OR of two independent signals so a live stack\n\t\t\t// is never mis-reported `live:false` (and so never swept by\n\t\t\t// `devstack prune --all`):\n\t\t\t//\n\t\t\t// 1. The per-stack roster carries a live holder (`livePids`\n\t\t\t// — the SAME `<root>/stacks/<stack>/roster.json` probe\n\t\t\t// `ensureNoLiveSupervisor` / `probeSupervisorPresence` use;\n\t\t\t// see `livePidsForStack`). Authoritative supervisor-presence\n\t\t\t// signal; also yields the PID detail.\n\t\t\t// 2. The Docker daemon reports a RUNNING container for the\n\t\t\t// group. A `devstack up` supervisor always has running\n\t\t\t// containers, so this catches a live stack even when the\n\t\t\t// roster probe transiently comes back empty — a missing/\n\t\t\t// stale roster file, a holder reaped by a peer sweep, or a\n\t\t\t// `ps -o lstart` start-time hiccup that harvests a live\n\t\t\t// holder as dead. Running containers are a daemon-\n\t\t\t// authoritative \"not idle\" signal, so this only ever ADDS\n\t\t\t// protection; it never reclassifies as idle a stack the\n\t\t\t// roster probe alone would have protected.\n\t\t\t//\n\t\t\t// The router branch already keyed liveness on\n\t\t\t// `runningContainers > 0` (router singletons carry no per-stack\n\t\t\t// roster); folding the same signal into normal groups makes the\n\t\t\t// classification uniform and closes the under-report.\n\t\t\tconst live = livePids.length > 0 || bucket.runningContainers > 0;\n\t\t\tgroups.push({\n\t\t\t\tkey: lifecyclePruneGroupKey(app, stack),\n\t\t\t\tapp,\n\t\t\t\tstack,\n\t\t\t\tlive,\n\t\t\t\tlivePids,\n\t\t\t\tshared: isSharedGroup(app, stack),\n\t\t\t\tsharedKind: sharedKindFor(app, stack),\n\t\t\t\tautoPrunable: isRouterGroup({ app, stack }),\n\t\t\t\tcontainers: bucket.containers,\n\t\t\t\trunningContainers: bucket.runningContainers,\n\t\t\t\tnetworks: bucket.networks,\n\t\t\t\tvolumes: bucket.volumes,\n\t\t\t\timages: bucket.images,\n\t\t\t});\n\t\t}\n\n\t\tgroups.sort((a, b) => {\n\t\t\tif (a.app !== b.app) return a.app < b.app ? -1 : 1;\n\t\t\treturn a.stack < b.stack ? -1 : a.stack > b.stack ? 1 : 0;\n\t\t});\n\n\t\treturn { groups };\n\t}).pipe(Effect.withSpan('orchestrator.lifecycle-prune.inventory'));\n\nconst selectedGroups = (\n\tinventory: LifecyclePruneInventory,\n\tselection: LifecyclePruneSelection,\n): ReadonlyArray<LifecyclePruneGroup> => {\n\tconst selected = new Set(selection.groupKeys);\n\treturn inventory.groups.filter((group) => selected.has(group.key));\n};\n\n/** Defensive enforcement of the per-app-shared pinning rule for a\n * caller-supplied selection. Resolves `selection.groupKeys` against\n * `inventory` and drops any `per-app-shared` group whose app still has\n * a live sibling — reusing the SAME predicate as\n * `defaultLifecyclePruneSelection` so a selection built outside the\n * default path (interactive picker, scripted, programmatic) can never\n * remove a shared group a live sibling depends on. Exported so the\n * invariant is unit-testable without a Docker daemon. */\nexport const enforcePinnedLifecyclePruneSelection = (\n\tinventory: LifecyclePruneInventory,\n\tselection: LifecyclePruneSelection,\n): ReadonlyArray<LifecyclePruneGroup> => {\n\tconst pinned = lifecyclePruneAppsWithLiveSiblings(inventory);\n\treturn selectedGroups(inventory, selection).filter(\n\t\t(group) => !(group.sharedKind === 'per-app-shared' && pinned.has(group.app)),\n\t);\n};\n\n/** Default resource scope for the lifecycle-prune orchestrator —\n * containers + networks + volumes, never images. Surfaces consume\n * this directly so the default never drifts between L3 and L4. */\nexport const DEFAULT_LIFECYCLE_PRUNE_RESOURCES: LifecyclePruneResourceScope = {\n\tcontainers: true,\n\tnetworks: true,\n\tvolumes: true,\n\timages: false,\n};\n\n/** Apps with at least one live non-shared group — their `_per-app_`\n * shared resources stay pinned because something under the app is\n * still running. Exported so L4 surfaces consume the orchestrator's\n * pinning predicate. */\nexport const lifecyclePruneAppsWithLiveSiblings = (\n\tinventory: LifecyclePruneInventory,\n): ReadonlySet<string> => {\n\tconst apps = new Set<string>();\n\tfor (const group of inventory.groups) {\n\t\tif (!group.shared && group.live) apps.add(group.app);\n\t}\n\treturn apps;\n};\n\nconst groupHasResource = (\n\tgroup: LifecyclePruneGroup,\n\tresources: LifecyclePruneResourceScope,\n): boolean =>\n\t(resources.containers && group.containers > 0) ||\n\t(resources.networks && group.networks > 0) ||\n\t(resources.volumes && group.volumes > 0) ||\n\t(resources.images && group.images > 0);\n\n/** Default selection: every non-live group whose shared shape is\n * prunable in non-interactive flows. Surfaces and the live-supervisor\n * command handler share this so the orchestrator owns the policy. */\nexport const defaultLifecyclePruneSelection = (\n\tinventory: LifecyclePruneInventory,\n\tresources: LifecyclePruneResourceScope = DEFAULT_LIFECYCLE_PRUNE_RESOURCES,\n): ReadonlyArray<string> => {\n\tconst pinned = lifecyclePruneAppsWithLiveSiblings(inventory);\n\tconst keys: string[] = [];\n\tfor (const group of inventory.groups) {\n\t\tif (group.live) continue;\n\t\tif (!groupHasResource(group, resources)) continue;\n\t\tif (group.sharedKind === 'per-app-shared' && pinned.has(group.app)) continue;\n\t\tif (group.shared && !group.autoPrunable && group.sharedKind !== 'per-app-shared') continue;\n\t\tkeys.push(group.key);\n\t}\n\treturn keys;\n};\n\nexport const runLifecyclePrune = (\n\toptions: LifecyclePruneOptions,\n\tselection: LifecyclePruneSelection,\n): Effect.Effect<LifecyclePruneSummary, LifecyclePruneError> =>\n\tEffect.gen(function* () {\n\t\tconst inventory = yield* collectLifecyclePruneInventory(options);\n\t\t// Defensive enforcement: a caller-supplied selection (interactive\n\t\t// picker, scripted `--all`, or a programmatic caller) must never\n\t\t// remove a `per-app-shared` group pinned by a live sibling. The\n\t\t// default selection already drops these (see\n\t\t// `defaultLifecyclePruneSelection`); re-applying the SAME predicate\n\t\t// here guarantees the rule holds even when the selection was built\n\t\t// elsewhere.\n\t\tconst groups = enforcePinnedLifecyclePruneSelection(inventory, selection);\n\t\tlet skippedLiveGroups = 0;\n\t\tlet containersRemoved = 0;\n\t\tlet networksRemoved = 0;\n\t\tlet networksSkipped = 0;\n\t\tlet volumesRemoved = 0;\n\t\tlet imagesRemoved = 0;\n\n\t\tconst prunableGroups: Array<LifecyclePruneGroup> = [];\n\t\tfor (const group of groups) {\n\t\t\tif (group.live) {\n\t\t\t\tskippedLiveGroups += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (selection.dryRun) {\n\t\t\t\tprunableGroups.push(group);\n\t\t\t\tif (selection.resources.containers) containersRemoved += group.containers;\n\t\t\t\tif (selection.resources.networks) networksRemoved += group.networks;\n\t\t\t\tif (selection.resources.volumes) volumesRemoved += group.volumes;\n\t\t\t\tif (selection.resources.images) imagesRemoved += group.images;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// TOCTOU close: `collectLifecyclePruneInventory` probed liveness\n\t\t\t// ONCE at the top of this run. A stack that booted in the window\n\t\t\t// between that snapshot and here (a concurrent `devstack up` of a\n\t\t\t// previously-dead stack) would have its containers/networks/volumes\n\t\t\t// removed out from under the freshly-started supervisor. Re-probe\n\t\t\t// each non-router victim's roster via the SAME helper the inventory\n\t\t\t// pass used (`livePidsForStack`) immediately before committing it to\n\t\t\t// removal, and skip any group that came alive since. Router groups\n\t\t\t// carry no per-stack roster (their inventory liveness is\n\t\t\t// `runningContainers`), so they keep the snapshot classification —\n\t\t\t// mirroring the `routerGroup` branch in the inventory pass.\n\t\t\tif (!isRouterGroup(group)) {\n\t\t\t\tconst livePids = yield* livePidsForStack(options.runtimeRoot, group.stack);\n\t\t\t\tif (livePids.length > 0) {\n\t\t\t\t\tskippedLiveGroups += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tprunableGroups.push(group);\n\t\t}\n\n\t\tconst foreignNetworkHolders: Array<ForeignNetworkHolder> = [];\n\t\tconst staleNetworkEndpoints: Array<StaleNetworkEndpoint> = [];\n\t\t// Provide `dockerLayer` ONCE around the whole removal section rather\n\t\t// than re-providing it per group per resource. The inventory pass\n\t\t// already provides it once for its parallel scan; the removal loops\n\t\t// mirror that so the layer's docker-host resolution is built a single\n\t\t// time, not rebuilt for every container/network/volume/image removal.\n\t\t// The per-call `Effect.mapError(failPhase(...))` stays inside each\n\t\t// loop so each removal still carries its phase-specific tag.\n\t\tyield* Effect.gen(function* () {\n\t\t\tif (!selection.dryRun && selection.resources.containers) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tif (isRouterGroup(group)) {\n\t\t\t\t\t\tcontainersRemoved += yield* removeDevstackContainersByKindAndName(\n\t\t\t\t\t\t\tROUTER_KIND_LABEL_VALUE,\n\t\t\t\t\t\t\tgroup.stack,\n\t\t\t\t\t\t).pipe(Effect.mapError(failPhase('remove-containers')));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst match = { app: group.app, stack: group.stack };\n\t\t\t\t\t\tcontainersRemoved += yield* removeDevstackContainers(match).pipe(\n\t\t\t\t\t\t\tEffect.mapError(failPhase('remove-containers')),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.networks) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\tconst result = yield* removeDevstackNetworksBestEffort(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-networks')),\n\t\t\t\t\t);\n\t\t\t\t\tnetworksRemoved += result.removed;\n\t\t\t\t\tnetworksSkipped += result.skippedInUse;\n\t\t\t\t\tfor (const holder of result.foreignHolders) foreignNetworkHolders.push(holder);\n\t\t\t\t\tfor (const ep of result.staleEndpoints) staleNetworkEndpoints.push(ep);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.volumes) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\tvolumesRemoved += yield* removeDevstackVolumes(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-volumes')),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.images) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\timagesRemoved += yield* removeDevstackImages(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-images')),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}).pipe(Effect.provide(dockerLayer));\n\n\t\tif (!selection.dryRun) {\n\t\t\tyield* Effect.gen(function* () {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tif (!isRouterGroup(group)) continue;\n\t\t\t\t\tyield* removeRouterProfileStateForDockerStack({\n\t\t\t\t\t\truntimeRoot: options.runtimeRoot,\n\t\t\t\t\t\trouterStack: group.stack,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}).pipe(\n\t\t\t\tEffect.provide(NodeFileSystem.layer),\n\t\t\t\tEffect.withSpan('orchestrator.lifecycle-prune.removeRouterProfileState'),\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tinspectedGroups: groups.length,\n\t\t\t// Groups actually selected for pruning — `groups.length` minus\n\t\t\t// the live ones skipped above, NOT the inspected total.\n\t\t\tselectedGroups: prunableGroups.length,\n\t\t\tskippedLiveGroups,\n\t\t\tcontainersRemoved,\n\t\t\tnetworksRemoved,\n\t\t\tnetworksSkipped,\n\t\t\tvolumesRemoved,\n\t\t\timagesRemoved,\n\t\t\tforeignNetworkHolders,\n\t\t\tstaleNetworkEndpoints,\n\t\t};\n\t}).pipe(Effect.withSpan('orchestrator.lifecycle-prune.run'));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["joinPath"],"sources":["../../../src/orchestrators/lifecycle-prune/index.ts"],"sourcesContent":["// Lifecycle prune orchestrator — cross-stack Docker sweep.\n//\n// Architecture § L3 orchestrators. Walks the L1 docker inventory (the\n// reference `ContainerRuntime` adapter), groups labelled resources by\n// `(app, stack)`, consults the cross-process roster + claim ledger for\n// liveness, and dispatches removal through the sibling router cleanup\n// helper for router-shared resources.\n//\n// Name-blindness: this orchestrator never names a service. It walks\n// the engine label tuple (`LabelKey.app` / `LabelKey.stack`) which is\n// L1-substrate-level vocabulary and dispatches on the\n// `ROUTER_SHARED_APP` sentinel exported by the router orchestrator.\n//\n// Consumers: `cli/prune-direct.ts` (the L4-adjacent CLI infrastructure\n// for the `devstack prune` verb) calls `runLifecyclePrune` /\n// `collectLifecyclePruneInventory`. No L4 surface module imports L1\n// runtime adapters directly; that boundary lives here.\n\nimport { existsSync } from 'node:fs';\nimport { join as joinPath } from 'node:path';\n\nimport * as NodeChildProcessSpawner from '@effect/platform-node/NodeChildProcessSpawner';\nimport * as NodeFileSystem from '@effect/platform-node/NodeFileSystem';\nimport * as NodePath from '@effect/platform-node/NodePath';\nimport { Effect, Layer } from 'effect';\nimport { ChildProcessSpawner } from 'effect/unstable/process/ChildProcessSpawner';\n\nimport {\n\tDockerHost,\n\tDockerSpawner,\n\ttype ForeignNetworkHolder,\n\tLabelKey,\n\tlayerDockerHostDefault,\n\tlistDevstackContainers,\n\tlistDevstackContainersByKind,\n\tlistDevstackImages,\n\tlistDevstackNetworks,\n\tlistDevstackVolumes,\n\tremoveDevstackContainers,\n\tremoveDevstackContainersByKindAndName,\n\tremoveDevstackImages,\n\tremoveDevstackNetworksBestEffort,\n\tremoveDevstackVolumes,\n\ttype StaleNetworkEndpoint,\n} from '../../runtime/docker/index.ts';\nimport {\n\tlayerLivenessProbeScope,\n\tLivenessProbeScope,\n\treadRoster,\n} from '../../substrate/runtime/cross-process/index.ts';\nimport { PER_APP_SHARED_STACK } from '../../substrate/runtime/managed-container.ts';\nimport { logDebugAndFallback } from '../../substrate/runtime/observability/index.ts';\nimport { ROUTER_SHARED_APP, removeRouterProfileStateForDockerStack } from '../router/cleanup.ts';\n// Import the kind sentinel from its canonical source rather than the\n// `traefik-container.ts` re-export — the router-singleton label literals\n// live in `sentinels.ts`, and lifecycle-prune only needs the constant\n// (not the container ops), so depending on the sentinel module keeps the\n// import edge minimal and the source-of-truth direct.\nimport { ROUTER_KIND_LABEL_VALUE } from '../router/sentinels.ts';\nimport { failPhase, type LifecyclePruneError } from './errors.ts';\n\nexport { LifecyclePruneError, LifecyclePrunePhase } from './errors.ts';\n\n// -----------------------------------------------------------------------------\n// Public shapes — mirror `surfaces/cli/commands/prune.ts` field-for-field so\n// the verb dispatcher can consume the orchestrator output without an adapter.\n// -----------------------------------------------------------------------------\n\nexport interface LifecyclePruneOptions {\n\treadonly runtimeRoot: string;\n}\n\nexport interface LifecyclePruneGroup {\n\treadonly key: string;\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly live: boolean;\n\treadonly livePids: ReadonlyArray<number>;\n\treadonly shared: boolean;\n\t/** Discriminator for shared-resource groups. `'per-app-shared'`\n\t * groups (the `_per-app_` synthetic stack) stay pinned while any\n\t * sibling stack under the same app is live; `'router'` groups are\n\t * auto-prunable when no app pins them. `null` for normal groups. */\n\treadonly sharedKind: SharedGroupKind | null;\n\t/** True when the group represents a router-shared resource set that\n\t * is auto-prunable in non-interactive flows (`devstack prune --all`).\n\t * Computed by the orchestrator so surfaces never recompute the\n\t * router-stack naming predicate. */\n\treadonly autoPrunable: boolean;\n\treadonly containers: number;\n\treadonly runningContainers: number;\n\treadonly networks: number;\n\treadonly volumes: number;\n\treadonly images: number;\n}\n\n/** Kind of a shared-resource group. `'per-app-shared'` groups belong\n * to the `PER_APP_SHARED_STACK` synthetic stack under a real app and\n * stay pinned while a sibling stack is live. `'router'` groups are\n * router-singleton resources. */\nexport type SharedGroupKind = 'per-app-shared' | 'router';\n\nexport interface LifecyclePruneInventory {\n\treadonly groups: ReadonlyArray<LifecyclePruneGroup>;\n}\n\nexport interface LifecyclePruneResourceScope {\n\treadonly containers: boolean;\n\treadonly networks: boolean;\n\treadonly volumes: boolean;\n\treadonly images: boolean;\n}\n\nexport interface LifecyclePruneSelection {\n\treadonly groupKeys: ReadonlyArray<string>;\n\treadonly resources: LifecyclePruneResourceScope;\n\treadonly dryRun: boolean;\n}\n\nexport interface LifecyclePruneSummary {\n\treadonly inspectedGroups: number;\n\treadonly selectedGroups: number;\n\treadonly skippedLiveGroups: number;\n\treadonly containersRemoved: number;\n\treadonly networksRemoved: number;\n\treadonly networksSkipped: number;\n\treadonly volumesRemoved: number;\n\treadonly imagesRemoved: number;\n\t/** Non-devstack containers still holding networks that prune could\n\t * not remove. Empty when every network came down cleanly. */\n\treadonly foreignNetworkHolders: ReadonlyArray<ForeignNetworkHolder>;\n\t/** Endpoints Docker insists exist on a network but which no CLI/API\n\t * path can address — symptom of a Docker engine bug (the bridge\n\t * driver leaked endpoint metadata after a container was reaped).\n\t * Only a Docker daemon restart clears these. */\n\treadonly staleNetworkEndpoints: ReadonlyArray<StaleNetworkEndpoint>;\n}\n\n// -----------------------------------------------------------------------------\n// Internal helpers\n// -----------------------------------------------------------------------------\n\ntype ResourceBucket = {\n\tcontainers: number;\n\trunningContainers: number;\n\tnetworks: number;\n\tvolumes: number;\n\timages: number;\n};\n\nconst layerDockerSpawnerFromNode: Layer.Layer<DockerSpawner, never, ChildProcessSpawner> =\n\tLayer.effect(\n\t\tDockerSpawner,\n\t\tEffect.gen(function* () {\n\t\t\treturn yield* ChildProcessSpawner;\n\t\t}),\n\t);\n\nconst dockerLayer: Layer.Layer<DockerHost | DockerSpawner> = Layer.merge(\n\tlayerDockerHostDefault,\n\tlayerDockerSpawnerFromNode.pipe(\n\t\tLayer.provideMerge(\n\t\t\tNodeChildProcessSpawner.layer.pipe(\n\t\t\t\tLayer.provideMerge(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)),\n\t\t\t),\n\t\t),\n\t),\n);\n\nconst emptyBucket = (): ResourceBucket => ({\n\tcontainers: 0,\n\trunningContainers: 0,\n\tnetworks: 0,\n\tvolumes: 0,\n\timages: 0,\n});\n\n/** Public, parser-safe key for a `(app, stack)` group. Display-only —\n * internal grouping uses the structural `{app, stack}` tuple via the\n * `GroupBuckets` map below, so a slash inside `app` or `stack` cannot\n * produce a wrong tuple. The separator is a forward slash for human-\n * readable log/JSON output (`arena/main`); membership tests round-trip\n * through this same constructor so callers never re-split the key —\n * any potential `/`-in-app-or-stack ambiguity is resolved structurally\n * by `GroupBuckets`, not by string-splitting the key. */\nconst GROUP_KEY_SEPARATOR = '/';\n\nexport const lifecyclePruneGroupKey = (app: string, stack: string): string =>\n\t`${app}${GROUP_KEY_SEPARATOR}${stack}`;\n\n/** Map keyed on `(app, stack)` tuples without string encoding. Tuple\n * equality is achieved by interning each `(app, stack)` pair through\n * a nested `app → stack → bucket` index, so the map is collision-\n * free even when `app` or `stack` contains a separator character. */\nclass GroupBuckets {\n\tprivate readonly buckets = new Map<string, Map<string, ResourceBucket>>();\n\n\tget(identity: { readonly app: string; readonly stack: string }): ResourceBucket {\n\t\tlet perApp = this.buckets.get(identity.app);\n\t\tif (perApp === undefined) {\n\t\t\tperApp = new Map<string, ResourceBucket>();\n\t\t\tthis.buckets.set(identity.app, perApp);\n\t\t}\n\t\tlet bucket = perApp.get(identity.stack);\n\t\tif (bucket === undefined) {\n\t\t\tbucket = emptyBucket();\n\t\t\tperApp.set(identity.stack, bucket);\n\t\t}\n\t\treturn bucket;\n\t}\n\n\t*entries(): Iterable<{\n\t\treadonly app: string;\n\t\treadonly stack: string;\n\t\treadonly bucket: ResourceBucket;\n\t}> {\n\t\tfor (const [app, perApp] of this.buckets) {\n\t\t\tfor (const [stack, bucket] of perApp) {\n\t\t\t\tyield { app, stack, bucket };\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst readAppStack = (\n\tlabels: Readonly<Record<string, string>>,\n): { readonly app: string; readonly stack: string } | null => {\n\tconst app = labels[LabelKey.app];\n\tconst stack = labels[LabelKey.stack];\n\tif (app === undefined || app.length === 0) return null;\n\tif (stack === undefined || stack.length === 0) return null;\n\treturn { app, stack };\n};\n\nconst routerStackForContainer = (\n\tname: string,\n): { readonly app: string; readonly stack: string } => ({\n\tapp: ROUTER_SHARED_APP,\n\tstack: name,\n});\n\n/** Read the live-PID set for a stack from its roster.\n *\n * Path policy: `<runtimeRoot>/stacks/<stack>/roster.json`. This is the\n * same key the entire substrate uses — see `substrate/runtime/paths.ts`\n * (`path.join(root, 'stacks', identity.stack)`) — so the path here\n * carries no NEW cross-attribution risk beyond the substrate-wide\n * decision to key per-stack-disk-state on `stack` alone. (Two stacks\n * in DIFFERENT apps that pick the SAME stack name would already share\n * every other on-disk artifact — `state.json`, `cache/`, `snapshots/`,\n * `stack.lock`. The Docker-level discriminator\n * is the container/network/volume NAME, which IS `<app>-<stack>-…`\n * composed inside each plugin; so cross-attribution at the daemon\n * level is prevented by name uniqueness even when disk state collides.)\n *\n * This function deliberately omits `app` from the path so the\n * prune sweeper agrees with every other consumer (`cli/main.ts`,\n * `cli/wirings/identity.ts`, `cli/wirings/up.ts`, the roster module\n * itself). Promoting `app` into the stack-state path is an\n * architectural change that must update all of those call sites in\n * lockstep — out of scope for this orchestrator. */\n// The cross-stack lifecycle-prune sweep consults per-stack liveness. Each\n// pass runs under a FRESH `LivenessProbeScope` (provided inline below) so\n// a recycled-PID corner case forks the OS liveness probe at most once per\n// pid per pass. Same `(app, stack)` grouping, same L4-user-command\n// trigger, same per-stack roster probe.\n\nconst livePidsForStack = (\n\truntimeRoot: string,\n\tstack: string,\n): Effect.Effect<ReadonlyArray<number>> => {\n\tconst rosterFile = joinPath(runtimeRoot, 'stacks', stack, 'roster.json');\n\tif (!existsSync(rosterFile)) return Effect.succeed([]);\n\t// Run under a fresh `LivenessProbeScope` so a recycled-PID corner case\n\t// (multiple holders sharing one pid in the same roster) forks the OS\n\t// liveness probe once per pid across this scan.\n\treturn Effect.gen(function* () {\n\t\tconst doc = yield* readRoster(rosterFile).pipe(\n\t\t\tlogDebugAndFallback(null, 'lifecycle-prune: roster read failed; treating as empty', {\n\t\t\t\trosterFile,\n\t\t\t}),\n\t\t);\n\t\tif (doc === null) return [];\n\t\tconst probe = yield* LivenessProbeScope;\n\t\tconst pids: Array<number> = [];\n\t\tfor (const holder of doc.holders) {\n\t\t\tconst live = yield* probe\n\t\t\t\t.probeHolderLiveness(holder)\n\t\t\t\t.pipe(\n\t\t\t\t\tlogDebugAndFallback(\n\t\t\t\t\t\t'alive' as const,\n\t\t\t\t\t\t'lifecycle-prune: liveness check failed; assuming alive',\n\t\t\t\t\t\t{ pid: holder.pid },\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\tif (live === 'alive') pids.push(holder.pid);\n\t\t}\n\t\treturn pids;\n\t}).pipe(Effect.provide(layerLivenessProbeScope));\n};\n\n/** True when the group is one of the two shared shapes the\n * orchestrator knows about: `_per-app_` (cross-stack-per-app shared)\n * or the router-singleton (`ROUTER_SHARED_APP`). */\nconst isSharedGroup = (app: string, stack: string): boolean =>\n\tstack === PER_APP_SHARED_STACK || app === ROUTER_SHARED_APP;\n\nconst sharedKindFor = (app: string, stack: string): SharedGroupKind | null => {\n\tif (app === ROUTER_SHARED_APP) return 'router';\n\tif (stack === PER_APP_SHARED_STACK) return 'per-app-shared';\n\treturn null;\n};\n\n/** True when the group represents a router-singleton resource set.\n * Exported so L4 surfaces can call this rather than re-implement the\n * router-stack naming predicate. */\nexport const isRouterLifecyclePruneGroup = (\n\tgroup: Pick<LifecyclePruneGroup, 'app' | 'stack'>,\n): boolean => group.app === ROUTER_SHARED_APP && group.stack.startsWith(`${ROUTER_SHARED_APP}-`);\n\n// Internal alias for back-compat call sites in this file.\nconst isRouterGroup = isRouterLifecyclePruneGroup;\n\n/** Label-tuple match for the `removeDevstack*` sweepers — router-shared\n * resources stamp `{app: ROUTER_SHARED_APP, stack: <profile-name>}`\n * (see `traefik-container.ts:ensureNetwork`) and the inventory pass\n * buckets them under `routerStackForContainer(container.name)`, so for\n * router groups the tuple is `{app: ROUTER_SHARED_APP, stack: group.stack}`.\n * For non-router groups it's the bucket's literal `{app, stack}`. The\n * branch is structurally identical to the container-removal branch's\n * router-specific dispatch via `removeDevstackContainersByKindAndName`,\n * so dry-run inventory counts and real-run removal stay in lockstep\n * for router resources. Exported so tests can pin the dry-run ↔\n * real-run parity without standing up a Docker daemon. */\nexport const lifecyclePruneRemovalMatchTuple = (\n\tgroup: Pick<LifecyclePruneGroup, 'app' | 'stack'>,\n): { readonly app: string; readonly stack: string } =>\n\tisRouterGroup(group)\n\t\t? { app: ROUTER_SHARED_APP, stack: group.stack }\n\t\t: { app: group.app, stack: group.stack };\n\nconst matchTupleForGroup = lifecyclePruneRemovalMatchTuple;\n\n// -----------------------------------------------------------------------------\n// Orchestrator entry points\n// -----------------------------------------------------------------------------\n\nexport const collectLifecyclePruneInventory = (\n\toptions: LifecyclePruneOptions,\n): Effect.Effect<LifecyclePruneInventory, LifecyclePruneError> =>\n\tEffect.gen(function* () {\n\t\tconst [containers, routerContainers, networks, volumes, images] = yield* Effect.all(\n\t\t\t[\n\t\t\t\tlistDevstackContainers(),\n\t\t\t\tlistDevstackContainersByKind(ROUTER_KIND_LABEL_VALUE),\n\t\t\t\tlistDevstackNetworks(),\n\t\t\t\tlistDevstackVolumes(),\n\t\t\t\tlistDevstackImages(),\n\t\t\t],\n\t\t\t{ concurrency: 'unbounded' },\n\t\t).pipe(Effect.provide(dockerLayer), Effect.mapError(failPhase('inventory')));\n\n\t\tconst buckets = new GroupBuckets();\n\n\t\tfor (const container of containers) {\n\t\t\tconst identity = readAppStack(container.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tconst bucket = buckets.get(identity);\n\t\t\tbucket.containers += 1;\n\t\t\tif (container.state === 'running') bucket.runningContainers += 1;\n\t\t}\n\t\tfor (const container of routerContainers) {\n\t\t\tconst bucket = buckets.get(routerStackForContainer(container.name));\n\t\t\tbucket.containers += 1;\n\t\t\tif (container.state === 'running') bucket.runningContainers += 1;\n\t\t}\n\t\tfor (const network of networks) {\n\t\t\tconst identity = readAppStack(network.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).networks += 1;\n\t\t}\n\t\tfor (const volume of volumes) {\n\t\t\tconst identity = readAppStack(volume.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).volumes += 1;\n\t\t}\n\t\tfor (const image of images) {\n\t\t\tconst identity = readAppStack(image.labels);\n\t\t\tif (identity === null) continue;\n\t\t\tbuckets.get(identity).images += 1;\n\t\t}\n\n\t\tconst groups: Array<LifecyclePruneGroup> = [];\n\t\tfor (const entry of buckets.entries()) {\n\t\t\tconst { app, stack, bucket } = entry;\n\t\t\tconst routerGroup = app === ROUTER_SHARED_APP;\n\t\t\tconst livePids = routerGroup ? [] : yield* livePidsForStack(options.runtimeRoot, stack);\n\t\t\t// Liveness is the OR of two independent signals so a live stack\n\t\t\t// is never mis-reported `live:false` (and so never swept by\n\t\t\t// `devstack prune --all`):\n\t\t\t//\n\t\t\t// 1. The per-stack roster carries a live holder (`livePids`\n\t\t\t// — the SAME `<root>/stacks/<stack>/roster.json` probe\n\t\t\t// `ensureNoLiveSupervisor` / `probeSupervisorPresence` use;\n\t\t\t// see `livePidsForStack`). Authoritative supervisor-presence\n\t\t\t// signal; also yields the PID detail.\n\t\t\t// 2. The Docker daemon reports a RUNNING container for the\n\t\t\t// group. A `devstack up` supervisor always has running\n\t\t\t// containers, so this catches a live stack even when the\n\t\t\t// roster probe transiently comes back empty — a missing/\n\t\t\t// stale roster file, a holder reaped by a peer sweep, or a\n\t\t\t// `ps -o lstart` start-time hiccup that harvests a live\n\t\t\t// holder as dead. Running containers are a daemon-\n\t\t\t// authoritative \"not idle\" signal, so this only ever ADDS\n\t\t\t// protection; it never reclassifies as idle a stack the\n\t\t\t// roster probe alone would have protected.\n\t\t\t//\n\t\t\t// The router branch already keyed liveness on\n\t\t\t// `runningContainers > 0` (router singletons carry no per-stack\n\t\t\t// roster); folding the same signal into normal groups makes the\n\t\t\t// classification uniform and closes the under-report.\n\t\t\tconst live = livePids.length > 0 || bucket.runningContainers > 0;\n\t\t\tgroups.push({\n\t\t\t\tkey: lifecyclePruneGroupKey(app, stack),\n\t\t\t\tapp,\n\t\t\t\tstack,\n\t\t\t\tlive,\n\t\t\t\tlivePids,\n\t\t\t\tshared: isSharedGroup(app, stack),\n\t\t\t\tsharedKind: sharedKindFor(app, stack),\n\t\t\t\tautoPrunable: isRouterGroup({ app, stack }),\n\t\t\t\tcontainers: bucket.containers,\n\t\t\t\trunningContainers: bucket.runningContainers,\n\t\t\t\tnetworks: bucket.networks,\n\t\t\t\tvolumes: bucket.volumes,\n\t\t\t\timages: bucket.images,\n\t\t\t});\n\t\t}\n\n\t\tgroups.sort((a, b) => {\n\t\t\tif (a.app !== b.app) return a.app < b.app ? -1 : 1;\n\t\t\treturn a.stack < b.stack ? -1 : a.stack > b.stack ? 1 : 0;\n\t\t});\n\n\t\treturn { groups };\n\t});\n\nconst selectedGroups = (\n\tinventory: LifecyclePruneInventory,\n\tselection: LifecyclePruneSelection,\n): ReadonlyArray<LifecyclePruneGroup> => {\n\tconst selected = new Set(selection.groupKeys);\n\treturn inventory.groups.filter((group) => selected.has(group.key));\n};\n\n/** Defensive enforcement of the per-app-shared pinning rule for a\n * caller-supplied selection. Resolves `selection.groupKeys` against\n * `inventory` and drops any `per-app-shared` group whose app still has\n * a live sibling — reusing the SAME predicate as\n * `defaultLifecyclePruneSelection` so a selection built outside the\n * default path (interactive picker, scripted, programmatic) can never\n * remove a shared group a live sibling depends on. Exported so the\n * invariant is unit-testable without a Docker daemon. */\nexport const enforcePinnedLifecyclePruneSelection = (\n\tinventory: LifecyclePruneInventory,\n\tselection: LifecyclePruneSelection,\n): ReadonlyArray<LifecyclePruneGroup> => {\n\tconst pinned = lifecyclePruneAppsWithLiveSiblings(inventory);\n\treturn selectedGroups(inventory, selection).filter(\n\t\t(group) => !(group.sharedKind === 'per-app-shared' && pinned.has(group.app)),\n\t);\n};\n\n/** Default resource scope for the lifecycle-prune orchestrator —\n * containers + networks + volumes, never images. Surfaces consume\n * this directly so the default never drifts between L3 and L4. */\nexport const DEFAULT_LIFECYCLE_PRUNE_RESOURCES: LifecyclePruneResourceScope = {\n\tcontainers: true,\n\tnetworks: true,\n\tvolumes: true,\n\timages: false,\n};\n\n/** Apps with at least one live non-shared group — their `_per-app_`\n * shared resources stay pinned because something under the app is\n * still running. Exported so L4 surfaces consume the orchestrator's\n * pinning predicate. */\nexport const lifecyclePruneAppsWithLiveSiblings = (\n\tinventory: LifecyclePruneInventory,\n): ReadonlySet<string> => {\n\tconst apps = new Set<string>();\n\tfor (const group of inventory.groups) {\n\t\tif (!group.shared && group.live) apps.add(group.app);\n\t}\n\treturn apps;\n};\n\nconst groupHasResource = (\n\tgroup: LifecyclePruneGroup,\n\tresources: LifecyclePruneResourceScope,\n): boolean =>\n\t(resources.containers && group.containers > 0) ||\n\t(resources.networks && group.networks > 0) ||\n\t(resources.volumes && group.volumes > 0) ||\n\t(resources.images && group.images > 0);\n\n/** Default selection: every non-live group whose shared shape is\n * prunable in non-interactive flows. Surfaces and the live-supervisor\n * command handler share this so the orchestrator owns the policy. */\nexport const defaultLifecyclePruneSelection = (\n\tinventory: LifecyclePruneInventory,\n\tresources: LifecyclePruneResourceScope = DEFAULT_LIFECYCLE_PRUNE_RESOURCES,\n): ReadonlyArray<string> => {\n\tconst pinned = lifecyclePruneAppsWithLiveSiblings(inventory);\n\tconst keys: string[] = [];\n\tfor (const group of inventory.groups) {\n\t\tif (group.live) continue;\n\t\tif (!groupHasResource(group, resources)) continue;\n\t\tif (group.sharedKind === 'per-app-shared' && pinned.has(group.app)) continue;\n\t\tif (group.shared && !group.autoPrunable && group.sharedKind !== 'per-app-shared') continue;\n\t\tkeys.push(group.key);\n\t}\n\treturn keys;\n};\n\nexport const runLifecyclePrune = (\n\toptions: LifecyclePruneOptions,\n\tselection: LifecyclePruneSelection,\n): Effect.Effect<LifecyclePruneSummary, LifecyclePruneError> =>\n\tEffect.gen(function* () {\n\t\tconst inventory = yield* collectLifecyclePruneInventory(options);\n\t\t// Defensive enforcement: a caller-supplied selection (interactive\n\t\t// picker, scripted `--all`, or a programmatic caller) must never\n\t\t// remove a `per-app-shared` group pinned by a live sibling. The\n\t\t// default selection already drops these (see\n\t\t// `defaultLifecyclePruneSelection`); re-applying the SAME predicate\n\t\t// here guarantees the rule holds even when the selection was built\n\t\t// elsewhere.\n\t\tconst groups = enforcePinnedLifecyclePruneSelection(inventory, selection);\n\t\tlet skippedLiveGroups = 0;\n\t\tlet containersRemoved = 0;\n\t\tlet networksRemoved = 0;\n\t\tlet networksSkipped = 0;\n\t\tlet volumesRemoved = 0;\n\t\tlet imagesRemoved = 0;\n\n\t\tconst prunableGroups: Array<LifecyclePruneGroup> = [];\n\t\tfor (const group of groups) {\n\t\t\tif (group.live) {\n\t\t\t\tskippedLiveGroups += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (selection.dryRun) {\n\t\t\t\tprunableGroups.push(group);\n\t\t\t\tif (selection.resources.containers) containersRemoved += group.containers;\n\t\t\t\tif (selection.resources.networks) networksRemoved += group.networks;\n\t\t\t\tif (selection.resources.volumes) volumesRemoved += group.volumes;\n\t\t\t\tif (selection.resources.images) imagesRemoved += group.images;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// TOCTOU close: `collectLifecyclePruneInventory` probed liveness\n\t\t\t// ONCE at the top of this run. A stack that booted in the window\n\t\t\t// between that snapshot and here (a concurrent `devstack up` of a\n\t\t\t// previously-dead stack) would have its containers/networks/volumes\n\t\t\t// removed out from under the freshly-started supervisor. Re-probe\n\t\t\t// each non-router victim's roster via the SAME helper the inventory\n\t\t\t// pass used (`livePidsForStack`) immediately before committing it to\n\t\t\t// removal, and skip any group that came alive since. Router groups\n\t\t\t// carry no per-stack roster (their inventory liveness is\n\t\t\t// `runningContainers`), so they keep the snapshot classification —\n\t\t\t// mirroring the `routerGroup` branch in the inventory pass.\n\t\t\tif (!isRouterGroup(group)) {\n\t\t\t\tconst livePids = yield* livePidsForStack(options.runtimeRoot, group.stack);\n\t\t\t\tif (livePids.length > 0) {\n\t\t\t\t\tskippedLiveGroups += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tprunableGroups.push(group);\n\t\t}\n\n\t\tconst foreignNetworkHolders: Array<ForeignNetworkHolder> = [];\n\t\tconst staleNetworkEndpoints: Array<StaleNetworkEndpoint> = [];\n\t\t// Provide `dockerLayer` ONCE around the whole removal section rather\n\t\t// than re-providing it per group per resource. The inventory pass\n\t\t// already provides it once for its parallel scan; the removal loops\n\t\t// mirror that so the layer's docker-host resolution is built a single\n\t\t// time, not rebuilt for every container/network/volume/image removal.\n\t\t// The per-call `Effect.mapError(failPhase(...))` stays inside each\n\t\t// loop so each removal still carries its phase-specific tag.\n\t\tyield* Effect.gen(function* () {\n\t\t\tif (!selection.dryRun && selection.resources.containers) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tif (isRouterGroup(group)) {\n\t\t\t\t\t\tcontainersRemoved += yield* removeDevstackContainersByKindAndName(\n\t\t\t\t\t\t\tROUTER_KIND_LABEL_VALUE,\n\t\t\t\t\t\t\tgroup.stack,\n\t\t\t\t\t\t).pipe(Effect.mapError(failPhase('remove-containers')));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst match = { app: group.app, stack: group.stack };\n\t\t\t\t\t\tcontainersRemoved += yield* removeDevstackContainers(match).pipe(\n\t\t\t\t\t\t\tEffect.mapError(failPhase('remove-containers')),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.networks) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\tconst result = yield* removeDevstackNetworksBestEffort(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-networks')),\n\t\t\t\t\t);\n\t\t\t\t\tnetworksRemoved += result.removed;\n\t\t\t\t\tnetworksSkipped += result.skippedInUse;\n\t\t\t\t\tfor (const holder of result.foreignHolders) foreignNetworkHolders.push(holder);\n\t\t\t\t\tfor (const ep of result.staleEndpoints) staleNetworkEndpoints.push(ep);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.volumes) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\tvolumesRemoved += yield* removeDevstackVolumes(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-volumes')),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!selection.dryRun && selection.resources.images) {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tconst match = matchTupleForGroup(group);\n\t\t\t\t\timagesRemoved += yield* removeDevstackImages(match).pipe(\n\t\t\t\t\t\tEffect.mapError(failPhase('remove-images')),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}).pipe(Effect.provide(dockerLayer));\n\n\t\tif (!selection.dryRun) {\n\t\t\tyield* Effect.gen(function* () {\n\t\t\t\tfor (const group of prunableGroups) {\n\t\t\t\t\tif (!isRouterGroup(group)) continue;\n\t\t\t\t\tyield* removeRouterProfileStateForDockerStack({\n\t\t\t\t\t\truntimeRoot: options.runtimeRoot,\n\t\t\t\t\t\trouterStack: group.stack,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}).pipe(Effect.provide(NodeFileSystem.layer));\n\t\t}\n\n\t\treturn {\n\t\t\tinspectedGroups: groups.length,\n\t\t\t// Groups actually selected for pruning — `groups.length` minus\n\t\t\t// the live ones skipped above, NOT the inspected total.\n\t\t\tselectedGroups: prunableGroups.length,\n\t\t\tskippedLiveGroups,\n\t\t\tcontainersRemoved,\n\t\t\tnetworksRemoved,\n\t\t\tnetworksSkipped,\n\t\t\tvolumesRemoved,\n\t\t\timagesRemoved,\n\t\t\tforeignNetworkHolders,\n\t\t\tstaleNetworkEndpoints,\n\t\t};\n\t});\n"],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//#region src/orchestrators/network-options.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Per-network dev-convenience toggles. All optional — an unset field
|
|
4
|
+
* defers to the default policy (on for every network EXCEPT live
|
|
5
|
+
* `mainnet`). A CONSISTENT mechanism: declare these once per network in
|
|
6
|
+
* the config instead of scattering one-off flags.
|
|
7
|
+
*
|
|
8
|
+
* - `devWallet` — per-network override is FORWARDED. Gates the
|
|
9
|
+
* dev-wallet `generated-extras` flush at boot (`orchestrators/boot.ts`,
|
|
10
|
+
* which resolves these options against the substrate-forwarded
|
|
11
|
+
* `networkOptions` record), which the Vite plugin injects.
|
|
12
|
+
* - `autoApproveSigning` — per-network override is FORWARDED. Gates the
|
|
13
|
+
* dev-wallet auto-approve policy the Vite plugin emits into the
|
|
14
|
+
* injected dev-wallet (`build-integrations/vite/index.ts`) — on
|
|
15
|
+
* `mainnet` signing is never silently auto-approved.
|
|
16
|
+
* - `faucet` — per-network override is NOT forwarded to the sui plugin
|
|
17
|
+
* (see the field doc below). The plugin honours only the policy
|
|
18
|
+
* default, which already carries the load-bearing `mainnet`
|
|
19
|
+
* hard-clamp, so a non-faucet `mainnet` never registers
|
|
20
|
+
* `faucet:request:<chainId>`.
|
|
21
|
+
*/
|
|
22
|
+
interface NetworkScopedOptions {
|
|
23
|
+
/** ENFORCED. Mount the test-only dev wallet and flush its
|
|
24
|
+
* `generated-extras` tree (`dev-wallet.ts` + `accounts.ts`) at boot so
|
|
25
|
+
* the Vite plugin's `@devstack-dev` injection has files to load. Off →
|
|
26
|
+
* no flush, and the Vite `load` hook gracefully no-ops. */
|
|
27
|
+
readonly devWallet?: boolean;
|
|
28
|
+
/** Funding-faucet strategy gate (`faucet:request:<chainId>`) in the sui
|
|
29
|
+
* plugin. The plugin follows the POLICY DEFAULT only — on for every
|
|
30
|
+
* non-`mainnet` network, hard-clamped off on live `mainnet` (so a
|
|
31
|
+
* non-faucet network never registers the strategy and account funding
|
|
32
|
+
* surfaces the actionable "no faucet strategy" error instead of
|
|
33
|
+
* faucet-funding). A per-network `faucet` OVERRIDE here is NOT currently
|
|
34
|
+
* forwarded to the plugin (unlike `devWallet` / `autoApproveSigning`,
|
|
35
|
+
* which boot resolves against the substrate-forwarded `networkOptions`):
|
|
36
|
+
* the name-blind substrate does not thread `networkOptions` into plugins,
|
|
37
|
+
* and the sui plugin only receives `IdentityContext`. The sui mode still
|
|
38
|
+
* decides HOW a faucet is provisioned (local container / fork whale /
|
|
39
|
+
* live endpoint); the policy default decides WHETHER the resolved
|
|
40
|
+
* strategy is exposed. */
|
|
41
|
+
readonly faucet?: boolean;
|
|
42
|
+
/** ENFORCED. Default the injected dev-wallet's auto-approve policy for
|
|
43
|
+
* this network (`build-integrations/vite/index.ts`). On → dev-wallet
|
|
44
|
+
* signing requests auto-approve (headless Playwright / in-app "Open as"
|
|
45
|
+
* ergonomics) unless an explicit `autoApprove` / `DEVSTACK_AUTO_APPROVE`
|
|
46
|
+
* overrides. Hard-clamped off on live `mainnet`, so a real-funds
|
|
47
|
+
* signature is never granted without a human in the loop. */
|
|
48
|
+
readonly autoApproveSigning?: boolean;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { NetworkScopedOptions };
|
|
52
|
+
//# sourceMappingURL=network-options.d.mts.map
|