@mysten-incubation/devstack 0.1.1 → 0.3.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-Clz0oOtc.js +3 -0
- package/dashboard-ui/assets/index-B82Bh84P.css +1 -0
- package/dashboard-ui/assets/index-Cmqv9kiU.js +1283 -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 +203 -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 +3 -31
- package/dist/build-integrations/playwright/config.mjs +2 -28
- 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 +114 -19
- package/dist/build-integrations/vite/index.mjs +239 -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 +15 -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 +8 -5
- 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 +225 -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 +450 -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 +303 -60
- 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 +124 -168
- 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 +25 -46
- package/dist/plugins/dashboard/schema/root.mjs.map +1 -1
- package/dist/plugins/dashboard/schema/types.mjs +52 -93
- 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 +45 -37
- package/dist/plugins/deepbook/index.mjs +136 -67
- 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 +44 -93
- 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 +37 -6
- 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 +2 -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/file-watcher.mjs +54 -0
- package/dist/substrate/runtime/lifecycle/file-watcher.mjs.map +1 -0
- 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 +3 -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 +76 -7
- 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.d.mts +1 -0
- 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 +61 -75
- 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/{cross-process/snapshot-reservation.d.mts → lifecycle/file-watcher.d.mts} +0 -0
- /package/dist/substrate/runtime/{state-store/index.d.mts → reconcile/graph.d.mts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.mjs","names":[],"sources":["../../../src/plugins/dashboard/domain.ts"],"sourcesContent":["// Dashboard plugin-domain shaping.\n//\n// The substrate control-plane is GENERIC + name-blind: it hands out the\n// snapshot catalog, the observability rings, and a single uninterpreted\n// `resolvedValues` accessor (see `substrate/runtime/control-plane/`). This\n// module — which lives in the PLUGIN layer and is allowed to name plugins —\n// owns ALL plugin-name-aware shaping: it matches resolved plugin values by\n// resource id (`deepbook/`-prefix, `seal:`-prefix, `coin:`-prefix,\n// `id === 'sui'`, `id === 'postgres'`) and projects them into the\n// app-agnostic shapes the\n// GraphQL schema renders. It also owns the `mode` derivation, the coin\n// `mint` action, and the Postgres `psql`-exec wire-protocol stats.\n//\n// Design discipline (mirrors the old substrate seam, one layer up):\n// - We import NO plugin types — we narrow the opaque `unknown` resolved\n// value through shallow structural shapes that mirror the relevant\n// fields. A field missing on the live value collapses to the\n// null/empty default.\n// - Every accessor degrades to empty/`null` rather than failing, so a\n// single missing/uninitialised plugin can't take down a dashboard\n// query (`E = never` on the public surface).\n// - We match plugins by RESOURCE ID — a prefix for the multi-instance\n// kinds (`deepbook/`, `seal:`, `coin:`) and an exact id for the\n// singletons (`id === 'postgres'`, `id === 'sui'`) — rather than\n// plugin-key substrings: the resource id is the stable identity the\n// plugin factories mint.\n\nimport { Effect } from 'effect';\n\nimport type { ContainerRuntime } from '../../contracts/container-runtime.ts';\nimport type { Identity } from '../../substrate/identity.ts';\nimport type {\n\tControlPlaneDomain,\n\tControlPlaneResolvedValue,\n} from '../../substrate/runtime/control-plane/service.ts';\nimport type { StrategyRegistry } from '../../contracts/strategy-contributor.ts';\nimport type { FaucetStrategy } from '../../contracts/faucet-strategy.ts';\nimport type { AccountFundingStrategy } from '../../contracts/funding-strategy.ts';\nimport type { AccountValue } from '../account/index.ts';\n// The faucet capability-key prefix is owned by the faucet plugin (single\n// source of truth). The dashboard plugin layer is allowed to name plugins,\n// so importing the key constructor is the same cross-plugin reference the\n// account funding pass (`plugins/account/funding.ts`) uses — NOT a resolved\n// plugin-VALUE import (those stay structurally narrowed below). Imported via\n// the sibling's barrel to satisfy the plugin-boundary invariant.\nimport { FAUCET_CAPABILITY_KEY_PREFIX } from '../faucet/index.ts';\n\n// -----------------------------------------------------------------------------\n// App-agnostic domain shapes the GraphQL schema renders.\n// -----------------------------------------------------------------------------\n\n/** One DeepBook pool object id + coin types (prices are chain-direct). */\nexport interface DashboardDeepbookPool {\n\treadonly name: string;\n\treadonly poolId: string;\n\treadonly baseCoinType: string;\n\treadonly quoteCoinType: string;\n}\n\nexport interface DashboardDeepbookInfo {\n\treadonly pluginKey: string;\n\treadonly name: string;\n\treadonly mode: 'local' | 'override' | 'known';\n\treadonly chain: string;\n\treadonly packageId: string;\n\treadonly registryId: string;\n\treadonly adminCapId: string | null;\n\treadonly deepTreasuryId: string | null;\n\treadonly pools: ReadonlyArray<DashboardDeepbookPool>;\n\treadonly marketMakerRunning: boolean;\n\treadonly serverUrl: string | null;\n\treadonly indexerUrl: string | null;\n}\n\nexport interface DashboardSealKeyServer {\n\treadonly objectId: string;\n\treadonly weight: number;\n}\n\nexport interface DashboardSealInfo {\n\treadonly pluginKey: string;\n\treadonly mode: 'local-keygen' | 'live' | 'fork-known';\n\treadonly objectId: string;\n\treadonly keyServerUrl: string;\n\treadonly keyServers: ReadonlyArray<DashboardSealKeyServer>;\n\t/** Threshold = number of registered key-server configs. */\n\treadonly threshold: number;\n}\n\n/** A coin's treasury-cap id (drives the Mint action) + addressing facts. */\nexport interface DashboardCoinCap {\n\treadonly pluginKey: string;\n\treadonly symbol: string | null;\n\treadonly fullCoinType: string;\n\treadonly decimals: number;\n\treadonly source: 'registry' | 'on-chain' | 'builtin';\n\treadonly treasuryCapId: string | null;\n\treadonly packageId: string | null;\n}\n\n/** Input for the dashboard mint ACTION. `amountBaseUnits` is the raw\n * integer amount in the coin's smallest unit (decimals already applied\n * by the caller / form) — a string so large u64 values survive the wire\n * without precision loss. */\nexport interface DashboardMintInput {\n\treadonly coinType: string;\n\treadonly recipient: string;\n\treadonly amountBaseUnits: string;\n}\n\n/** Outcome of a dashboard mint ACTION. Mirrors the snapshot\n * restore/delete result shape plus the on-chain tx `digest` on success. */\nexport interface DashboardMintResult {\n\treadonly ok: boolean;\n\treadonly detail: string;\n\treadonly digest: string | null;\n}\n\n/** Input for the dashboard fund ACTION.\n *\n * `coinType` selects the funding strategy: absent or the canonical SUI\n * type routes through the chain's faucet strategy (`faucet:request:<chainId>`,\n * fixed-amount); any other full coin type routes through the coin-specific\n * `coinType:<fullCoinType>` strategy (WAL exchange swap / DEEP pool swap).\n *\n * `amountBaseUnits` is the raw integer amount in the coin's smallest unit\n * (a string so large u64 values survive the wire without precision loss).\n * The SUI faucet is fixed-amount and IGNORES it; WAL/DEEP honor it. */\nexport interface DashboardFundInput {\n\treadonly recipient: string;\n\treadonly coinType?: string | null;\n\treadonly amountBaseUnits?: string | null;\n}\n\n/** Outcome of a dashboard fund ACTION. The in-process funding strategies\n * return `void` (not a digest), so `digest` is always `null` here — kept\n * for shape-parity with `DashboardMintResult`. `ok:true` means the\n * strategy's `request(...)` completed (the faucet POST landed / the swap\n * executed on-chain); `detail` carries the reason on failure. */\nexport interface DashboardFundResult {\n\treadonly ok: boolean;\n\treadonly detail: string;\n\treadonly digest: string | null;\n}\n\n/** One coin the dashboard faucet can actually fund, with whether the\n * underlying strategy honors a caller-supplied amount.\n *\n * SUI is always present (fixed-amount faucet, `honorsAmount: false`).\n * WAL/DEEP appear only when their plugin registered a `coinType:<X>`\n * funding strategy AND (for WAL) carries a swap that spends the\n * recipient account's SUI — so `honorsAmount: true`. */\nexport interface DashboardFundableCoin {\n\treadonly symbol: string;\n\treadonly coinType: string;\n\treadonly honorsAmount: boolean;\n\t/** True when funding requires the recipient to BE a resolved account in\n\t * the stack (the swap spends that account's own SUI via its in-process\n\t * signer). SUI is `false` (any 0x address); WAL/DEEP are `true`. */\n\treadonly requiresAccountSigner: boolean;\n}\n\nexport interface DashboardPostgresTable {\n\treadonly schema: string;\n\treadonly name: string;\n\treadonly rowEstimate: number;\n\treadonly totalBytes: number;\n}\n\nexport interface DashboardPostgresStats {\n\treadonly pluginKey: string;\n\treadonly database: string;\n\t/** Plain (password-less) DSN — the credentialed form NEVER leaves the\n\t * backend. */\n\treadonly plainUrl: string;\n\treadonly databaseBytes: number;\n\treadonly connectionCount: number;\n\treadonly tables: ReadonlyArray<DashboardPostgresTable>;\n\t/** `false` when stats could not be gathered (container down, exec\n\t * failure). The dashboard renders a degraded state rather than\n\t * failing the whole query. */\n\treadonly available: boolean;\n\treadonly detail: string | null;\n}\n\n/** The dashboard plugin-domain accessor surface. Each member is a\n * self-contained Effect that never fails (`E = never`); they degrade to\n * empty/`null` so a single missing plugin can't take down the dashboard\n * query. */\nexport interface DashboardDomain {\n\t/** Fork-vs-local mode, derived from the resolved sui plugin's chain\n\t * identity. Used for advance-clock gating. `null` when no sui plugin\n\t * is present. */\n\treadonly mode: Effect.Effect<'fork' | 'local' | 'live' | null>;\n\t/** DeepBook deployments (registry/admin/pool ids + MM state). */\n\treadonly deepbook: Effect.Effect<ReadonlyArray<DashboardDeepbookInfo>>;\n\t/** Seal key-server deployments (objectId/threshold/mode). */\n\treadonly seal: Effect.Effect<ReadonlyArray<DashboardSealInfo>>;\n\t/** Coin treasury caps (drives Mint). */\n\treadonly coinCaps: Effect.Effect<ReadonlyArray<DashboardCoinCap>>;\n\t/** Postgres wire-protocol stats per postgres plugin instance. */\n\treadonly postgresStats: Effect.Effect<ReadonlyArray<DashboardPostgresStats>>;\n\t/** Mint ACTION — mints `amountBaseUnits` of `coinType` to `recipient`,\n\t * signed in-process by the treasury-cap-owning publisher signer the\n\t * resolved coin value's self-contained `mintFromCap` closure holds. */\n\treadonly mintCoin: (input: DashboardMintInput) => Effect.Effect<DashboardMintResult>;\n\t/** Coins the faucet can actually fund right now (SUI always; WAL/DEEP\n\t * only when their plugin registered a funding strategy). Drives the\n\t * faucet panel's coin pills + amount-field gating. */\n\treadonly fundableCoins: Effect.Effect<ReadonlyArray<DashboardFundableCoin>>;\n\t/** Fund ACTION — reuses devstack's in-process funding strategies. SUI\n\t * (absent/canonical type) routes through the chain faucet strategy\n\t * (fixed-amount); WAL/DEEP route through the coin-specific funding\n\t * strategy (account-signed exchange/pool swap). Real result — `ok`\n\t * reflects whether the strategy's `request(...)` completed. */\n\treadonly fundAccount: (input: DashboardFundInput) => Effect.Effect<DashboardFundResult>;\n}\n\n// -----------------------------------------------------------------------------\n// Structural projections of plugin-resolved values\n//\n// We narrow the opaque `unknown` resolved value through shallow structural\n// shapes that mirror the relevant fields. A field missing on the live value\n// collapses to the null/empty default.\n// -----------------------------------------------------------------------------\n\ninterface DeepbookShape {\n\treadonly mode?: unknown;\n\treadonly chain?: unknown;\n\treadonly packageId?: unknown;\n\treadonly registryId?: unknown;\n\treadonly adminCapId?: unknown;\n\treadonly deepTreasuryId?: unknown;\n\treadonly pools?: ReadonlyArray<{\n\t\treadonly name?: unknown;\n\t\treadonly poolId?: unknown;\n\t\treadonly baseCoinType?: unknown;\n\t\treadonly quoteCoinType?: unknown;\n\t}>;\n\treadonly marketMakerRunning?: unknown;\n\treadonly serverUrl?: unknown;\n\treadonly indexerUrl?: unknown;\n}\n\ninterface SealShape {\n\treadonly mode?: unknown;\n\treadonly objectId?: unknown;\n\treadonly keyServerUrl?: unknown;\n\treadonly serverConfigs?: ReadonlyArray<{\n\t\treadonly objectId?: unknown;\n\t\treadonly weight?: unknown;\n\t}>;\n}\n\ninterface CoinShape {\n\treadonly symbol?: unknown;\n\treadonly fullCoinType?: unknown;\n\treadonly decimals?: unknown;\n\treadonly source?: unknown;\n\treadonly treasuryCapId?: unknown;\n\treadonly packageId?: unknown;\n\t/** Self-contained mint closure on the resolved coin value (present\n\t * for witness-form coins whose publisher still owns the cap). Read\n\t * structurally — we import no coin types. Returns an Effect that\n\t * resolves a `{ digest }`-bearing result or fails with a\n\t * coin/artifact-publisher tagged error. */\n\treadonly mintFromCap?: (opts: {\n\t\treadonly to: string;\n\t\treadonly amount: bigint;\n\t}) => Effect.Effect<{ readonly digest: string }, { readonly message?: unknown }>;\n}\n\ninterface PostgresShape {\n\treadonly name?: unknown;\n\treadonly user?: unknown;\n\treadonly databases?: ReadonlyArray<unknown>;\n\treadonly plainEndpoint?: unknown;\n\treadonly networkAlias?: unknown;\n\treadonly port?: unknown;\n}\n\ninterface SuiShape {\n\treadonly mode?: unknown;\n\t/** Substrate-level chain id (`'sui:localnet'`, `'sui:testnet'`,\n\t * `'sui:mainnet-fork@123'`). Composes the faucet capability key\n\t * (`faucet:request:<chainId>`) the SUI funding path resolves. */\n\treadonly chain?: unknown;\n}\n\n/** Structural shape of the resolved account value (`account/<name>`). The\n * WAL/DEEP funding strategies need this handle as `req.account` — the swap\n * spends the recipient account's own SUI through its in-process signer.\n * Narrowed to the fields the strategy dispatch reads; the full\n * `AccountValue` is assignable to it, so we cast the matched value across\n * the seam without importing the account VALUE shape structurally\n * elsewhere. */\ninterface AccountShape {\n\treadonly address?: unknown;\n\treadonly name?: unknown;\n}\n\n/** A 0x-prefixed Sui address: `0x` + 1..64 hex digits. Mirrors the\n * address validation the mint PTB's `tx.pure.address` ultimately\n * enforces, surfaced up front so the dashboard gets a clean rejection\n * rather than an opaque build failure. */\nconst SUI_ADDRESS_RE = /^0x[0-9a-fA-F]{1,64}$/;\n\n/** The canonical builtin SUI coin type — selects the SUI faucet funding\n * path. Mirrors `SUI_FULL_COIN_TYPE` in `plugins/account/funding.ts`\n * (inlined here so the dashboard plugin does not cross-import the account\n * funding module just for the string). */\nconst SUI_FULL_COIN_TYPE = '0x2::sui::SUI' as const;\n\n/** A positive integer base-unit amount string (no sign, no decimal\n * point, no leading zeros beyond a bare `0` — which is itself rejected\n * as non-positive). */\nconst isPositiveIntegerString = (s: string): boolean => {\n\tif (!/^\\d+$/.test(s)) return false;\n\ttry {\n\t\treturn BigInt(s) > 0n;\n\t} catch {\n\t\treturn false;\n\t}\n};\n\n/** Compose the chain faucet capability key (`faucet:request:<chainId>`)\n * the SUI funding path resolves. Uses the faucet plugin's prefix constant\n * (single source of truth) — same key the boot-time funding pass builds. */\nconst faucetCapabilityKeyFor = (\n\tchain: string,\n): `${typeof FAUCET_CAPABILITY_KEY_PREFIX}:${string}` => `${FAUCET_CAPABILITY_KEY_PREFIX}:${chain}`;\n\n/** Parse a base-unit amount string into a positive bigint, or `null` when\n * absent / non-positive / non-integer. */\nconst parseAmount = (s: string | null | undefined): bigint | null => {\n\tif (s == null) return null;\n\tconst trimmed = s.trim();\n\tif (!isPositiveIntegerString(trimmed)) return null;\n\treturn BigInt(trimmed);\n};\n\n/** Derive a display symbol from a full coin type's module path\n * (`0x…::wal::WAL` → `WAL`, `0x…::deep::DEEP` → `DEEP`). Falls back to the\n * struct name, then the raw type. Keeps the dashboard name-blind on the\n * resolved coin VALUE while still labeling the faucet pill. */\nconst coinSymbolFromType = (coinType: string): string => {\n\tconst parts = coinType.split('::');\n\tconst struct = parts[parts.length - 1];\n\treturn struct !== undefined && struct.length > 0 ? struct : coinType;\n};\n\n/** Extract a human detail from a faucet/funding strategy failure. The\n * strategy error channels are tagged structs carrying `message`/`reason`;\n * read them structurally without importing each plugin's error type. */\nconst causeDetail = (cause: unknown): string => {\n\tif (cause !== null && typeof cause === 'object') {\n\t\tconst c = cause as {\n\t\t\treadonly message?: unknown;\n\t\t\treadonly reason?: unknown;\n\t\t\treadonly _tag?: unknown;\n\t\t};\n\t\tif (typeof c.message === 'string' && c.message.length > 0) return c.message;\n\t\tif (typeof c.reason === 'string' && c.reason.length > 0) return c.reason;\n\t\tif (typeof c._tag === 'string' && c._tag.length > 0) return c._tag;\n\t}\n\treturn String(cause);\n};\nconst faucetCauseDetail = causeDetail;\nconst fundingCauseDetail = causeDetail;\n\nconst str = (v: unknown): string | null => (typeof v === 'string' ? v : null);\nconst strReq = (v: unknown): string => (typeof v === 'string' ? v : '');\nconst num = (v: unknown): number | null => (typeof v === 'number' && Number.isFinite(v) ? v : null);\nconst bool = (v: unknown): boolean => v === true;\n\n/** Filter the generic resolved values down to those whose resource id\n * matches a predicate, in graph order. Returns the `{ pluginKey, value }`\n * the shaping functions consume. */\nconst matching = (\n\tvalues: ReadonlyArray<ControlPlaneResolvedValue>,\n\tmatches: (resourceId: string) => boolean,\n): ReadonlyArray<ControlPlaneResolvedValue> => values.filter((v) => matches(v.id));\n\n// -----------------------------------------------------------------------------\n// Postgres wire-protocol stats via `psql` exec inside the container\n// -----------------------------------------------------------------------------\n\n/** SQL that emits a single line of JSON the dashboard can parse without a\n * PG client. Keeps the round trip to one exec. */\nconst PG_STATS_SQL = `SELECT json_build_object(\n 'db_bytes', pg_database_size(current_database()),\n 'connections', (SELECT count(*) FROM pg_stat_activity WHERE datname = current_database()),\n 'tables', COALESCE((\n SELECT json_agg(t) FROM (\n SELECT schemaname AS schema, relname AS name,\n n_live_tup AS row_estimate,\n pg_total_relation_size(relid) AS total_bytes\n FROM pg_stat_user_tables\n ORDER BY pg_total_relation_size(relid) DESC\n LIMIT 200\n ) t\n ), '[]'::json)\n)`;\n\nconst parsePgStats = (\n\tpluginKey: string,\n\tpg: PostgresShape,\n\tstdout: string,\n): DashboardPostgresStats => {\n\tconst database = strReq((pg.databases ?? [])[0]) || 'postgres';\n\tconst host = str(pg.networkAlias) ?? 'postgres';\n\tconst port = num(pg.port) ?? 5432;\n\tconst plainUrl = `postgres://${host}:${port}`;\n\tconst base = {\n\t\tpluginKey,\n\t\tdatabase,\n\t\tplainUrl,\n\t} as const;\n\ttry {\n\t\tconst trimmed = stdout.trim();\n\t\tconst parsed = JSON.parse(trimmed) as {\n\t\t\tdb_bytes?: number;\n\t\t\tconnections?: number;\n\t\t\ttables?: ReadonlyArray<{\n\t\t\t\tschema?: string;\n\t\t\t\tname?: string;\n\t\t\t\trow_estimate?: number;\n\t\t\t\ttotal_bytes?: number;\n\t\t\t}>;\n\t\t};\n\t\tconst tables: DashboardPostgresTable[] = (parsed.tables ?? []).map((t) => ({\n\t\t\tschema: t.schema ?? 'public',\n\t\t\tname: t.name ?? '',\n\t\t\trowEstimate: num(t.row_estimate) ?? 0,\n\t\t\ttotalBytes: num(t.total_bytes) ?? 0,\n\t\t}));\n\t\treturn {\n\t\t\t...base,\n\t\t\tdatabaseBytes: num(parsed.db_bytes) ?? 0,\n\t\t\tconnectionCount: num(parsed.connections) ?? 0,\n\t\t\ttables,\n\t\t\tavailable: true,\n\t\t\tdetail: null,\n\t\t};\n\t} catch (cause) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tdatabaseBytes: 0,\n\t\t\tconnectionCount: 0,\n\t\t\ttables: [],\n\t\t\tavailable: false,\n\t\t\tdetail: `failed to parse psql output: ${String(cause)}`,\n\t\t};\n\t}\n};\n\nconst unavailablePgStats = (\n\tpluginKey: string,\n\tpg: PostgresShape,\n\tdetail: string,\n): DashboardPostgresStats => {\n\tconst database = strReq((pg.databases ?? [])[0]) || 'postgres';\n\tconst host = str(pg.networkAlias) ?? 'postgres';\n\tconst port = num(pg.port) ?? 5432;\n\treturn {\n\t\tpluginKey,\n\t\tdatabase,\n\t\tplainUrl: `postgres://${host}:${port}`,\n\t\tdatabaseBytes: 0,\n\t\tconnectionCount: 0,\n\t\ttables: [],\n\t\tavailable: false,\n\t\tdetail,\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// Builder\n// -----------------------------------------------------------------------------\n\nexport interface DashboardDomainDeps {\n\t/** The generic, name-blind control-plane domain (resolved values). */\n\treadonly control: ControlPlaneDomain;\n\t/** Stack identity, used for the postgres container label probe. */\n\treadonly identity: Identity;\n\t/** Container runtime for the Postgres `psql` exec-probe. `null` in bare\n\t * test paths; `postgresStats` degrades to an unavailable entry then. */\n\treadonly containerRuntime: ContainerRuntime | null;\n\t/** The scope-local strategy registry — the SAME registry the boot-time\n\t * account funding pass dispatches through (`plugins/account/funding.ts`).\n\t * Drives `fundableCoins` + `fundAccount`: SUI via `faucet:request:<chainId>`,\n\t * WAL/DEEP via `coinType:<fullCoinType>`. `null` in bare test paths; the\n\t * fund accessors degrade to unavailable then. */\n\treadonly strategyRegistry: StrategyRegistry | null;\n}\n\nexport const buildDashboardDomain = (deps: DashboardDomainDeps): DashboardDomain => {\n\tconst { control, identity, containerRuntime, strategyRegistry } = deps;\n\n\tconst mode: DashboardDomain['mode'] = control.resolvedValues.pipe(\n\t\tEffect.map((values) => {\n\t\t\tconst sui = matching(values, (id) => id === 'sui')[0];\n\t\t\tif (sui === undefined) return null;\n\t\t\tconst m = (sui.value as SuiShape).mode;\n\t\t\tswitch (m) {\n\t\t\t\tcase 'fork':\n\t\t\t\t\treturn 'fork';\n\t\t\t\tcase 'live':\n\t\t\t\t\treturn 'live';\n\t\t\t\tcase 'local':\n\t\t\t\tcase 'local-rpc':\n\t\t\t\t\treturn 'local';\n\t\t\t\tdefault:\n\t\t\t\t\treturn null;\n\t\t\t}\n\t\t}),\n\t);\n\n\tconst deepbook: DashboardDomain['deepbook'] = control.resolvedValues.pipe(\n\t\tEffect.map((values) =>\n\t\t\tmatching(values, (id) => id.startsWith('deepbook/')).map(\n\t\t\t\t({ pluginKey, value }): DashboardDeepbookInfo => {\n\t\t\t\t\tconst v = value as DeepbookShape;\n\t\t\t\t\tconst modeRaw = v.mode;\n\t\t\t\t\tconst dbMode =\n\t\t\t\t\t\tmodeRaw === 'override' ? 'override' : modeRaw === 'known' ? 'known' : 'local';\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpluginKey,\n\t\t\t\t\t\tname: pluginKey.replace(/^deepbook:/, ''),\n\t\t\t\t\t\tmode: dbMode,\n\t\t\t\t\t\tchain: strReq(v.chain),\n\t\t\t\t\t\tpackageId: strReq(v.packageId),\n\t\t\t\t\t\tregistryId: strReq(v.registryId),\n\t\t\t\t\t\tadminCapId: str(v.adminCapId),\n\t\t\t\t\t\tdeepTreasuryId: str(v.deepTreasuryId),\n\t\t\t\t\t\tpools: (v.pools ?? []).map((p) => ({\n\t\t\t\t\t\t\tname: strReq(p.name),\n\t\t\t\t\t\t\tpoolId: strReq(p.poolId),\n\t\t\t\t\t\t\tbaseCoinType: strReq(p.baseCoinType),\n\t\t\t\t\t\t\tquoteCoinType: strReq(p.quoteCoinType),\n\t\t\t\t\t\t})),\n\t\t\t\t\t\tmarketMakerRunning: bool(v.marketMakerRunning),\n\t\t\t\t\t\tserverUrl: str(v.serverUrl),\n\t\t\t\t\t\tindexerUrl: str(v.indexerUrl),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t),\n\t\t),\n\t);\n\n\tconst seal: DashboardDomain['seal'] = control.resolvedValues.pipe(\n\t\tEffect.map((values) =>\n\t\t\tmatching(values, (id) => id.startsWith('seal:')).map(\n\t\t\t\t({ pluginKey, value }): DashboardSealInfo => {\n\t\t\t\t\tconst v = value as SealShape;\n\t\t\t\t\tconst sealMode =\n\t\t\t\t\t\tv.mode === 'live' ? 'live' : v.mode === 'fork-known' ? 'fork-known' : 'local-keygen';\n\t\t\t\t\tconst keyServers = (v.serverConfigs ?? []).map((c) => ({\n\t\t\t\t\t\tobjectId: strReq(c.objectId),\n\t\t\t\t\t\tweight: num(c.weight) ?? 1,\n\t\t\t\t\t}));\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpluginKey,\n\t\t\t\t\t\tmode: sealMode,\n\t\t\t\t\t\tobjectId: strReq(v.objectId),\n\t\t\t\t\t\tkeyServerUrl: strReq(v.keyServerUrl),\n\t\t\t\t\t\tkeyServers,\n\t\t\t\t\t\tthreshold: keyServers.length,\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t),\n\t\t),\n\t);\n\n\tconst coinCaps: DashboardDomain['coinCaps'] = control.resolvedValues.pipe(\n\t\tEffect.map((values) =>\n\t\t\tmatching(values, (id) => id.startsWith('coin:')).map(\n\t\t\t\t({ pluginKey, value }): DashboardCoinCap => {\n\t\t\t\t\tconst v = value as CoinShape;\n\t\t\t\t\tconst source =\n\t\t\t\t\t\tv.source === 'registry' ? 'registry' : v.source === 'builtin' ? 'builtin' : 'on-chain';\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpluginKey,\n\t\t\t\t\t\tsymbol: str(v.symbol),\n\t\t\t\t\t\tfullCoinType: strReq(v.fullCoinType),\n\t\t\t\t\t\tdecimals: num(v.decimals) ?? 0,\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\ttreasuryCapId: str(v.treasuryCapId),\n\t\t\t\t\t\tpackageId: str(v.packageId),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t),\n\t\t),\n\t);\n\n\t// Mint ACTION — drives the dashboard Coins panel's Mint button.\n\t//\n\t// Signer source: the resolved coin VALUE carries a self-contained\n\t// `mintFromCap` closure (present only for witness-form coins whose\n\t// publisher still owns the TreasuryCap). That closure already captures\n\t// the treasury-cap-owning publisher `MintSigner` + the resolved cap id\n\t// in-process — the same lease-owning path `coin/service.ts`'s\n\t// `fundingStrategy` uses — so we mint WITHOUT threading a signer through\n\t// this seam (we read the resolved value, never plugin internals).\n\t//\n\t// Never fails (`E = never`): every reject path (bad address, non-\n\t// positive amount, no matching coin, cap-not-owned, on-chain failure)\n\t// degrades to `{ ok: false, detail, digest: null }` so the dashboard\n\t// query can't be taken down by a single bad mint.\n\tconst mintCoin: DashboardDomain['mintCoin'] = (input) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst recipient = input.recipient.trim();\n\t\t\tif (!SUI_ADDRESS_RE.test(recipient)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: `invalid recipient '${input.recipient}': expected a 0x-prefixed Sui address`,\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardMintResult;\n\t\t\t}\n\t\t\tif (!isPositiveIntegerString(input.amountBaseUnits)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: `invalid amountBaseUnits '${input.amountBaseUnits}': expected a positive integer string`,\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardMintResult;\n\t\t\t}\n\n\t\t\t// Locate the resolved coin whose fullCoinType matches. Match on the\n\t\t\t// resolved value's `fullCoinType` (the stable on-chain type), not the\n\t\t\t// resource-id prefix, so callers pass the same `coinType` the\n\t\t\t// `coinCaps` query surfaced.\n\t\t\tconst values = yield* control.resolvedValues;\n\t\t\tconst match = matching(values, (id) => id.startsWith('coin:'))\n\t\t\t\t.map(({ value }) => value as CoinShape)\n\t\t\t\t.find((v) => strReq(v.fullCoinType) === input.coinType);\n\n\t\t\tif (match === undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: `no resolved coin found for type '${input.coinType}'`,\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardMintResult;\n\t\t\t}\n\t\t\tif (typeof match.mintFromCap !== 'function') {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail:\n\t\t\t\t\t\t`coin '${input.coinType}' has no in-process treasury cap signer — ` +\n\t\t\t\t\t\t'mint is only available for local-package coins whose publisher still owns the TreasuryCap',\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardMintResult;\n\t\t\t}\n\n\t\t\treturn yield* match\n\t\t\t\t.mintFromCap({ to: recipient, amount: BigInt(input.amountBaseUnits) })\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.map(\n\t\t\t\t\t\t(r): DashboardMintResult => ({\n\t\t\t\t\t\t\tok: true,\n\t\t\t\t\t\t\tdetail: `minted ${input.amountBaseUnits} of ${input.coinType} to ${recipient}`,\n\t\t\t\t\t\t\tdigest: r.digest,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\t// Typed coin/artifact-publisher failures carry `.message`.\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardMintResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail:\n\t\t\t\t\t\t\t\ttypeof cause?.message === 'string'\n\t\t\t\t\t\t\t\t\t? cause.message\n\t\t\t\t\t\t\t\t\t: `mint failed: ${String(cause)}`,\n\t\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\t// Residual defects (interrupts, unexpected throws) — degrade\n\t\t\t\t\t// rather than crash the dashboard query.\n\t\t\t\t\tEffect.catchCause((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardMintResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail: `mint crashed: ${String(cause)}`,\n\t\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t});\n\n\t// --- Fund ACTION + fundable-coin derivation ------------------------\n\t//\n\t// Reuses devstack's IN-PROCESS funding strategies — the same strategy\n\t// registry the boot-time account funding pass dispatches through\n\t// (`plugins/account/funding.ts`). We never re-implement signing or the\n\t// exchange/pool swaps:\n\t//\n\t// - SUI (absent / canonical type) → `faucet:request:<chainId>`. The SUI\n\t// faucet strategy takes `{ address, amount }` only (no account handle)\n\t// and is FIXED-AMOUNT (the standard faucet ignores `amount`). Recipient\n\t// may be ANY 0x address.\n\t// - WAL/DEEP → `coinType:<fullCoinType>`. The strategy takes\n\t// `{ address, amount, account }` and runs an account-signed swap, so\n\t// the recipient MUST be a resolved account in the stack (we match the\n\t// recipient address against the resolved `account/<name>` values and\n\t// pass the live `AccountValue` as `req.account`).\n\t//\n\t// Never fails (`E = never`): every reject path degrades to\n\t// `{ ok:false, detail }` so a single bad fund can't take down the query.\n\n\t/** The chain id the SUI funding path keys on, read from the resolved sui\n\t * value (same `chain` field the sui codegen/snapshot decls stamp). */\n\tconst readChainId = control.resolvedValues.pipe(\n\t\tEffect.map((values) => {\n\t\t\tconst sui = matching(values, (id) => id === 'sui')[0];\n\t\t\treturn sui === undefined ? null : str((sui.value as SuiShape).chain);\n\t\t}),\n\t);\n\n\tconst fundableCoins: DashboardDomain['fundableCoins'] = Effect.gen(function* () {\n\t\t// SUI is always fundable when a faucet strategy is registered for the\n\t\t// active chain; the fixed-amount faucet ignores the requested amount.\n\t\tconst out: DashboardFundableCoin[] = [];\n\t\tif (strategyRegistry === null) return out;\n\n\t\tconst chain = yield* readChainId;\n\t\tconst keys = yield* strategyRegistry.list();\n\t\tif (chain !== null && keys.includes(faucetCapabilityKeyFor(chain))) {\n\t\t\tout.push({\n\t\t\t\tsymbol: 'SUI',\n\t\t\t\tcoinType: SUI_FULL_COIN_TYPE,\n\t\t\t\thonorsAmount: false,\n\t\t\t\trequiresAccountSigner: false,\n\t\t\t});\n\t\t}\n\n\t\t// A coin is fundable iff a `coinType:<fullCoinType>` strategy is actually\n\t\t// registered (the walrus/deepbook/coin plugin contributed one). Derive the\n\t\t// display symbol from the coin type's module path (`::wal::WAL` → WAL,\n\t\t// `::deep::DEEP` → DEEP) so we stay name-blind on the coin VALUE while\n\t\t// still labeling the pill. `requiresAccountSigner` is read from the\n\t\t// strategy's own `requiresRecipientAccount` flag — NOT assumed true for\n\t\t// every coin: WAL/DEEP swaps spend the recipient's SUI (true), but a\n\t\t// managed-coin MINT strategy transfers to a passive recipient (false), so\n\t\t// it can fund any 0x address.\n\t\tfor (const key of keys) {\n\t\t\tif (!key.startsWith('coinType:')) continue;\n\t\t\tconst coinType = key.slice('coinType:'.length);\n\t\t\tif (coinType === SUI_FULL_COIN_TYPE) continue;\n\t\t\tconst strategy = yield* strategyRegistry\n\t\t\t\t.get<typeof key, AccountFundingStrategy<unknown, AccountValue>>(key)\n\t\t\t\t.pipe(Effect.catchTag('StrategyNotFoundError', () => Effect.succeed(null)));\n\t\t\tout.push({\n\t\t\t\tsymbol: coinSymbolFromType(coinType),\n\t\t\t\tcoinType,\n\t\t\t\thonorsAmount: true,\n\t\t\t\trequiresAccountSigner: strategy?.requiresRecipientAccount ?? false,\n\t\t\t});\n\t\t}\n\t\treturn out;\n\t});\n\n\tconst fundAccount: DashboardDomain['fundAccount'] = (input) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst recipient = input.recipient.trim();\n\t\t\tif (!SUI_ADDRESS_RE.test(recipient)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: `invalid recipient '${input.recipient}': expected a 0x-prefixed Sui address`,\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t}\n\t\t\tif (strategyRegistry === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: 'funding unavailable: no strategy registry wired',\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t}\n\n\t\t\tconst coinType = input.coinType?.trim() ?? '';\n\t\t\tconst isSui = coinType === '' || coinType === SUI_FULL_COIN_TYPE;\n\n\t\t\t// SUI path — chain faucet strategy. Fixed-amount: we pass a nominal\n\t\t\t// amount but the standard faucet ignores it. Recipient may be any\n\t\t\t// 0x address (no account handle required).\n\t\t\tif (isSui) {\n\t\t\t\tconst chain = yield* readChainId;\n\t\t\t\tif (chain === null) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\tdetail: 'cannot fund SUI: no resolved sui plugin / chain id in this stack',\n\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t\t}\n\t\t\t\tconst key = faucetCapabilityKeyFor(chain);\n\t\t\t\tconst strategy = yield* strategyRegistry\n\t\t\t\t\t.get<typeof key, FaucetStrategy>(key)\n\t\t\t\t\t.pipe(Effect.catchTag('StrategyNotFoundError', () => Effect.succeed(null)));\n\t\t\t\tif (strategy === null) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\tdetail: `no SUI faucet strategy registered for chain '${chain}'`,\n\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t\t}\n\t\t\t\t// `amount` is nominal — the standard faucet grant is fixed and\n\t\t\t\t// ignores it. Default to 1 SUI in MIST so a strategy that DID\n\t\t\t\t// honor it lands a sane value.\n\t\t\t\tconst amount = parseAmount(input.amountBaseUnits) ?? 1_000_000_000n;\n\t\t\t\treturn yield* strategy.request({ address: recipient, amount }).pipe(\n\t\t\t\t\tEffect.map(\n\t\t\t\t\t\t(): DashboardFundResult => ({\n\t\t\t\t\t\t\tok: true,\n\t\t\t\t\t\t\tdetail: `requested SUI for ${recipient} (fixed-amount faucet grant)`,\n\t\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardFundResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail: `SUI faucet request failed: ${faucetCauseDetail(cause)}`,\n\t\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catchCause((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardFundResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail: `SUI faucet request crashed: ${String(cause)}`,\n\t\t\t\t\t\t\tdigest: null,\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\t// WAL/DEEP path — coin-specific account-signed funding strategy.\n\t\t\tconst amount = parseAmount(input.amountBaseUnits);\n\t\t\tif (amount === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: `invalid amountBaseUnits '${input.amountBaseUnits ?? ''}': expected a positive integer string`,\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t}\n\n\t\t\tconst key = `coinType:${coinType}` as const;\n\t\t\tconst strategy = yield* strategyRegistry\n\t\t\t\t.get<typeof key, AccountFundingStrategy<unknown, AccountValue>>(key)\n\t\t\t\t.pipe(Effect.catchTag('StrategyNotFoundError', () => Effect.succeed(null)));\n\t\t\tif (strategy === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail:\n\t\t\t\t\t\t`no funding strategy registered for coin '${coinType}' — ` +\n\t\t\t\t\t\t'WAL needs the walrus plugin (with an exchange) and DEEP needs the deepbook plugin',\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t}\n\n\t\t\t// Match the recipient to a resolved account, if one holds this address.\n\t\t\tconst values = yield* control.resolvedValues;\n\t\t\tconst account = matching(values, (id) => id.startsWith('account/'))\n\t\t\t\t.map(({ value }) => value as AccountShape)\n\t\t\t\t.find((v) => str(v.address) === recipient);\n\n\t\t\t// Account-spending strategies (WAL/DEEP) swap the recipient's OWN SUI,\n\t\t\t// so the recipient must BE a resolved account with a signer. Mint-style\n\t\t\t// strategies (managed coins) transfer to a passive recipient, so any 0x\n\t\t\t// address is fine — gate the rejection on the strategy's flag, not on\n\t\t\t// the coin being non-SUI.\n\t\t\tif (strategy.requiresRecipientAccount === true && account === undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail:\n\t\t\t\t\t\t`coin '${coinType}' is funded by an account-signed swap, but '${recipient}' ` +\n\t\t\t\t\t\t'is not a resolved account in this stack — fund a configured account (the swap spends its SUI)',\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t}\n\n\t\t\treturn yield* strategy\n\t\t\t\t.request({\n\t\t\t\t\taddress: recipient,\n\t\t\t\t\tamount,\n\t\t\t\t\t...(account === undefined ? {} : { account: account as unknown as AccountValue }),\n\t\t\t\t})\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.map(\n\t\t\t\t\t\t(): DashboardFundResult => ({\n\t\t\t\t\t\t\tok: true,\n\t\t\t\t\t\t\tdetail: `funded ${amount} base units of ${coinType} to ${recipient}`,\n\t\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardFundResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail: `${coinSymbolFromType(coinType)} funding failed: ${fundingCauseDetail(cause)}`,\n\t\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catchCause((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardFundResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail: `${coinSymbolFromType(coinType)} funding crashed: ${String(cause)}`,\n\t\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t});\n\n\tconst postgresStats: DashboardDomain['postgresStats'] = Effect.gen(function* () {\n\t\tconst values = yield* control.resolvedValues;\n\t\tconst instances = matching(values, (id) => id === 'postgres');\n\t\tif (instances.length === 0) return [];\n\t\tconst out: DashboardPostgresStats[] = [];\n\t\tfor (const { pluginKey, value } of instances) {\n\t\t\tconst pg = value as PostgresShape;\n\t\t\tif (containerRuntime === null) {\n\t\t\t\tout.push(unavailablePgStats(pluginKey, pg, 'container runtime unavailable'));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst database = strReq((pg.databases ?? [])[0]) || 'postgres';\n\t\t\tconst pgUser = str(pg.user) ?? 'postgres';\n\t\t\tconst role = str(pg.name) ?? 'postgres';\n\t\t\tconst probe = Effect.gen(function* () {\n\t\t\t\tconst handles = yield* containerRuntime.inspectByLabels({\n\t\t\t\t\tapp: String(identity.app),\n\t\t\t\t\tstack: String(identity.stack),\n\t\t\t\t\tplugin: 'postgres',\n\t\t\t\t\trole,\n\t\t\t\t});\n\t\t\t\tconst running = handles.find((h) => h.status === 'running') ?? handles[0];\n\t\t\t\tif (running === undefined) {\n\t\t\t\t\treturn unavailablePgStats(pluginKey, pg, 'no running postgres container found');\n\t\t\t\t}\n\t\t\t\tconst result = yield* containerRuntime.exec(running, [\n\t\t\t\t\t'psql',\n\t\t\t\t\t'-U',\n\t\t\t\t\tpgUser,\n\t\t\t\t\t'-d',\n\t\t\t\t\tdatabase,\n\t\t\t\t\t'-tAc',\n\t\t\t\t\tPG_STATS_SQL,\n\t\t\t\t]);\n\t\t\t\tif (result.exitCode !== 0) {\n\t\t\t\t\treturn unavailablePgStats(\n\t\t\t\t\t\tpluginKey,\n\t\t\t\t\t\tpg,\n\t\t\t\t\t\t`psql exited ${result.exitCode}: ${result.stderr.trim().slice(0, 200)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn parsePgStats(pluginKey, pg, result.stdout);\n\t\t\t});\n\t\t\tconst stats = yield* probe.pipe(\n\t\t\t\tEffect.catchCause((cause) =>\n\t\t\t\t\tEffect.succeed(unavailablePgStats(pluginKey, pg, String(cause))),\n\t\t\t\t),\n\t\t\t);\n\t\t\tout.push(stats);\n\t\t}\n\t\treturn out;\n\t});\n\n\treturn {\n\t\tmode,\n\t\tdeepbook,\n\t\tseal,\n\t\tcoinCaps,\n\t\tpostgresStats,\n\t\tmintCoin,\n\t\tfundableCoins,\n\t\tfundAccount,\n\t};\n};\n\n/** An all-empty dashboard domain. Used by tests that exercise the\n * schema/server without a live registry. Every accessor resolves to\n * empty/`null`. */\nexport const emptyDashboardDomain: DashboardDomain = {\n\tmode: Effect.succeed(null),\n\tdeepbook: Effect.succeed([]),\n\tseal: Effect.succeed([]),\n\tcoinCaps: Effect.succeed([]),\n\tpostgresStats: Effect.succeed([]),\n\tmintCoin: () => Effect.succeed({ ok: false, detail: 'unavailable', digest: null }),\n\tfundableCoins: Effect.succeed([]),\n\tfundAccount: () => Effect.succeed({ ok: false, detail: 'unavailable', digest: null }),\n};\n"],"mappings":";;;;;;;;AAiTA,MAAM,iBAAiB;;;;;AAMvB,MAAM,qBAAqB;;;;AAK3B,MAAM,2BAA2B,MAAuB;AACvD,KAAI,CAAC,QAAQ,KAAK,EAAE,CAAE,QAAO;AAC7B,KAAI;AACH,SAAO,OAAO,EAAE,GAAG;SACZ;AACP,SAAO;;;;;;AAOT,MAAM,0BACL,UACwD,GAAG,6BAA6B,GAAG;;;AAI5F,MAAM,eAAe,MAAgD;AACpE,KAAI,KAAK,KAAM,QAAO;CACtB,MAAM,UAAU,EAAE,MAAM;AACxB,KAAI,CAAC,wBAAwB,QAAQ,CAAE,QAAO;AAC9C,QAAO,OAAO,QAAQ;;;;;;AAOvB,MAAM,sBAAsB,aAA6B;CACxD,MAAM,QAAQ,SAAS,MAAM,KAAK;CAClC,MAAM,SAAS,MAAM,MAAM,SAAS;AACpC,QAAO,WAAW,KAAA,KAAa,OAAO,SAAS,IAAI,SAAS;;;;;AAM7D,MAAM,eAAe,UAA2B;AAC/C,KAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;EAChD,MAAM,IAAI;AAKV,MAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAQ,SAAS,EAAG,QAAO,EAAE;AACpE,MAAI,OAAO,EAAE,WAAW,YAAY,EAAE,OAAO,SAAS,EAAG,QAAO,EAAE;AAClE,MAAI,OAAO,EAAE,SAAS,YAAY,EAAE,KAAK,SAAS,EAAG,QAAO,EAAE;;AAE/D,QAAO,OAAO,MAAM;;AAErB,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AAE3B,MAAM,OAAO,MAA+B,OAAO,MAAM,WAAW,IAAI;AACxE,MAAM,UAAU,MAAwB,OAAO,MAAM,WAAW,IAAI;AACpE,MAAM,OAAO,MAA+B,OAAO,MAAM,YAAY,OAAO,SAAS,EAAE,GAAG,IAAI;AAC9F,MAAM,QAAQ,MAAwB,MAAM;;;;AAK5C,MAAM,YACL,QACA,YAC8C,OAAO,QAAQ,MAAM,QAAQ,EAAE,GAAG,CAAC;;;AAQlF,MAAM,eAAe;;;;;;;;;;;;;;AAerB,MAAM,gBACL,WACA,IACA,WAC4B;CAK5B,MAAM,OAAO;EACZ;EACA,UANgB,QAAQ,GAAG,aAAa,EAAE,EAAE,GAAG,IAAI;EAOnD,UAAA,cANY,IAAI,GAAG,aAAa,IAAI,WAED,GADvB,IAAI,GAAG,KAAK,IAAI;EAM5B;AACD,KAAI;EACH,MAAM,UAAU,OAAO,MAAM;EAC7B,MAAM,SAAS,KAAK,MAAM,QAAQ;EAUlC,MAAM,UAAoC,OAAO,UAAU,EAAE,EAAE,KAAK,OAAO;GAC1E,QAAQ,EAAE,UAAU;GACpB,MAAM,EAAE,QAAQ;GAChB,aAAa,IAAI,EAAE,aAAa,IAAI;GACpC,YAAY,IAAI,EAAE,YAAY,IAAI;GAClC,EAAE;AACH,SAAO;GACN,GAAG;GACH,eAAe,IAAI,OAAO,SAAS,IAAI;GACvC,iBAAiB,IAAI,OAAO,YAAY,IAAI;GAC5C;GACA,WAAW;GACX,QAAQ;GACR;UACO,OAAO;AACf,SAAO;GACN,GAAG;GACH,eAAe;GACf,iBAAiB;GACjB,QAAQ,EAAE;GACV,WAAW;GACX,QAAQ,gCAAgC,OAAO,MAAM;GACrD;;;AAIH,MAAM,sBACL,WACA,IACA,WAC4B;AAI5B,QAAO;EACN;EACA,UALgB,QAAQ,GAAG,aAAa,EAAE,EAAE,GAAG,IAAI;EAMnD,UAAU,cALE,IAAI,GAAG,aAAa,IAAI,WAKP,GAJjB,IAAI,GAAG,KAAK,IAAI;EAK5B,eAAe;EACf,iBAAiB;EACjB,QAAQ,EAAE;EACV,WAAW;EACX;EACA;;AAuBF,MAAa,wBAAwB,SAA+C;CACnF,MAAM,EAAE,SAAS,UAAU,kBAAkB,qBAAqB;CAElE,MAAM,OAAgC,QAAQ,eAAe,KAC5D,OAAO,KAAK,WAAW;EACtB,MAAM,MAAM,SAAS,SAAS,OAAO,OAAO,MAAM,CAAC;AACnD,MAAI,QAAQ,KAAA,EAAW,QAAO;AAE9B,UADW,IAAI,MAAmB,MAClC;GACC,KAAK,OACJ,QAAO;GACR,KAAK,OACJ,QAAO;GACR,KAAK;GACL,KAAK,YACJ,QAAO;GACR,QACC,QAAO;;GAER,CACF;CAED,MAAM,WAAwC,QAAQ,eAAe,KACpE,OAAO,KAAK,WACX,SAAS,SAAS,OAAO,GAAG,WAAW,YAAY,CAAC,CAAC,KACnD,EAAE,WAAW,YAAmC;EAChD,MAAM,IAAI;EACV,MAAM,UAAU,EAAE;EAClB,MAAM,SACL,YAAY,aAAa,aAAa,YAAY,UAAU,UAAU;AACvE,SAAO;GACN;GACA,MAAM,UAAU,QAAQ,cAAc,GAAG;GACzC,MAAM;GACN,OAAO,OAAO,EAAE,MAAM;GACtB,WAAW,OAAO,EAAE,UAAU;GAC9B,YAAY,OAAO,EAAE,WAAW;GAChC,YAAY,IAAI,EAAE,WAAW;GAC7B,gBAAgB,IAAI,EAAE,eAAe;GACrC,QAAQ,EAAE,SAAS,EAAE,EAAE,KAAK,OAAO;IAClC,MAAM,OAAO,EAAE,KAAK;IACpB,QAAQ,OAAO,EAAE,OAAO;IACxB,cAAc,OAAO,EAAE,aAAa;IACpC,eAAe,OAAO,EAAE,cAAc;IACtC,EAAE;GACH,oBAAoB,KAAK,EAAE,mBAAmB;GAC9C,WAAW,IAAI,EAAE,UAAU;GAC3B,YAAY,IAAI,EAAE,WAAW;GAC7B;GAEF,CACD,CACD;CAED,MAAM,OAAgC,QAAQ,eAAe,KAC5D,OAAO,KAAK,WACX,SAAS,SAAS,OAAO,GAAG,WAAW,QAAQ,CAAC,CAAC,KAC/C,EAAE,WAAW,YAA+B;EAC5C,MAAM,IAAI;EACV,MAAM,WACL,EAAE,SAAS,SAAS,SAAS,EAAE,SAAS,eAAe,eAAe;EACvE,MAAM,cAAc,EAAE,iBAAiB,EAAE,EAAE,KAAK,OAAO;GACtD,UAAU,OAAO,EAAE,SAAS;GAC5B,QAAQ,IAAI,EAAE,OAAO,IAAI;GACzB,EAAE;AACH,SAAO;GACN;GACA,MAAM;GACN,UAAU,OAAO,EAAE,SAAS;GAC5B,cAAc,OAAO,EAAE,aAAa;GACpC;GACA,WAAW,WAAW;GACtB;GAEF,CACD,CACD;CAED,MAAM,WAAwC,QAAQ,eAAe,KACpE,OAAO,KAAK,WACX,SAAS,SAAS,OAAO,GAAG,WAAW,QAAQ,CAAC,CAAC,KAC/C,EAAE,WAAW,YAA8B;EAC3C,MAAM,IAAI;EACV,MAAM,SACL,EAAE,WAAW,aAAa,aAAa,EAAE,WAAW,YAAY,YAAY;AAC7E,SAAO;GACN;GACA,QAAQ,IAAI,EAAE,OAAO;GACrB,cAAc,OAAO,EAAE,aAAa;GACpC,UAAU,IAAI,EAAE,SAAS,IAAI;GAC7B;GACA,eAAe,IAAI,EAAE,cAAc;GACnC,WAAW,IAAI,EAAE,UAAU;GAC3B;GAEF,CACD,CACD;CAgBD,MAAM,YAAyC,UAC9C,OAAO,IAAI,aAAa;EACvB,MAAM,YAAY,MAAM,UAAU,MAAM;AACxC,MAAI,CAAC,eAAe,KAAK,UAAU,CAClC,QAAO;GACN,IAAI;GACJ,QAAQ,sBAAsB,MAAM,UAAU;GAC9C,QAAQ;GACR;AAEF,MAAI,CAAC,wBAAwB,MAAM,gBAAgB,CAClD,QAAO;GACN,IAAI;GACJ,QAAQ,4BAA4B,MAAM,gBAAgB;GAC1D,QAAQ;GACR;EAQF,MAAM,QAAQ,SAAS,OADD,QAAQ,iBACE,OAAO,GAAG,WAAW,QAAQ,CAAC,CAC5D,KAAK,EAAE,YAAY,MAAmB,CACtC,MAAM,MAAM,OAAO,EAAE,aAAa,KAAK,MAAM,SAAS;AAExD,MAAI,UAAU,KAAA,EACb,QAAO;GACN,IAAI;GACJ,QAAQ,oCAAoC,MAAM,SAAS;GAC3D,QAAQ;GACR;AAEF,MAAI,OAAO,MAAM,gBAAgB,WAChC,QAAO;GACN,IAAI;GACJ,QACC,SAAS,MAAM,SAAS;GAEzB,QAAQ;GACR;AAGF,SAAO,OAAO,MACZ,YAAY;GAAE,IAAI;GAAW,QAAQ,OAAO,MAAM,gBAAgB;GAAE,CAAC,CACrE,KACA,OAAO,KACL,OAA4B;GAC5B,IAAI;GACJ,QAAQ,UAAU,MAAM,gBAAgB,MAAM,MAAM,SAAS,MAAM;GACnE,QAAQ,EAAE;GACV,EACD,EAED,OAAO,OAAO,UACb,OAAO,QAA6B;GACnC,IAAI;GACJ,QACC,OAAO,OAAO,YAAY,WACvB,MAAM,UACN,gBAAgB,OAAO,MAAM;GACjC,QAAQ;GACR,CAAC,CACF,EAGD,OAAO,YAAY,UAClB,OAAO,QAA6B;GACnC,IAAI;GACJ,QAAQ,iBAAiB,OAAO,MAAM;GACtC,QAAQ;GACR,CAAC,CACF,CACD;GACD;;;CAwBH,MAAM,cAAc,QAAQ,eAAe,KAC1C,OAAO,KAAK,WAAW;EACtB,MAAM,MAAM,SAAS,SAAS,OAAO,OAAO,MAAM,CAAC;AACnD,SAAO,QAAQ,KAAA,IAAY,OAAO,IAAK,IAAI,MAAmB,MAAM;GACnE,CACF;CAED,MAAM,gBAAkD,OAAO,IAAI,aAAa;EAG/E,MAAM,MAA+B,EAAE;AACvC,MAAI,qBAAqB,KAAM,QAAO;EAEtC,MAAM,QAAQ,OAAO;EACrB,MAAM,OAAO,OAAO,iBAAiB,MAAM;AAC3C,MAAI,UAAU,QAAQ,KAAK,SAAS,uBAAuB,MAAM,CAAC,CACjE,KAAI,KAAK;GACR,QAAQ;GACR,UAAU;GACV,cAAc;GACd,uBAAuB;GACvB,CAAC;AAYH,OAAK,MAAM,OAAO,MAAM;AACvB,OAAI,CAAC,IAAI,WAAW,YAAY,CAAE;GAClC,MAAM,WAAW,IAAI,MAAM,EAAmB;AAC9C,OAAI,aAAa,mBAAoB;GACrC,MAAM,WAAW,OAAO,iBACtB,IAA+D,IAAI,CACnE,KAAK,OAAO,SAAS,+BAA+B,OAAO,QAAQ,KAAK,CAAC,CAAC;AAC5E,OAAI,KAAK;IACR,QAAQ,mBAAmB,SAAS;IACpC;IACA,cAAc;IACd,uBAAuB,UAAU,4BAA4B;IAC7D,CAAC;;AAEH,SAAO;GACN;CAEF,MAAM,eAA+C,UACpD,OAAO,IAAI,aAAa;EACvB,MAAM,YAAY,MAAM,UAAU,MAAM;AACxC,MAAI,CAAC,eAAe,KAAK,UAAU,CAClC,QAAO;GACN,IAAI;GACJ,QAAQ,sBAAsB,MAAM,UAAU;GAC9C,QAAQ;GACR;AAEF,MAAI,qBAAqB,KACxB,QAAO;GACN,IAAI;GACJ,QAAQ;GACR,QAAQ;GACR;EAGF,MAAM,WAAW,MAAM,UAAU,MAAM,IAAI;AAM3C,MALc,aAAa,MAAM,aAAa,oBAKnC;GACV,MAAM,QAAQ,OAAO;AACrB,OAAI,UAAU,KACb,QAAO;IACN,IAAI;IACJ,QAAQ;IACR,QAAQ;IACR;GAEF,MAAM,MAAM,uBAAuB,MAAM;GACzC,MAAM,WAAW,OAAO,iBACtB,IAAgC,IAAI,CACpC,KAAK,OAAO,SAAS,+BAA+B,OAAO,QAAQ,KAAK,CAAC,CAAC;AAC5E,OAAI,aAAa,KAChB,QAAO;IACN,IAAI;IACJ,QAAQ,gDAAgD,MAAM;IAC9D,QAAQ;IACR;GAKF,MAAM,SAAS,YAAY,MAAM,gBAAgB,IAAI;AACrD,UAAO,OAAO,SAAS,QAAQ;IAAE,SAAS;IAAW;IAAQ,CAAC,CAAC,KAC9D,OAAO,WACsB;IAC3B,IAAI;IACJ,QAAQ,qBAAqB,UAAU;IACvC,QAAQ;IACR,EACD,EACD,OAAO,OAAO,UACb,OAAO,QAA6B;IACnC,IAAI;IACJ,QAAQ,8BAA8B,kBAAkB,MAAM;IAC9D,QAAQ;IACR,CAAC,CACF,EACD,OAAO,YAAY,UAClB,OAAO,QAA6B;IACnC,IAAI;IACJ,QAAQ,+BAA+B,OAAO,MAAM;IACpD,QAAQ;IACR,CAAC,CACF,CACD;;EAIF,MAAM,SAAS,YAAY,MAAM,gBAAgB;AACjD,MAAI,WAAW,KACd,QAAO;GACN,IAAI;GACJ,QAAQ,4BAA4B,MAAM,mBAAmB,GAAG;GAChE,QAAQ;GACR;EAGF,MAAM,MAAM,YAAY;EACxB,MAAM,WAAW,OAAO,iBACtB,IAA+D,IAAI,CACnE,KAAK,OAAO,SAAS,+BAA+B,OAAO,QAAQ,KAAK,CAAC,CAAC;AAC5E,MAAI,aAAa,KAChB,QAAO;GACN,IAAI;GACJ,QACC,4CAA4C,SAAS;GAEtD,QAAQ;GACR;EAKF,MAAM,UAAU,SAAS,OADH,QAAQ,iBACI,OAAO,GAAG,WAAW,WAAW,CAAC,CACjE,KAAK,EAAE,YAAY,MAAsB,CACzC,MAAM,MAAM,IAAI,EAAE,QAAQ,KAAK,UAAU;AAO3C,MAAI,SAAS,6BAA6B,QAAQ,YAAY,KAAA,EAC7D,QAAO;GACN,IAAI;GACJ,QACC,SAAS,SAAS,8CAA8C,UAAU;GAE3E,QAAQ;GACR;AAGF,SAAO,OAAO,SACZ,QAAQ;GACR,SAAS;GACT;GACA,GAAI,YAAY,KAAA,IAAY,EAAE,GAAG,EAAW,SAAoC;GAChF,CAAC,CACD,KACA,OAAO,WACsB;GAC3B,IAAI;GACJ,QAAQ,UAAU,OAAO,iBAAiB,SAAS,MAAM;GACzD,QAAQ;GACR,EACD,EACD,OAAO,OAAO,UACb,OAAO,QAA6B;GACnC,IAAI;GACJ,QAAQ,GAAG,mBAAmB,SAAS,CAAC,mBAAmB,mBAAmB,MAAM;GACpF,QAAQ;GACR,CAAC,CACF,EACD,OAAO,YAAY,UAClB,OAAO,QAA6B;GACnC,IAAI;GACJ,QAAQ,GAAG,mBAAmB,SAAS,CAAC,oBAAoB,OAAO,MAAM;GACzE,QAAQ;GACR,CAAC,CACF,CACD;GACD;AAuDH,QAAO;EACN;EACA;EACA;EACA;EACA,eA1DuD,OAAO,IAAI,aAAa;GAE/E,MAAM,YAAY,SAAS,OADL,QAAQ,iBACM,OAAO,OAAO,WAAW;AAC7D,OAAI,UAAU,WAAW,EAAG,QAAO,EAAE;GACrC,MAAM,MAAgC,EAAE;AACxC,QAAK,MAAM,EAAE,WAAW,WAAW,WAAW;IAC7C,MAAM,KAAK;AACX,QAAI,qBAAqB,MAAM;AAC9B,SAAI,KAAK,mBAAmB,WAAW,IAAI,gCAAgC,CAAC;AAC5E;;IAED,MAAM,WAAW,QAAQ,GAAG,aAAa,EAAE,EAAE,GAAG,IAAI;IACpD,MAAM,SAAS,IAAI,GAAG,KAAK,IAAI;IAC/B,MAAM,OAAO,IAAI,GAAG,KAAK,IAAI;IA8B7B,MAAM,QAAQ,OA7BA,OAAO,IAAI,aAAa;KACrC,MAAM,UAAU,OAAO,iBAAiB,gBAAgB;MACvD,KAAK,OAAO,SAAS,IAAI;MACzB,OAAO,OAAO,SAAS,MAAM;MAC7B,QAAQ;MACR;MACA,CAAC;KACF,MAAM,UAAU,QAAQ,MAAM,MAAM,EAAE,WAAW,UAAU,IAAI,QAAQ;AACvE,SAAI,YAAY,KAAA,EACf,QAAO,mBAAmB,WAAW,IAAI,sCAAsC;KAEhF,MAAM,SAAS,OAAO,iBAAiB,KAAK,SAAS;MACpD;MACA;MACA;MACA;MACA;MACA;MACA;MACA,CAAC;AACF,SAAI,OAAO,aAAa,EACvB,QAAO,mBACN,WACA,IACA,eAAe,OAAO,SAAS,IAAI,OAAO,OAAO,MAAM,CAAC,MAAM,GAAG,IAAI,GACrE;AAEF,YAAO,aAAa,WAAW,IAAI,OAAO,OAAO;MAExB,CAAC,KAC1B,OAAO,YAAY,UAClB,OAAO,QAAQ,mBAAmB,WAAW,IAAI,OAAO,MAAM,CAAC,CAAC,CAChE,CACD;AACD,QAAI,KAAK,MAAM;;AAEhB,UAAO;IAQM;EACb;EACA;EACA;EACA;;AAOK,OAAO,QAAQ,KAAK,EAChB,OAAO,QAAQ,EAAE,CAAC,EACtB,OAAO,QAAQ,EAAE,CAAC,EACd,OAAO,QAAQ,EAAE,CAAC,EACb,OAAO,QAAQ,EAAE,CAAC,EAElB,OAAO,QAAQ,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"domain.mjs","names":[],"sources":["../../../src/plugins/dashboard/domain.ts"],"sourcesContent":["// Dashboard plugin-domain shaping.\n//\n// The substrate control-plane is GENERIC + name-blind: it hands out the\n// snapshot catalog, the observability rings, and a single uninterpreted\n// `resolvedValues` accessor (see `substrate/runtime/control-plane/`). This\n// module — which lives in the PLUGIN layer and is allowed to name plugins —\n// owns ALL plugin-name-aware shaping: it matches resolved plugin values by\n// resource id (`deepbook/`-prefix, `seal:`-prefix, `coin:`-prefix, `id ===\n// 'sui'`) and projects them into the app-agnostic shapes the GraphQL schema\n// renders. It also owns the `mode` derivation and the coin `mint` action.\n//\n// Design discipline (mirrors the old substrate seam, one layer up):\n// - We import NO plugin types — we narrow the opaque `unknown` resolved\n// value through shallow structural shapes that mirror the relevant\n// fields. A field missing on the live value collapses to the\n// null/empty default.\n// - Every accessor degrades to empty/`null` rather than failing, so a\n// single missing/uninitialised plugin can't take down a dashboard\n// query (`E = never` on the public surface).\n// - We match plugins by RESOURCE ID — a prefix for the multi-instance\n// kinds (`deepbook/`, `seal:`, `coin:`) and an exact id for the\n// singletons (`id === 'sui'`) — rather than\n// plugin-key substrings: the resource id is the stable identity the\n// plugin factories mint.\n\nimport { Effect } from 'effect';\n\nimport type {\n\tControlPlaneDomain,\n\tControlPlaneResolvedValue,\n} from '../../substrate/runtime/control-plane/service.ts';\nimport type { StrategyRegistry } from '../../contracts/strategy-contributor.ts';\nimport type { FaucetStrategy } from '../../contracts/faucet-strategy.ts';\nimport type { AccountFundingStrategy } from '../../contracts/funding-strategy.ts';\nimport type { AccountValue } from '../account/index.ts';\n// The faucet capability-key prefix is owned by the faucet plugin (single\n// source of truth). The dashboard plugin layer is allowed to name plugins,\n// so importing the key constructor is the same cross-plugin reference the\n// account funding pass (`plugins/account/funding.ts`) uses — NOT a resolved\n// plugin-VALUE import (those stay structurally narrowed below). Imported via\n// the sibling's barrel to satisfy the plugin-boundary invariant.\nimport { FAUCET_CAPABILITY_KEY_PREFIX } from '../faucet/index.ts';\n\n// -----------------------------------------------------------------------------\n// App-agnostic domain shapes the GraphQL schema renders.\n// -----------------------------------------------------------------------------\n\n/** One DeepBook pool object id + coin types (prices are chain-direct). */\nexport interface DashboardDeepbookPool {\n\treadonly name: string;\n\treadonly poolId: string;\n\treadonly baseCoinType: string;\n\treadonly quoteCoinType: string;\n}\n\n/** One Pyth price feed seeded inside DeepBook at boot. `price`/`expo` are the\n * FIXED boot-time mock values (static, not a live quote); `price` is carried\n * as a string so large signed values survive the wire without precision loss. */\nexport interface DashboardPythFeed {\n\treadonly symbol: string;\n\treadonly priceInfoObjectId: string;\n\treadonly price: string;\n\treadonly expo: number;\n}\n\nexport interface DashboardDeepbookInfo {\n\treadonly pluginKey: string;\n\treadonly name: string;\n\treadonly mode: 'local' | 'override' | 'known';\n\treadonly network: string;\n\treadonly packageId: string;\n\treadonly registryId: string;\n\treadonly adminCapId: string | null;\n\treadonly deepTreasuryId: string | null;\n\treadonly pools: ReadonlyArray<DashboardDeepbookPool>;\n\t/** Pyth price feeds seeded inside this DeepBook deployment (boot-time mock\n\t * prices). Empty when no Pyth feeds were configured. */\n\treadonly pythFeeds: ReadonlyArray<DashboardPythFeed>;\n\t/** `true` when one or more pools placed seed orders at boot (NOT a\n\t * market-maker process state). */\n\treadonly hasSeedLiquidity: boolean;\n\treadonly serverUrl: string | null;\n\treadonly indexerUrl: string | null;\n\t/** Non-null when one or more required fields failed to narrow off the\n\t * opaque resolved value (fail-loud: schema drift surfaced to the caller\n\t * instead of silently degrading). */\n\treadonly narrowingFault: string | null;\n}\n\nexport interface DashboardSealKeyServer {\n\treadonly objectId: string;\n\treadonly weight: number;\n}\n\nexport interface DashboardSealInfo {\n\treadonly pluginKey: string;\n\treadonly mode: 'local-keygen' | 'live' | 'fork-known';\n\treadonly objectId: string;\n\treadonly keyServerUrl: string;\n\treadonly keyServers: ReadonlyArray<DashboardSealKeyServer>;\n\t/** Threshold = number of registered key-server configs. */\n\treadonly threshold: number;\n\t/** Non-null when one or more required fields failed to narrow off the\n\t * opaque resolved value (fail-loud). */\n\treadonly narrowingFault: string | null;\n}\n\n/** A coin's treasury-cap id (drives the Mint action) + addressing facts. */\nexport interface DashboardCoinCap {\n\treadonly pluginKey: string;\n\treadonly symbol: string | null;\n\treadonly fullCoinType: string;\n\treadonly decimals: number;\n\treadonly source: 'registry' | 'on-chain' | 'builtin';\n\treadonly treasuryCapId: string | null;\n\treadonly packageId: string | null;\n\t/** Non-null when one or more required fields failed to narrow off the\n\t * opaque resolved value (fail-loud). */\n\treadonly narrowingFault: string | null;\n}\n\n/** Input for the dashboard mint ACTION. `amountBaseUnits` is the raw\n * integer amount in the coin's smallest unit (decimals already applied\n * by the caller / form) — a string so large u64 values survive the wire\n * without precision loss. */\nexport interface DashboardMintInput {\n\treadonly coinType: string;\n\treadonly recipient: string;\n\treadonly amountBaseUnits: string;\n}\n\n/** Outcome of a dashboard mint ACTION. Mirrors the snapshot\n * restore/delete result shape plus the on-chain tx `digest` on success. */\nexport interface DashboardMintResult {\n\treadonly ok: boolean;\n\treadonly detail: string;\n\treadonly digest: string | null;\n}\n\n/** Input for the dashboard fund ACTION.\n *\n * `coinType` selects the funding strategy: absent or the canonical SUI\n * type routes through the chain's faucet strategy (`faucet:request:<chainId>`,\n * fixed-amount); any other full coin type routes through the coin-specific\n * `coinType:<fullCoinType>` strategy (WAL exchange swap / DEEP pool swap).\n *\n * `amountBaseUnits` is the raw integer amount in the coin's smallest unit\n * (a string so large u64 values survive the wire without precision loss).\n * The SUI faucet is fixed-amount and IGNORES it; WAL/DEEP honor it. */\nexport interface DashboardFundInput {\n\treadonly recipient: string;\n\treadonly coinType?: string | null;\n\treadonly amountBaseUnits?: string | null;\n}\n\n/** Outcome of a dashboard fund ACTION. The in-process funding strategies\n * return `void` (not a digest), so the result carries only `ok`/`detail`.\n * `ok:true` means the strategy's `request(...)` completed (the faucet POST\n * landed / the swap executed on-chain); `detail` carries the reason on\n * failure. */\nexport interface DashboardFundResult {\n\treadonly ok: boolean;\n\treadonly detail: string;\n}\n\n/** One coin the dashboard faucet can actually fund, with whether the\n * underlying strategy honors a caller-supplied amount.\n *\n * SUI is always present (fixed-amount faucet, `honorsAmount: false`).\n * WAL/DEEP appear only when their plugin registered a `coinType:<X>`\n * funding strategy AND (for WAL) carries a swap that spends the\n * recipient account's SUI — so `honorsAmount: true`. */\nexport interface DashboardFundableCoin {\n\treadonly symbol: string;\n\treadonly coinType: string;\n\treadonly honorsAmount: boolean;\n\t/** True when funding requires the recipient to BE a resolved account in\n\t * the stack (the swap spends that account's own SUI via its in-process\n\t * signer). SUI is `false` (any 0x address); WAL/DEEP are `true`. */\n\treadonly requiresAccountSigner: boolean;\n}\n\n/** The dashboard plugin-domain accessor surface. Each member is a\n * self-contained Effect that never fails (`E = never`); they degrade to\n * empty/`null` so a single missing plugin can't take down the dashboard\n * query. */\nexport interface DashboardDomain {\n\t/** Fork-vs-local mode, derived from the resolved sui plugin's chain\n\t * identity. Used for advance-clock gating. `null` when no sui plugin\n\t * is present. */\n\treadonly mode: Effect.Effect<'fork' | 'local' | 'live' | null>;\n\t/** DeepBook deployments (registry/admin/pool ids + MM state). */\n\treadonly deepbook: Effect.Effect<ReadonlyArray<DashboardDeepbookInfo>>;\n\t/** Seal key-server deployments (objectId/threshold/mode). */\n\treadonly seal: Effect.Effect<ReadonlyArray<DashboardSealInfo>>;\n\t/** Coin treasury caps (drives Mint). */\n\treadonly coinCaps: Effect.Effect<ReadonlyArray<DashboardCoinCap>>;\n\t/** Mint ACTION — mints `amountBaseUnits` of `coinType` to `recipient`,\n\t * signed in-process by the treasury-cap-owning publisher signer the\n\t * resolved coin value's self-contained `mintFromCap` closure holds. */\n\treadonly mintCoin: (input: DashboardMintInput) => Effect.Effect<DashboardMintResult>;\n\t/** Coins the faucet can actually fund right now (SUI always; WAL/DEEP\n\t * only when their plugin registered a funding strategy). Drives the\n\t * faucet panel's coin pills + amount-field gating. */\n\treadonly fundableCoins: Effect.Effect<ReadonlyArray<DashboardFundableCoin>>;\n\t/** Fund ACTION — reuses devstack's in-process funding strategies. SUI\n\t * (absent/canonical type) routes through the chain faucet strategy\n\t * (fixed-amount); WAL/DEEP route through the coin-specific funding\n\t * strategy (account-signed exchange/pool swap). Real result — `ok`\n\t * reflects whether the strategy's `request(...)` completed. */\n\treadonly fundAccount: (input: DashboardFundInput) => Effect.Effect<DashboardFundResult>;\n}\n\n// -----------------------------------------------------------------------------\n// Structural projections of plugin-resolved values\n//\n// We narrow the opaque `unknown` resolved value through shallow structural\n// shapes that mirror the relevant fields. A field missing on the live value\n// collapses to the null/empty default.\n// -----------------------------------------------------------------------------\n\ninterface DeepbookShape {\n\treadonly mode?: unknown;\n\treadonly network?: unknown;\n\treadonly packageId?: unknown;\n\treadonly registryId?: unknown;\n\treadonly adminCapId?: unknown;\n\treadonly deepTreasuryId?: unknown;\n\treadonly pools?: ReadonlyArray<{\n\t\treadonly name?: unknown;\n\t\treadonly poolId?: unknown;\n\t\treadonly baseCoinType?: unknown;\n\t\treadonly quoteCoinType?: unknown;\n\t}>;\n\t/** Resolved Pyth handle (`PythHandle | null`); we read only `feeds`. */\n\treadonly pyth?: {\n\t\treadonly feeds?: ReadonlyArray<{\n\t\t\treadonly symbol?: unknown;\n\t\t\treadonly priceInfoObjectId?: unknown;\n\t\t\treadonly price?: unknown;\n\t\t\treadonly expo?: unknown;\n\t\t}>;\n\t} | null;\n\treadonly hasSeedLiquidity?: unknown;\n\treadonly serverUrl?: unknown;\n\treadonly indexerUrl?: unknown;\n}\n\ninterface SealShape {\n\treadonly mode?: unknown;\n\treadonly objectId?: unknown;\n\treadonly keyServerUrl?: unknown;\n\treadonly serverConfigs?: ReadonlyArray<{\n\t\treadonly objectId?: unknown;\n\t\treadonly weight?: unknown;\n\t}>;\n}\n\ninterface CoinShape {\n\treadonly symbol?: unknown;\n\treadonly fullCoinType?: unknown;\n\treadonly decimals?: unknown;\n\treadonly source?: unknown;\n\treadonly treasuryCapId?: unknown;\n\treadonly packageId?: unknown;\n\t/** Self-contained mint closure on the resolved coin value (present\n\t * for witness-form coins whose publisher still owns the cap). Read\n\t * structurally — we import no coin types. Returns an Effect that\n\t * resolves a `{ digest }`-bearing result or fails with a\n\t * coin/artifact-publisher tagged error. */\n\treadonly mintFromCap?: (opts: {\n\t\treadonly to: string;\n\t\treadonly amount: bigint;\n\t}) => Effect.Effect<{ readonly digest: string }, { readonly message?: unknown }>;\n}\n\ninterface SuiShape {\n\treadonly mode?: unknown;\n\t/** Genesis-digest chain id (`'sui:localnet'`, `'sui:testnet'`,\n\t * `'sui:mainnet-fork@123'`). Composes the faucet capability key\n\t * (`faucet:request:<chainId>`) the SUI funding path resolves. */\n\treadonly chainId?: unknown;\n}\n\n/** Structural shape of the resolved account value (`account/<name>`). The\n * WAL/DEEP funding strategies need this handle as `req.account` — the swap\n * spends the recipient account's own SUI through its in-process signer.\n * Narrowed to the fields the strategy dispatch reads; the full\n * `AccountValue` is assignable to it, so we cast the matched value across\n * the seam without importing the account VALUE shape structurally\n * elsewhere. */\ninterface AccountShape {\n\treadonly address?: unknown;\n\treadonly name?: unknown;\n}\n\n/** A 0x-prefixed Sui address: `0x` + 1..64 hex digits. Mirrors the\n * address validation the mint PTB's `tx.pure.address` ultimately\n * enforces, surfaced up front so the dashboard gets a clean rejection\n * rather than an opaque build failure. */\nconst SUI_ADDRESS_RE = /^0x[0-9a-fA-F]{1,64}$/;\n\n/** The canonical builtin SUI coin type — selects the SUI faucet funding\n * path. Mirrors `SUI_FULL_COIN_TYPE` in `plugins/account/funding.ts`\n * (inlined here so the dashboard plugin does not cross-import the account\n * funding module just for the string). */\nconst SUI_FULL_COIN_TYPE = '0x2::sui::SUI' as const;\n\n/** A positive integer base-unit amount string (no sign, no decimal\n * point, no leading zeros beyond a bare `0` — which is itself rejected\n * as non-positive). */\nconst isPositiveIntegerString = (s: string): boolean => {\n\tif (!/^\\d+$/.test(s)) return false;\n\ttry {\n\t\treturn BigInt(s) > 0n;\n\t} catch {\n\t\treturn false;\n\t}\n};\n\n/** Compose the chain faucet capability key (`faucet:request:<chainId>`)\n * the SUI funding path resolves. Uses the faucet plugin's prefix constant\n * (single source of truth) — same key the boot-time funding pass builds. */\nconst faucetCapabilityKeyFor = (\n\tchain: string,\n): `${typeof FAUCET_CAPABILITY_KEY_PREFIX}:${string}` => `${FAUCET_CAPABILITY_KEY_PREFIX}:${chain}`;\n\n/** Parse a base-unit amount string into a positive bigint, or `null` when\n * absent / non-positive / non-integer. */\nconst parseAmount = (s: string | null | undefined): bigint | null => {\n\tif (s == null) return null;\n\tconst trimmed = s.trim();\n\tif (!isPositiveIntegerString(trimmed)) return null;\n\treturn BigInt(trimmed);\n};\n\n/** Derive a display symbol from a full coin type's module path\n * (`0x…::wal::WAL` → `WAL`, `0x…::deep::DEEP` → `DEEP`). Falls back to the\n * struct name, then the raw type. Keeps the dashboard name-blind on the\n * resolved coin VALUE while still labeling the faucet pill. */\nconst coinSymbolFromType = (coinType: string): string => {\n\tconst parts = coinType.split('::');\n\tconst struct = parts[parts.length - 1];\n\treturn struct !== undefined && struct.length > 0 ? struct : coinType;\n};\n\n/** Extract a human detail from a faucet/funding strategy failure. The\n * strategy error channels are tagged structs carrying `message`/`reason`;\n * read them structurally without importing each plugin's error type. */\nconst causeDetail = (cause: unknown): string => {\n\tif (cause !== null && typeof cause === 'object') {\n\t\tconst c = cause as {\n\t\t\treadonly message?: unknown;\n\t\t\treadonly reason?: unknown;\n\t\t\treadonly _tag?: unknown;\n\t\t};\n\t\tif (typeof c.message === 'string' && c.message.length > 0) return c.message;\n\t\tif (typeof c.reason === 'string' && c.reason.length > 0) return c.reason;\n\t\tif (typeof c._tag === 'string' && c._tag.length > 0) return c._tag;\n\t}\n\treturn String(cause);\n};\nconst faucetCauseDetail = causeDetail;\nconst fundingCauseDetail = causeDetail;\n\n// -----------------------------------------------------------------------------\n// Fail-aware structural narrowing\n//\n// The substrate hands us opaque `unknown` resolved values. We still narrow\n// through shallow structural shapes, but instead of SILENTLY degrading a\n// missing/wrong-typed required field to ''/0 (which hid integration-seam\n// drift), the `req*` readers ACCUMULATE a typed narrowing fault into a\n// per-shaping-call collector. The fault is threaded into the GraphQL-visible\n// result (`narrowingFault` / `detail`) so consumers SEE the drift — WITHOUT\n// ever throwing (the dashboard's `E = never` surface semantic is preserved).\n// `opt*` readers stay quiet: they are for genuinely-optional fields.\n// -----------------------------------------------------------------------------\n\n/** Mutable per-call sink for structural-narrowing faults. */\ntype FaultSink = Array<string>;\n\nconst newFaults = (): FaultSink => [];\n\n/** Collapse accumulated faults into a single nullable detail string for the\n * GraphQL-visible result. `null` when nothing drifted. */\nconst faultDetail = (faults: FaultSink): string | null =>\n\tfaults.length === 0 ? null : faults.join('; ');\n\n/** Describe why a value failed to narrow to the expected type — distinguishing\n * 'absent' from 'present but wrong type'. */\nconst typeFault = (field: string, expected: string, v: unknown): string => {\n\tif (v === undefined || v === null) return `${field}: missing (expected ${expected})`;\n\treturn `${field}: expected ${expected}, got ${typeof v}`;\n};\n\n/** Required string. On mismatch records a fault and returns '' so the panel\n * still renders a (degraded) cell. */\nconst reqStr = (v: unknown, field: string, faults: FaultSink): string => {\n\tif (typeof v === 'string') return v;\n\tfaults.push(typeFault(field, 'string', v));\n\treturn '';\n};\n\n/** Required finite number. On mismatch records a fault and returns the safe\n * display fallback (`fallback`, default 0). */\nconst reqNum = (v: unknown, field: string, faults: FaultSink, fallback = 0): number => {\n\tif (typeof v === 'number' && Number.isFinite(v)) return v;\n\tfaults.push(typeFault(field, 'number', v));\n\treturn fallback;\n};\n\n/** Allow-list enum narrow. Records a fault for an out-of-enum value while\n * still returning a safe display fallback so the panel renders. */\nconst narrowEnum = <T extends string>(\n\traw: unknown,\n\tallowed: ReadonlyArray<T>,\n\tfield: string,\n\tfaults: FaultSink,\n\tfallback: T,\n): T => {\n\tif (typeof raw === 'string' && (allowed as ReadonlyArray<string>).includes(raw)) return raw as T;\n\tfaults.push(\n\t\traw === undefined || raw === null\n\t\t\t? `${field}: missing (expected one of ${allowed.join('|')})`\n\t\t\t: `${field}: '${String(raw)}' not in ${allowed.join('|')}`,\n\t);\n\treturn fallback;\n};\n\n/** Required string-or-numeric coerced to a string. Pyth feed prices arrive as a\n * `bigint` on the resolved value (string in the cached/known shape); accept any\n * of string/number/bigint and record a fault otherwise. Returns '0' on\n * mismatch so the panel still renders a (degraded) cell. */\nconst reqNumericStr = (v: unknown, field: string, faults: FaultSink): string => {\n\tif (typeof v === 'string') return v;\n\tif (typeof v === 'bigint') return v.toString();\n\tif (typeof v === 'number' && Number.isFinite(v)) return String(v);\n\tfaults.push(typeFault(field, 'string|number|bigint', v));\n\treturn '0';\n};\n\n// Genuinely-optional readers — silent by design (absent is a valid state).\nconst optStr = (v: unknown): string | null => (typeof v === 'string' ? v : null);\nconst optNum = (v: unknown): number | null =>\n\ttypeof v === 'number' && Number.isFinite(v) ? v : null;\nconst bool = (v: unknown): boolean => v === true;\n\n/** Filter the generic resolved values down to those whose resource id\n * matches a predicate, in graph order. Returns the `{ pluginKey, value }`\n * the shaping functions consume. */\nconst matching = (\n\tvalues: ReadonlyArray<ControlPlaneResolvedValue>,\n\tmatches: (resourceId: string) => boolean,\n): ReadonlyArray<ControlPlaneResolvedValue> => values.filter((v) => matches(v.id));\n\n// -----------------------------------------------------------------------------\n// Builder\n// -----------------------------------------------------------------------------\n\nexport interface DashboardDomainDeps {\n\t/** The generic, name-blind control-plane domain (resolved values). */\n\treadonly control: ControlPlaneDomain;\n\t/** The scope-local strategy registry — the SAME registry the boot-time\n\t * account funding pass dispatches through (`plugins/account/funding.ts`).\n\t * Drives `fundableCoins` + `fundAccount`: SUI via `faucet:request:<chainId>`,\n\t * WAL/DEEP via `coinType:<fullCoinType>`. `null` in bare test paths; the\n\t * fund accessors degrade to unavailable then. */\n\treadonly strategyRegistry: StrategyRegistry | null;\n}\n\nexport const buildDashboardDomain = (deps: DashboardDomainDeps): DashboardDomain => {\n\tconst { control, strategyRegistry } = deps;\n\n\tconst mode: DashboardDomain['mode'] = control.resolvedValues.pipe(\n\t\tEffect.map((values) => {\n\t\t\tconst sui = matching(values, (id) => id === 'sui')[0];\n\t\t\tif (sui === undefined) return null;\n\t\t\tconst m = (sui.value as SuiShape).mode;\n\t\t\tswitch (m) {\n\t\t\t\tcase 'fork':\n\t\t\t\t\treturn 'fork';\n\t\t\t\tcase 'live':\n\t\t\t\t\treturn 'live';\n\t\t\t\tcase 'local':\n\t\t\t\tcase 'local-rpc':\n\t\t\t\t\treturn 'local';\n\t\t\t\tdefault:\n\t\t\t\t\treturn null;\n\t\t\t}\n\t\t}),\n\t);\n\n\tconst deepbook: DashboardDomain['deepbook'] = control.resolvedValues.pipe(\n\t\tEffect.map((values) =>\n\t\t\tmatching(values, (id) => id.startsWith('deepbook/')).map(\n\t\t\t\t({ pluginKey, value }): DashboardDeepbookInfo => {\n\t\t\t\t\tconst v = value as DeepbookShape;\n\t\t\t\t\tconst faults = newFaults();\n\t\t\t\t\tconst dbMode = narrowEnum(\n\t\t\t\t\t\tv.mode,\n\t\t\t\t\t\t['local', 'override', 'known'] as const,\n\t\t\t\t\t\t'deepbook.mode',\n\t\t\t\t\t\tfaults,\n\t\t\t\t\t\t'local',\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpluginKey,\n\t\t\t\t\t\tname: pluginKey.replace(/^deepbook:/, ''),\n\t\t\t\t\t\tmode: dbMode,\n\t\t\t\t\t\tnetwork: reqStr(v.network, 'deepbook.network', faults),\n\t\t\t\t\t\tpackageId: reqStr(v.packageId, 'deepbook.packageId', faults),\n\t\t\t\t\t\tregistryId: reqStr(v.registryId, 'deepbook.registryId', faults),\n\t\t\t\t\t\tadminCapId: optStr(v.adminCapId),\n\t\t\t\t\t\tdeepTreasuryId: optStr(v.deepTreasuryId),\n\t\t\t\t\t\tpools: (v.pools ?? []).map((p, i) => ({\n\t\t\t\t\t\t\tname: reqStr(p.name, `deepbook.pools[${i}].name`, faults),\n\t\t\t\t\t\t\tpoolId: reqStr(p.poolId, `deepbook.pools[${i}].poolId`, faults),\n\t\t\t\t\t\t\tbaseCoinType: reqStr(p.baseCoinType, `deepbook.pools[${i}].baseCoinType`, faults),\n\t\t\t\t\t\t\tquoteCoinType: reqStr(p.quoteCoinType, `deepbook.pools[${i}].quoteCoinType`, faults),\n\t\t\t\t\t\t})),\n\t\t\t\t\t\t// Pyth feeds are seeded inside DeepBook (`pyth.feeds`); absent\n\t\t\t\t\t\t// when no Pyth feeds were configured (`pyth` is null/empty).\n\t\t\t\t\t\tpythFeeds: (v.pyth?.feeds ?? []).map((f, i) => ({\n\t\t\t\t\t\t\tsymbol: reqStr(f.symbol, `deepbook.pyth.feeds[${i}].symbol`, faults),\n\t\t\t\t\t\t\tpriceInfoObjectId: reqStr(\n\t\t\t\t\t\t\t\tf.priceInfoObjectId,\n\t\t\t\t\t\t\t\t`deepbook.pyth.feeds[${i}].priceInfoObjectId`,\n\t\t\t\t\t\t\t\tfaults,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tprice: reqNumericStr(f.price, `deepbook.pyth.feeds[${i}].price`, faults),\n\t\t\t\t\t\t\texpo: reqNum(f.expo, `deepbook.pyth.feeds[${i}].expo`, faults),\n\t\t\t\t\t\t})),\n\t\t\t\t\t\thasSeedLiquidity: bool(v.hasSeedLiquidity),\n\t\t\t\t\t\tserverUrl: optStr(v.serverUrl),\n\t\t\t\t\t\tindexerUrl: optStr(v.indexerUrl),\n\t\t\t\t\t\tnarrowingFault: faultDetail(faults),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t),\n\t\t),\n\t);\n\n\tconst seal: DashboardDomain['seal'] = control.resolvedValues.pipe(\n\t\tEffect.map((values) =>\n\t\t\tmatching(values, (id) => id.startsWith('seal:')).map(\n\t\t\t\t({ pluginKey, value }): DashboardSealInfo => {\n\t\t\t\t\tconst v = value as SealShape;\n\t\t\t\t\tconst faults = newFaults();\n\t\t\t\t\tconst sealMode = narrowEnum(\n\t\t\t\t\t\tv.mode,\n\t\t\t\t\t\t['local-keygen', 'live', 'fork-known'] as const,\n\t\t\t\t\t\t'seal.mode',\n\t\t\t\t\t\tfaults,\n\t\t\t\t\t\t'local-keygen',\n\t\t\t\t\t);\n\t\t\t\t\tconst keyServers = (v.serverConfigs ?? []).map((c, i) => ({\n\t\t\t\t\t\tobjectId: reqStr(c.objectId, `seal.serverConfigs[${i}].objectId`, faults),\n\t\t\t\t\t\tweight: optNum(c.weight) ?? 1,\n\t\t\t\t\t}));\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpluginKey,\n\t\t\t\t\t\tmode: sealMode,\n\t\t\t\t\t\tobjectId: reqStr(v.objectId, 'seal.objectId', faults),\n\t\t\t\t\t\tkeyServerUrl: reqStr(v.keyServerUrl, 'seal.keyServerUrl', faults),\n\t\t\t\t\t\tkeyServers,\n\t\t\t\t\t\tthreshold: keyServers.length,\n\t\t\t\t\t\tnarrowingFault: faultDetail(faults),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t),\n\t\t),\n\t);\n\n\tconst coinCaps: DashboardDomain['coinCaps'] = control.resolvedValues.pipe(\n\t\tEffect.map((values) =>\n\t\t\tmatching(values, (id) => id.startsWith('coin:')).map(\n\t\t\t\t({ pluginKey, value }): DashboardCoinCap => {\n\t\t\t\t\tconst v = value as CoinShape;\n\t\t\t\t\tconst faults = newFaults();\n\t\t\t\t\tconst source = narrowEnum(\n\t\t\t\t\t\tv.source,\n\t\t\t\t\t\t['registry', 'on-chain', 'builtin'] as const,\n\t\t\t\t\t\t'coin.source',\n\t\t\t\t\t\tfaults,\n\t\t\t\t\t\t'on-chain',\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpluginKey,\n\t\t\t\t\t\tsymbol: optStr(v.symbol),\n\t\t\t\t\t\tfullCoinType: reqStr(v.fullCoinType, 'coin.fullCoinType', faults),\n\t\t\t\t\t\tdecimals: reqNum(v.decimals, 'coin.decimals', faults, 0),\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\ttreasuryCapId: optStr(v.treasuryCapId),\n\t\t\t\t\t\tpackageId: optStr(v.packageId),\n\t\t\t\t\t\tnarrowingFault: faultDetail(faults),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t),\n\t\t),\n\t);\n\n\t// Mint ACTION — drives the dashboard Coins panel's Mint button.\n\t//\n\t// Signer source: the resolved coin VALUE carries a self-contained\n\t// `mintFromCap` closure (present only for witness-form coins whose\n\t// publisher still owns the TreasuryCap). That closure already captures\n\t// the treasury-cap-owning publisher `MintSigner` + the resolved cap id\n\t// in-process — the same lease-owning path `coin/service.ts`'s\n\t// `fundingStrategy` uses — so we mint WITHOUT threading a signer through\n\t// this seam (we read the resolved value, never plugin internals).\n\t//\n\t// Never fails (`E = never`): every reject path (bad address, non-\n\t// positive amount, no matching coin, cap-not-owned, on-chain failure)\n\t// degrades to `{ ok: false, detail, digest: null }` so the dashboard\n\t// query can't be taken down by a single bad mint.\n\tconst mintCoin: DashboardDomain['mintCoin'] = (input) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst recipient = input.recipient.trim();\n\t\t\tif (!SUI_ADDRESS_RE.test(recipient)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: `invalid recipient '${input.recipient}': expected a 0x-prefixed Sui address`,\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardMintResult;\n\t\t\t}\n\t\t\tif (!isPositiveIntegerString(input.amountBaseUnits)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: `invalid amountBaseUnits '${input.amountBaseUnits}': expected a positive integer string`,\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardMintResult;\n\t\t\t}\n\n\t\t\t// Locate the resolved coin whose fullCoinType matches. Match on the\n\t\t\t// resolved value's `fullCoinType` (the stable on-chain type), not the\n\t\t\t// resource-id prefix, so callers pass the same `coinType` the\n\t\t\t// `coinCaps` query surfaced. Read `fullCoinType` fail-loud so a coin\n\t\t\t// value missing/empty `fullCoinType` names the REAL cause (a drifted\n\t\t\t// resolved shape) rather than collapsing into the generic\n\t\t\t// 'no resolved coin found' reject.\n\t\t\tconst values = yield* control.resolvedValues;\n\t\t\tconst candidates = matching(values, (id) => id.startsWith('coin:')).map(\n\t\t\t\t({ value }) => value as CoinShape,\n\t\t\t);\n\t\t\tconst matchFaults = newFaults();\n\t\t\tconst match = candidates.find(\n\t\t\t\t(v) => reqStr(v.fullCoinType, 'coin.fullCoinType', matchFaults) === input.coinType,\n\t\t\t);\n\n\t\t\tif (match === undefined) {\n\t\t\t\tconst fault = faultDetail(matchFaults);\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail:\n\t\t\t\t\t\tfault === null\n\t\t\t\t\t\t\t? `no resolved coin found for type '${input.coinType}'`\n\t\t\t\t\t\t\t: `no resolved coin found for type '${input.coinType}' (${fault})`,\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardMintResult;\n\t\t\t}\n\t\t\tif (typeof match.mintFromCap !== 'function') {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail:\n\t\t\t\t\t\t`coin '${input.coinType}' has no in-process treasury cap signer — ` +\n\t\t\t\t\t\t'mint is only available for local-package coins whose publisher still owns the TreasuryCap',\n\t\t\t\t\tdigest: null,\n\t\t\t\t} satisfies DashboardMintResult;\n\t\t\t}\n\n\t\t\treturn yield* match\n\t\t\t\t.mintFromCap({ to: recipient, amount: BigInt(input.amountBaseUnits) })\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.map(\n\t\t\t\t\t\t(r): DashboardMintResult => ({\n\t\t\t\t\t\t\tok: true,\n\t\t\t\t\t\t\tdetail: `minted ${input.amountBaseUnits} of ${input.coinType} to ${recipient}`,\n\t\t\t\t\t\t\tdigest: r.digest,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\t// Typed coin/artifact-publisher failures carry `.message`.\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardMintResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail:\n\t\t\t\t\t\t\t\ttypeof cause?.message === 'string'\n\t\t\t\t\t\t\t\t\t? cause.message\n\t\t\t\t\t\t\t\t\t: `mint failed: ${String(cause)}`,\n\t\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\t// Residual defects (interrupts, unexpected throws) — degrade\n\t\t\t\t\t// rather than crash the dashboard query.\n\t\t\t\t\tEffect.catchCause((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardMintResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail: `mint crashed: ${String(cause)}`,\n\t\t\t\t\t\t\tdigest: null,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t});\n\n\t// --- Fund ACTION + fundable-coin derivation ------------------------\n\t//\n\t// Reuses devstack's IN-PROCESS funding strategies — the same strategy\n\t// registry the boot-time account funding pass dispatches through\n\t// (`plugins/account/funding.ts`). We never re-implement signing or the\n\t// exchange/pool swaps:\n\t//\n\t// - SUI (absent / canonical type) → `faucet:request:<chainId>`. The SUI\n\t// faucet strategy takes `{ address, amount }` only (no account handle)\n\t// and is FIXED-AMOUNT (the standard faucet ignores `amount`). Recipient\n\t// may be ANY 0x address.\n\t// - WAL/DEEP → `coinType:<fullCoinType>`. The strategy takes\n\t// `{ address, amount, account }` and runs an account-signed swap, so\n\t// the recipient MUST be a resolved account in the stack (we match the\n\t// recipient address against the resolved `account/<name>` values and\n\t// pass the live `AccountValue` as `req.account`).\n\t//\n\t// Never fails (`E = never`): every reject path degrades to\n\t// `{ ok:false, detail }` so a single bad fund can't take down the query.\n\n\t/** The chain id the SUI funding path keys on, read from the resolved sui\n\t * value (same `chainId` field the sui codegen/snapshot decls stamp). */\n\tconst readChainId = control.resolvedValues.pipe(\n\t\tEffect.map((values) => {\n\t\t\tconst sui = matching(values, (id) => id === 'sui')[0];\n\t\t\treturn sui === undefined ? null : optStr((sui.value as SuiShape).chainId);\n\t\t}),\n\t);\n\n\tconst fundableCoins: DashboardDomain['fundableCoins'] = Effect.gen(function* () {\n\t\t// SUI is always fundable when a faucet strategy is registered for the\n\t\t// active chain; the fixed-amount faucet ignores the requested amount.\n\t\tconst out: DashboardFundableCoin[] = [];\n\t\tif (strategyRegistry === null) return out;\n\n\t\tconst chain = yield* readChainId;\n\t\tconst keys = yield* strategyRegistry.list();\n\t\tif (chain !== null && keys.includes(faucetCapabilityKeyFor(chain))) {\n\t\t\tout.push({\n\t\t\t\tsymbol: 'SUI',\n\t\t\t\tcoinType: SUI_FULL_COIN_TYPE,\n\t\t\t\thonorsAmount: false,\n\t\t\t\trequiresAccountSigner: false,\n\t\t\t});\n\t\t}\n\n\t\t// A coin is fundable iff a `coinType:<fullCoinType>` strategy is actually\n\t\t// registered (the walrus/deepbook/coin plugin contributed one). Derive the\n\t\t// display symbol from the coin type's module path (`::wal::WAL` → WAL,\n\t\t// `::deep::DEEP` → DEEP) so we stay name-blind on the coin VALUE while\n\t\t// still labeling the pill. `requiresAccountSigner` is read from the\n\t\t// strategy's own `requiresRecipientAccount` flag — NOT assumed true for\n\t\t// every coin: WAL/DEEP swaps spend the recipient's SUI (true), but a\n\t\t// managed-coin MINT strategy transfers to a passive recipient (false), so\n\t\t// it can fund any 0x address.\n\t\tfor (const key of keys) {\n\t\t\tif (!key.startsWith('coinType:')) continue;\n\t\t\tconst coinType = key.slice('coinType:'.length);\n\t\t\tif (coinType === SUI_FULL_COIN_TYPE) continue;\n\t\t\tconst strategy = yield* strategyRegistry\n\t\t\t\t.get<typeof key, AccountFundingStrategy<unknown, AccountValue>>(key)\n\t\t\t\t.pipe(Effect.catchTag('StrategyNotFoundError', () => Effect.succeed(null)));\n\t\t\tout.push({\n\t\t\t\tsymbol: coinSymbolFromType(coinType),\n\t\t\t\tcoinType,\n\t\t\t\thonorsAmount: true,\n\t\t\t\trequiresAccountSigner: strategy?.requiresRecipientAccount ?? false,\n\t\t\t});\n\t\t}\n\t\treturn out;\n\t});\n\n\tconst fundAccount: DashboardDomain['fundAccount'] = (input) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst recipient = input.recipient.trim();\n\t\t\tif (!SUI_ADDRESS_RE.test(recipient)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: `invalid recipient '${input.recipient}': expected a 0x-prefixed Sui address`,\n\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t}\n\t\t\tif (strategyRegistry === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: 'funding unavailable: no strategy registry wired',\n\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t}\n\n\t\t\tconst coinType = input.coinType?.trim() ?? '';\n\t\t\tconst isSui = coinType === '' || coinType === SUI_FULL_COIN_TYPE;\n\n\t\t\t// SUI path — chain faucet strategy. Fixed-amount: we pass a nominal\n\t\t\t// amount but the standard faucet ignores it. Recipient may be any\n\t\t\t// 0x address (no account handle required).\n\t\t\tif (isSui) {\n\t\t\t\tconst chain = yield* readChainId;\n\t\t\t\tif (chain === null) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\tdetail: 'cannot fund SUI: no resolved sui plugin / chain id in this stack',\n\t\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t\t}\n\t\t\t\tconst key = faucetCapabilityKeyFor(chain);\n\t\t\t\tconst strategy = yield* strategyRegistry\n\t\t\t\t\t.get<typeof key, FaucetStrategy>(key)\n\t\t\t\t\t.pipe(Effect.catchTag('StrategyNotFoundError', () => Effect.succeed(null)));\n\t\t\t\tif (strategy === null) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\tdetail: `no SUI faucet strategy registered for chain '${chain}'`,\n\t\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t\t}\n\t\t\t\t// `amount` is nominal — the standard faucet grant is fixed and\n\t\t\t\t// ignores it. Default to 1 SUI in MIST so a strategy that DID\n\t\t\t\t// honor it lands a sane value.\n\t\t\t\tconst amount = parseAmount(input.amountBaseUnits) ?? 1_000_000_000n;\n\t\t\t\treturn yield* strategy.request({ address: recipient, amount }).pipe(\n\t\t\t\t\tEffect.map(\n\t\t\t\t\t\t(): DashboardFundResult => ({\n\t\t\t\t\t\t\tok: true,\n\t\t\t\t\t\t\tdetail: `requested SUI for ${recipient} (fixed-amount faucet grant)`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardFundResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail: `SUI faucet request failed: ${faucetCauseDetail(cause)}`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catchCause((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardFundResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail: `SUI faucet request crashed: ${String(cause)}`,\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\t// WAL/DEEP path — coin-specific account-signed funding strategy.\n\t\t\tconst amount = parseAmount(input.amountBaseUnits);\n\t\t\tif (amount === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail: `invalid amountBaseUnits '${input.amountBaseUnits ?? ''}': expected a positive integer string`,\n\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t}\n\n\t\t\tconst key = `coinType:${coinType}` as const;\n\t\t\tconst strategy = yield* strategyRegistry\n\t\t\t\t.get<typeof key, AccountFundingStrategy<unknown, AccountValue>>(key)\n\t\t\t\t.pipe(Effect.catchTag('StrategyNotFoundError', () => Effect.succeed(null)));\n\t\t\tif (strategy === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail:\n\t\t\t\t\t\t`no funding strategy registered for coin '${coinType}' — ` +\n\t\t\t\t\t\t'WAL needs the walrus plugin (with an exchange) and DEEP needs the deepbook plugin',\n\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t}\n\n\t\t\t// Match the recipient to a resolved account, if one holds this address.\n\t\t\tconst values = yield* control.resolvedValues;\n\t\t\tconst account = matching(values, (id) => id.startsWith('account/'))\n\t\t\t\t.map(({ value }) => value as AccountShape)\n\t\t\t\t.find((v) => optStr(v.address) === recipient);\n\n\t\t\t// Account-spending strategies (WAL/DEEP) swap the recipient's OWN SUI,\n\t\t\t// so the recipient must BE a resolved account with a signer. Mint-style\n\t\t\t// strategies (managed coins) transfer to a passive recipient, so any 0x\n\t\t\t// address is fine — gate the rejection on the strategy's flag, not on\n\t\t\t// the coin being non-SUI.\n\t\t\tif (strategy.requiresRecipientAccount === true && account === undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tdetail:\n\t\t\t\t\t\t`coin '${coinType}' is funded by an account-signed swap, but '${recipient}' ` +\n\t\t\t\t\t\t'is not a resolved account in this stack — fund a configured account (the swap spends its SUI)',\n\t\t\t\t} satisfies DashboardFundResult;\n\t\t\t}\n\n\t\t\treturn yield* strategy\n\t\t\t\t.request({\n\t\t\t\t\taddress: recipient,\n\t\t\t\t\tamount,\n\t\t\t\t\t...(account === undefined ? {} : { account: account as unknown as AccountValue }),\n\t\t\t\t})\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.map(\n\t\t\t\t\t\t(): DashboardFundResult => ({\n\t\t\t\t\t\t\tok: true,\n\t\t\t\t\t\t\tdetail: `funded ${amount} base units of ${coinType} to ${recipient}`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardFundResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail: `${coinSymbolFromType(coinType)} funding failed: ${fundingCauseDetail(cause)}`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catchCause((cause) =>\n\t\t\t\t\t\tEffect.succeed<DashboardFundResult>({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\tdetail: `${coinSymbolFromType(coinType)} funding crashed: ${String(cause)}`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t});\n\n\treturn {\n\t\tmode,\n\t\tdeepbook,\n\t\tseal,\n\t\tcoinCaps,\n\t\tmintCoin,\n\t\tfundableCoins,\n\t\tfundAccount,\n\t};\n};\n\n/** An all-empty dashboard domain. Used by tests that exercise the\n * schema/server without a live registry. Every accessor resolves to\n * empty/`null`. */\nexport const emptyDashboardDomain: DashboardDomain = {\n\tmode: Effect.succeed(null),\n\tdeepbook: Effect.succeed([]),\n\tseal: Effect.succeed([]),\n\tcoinCaps: Effect.succeed([]),\n\tmintCoin: () => Effect.succeed({ ok: false, detail: 'unavailable', digest: null }),\n\tfundableCoins: Effect.succeed([]),\n\tfundAccount: () => Effect.succeed({ ok: false, detail: 'unavailable' }),\n};\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { definePlugin, resource } from "../../substrate/plugin.mjs";
|
|
2
2
|
import "../../api/define-plugin.mjs";
|
|
3
|
-
import { IdentityContext } from "../../substrate/runtime/paths.mjs";
|
|
4
3
|
import { ControlPlaneService } from "../../substrate/runtime/control-plane/service.mjs";
|
|
5
|
-
import {
|
|
4
|
+
import { IdentityContext } from "../../substrate/runtime/paths.mjs";
|
|
5
|
+
import { PluginContext } from "../../substrate/plugin-ctx.mjs";
|
|
6
6
|
import { StrategyRegistryService } from "../../substrate/runtime/strategy-registry/service.mjs";
|
|
7
7
|
import { PortBrokerService } from "../../substrate/runtime/port-broker/service.mjs";
|
|
8
8
|
import "../../substrate/runtime/port-broker/index.mjs";
|
|
@@ -23,18 +23,16 @@ function dashboard(opts = {}) {
|
|
|
23
23
|
id: dashboardResource.id,
|
|
24
24
|
role: "service",
|
|
25
25
|
section: "service",
|
|
26
|
+
keepAliveOnRestore: true,
|
|
26
27
|
start: () => Effect.gen(function* () {
|
|
28
|
+
const ctx = yield* PluginContext;
|
|
27
29
|
const portBroker = yield* PortBrokerService;
|
|
28
30
|
const control = yield* ControlPlaneService;
|
|
29
31
|
const identity = yield* IdentityContext;
|
|
30
|
-
const containerRuntimeOpt = yield* Effect.serviceOption(ContainerRuntimeService);
|
|
31
|
-
const containerRuntime = containerRuntimeOpt._tag === "Some" ? containerRuntimeOpt.value : null;
|
|
32
32
|
const strategyRegistryOpt = yield* Effect.serviceOption(StrategyRegistryService);
|
|
33
33
|
const strategyRegistry = strategyRegistryOpt._tag === "Some" ? strategyRegistryOpt.value : null;
|
|
34
34
|
const pluginDomain = buildDashboardDomain({
|
|
35
35
|
control: control.domain,
|
|
36
|
-
identity,
|
|
37
|
-
containerRuntime,
|
|
38
36
|
strategyRegistry
|
|
39
37
|
});
|
|
40
38
|
const bindAddress = opts.bindAddress ?? DASHBOARD_DEFAULT_BIND;
|
|
@@ -60,7 +58,7 @@ function dashboard(opts = {}) {
|
|
|
60
58
|
directOrigins,
|
|
61
59
|
extraOrigins: opts.allowedOrigins ?? []
|
|
62
60
|
});
|
|
63
|
-
|
|
61
|
+
const resolved = {
|
|
64
62
|
url: (yield* listenScopedHttpServer({
|
|
65
63
|
bindAddress,
|
|
66
64
|
port: allocated.port,
|
|
@@ -69,6 +67,7 @@ function dashboard(opts = {}) {
|
|
|
69
67
|
context: {
|
|
70
68
|
state: control.state,
|
|
71
69
|
publishCommand: control.publishCommand,
|
|
70
|
+
submitCommand: control.submitCommand,
|
|
72
71
|
domain: control.domain,
|
|
73
72
|
pluginDomain
|
|
74
73
|
}
|
|
@@ -77,12 +76,13 @@ function dashboard(opts = {}) {
|
|
|
77
76
|
})).url,
|
|
78
77
|
localPort: allocated.port
|
|
79
78
|
};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
ctx.endpoint(makeDashboardRoutable({
|
|
80
|
+
app: identity.app,
|
|
81
|
+
stack: identity.stack,
|
|
82
|
+
port: resolved.localPort
|
|
83
|
+
}));
|
|
84
|
+
return resolved;
|
|
85
|
+
})
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
88
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/dashboard/index.ts"],"sourcesContent":["// Dashboard plugin.\n//\n// Runs an in-process GraphQL server (Pothos schema + graphql-yoga) that\n// exposes the live projection, control commands, and the projection\n// subscription. Modeled on the `wallet` plugin: allocate a loopback port\n// from the port broker, start a scoped HTTP server, and contribute a\n// Routable so the router fronts it under `dashboard.<app>.<stack>.localhost`.\n//\n// Data access is pure control plane: the projection (`state`) and command\n// publisher (`publishCommand`) come from `ControlPlaneService`, handed to\n// the server as the GraphQL context.\n\nimport { Effect } from 'effect';\nimport { type AnyPlugin, definePlugin, resource } from '../../api/define-plugin.ts';\nimport { ContainerRuntimeService } from '../../runtime/docker/service.ts';\nimport type { ContainerRuntime } from '../../contracts/container-runtime.ts';\nimport type { StrategyRegistry } from '../../contracts/strategy-contributor.ts';\nimport { StrategyRegistryService } from '../../substrate/runtime/strategy-registry/index.ts';\nimport { ControlPlaneService } from '../../substrate/runtime/control-plane/service.ts';\nimport { IdentityContext } from '../../substrate/runtime/paths.ts';\nimport { PortBrokerService } from '../../substrate/runtime/port-broker/index.ts';\nimport { renderUrl, routedHostname } from '../../substrate/runtime/routed-url.ts';\nimport { listenScopedHttpServer } from '../../substrate/runtime/scoped-http-server.ts';\nimport { buildDashboardDomain } from './domain.ts';\nimport { resolveOriginPolicy } from './origin-policy.ts';\nimport {\n\tDASHBOARD_ENTRYPOINT_PORT,\n\tDASHBOARD_ROUTE_ROLE,\n\tmakeDashboardRoutable,\n} from './routable.ts';\nimport { makeDashboardListener } from './server.ts';\n\nexport interface DashboardOptions {\n\t/** Preferred loopback port; the broker forward-scans if it's busy. */\n\treadonly port?: number;\n\t/** NIC the HTTP server binds. Defaults to `'0.0.0.0'` because the router\n\t * runs in Docker and must reach this host process through the host-gateway\n\t * address on native Linux (a `127.0.0.1`-only listener is unreachable from\n\t * the host-gateway IP). The public dashboard URL stays router-fronted and\n\t * stack-scoped. */\n\treadonly bindAddress?: string;\n\t/** Extra origins merged on top of the dashboard's own router-fronted origin\n\t * for this stack (and the direct loopback origins). Useful for headless\n\t * test runners and custom dev hosts. The dashboard does NOT auto-allowlist\n\t * a bare `*.localhost` form — that is not stack-scoped, so a sibling stack\n\t * could drive the destructive control-plane mutations cross-origin. */\n\treadonly allowedOrigins?: ReadonlyArray<string>;\n\t// Note on log retention: the queryable cross-service log store the\n\t// dashboard reads is process-scoped and owned by the SUPERVISOR (not this\n\t// plugin), so its capacity is configured there. Tune it with the\n\t// `DEVSTACK_DASHBOARD_LOG_CAPACITY` (per-service record cap, default 2000)\n\t// and `DEVSTACK_DASHBOARD_LOG_MAX_SERVICES` (distinct-ring cap, default\n\t// 256) env vars, or programmatically via `SupervisorStartupOptions.logStore`.\n\t// See substrate/runtime/observability/log-store.ts.\n}\n\nexport interface DashboardValue {\n\t/** Loopback URL of the dashboard server. */\n\treadonly url: string;\n\t/** The allocated loopback port. */\n\treadonly localPort: number;\n}\n\nconst dashboardResource = resource<'dashboard', DashboardValue>('dashboard');\n// `0.0.0.0`: the router runs in Docker, so on native Linux it reaches this\n// host process through the Docker host-gateway address instead of host\n// loopback — a `127.0.0.1`-only listener is unreachable from that IP, which\n// produced a 502 from Traefik and the `RouteReadinessProbeFailed` WARN. The\n// published dashboard URL remains stack-scoped through the router. Mirrors\n// `WALLET_DEFAULT_BIND_ADDRESS`.\nconst DASHBOARD_DEFAULT_BIND = '0.0.0.0';\n\n/** Construct the devstack dashboard plugin. */\nexport function dashboard(opts: DashboardOptions = {}): AnyPlugin {\n\treturn definePlugin({\n\t\tid: dashboardResource.id,\n\t\trole: 'service',\n\t\tsection: 'service',\n\t\tstart: () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst portBroker = yield* PortBrokerService;\n\t\t\t\tconst control = yield* ControlPlaneService;\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\t// The ContainerRuntime drives the Postgres `psql` exec-probe.\n\t\t\t\t// It is in the base substrate plugin context in production\n\t\t\t\t// wiring; read it optionally so bare smoke-test paths that\n\t\t\t\t// don't layer it degrade `postgresStats` to unavailable\n\t\t\t\t// rather than failing acquisition.\n\t\t\t\tconst containerRuntimeOpt = yield* Effect.serviceOption(ContainerRuntimeService);\n\t\t\t\tconst containerRuntime: ContainerRuntime | null =\n\t\t\t\t\tcontainerRuntimeOpt._tag === 'Some' ? containerRuntimeOpt.value : null;\n\n\t\t\t\t// The scope-local strategy registry — the SAME registry the\n\t\t\t\t// boot-time account funding pass dispatches through. Drives the\n\t\t\t\t// Faucet panel's `fundableCoins` + `fundAccount` (SUI via the\n\t\t\t\t// faucet strategy, WAL/DEEP via the coin-specific funding\n\t\t\t\t// strategies). Read optionally so bare smoke-test paths that don't\n\t\t\t\t// layer it degrade the fund accessors to unavailable.\n\t\t\t\tconst strategyRegistryOpt = yield* Effect.serviceOption(StrategyRegistryService);\n\t\t\t\tconst strategyRegistry: StrategyRegistry | null =\n\t\t\t\t\tstrategyRegistryOpt._tag === 'Some' ? strategyRegistryOpt.value : null;\n\n\t\t\t\t// Plugin-name-aware shaping lives HERE (the plugin layer is\n\t\t\t\t// allowed to name plugins), built off the generic, name-blind\n\t\t\t\t// control-plane `resolvedValues` seam + the container runtime.\n\t\t\t\tconst pluginDomain = buildDashboardDomain({\n\t\t\t\t\tcontrol: control.domain,\n\t\t\t\t\tidentity,\n\t\t\t\t\tcontainerRuntime,\n\t\t\t\t\tstrategyRegistry,\n\t\t\t\t});\n\n\t\t\t\tconst bindAddress = opts.bindAddress ?? DASHBOARD_DEFAULT_BIND;\n\t\t\t\tconst allocated = yield* portBroker.allocate({\n\t\t\t\t\towner: 'dashboard',\n\t\t\t\t\twindowHint: { start: 39300, size: 1000 },\n\t\t\t\t\tprobeHost: bindAddress === '0.0.0.0' ? '0.0.0.0' : '127.0.0.1',\n\t\t\t\t\t...(opts.port === undefined ? {} : { preferredPort: opts.port }),\n\t\t\t\t});\n\n\t\t\t\t// Derive the dashboard's OWN router-fronted origin for this stack.\n\t\t\t\t// The bundled SPA is served same-origin from this hostname, so the\n\t\t\t\t// browser's `Origin` on `/graphql` is exactly this value — it MUST\n\t\t\t\t// be in the allowlist. Host = `<role:api>.<app>.<stack>.localhost`\n\t\t\t\t// (per `routedHostname`), port = the router entrypoint port (9810),\n\t\t\t\t// which is the port the browser actually uses, NOT the broker\n\t\t\t\t// loopback port. Router-derivation failure → `null` (allowlist still\n\t\t\t\t// includes the direct loopback + caller origins).\n\t\t\t\tconst routedDashboardOrigin = yield* routedHostname(identity, DASHBOARD_ROUTE_ROLE).pipe(\n\t\t\t\t\tEffect.map((hostname) =>\n\t\t\t\t\t\trenderUrl({ protocol: 'http', hostname, port: DASHBOARD_ENTRYPOINT_PORT }),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.orElseSucceed(() => null),\n\t\t\t\t);\n\n\t\t\t\t// Direct loopback origins: when the dashboard is reached on the raw\n\t\t\t\t// broker port (host-loopback fallback / direct tooling) the SPA's\n\t\t\t\t// same-origin `Origin` is the loopback form. Both 127.0.0.1 and\n\t\t\t\t// localhost name the same listener; allow both for this stack's port.\n\t\t\t\tconst directOrigins = [\n\t\t\t\t\t`http://127.0.0.1:${allocated.port}`,\n\t\t\t\t\t`http://localhost:${allocated.port}`,\n\t\t\t\t];\n\n\t\t\t\tconst originPolicy = yield* resolveOriginPolicy({\n\t\t\t\t\tapp: identity.app,\n\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\troutedDashboardOrigin,\n\t\t\t\t\tdirectOrigins,\n\t\t\t\t\textraOrigins: opts.allowedOrigins ?? [],\n\t\t\t\t});\n\n\t\t\t\tconst server = yield* listenScopedHttpServer({\n\t\t\t\t\tbindAddress,\n\t\t\t\t\tport: allocated.port,\n\t\t\t\t\tlistener: makeDashboardListener({\n\t\t\t\t\t\toriginPolicy,\n\t\t\t\t\t\tcontext: {\n\t\t\t\t\t\t\tstate: control.state,\n\t\t\t\t\t\t\tpublishCommand: control.publishCommand,\n\t\t\t\t\t\t\tdomain: control.domain,\n\t\t\t\t\t\t\tpluginDomain,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tonListenError: (cause) =>\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`dashboard HTTP server listen failed on ${bindAddress}:${allocated.port}: ${String(cause)}`,\n\t\t\t\t\t\t),\n\t\t\t\t});\n\n\t\t\t\treturn { url: server.url, localPort: allocated.port } satisfies DashboardValue;\n\t\t\t}),\n\t\tcapabilities: ({ value, runtime }) => [\n\t\t\tmakeDashboardRoutable({\n\t\t\t\tapp: runtime.identity.app,\n\t\t\t\tstack: runtime.identity.stack,\n\t\t\t\tport: value.localPort,\n\t\t\t}),\n\t\t],\n\t});\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA+DA,MAAM,oBAAoB,SAAsC,YAAY;AAO5E,MAAM,yBAAyB;;AAG/B,SAAgB,UAAU,OAAyB,EAAE,EAAa;AACjE,QAAO,aAAa;EACnB,IAAI,kBAAkB;EACtB,MAAM;EACN,SAAS;EACT,aACC,OAAO,IAAI,aAAa;GACvB,MAAM,aAAa,OAAO;GAC1B,MAAM,UAAU,OAAO;GACvB,MAAM,WAAW,OAAO;GAMxB,MAAM,sBAAsB,OAAO,OAAO,cAAc,wBAAwB;GAChF,MAAM,mBACL,oBAAoB,SAAS,SAAS,oBAAoB,QAAQ;GAQnE,MAAM,sBAAsB,OAAO,OAAO,cAAc,wBAAwB;GAChF,MAAM,mBACL,oBAAoB,SAAS,SAAS,oBAAoB,QAAQ;GAKnE,MAAM,eAAe,qBAAqB;IACzC,SAAS,QAAQ;IACjB;IACA;IACA;IACA,CAAC;GAEF,MAAM,cAAc,KAAK,eAAe;GACxC,MAAM,YAAY,OAAO,WAAW,SAAS;IAC5C,OAAO;IACP,YAAY;KAAE,OAAO;KAAO,MAAM;KAAM;IACxC,WAAW,gBAAgB,YAAY,YAAY;IACnD,GAAI,KAAK,SAAS,KAAA,IAAY,EAAE,GAAG,EAAE,eAAe,KAAK,MAAM;IAC/D,CAAC;GAUF,MAAM,wBAAwB,OAAO,eAAe,UAAA,MAA+B,CAAC,KACnF,OAAO,KAAK,aACX,UAAU;IAAE,UAAU;IAAQ;IAAU,MAAM;IAA2B,CAAC,CAC1E,EACD,OAAO,oBAAoB,KAAK,CAChC;GAMD,MAAM,gBAAgB,CACrB,oBAAoB,UAAU,QAC9B,oBAAoB,UAAU,OAC9B;GAED,MAAM,eAAe,OAAO,oBAAoB;IAC/C,KAAK,SAAS;IACd,OAAO,SAAS;IAChB;IACA;IACA,cAAc,KAAK,kBAAkB,EAAE;IACvC,CAAC;AAoBF,UAAO;IAAE,MAAK,OAlBQ,uBAAuB;KAC5C;KACA,MAAM,UAAU;KAChB,UAAU,sBAAsB;MAC/B;MACA,SAAS;OACR,OAAO,QAAQ;OACf,gBAAgB,QAAQ;OACxB,QAAQ,QAAQ;OAChB;OACA;MACD,CAAC;KACF,gBAAgB,0BACf,IAAI,MACH,0CAA0C,YAAY,GAAG,UAAU,KAAK,IAAI,OAAO,MAAM,GACzF;KACF,CAAC,EAEmB;IAAK,WAAW,UAAU;IAAM;IACpD;EACH,eAAe,EAAE,OAAO,cAAc,CACrC,sBAAsB;GACrB,KAAK,QAAQ,SAAS;GACtB,OAAO,QAAQ,SAAS;GACxB,MAAM,MAAM;GACZ,CAAC,CACF;EACD,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/dashboard/index.ts"],"sourcesContent":["// Dashboard plugin.\n//\n// Runs an in-process GraphQL server (Pothos schema + graphql-yoga) that\n// exposes the live projection, control commands, and the projection\n// subscription. Modeled on the `wallet` plugin: allocate a loopback port\n// from the port broker, start a scoped HTTP server, and contribute a\n// Routable so the router fronts it under `dashboard.<app>.<stack>.localhost`.\n//\n// Data access is pure control plane: the projection (`state`) and command\n// publisher (`publishCommand`) come from `ControlPlaneService`, handed to\n// the server as the GraphQL context.\n\nimport { Effect } from 'effect';\nimport { type AnyPlugin, definePlugin, resource } from '../../api/define-plugin.ts';\nimport { PluginContext } from '../../substrate/plugin-ctx.ts';\nimport type { StrategyRegistry } from '../../contracts/strategy-contributor.ts';\nimport { StrategyRegistryService } from '../../substrate/runtime/strategy-registry/index.ts';\nimport { ControlPlaneService } from '../../substrate/runtime/control-plane/service.ts';\nimport { IdentityContext } from '../../substrate/runtime/paths.ts';\nimport { PortBrokerService } from '../../substrate/runtime/port-broker/index.ts';\nimport { renderUrl, routedHostname } from '../../substrate/runtime/routed-url.ts';\nimport { listenScopedHttpServer } from '../../substrate/runtime/scoped-http-server.ts';\nimport { buildDashboardDomain } from './domain.ts';\nimport { resolveOriginPolicy } from './origin-policy.ts';\nimport {\n\tDASHBOARD_ENTRYPOINT_PORT,\n\tDASHBOARD_ROUTE_ROLE,\n\tmakeDashboardRoutable,\n} from './routable.ts';\nimport { makeDashboardListener } from './server.ts';\n\nexport interface DashboardOptions {\n\t/** Preferred loopback port; the broker forward-scans if it's busy. */\n\treadonly port?: number;\n\t/** NIC the HTTP server binds. Defaults to `'0.0.0.0'` because the router\n\t * runs in Docker and must reach this host process through the host-gateway\n\t * address on native Linux (a `127.0.0.1`-only listener is unreachable from\n\t * the host-gateway IP). The public dashboard URL stays router-fronted and\n\t * stack-scoped. */\n\treadonly bindAddress?: string;\n\t/** Extra origins merged on top of the dashboard's own router-fronted origin\n\t * for this stack (and the direct loopback origins). Useful for headless\n\t * test runners and custom dev hosts. The dashboard does NOT auto-allowlist\n\t * a bare `*.localhost` form — that is not stack-scoped, so a sibling stack\n\t * could drive the destructive control-plane mutations cross-origin. */\n\treadonly allowedOrigins?: ReadonlyArray<string>;\n\t// Note on log retention: the queryable cross-service log store the\n\t// dashboard reads is process-scoped and owned by the SUPERVISOR (not this\n\t// plugin), so its capacity is configured there. Tune it with the\n\t// `DEVSTACK_DASHBOARD_LOG_CAPACITY` (per-service record cap, default 2000)\n\t// and `DEVSTACK_DASHBOARD_LOG_MAX_SERVICES` (distinct-ring cap, default\n\t// 256) env vars, or programmatically via `SupervisorStartupOptions.logStore`.\n\t// See substrate/runtime/observability/log-store.ts.\n}\n\nexport interface DashboardValue {\n\t/** Loopback URL of the dashboard server. */\n\treadonly url: string;\n\t/** The allocated loopback port. */\n\treadonly localPort: number;\n}\n\nconst dashboardResource = resource<'dashboard', DashboardValue>('dashboard');\n// `0.0.0.0`: the router runs in Docker, so on native Linux it reaches this\n// host process through the Docker host-gateway address instead of host\n// loopback — a `127.0.0.1`-only listener is unreachable from that IP, which\n// produced a 502 from Traefik and the `RouteReadinessProbeFailed` WARN. The\n// published dashboard URL remains stack-scoped through the router. Mirrors\n// `WALLET_DEFAULT_BIND_ADDRESS`.\nconst DASHBOARD_DEFAULT_BIND = '0.0.0.0';\n\n/** Construct the devstack dashboard plugin. */\nexport function dashboard(opts: DashboardOptions = {}): AnyPlugin {\n\treturn definePlugin({\n\t\tid: dashboardResource.id,\n\t\trole: 'service',\n\t\tsection: 'service',\n\t\t// Operator transport: a live snapshot-restore re-acquire must leave\n\t\t// this plugin running, or a dashboard-initiated restore would tear\n\t\t// down the very HTTP connection it's answering on (502). It holds no\n\t\t// restorable chain state, so staying live is safe.\n\t\tkeepAliveOnRestore: true,\n\t\tstart: () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst portBroker = yield* PortBrokerService;\n\t\t\t\tconst control = yield* ControlPlaneService;\n\t\t\t\tconst identity = yield* IdentityContext;\n\n\t\t\t\t// The scope-local strategy registry — the SAME registry the\n\t\t\t\t// boot-time account funding pass dispatches through. Drives the\n\t\t\t\t// Faucet panel's `fundableCoins` + `fundAccount` (SUI via the\n\t\t\t\t// faucet strategy, WAL/DEEP via the coin-specific funding\n\t\t\t\t// strategies). Read optionally so bare smoke-test paths that don't\n\t\t\t\t// layer it degrade the fund accessors to unavailable.\n\t\t\t\tconst strategyRegistryOpt = yield* Effect.serviceOption(StrategyRegistryService);\n\t\t\t\tconst strategyRegistry: StrategyRegistry | null =\n\t\t\t\t\tstrategyRegistryOpt._tag === 'Some' ? strategyRegistryOpt.value : null;\n\n\t\t\t\t// Plugin-name-aware shaping lives HERE (the plugin layer is\n\t\t\t\t// allowed to name plugins), built off the generic, name-blind\n\t\t\t\t// control-plane `resolvedValues` seam.\n\t\t\t\tconst pluginDomain = buildDashboardDomain({\n\t\t\t\t\tcontrol: control.domain,\n\t\t\t\t\tstrategyRegistry,\n\t\t\t\t});\n\n\t\t\t\tconst bindAddress = opts.bindAddress ?? DASHBOARD_DEFAULT_BIND;\n\t\t\t\tconst allocated = yield* portBroker.allocate({\n\t\t\t\t\towner: 'dashboard',\n\t\t\t\t\twindowHint: { start: 39300, size: 1000 },\n\t\t\t\t\tprobeHost: bindAddress === '0.0.0.0' ? '0.0.0.0' : '127.0.0.1',\n\t\t\t\t\t...(opts.port === undefined ? {} : { preferredPort: opts.port }),\n\t\t\t\t});\n\n\t\t\t\t// Derive the dashboard's OWN router-fronted origin for this stack.\n\t\t\t\t// The bundled SPA is served same-origin from this hostname, so the\n\t\t\t\t// browser's `Origin` on `/graphql` is exactly this value — it MUST\n\t\t\t\t// be in the allowlist. Host = `<role:api>.<app>.<stack>.localhost`\n\t\t\t\t// (per `routedHostname`), port = the router entrypoint port (9810),\n\t\t\t\t// which is the port the browser actually uses, NOT the broker\n\t\t\t\t// loopback port. Router-derivation failure → `null` (allowlist still\n\t\t\t\t// includes the direct loopback + caller origins).\n\t\t\t\tconst routedDashboardOrigin = yield* routedHostname(identity, DASHBOARD_ROUTE_ROLE).pipe(\n\t\t\t\t\tEffect.map((hostname) =>\n\t\t\t\t\t\trenderUrl({ protocol: 'http', hostname, port: DASHBOARD_ENTRYPOINT_PORT }),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.orElseSucceed(() => null),\n\t\t\t\t);\n\n\t\t\t\t// Direct loopback origins: when the dashboard is reached on the raw\n\t\t\t\t// broker port (host-loopback fallback / direct tooling) the SPA's\n\t\t\t\t// same-origin `Origin` is the loopback form. Both 127.0.0.1 and\n\t\t\t\t// localhost name the same listener; allow both for this stack's port.\n\t\t\t\tconst directOrigins = [\n\t\t\t\t\t`http://127.0.0.1:${allocated.port}`,\n\t\t\t\t\t`http://localhost:${allocated.port}`,\n\t\t\t\t];\n\n\t\t\t\tconst originPolicy = yield* resolveOriginPolicy({\n\t\t\t\t\tapp: identity.app,\n\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\troutedDashboardOrigin,\n\t\t\t\t\tdirectOrigins,\n\t\t\t\t\textraOrigins: opts.allowedOrigins ?? [],\n\t\t\t\t});\n\n\t\t\t\tconst server = yield* listenScopedHttpServer({\n\t\t\t\t\tbindAddress,\n\t\t\t\t\tport: allocated.port,\n\t\t\t\t\tlistener: makeDashboardListener({\n\t\t\t\t\t\toriginPolicy,\n\t\t\t\t\t\tcontext: {\n\t\t\t\t\t\t\tstate: control.state,\n\t\t\t\t\t\t\tpublishCommand: control.publishCommand,\n\t\t\t\t\t\t\tsubmitCommand: control.submitCommand,\n\t\t\t\t\t\t\tdomain: control.domain,\n\t\t\t\t\t\t\tpluginDomain,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t\tonListenError: (cause) =>\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`dashboard HTTP server listen failed on ${bindAddress}:${allocated.port}: ${String(cause)}`,\n\t\t\t\t\t\t),\n\t\t\t\t});\n\n\t\t\t\tconst resolved = { url: server.url, localPort: allocated.port } satisfies DashboardValue;\n\n\t\t\t\t// Emit the Routable via the ctx verb before returning.\n\t\t\t\t// `value.localPort` is this resolved `localPort`;\n\t\t\t\t// `runtime.identity.app`/`.stack` is the `identity` this\n\t\t\t\t// `start` already holds from `IdentityContext`.\n\t\t\t\tctx.endpoint(\n\t\t\t\t\tmakeDashboardRoutable({\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\tport: resolved.localPort,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\treturn resolved;\n\t\t\t}),\n\t});\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA8DA,MAAM,oBAAoB,SAAsC,WAAW;AAO3E,MAAM,yBAAyB;;AAG/B,SAAgB,UAAU,OAAyB,CAAC,GAAc;CACjE,OAAO,aAAa;EACnB,IAAI,kBAAkB;EACtB,MAAM;EACN,SAAS;EAKT,oBAAoB;EACpB,aACC,OAAO,IAAI,aAAa;GACvB,MAAM,MAAM,OAAO;GACnB,MAAM,aAAa,OAAO;GAC1B,MAAM,UAAU,OAAO;GACvB,MAAM,WAAW,OAAO;GAQxB,MAAM,sBAAsB,OAAO,OAAO,cAAc,uBAAuB;GAC/E,MAAM,mBACL,oBAAoB,SAAS,SAAS,oBAAoB,QAAQ;GAKnE,MAAM,eAAe,qBAAqB;IACzC,SAAS,QAAQ;IACjB;GACD,CAAC;GAED,MAAM,cAAc,KAAK,eAAe;GACxC,MAAM,YAAY,OAAO,WAAW,SAAS;IAC5C,OAAO;IACP,YAAY;KAAE,OAAO;KAAO,MAAM;IAAK;IACvC,WAAW,gBAAgB,YAAY,YAAY;IACnD,GAAI,KAAK,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,eAAe,KAAK,KAAK;GAC/D,CAAC;GAUD,MAAM,wBAAwB,OAAO,eAAe,UAAA,KAA8B,CAAC,CAAC,KACnF,OAAO,KAAK,aACX,UAAU;IAAE,UAAU;IAAQ;IAAU,MAAM;GAA0B,CAAC,CAC1E,GACA,OAAO,oBAAoB,IAAI,CAChC;GAMA,MAAM,gBAAgB,CACrB,oBAAoB,UAAU,QAC9B,oBAAoB,UAAU,MAC/B;GAEA,MAAM,eAAe,OAAO,oBAAoB;IAC/C,KAAK,SAAS;IACd,OAAO,SAAS;IAChB;IACA;IACA,cAAc,KAAK,kBAAkB,CAAC;GACvC,CAAC;GAqBD,MAAM,WAAW;IAAE,MAAK,OAnBF,uBAAuB;KAC5C;KACA,MAAM,UAAU;KAChB,UAAU,sBAAsB;MAC/B;MACA,SAAS;OACR,OAAO,QAAQ;OACf,gBAAgB,QAAQ;OACxB,eAAe,QAAQ;OACvB,QAAQ,QAAQ;OAChB;MACD;KACD,CAAC;KACD,gBAAgB,0BACf,IAAI,MACH,0CAA0C,YAAY,GAAG,UAAU,KAAK,IAAI,OAAO,KAAK,GACzF;IACF,CAAC,EAAA,CAE8B;IAAK,WAAW,UAAU;GAAK;GAM9D,IAAI,SACH,sBAAsB;IACrB,KAAK,SAAS;IACd,OAAO,SAAS;IAChB,MAAM,SAAS;GAChB,CAAC,CACF;GAEA,OAAO;EACR,CAAC;CACH,CAAC;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LogAttr } from "../../substrate/runtime/observability/log-attrs.mjs";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
3
|
//#region src/plugins/dashboard/origin-policy.ts
|
|
4
4
|
/**
|
|
@@ -21,8 +21,8 @@ const resolveOriginPolicy = (inputs) => Effect.gen(function* () {
|
|
|
21
21
|
for (const o of inputs.directOrigins) allowed.add(o);
|
|
22
22
|
for (const o of inputs.extraOrigins) allowed.add(o);
|
|
23
23
|
if (allowed.size === 0) yield* Effect.logWarning("dashboard origin allowlist is empty").pipe(Effect.annotateLogs({
|
|
24
|
-
[
|
|
25
|
-
[
|
|
24
|
+
[LogAttr.app]: inputs.app,
|
|
25
|
+
[LogAttr.stack]: inputs.stack
|
|
26
26
|
}));
|
|
27
27
|
return { allowed };
|
|
28
28
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"origin-policy.mjs","names":[],"sources":["../../../src/plugins/dashboard/origin-policy.ts"],"sourcesContent":["// Dashboard plugin — CORS / origin allowlist policy.\n//\n// This MIRRORS the wallet's `plugins/wallet/origin-policy.ts` pattern\n// (stack-scoped allowlist, `resolveOriginPolicy` / `checkOrigin`) rather\n// than importing it, to avoid coupling dashboard→wallet. The two policies\n// are intentionally small and self-contained; the wallet copy carries\n// bearer-specific history that does not apply here.\n//\n// The allowed set is built from real, substrate-wired sources:\n//\n// - the dashboard's OWN router-fronted origin for THIS stack\n// (`routedDashboardOrigin`) — the SPA is served same-origin from this\n// hostname, so the browser's `Origin` on `/graphql` IS this value;\n// - the direct loopback origin(s) the bundled SPA might be loaded from\n// when reached on the raw broker port (`directOrigins`);\n// - any explicit caller-supplied origins (`extraOrigins` →\n// `DashboardOptions.allowedOrigins`).\n//\n// We deliberately do NOT auto-allow a bare `*.localhost` / `localhost:<port>`\n// form: `localhost` is not stack-scoped, so a sibling stack on the same\n// port could drive the destructive control-plane mutations cross-origin.\n\nimport { Effect } from 'effect';\n\nimport {
|
|
1
|
+
{"version":3,"file":"origin-policy.mjs","names":[],"sources":["../../../src/plugins/dashboard/origin-policy.ts"],"sourcesContent":["// Dashboard plugin — CORS / origin allowlist policy.\n//\n// This MIRRORS the wallet's `plugins/wallet/origin-policy.ts` pattern\n// (stack-scoped allowlist, `resolveOriginPolicy` / `checkOrigin`) rather\n// than importing it, to avoid coupling dashboard→wallet. The two policies\n// are intentionally small and self-contained; the wallet copy carries\n// bearer-specific history that does not apply here.\n//\n// The allowed set is built from real, substrate-wired sources:\n//\n// - the dashboard's OWN router-fronted origin for THIS stack\n// (`routedDashboardOrigin`) — the SPA is served same-origin from this\n// hostname, so the browser's `Origin` on `/graphql` IS this value;\n// - the direct loopback origin(s) the bundled SPA might be loaded from\n// when reached on the raw broker port (`directOrigins`);\n// - any explicit caller-supplied origins (`extraOrigins` →\n// `DashboardOptions.allowedOrigins`).\n//\n// We deliberately do NOT auto-allow a bare `*.localhost` / `localhost:<port>`\n// form: `localhost` is not stack-scoped, so a sibling stack on the same\n// port could drive the destructive control-plane mutations cross-origin.\n\nimport { Effect } from 'effect';\n\nimport { LogAttr } from '../../substrate/runtime/observability/log-attrs.ts';\n\n/** Result of resolving the origin allowlist at boot. Captured into the\n * server closure so per-request checks are pure string comparison. */\nexport interface OriginPolicy {\n\treadonly allowed: ReadonlySet<string>;\n}\n\n/** Per-stack inputs the policy resolver needs. Supplied by the plugin at\n * start time (identity + routed-url derivation); this module does not\n * reach into the broker or router itself. */\nexport interface OriginPolicyInputs {\n\treadonly app: string;\n\treadonly stack: string;\n\t/** The dashboard's router-fronted origin for this stack\n\t * (`http://api.<app>.<stack>.localhost:<entrypoint-port>`). `null`\n\t * only in tests that bypass the router derivation. */\n\treadonly routedDashboardOrigin: string | null;\n\t/** Direct loopback origins the SPA may be loaded from when reached on\n\t * the raw broker port (host-loopback fallback / direct tooling). */\n\treadonly directOrigins: ReadonlyArray<string>;\n\t/** Explicit caller-supplied origins (`DashboardOptions.allowedOrigins`). */\n\treadonly extraOrigins: ReadonlyArray<string>;\n}\n\n/**\n * Resolve the per-stack origin allowlist.\n *\n * - Always allowlisted: the dashboard's router-fronted origin for this\n * stack (`routedDashboardOrigin`), when the router derivation produced\n * one — this is the same-origin the bundled SPA loads from.\n * - Always allowlisted: the direct loopback origins + any explicit\n * caller-supplied `extraOrigins`.\n *\n * Empty-allowlist policy: allowed. The dashboard boots normally; with an\n * empty allowlist the per-request gate refuses every cross-origin request\n * (every Origin lands in `forbidden`). A `logWarning` surfaces the\n * configuration for operator visibility.\n */\nexport const resolveOriginPolicy = (inputs: OriginPolicyInputs): Effect.Effect<OriginPolicy> =>\n\tEffect.gen(function* () {\n\t\tconst allowed = new Set<string>();\n\n\t\tif (inputs.routedDashboardOrigin !== null) {\n\t\t\tallowed.add(inputs.routedDashboardOrigin);\n\t\t}\n\t\tfor (const o of inputs.directOrigins) {\n\t\t\tallowed.add(o);\n\t\t}\n\t\tfor (const o of inputs.extraOrigins) {\n\t\t\tallowed.add(o);\n\t\t}\n\n\t\tif (allowed.size === 0) {\n\t\t\tyield* Effect.logWarning('dashboard origin allowlist is empty').pipe(\n\t\t\t\tEffect.annotateLogs({\n\t\t\t\t\t[LogAttr.app]: inputs.app,\n\t\t\t\t\t[LogAttr.stack]: inputs.stack,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\treturn { allowed } satisfies OriginPolicy;\n\t});\n\n/** Per-request origin gate. Returns `'missing'` for absent Origin,\n * `'forbidden'` for an Origin not in the allowlist, `'ok'` for accepted.\n *\n * `'missing'` is its OWN shape: a request with no Origin (server-side\n * tooling, the route-readiness probe, curl) is NOT a cross-origin browser\n * request, so CORS does not govern it — the caller emits a normal response\n * with no `Access-Control-Allow-Origin` header. */\nexport type OriginCheckResult = 'missing' | 'forbidden' | 'ok';\n\nexport const checkOrigin = (\n\tpolicy: OriginPolicy,\n\theaderValue: string | null | undefined,\n): OriginCheckResult => {\n\tif (headerValue === undefined || headerValue === null || headerValue.length === 0) {\n\t\treturn 'missing';\n\t}\n\treturn policy.allowed.has(headerValue) ? 'ok' : 'forbidden';\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AA+DA,MAAa,uBAAuB,WACnC,OAAO,IAAI,aAAa;CACvB,MAAM,0BAAU,IAAI,IAAY;CAEhC,IAAI,OAAO,0BAA0B,MACpC,QAAQ,IAAI,OAAO,qBAAqB;CAEzC,KAAK,MAAM,KAAK,OAAO,eACtB,QAAQ,IAAI,CAAC;CAEd,KAAK,MAAM,KAAK,OAAO,cACtB,QAAQ,IAAI,CAAC;CAGd,IAAI,QAAQ,SAAS,GACpB,OAAO,OAAO,WAAW,qCAAqC,CAAC,CAAC,KAC/D,OAAO,aAAa;GAClB,QAAQ,MAAM,OAAO;GACrB,QAAQ,QAAQ,OAAO;CACzB,CAAC,CACF;CAGD,OAAO,EAAE,QAAQ;AAClB,CAAC;AAWF,MAAa,eACZ,QACA,gBACuB;CACvB,IAAI,gBAAgB,KAAA,KAAa,gBAAgB,QAAQ,YAAY,WAAW,GAC/E,OAAO;CAER,OAAO,OAAO,QAAQ,IAAI,WAAW,IAAI,OAAO;AACjD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routable.mjs","names":[],"sources":["../../../src/plugins/dashboard/routable.ts"],"sourcesContent":["// Dashboard plugin — Routable contribution.\n//\n// The dashboard's HTTP/GraphQL server is a host process (not a docker\n// container) listening on a loopback port the substrate's port broker\n// hands us. The router (Traefik) fronts the loopback port under a\n// stack-scoped hostname like `dashboard.<app>.localhost:<router-port>`.\n//\n// Mirrors `plugins/wallet/routable.ts`. The entrypoint declared here must\n// be aggregated into `plugins/router-entrypoints.ts` so Traefik opens the\n// listener port.\n\nimport type { EntrypointDecl, RoutableDecl } from '../../contracts/routable.ts';\n\nexport const DASHBOARD_ENDPOINT_NAME = 'dashboard';\nexport const DASHBOARD_ROUTE_ROLE = 'api' as const;\nexport const DASHBOARD_ENTRYPOINT_PORT = 9810;\n\nexport const DASHBOARD_ENTRYPOINTS: ReadonlyArray<EntrypointDecl> = [\n\t{ name: DASHBOARD_ENDPOINT_NAME, port: DASHBOARD_ENTRYPOINT_PORT, protocol: 'http' },\n];\n\n/** Construct the Routable decl for the stack-scoped dashboard endpoint. */\nexport const makeDashboardRoutable = (parts: {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly port: number;\n}): RoutableDecl => ({\n\tkind: 'routable',\n\tendpointName: DASHBOARD_ENDPOINT_NAME,\n\tdispatchId: {\n\t\tserviceKey: `dashboard.${parts.app}.${parts.stack}`,\n\t\trole: DASHBOARD_ROUTE_ROLE,\n\t},\n\tupstream: { type: 'host-loopback', port: parts.port },\n\t// CORS is owned by the dashboard server itself (`server.ts` `corsOptionsFor`\n\t// + `origin-policy.ts`): a STACK-SCOPED origin allowlist (this dashboard's\n\t// own router-fronted origin + direct loopback origins + caller-supplied\n\t// `allowedOrigins`) that guards the destructive control-plane mutations and\n\t// closes the cross-stack `*.localhost` hole. We deliberately do NOT attach\n\t// the shared `devstack-cors` Traefik middleware here — that middleware is\n\t// permissive (`*` allow-origin, allow-credentials) and, when the dashboard\n\t// is reached via the router hostname, would overwrite the server's allowlist\n\t// headers and re-open the cross-origin hole. `false` keeps the in-process\n\t// policy authoritative on both the direct loopback port and the router-\n\t// fronted hostname.\n\t//\n\t// The upstream host process binds `0.0.0.0` (see `DASHBOARD_DEFAULT_BIND` in\n\t// `index.ts`) so the Docker-hosted router reaches it via the host-gateway IP\n\t// on native Linux — a `127.0.0.1`-only listener would 502 and trip the route\n\t// readiness probe.\n\tcors: false,\n\twireProtocol: 'http',\n});\n"],"mappings":";AAaA,MAAa,0BAA0B;AAEvC,MAAa,4BAA4B;AAEzC,MAAa,wBAAuD,CACnE;CAAE,MAAM;CAAyB,MAAM;CAA2B,UAAU;
|
|
1
|
+
{"version":3,"file":"routable.mjs","names":[],"sources":["../../../src/plugins/dashboard/routable.ts"],"sourcesContent":["// Dashboard plugin — Routable contribution.\n//\n// The dashboard's HTTP/GraphQL server is a host process (not a docker\n// container) listening on a loopback port the substrate's port broker\n// hands us. The router (Traefik) fronts the loopback port under a\n// stack-scoped hostname like `dashboard.<app>.localhost:<router-port>`.\n//\n// Mirrors `plugins/wallet/routable.ts`. The entrypoint declared here must\n// be aggregated into `plugins/router-entrypoints.ts` so Traefik opens the\n// listener port.\n\nimport type { EntrypointDecl, RoutableDecl } from '../../contracts/routable.ts';\n\nexport const DASHBOARD_ENDPOINT_NAME = 'dashboard';\nexport const DASHBOARD_ROUTE_ROLE = 'api' as const;\nexport const DASHBOARD_ENTRYPOINT_PORT = 9810;\n\nexport const DASHBOARD_ENTRYPOINTS: ReadonlyArray<EntrypointDecl> = [\n\t{ name: DASHBOARD_ENDPOINT_NAME, port: DASHBOARD_ENTRYPOINT_PORT, protocol: 'http' },\n];\n\n/** Construct the Routable decl for the stack-scoped dashboard endpoint. */\nexport const makeDashboardRoutable = (parts: {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly port: number;\n}): RoutableDecl => ({\n\tkind: 'routable',\n\tendpointName: DASHBOARD_ENDPOINT_NAME,\n\tdispatchId: {\n\t\tserviceKey: `dashboard.${parts.app}.${parts.stack}`,\n\t\trole: DASHBOARD_ROUTE_ROLE,\n\t},\n\tupstream: { type: 'host-loopback', port: parts.port },\n\t// CORS is owned by the dashboard server itself (`server.ts` `corsOptionsFor`\n\t// + `origin-policy.ts`): a STACK-SCOPED origin allowlist (this dashboard's\n\t// own router-fronted origin + direct loopback origins + caller-supplied\n\t// `allowedOrigins`) that guards the destructive control-plane mutations and\n\t// closes the cross-stack `*.localhost` hole. We deliberately do NOT attach\n\t// the shared `devstack-cors` Traefik middleware here — that middleware is\n\t// permissive (`*` allow-origin, allow-credentials) and, when the dashboard\n\t// is reached via the router hostname, would overwrite the server's allowlist\n\t// headers and re-open the cross-origin hole. `false` keeps the in-process\n\t// policy authoritative on both the direct loopback port and the router-\n\t// fronted hostname.\n\t//\n\t// The upstream host process binds `0.0.0.0` (see `DASHBOARD_DEFAULT_BIND` in\n\t// `index.ts`) so the Docker-hosted router reaches it via the host-gateway IP\n\t// on native Linux — a `127.0.0.1`-only listener would 502 and trip the route\n\t// readiness probe.\n\tcors: false,\n\twireProtocol: 'http',\n});\n"],"mappings":";AAaA,MAAa,0BAA0B;AAEvC,MAAa,4BAA4B;AAEzC,MAAa,wBAAuD,CACnE;CAAE,MAAM;CAAyB,MAAM;CAA2B,UAAU;AAAO,CACpF;;AAGA,MAAa,yBAAyB,WAIjB;CACpB,MAAM;CACN,cAAc;CACd,YAAY;EACX,YAAY,aAAa,MAAM,IAAI,GAAG,MAAM;EAC5C,MAAA;CACD;CACA,UAAU;EAAE,MAAM;EAAiB,MAAM,MAAM;CAAK;CAiBpD,MAAM;CACN,cAAc;AACf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.mjs","names":[],"sources":["../../../../src/plugins/dashboard/schema/builder.ts"],"sourcesContent":["// Dashboard Pothos `SchemaBuilder` — the single module-level instance.\n//\n// One typed schema covers the control plane: the live projection (`state`),\n// relay node/connection access over the snapshot arrays, control mutations,\n// and the projection subscription. The builder is parameterized with the\n// relay, simple-objects, and with-input plugins (Pothos v4 defaults).\n//\n// Dependencies (the `state` ref + a `publishCommand`) are supplied per\n// request through the GraphQL context (`DashboardContext`), so the schema is\n// a single module-level value with no per-instance closure state. Resolvers\n// read deps from the 3rd resolver argument.\n\nimport SchemaBuilder from '@pothos/core';\nimport RelayPlugin from '@pothos/plugin-relay';\nimport SimpleObjectsPlugin from '@pothos/plugin-simple-objects';\nimport WithInputPlugin from '@pothos/plugin-with-input';\nimport type { Effect, SubscriptionRef } from 'effect';\nimport type { EngineCommand } from '../../../substrate/events.ts';\nimport type { SubscribableState } from '../../../substrate/projection.ts';\nimport type { ControlPlaneDomain } from '../../../substrate/runtime/control-plane/service.ts';\nimport type { DashboardDomain } from '../domain.ts';\n\n/** Per-request GraphQL context for the dashboard control plane. Resolvers\n * read the live projection ref, the command publisher, and the\n * plugin-domain accessor surfaces from here (3rd resolver arg), so the\n * schema carries no closed-over state. */\nexport interface DashboardContext {\n\treadonly state: SubscriptionRef.SubscriptionRef<SubscribableState>;\n\treadonly publishCommand: (command: EngineCommand) => Effect.Effect<void>;\n\t/** Generic, name-blind control-plane accessors: snapshot catalog +\n\t * restore/delete, the observability rings, and the resolved-values\n\t * seam. (Plugin-name-aware shaping lives in `pluginDomain`.) */\n\treadonly domain: ControlPlaneDomain;\n\t/** Plugin-name-aware accessors the dashboard plugin shapes from the\n\t * generic control-plane `resolvedValues`: fork-vs-local mode,\n\t * deepbook/seal/coin capability ids,
|
|
1
|
+
{"version":3,"file":"builder.mjs","names":[],"sources":["../../../../src/plugins/dashboard/schema/builder.ts"],"sourcesContent":["// Dashboard Pothos `SchemaBuilder` — the single module-level instance.\n//\n// One typed schema covers the control plane: the live projection (`state`),\n// relay node/connection access over the snapshot arrays, control mutations,\n// and the projection subscription. The builder is parameterized with the\n// relay, simple-objects, and with-input plugins (Pothos v4 defaults).\n//\n// Dependencies (the `state` ref + a `publishCommand`) are supplied per\n// request through the GraphQL context (`DashboardContext`), so the schema is\n// a single module-level value with no per-instance closure state. Resolvers\n// read deps from the 3rd resolver argument.\n\nimport SchemaBuilder from '@pothos/core';\nimport RelayPlugin from '@pothos/plugin-relay';\nimport SimpleObjectsPlugin from '@pothos/plugin-simple-objects';\nimport WithInputPlugin from '@pothos/plugin-with-input';\nimport type { Effect, SubscriptionRef } from 'effect';\nimport type { EngineCommand } from '../../../substrate/events.ts';\nimport type { SubscribableState } from '../../../substrate/projection.ts';\nimport type { ControlPlaneDomain } from '../../../substrate/runtime/control-plane/service.ts';\nimport type { DashboardDomain } from '../domain.ts';\n\n/** Per-request GraphQL context for the dashboard control plane. Resolvers\n * read the live projection ref, the command publisher, and the\n * plugin-domain accessor surfaces from here (3rd resolver arg), so the\n * schema carries no closed-over state. */\nexport interface DashboardContext {\n\treadonly state: SubscriptionRef.SubscriptionRef<SubscribableState>;\n\treadonly publishCommand: (command: EngineCommand) => Effect.Effect<void>;\n\t/** Submit a command to the supervisor's command-loop and AWAIT its real\n\t * exit (unlike the fire-and-forget `publishCommand`). The restore\n\t * mutation routes through this so the destructive restore + re-acquire\n\t * runs in-band with the single command-loop consumer and the mutation\n\t * only resolves once services are actually back. */\n\treadonly submitCommand: (command: EngineCommand) => Effect.Effect<void, unknown>;\n\t/** Generic, name-blind control-plane accessors: snapshot catalog +\n\t * restore/delete, the observability rings, and the resolved-values\n\t * seam. (Plugin-name-aware shaping lives in `pluginDomain`.) */\n\treadonly domain: ControlPlaneDomain;\n\t/** Plugin-name-aware accessors the dashboard plugin shapes from the\n\t * generic control-plane `resolvedValues`: fork-vs-local mode,\n\t * deepbook/seal/coin capability ids, and the coin mint action. */\n\treadonly pluginDomain: DashboardDomain;\n}\n\ninterface SchemaTypes {\n\tContext: DashboardContext;\n\t// Output fields are non-nullable unless a resolver opts in with\n\t// `nullable: true` — the projection always resolves a concrete value.\n\tDefaultFieldNullability: false;\n}\n\n/** The single module-level builder. Object types, enums, and root types are\n * defined against this instance across the sibling `schema/*` modules; the\n * built schema is `dashboardSchema` (see `../schema.ts`). */\nexport const builder = new SchemaBuilder<SchemaTypes>({\n\tplugins: [SimpleObjectsPlugin, RelayPlugin, WithInputPlugin],\n\trelay: {},\n\tdefaultFieldNullability: false,\n});\n"],"mappings":";;;;;;;;AAuDA,MAAa,UAAU,IAAI,cAA2B;CACrD,SAAS;EAAC;EAAqB;EAAa;CAAe;CAC3D,OAAO,CAAC;CACR,yBAAyB;AAC1B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enums.mjs","names":[],"sources":["../../../../src/plugins/dashboard/schema/enums.ts"],"sourcesContent":["// Pothos enums mirroring the closed unions in projection.ts / lifecycle.ts.\n//\n// Each `values` array is the EXACT string union from the source type — if\n// a substrate union gains a member, the corresponding `satisfies` below\n// stops compiling until this list is updated. That keeps the GraphQL\n// vocabulary in lockstep with the projection vocabulary.\n\nimport { builder } from './builder.ts';\nimport type {\n\tLifecycleStatus as LifecycleStatusUnion,\n\tPluginRole as PluginRoleUnion,\n} from '../../../substrate/lifecycle.ts';\nimport type {\n\tAccountProjection,\n\tEndpoint,\n\tLogTail,\n\tPackageProjection,\n\tRowSection as RowSectionUnion,\n\tStructuredError,\n} from '../../../substrate/projection.ts';\n\n// NOTE: CyclePhase ('shutting-down') and FundingEntryStatus ('already-satisfied')\n// are intentionally NOT GraphQL enums — their values contain hyphens, which are\n// illegal in GraphQL enum value names. Those fields are exposed as String (the\n// raw union string is the wire contract). See schema/types.ts.\ntype WireProtocol = Endpoint['wireProtocol'];\ntype LogLevel = LogTail['level'];\ntype ErrorSeverity = StructuredError['severity'];\ntype AccountScheme = NonNullable<AccountProjection['scheme']>;\ntype AccountSource = NonNullable<AccountProjection['source']>;\ntype FundingStatus = AccountProjection['funding']['status'];\ntype PackageKind = PackageProjection['kind'];\n\n/** Derived health classification for a single service row + the stack. */\nexport type Health = 'ready' | 'active' | 'blocked' | 'empty';\n\nexport const LifecycleStatus = builder.enumType('LifecycleStatus', {\n\tvalues: [\n\t\t'pending',\n\t\t'acquiring',\n\t\t'ready',\n\t\t'failed',\n\t\t'stopping',\n\t\t'stopped',\n\t\t'done',\n\t] satisfies LifecycleStatusUnion[],\n});\n\nexport const PluginRole = builder.enumType('PluginRole', {\n\tvalues: ['service', 'task'] satisfies PluginRoleUnion[],\n});\n\nexport const RowSection = builder.enumType('RowSection', {\n\tvalues: ['service', 'package', 'account', 'action', 'app', 'other'] satisfies RowSectionUnion[],\n});\n\nexport const WireProtocol = builder.enumType('WireProtocol', {\n\tvalues: ['http', 'h2c', 'tcp'] satisfies WireProtocol[],\n});\n\nexport const LogLevel = builder.enumType('LogLevel', {\n\tvalues: ['info', 'warn', 'error'] satisfies LogLevel[],\n});\n\nexport const ErrorSeverity = builder.enumType('ErrorSeverity', {\n\tvalues: ['warn', 'error', 'fatal'] satisfies ErrorSeverity[],\n});\n\nexport const AccountScheme = builder.enumType('AccountScheme', {\n\tvalues: ['ed25519', 'secp256k1', 'secp256r1'] satisfies AccountScheme[],\n});\n\nexport const AccountSource = builder.enumType('AccountSource', {\n\tvalues: ['real', 'impersonate'] satisfies AccountSource[],\n});\n\nexport const FundingStatus = builder.enumType('FundingStatus', {\n\tvalues: ['pending', 'funded', 'skipped', 'failed', 'unknown'] satisfies FundingStatus[],\n});\n\nexport const PackageKind = builder.enumType('PackageKind', {\n\tvalues: ['local', 'known'] satisfies PackageKind[],\n});\n\nexport const Health = builder.enumType('Health', {\n\tvalues: ['ready', 'active', 'blocked', 'empty'] satisfies Health[],\n});\n\n// --- Plugin-domain enums (control-plane `domain` surface) ------------------\n//\n// These mirror the closed unions on the control-plane domain shapes. They\n// carry no hyphenated members EXCEPT seal's `fork-known` / `local-keygen`,\n// which would be illegal GraphQL enum names — so SealMode is exposed as a\n// raw String field (see schema/types.ts), NOT an enum.\n\n/** Fork-vs-local stack mode (advance-clock gating). Derived from the sui\n * plugin's resolved mode; `local-rpc` collapses to `local`. */\nexport const StackMode = builder.enumType('StackMode', {\n\tvalues: ['fork', 'local', 'live'] as const,\n});\n\n/** DeepBook deployment mode. */\nexport const DeepbookMode = builder.enumType('DeepbookMode', {\n\tvalues: ['local', 'override', 'known'] as const,\n});\n\n// NOTE: CoinSource ('on-chain') and SealMode ('local-keygen' / 'fork-known')\n// are intentionally NOT GraphQL enums — their values contain hyphens, which\n// are illegal in GraphQL enum value names. Those fields are exposed as String\n// (the raw union string is the wire contract). See schema/types.ts.\n"],"mappings":";;AAoCA,MAAa,kBAAkB,QAAQ,SAAS,mBAAmB,EAClE,QAAQ;CACP;CACA;CACA;CACA;CACA;CACA;CACA;
|
|
1
|
+
{"version":3,"file":"enums.mjs","names":[],"sources":["../../../../src/plugins/dashboard/schema/enums.ts"],"sourcesContent":["// Pothos enums mirroring the closed unions in projection.ts / lifecycle.ts.\n//\n// Each `values` array is the EXACT string union from the source type — if\n// a substrate union gains a member, the corresponding `satisfies` below\n// stops compiling until this list is updated. That keeps the GraphQL\n// vocabulary in lockstep with the projection vocabulary.\n\nimport { builder } from './builder.ts';\nimport type {\n\tLifecycleStatus as LifecycleStatusUnion,\n\tPluginRole as PluginRoleUnion,\n} from '../../../substrate/lifecycle.ts';\nimport type {\n\tAccountProjection,\n\tEndpoint,\n\tLogTail,\n\tPackageProjection,\n\tRowSection as RowSectionUnion,\n\tStructuredError,\n} from '../../../substrate/projection.ts';\n\n// NOTE: CyclePhase ('shutting-down') and FundingEntryStatus ('already-satisfied')\n// are intentionally NOT GraphQL enums — their values contain hyphens, which are\n// illegal in GraphQL enum value names. Those fields are exposed as String (the\n// raw union string is the wire contract). See schema/types.ts.\ntype WireProtocol = Endpoint['wireProtocol'];\ntype LogLevel = LogTail['level'];\ntype ErrorSeverity = StructuredError['severity'];\ntype AccountScheme = NonNullable<AccountProjection['scheme']>;\ntype AccountSource = NonNullable<AccountProjection['source']>;\ntype FundingStatus = AccountProjection['funding']['status'];\ntype PackageKind = PackageProjection['kind'];\n\n/** Derived health classification for a single service row + the stack. */\nexport type Health = 'ready' | 'active' | 'blocked' | 'empty';\n\nexport const LifecycleStatus = builder.enumType('LifecycleStatus', {\n\tvalues: [\n\t\t'pending',\n\t\t'acquiring',\n\t\t'ready',\n\t\t'failed',\n\t\t'stopping',\n\t\t'stopped',\n\t\t'done',\n\t] satisfies LifecycleStatusUnion[],\n});\n\nexport const PluginRole = builder.enumType('PluginRole', {\n\tvalues: ['service', 'task'] satisfies PluginRoleUnion[],\n});\n\nexport const RowSection = builder.enumType('RowSection', {\n\tvalues: ['service', 'package', 'account', 'action', 'app', 'other'] satisfies RowSectionUnion[],\n});\n\nexport const WireProtocol = builder.enumType('WireProtocol', {\n\tvalues: ['http', 'h2c', 'tcp'] satisfies WireProtocol[],\n});\n\nexport const LogLevel = builder.enumType('LogLevel', {\n\tvalues: ['info', 'warn', 'error'] satisfies LogLevel[],\n});\n\nexport const ErrorSeverity = builder.enumType('ErrorSeverity', {\n\tvalues: ['warn', 'error', 'fatal'] satisfies ErrorSeverity[],\n});\n\nexport const AccountScheme = builder.enumType('AccountScheme', {\n\tvalues: ['ed25519', 'secp256k1', 'secp256r1'] satisfies AccountScheme[],\n});\n\nexport const AccountSource = builder.enumType('AccountSource', {\n\tvalues: ['real', 'impersonate'] satisfies AccountSource[],\n});\n\nexport const FundingStatus = builder.enumType('FundingStatus', {\n\tvalues: ['pending', 'funded', 'skipped', 'failed', 'unknown'] satisfies FundingStatus[],\n});\n\nexport const PackageKind = builder.enumType('PackageKind', {\n\tvalues: ['local', 'known'] satisfies PackageKind[],\n});\n\nexport const Health = builder.enumType('Health', {\n\tvalues: ['ready', 'active', 'blocked', 'empty'] satisfies Health[],\n});\n\n// --- Plugin-domain enums (control-plane `domain` surface) ------------------\n//\n// These mirror the closed unions on the control-plane domain shapes. They\n// carry no hyphenated members EXCEPT seal's `fork-known` / `local-keygen`,\n// which would be illegal GraphQL enum names — so SealMode is exposed as a\n// raw String field (see schema/types.ts), NOT an enum.\n\n/** Fork-vs-local stack mode (advance-clock gating). Derived from the sui\n * plugin's resolved mode; `local-rpc` collapses to `local`. */\nexport const StackMode = builder.enumType('StackMode', {\n\tvalues: ['fork', 'local', 'live'] as const,\n});\n\n/** DeepBook deployment mode. */\nexport const DeepbookMode = builder.enumType('DeepbookMode', {\n\tvalues: ['local', 'override', 'known'] as const,\n});\n\n// NOTE: CoinSource ('on-chain') and SealMode ('local-keygen' / 'fork-known')\n// are intentionally NOT GraphQL enums — their values contain hyphens, which\n// are illegal in GraphQL enum value names. Those fields are exposed as String\n// (the raw union string is the wire contract). See schema/types.ts.\n"],"mappings":";;AAoCA,MAAa,kBAAkB,QAAQ,SAAS,mBAAmB,EAClE,QAAQ;CACP;CACA;CACA;CACA;CACA;CACA;CACA;AACD,EACD,CAAC;AAED,MAAa,aAAa,QAAQ,SAAS,cAAc,EACxD,QAAQ,CAAC,WAAW,MAAM,EAC3B,CAAC;AAED,MAAa,aAAa,QAAQ,SAAS,cAAc,EACxD,QAAQ;CAAC;CAAW;CAAW;CAAW;CAAU;CAAO;AAAO,EACnE,CAAC;AAED,MAAa,eAAe,QAAQ,SAAS,gBAAgB,EAC5D,QAAQ;CAAC;CAAQ;CAAO;AAAK,EAC9B,CAAC;AAED,MAAa,WAAW,QAAQ,SAAS,YAAY,EACpD,QAAQ;CAAC;CAAQ;CAAQ;AAAO,EACjC,CAAC;AAED,MAAa,gBAAgB,QAAQ,SAAS,iBAAiB,EAC9D,QAAQ;CAAC;CAAQ;CAAS;AAAO,EAClC,CAAC;AAED,MAAa,gBAAgB,QAAQ,SAAS,iBAAiB,EAC9D,QAAQ;CAAC;CAAW;CAAa;AAAW,EAC7C,CAAC;AAED,MAAa,gBAAgB,QAAQ,SAAS,iBAAiB,EAC9D,QAAQ,CAAC,QAAQ,aAAa,EAC/B,CAAC;AAED,MAAa,gBAAgB,QAAQ,SAAS,iBAAiB,EAC9D,QAAQ;CAAC;CAAW;CAAU;CAAW;CAAU;AAAS,EAC7D,CAAC;AAED,MAAa,cAAc,QAAQ,SAAS,eAAe,EAC1D,QAAQ,CAAC,SAAS,OAAO,EAC1B,CAAC;AAED,MAAa,SAAS,QAAQ,SAAS,UAAU,EAChD,QAAQ;CAAC;CAAS;CAAU;CAAW;AAAO,EAC/C,CAAC;;;AAWD,MAAa,YAAY,QAAQ,SAAS,aAAa,EACtD,QAAQ;CAAC;CAAQ;CAAS;AAAM,EACjC,CAAC;;AAGD,MAAa,eAAe,QAAQ,SAAS,gBAAgB,EAC5D,QAAQ;CAAC;CAAS;CAAY;AAAO,EACtC,CAAC"}
|