@mysten-incubation/devstack 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dashboard-ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dashboard-ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dashboard-ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dashboard-ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
- package/dashboard-ui/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
- package/dashboard-ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dashboard-ui/assets/index-Bmi1UtAg.js +1356 -0
- package/dashboard-ui/assets/index-D5EShVt4.js +3 -0
- package/dashboard-ui/assets/index-Deml9drg.css +1 -0
- package/dashboard-ui/index.html +13 -0
- package/dist/api/define-capabilities.d.mts +14 -3
- package/dist/api/define-capabilities.mjs +18 -1
- package/dist/api/define-capabilities.mjs.map +1 -1
- package/dist/api/define-devstack.d.mts +1 -1
- package/dist/api/define-devstack.mjs +6 -26
- package/dist/api/define-devstack.mjs.map +1 -1
- package/dist/api/define-plugin.d.mts +1 -0
- package/dist/api/define-plugin.mjs +2 -0
- package/dist/api/inference-network.d.mts +132 -0
- package/dist/api/inference-network.mjs +105 -16
- package/dist/api/inference-network.mjs.map +1 -1
- package/dist/api/plugin-errors.d.mts +7 -0
- package/dist/api/run-stack.d.mts +20 -9
- package/dist/api/run-stack.mjs +56 -29
- package/dist/api/run-stack.mjs.map +1 -1
- package/dist/build-integrations/playwright/config.d.mts +6 -4
- package/dist/build-integrations/playwright/config.mjs +5 -3
- package/dist/build-integrations/playwright/config.mjs.map +1 -1
- package/dist/build-integrations/playwright/errors.d.mts +1 -1
- package/dist/build-integrations/playwright/global-setup.d.mts +43 -17
- package/dist/build-integrations/playwright/global-setup.mjs +250 -13
- package/dist/build-integrations/playwright/global-setup.mjs.map +1 -1
- package/dist/build-integrations/playwright/index.d.mts +5 -3
- package/dist/build-integrations/playwright/index.mjs +4 -2
- package/dist/build-integrations/playwright/stack-context.d.mts +0 -1
- package/dist/build-integrations/playwright/stack-context.mjs +24 -63
- package/dist/build-integrations/playwright/stack-context.mjs.map +1 -1
- package/dist/build-integrations/playwright/wallet-context.d.mts +7 -10
- package/dist/build-integrations/playwright/wallet-context.mjs +8 -13
- package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/cold-start-url.d.mts +5 -0
- package/dist/build-integrations/runtime/cold-start-url.mjs +4 -3
- package/dist/build-integrations/runtime/cold-start-url.mjs.map +1 -1
- package/dist/build-integrations/runtime/conventional-routes.d.mts +51 -0
- package/dist/build-integrations/runtime/conventional-routes.mjs +92 -0
- package/dist/build-integrations/runtime/conventional-routes.mjs.map +1 -0
- package/dist/build-integrations/runtime/discover.d.mts +38 -30
- package/dist/build-integrations/runtime/discover.mjs +73 -70
- package/dist/build-integrations/runtime/discover.mjs.map +1 -1
- package/dist/build-integrations/runtime/endpoint-registry.d.mts +3 -1
- package/dist/build-integrations/runtime/endpoint-registry.mjs +3 -1
- package/dist/build-integrations/runtime/endpoint-registry.mjs.map +1 -1
- package/dist/build-integrations/runtime/index.d.mts +7 -2
- package/dist/build-integrations/runtime/index.mjs +9 -2
- package/dist/build-integrations/runtime/manifest-types.d.mts +2 -0
- package/dist/build-integrations/runtime/manifest-types.mjs +3 -0
- package/dist/build-integrations/runtime/playwright-stack-context-slot.d.mts +29 -0
- package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs +10 -0
- package/dist/build-integrations/runtime/playwright-stack-context-slot.mjs.map +1 -0
- package/dist/build-integrations/runtime/read-stack-context.d.mts +1 -1
- package/dist/build-integrations/runtime/read-stack-context.mjs +10 -4
- package/dist/build-integrations/runtime/read-stack-context.mjs.map +1 -1
- package/dist/build-integrations/runtime/resolve-discovery-env.d.mts +48 -0
- package/dist/build-integrations/runtime/resolve-discovery-env.mjs +42 -0
- package/dist/build-integrations/runtime/resolve-discovery-env.mjs.map +1 -0
- package/dist/build-integrations/runtime/stack-context.d.mts +1 -1
- package/dist/build-integrations/runtime/wallet-paths.mjs +2 -0
- package/dist/build-integrations/vite/index.d.mts +51 -0
- package/dist/build-integrations/vite/index.mjs +73 -0
- package/dist/build-integrations/vite/index.mjs.map +1 -0
- package/dist/build-integrations/vitest/env.d.mts +4 -3
- package/dist/build-integrations/vitest/env.mjs +6 -4
- package/dist/build-integrations/vitest/env.mjs.map +1 -1
- package/dist/build-integrations/vitest/errors.d.mts +25 -4
- package/dist/build-integrations/vitest/errors.mjs +15 -2
- package/dist/build-integrations/vitest/errors.mjs.map +1 -1
- package/dist/build-integrations/vitest/index.d.mts +2 -2
- package/dist/build-integrations/vitest/index.mjs +3 -3
- package/dist/build-integrations/vitest/setup.d.mts +4 -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 +0 -1
- package/dist/build-integrations/vitest/stack-context.mjs +33 -13
- package/dist/build-integrations/vitest/stack-context.mjs.map +1 -1
- package/dist/cli/bail.mjs +23 -0
- package/dist/cli/bail.mjs.map +1 -0
- package/dist/{surfaces/cli/commands → cli}/doctor-probes.mjs +26 -16
- package/dist/cli/doctor-probes.mjs.map +1 -0
- package/dist/cli/main.d.mts +15 -1
- package/dist/cli/main.mjs +166 -482
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/prune-direct.mjs +31 -201
- package/dist/cli/prune-direct.mjs.map +1 -1
- package/dist/cli/snapshot-reader.mjs +14 -11
- package/dist/cli/snapshot-reader.mjs.map +1 -1
- package/dist/cli/up-lifecycle.mjs +4 -1
- package/dist/cli/up-lifecycle.mjs.map +1 -1
- package/dist/cli/wirings/apply.mjs +95 -0
- package/dist/cli/wirings/apply.mjs.map +1 -0
- package/dist/cli/wirings/build-verb-layers.mjs +42 -0
- package/dist/cli/wirings/build-verb-layers.mjs.map +1 -0
- package/dist/cli/wirings/config-loader.mjs +69 -0
- package/dist/cli/wirings/config-loader.mjs.map +1 -0
- package/dist/cli/wirings/engine-command.mjs +30 -0
- package/dist/cli/wirings/engine-command.mjs.map +1 -0
- package/dist/cli/wirings/identity.mjs +72 -0
- package/dist/cli/wirings/identity.mjs.map +1 -0
- package/dist/cli/wirings/provide-file-system.mjs +7 -0
- package/dist/cli/wirings/provide-file-system.mjs.map +1 -0
- package/dist/cli/wirings/snapshot.mjs +177 -0
- package/dist/cli/wirings/snapshot.mjs.map +1 -0
- package/dist/cli/wirings/up.mjs +287 -0
- package/dist/cli/wirings/up.mjs.map +1 -0
- package/dist/cli/wirings/wipe.mjs +40 -0
- package/dist/cli/wirings/wipe.mjs.map +1 -0
- package/dist/contracts/capability-decl.d.mts +0 -2
- package/dist/contracts/codegenable.d.mts +43 -0
- package/dist/contracts/container-runtime.d.mts +26 -1
- package/dist/contracts/faucet-strategy.d.mts +25 -0
- package/dist/contracts/funding-strategy.d.mts +42 -0
- package/dist/contracts/plugin-expander.mjs +60 -0
- package/dist/contracts/plugin-expander.mjs.map +1 -0
- package/dist/contracts/projection.d.mts +5 -3
- package/dist/contracts/renderer.d.mts +23 -0
- package/dist/contracts/routable.d.mts +14 -1
- package/dist/contracts/snapshotable.d.mts +25 -3
- package/dist/contracts/wallet-protocol.d.mts +52 -0
- package/dist/contracts/wallet-protocol.mjs +52 -0
- package/dist/contracts/wallet-protocol.mjs.map +1 -0
- package/dist/index.d.mts +26 -25
- package/dist/index.mjs +20 -13
- package/dist/{runtime → orchestrators}/built-in-plugin-layers.mjs +10 -5
- package/dist/orchestrators/built-in-plugin-layers.mjs.map +1 -0
- package/dist/orchestrators/codegen/bindings.d.mts +1 -2
- package/dist/orchestrators/codegen/bindings.mjs +52 -186
- package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
- package/dist/orchestrators/codegen/emit.mjs +19 -1
- package/dist/orchestrators/codegen/emit.mjs.map +1 -1
- package/dist/orchestrators/codegen/errors.mjs +8 -3
- package/dist/orchestrators/codegen/errors.mjs.map +1 -1
- package/dist/orchestrators/codegen/format.mjs +47 -39
- package/dist/orchestrators/codegen/format.mjs.map +1 -1
- package/dist/orchestrators/codegen/gitignore.mjs +3 -1
- package/dist/orchestrators/codegen/gitignore.mjs.map +1 -1
- package/dist/orchestrators/codegen/output-location.mjs +40 -0
- package/dist/orchestrators/codegen/output-location.mjs.map +1 -0
- package/dist/orchestrators/codegen/paths.d.mts +1 -1
- package/dist/orchestrators/codegen/paths.mjs +40 -14
- package/dist/orchestrators/codegen/paths.mjs.map +1 -1
- package/dist/orchestrators/codegen/permissions.mjs +3 -1
- package/dist/orchestrators/codegen/permissions.mjs.map +1 -1
- package/dist/orchestrators/codegen/service.mjs +157 -105
- package/dist/orchestrators/codegen/service.mjs.map +1 -1
- package/dist/orchestrators/lifecycle-prune/errors.mjs +31 -0
- package/dist/orchestrators/lifecycle-prune/errors.mjs.map +1 -0
- package/dist/orchestrators/lifecycle-prune/index.mjs +350 -0
- package/dist/orchestrators/lifecycle-prune/index.mjs.map +1 -0
- package/dist/orchestrators/router/cleanup.mjs +26 -27
- package/dist/orchestrators/router/cleanup.mjs.map +1 -1
- package/dist/orchestrators/router/entrypoints.mjs +2 -2
- package/dist/orchestrators/router/entrypoints.mjs.map +1 -1
- package/dist/orchestrators/router/file-provider.mjs +14 -5
- package/dist/orchestrators/router/file-provider.mjs.map +1 -1
- package/dist/orchestrators/router/hostname.mjs +19 -55
- package/dist/orchestrators/router/hostname.mjs.map +1 -1
- package/dist/orchestrators/router/index.mjs +11 -0
- package/dist/orchestrators/router/profile.mjs +2 -2
- package/dist/orchestrators/router/sentinels.mjs +26 -0
- package/dist/orchestrators/router/sentinels.mjs.map +1 -0
- package/dist/orchestrators/router/service.mjs +40 -37
- package/dist/orchestrators/router/service.mjs.map +1 -1
- package/dist/orchestrators/router/traefik-container.mjs +27 -16
- package/dist/orchestrators/router/traefik-container.mjs.map +1 -1
- package/dist/{substrate/runtime → orchestrators}/run.mjs +26 -21
- package/dist/orchestrators/run.mjs.map +1 -0
- package/dist/orchestrators/runtime-composition.d.mts +1 -1
- package/dist/orchestrators/runtime-composition.mjs +123 -39
- package/dist/orchestrators/runtime-composition.mjs.map +1 -1
- package/dist/orchestrators/snapshot/capture-command.d.mts +1 -0
- package/dist/orchestrators/snapshot/capture-command.mjs +25 -0
- package/dist/orchestrators/snapshot/capture-command.mjs.map +1 -0
- package/dist/orchestrators/snapshot/capture.mjs +42 -28
- package/dist/orchestrators/snapshot/capture.mjs.map +1 -1
- package/dist/orchestrators/snapshot/descriptor.mjs +35 -10
- package/dist/orchestrators/snapshot/descriptor.mjs.map +1 -1
- package/dist/orchestrators/snapshot/identity-guard.mjs +1 -1
- package/dist/orchestrators/snapshot/index.mjs +13 -0
- package/dist/orchestrators/snapshot/integrity.d.mts +1 -1
- package/dist/orchestrators/snapshot/integrity.mjs +73 -17
- package/dist/orchestrators/snapshot/integrity.mjs.map +1 -1
- package/dist/orchestrators/snapshot/pending-marker.d.mts +1 -0
- package/dist/orchestrators/snapshot/pending-marker.mjs +99 -0
- package/dist/orchestrators/snapshot/pending-marker.mjs.map +1 -0
- package/dist/orchestrators/snapshot/phase-error.mjs +23 -0
- package/dist/orchestrators/snapshot/phase-error.mjs.map +1 -0
- package/dist/orchestrators/snapshot/prune.mjs +20 -55
- package/dist/orchestrators/snapshot/prune.mjs.map +1 -1
- package/dist/orchestrators/snapshot/recover-pending.d.mts +1 -0
- package/dist/orchestrators/snapshot/recover-pending.mjs +223 -0
- package/dist/orchestrators/snapshot/recover-pending.mjs.map +1 -0
- package/dist/orchestrators/snapshot/restore.mjs +118 -84
- package/dist/orchestrators/snapshot/restore.mjs.map +1 -1
- package/dist/orchestrators/snapshot/service.mjs +53 -56
- package/dist/orchestrators/snapshot/service.mjs.map +1 -1
- package/dist/orchestrators/snapshot/state-document.d.mts +1 -0
- package/dist/orchestrators/snapshot/state-document.mjs +47 -7
- package/dist/orchestrators/snapshot/state-document.mjs.map +1 -1
- package/dist/orchestrators/snapshot/wipe.mjs +67 -10
- package/dist/orchestrators/snapshot/wipe.mjs.map +1 -1
- package/dist/plugins/account/codegen.mjs +9 -8
- package/dist/plugins/account/codegen.mjs.map +1 -1
- package/dist/plugins/account/errors.d.mts +33 -6
- package/dist/plugins/account/errors.mjs.map +1 -1
- package/dist/plugins/account/funding.d.mts +29 -19
- package/dist/plugins/account/funding.mjs +84 -66
- package/dist/plugins/account/funding.mjs.map +1 -1
- package/dist/plugins/account/index.d.mts +2 -4
- package/dist/plugins/account/index.mjs +64 -31
- package/dist/plugins/account/index.mjs.map +1 -1
- package/dist/plugins/account/keypair.mjs +6 -9
- package/dist/plugins/account/keypair.mjs.map +1 -1
- package/dist/plugins/account/lease.mjs +1 -0
- package/dist/plugins/account/lease.mjs.map +1 -1
- package/dist/plugins/account/registry.d.mts +14 -1
- package/dist/plugins/account/registry.mjs +18 -18
- package/dist/plugins/account/registry.mjs.map +1 -1
- package/dist/plugins/account/service.d.mts +23 -4
- package/dist/plugins/account/service.mjs +83 -45
- package/dist/plugins/account/service.mjs.map +1 -1
- package/dist/plugins/account/snapshot.mjs +2 -5
- package/dist/plugins/account/snapshot.mjs.map +1 -1
- package/dist/plugins/account/spans.mjs +16 -0
- package/dist/plugins/account/spans.mjs.map +1 -0
- package/dist/plugins/account/variants/keystore.mjs +6 -8
- package/dist/plugins/account/variants/keystore.mjs.map +1 -1
- package/dist/plugins/action/build-context.d.mts +3 -2
- package/dist/plugins/action/errors.d.mts +16 -12
- package/dist/plugins/action/errors.mjs.map +1 -1
- package/dist/plugins/action/execute.mjs +65 -103
- package/dist/plugins/action/execute.mjs.map +1 -1
- package/dist/plugins/action/index.d.mts +0 -2
- package/dist/plugins/action/index.mjs +17 -13
- package/dist/plugins/action/index.mjs.map +1 -1
- package/dist/plugins/action/service.mjs +38 -35
- package/dist/plugins/action/service.mjs.map +1 -1
- package/dist/plugins/action/spans.mjs +11 -0
- package/dist/plugins/action/spans.mjs.map +1 -0
- package/dist/plugins/coin/codegen.mjs +9 -8
- package/dist/plugins/coin/codegen.mjs.map +1 -1
- package/dist/plugins/coin/discovery.mjs +8 -4
- package/dist/plugins/coin/discovery.mjs.map +1 -1
- package/dist/plugins/coin/index.d.mts +11 -29
- package/dist/plugins/coin/index.mjs +56 -27
- package/dist/plugins/coin/index.mjs.map +1 -1
- package/dist/plugins/coin/metadata.mjs +8 -15
- package/dist/plugins/coin/metadata.mjs.map +1 -1
- package/dist/plugins/coin/mint.d.mts +30 -11
- package/dist/plugins/coin/mint.mjs +78 -74
- package/dist/plugins/coin/mint.mjs.map +1 -1
- package/dist/plugins/coin/registry.mjs +4 -7
- package/dist/plugins/coin/registry.mjs.map +1 -1
- package/dist/plugins/coin/service.d.mts +39 -1
- package/dist/plugins/coin/service.mjs +11 -4
- package/dist/plugins/coin/service.mjs.map +1 -1
- package/dist/plugins/coin/spans.mjs +20 -0
- package/dist/plugins/coin/spans.mjs.map +1 -0
- package/dist/plugins/dashboard/domain.mjs +402 -0
- package/dist/plugins/dashboard/domain.mjs.map +1 -0
- package/dist/plugins/dashboard/index.d.mts +29 -0
- package/dist/plugins/dashboard/index.mjs +91 -0
- package/dist/plugins/dashboard/index.mjs.map +1 -0
- package/dist/plugins/dashboard/origin-policy.mjs +36 -0
- package/dist/plugins/dashboard/origin-policy.mjs.map +1 -0
- package/dist/plugins/dashboard/routable.mjs +27 -0
- package/dist/plugins/dashboard/routable.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/builder.mjs +21 -0
- package/dist/plugins/dashboard/schema/builder.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/enums.mjs +72 -0
- package/dist/plugins/dashboard/schema/enums.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/root.mjs +351 -0
- package/dist/plugins/dashboard/schema/root.mjs.map +1 -0
- package/dist/plugins/dashboard/schema/types.mjs +508 -0
- package/dist/plugins/dashboard/schema/types.mjs.map +1 -0
- package/dist/plugins/dashboard/schema.mjs +11 -0
- package/dist/plugins/dashboard/schema.mjs.map +1 -0
- package/dist/plugins/dashboard/server.mjs +166 -0
- package/dist/plugins/dashboard/server.mjs.map +1 -0
- package/dist/plugins/deepbook/deploy.mjs +53 -115
- package/dist/plugins/deepbook/deploy.mjs.map +1 -1
- package/dist/plugins/deepbook/errors.mjs +1 -0
- package/dist/plugins/deepbook/errors.mjs.map +1 -1
- package/dist/plugins/deepbook/faucet-strategy.mjs +47 -34
- package/dist/plugins/deepbook/faucet-strategy.mjs.map +1 -1
- package/dist/plugins/deepbook/hash.mjs +9 -0
- package/dist/plugins/deepbook/hash.mjs.map +1 -0
- package/dist/plugins/deepbook/index.d.mts +1 -10
- package/dist/plugins/deepbook/index.mjs +25 -22
- package/dist/plugins/deepbook/index.mjs.map +1 -1
- package/dist/plugins/deepbook/pyth/index.mjs +26 -55
- package/dist/plugins/deepbook/pyth/index.mjs.map +1 -1
- package/dist/plugins/deepbook/snapshot.mjs +17 -29
- package/dist/plugins/deepbook/snapshot.mjs.map +1 -1
- package/dist/plugins/deepbook/spans.mjs +18 -0
- package/dist/plugins/deepbook/spans.mjs.map +1 -0
- package/dist/plugins/deepbook/type-strings.mjs +65 -0
- package/dist/plugins/deepbook/type-strings.mjs.map +1 -0
- package/dist/plugins/deepbook/types.d.mts +2 -3
- package/dist/plugins/deepbook/types.mjs.map +1 -1
- package/dist/plugins/faucet/dispatcher.d.mts +21 -2
- package/dist/plugins/faucet/dispatcher.mjs +1 -2
- package/dist/plugins/faucet/dispatcher.mjs.map +1 -1
- package/dist/plugins/faucet/errors.d.mts +11 -24
- package/dist/plugins/faucet/errors.mjs.map +1 -1
- package/dist/plugins/faucet/http.mjs +33 -14
- package/dist/plugins/faucet/http.mjs.map +1 -1
- package/dist/plugins/faucet/index.d.mts +15 -5
- package/dist/plugins/faucet/index.mjs +3 -0
- package/dist/plugins/faucet/index.mjs.map +1 -1
- package/dist/plugins/faucet/spans.mjs +12 -0
- package/dist/plugins/faucet/spans.mjs.map +1 -0
- package/dist/plugins/host-service/errors.d.mts +1 -1
- package/dist/plugins/host-service/index.mjs +34 -8
- package/dist/plugins/host-service/index.mjs.map +1 -1
- package/dist/plugins/host-service/routable.mjs +2 -1
- package/dist/plugins/host-service/routable.mjs.map +1 -1
- package/dist/plugins/host-service/service.d.mts +21 -0
- package/dist/plugins/host-service/service.mjs +136 -81
- package/dist/plugins/host-service/service.mjs.map +1 -1
- package/dist/plugins/internal/acquire-on-chain-artifact.mjs +23 -0
- package/dist/plugins/internal/acquire-on-chain-artifact.mjs.map +1 -0
- package/dist/plugins/internal/codegen-helpers.mjs +18 -0
- package/dist/plugins/internal/codegen-helpers.mjs.map +1 -0
- package/dist/plugins/internal/funding-failure-error.mjs +32 -0
- package/dist/plugins/internal/funding-failure-error.mjs.map +1 -0
- package/dist/plugins/package/build.mjs +1 -1
- package/dist/plugins/package/build.mjs.map +1 -1
- package/dist/plugins/package/codegen.mjs +26 -0
- package/dist/plugins/package/codegen.mjs.map +1 -1
- package/dist/plugins/package/errors.d.mts +16 -6
- package/dist/plugins/package/errors.mjs.map +1 -1
- package/dist/plugins/package/index.d.mts +0 -4
- package/dist/plugins/package/index.mjs +27 -21
- package/dist/plugins/package/index.mjs.map +1 -1
- package/dist/plugins/package/mode-known.mjs +26 -12
- package/dist/plugins/package/mode-known.mjs.map +1 -1
- package/dist/plugins/package/mode-local.mjs +47 -41
- package/dist/plugins/package/mode-local.mjs.map +1 -1
- package/dist/plugins/package/publish-executor.mjs +55 -114
- package/dist/plugins/package/publish-executor.mjs.map +1 -1
- package/dist/plugins/package/registry.mjs +2 -1
- package/dist/plugins/package/registry.mjs.map +1 -1
- package/dist/plugins/package/service.mjs.map +1 -1
- package/dist/plugins/package/spans.mjs +14 -0
- package/dist/plugins/package/spans.mjs.map +1 -0
- package/dist/plugins/postgres/db-ensure.mjs +19 -12
- package/dist/plugins/postgres/db-ensure.mjs.map +1 -1
- package/dist/plugins/postgres/errors.d.mts +5 -0
- package/dist/plugins/postgres/errors.mjs.map +1 -1
- package/dist/plugins/postgres/index.mjs +10 -17
- package/dist/plugins/postgres/index.mjs.map +1 -1
- package/dist/plugins/postgres/service.mjs +58 -24
- package/dist/plugins/postgres/service.mjs.map +1 -1
- package/dist/plugins/postgres/snapshot.mjs.map +1 -1
- package/dist/plugins/postgres/spans.mjs +11 -0
- package/dist/plugins/postgres/spans.mjs.map +1 -0
- package/dist/plugins/router-entrypoints.mjs +4 -4
- package/dist/plugins/router-entrypoints.mjs.map +1 -1
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs +15 -13
- package/dist/plugins/seal/bootstrap-assets/cargo-image.mjs.map +1 -1
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs +15 -19
- package/dist/plugins/seal/bootstrap-assets/source-fetch.mjs.map +1 -1
- package/dist/plugins/seal/codegen.d.mts +2 -3
- package/dist/plugins/seal/codegen.mjs.map +1 -1
- package/dist/plugins/seal/config-render.mjs +35 -8
- package/dist/plugins/seal/config-render.mjs.map +1 -1
- package/dist/plugins/seal/deploy.mjs +31 -39
- package/dist/plugins/seal/deploy.mjs.map +1 -1
- package/dist/plugins/seal/errors.d.mts +1 -3
- package/dist/plugins/seal/errors.mjs +7 -1
- package/dist/plugins/seal/errors.mjs.map +1 -1
- package/dist/plugins/seal/index.d.mts +1 -6
- package/dist/plugins/seal/index.mjs +40 -14
- package/dist/plugins/seal/index.mjs.map +1 -1
- package/dist/plugins/seal/key-manager.d.mts +1 -9
- package/dist/plugins/seal/key-manager.mjs +2 -18
- package/dist/plugins/seal/key-manager.mjs.map +1 -1
- package/dist/plugins/seal/key-server.mjs +37 -36
- package/dist/plugins/seal/key-server.mjs.map +1 -1
- package/dist/plugins/seal/keygen.mjs +16 -17
- package/dist/plugins/seal/keygen.mjs.map +1 -1
- package/dist/plugins/seal/mode/fork-known.mjs +36 -14
- package/dist/plugins/seal/mode/fork-known.mjs.map +1 -1
- package/dist/plugins/seal/mode/live.d.mts +6 -4
- package/dist/plugins/seal/mode/live.mjs +14 -21
- package/dist/plugins/seal/mode/live.mjs.map +1 -1
- package/dist/plugins/seal/mode/local-keygen.mjs +57 -21
- package/dist/plugins/seal/mode/local-keygen.mjs.map +1 -1
- package/dist/plugins/seal/registry-publish.mjs +1 -0
- 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/spans.mjs +18 -0
- package/dist/plugins/seal/spans.mjs.map +1 -0
- package/dist/plugins/sui/auto-tick.mjs +17 -23
- package/dist/plugins/sui/auto-tick.mjs.map +1 -1
- package/dist/plugins/sui/chain-build-container.mjs +18 -0
- package/dist/plugins/sui/chain-build-container.mjs.map +1 -0
- package/dist/plugins/sui/chain-probe.d.mts +5 -5
- package/dist/plugins/sui/chain-probe.mjs +28 -58
- package/dist/plugins/sui/chain-probe.mjs.map +1 -1
- package/dist/plugins/sui/codegen.mjs +26 -0
- package/dist/plugins/sui/codegen.mjs.map +1 -1
- package/dist/plugins/sui/errors.d.mts +2 -29
- package/dist/plugins/sui/errors.mjs +1 -3
- package/dist/plugins/sui/errors.mjs.map +1 -1
- package/dist/plugins/sui/fork-faucet-strategy.d.mts +1 -0
- package/dist/plugins/sui/fork-faucet-strategy.mjs +55 -0
- package/dist/plugins/sui/fork-faucet-strategy.mjs.map +1 -0
- package/dist/plugins/sui/fork-orchestration.d.mts +1 -0
- package/dist/plugins/sui/fork-orchestration.mjs +170 -2
- package/dist/plugins/sui/fork-orchestration.mjs.map +1 -1
- package/dist/plugins/sui/fork-transaction.d.mts +2 -0
- package/dist/plugins/sui/fork-transaction.mjs +92 -28
- package/dist/plugins/sui/fork-transaction.mjs.map +1 -1
- package/dist/plugins/sui/index.d.mts +33 -70
- package/dist/plugins/sui/index.mjs +58 -35
- package/dist/plugins/sui/index.mjs.map +1 -1
- package/dist/plugins/{faucet/strategies/sui-local.mjs → sui/local-faucet-strategy.mjs} +10 -7
- package/dist/plugins/sui/local-faucet-strategy.mjs.map +1 -0
- package/dist/plugins/sui/mode/external.mjs +7 -13
- package/dist/plugins/sui/mode/external.mjs.map +1 -1
- package/dist/plugins/sui/mode/fork.mjs +136 -59
- package/dist/plugins/sui/mode/fork.mjs.map +1 -1
- package/dist/plugins/sui/mode/live.mjs +10 -16
- package/dist/plugins/sui/mode/live.mjs.map +1 -1
- package/dist/plugins/sui/mode/local.mjs +37 -36
- package/dist/plugins/sui/mode/local.mjs.map +1 -1
- package/dist/plugins/sui/mode/shared-boot.mjs +38 -115
- package/dist/plugins/sui/mode/shared-boot.mjs.map +1 -1
- package/dist/plugins/sui/mode/shared.d.mts +1 -5
- package/dist/plugins/sui/mode/shared.mjs.map +1 -1
- package/dist/plugins/sui/mode/spec.d.mts +18 -0
- package/dist/plugins/sui/move-summary-runner.mjs +207 -0
- package/dist/plugins/sui/move-summary-runner.mjs.map +1 -0
- package/dist/plugins/sui/network-resolver.d.mts +4 -3
- package/dist/plugins/sui/service.mjs.map +1 -1
- package/dist/plugins/sui/snapshot.mjs +3 -0
- package/dist/plugins/sui/snapshot.mjs.map +1 -1
- package/dist/plugins/sui/spans.mjs +17 -0
- package/dist/plugins/sui/spans.mjs.map +1 -0
- package/dist/plugins/wallet/codegen.d.mts +0 -1
- package/dist/plugins/wallet/codegen.mjs +8 -9
- package/dist/plugins/wallet/codegen.mjs.map +1 -1
- package/dist/plugins/wallet/index.d.mts +6 -8
- package/dist/plugins/wallet/index.mjs +46 -35
- package/dist/plugins/wallet/index.mjs.map +1 -1
- package/dist/plugins/wallet/origin-policy.mjs +12 -28
- package/dist/plugins/wallet/origin-policy.mjs.map +1 -1
- package/dist/plugins/wallet/pairing.mjs +6 -4
- package/dist/plugins/wallet/pairing.mjs.map +1 -1
- package/dist/plugins/wallet/protocol.d.mts +1 -0
- package/dist/plugins/wallet/protocol.mjs +5 -44
- package/dist/plugins/wallet/protocol.mjs.map +1 -1
- package/dist/plugins/wallet/routable.mjs +2 -8
- package/dist/plugins/wallet/routable.mjs.map +1 -1
- package/dist/plugins/wallet/server.mjs +36 -44
- package/dist/plugins/wallet/server.mjs.map +1 -1
- package/dist/plugins/wallet/service.d.mts +1 -5
- package/dist/plugins/wallet/service.mjs +22 -18
- package/dist/plugins/wallet/service.mjs.map +1 -1
- package/dist/plugins/wallet/spans.mjs +22 -0
- package/dist/plugins/wallet/spans.mjs.map +1 -0
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs +33 -8
- package/dist/plugins/walrus/bootstrap-assets/cargo-image.mjs.map +1 -1
- package/dist/plugins/walrus/codegen.d.mts +3 -3
- package/dist/plugins/walrus/codegen.mjs.map +1 -1
- package/dist/plugins/walrus/deploy-paths.mjs +21 -5
- package/dist/plugins/walrus/deploy-paths.mjs.map +1 -1
- package/dist/plugins/walrus/deploy.mjs +67 -69
- package/dist/plugins/walrus/deploy.mjs.map +1 -1
- package/dist/plugins/walrus/errors.mjs +1 -0
- package/dist/plugins/walrus/errors.mjs.map +1 -1
- package/dist/plugins/walrus/faucet-strategy.mjs +12 -7
- package/dist/plugins/walrus/faucet-strategy.mjs.map +1 -1
- package/dist/plugins/walrus/index.d.mts +2 -13
- package/dist/plugins/walrus/index.mjs +18 -23
- package/dist/plugins/walrus/index.mjs.map +1 -1
- package/dist/plugins/walrus/mode/known-deploy.mjs +7 -5
- package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
- package/dist/plugins/walrus/mode/local-cluster.mjs +26 -7
- package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
- package/dist/plugins/walrus/registry-publish.mjs.map +1 -1
- package/dist/plugins/walrus/routable.mjs +7 -23
- package/dist/plugins/walrus/routable.mjs.map +1 -1
- package/dist/plugins/walrus/service.mjs.map +1 -1
- package/dist/plugins/walrus/spans.mjs +18 -0
- package/dist/plugins/walrus/spans.mjs.map +1 -0
- package/dist/plugins/walrus/storage-nodes.d.mts +6 -2
- package/dist/plugins/walrus/storage-nodes.mjs +15 -8
- package/dist/plugins/walrus/storage-nodes.mjs.map +1 -1
- package/dist/plugins/walrus/wal-swap.mjs +58 -15
- package/dist/plugins/walrus/wal-swap.mjs.map +1 -1
- package/dist/primitives/artifact-publisher.d.mts +1 -1
- package/dist/primitives/artifact-publisher.mjs +15 -0
- package/dist/primitives/artifact-publisher.mjs.map +1 -0
- package/dist/runtime/docker/client.mjs +17 -4
- package/dist/runtime/docker/client.mjs.map +1 -1
- package/dist/runtime/docker/container.mjs +219 -132
- package/dist/runtime/docker/container.mjs.map +1 -1
- package/dist/runtime/docker/errors.mjs +0 -6
- package/dist/runtime/docker/errors.mjs.map +1 -1
- package/dist/runtime/docker/exec.mjs +21 -18
- package/dist/runtime/docker/exec.mjs.map +1 -1
- package/dist/runtime/docker/image.mjs +36 -13
- package/dist/runtime/docker/image.mjs.map +1 -1
- package/dist/runtime/docker/index.mjs +14 -0
- package/dist/runtime/docker/inspect-and-decode.mjs +51 -0
- package/dist/runtime/docker/inspect-and-decode.mjs.map +1 -0
- package/dist/runtime/docker/inventory.mjs +14 -11
- package/dist/runtime/docker/inventory.mjs.map +1 -1
- package/dist/runtime/docker/labels.mjs +38 -5
- package/dist/runtime/docker/labels.mjs.map +1 -1
- package/dist/runtime/docker/logs.d.mts +1 -1
- package/dist/runtime/docker/logs.mjs.map +1 -1
- package/dist/runtime/docker/network.mjs +83 -47
- package/dist/runtime/docker/network.mjs.map +1 -1
- package/dist/runtime/docker/render-run-args.mjs +82 -0
- package/dist/runtime/docker/render-run-args.mjs.map +1 -0
- package/dist/runtime/docker/service.d.mts +1 -1
- package/dist/runtime/docker/service.mjs +12 -7
- package/dist/runtime/docker/service.mjs.map +1 -1
- package/dist/runtime/docker/sweep.mjs +118 -43
- package/dist/runtime/docker/sweep.mjs.map +1 -1
- package/dist/runtime/docker/volume.mjs +2 -0
- package/dist/runtime/docker/volume.mjs.map +1 -1
- package/dist/runtime/docker/wrap.mjs +30 -1
- package/dist/runtime/docker/wrap.mjs.map +1 -1
- package/dist/substrate/cross-process.mjs +14 -8
- package/dist/substrate/cross-process.mjs.map +1 -1
- package/dist/substrate/event-time.mjs +33 -0
- package/dist/substrate/event-time.mjs.map +1 -0
- package/dist/substrate/events.d.mts +28 -8
- package/dist/substrate/identity.d.mts +2 -1
- package/dist/substrate/manifest.d.mts +68 -5
- package/dist/substrate/manifest.mjs +34 -5
- package/dist/substrate/manifest.mjs.map +1 -1
- package/dist/substrate/network.d.mts +3 -5
- package/dist/substrate/options.d.mts +0 -2
- package/dist/substrate/plugin.d.mts +13 -0
- package/dist/substrate/plugin.mjs +4 -2
- package/dist/substrate/plugin.mjs.map +1 -1
- package/dist/substrate/projection.d.mts +35 -3
- package/dist/substrate/runtime/artifact-publisher/index.mjs +19 -9
- package/dist/substrate/runtime/artifact-publisher/index.mjs.map +1 -1
- package/dist/substrate/runtime/atomic-write.mjs +97 -32
- package/dist/substrate/runtime/atomic-write.mjs.map +1 -1
- package/dist/substrate/runtime/cache/index.mjs +3 -0
- package/dist/substrate/runtime/cache/schema.mjs +2 -2
- package/dist/substrate/runtime/cache/schema.mjs.map +1 -1
- package/dist/substrate/runtime/cache/service.mjs +9 -7
- package/dist/substrate/runtime/cache/service.mjs.map +1 -1
- package/dist/substrate/runtime/capability-sinks/index.d.mts +1 -1
- package/dist/substrate/runtime/capability-sinks/index.mjs +3 -0
- package/dist/substrate/runtime/capability-sinks/layer.mjs +1 -1
- package/dist/substrate/runtime/capability-sinks/service.d.mts +61 -2
- package/dist/substrate/runtime/capability-sinks/service.mjs +21 -26
- package/dist/substrate/runtime/capability-sinks/service.mjs.map +1 -1
- package/dist/substrate/runtime/config-validation.d.mts +1 -13
- package/dist/substrate/runtime/config-validation.mjs +2 -91
- package/dist/substrate/runtime/config-validation.mjs.map +1 -1
- package/dist/substrate/runtime/container-runtime.d.mts +1 -0
- package/dist/substrate/runtime/container-runtime.mjs +2 -0
- package/dist/substrate/runtime/control-plane/domain.mjs +101 -0
- package/dist/substrate/runtime/control-plane/domain.mjs.map +1 -0
- package/dist/substrate/runtime/control-plane/service.mjs +7 -0
- package/dist/substrate/runtime/control-plane/service.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs +46 -19
- package/dist/substrate/runtime/cross-process/command-channel/channel.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs +30 -12
- package/dist/substrate/runtime/cross-process/command-channel/file-channel.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/command-channel/index.mjs +5 -0
- package/dist/substrate/runtime/cross-process/command-channel/protocol.mjs +11 -2
- 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 +1 -1
- package/dist/substrate/runtime/cross-process/index.mjs +11 -0
- package/dist/substrate/runtime/cross-process/live-clock.mjs +34 -0
- package/dist/substrate/runtime/cross-process/live-clock.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/liveness.mjs +52 -10
- package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/lock.d.mts +1 -0
- package/dist/substrate/runtime/cross-process/lock.mjs +23 -0
- package/dist/substrate/runtime/cross-process/lock.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs +64 -0
- package/dist/substrate/runtime/cross-process/reclaim-stale-file.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/roster.mjs +97 -83
- package/dist/substrate/runtime/cross-process/roster.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/self-pid.mjs +8 -0
- package/dist/substrate/runtime/cross-process/self-pid.mjs.map +1 -0
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs +24 -28
- package/dist/substrate/runtime/cross-process/snapshot-reservation.mjs.map +1 -1
- package/dist/substrate/runtime/cross-process/stack-lock.mjs +41 -20
- package/dist/substrate/runtime/cross-process/stack-lock.mjs.map +1 -1
- package/dist/substrate/runtime/current-plugin.mjs +2 -2
- package/dist/substrate/runtime/current-plugin.mjs.map +1 -1
- package/dist/substrate/runtime/errors.mjs +12 -6
- package/dist/substrate/runtime/errors.mjs.map +1 -1
- package/dist/substrate/runtime/format-unknown-error.mjs +30 -0
- package/dist/substrate/runtime/format-unknown-error.mjs.map +1 -0
- package/dist/substrate/runtime/host-bind-mount-owner.mjs +10 -0
- package/dist/substrate/runtime/host-bind-mount-owner.mjs.map +1 -0
- package/dist/substrate/runtime/host-gateway.mjs +20 -0
- package/dist/substrate/runtime/host-gateway.mjs.map +1 -0
- package/dist/substrate/runtime/host-tree-tar/index.mjs +61 -50
- package/dist/substrate/runtime/host-tree-tar/index.mjs.map +1 -1
- package/dist/substrate/runtime/http-probe.d.mts +1 -1
- package/dist/substrate/runtime/index.mjs +14 -0
- package/dist/substrate/runtime/lease-broker/index.d.mts +1 -1
- package/dist/substrate/runtime/lease-broker/index.mjs +2 -0
- package/dist/substrate/runtime/lease-broker/service.d.mts +1 -64
- package/dist/substrate/runtime/lease-broker/service.mjs +1 -1
- package/dist/substrate/runtime/lease-broker/service.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs +22 -3
- package/dist/substrate/runtime/lifecycle/dep-graph.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/index.mjs +9 -0
- package/dist/substrate/runtime/lifecycle/lifecycle-fact.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs +20 -3
- package/dist/substrate/runtime/lifecycle/plugin-registry.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/selective-restart.mjs +1 -1
- package/dist/substrate/runtime/lifecycle/signals.mjs +5 -5
- package/dist/substrate/runtime/lifecycle/signals.mjs.map +1 -1
- package/dist/substrate/runtime/lifecycle/state-machine.mjs +1 -1
- package/dist/substrate/runtime/managed-container.d.mts +19 -1
- package/dist/substrate/runtime/managed-container.mjs +20 -1
- package/dist/substrate/runtime/managed-container.mjs.map +1 -1
- package/dist/substrate/runtime/manifest/index.mjs +3 -0
- package/dist/substrate/runtime/manifest/manifest.mjs +15 -11
- package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
- package/dist/substrate/runtime/mode-errors.mjs +20 -0
- package/dist/substrate/runtime/mode-errors.mjs.map +1 -0
- package/dist/substrate/runtime/observability/cascade-formatter.mjs +9 -13
- package/dist/substrate/runtime/observability/cascade-formatter.mjs.map +1 -1
- package/dist/substrate/runtime/observability/formatter-registry.mjs +19 -36
- package/dist/substrate/runtime/observability/formatter-registry.mjs.map +1 -1
- package/dist/substrate/runtime/observability/ignore-with-log.d.mts +1 -0
- package/dist/substrate/runtime/observability/ignore-with-log.mjs +17 -0
- package/dist/substrate/runtime/observability/ignore-with-log.mjs.map +1 -0
- package/dist/substrate/runtime/observability/index.d.mts +1 -1
- package/dist/substrate/runtime/observability/index.mjs +13 -0
- package/dist/substrate/runtime/observability/log-store.d.mts +1 -0
- package/dist/substrate/runtime/observability/log-store.mjs +175 -0
- package/dist/substrate/runtime/observability/log-store.mjs.map +1 -0
- package/dist/substrate/runtime/observability/logger.d.mts +2 -1
- package/dist/substrate/runtime/observability/logger.mjs +5 -9
- package/dist/substrate/runtime/observability/logger.mjs.map +1 -1
- package/dist/substrate/runtime/observability/output-truncate.mjs +35 -0
- package/dist/substrate/runtime/observability/output-truncate.mjs.map +1 -0
- package/dist/substrate/runtime/observability/pretty-error.mjs +9 -12
- package/dist/substrate/runtime/observability/pretty-error.mjs.map +1 -1
- package/dist/substrate/runtime/observability/redaction.d.mts +2 -13
- package/dist/substrate/runtime/observability/redaction.mjs +8 -14
- package/dist/substrate/runtime/observability/redaction.mjs.map +1 -1
- package/dist/substrate/runtime/observability/span-store.d.mts +1 -0
- package/dist/substrate/runtime/observability/span-store.mjs +110 -0
- package/dist/substrate/runtime/observability/span-store.mjs.map +1 -0
- package/dist/substrate/runtime/observability/spans.mjs +14 -14
- package/dist/substrate/runtime/observability/spans.mjs.map +1 -1
- package/dist/substrate/runtime/observability/subprocess-capture.mjs +1 -1
- package/dist/substrate/runtime/passthrough-or-wrap.mjs +33 -0
- package/dist/substrate/runtime/passthrough-or-wrap.mjs.map +1 -0
- package/dist/substrate/runtime/paths.d.mts +1 -1
- package/dist/substrate/runtime/paths.mjs +4 -3
- package/dist/substrate/runtime/paths.mjs.map +1 -1
- package/dist/substrate/runtime/phase-preserving-produce.mjs +40 -0
- package/dist/substrate/runtime/phase-preserving-produce.mjs.map +1 -0
- package/dist/substrate/runtime/port-broker/index.mjs +3 -0
- package/dist/substrate/runtime/port-broker/service.mjs +170 -126
- package/dist/substrate/runtime/port-broker/service.mjs.map +1 -1
- package/dist/substrate/runtime/post-acquire-tasks.mjs +1 -1
- package/dist/substrate/runtime/post-acquire-tasks.mjs.map +1 -1
- package/dist/substrate/runtime/probes.d.mts +7 -5
- package/dist/substrate/runtime/probes.mjs +3 -1
- package/dist/substrate/runtime/probes.mjs.map +1 -1
- package/dist/substrate/runtime/projection/index.mjs +4 -0
- package/dist/substrate/runtime/projection/operational-endpoints.mjs +1 -2
- package/dist/substrate/runtime/projection/operational-endpoints.mjs.map +1 -1
- package/dist/substrate/runtime/projection/persisted.mjs +33 -15
- package/dist/substrate/runtime/projection/persisted.mjs.map +1 -1
- package/dist/substrate/runtime/projection/state-ref.mjs +26 -2
- package/dist/substrate/runtime/projection/state-ref.mjs.map +1 -1
- package/dist/substrate/runtime/projection/update.mjs +88 -24
- package/dist/substrate/runtime/projection/update.mjs.map +1 -1
- package/dist/substrate/runtime/random-suffix.mjs +11 -0
- package/dist/substrate/runtime/random-suffix.mjs.map +1 -0
- package/dist/substrate/runtime/retry-policy.d.mts +59 -1
- package/dist/substrate/runtime/retry-policy.mjs +66 -1
- package/dist/substrate/runtime/retry-policy.mjs.map +1 -1
- package/dist/substrate/runtime/routed-url.d.mts +1 -0
- package/dist/substrate/runtime/routed-url.mjs +79 -0
- package/dist/substrate/runtime/routed-url.mjs.map +1 -0
- package/dist/substrate/runtime/runtime-decode.d.mts +1 -1
- package/dist/substrate/runtime/runtime-decode.mjs.map +1 -1
- package/dist/substrate/runtime/scoped-http-server.mjs +2 -3
- package/dist/substrate/runtime/scoped-http-server.mjs.map +1 -1
- package/dist/substrate/runtime/scoped-multimap/index.mjs +2 -0
- package/dist/substrate/runtime/scoped-multimap/service.mjs +52 -0
- package/dist/substrate/runtime/scoped-multimap/service.mjs.map +1 -0
- package/dist/substrate/runtime/scoped-ref-map/index.mjs +2 -0
- package/dist/substrate/runtime/scoped-ref-map/service.mjs +2 -2
- package/dist/substrate/runtime/scoped-ref-map/service.mjs.map +1 -1
- package/dist/substrate/runtime/stage-and-swap/index.mjs +72 -4
- package/dist/substrate/runtime/stage-and-swap/index.mjs.map +1 -1
- package/dist/substrate/runtime/state-store/index.d.mts +1 -0
- package/dist/substrate/runtime/state-store/index.mjs +3 -0
- package/dist/substrate/runtime/state-store/schema.d.mts +1 -0
- package/dist/substrate/runtime/state-store/schema.mjs +19 -14
- package/dist/substrate/runtime/state-store/schema.mjs.map +1 -1
- package/dist/substrate/runtime/state-store/service.d.mts +1 -0
- package/dist/substrate/runtime/state-store/service.mjs +145 -0
- package/dist/substrate/runtime/state-store/service.mjs.map +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.d.mts +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs +33 -0
- package/dist/substrate/runtime/strategy-registry/chain-keyed-strategy-for.mjs.map +1 -0
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs +1 -1
- package/dist/substrate/runtime/strategy-registry/chain-probe-for.mjs.map +1 -1
- package/dist/substrate/runtime/strategy-registry/index.mjs +4 -0
- package/dist/substrate/runtime/strategy-registry/service.mjs +19 -33
- package/dist/substrate/runtime/strategy-registry/service.mjs.map +1 -1
- package/dist/substrate/runtime/subnet-broker.mjs +60 -0
- package/dist/substrate/runtime/subnet-broker.mjs.map +1 -0
- package/dist/substrate/runtime/sui-execute/index.d.mts +21 -1
- package/dist/substrate/runtime/sui-execute/index.mjs +84 -20
- package/dist/substrate/runtime/sui-execute/index.mjs.map +1 -1
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.d.mts +1 -0
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs +32 -0
- package/dist/substrate/runtime/sui-execute/sign-and-dispatch.mjs.map +1 -0
- package/dist/substrate/runtime/sui-ledger/object-ref.mjs +32 -0
- package/dist/substrate/runtime/sui-ledger/object-ref.mjs.map +1 -0
- package/dist/substrate/runtime/sui-move-build/index.mjs +93 -19
- package/dist/substrate/runtime/sui-move-build/index.mjs.map +1 -1
- package/dist/substrate/runtime/supervisor/acquire-node.mjs +181 -0
- package/dist/substrate/runtime/supervisor/acquire-node.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/background-tasks.mjs +144 -0
- package/dist/substrate/runtime/supervisor/background-tasks.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/command-loop.mjs +68 -0
- package/dist/substrate/runtime/supervisor/command-loop.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs +151 -0
- package/dist/substrate/runtime/supervisor/dispatch-contributions.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/errors.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/errors.mjs +9 -0
- package/dist/substrate/runtime/supervisor/errors.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/index.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/index.mjs +3 -0
- package/dist/substrate/runtime/supervisor/shutdown.mjs +51 -0
- package/dist/substrate/runtime/supervisor/shutdown.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/start-supervisor.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs +250 -0
- package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/state.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/state.mjs +22 -0
- package/dist/substrate/runtime/supervisor/state.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/teardown.mjs +57 -0
- package/dist/substrate/runtime/supervisor/teardown.mjs.map +1 -0
- package/dist/substrate/runtime/supervisor/types.d.mts +1 -0
- package/dist/substrate/runtime/supervisor/wiring.mjs +133 -0
- package/dist/substrate/runtime/supervisor/wiring.mjs.map +1 -0
- package/dist/substrate/runtime/typed-env.mjs +8 -0
- package/dist/substrate/runtime/typed-env.mjs.map +1 -0
- package/dist/substrate/state-store.d.mts +1 -0
- package/dist/substrate/versioned-doc-schema.mjs +17 -0
- package/dist/substrate/versioned-doc-schema.mjs.map +1 -0
- package/dist/substrate/versioned-doc-sync.mjs +94 -0
- package/dist/substrate/versioned-doc-sync.mjs.map +1 -0
- package/dist/surfaces/cli/command-tree.mjs.map +1 -1
- package/dist/surfaces/cli/commands/index.mjs +9 -0
- package/dist/surfaces/cli/commands/prune-picker-entry.mjs +13 -0
- package/dist/surfaces/cli/commands/prune-picker-entry.mjs.map +1 -0
- package/dist/surfaces/cli/commands/prune-picker.mjs +3 -2
- package/dist/surfaces/cli/commands/prune-picker.mjs.map +1 -1
- package/dist/surfaces/cli/commands/prune.mjs +34 -17
- package/dist/surfaces/cli/commands/prune.mjs.map +1 -1
- package/dist/surfaces/cli/commands/status.mjs +2 -2
- package/dist/surfaces/cli/commands/status.mjs.map +1 -1
- package/dist/surfaces/cli/commands/supervisor-presence.mjs +5 -3
- package/dist/surfaces/cli/commands/supervisor-presence.mjs.map +1 -1
- package/dist/surfaces/cli/commands/wipe.mjs +38 -4
- package/dist/surfaces/cli/commands/wipe.mjs.map +1 -1
- package/dist/surfaces/cli/errors.mjs +1 -1
- package/dist/surfaces/cli/errors.mjs.map +1 -1
- package/dist/surfaces/cli/flags.mjs +0 -1
- package/dist/surfaces/cli/flags.mjs.map +1 -1
- package/dist/surfaces/cli/index.mjs +98 -18
- package/dist/surfaces/cli/index.mjs.map +1 -1
- package/dist/surfaces/cli/output.mjs +1 -0
- package/dist/surfaces/cli/output.mjs.map +1 -1
- package/dist/surfaces/cli/sysexits.mjs +2 -1
- package/dist/surfaces/cli/sysexits.mjs.map +1 -1
- package/dist/surfaces/tui/app.mjs +25 -3
- package/dist/surfaces/tui/app.mjs.map +1 -1
- package/dist/surfaces/tui/display-derivation.mjs +23 -74
- package/dist/surfaces/tui/display-derivation.mjs.map +1 -1
- package/dist/surfaces/tui/event-log.mjs +35 -23
- package/dist/surfaces/tui/event-log.mjs.map +1 -1
- package/dist/surfaces/tui/input.mjs +5 -1
- package/dist/surfaces/tui/input.mjs.map +1 -1
- package/dist/surfaces/tui/plain-renderer.mjs +70 -34
- package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
- package/package.json +24 -4
- package/dist/contracts/liveness-classifier.d.mts +0 -19
- package/dist/contracts/network-resolver.d.mts +0 -15
- package/dist/contracts/network-resolver.mjs +0 -7
- package/dist/contracts/network-resolver.mjs.map +0 -1
- package/dist/orchestrators/codegen/extras.mjs +0 -16
- package/dist/orchestrators/codegen/extras.mjs.map +0 -1
- package/dist/plugins/deepbook/routable.mjs +0 -21
- package/dist/plugins/deepbook/routable.mjs.map +0 -1
- package/dist/plugins/faucet/service.d.mts +0 -20
- package/dist/plugins/faucet/strategies/sui-local.d.mts +0 -23
- package/dist/plugins/faucet/strategies/sui-local.mjs.map +0 -1
- package/dist/plugins/sui/seed-objects.d.mts +0 -18
- package/dist/plugins/sui/seed-objects.mjs +0 -25
- package/dist/plugins/sui/seed-objects.mjs.map +0 -1
- package/dist/runtime/built-in-plugin-layers.mjs.map +0 -1
- package/dist/substrate/runtime/context-helpers.mjs +0 -32
- package/dist/substrate/runtime/context-helpers.mjs.map +0 -1
- package/dist/substrate/runtime/run.mjs.map +0 -1
- package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs +0 -30
- package/dist/substrate/runtime/strategy-registry/faucet-capability-for.mjs.map +0 -1
- package/dist/substrate/runtime/supervisor.d.mts +0 -1
- package/dist/substrate/runtime/supervisor.mjs +0 -831
- package/dist/substrate/runtime/supervisor.mjs.map +0 -1
- package/dist/surfaces/cli/commands/doctor-probes.mjs.map +0 -1
- /package/dist/{substrate/runtime/strategy-registry/faucet-capability-for.d.mts → plugins/faucet/http.d.mts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot-reservation.mjs","names":[],"sources":["../../../../src/substrate/runtime/cross-process/snapshot-reservation.ts"],"sourcesContent":["// `snapshot.reservation` — presence-or-absence semaphore.\n//\n// Architecture § Cross-process safety protocol § Concurrent snapshot:\n// \"Snapshot requires exclusive control of the stack's container set\n// (pause-around-commit means peer processes' ready-probes would fail\n// if uncoordinated). The snapshotting process acquires\n// `snapshot.reservation` via O_EXCL create. If creation fails (peer\n// holds it), the snapshot refuses with a structured 'snapshot in\n// progress by peer pid X' error.\"\n//\n// Discipline:\n// - Acquire is atomic O_EXCL create with a JSON body carrying\n// `{ creatorPid, creatorStartTime, createdAt }`.\n// - Release is `unlink`.\n// - Orphan sweep: on the next claim, if the body's creator pid is\n// dead (PID + start-time check), the reservation is unlinked. This\n// mirrors the roster's stale-PID reclaim.\n// - Acquire DOES NOT retry — snapshot is a one-shot intent; the\n// caller surfaces the structured error to the user immediately.\n\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\n\nimport { Data, Effect, Scope } from 'effect';\n\nimport { type SnapshotReservation, SnapshotReservationSchema } from '../../cross-process.ts';\nimport { decodeJsonTextSync } from '../runtime-decode.ts';\nimport { checkHolderLiveness } from './liveness.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\nexport class SnapshotReservationHeldError extends Data.TaggedError('SnapshotReservationHeldError')<{\n\treadonly path: string;\n\treadonly holder: SnapshotReservation | null;\n}> {}\n\nexport class SnapshotReservationIoError extends Data.TaggedError('SnapshotReservationIoError')<{\n\treadonly path: string;\n\treadonly cause: unknown;\n}> {}\n\nexport type SnapshotReservationError = SnapshotReservationHeldError | SnapshotReservationIoError;\n\n// -----------------------------------------------------------------------------\n// Codec\n// -----------------------------------------------------------------------------\n\nconst parseReservation = (raw: string): SnapshotReservation | null => {\n\ttry {\n\t\treturn decodeJsonTextSync(SnapshotReservationSchema, raw, {\n\t\t\tsource: 'snapshot.reservation',\n\t\t\tmkError: (issue) => issue,\n\t\t});\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Acquire / release\n// -----------------------------------------------------------------------------\n\nconst ownReservation = (startTime: number): SnapshotReservation => ({\n\tcreatorPid: process.pid,\n\tcreatorStartTime: startTime,\n\tcreatedAt: Date.now(),\n});\n\n/**\n * Sweep an orphaned reservation. If the file exists AND its creator is\n * dead, unlink it. Returns whether a sweep happened so callers can\n * observe / report.\n *\n * Architecture § Concurrent snapshot last paragraph:\n * \"If the snapshotting process crashes mid-snapshot, the reservation\n * file persists. The next process's claim-protocol sweep detects an\n * orphan reservation (the holder whose pid matches the\n * reservation's creator entry is dead) and unlinks it.\"\n */\nexport const sweepOrphan = (\n\tpath: string,\n): Effect.Effect<{ readonly swept: boolean }, SnapshotReservationError> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.annotateCurrentSpan({ 'devstack.snapshot-reservation.path': path });\n\t\tif (!existsSync(path)) return { swept: false };\n\t\tconst raw = yield* Effect.try({\n\t\t\ttry: () => readFileSync(path, 'utf8'),\n\t\t\tcatch: (cause) => new SnapshotReservationIoError({ path, cause }),\n\t\t});\n\t\tconst reservation = parseReservation(raw);\n\t\tif (reservation === null) {\n\t\t\t// Malformed body — the creator wrote a half-written file then\n\t\t\t// died. Treat as orphan: unlink it so future acquires don't\n\t\t\t// fail forever.\n\t\t\tyield* Effect.try({\n\t\t\t\ttry: () => unlinkSync(path),\n\t\t\t\tcatch: (cause) => new SnapshotReservationIoError({ path, cause }),\n\t\t\t});\n\t\t\treturn { swept: true };\n\t\t}\n\t\t// PID-liveness check: synthesize a roster-shaped holder so the\n\t\t// shared liveness predicate applies.\n\t\tconst liveness = yield* checkHolderLiveness(\n\t\t\t{\n\t\t\t\tpid: reservation.creatorPid,\n\t\t\t\tstartTime: reservation.creatorStartTime,\n\t\t\t\thostname: '',\n\t\t\t\tclaimedAt: reservation.createdAt,\n\t\t\t\theartbeatAt: reservation.createdAt,\n\t\t\t\tintent: 'snapshot',\n\t\t\t},\n\t\t\t'',\n\t\t).pipe(Effect.catch(() => Effect.succeed('alive' as const)));\n\t\tif (liveness === 'alive') return { swept: false };\n\t\tyield* Effect.try({\n\t\t\ttry: () => unlinkSync(path),\n\t\t\tcatch: (cause) => new SnapshotReservationIoError({ path, cause }),\n\t\t});\n\t\treturn { swept: true };\n\t}).pipe(Effect.withSpan('cross-process.snapshot-reservation.sweepOrphan'));\n\n/**\n * Acquire the snapshot reservation as a scoped resource. The\n * reservation is unlinked when the surrounding Scope closes.\n *\n * Architecture § Concurrent snapshot step 1: O_EXCL create, no retry.\n * Callers that fail with `SnapshotReservationHeldError` propagate the\n * structured \"snapshot in progress\" error to the user — the typical\n * `Effect.catchTag` shape.\n *\n * `startTime` is the same value the roster carries for THIS process so\n * the orphan sweep's pid-startTime check resolves identically.\n */\nexport const acquireReservation = (\n\tpath: string,\n\tstartTime: number,\n): Effect.Effect<SnapshotReservation, SnapshotReservationError, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.annotateCurrentSpan({ 'devstack.snapshot-reservation.path': path });\n\t\t// One-shot orphan sweep before our O_EXCL attempt. The sweep is\n\t\t// idempotent: if a peer just wrote the reservation while we ran\n\t\t// the sweep, the O_EXCL create fails below and we surface the\n\t\t// structured \"held\" error.\n\t\tyield* sweepOrphan(path);\n\t\tconst body = ownReservation(startTime);\n\t\tyield* Effect.try({\n\t\t\ttry: () => writeFileSync(path, JSON.stringify(body), { flag: 'wx' }),\n\t\t\tcatch: (cause) => {\n\t\t\t\tconst code = (cause as NodeJS.ErrnoException).code;\n\t\t\t\tif (code === 'EEXIST') {\n\t\t\t\t\tlet holder: SnapshotReservation | null = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tholder = parseReservation(readFileSync(path, 'utf8'));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Body unreadable; leave holder as null.\n\t\t\t\t\t}\n\t\t\t\t\treturn new SnapshotReservationHeldError({ path, holder });\n\t\t\t\t}\n\t\t\t\treturn new SnapshotReservationIoError({ path, cause });\n\t\t\t},\n\t\t});\n\t\t// Finalizer: best-effort unlink on scope close. Architecture §\n\t\t// Concurrent snapshot step 5: \"It unlinks `snapshot.reservation`.\"\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.sync(() => {\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(path);\n\t\t\t\t} catch {\n\t\t\t\t\t// Already gone — ok. A peer's sweep may have unlinked our\n\t\t\t\t\t// stale reservation if we somehow lost the process between\n\t\t\t\t\t// acquire and the finalizer fire.\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t\treturn body;\n\t}).pipe(Effect.withSpan('cross-process.snapshot-reservation.acquire'));\n\n/** Inspect (without acquiring) the current reservation. Returns the\n * parsed body or `null` if absent / malformed. Used by the supervisor\n * to decide whether to apply the \"snapshot-in-progress\" tolerance\n * window to ready probes. Architecture § Concurrent snapshot step 3. */\nexport const peekReservation = (\n\tpath: string,\n): Effect.Effect<SnapshotReservation | null, SnapshotReservationError> =>\n\tEffect.gen(function* () {\n\t\tif (!existsSync(path)) return null;\n\t\tconst raw = yield* Effect.try({\n\t\t\ttry: () => readFileSync(path, 'utf8'),\n\t\t\tcatch: (cause) => new SnapshotReservationIoError({ path, cause }),\n\t\t});\n\t\treturn parseReservation(raw);\n\t}).pipe(Effect.withSpan('cross-process.snapshot-reservation.peek'));\n"],"mappings":";;;;;;AAgCA,IAAa,+BAAb,cAAkD,KAAK,YAAY,+BAA+B,CAG/F;AAEH,IAAa,6BAAb,cAAgD,KAAK,YAAY,6BAA6B,CAG3F;AAQH,MAAM,oBAAoB,QAA4C;AACrE,KAAI;AACH,SAAO,mBAAmB,2BAA2B,KAAK;GACzD,QAAQ;GACR,UAAU,UAAU;GACpB,CAAC;SACK;AACP,SAAO;;;AAQT,MAAM,kBAAkB,eAA4C;CACnE,YAAY,QAAQ;CACpB,kBAAkB;CAClB,WAAW,KAAK,KAAK;CACrB;;;;;;;;;;;;AAaD,MAAa,eACZ,SAEA,OAAO,IAAI,aAAa;AACvB,QAAO,OAAO,oBAAoB,EAAE,sCAAsC,MAAM,CAAC;AACjF,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO,EAAE,OAAO,OAAO;CAK9C,MAAM,cAAc,iBAAiB,OAJlB,OAAO,IAAI;EAC7B,WAAW,aAAa,MAAM,OAAO;EACrC,QAAQ,UAAU,IAAI,2BAA2B;GAAE;GAAM;GAAO,CAAC;EACjE,CAAC,CACuC;AACzC,KAAI,gBAAgB,MAAM;AAIzB,SAAO,OAAO,IAAI;GACjB,WAAW,WAAW,KAAK;GAC3B,QAAQ,UAAU,IAAI,2BAA2B;IAAE;IAAM;IAAO,CAAC;GACjE,CAAC;AACF,SAAO,EAAE,OAAO,MAAM;;AAevB,MAAI,OAXoB,oBACvB;EACC,KAAK,YAAY;EACjB,WAAW,YAAY;EACvB,UAAU;EACV,WAAW,YAAY;EACvB,aAAa,YAAY;EACzB,QAAQ;EACR,EACD,GACA,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,QAAiB,CAAC,CAAC,MAC3C,QAAS,QAAO,EAAE,OAAO,OAAO;AACjD,QAAO,OAAO,IAAI;EACjB,WAAW,WAAW,KAAK;EAC3B,QAAQ,UAAU,IAAI,2BAA2B;GAAE;GAAM;GAAO,CAAC;EACjE,CAAC;AACF,QAAO,EAAE,OAAO,MAAM;EACrB,CAAC,KAAK,OAAO,SAAS,iDAAiD,CAAC;;;;;;;;;;;;;AAc3E,MAAa,sBACZ,MACA,cAEA,OAAO,IAAI,aAAa;AACvB,QAAO,OAAO,oBAAoB,EAAE,sCAAsC,MAAM,CAAC;AAKjF,QAAO,YAAY,KAAK;CACxB,MAAM,OAAO,eAAe,UAAU;AACtC,QAAO,OAAO,IAAI;EACjB,WAAW,cAAc,MAAM,KAAK,UAAU,KAAK,EAAE,EAAE,MAAM,MAAM,CAAC;EACpE,QAAQ,UAAU;AAEjB,OADc,MAAgC,SACjC,UAAU;IACtB,IAAI,SAAqC;AACzC,QAAI;AACH,cAAS,iBAAiB,aAAa,MAAM,OAAO,CAAC;YAC9C;AAGR,WAAO,IAAI,6BAA6B;KAAE;KAAM;KAAQ,CAAC;;AAE1D,UAAO,IAAI,2BAA2B;IAAE;IAAM;IAAO,CAAC;;EAEvD,CAAC;AAGF,QAAO,OAAO,mBACb,OAAO,WAAW;AACjB,MAAI;AACH,cAAW,KAAK;UACT;GAKP,CACF;AACD,QAAO;EACN,CAAC,KAAK,OAAO,SAAS,6CAA6C,CAAC"}
|
|
1
|
+
{"version":3,"file":"snapshot-reservation.mjs","names":["nodeHostname"],"sources":["../../../../src/substrate/runtime/cross-process/snapshot-reservation.ts"],"sourcesContent":["// `snapshot.reservation` — presence-or-absence semaphore.\n//\n// Architecture § Cross-process safety protocol § Concurrent snapshot:\n// \"Snapshot requires exclusive control of the stack's container set\n// (pause-around-commit means peer processes' ready-probes would fail\n// if uncoordinated). The snapshotting process acquires\n// `snapshot.reservation` via O_EXCL create. If creation fails (peer\n// holds it), the snapshot refuses with a structured 'snapshot in\n// progress by peer pid X' error.\"\n//\n// Discipline:\n// - Acquire is atomic O_EXCL create with a JSON body carrying\n// `{ version: 1, creatorPid, creatorStartTime, createdAt, hostname }`.\n// - Release is `unlink`.\n// - Orphan sweep: on the next claim, the body's `hostname` is checked\n// first. Foreign-host reservations short-circuit as alive (NFS-safe\n// conservative default — `kill(pid, 0)` is meaningless on a remote\n// kernel). Same-host reservations fall through to the PID +\n// start-time check, which mirrors the roster's stale-PID reclaim.\n// - Acquire DOES NOT retry — snapshot is a one-shot intent; the\n// caller surfaces the structured error to the user immediately.\n\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { hostname as nodeHostname } from 'node:os';\n\nimport { Data, Effect, Scope } from 'effect';\n\nimport { type SnapshotReservation, SnapshotReservationSchema } from '../../cross-process.ts';\nimport { parseVersionedDocumentBodyOrNull } from '../../versioned-doc-sync.ts';\nimport { SpanAttr } from '../observability/spans.ts';\nimport { checkHolderLiveness } from './liveness.ts';\nimport { reclaimUnparseableStaleFile } from './reclaim-stale-file.ts';\nimport { selfPid } from './self-pid.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\nexport class SnapshotReservationHeldError extends Data.TaggedError('SnapshotReservationHeldError')<{\n\treadonly path: string;\n\treadonly holder: SnapshotReservation | null;\n}> {}\n\nexport class SnapshotReservationIoError extends Data.TaggedError('SnapshotReservationIoError')<{\n\treadonly path: string;\n\treadonly cause: unknown;\n}> {}\n\nexport type SnapshotReservationError = SnapshotReservationHeldError | SnapshotReservationIoError;\n\n// -----------------------------------------------------------------------------\n// Codec\n// -----------------------------------------------------------------------------\n\nconst parseReservation = (raw: string): SnapshotReservation | null =>\n\tparseVersionedDocumentBodyOrNull(raw, SnapshotReservationSchema, 'snapshot.reservation');\n\n// -----------------------------------------------------------------------------\n// Acquire / release\n// -----------------------------------------------------------------------------\n\nconst ownReservation = (startTime: number | null): SnapshotReservation => ({\n\tversion: 1,\n\tcreatorPid: selfPid(),\n\tcreatorStartTime: startTime,\n\tcreatedAt: Date.now(),\n\thostname: nodeHostname(),\n});\n\n/**\n * Sweep an orphaned reservation. If the file exists AND its creator is\n * dead, unlink it. Returns whether a sweep happened so callers can\n * observe / report.\n *\n * Architecture § Concurrent snapshot last paragraph:\n * \"If the snapshotting process crashes mid-snapshot, the reservation\n * file persists. The next process's claim-protocol sweep detects an\n * orphan reservation (the holder whose pid matches the\n * reservation's creator entry is dead) and unlinks it.\"\n */\nexport const sweepOrphan = (\n\tpath: string,\n): Effect.Effect<{ readonly swept: boolean }, SnapshotReservationError> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.annotateCurrentSpan({ [SpanAttr.snapshotReservationPath]: path });\n\t\tif (!existsSync(path)) return { swept: false };\n\t\tconst raw = yield* Effect.try({\n\t\t\ttry: () => readFileSync(path, 'utf8'),\n\t\t\tcatch: (cause) => new SnapshotReservationIoError({ path, cause }),\n\t\t});\n\t\tconst reservation = parseReservation(raw);\n\t\tif (reservation === null) {\n\t\t\t// Malformed body — the creator may have written a half-written\n\t\t\t// file. Reclaim ONLY through the shared re-stat guard: gating a\n\t\t\t// bare mtime read + unlink races a competitor who legitimately\n\t\t\t// reclaims the garbage and writes a fresh valid O_EXCL body in\n\t\t\t// the window — the unlink would clobber that LIVE reservation.\n\t\t\t// `reclaimUnparseableStaleFile` honors the SAME mtime staleness\n\t\t\t// window stack-lock uses (a body younger than the window is\n\t\t\t// presumed mid-write and left alone) AND re-confirms the file\n\t\t\t// is still the same stale, unparseable inode immediately before\n\t\t\t// unlinking.\n\t\t\tconst outcome = yield* Effect.try({\n\t\t\t\ttry: () => reclaimUnparseableStaleFile(path, parseReservation),\n\t\t\t\tcatch: (cause) => new SnapshotReservationIoError({ path, cause }),\n\t\t\t});\n\t\t\treturn { swept: outcome === 'reclaimed' };\n\t\t}\n\t\t// Foreign-host reservation: NFS-safe — we cannot probe a remote\n\t\t// kernel for pid liveness, so treat the peer as alive (matches\n\t\t// the roster's `trustForeignHosts` policy). Before adding the\n\t\t// `hostname` field we synthesized a holder with hostname='' and\n\t\t// passed ownHost='', which bypassed the foreign-host fast-path\n\t\t// in `checkHolderLiveness` and probed PID/startTime locally — a\n\t\t// live remote process could be declared dead and its reservation\n\t\t// unlinked. The fix is to short-circuit here.\n\t\tconst ownHost = nodeHostname();\n\t\tif (reservation.hostname !== ownHost) return { swept: false };\n\t\t// PID-liveness check: synthesize a roster-shaped holder so the\n\t\t// shared liveness predicate applies.\n\t\tconst liveness = yield* checkHolderLiveness(\n\t\t\t{\n\t\t\t\tpid: reservation.creatorPid,\n\t\t\t\tstartTime: reservation.creatorStartTime,\n\t\t\t\thostname: reservation.hostname,\n\t\t\t\tclaimedAt: reservation.createdAt,\n\t\t\t\theartbeatAt: reservation.createdAt,\n\t\t\t\tintent: 'snapshot',\n\t\t\t},\n\t\t\townHost,\n\t\t).pipe(Effect.catch(() => Effect.succeed('alive' as const)));\n\t\tif (liveness === 'alive') return { swept: false };\n\t\tyield* Effect.try({\n\t\t\ttry: () => unlinkSync(path),\n\t\t\tcatch: (cause) => new SnapshotReservationIoError({ path, cause }),\n\t\t});\n\t\treturn { swept: true };\n\t}).pipe(Effect.withSpan('cross-process.snapshot-reservation.sweepOrphan'));\n\n/**\n * Acquire the snapshot reservation as a scoped resource. The\n * reservation is unlinked when the surrounding Scope closes.\n *\n * Architecture § Concurrent snapshot step 1: O_EXCL create, no retry.\n * Callers that fail with `SnapshotReservationHeldError` propagate the\n * structured \"snapshot in progress\" error to the user — the typical\n * `Effect.catchTag` shape.\n *\n * `startTime` is the same value the roster carries for THIS process so\n * the orphan sweep's pid-startTime check resolves identically.\n */\nexport const acquireReservation = (\n\tpath: string,\n\tstartTime: number | null,\n): Effect.Effect<SnapshotReservation, SnapshotReservationError, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.annotateCurrentSpan({ [SpanAttr.snapshotReservationPath]: path });\n\t\t// One-shot orphan sweep before our O_EXCL attempt. The sweep is\n\t\t// idempotent: if a peer just wrote the reservation while we ran\n\t\t// the sweep, the O_EXCL create fails below and we surface the\n\t\t// structured \"held\" error.\n\t\tyield* sweepOrphan(path);\n\t\tconst body = ownReservation(startTime);\n\t\tyield* Effect.try({\n\t\t\ttry: () => writeFileSync(path, JSON.stringify(body), { flag: 'wx' }),\n\t\t\tcatch: (cause) => {\n\t\t\t\tconst code = (cause as NodeJS.ErrnoException).code;\n\t\t\t\tif (code === 'EEXIST') {\n\t\t\t\t\tlet holder: SnapshotReservation | null = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tholder = parseReservation(readFileSync(path, 'utf8'));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Body unreadable; leave holder as null.\n\t\t\t\t\t}\n\t\t\t\t\treturn new SnapshotReservationHeldError({ path, holder });\n\t\t\t\t}\n\t\t\t\treturn new SnapshotReservationIoError({ path, cause });\n\t\t\t},\n\t\t});\n\t\t// Finalizer: best-effort unlink on scope close. Architecture §\n\t\t// Concurrent snapshot step 5: \"It unlinks `snapshot.reservation`.\"\n\t\tyield* Effect.addFinalizer(() =>\n\t\t\tEffect.sync(() => {\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(path);\n\t\t\t\t} catch {\n\t\t\t\t\t// Already gone — ok. A peer's sweep may have unlinked our\n\t\t\t\t\t// stale reservation if we somehow lost the process between\n\t\t\t\t\t// acquire and the finalizer fire.\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t\treturn body;\n\t}).pipe(Effect.withSpan('cross-process.snapshot-reservation.acquire'));\n\n/** Inspect (without acquiring) the current reservation. Returns the\n * parsed body or `null` if absent / malformed. Used by the supervisor\n * to decide whether to apply the \"snapshot-in-progress\" tolerance\n * window to ready probes. Architecture § Concurrent snapshot step 3. */\nexport const peekReservation = (\n\tpath: string,\n): Effect.Effect<SnapshotReservation | null, SnapshotReservationError> =>\n\tEffect.gen(function* () {\n\t\tif (!existsSync(path)) return null;\n\t\tconst raw = yield* Effect.try({\n\t\t\ttry: () => readFileSync(path, 'utf8'),\n\t\t\tcatch: (cause) => new SnapshotReservationIoError({ path, cause }),\n\t\t});\n\t\treturn parseReservation(raw);\n\t}).pipe(Effect.withSpan('cross-process.snapshot-reservation.peek'));\n"],"mappings":";;;;;;;;;;AAsCA,IAAa,+BAAb,cAAkD,KAAK,YAAY,+BAA+B,CAG/F;AAEH,IAAa,6BAAb,cAAgD,KAAK,YAAY,6BAA6B,CAG3F;AAQH,MAAM,oBAAoB,QACzB,iCAAiC,KAAK,2BAA2B,uBAAuB;AAMzF,MAAM,kBAAkB,eAAmD;CAC1E,SAAS;CACT,YAAY,SAAS;CACrB,kBAAkB;CAClB,WAAW,KAAK,KAAK;CACrB,UAAUA,UAAc;CACxB;;;;;;;;;;;;AAaD,MAAa,eACZ,SAEA,OAAO,IAAI,aAAa;AACvB,QAAO,OAAO,oBAAoB,GAAG,SAAS,0BAA0B,MAAM,CAAC;AAC/E,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO,EAAE,OAAO,OAAO;CAK9C,MAAM,cAAc,iBAAiB,OAJlB,OAAO,IAAI;EAC7B,WAAW,aAAa,MAAM,OAAO;EACrC,QAAQ,UAAU,IAAI,2BAA2B;GAAE;GAAM;GAAO,CAAC;EACjE,CAAC,CACuC;AACzC,KAAI,gBAAgB,KAenB,QAAO,EAAE,QAAO,OAJO,OAAO,IAAI;EACjC,WAAW,4BAA4B,MAAM,iBAAiB;EAC9D,QAAQ,UAAU,IAAI,2BAA2B;GAAE;GAAM;GAAO,CAAC;EACjE,CAAC,MAC0B,aAAa;CAU1C,MAAM,UAAUA,UAAc;AAC9B,KAAI,YAAY,aAAa,QAAS,QAAO,EAAE,OAAO,OAAO;AAc7D,MAAI,OAXoB,oBACvB;EACC,KAAK,YAAY;EACjB,WAAW,YAAY;EACvB,UAAU,YAAY;EACtB,WAAW,YAAY;EACvB,aAAa,YAAY;EACzB,QAAQ;EACR,EACD,QACA,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,QAAiB,CAAC,CAAC,MAC3C,QAAS,QAAO,EAAE,OAAO,OAAO;AACjD,QAAO,OAAO,IAAI;EACjB,WAAW,WAAW,KAAK;EAC3B,QAAQ,UAAU,IAAI,2BAA2B;GAAE;GAAM;GAAO,CAAC;EACjE,CAAC;AACF,QAAO,EAAE,OAAO,MAAM;EACrB,CAAC,KAAK,OAAO,SAAS,iDAAiD,CAAC;;;;;;;;;;;;;AAc3E,MAAa,sBACZ,MACA,cAEA,OAAO,IAAI,aAAa;AACvB,QAAO,OAAO,oBAAoB,GAAG,SAAS,0BAA0B,MAAM,CAAC;AAK/E,QAAO,YAAY,KAAK;CACxB,MAAM,OAAO,eAAe,UAAU;AACtC,QAAO,OAAO,IAAI;EACjB,WAAW,cAAc,MAAM,KAAK,UAAU,KAAK,EAAE,EAAE,MAAM,MAAM,CAAC;EACpE,QAAQ,UAAU;AAEjB,OADc,MAAgC,SACjC,UAAU;IACtB,IAAI,SAAqC;AACzC,QAAI;AACH,cAAS,iBAAiB,aAAa,MAAM,OAAO,CAAC;YAC9C;AAGR,WAAO,IAAI,6BAA6B;KAAE;KAAM;KAAQ,CAAC;;AAE1D,UAAO,IAAI,2BAA2B;IAAE;IAAM;IAAO,CAAC;;EAEvD,CAAC;AAGF,QAAO,OAAO,mBACb,OAAO,WAAW;AACjB,MAAI;AACH,cAAW,KAAK;UACT;GAKP,CACF;AACD,QAAO;EACN,CAAC,KAAK,OAAO,SAAS,6CAA6C,CAAC"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { SpanAttr } from "../observability/spans.mjs";
|
|
2
|
+
import "../../cross-process.mjs";
|
|
3
|
+
import { parseVersionedDocumentBodyOrNull } from "../../versioned-doc-sync.mjs";
|
|
4
|
+
import { underLiveClock } from "./live-clock.mjs";
|
|
1
5
|
import { checkHolderLiveness, ownHolder } from "./liveness.mjs";
|
|
2
|
-
import {
|
|
6
|
+
import { reclaimUnparseableStaleFile } from "./reclaim-stale-file.mjs";
|
|
3
7
|
import { Data, Effect, Schema } from "effect";
|
|
4
8
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
9
|
import { dirname } from "node:path";
|
|
@@ -11,31 +15,31 @@ var StackLockTimeoutError = class extends Data.TaggedError("StackLockTimeoutErro
|
|
|
11
15
|
/** Tagged failure: I/O error other than EEXIST during acquire. */
|
|
12
16
|
var StackLockIoError = class extends Data.TaggedError("StackLockIoError") {};
|
|
13
17
|
/** The on-disk body is the same `RosterHolder` shape — re-using the schema
|
|
14
|
-
* means roster sweep + stack-lock reclaim see the same fields.
|
|
18
|
+
* means roster sweep + stack-lock reclaim see the same fields.
|
|
19
|
+
*
|
|
20
|
+
* `startTime` mirrors `RosterHolderSchema`: `number | null` so an
|
|
21
|
+
* unprobable platform's lock body round-trips cleanly through the
|
|
22
|
+
* decoder. The shared liveness predicate honors null conservatively. */
|
|
15
23
|
const StackLockBodySchema = Schema.Struct({
|
|
16
24
|
pid: Schema.Number,
|
|
17
|
-
startTime: Schema.Number,
|
|
25
|
+
startTime: Schema.NullOr(Schema.Number),
|
|
18
26
|
hostname: Schema.String,
|
|
19
27
|
claimedAt: Schema.Number,
|
|
20
28
|
heartbeatAt: Schema.Number,
|
|
21
29
|
intent: Schema.Literals(["normal", "snapshot"])
|
|
22
30
|
});
|
|
23
|
-
const parseLockBody = (raw) =>
|
|
24
|
-
try {
|
|
25
|
-
return decodeJsonTextSync(StackLockBodySchema, raw, {
|
|
26
|
-
source: "stack.lock",
|
|
27
|
-
mkError: (issue) => issue
|
|
28
|
-
});
|
|
29
|
-
} catch {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
31
|
+
const parseLockBody = (raw) => parseVersionedDocumentBodyOrNull(raw, StackLockBodySchema, "stack.lock");
|
|
33
32
|
/** Default acquire timeout — architecture § Claim protocol step 1
|
|
34
33
|
* ("Block up to 5 seconds; if unavailable, retry with backoff"). */
|
|
35
34
|
const DEFAULT_ACQUIRE_TIMEOUT_MILLIS = 5e3;
|
|
36
|
-
/** Per-attempt initial wait. Doubles each retry up to the cap.
|
|
35
|
+
/** Per-attempt initial wait. Doubles each retry up to the cap. The
|
|
36
|
+
* backoff also resets whenever the holder identity changes — see the
|
|
37
|
+
* reclaim loop comment for the contention story (review fix phase
|
|
38
|
+
* 22f). The cap is intentionally tight (200ms) so peers react quickly
|
|
39
|
+
* to a release; combined with the O_EXCL-arbitrated retry on reclaim,
|
|
40
|
+
* no staggering jitter is needed. */
|
|
37
41
|
const INITIAL_BACKOFF_MILLIS = 25;
|
|
38
|
-
const MAX_BACKOFF_MILLIS =
|
|
42
|
+
const MAX_BACKOFF_MILLIS = 200;
|
|
39
43
|
/**
|
|
40
44
|
* Sync attempt at O_EXCL-create. Returns whether we own the lock now.
|
|
41
45
|
*
|
|
@@ -86,12 +90,13 @@ const tryAcquireSync = (path) => {
|
|
|
86
90
|
*/
|
|
87
91
|
const acquireStackLock = (path, timeoutMillis = DEFAULT_ACQUIRE_TIMEOUT_MILLIS) => Effect.gen(function* () {
|
|
88
92
|
yield* Effect.annotateCurrentSpan({
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
[SpanAttr.stackLockPath]: path,
|
|
94
|
+
[SpanAttr.stackLockTimeoutMillis]: timeoutMillis
|
|
91
95
|
});
|
|
92
96
|
const startedAt = Date.now();
|
|
93
97
|
let backoff = INITIAL_BACKOFF_MILLIS;
|
|
94
98
|
let lastHolder = null;
|
|
99
|
+
let prevHolderPid = null;
|
|
95
100
|
while (true) {
|
|
96
101
|
const elapsed = Date.now() - startedAt;
|
|
97
102
|
if (elapsed > timeoutMillis) return yield* Effect.fail(new StackLockTimeoutError({
|
|
@@ -115,6 +120,12 @@ const acquireStackLock = (path, timeoutMillis = DEFAULT_ACQUIRE_TIMEOUT_MILLIS)
|
|
|
115
120
|
return;
|
|
116
121
|
}
|
|
117
122
|
lastHolder = attempt.holder;
|
|
123
|
+
const currentHolderPid = lastHolder?.pid ?? null;
|
|
124
|
+
if (currentHolderPid !== prevHolderPid) {
|
|
125
|
+
backoff = INITIAL_BACKOFF_MILLIS;
|
|
126
|
+
prevHolderPid = currentHolderPid;
|
|
127
|
+
}
|
|
128
|
+
let reclaimed = false;
|
|
118
129
|
if (lastHolder !== null) {
|
|
119
130
|
if ((yield* checkHolderLiveness(lastHolder).pipe(Effect.catch(() => Effect.succeed("alive")))) === "dead") {
|
|
120
131
|
yield* Effect.try({
|
|
@@ -129,14 +140,24 @@ const acquireStackLock = (path, timeoutMillis = DEFAULT_ACQUIRE_TIMEOUT_MILLIS)
|
|
|
129
140
|
cause
|
|
130
141
|
})
|
|
131
142
|
});
|
|
132
|
-
|
|
143
|
+
reclaimed = true;
|
|
133
144
|
}
|
|
145
|
+
} else reclaimed = (yield* Effect.try({
|
|
146
|
+
try: () => reclaimUnparseableStaleFile(path, parseLockBody),
|
|
147
|
+
catch: (cause) => new StackLockIoError({
|
|
148
|
+
path,
|
|
149
|
+
cause
|
|
150
|
+
})
|
|
151
|
+
})) === "reclaimed";
|
|
152
|
+
if (reclaimed) {
|
|
153
|
+
backoff = INITIAL_BACKOFF_MILLIS;
|
|
154
|
+
continue;
|
|
134
155
|
}
|
|
135
|
-
yield* Effect.sleep(`${backoff} millis`);
|
|
156
|
+
yield* underLiveClock(Effect.sleep(`${backoff} millis`));
|
|
136
157
|
backoff = Math.min(backoff * 2, MAX_BACKOFF_MILLIS);
|
|
137
158
|
}
|
|
138
159
|
}).pipe(Effect.withSpan("cross-process.stack-lock.acquire"));
|
|
139
160
|
//#endregion
|
|
140
|
-
export { acquireStackLock };
|
|
161
|
+
export { DEFAULT_ACQUIRE_TIMEOUT_MILLIS, StackLockIoError, StackLockTimeoutError, acquireStackLock };
|
|
141
162
|
|
|
142
163
|
//# sourceMappingURL=stack-lock.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stack-lock.mjs","names":[],"sources":["../../../../src/substrate/runtime/cross-process/stack-lock.ts"],"sourcesContent":["// `stack.lock` — O_EXCL exclusive lock for short critical sections.\n//\n// Architecture § Cross-process safety protocol § \"What is locked\":\n// - `stack.lock` — OS-advisory exclusive lock file. Used only for\n// short critical sections (roster mutations, snapshot reservation).\n// Acquired via `flock(LOCK_EX)` on Unix and `LockFileEx` on Windows;\n// never held across a long operation.\n//\n// The implementation here uses a portable O_EXCL-create dance — the\n// same approach the legacy `engine/file-lock.ts` proved out — because\n// `flock` isn't available cross-platform out of `node:fs`. The\n// architecture's wording (\"OS-advisory exclusive lock file\") covers\n// both `flock` and `O_EXCL`-with-pid-body; the latter is what every\n// portable POSIX tool reaches for when `flock` isn't on the table.\n//\n// Discipline:\n// - The lock is held BRIEFLY — within a single Effect.scoped block\n// that mutates the roster or the snapshot reservation.\n// - Stale locks (owner crashed under the lock) are reclaimed via the\n// PID + start-time liveness check before the acquire reattempts.\n// - Acquire retries with exponential backoff up to 5 seconds total\n// (architecture § Claim protocol step 1).\n\nimport { mkdirSync, unlinkSync, readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nimport { Data, Effect, Schema, Scope } from 'effect';\n\nimport { decodeJsonTextSync } from '../runtime-decode.ts';\nimport { checkHolderLiveness, ownHolder } from './liveness.ts';\nimport type { RosterHolder } from '../../cross-process.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** Tagged failure: acquire window elapsed and the lock is still held.\n * Carries the in-disk body (if parseable) so the caller can name the\n * blocking peer in user-facing error reporting. */\nexport class StackLockTimeoutError extends Data.TaggedError('StackLockTimeoutError')<{\n\treadonly path: string;\n\treadonly waitedMillis: number;\n\treadonly holder: RosterHolder | null;\n}> {}\n\n/** Tagged failure: I/O error other than EEXIST during acquire. */\nexport class StackLockIoError extends Data.TaggedError('StackLockIoError')<{\n\treadonly path: string;\n\treadonly cause: unknown;\n}> {}\n\nexport type StackLockError = StackLockTimeoutError | StackLockIoError;\n\n// -----------------------------------------------------------------------------\n// Codec\n// -----------------------------------------------------------------------------\n\n/** The on-disk body is the same `RosterHolder` shape — re-using the schema\n * means roster sweep + stack-lock reclaim see the same fields. */\nconst StackLockBodySchema = Schema.Struct({\n\tpid: Schema.Number,\n\tstartTime: Schema.Number,\n\thostname: Schema.String,\n\tclaimedAt: Schema.Number,\n\theartbeatAt: Schema.Number,\n\tintent: Schema.Literals(['normal', 'snapshot']),\n});\n\nconst parseLockBody = (raw: string): RosterHolder | null => {\n\ttry {\n\t\treturn decodeJsonTextSync(StackLockBodySchema, raw, {\n\t\t\tsource: 'stack.lock',\n\t\t\tmkError: (issue) => issue,\n\t\t});\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Acquire / release\n// -----------------------------------------------------------------------------\n\n/** Default acquire timeout — architecture § Claim protocol step 1\n * (\"Block up to 5 seconds; if unavailable, retry with backoff\"). */\nexport const DEFAULT_ACQUIRE_TIMEOUT_MILLIS = 5_000;\n\n/** Per-attempt initial wait. Doubles each retry up to the cap. */\nconst INITIAL_BACKOFF_MILLIS = 25;\nconst MAX_BACKOFF_MILLIS = 500;\n\n/**\n * Sync attempt at O_EXCL-create. Returns whether we own the lock now.\n *\n * Effect-platform's FileSystem doesn't expose a sync `open` shape, but\n * the critical-section discipline says we need a non-blocking attempt\n * inside a retry loop. Falling through to Node sync APIs here is the\n * cleanest path; the rest of the substrate stays Effect-native.\n */\nconst tryAcquireSync = (\n\tpath: string,\n): { readonly ok: true } | { readonly ok: false; readonly holder: RosterHolder | null } => {\n\tconst body = ownHolder();\n\t// The lock's parent directory may not exist on first-claim of a\n\t// fresh runtime root — devstack's `<runtimeRoot>/stacks/<stack>/` is\n\t// the substrate's responsibility to bring into being, but no other\n\t// L0 subsystem touches the disk until the first claim. `mkdir -p`\n\t// here is idempotent and harmless on the warm path; it transforms\n\t// the otherwise-fatal ENOENT-during-write into a clean acquire.\n\t// Architecture § Cross-process safety protocol: stack root is\n\t// created by whichever subsystem first reaches for a path under it.\n\tmkdirSync(dirname(path), { recursive: true });\n\ttry {\n\t\twriteFileSync(path, JSON.stringify(body), { flag: 'wx' });\n\t\treturn { ok: true };\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\tif (code !== 'EEXIST') {\n\t\t\tthrow err;\n\t\t}\n\t\t// Peer holds it (or stale). Probe the existing body.\n\t\tif (!existsSync(path)) {\n\t\t\t// Race: holder unlinked between our EEXIST and the existsSync.\n\t\t\t// Retry on the next loop iteration.\n\t\t\treturn { ok: false, holder: null };\n\t\t}\n\t\ttry {\n\t\t\tconst raw = readFileSync(path, 'utf8');\n\t\t\treturn { ok: false, holder: parseLockBody(raw) };\n\t\t} catch {\n\t\t\treturn { ok: false, holder: null };\n\t\t}\n\t}\n};\n\n/**\n * Acquire `stack.lock` as a scoped resource. The lock is released\n * when the surrounding Scope closes.\n *\n * Retry loop: exponential backoff up to `timeoutMillis` (default 5s).\n * On every miss, probe the holder's liveness — if it's dead, reclaim\n * by unlinking and looping. The reclaim path is the architecture's\n * \"stale lock\" handling — same PID + start-time semantics as the\n * roster sweep, so a process that crashed under the lock never blocks\n * the next peer indefinitely.\n *\n * IMPORTANT: this primitive yields a `void` resource. The caller MUST\n * keep its Scope tight — the architecture forbids holding the lock\n * across long operations.\n */\nexport const acquireStackLock = (\n\tpath: string,\n\ttimeoutMillis: number = DEFAULT_ACQUIRE_TIMEOUT_MILLIS,\n): Effect.Effect<void, StackLockError, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t'devstack.stack-lock.path': path,\n\t\t\t'devstack.stack-lock.timeoutMillis': timeoutMillis,\n\t\t});\n\t\tconst startedAt = Date.now();\n\t\tlet backoff = INITIAL_BACKOFF_MILLIS;\n\t\tlet lastHolder: RosterHolder | null = null;\n\t\twhile (true) {\n\t\t\tconst elapsed = Date.now() - startedAt;\n\t\t\tif (elapsed > timeoutMillis) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew StackLockTimeoutError({ path, waitedMillis: elapsed, holder: lastHolder }),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst attempt = yield* Effect.try({\n\t\t\t\ttry: () => tryAcquireSync(path),\n\t\t\t\tcatch: (cause) => new StackLockIoError({ path, cause }),\n\t\t\t});\n\t\t\tif (attempt.ok) {\n\t\t\t\t// Register a finalizer that unlinks the lock on scope close.\n\t\t\t\tyield* Effect.addFinalizer(() =>\n\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tunlinkSync(path);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Already gone — ok. Crash-during-release is handled\n\t\t\t\t\t\t\t// by the next peer's stale-PID reclaim.\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlastHolder = attempt.holder;\n\t\t\t// Reclaim if the holder is dead.\n\t\t\tif (lastHolder !== null) {\n\t\t\t\tconst status = yield* checkHolderLiveness(lastHolder).pipe(\n\t\t\t\t\tEffect.catch(() => Effect.succeed('alive' as const)),\n\t\t\t\t);\n\t\t\t\tif (status === 'dead') {\n\t\t\t\t\tyield* Effect.try({\n\t\t\t\t\t\ttry: () => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tunlinkSync(path);\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Race with another reclaimer — ok.\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcatch: (cause) => new StackLockIoError({ path, cause }),\n\t\t\t\t\t});\n\t\t\t\t\t// Loop again immediately; don't sleep for a reclaim.\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tyield* Effect.sleep(`${backoff} millis`);\n\t\t\tbackoff = Math.min(backoff * 2, MAX_BACKOFF_MILLIS);\n\t\t}\n\t}).pipe(Effect.withSpan('cross-process.stack-lock.acquire'));\n"],"mappings":";;;;;;;;;AAuCA,IAAa,wBAAb,cAA2C,KAAK,YAAY,wBAAwB,CAIjF;;AAGH,IAAa,mBAAb,cAAsC,KAAK,YAAY,mBAAmB,CAGvE;;;AAUH,MAAM,sBAAsB,OAAO,OAAO;CACzC,KAAK,OAAO;CACZ,WAAW,OAAO;CAClB,UAAU,OAAO;CACjB,WAAW,OAAO;CAClB,aAAa,OAAO;CACpB,QAAQ,OAAO,SAAS,CAAC,UAAU,WAAW,CAAC;CAC/C,CAAC;AAEF,MAAM,iBAAiB,QAAqC;AAC3D,KAAI;AACH,SAAO,mBAAmB,qBAAqB,KAAK;GACnD,QAAQ;GACR,UAAU,UAAU;GACpB,CAAC;SACK;AACP,SAAO;;;;;AAUT,MAAa,iCAAiC;;AAG9C,MAAM,yBAAyB;AAC/B,MAAM,qBAAqB;;;;;;;;;AAU3B,MAAM,kBACL,SAC0F;CAC1F,MAAM,OAAO,WAAW;AASxB,WAAU,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7C,KAAI;AACH,gBAAc,MAAM,KAAK,UAAU,KAAK,EAAE,EAAE,MAAM,MAAM,CAAC;AACzD,SAAO,EAAE,IAAI,MAAM;UACX,KAAK;AAEb,MADc,IAA8B,SAC/B,SACZ,OAAM;AAGP,MAAI,CAAC,WAAW,KAAK,CAGpB,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAM;AAEnC,MAAI;AAEH,UAAO;IAAE,IAAI;IAAO,QAAQ,cADhB,aAAa,MAAM,OACc,CAAC;IAAE;UACzC;AACP,UAAO;IAAE,IAAI;IAAO,QAAQ;IAAM;;;;;;;;;;;;;;;;;;;AAoBrC,MAAa,oBACZ,MACA,gBAAwB,mCAExB,OAAO,IAAI,aAAa;AACvB,QAAO,OAAO,oBAAoB;EACjC,4BAA4B;EAC5B,qCAAqC;EACrC,CAAC;CACF,MAAM,YAAY,KAAK,KAAK;CAC5B,IAAI,UAAU;CACd,IAAI,aAAkC;AACtC,QAAO,MAAM;EACZ,MAAM,UAAU,KAAK,KAAK,GAAG;AAC7B,MAAI,UAAU,cACb,QAAO,OAAO,OAAO,KACpB,IAAI,sBAAsB;GAAE;GAAM,cAAc;GAAS,QAAQ;GAAY,CAAC,CAC9E;EAEF,MAAM,UAAU,OAAO,OAAO,IAAI;GACjC,WAAW,eAAe,KAAK;GAC/B,QAAQ,UAAU,IAAI,iBAAiB;IAAE;IAAM;IAAO,CAAC;GACvD,CAAC;AACF,MAAI,QAAQ,IAAI;AAEf,UAAO,OAAO,mBACb,OAAO,WAAW;AACjB,QAAI;AACH,gBAAW,KAAK;YACT;KAIP,CACF;AACD;;AAED,eAAa,QAAQ;AAErB,MAAI,eAAe;QAId,OAHkB,oBAAoB,WAAW,CAAC,KACrD,OAAO,YAAY,OAAO,QAAQ,QAAiB,CAAC,CACpD,MACc,QAAQ;AACtB,WAAO,OAAO,IAAI;KACjB,WAAW;AACV,UAAI;AACH,kBAAW,KAAK;cACT;AAGR,aAAO;;KAER,QAAQ,UAAU,IAAI,iBAAiB;MAAE;MAAM;MAAO,CAAC;KACvD,CAAC;AAEF;;;AAGF,SAAO,OAAO,MAAM,GAAG,QAAQ,SAAS;AACxC,YAAU,KAAK,IAAI,UAAU,GAAG,mBAAmB;;EAEnD,CAAC,KAAK,OAAO,SAAS,mCAAmC,CAAC"}
|
|
1
|
+
{"version":3,"file":"stack-lock.mjs","names":[],"sources":["../../../../src/substrate/runtime/cross-process/stack-lock.ts"],"sourcesContent":["// `stack.lock` — O_EXCL exclusive lock for short critical sections.\n//\n// Architecture § Cross-process safety protocol § \"What is locked\":\n// - `stack.lock` — OS-advisory exclusive lock file. Used only for\n// short critical sections (roster mutations, snapshot reservation).\n// Acquired via `flock(LOCK_EX)` on Unix and `LockFileEx` on Windows;\n// never held across a long operation.\n//\n// The implementation here uses a portable O_EXCL-create dance — the\n// same approach the legacy `engine/file-lock.ts` proved out — because\n// `flock` isn't available cross-platform out of `node:fs`. The\n// architecture's wording (\"OS-advisory exclusive lock file\") covers\n// both `flock` and `O_EXCL`-with-pid-body; the latter is what every\n// portable POSIX tool reaches for when `flock` isn't on the table.\n//\n// Discipline:\n// - The lock is held BRIEFLY — within a single Effect.scoped block\n// that mutates the roster or the snapshot reservation.\n// - Stale locks (owner crashed under the lock) are reclaimed via the\n// PID + start-time liveness check before the acquire reattempts.\n// - Acquire retries with exponential backoff up to 5 seconds total\n// (architecture § Claim protocol step 1).\n\nimport { mkdirSync, unlinkSync, readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nimport { Data, Effect, Schema, Scope } from 'effect';\n\nimport { type RosterHolder } from '../../cross-process.ts';\nimport { parseVersionedDocumentBodyOrNull } from '../../versioned-doc-sync.ts';\nimport { SpanAttr } from '../observability/spans.ts';\nimport { underLiveClock } from './live-clock.ts';\nimport { checkHolderLiveness, ownHolder } from './liveness.ts';\nimport { reclaimUnparseableStaleFile } from './reclaim-stale-file.ts';\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\n/** Tagged failure: acquire window elapsed and the lock is still held.\n * Carries the in-disk body (if parseable) so the caller can name the\n * blocking peer in user-facing error reporting. */\nexport class StackLockTimeoutError extends Data.TaggedError('StackLockTimeoutError')<{\n\treadonly path: string;\n\treadonly waitedMillis: number;\n\treadonly holder: RosterHolder | null;\n}> {}\n\n/** Tagged failure: I/O error other than EEXIST during acquire. */\nexport class StackLockIoError extends Data.TaggedError('StackLockIoError')<{\n\treadonly path: string;\n\treadonly cause: unknown;\n}> {}\n\nexport type StackLockError = StackLockTimeoutError | StackLockIoError;\n\n// -----------------------------------------------------------------------------\n// Codec\n// -----------------------------------------------------------------------------\n\n/** The on-disk body is the same `RosterHolder` shape — re-using the schema\n * means roster sweep + stack-lock reclaim see the same fields.\n *\n * `startTime` mirrors `RosterHolderSchema`: `number | null` so an\n * unprobable platform's lock body round-trips cleanly through the\n * decoder. The shared liveness predicate honors null conservatively. */\nconst StackLockBodySchema = Schema.Struct({\n\tpid: Schema.Number,\n\tstartTime: Schema.NullOr(Schema.Number),\n\thostname: Schema.String,\n\tclaimedAt: Schema.Number,\n\theartbeatAt: Schema.Number,\n\tintent: Schema.Literals(['normal', 'snapshot']),\n});\n\nconst parseLockBody = (raw: string): RosterHolder | null =>\n\tparseVersionedDocumentBodyOrNull(raw, StackLockBodySchema, 'stack.lock');\n\n// -----------------------------------------------------------------------------\n// Acquire / release\n// -----------------------------------------------------------------------------\n\n/** Default acquire timeout — architecture § Claim protocol step 1\n * (\"Block up to 5 seconds; if unavailable, retry with backoff\"). */\nexport const DEFAULT_ACQUIRE_TIMEOUT_MILLIS = 5_000;\n\n/** Per-attempt initial wait. Doubles each retry up to the cap. The\n * backoff also resets whenever the holder identity changes — see the\n * reclaim loop comment for the contention story (review fix phase\n * 22f). The cap is intentionally tight (200ms) so peers react quickly\n * to a release; combined with the O_EXCL-arbitrated retry on reclaim,\n * no staggering jitter is needed. */\nconst INITIAL_BACKOFF_MILLIS = 25;\nconst MAX_BACKOFF_MILLIS = 200;\n\n/**\n * Sync attempt at O_EXCL-create. Returns whether we own the lock now.\n *\n * Effect-platform's FileSystem doesn't expose a sync `open` shape, but\n * the critical-section discipline says we need a non-blocking attempt\n * inside a retry loop. Falling through to Node sync APIs here is the\n * cleanest path; the rest of the substrate stays Effect-native.\n */\nconst tryAcquireSync = (\n\tpath: string,\n): { readonly ok: true } | { readonly ok: false; readonly holder: RosterHolder | null } => {\n\tconst body = ownHolder();\n\t// The lock's parent directory may not exist on first-claim of a\n\t// fresh runtime root — devstack's `<runtimeRoot>/stacks/<stack>/` is\n\t// the substrate's responsibility to bring into being, but no other\n\t// L0 subsystem touches the disk until the first claim. `mkdir -p`\n\t// here is idempotent and harmless on the warm path; it transforms\n\t// the otherwise-fatal ENOENT-during-write into a clean acquire.\n\t// Architecture § Cross-process safety protocol: stack root is\n\t// created by whichever subsystem first reaches for a path under it.\n\tmkdirSync(dirname(path), { recursive: true });\n\ttry {\n\t\twriteFileSync(path, JSON.stringify(body), { flag: 'wx' });\n\t\treturn { ok: true };\n\t} catch (err) {\n\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\tif (code !== 'EEXIST') {\n\t\t\tthrow err;\n\t\t}\n\t\t// Peer holds it (or stale). Probe the existing body.\n\t\tif (!existsSync(path)) {\n\t\t\t// Race: holder unlinked between our EEXIST and the existsSync.\n\t\t\t// Retry on the next loop iteration.\n\t\t\treturn { ok: false, holder: null };\n\t\t}\n\t\ttry {\n\t\t\tconst raw = readFileSync(path, 'utf8');\n\t\t\treturn { ok: false, holder: parseLockBody(raw) };\n\t\t} catch {\n\t\t\treturn { ok: false, holder: null };\n\t\t}\n\t}\n};\n\n/**\n * Acquire `stack.lock` as a scoped resource. The lock is released\n * when the surrounding Scope closes.\n *\n * Retry loop: exponential backoff up to `timeoutMillis` (default 5s).\n * On every miss, probe the holder's liveness — if it's dead, reclaim\n * by unlinking and looping. The reclaim path is the architecture's\n * \"stale lock\" handling — same PID + start-time semantics as the\n * roster sweep, so a process that crashed under the lock never blocks\n * the next peer indefinitely.\n *\n * IMPORTANT: this primitive yields a `void` resource. The caller MUST\n * keep its Scope tight — the architecture forbids holding the lock\n * across long operations.\n */\nexport const acquireStackLock = (\n\tpath: string,\n\ttimeoutMillis: number = DEFAULT_ACQUIRE_TIMEOUT_MILLIS,\n): Effect.Effect<void, StackLockError, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tyield* Effect.annotateCurrentSpan({\n\t\t\t[SpanAttr.stackLockPath]: path,\n\t\t\t[SpanAttr.stackLockTimeoutMillis]: timeoutMillis,\n\t\t});\n\t\tconst startedAt = Date.now();\n\t\tlet backoff = INITIAL_BACKOFF_MILLIS;\n\t\tlet lastHolder: RosterHolder | null = null;\n\t\tlet prevHolderPid: number | null = null;\n\t\twhile (true) {\n\t\t\tconst elapsed = Date.now() - startedAt;\n\t\t\tif (elapsed > timeoutMillis) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew StackLockTimeoutError({ path, waitedMillis: elapsed, holder: lastHolder }),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst attempt = yield* Effect.try({\n\t\t\t\ttry: () => tryAcquireSync(path),\n\t\t\t\tcatch: (cause) => new StackLockIoError({ path, cause }),\n\t\t\t});\n\t\t\tif (attempt.ok) {\n\t\t\t\t// Register a finalizer that unlinks the lock on scope close.\n\t\t\t\tyield* Effect.addFinalizer(() =>\n\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tunlinkSync(path);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Already gone — ok. Crash-during-release is handled\n\t\t\t\t\t\t\t// by the next peer's stale-PID reclaim.\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlastHolder = attempt.holder;\n\t\t\t// Reset the backoff whenever the holder identity changes —\n\t\t\t// a new holder means the previous one released, and our\n\t\t\t// long-saturated backoff (driven by waiting on the prior\n\t\t\t// holder) is now stale. Without this, late waiters under\n\t\t\t// heavy contention saturate at MAX_BACKOFF_MILLIS while\n\t\t\t// each peer holds the lock only briefly, exhausting the\n\t\t\t// claim budget before they can win (review fix phase 22f\n\t\t\t// reclaim-stress reproducer caught the case).\n\t\t\tconst currentHolderPid = lastHolder?.pid ?? null;\n\t\t\tif (currentHolderPid !== prevHolderPid) {\n\t\t\t\tbackoff = INITIAL_BACKOFF_MILLIS;\n\t\t\t\tprevHolderPid = currentHolderPid;\n\t\t\t}\n\t\t\t// Reclaim if the holder is dead OR the lock body is\n\t\t\t// unparseable AND old enough to be presumed-abandoned. The\n\t\t\t// unparseable case covers a peer that died mid-write: the\n\t\t\t// PID liveness check has nothing to consult, so the only\n\t\t\t// signal is the file's mtime falling outside the staleness\n\t\t\t// window (`DEFAULT_SWEEP_POLICY.staleAfterMillis`, shared\n\t\t\t// with the roster sweep). Without this branch, a mid-write\n\t\t\t// crash keeps every peer blocked for the full 5s claim\n\t\t\t// window — see review fix phase 22f (cross-process).\n\t\t\tlet reclaimed = false;\n\t\t\tif (lastHolder !== null) {\n\t\t\t\t// Dead-PID reclaim: gated on a liveness probe, so the\n\t\t\t\t// holder cannot have written a fresh body — a plain\n\t\t\t\t// unlink is safe here (no TOCTOU). An alive holder falls\n\t\t\t\t// through to the backoff below.\n\t\t\t\tconst status = yield* checkHolderLiveness(lastHolder).pipe(\n\t\t\t\t\tEffect.catch(() => Effect.succeed('alive' as const)),\n\t\t\t\t);\n\t\t\t\tif (status === 'dead') {\n\t\t\t\t\tyield* Effect.try({\n\t\t\t\t\t\ttry: () => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tunlinkSync(path);\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Race with another reclaimer — ok.\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcatch: (cause) => new StackLockIoError({ path, cause }),\n\t\t\t\t\t});\n\t\t\t\t\treclaimed = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Unparseable body: reclaim ONLY through the re-stat\n\t\t\t\t// guard. A bare mtime read + unlink races a competitor\n\t\t\t\t// who legitimately reclaims the garbage and writes a\n\t\t\t\t// fresh valid O_EXCL body in the window — the unlink\n\t\t\t\t// would clobber that LIVE lock (two simultaneous\n\t\t\t\t// holders). `reclaimUnparseableStaleFile` re-confirms the\n\t\t\t\t// file is still the same stale, unparseable inode\n\t\t\t\t// immediately before unlinking; any other outcome leaves\n\t\t\t\t// the file untouched and we fall through to back off.\n\t\t\t\tconst outcome = yield* Effect.try({\n\t\t\t\t\ttry: () => reclaimUnparseableStaleFile(path, parseLockBody),\n\t\t\t\t\tcatch: (cause) => new StackLockIoError({ path, cause }),\n\t\t\t\t});\n\t\t\t\treclaimed = outcome === 'reclaimed';\n\t\t\t}\n\t\t\tif (reclaimed) {\n\t\t\t\t// O_EXCL atomicity alone arbitrates the post-reclaim\n\t\t\t\t// race; reset the backoff so the contest starts fresh\n\t\t\t\t// (the prior growth was driven by a now-evicted dead\n\t\t\t\t// holder).\n\t\t\t\tbackoff = INITIAL_BACKOFF_MILLIS;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Peer holds an alive lock — back off exponentially.\n\t\t\tyield* underLiveClock(Effect.sleep(`${backoff} millis`));\n\t\t\tbackoff = Math.min(backoff * 2, MAX_BACKOFF_MILLIS);\n\t\t}\n\t}).pipe(Effect.withSpan('cross-process.stack-lock.acquire'));\n"],"mappings":";;;;;;;;;;;;;AA0CA,IAAa,wBAAb,cAA2C,KAAK,YAAY,wBAAwB,CAIjF;;AAGH,IAAa,mBAAb,cAAsC,KAAK,YAAY,mBAAmB,CAGvE;;;;;;;AAcH,MAAM,sBAAsB,OAAO,OAAO;CACzC,KAAK,OAAO;CACZ,WAAW,OAAO,OAAO,OAAO,OAAO;CACvC,UAAU,OAAO;CACjB,WAAW,OAAO;CAClB,aAAa,OAAO;CACpB,QAAQ,OAAO,SAAS,CAAC,UAAU,WAAW,CAAC;CAC/C,CAAC;AAEF,MAAM,iBAAiB,QACtB,iCAAiC,KAAK,qBAAqB,aAAa;;;AAQzE,MAAa,iCAAiC;;;;;;;AAQ9C,MAAM,yBAAyB;AAC/B,MAAM,qBAAqB;;;;;;;;;AAU3B,MAAM,kBACL,SAC0F;CAC1F,MAAM,OAAO,WAAW;AASxB,WAAU,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7C,KAAI;AACH,gBAAc,MAAM,KAAK,UAAU,KAAK,EAAE,EAAE,MAAM,MAAM,CAAC;AACzD,SAAO,EAAE,IAAI,MAAM;UACX,KAAK;AAEb,MADc,IAA8B,SAC/B,SACZ,OAAM;AAGP,MAAI,CAAC,WAAW,KAAK,CAGpB,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAM;AAEnC,MAAI;AAEH,UAAO;IAAE,IAAI;IAAO,QAAQ,cADhB,aAAa,MAAM,OACc,CAAC;IAAE;UACzC;AACP,UAAO;IAAE,IAAI;IAAO,QAAQ;IAAM;;;;;;;;;;;;;;;;;;;AAoBrC,MAAa,oBACZ,MACA,gBAAwB,mCAExB,OAAO,IAAI,aAAa;AACvB,QAAO,OAAO,oBAAoB;GAChC,SAAS,gBAAgB;GACzB,SAAS,yBAAyB;EACnC,CAAC;CACF,MAAM,YAAY,KAAK,KAAK;CAC5B,IAAI,UAAU;CACd,IAAI,aAAkC;CACtC,IAAI,gBAA+B;AACnC,QAAO,MAAM;EACZ,MAAM,UAAU,KAAK,KAAK,GAAG;AAC7B,MAAI,UAAU,cACb,QAAO,OAAO,OAAO,KACpB,IAAI,sBAAsB;GAAE;GAAM,cAAc;GAAS,QAAQ;GAAY,CAAC,CAC9E;EAEF,MAAM,UAAU,OAAO,OAAO,IAAI;GACjC,WAAW,eAAe,KAAK;GAC/B,QAAQ,UAAU,IAAI,iBAAiB;IAAE;IAAM;IAAO,CAAC;GACvD,CAAC;AACF,MAAI,QAAQ,IAAI;AAEf,UAAO,OAAO,mBACb,OAAO,WAAW;AACjB,QAAI;AACH,gBAAW,KAAK;YACT;KAIP,CACF;AACD;;AAED,eAAa,QAAQ;EASrB,MAAM,mBAAmB,YAAY,OAAO;AAC5C,MAAI,qBAAqB,eAAe;AACvC,aAAU;AACV,mBAAgB;;EAWjB,IAAI,YAAY;AAChB,MAAI,eAAe;QAQd,OAHkB,oBAAoB,WAAW,CAAC,KACrD,OAAO,YAAY,OAAO,QAAQ,QAAiB,CAAC,CACpD,MACc,QAAQ;AACtB,WAAO,OAAO,IAAI;KACjB,WAAW;AACV,UAAI;AACH,kBAAW,KAAK;cACT;AAGR,aAAO;;KAER,QAAQ,UAAU,IAAI,iBAAiB;MAAE;MAAM;MAAO,CAAC;KACvD,CAAC;AACF,gBAAY;;QAgBb,cAAY,OAJW,OAAO,IAAI;GACjC,WAAW,4BAA4B,MAAM,cAAc;GAC3D,QAAQ,UAAU,IAAI,iBAAiB;IAAE;IAAM;IAAO,CAAC;GACvD,CAAC,MACsB;AAEzB,MAAI,WAAW;AAKd,aAAU;AACV;;AAGD,SAAO,eAAe,OAAO,MAAM,GAAG,QAAQ,SAAS,CAAC;AACxD,YAAU,KAAK,IAAI,UAAU,GAAG,mBAAmB;;EAEnD,CAAC,KAAK,OAAO,SAAS,mCAAmC,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Context, Effect, Option } from "effect";
|
|
2
2
|
//#region src/substrate/runtime/current-plugin.ts
|
|
3
|
-
var CurrentPluginKey = class extends Context.Service()("@devstack
|
|
4
|
-
var CurrentPluginProgress = class extends Context.Service()("@devstack
|
|
3
|
+
var CurrentPluginKey = class extends Context.Service()("@devstack/substrate/CurrentPluginKey") {};
|
|
4
|
+
var CurrentPluginProgress = class extends Context.Service()("@devstack/substrate/CurrentPluginProgress") {};
|
|
5
5
|
/** Publish acquire-time row narration for the currently supervised
|
|
6
6
|
* plugin. Safe to call from plugin code: if the caller is running
|
|
7
7
|
* outside a supervisor, the helper is a no-op. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"current-plugin.mjs","names":[],"sources":["../../../src/substrate/runtime/current-plugin.ts"],"sourcesContent":["import { Context, Effect, Option } from 'effect';\n\nimport type { PluginKey } from '../brand.ts';\nimport type { PhaseNarration } from '../lifecycle.ts';\n\n/** Acquire-time identity for the plugin currently being supervised.\n *\n * Plugins that stream host-process output need the supervisor-minted\n * row key so logger lines publish as `log.appended` events for the\n * correct projection row. */\nexport interface CurrentPluginKeyShape {\n\treadonly key: PluginKey;\n}\n\nexport class CurrentPluginKey extends Context.Service<CurrentPluginKey, CurrentPluginKeyShape>()(\n\t'@devstack
|
|
1
|
+
{"version":3,"file":"current-plugin.mjs","names":[],"sources":["../../../src/substrate/runtime/current-plugin.ts"],"sourcesContent":["import { Context, Effect, Option } from 'effect';\n\nimport type { PluginKey } from '../brand.ts';\nimport type { PhaseNarration } from '../lifecycle.ts';\n\n/** Acquire-time identity for the plugin currently being supervised.\n *\n * Plugins that stream host-process output need the supervisor-minted\n * row key so logger lines publish as `log.appended` events for the\n * correct projection row. */\nexport interface CurrentPluginKeyShape {\n\treadonly key: PluginKey;\n}\n\nexport class CurrentPluginKey extends Context.Service<CurrentPluginKey, CurrentPluginKeyShape>()(\n\t'@devstack/substrate/CurrentPluginKey',\n) {}\n\nexport interface CurrentPluginProgressShape {\n\treadonly setPhase: (phase: PhaseNarration | null) => Effect.Effect<void>;\n}\n\nexport class CurrentPluginProgress extends Context.Service<\n\tCurrentPluginProgress,\n\tCurrentPluginProgressShape\n>()('@devstack/substrate/CurrentPluginProgress') {}\n\n/** Publish acquire-time row narration for the currently supervised\n * plugin. Safe to call from plugin code: if the caller is running\n * outside a supervisor, the helper is a no-op. */\nexport const setCurrentPluginPhase = (phase: PhaseNarration | null): Effect.Effect<void> =>\n\tEffect.gen(function* () {\n\t\tconst progress = yield* Effect.serviceOption(CurrentPluginProgress);\n\t\tif (Option.isSome(progress)) {\n\t\t\tyield* progress.value.setPhase(phase);\n\t\t}\n\t});\n"],"mappings":";;AAcA,IAAa,mBAAb,cAAsC,QAAQ,SAAkD,CAC/F,uCACA,CAAC;AAMF,IAAa,wBAAb,cAA2C,QAAQ,SAGhD,CAAC,4CAA4C,CAAC;;;;AAKjD,MAAa,yBAAyB,UACrC,OAAO,IAAI,aAAa;CACvB,MAAM,WAAW,OAAO,OAAO,cAAc,sBAAsB;AACnE,KAAI,OAAO,OAAO,SAAS,CAC1B,QAAO,SAAS,MAAM,SAAS,MAAM;EAErC"}
|
|
@@ -15,7 +15,10 @@ var AtomicWriteFailed = class extends Schema.TaggedErrorClass()("AtomicWriteFail
|
|
|
15
15
|
]),
|
|
16
16
|
cause: Schema.optional(Schema.Defect)
|
|
17
17
|
}) {};
|
|
18
|
-
|
|
18
|
+
/** State-store I/O / corruption surface. Distinct from
|
|
19
|
+
* `AtomicWriteFailed` so consumers can recover ("re-init on
|
|
20
|
+
* corruption") without catching write errors. */
|
|
21
|
+
var StateStoreError = class extends Schema.TaggedErrorClass()("StateStoreError", {
|
|
19
22
|
reason: Schema.Literals([
|
|
20
23
|
"io-failed",
|
|
21
24
|
"corruption",
|
|
@@ -23,7 +26,7 @@ Schema.TaggedErrorClass()("StateStoreError", {
|
|
|
23
26
|
]),
|
|
24
27
|
detail: Schema.String,
|
|
25
28
|
cause: Schema.optional(Schema.Defect)
|
|
26
|
-
});
|
|
29
|
+
}) {};
|
|
27
30
|
/** Cache surface — same tri-state as the L0 cache contract. */
|
|
28
31
|
var CacheError = class extends Schema.TaggedErrorClass()("CacheError", {
|
|
29
32
|
reason: Schema.Literals([
|
|
@@ -34,9 +37,12 @@ var CacheError = class extends Schema.TaggedErrorClass()("CacheError", {
|
|
|
34
37
|
detail: Schema.String,
|
|
35
38
|
cause: Schema.optional(Schema.Defect)
|
|
36
39
|
}) {};
|
|
37
|
-
/** Strategy registry missing-key surface
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
+
/** Strategy registry missing-key surface. Canonical tagged error
|
|
41
|
+
* yielded from `StrategyRegistry.get` when no contributor is
|
|
42
|
+
* registered under the requested capability key. The
|
|
43
|
+
* `contracts/strategy-contributor.ts` `StrategyRegistry` interface
|
|
44
|
+
* imports this class as its `E` channel — one `_tag` literal,
|
|
45
|
+
* one class, package-wide. */
|
|
40
46
|
var StrategyNotFoundError = class extends Schema.TaggedErrorClass()("StrategyNotFoundError", {
|
|
41
47
|
capabilityKey: Schema.String,
|
|
42
48
|
registeredKeys: Schema.Array(Schema.String)
|
|
@@ -71,6 +77,6 @@ var PortBrokerError = class extends Schema.TaggedErrorClass()("PortBrokerError",
|
|
|
71
77
|
cause: Schema.optional(Schema.Defect)
|
|
72
78
|
}) {};
|
|
73
79
|
//#endregion
|
|
74
|
-
export { AtomicWriteFailed, CacheError, PortBrokerError, StrategyNotFoundError };
|
|
80
|
+
export { AtomicWriteFailed, CacheError, PortBrokerError, StateStoreError, StrategyNotFoundError };
|
|
75
81
|
|
|
76
82
|
//# sourceMappingURL=errors.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/substrate/runtime/errors.ts"],"sourcesContent":["// Shared tagged errors for the runtime persistence layer.\n//\n// Per-subsystem tags so `Effect.catchTags({ ... })` reads at the call\n// site as \"what did the state-store / cache / registry just fail with\".\n\nimport { Schema } from 'effect';\n\n/** Atomic-write failure — one tag covers every stage. The `stage`\n * field discriminates inside renderer messages without\n * proliferating tag types. */\nexport class AtomicWriteFailed extends Schema.TaggedErrorClass<AtomicWriteFailed>()(\n\t'AtomicWriteFailed',\n\t{\n\t\tpath: Schema.String,\n\t\tstage: Schema.Literals(['mkdir-parent', 'open-temp', 'write', 'fsync', 'rename', 'encode']),\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** State-store I/O / corruption surface. Distinct from\n * `AtomicWriteFailed` so consumers can recover (\"re-init on\n * corruption\") without catching write errors. */\nexport class StateStoreError extends Schema.TaggedErrorClass<StateStoreError>()('StateStoreError', {\n\treason: Schema.Literals(['io-failed', 'corruption', 'lock-contention']),\n\tdetail: Schema.String,\n\tcause: Schema.optional(Schema.Defect),\n}) {}\n\n/** Cache surface — same tri-state as the L0 cache contract. */\nexport class CacheError extends Schema.TaggedErrorClass<CacheError>()('CacheError', {\n\treason: Schema.Literals(['io-failed', 'corruption', 'lock-contention']),\n\tdetail: Schema.String,\n\tcause: Schema.optional(Schema.Defect),\n}) {}\n\n/** Strategy registry missing-key surface
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/substrate/runtime/errors.ts"],"sourcesContent":["// Shared tagged errors for the runtime persistence layer.\n//\n// Per-subsystem tags so `Effect.catchTags({ ... })` reads at the call\n// site as \"what did the state-store / cache / registry just fail with\".\n\nimport { Schema } from 'effect';\n\n/** Atomic-write failure — one tag covers every stage. The `stage`\n * field discriminates inside renderer messages without\n * proliferating tag types. */\nexport class AtomicWriteFailed extends Schema.TaggedErrorClass<AtomicWriteFailed>()(\n\t'AtomicWriteFailed',\n\t{\n\t\tpath: Schema.String,\n\t\tstage: Schema.Literals(['mkdir-parent', 'open-temp', 'write', 'fsync', 'rename', 'encode']),\n\t\tcause: Schema.optional(Schema.Defect),\n\t},\n) {}\n\n/** State-store I/O / corruption surface. Distinct from\n * `AtomicWriteFailed` so consumers can recover (\"re-init on\n * corruption\") without catching write errors. */\nexport class StateStoreError extends Schema.TaggedErrorClass<StateStoreError>()('StateStoreError', {\n\treason: Schema.Literals(['io-failed', 'corruption', 'lock-contention']),\n\tdetail: Schema.String,\n\tcause: Schema.optional(Schema.Defect),\n}) {}\n\n/** Cache surface — same tri-state as the L0 cache contract. */\nexport class CacheError extends Schema.TaggedErrorClass<CacheError>()('CacheError', {\n\treason: Schema.Literals(['io-failed', 'corruption', 'lock-contention']),\n\tdetail: Schema.String,\n\tcause: Schema.optional(Schema.Defect),\n}) {}\n\n/** Strategy registry missing-key surface. Canonical tagged error\n * yielded from `StrategyRegistry.get` when no contributor is\n * registered under the requested capability key. The\n * `contracts/strategy-contributor.ts` `StrategyRegistry` interface\n * imports this class as its `E` channel — one `_tag` literal,\n * one class, package-wide. */\nexport class StrategyNotFoundError extends Schema.TaggedErrorClass<StrategyNotFoundError>()(\n\t'StrategyNotFoundError',\n\t{\n\t\tcapabilityKey: Schema.String,\n\t\tregisteredKeys: Schema.Array(Schema.String),\n\t},\n) {}\n\n/** Port-broker allocation failure — surfaced when no free port could be\n * found in the configured range, when a caller-supplied `preferredPort`\n * is already taken by another in-process allocation, or when the bind-\n * probe rejected every candidate.\n *\n * `reason` discriminates the failure mode for renderer mapping:\n * - `no-free-port` : scanned the whole window for `kind`, every\n * candidate failed the bind-probe (EADDRINUSE) or\n * was already held by a sibling allocation in\n * this process.\n * - `preferred-busy`: caller supplied a `preferredPort` and the\n * port is currently held by another in-process\n * allocation (refused per architecture §6).\n * - `bind-probe-failed`: the OS-level bind probe surfaced an error\n * OTHER than `EADDRINUSE` (e.g. EACCES on a\n * privileged port, EPERM under jail). Caller\n * should fall back to user opt-in / docs.\n * - `reservation-failed`: the runtime-root scoped port reservation\n * file could not be created/read/reclaimed. */\nexport class PortBrokerError extends Schema.TaggedErrorClass<PortBrokerError>()('PortBrokerError', {\n\treason: Schema.Literals([\n\t\t'no-free-port',\n\t\t'preferred-busy',\n\t\t'bind-probe-failed',\n\t\t'reservation-failed',\n\t]),\n\tdetail: Schema.String,\n\tcause: Schema.optional(Schema.Defect),\n}) {}\n"],"mappings":";;;;;AAUA,IAAa,oBAAb,cAAuC,OAAO,kBAAqC,CAClF,qBACA;CACC,MAAM,OAAO;CACb,OAAO,OAAO,SAAS;EAAC;EAAgB;EAAa;EAAS;EAAS;EAAU;EAAS,CAAC;CAC3F,OAAO,OAAO,SAAS,OAAO,OAAO;CACrC,CACD,CAAC;;;;AAKF,IAAa,kBAAb,cAAqC,OAAO,kBAAmC,CAAC,mBAAmB;CAClG,QAAQ,OAAO,SAAS;EAAC;EAAa;EAAc;EAAkB,CAAC;CACvE,QAAQ,OAAO;CACf,OAAO,OAAO,SAAS,OAAO,OAAO;CACrC,CAAC,CAAC;;AAGH,IAAa,aAAb,cAAgC,OAAO,kBAA8B,CAAC,cAAc;CACnF,QAAQ,OAAO,SAAS;EAAC;EAAa;EAAc;EAAkB,CAAC;CACvE,QAAQ,OAAO;CACf,OAAO,OAAO,SAAS,OAAO,OAAO;CACrC,CAAC,CAAC;;;;;;;AAQH,IAAa,wBAAb,cAA2C,OAAO,kBAAyC,CAC1F,yBACA;CACC,eAAe,OAAO;CACtB,gBAAgB,OAAO,MAAM,OAAO,OAAO;CAC3C,CACD,CAAC;;;;;;;;;;;;;;;;;;;;AAqBF,IAAa,kBAAb,cAAqC,OAAO,kBAAmC,CAAC,mBAAmB;CAClG,QAAQ,OAAO,SAAS;EACvB;EACA;EACA;EACA;EACA,CAAC;CACF,QAAQ,OAAO;CACf,OAAO,OAAO,SAAS,OAAO,OAAO;CACrC,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/substrate/runtime/format-unknown-error.ts
|
|
2
|
+
const MAX_CAUSE_DEPTH = 5;
|
|
3
|
+
const nestedCauseMessage = (cause, depth) => {
|
|
4
|
+
if (cause instanceof Error) return project(cause, depth);
|
|
5
|
+
if (typeof cause === "string") return cause.length > 0 ? cause : null;
|
|
6
|
+
if (typeof cause === "object" && cause !== null) {
|
|
7
|
+
const message = cause.message;
|
|
8
|
+
if (typeof message === "string" && message.length > 0) return project(cause, depth);
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
};
|
|
12
|
+
const project = (cause, depth) => {
|
|
13
|
+
if (typeof cause === "string") return cause;
|
|
14
|
+
const tagged = cause;
|
|
15
|
+
const message = cause instanceof Error ? cause.message : typeof cause === "object" && cause !== null && typeof tagged.message === "string" ? tagged.message : void 0;
|
|
16
|
+
if (message !== void 0 && message.length > 0) {
|
|
17
|
+
const nested = depth < MAX_CAUSE_DEPTH ? nestedCauseMessage(tagged.cause, depth + 1) : null;
|
|
18
|
+
return nested !== null && !message.includes(nested) ? `${message} [cause: ${nested}]` : message;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
return JSON.stringify(cause);
|
|
22
|
+
} catch {
|
|
23
|
+
return String(cause);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const formatUnknownError = (cause) => project(cause, 0);
|
|
27
|
+
//#endregion
|
|
28
|
+
export { formatUnknownError };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=format-unknown-error.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-unknown-error.mjs","names":[],"sources":["../../../src/substrate/runtime/format-unknown-error.ts"],"sourcesContent":["// Shared `unknown → string` projector for plugin error messages.\n//\n// Lifted from 5 identical local definitions across mode/local.ts,\n// mode/shared-boot.ts, mode/fork.ts, mode/external.ts, chain-probe.ts,\n// fork-transaction.ts. Callers concatenate this into plugin-tagged\n// error `message` fields; cascade-formatter walks the actual `cause`\n// separately for structured rendering.\n//\n// NB: this does NOT stringify Effect's `Cause` type — it projects an\n// arbitrary `unknown` (typically a caught throwable) to a short\n// string suitable for splicing into a human-readable error message.\n\n// Bounds the `.cause` walk so a self-referential or pathologically deep\n// chain can't produce a runaway string (or loop forever).\nconst MAX_CAUSE_DEPTH = 5;\n\n// A nested `.cause` only earns a place in the spliced message when it\n// carries a human-readable string of its own — an `Error`, a string, or\n// a tagged object with a `.message`. Raw detail bags (e.g. `{ sender,\n// objectCount }`) are skipped here so they don't dump JSON into the\n// headline message; the structured cascade-formatter still renders them.\nconst nestedCauseMessage = (cause: unknown, depth: number): string | null => {\n\tif (cause instanceof Error) return project(cause, depth);\n\tif (typeof cause === 'string') return cause.length > 0 ? cause : null;\n\tif (typeof cause === 'object' && cause !== null) {\n\t\tconst message = (cause as { readonly message?: unknown }).message;\n\t\tif (typeof message === 'string' && message.length > 0) {\n\t\t\treturn project(cause, depth);\n\t\t}\n\t}\n\treturn null;\n};\n\nconst project = (cause: unknown, depth: number): string => {\n\tif (typeof cause === 'string') return cause;\n\t// `Error` instances AND tagged devstack errors (`AccountSignError`,\n\t// `SuiPluginError`, … — plain objects, NOT `Error`s, so `String(obj)`\n\t// yields `[object Object]`) both carry a string `.message` and may carry\n\t// a `.cause`. Prefer the message, then chain the `.cause` so a generic\n\t// wrapper (\"… submit failed.\") still surfaces the actionable root\n\t// (\"… no SUI gas coins found for 0x…\"). `Error.cause` is read the same\n\t// way, so a thrown `new Error(msg, { cause })` chain is walked too.\n\tconst tagged = cause as { readonly message?: unknown; readonly cause?: unknown };\n\tconst message =\n\t\tcause instanceof Error\n\t\t\t? cause.message\n\t\t\t: typeof cause === 'object' && cause !== null && typeof tagged.message === 'string'\n\t\t\t\t? tagged.message\n\t\t\t\t: undefined;\n\tif (message !== undefined && message.length > 0) {\n\t\tconst nested = depth < MAX_CAUSE_DEPTH ? nestedCauseMessage(tagged.cause, depth + 1) : null;\n\t\treturn nested !== null && !message.includes(nested)\n\t\t\t? `${message} [cause: ${nested}]`\n\t\t\t: message;\n\t}\n\ttry {\n\t\treturn JSON.stringify(cause);\n\t} catch {\n\t\treturn String(cause);\n\t}\n};\n\nexport const formatUnknownError = (cause: unknown): string => project(cause, 0);\n"],"mappings":";AAcA,MAAM,kBAAkB;AAOxB,MAAM,sBAAsB,OAAgB,UAAiC;AAC5E,KAAI,iBAAiB,MAAO,QAAO,QAAQ,OAAO,MAAM;AACxD,KAAI,OAAO,UAAU,SAAU,QAAO,MAAM,SAAS,IAAI,QAAQ;AACjE,KAAI,OAAO,UAAU,YAAY,UAAU,MAAM;EAChD,MAAM,UAAW,MAAyC;AAC1D,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EACnD,QAAO,QAAQ,OAAO,MAAM;;AAG9B,QAAO;;AAGR,MAAM,WAAW,OAAgB,UAA0B;AAC1D,KAAI,OAAO,UAAU,SAAU,QAAO;CAQtC,MAAM,SAAS;CACf,MAAM,UACL,iBAAiB,QACd,MAAM,UACN,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAO,OAAO,YAAY,WACxE,OAAO,UACP,KAAA;AACL,KAAI,YAAY,KAAA,KAAa,QAAQ,SAAS,GAAG;EAChD,MAAM,SAAS,QAAQ,kBAAkB,mBAAmB,OAAO,OAAO,QAAQ,EAAE,GAAG;AACvF,SAAO,WAAW,QAAQ,CAAC,QAAQ,SAAS,OAAO,GAChD,GAAG,QAAQ,WAAW,OAAO,KAC7B;;AAEJ,KAAI;AACH,SAAO,KAAK,UAAU,MAAM;SACrB;AACP,SAAO,OAAO,MAAM;;;AAItB,MAAa,sBAAsB,UAA2B,QAAQ,OAAO,EAAE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/substrate/runtime/host-bind-mount-owner.ts
|
|
2
|
+
const hostBindMountOwner = () => {
|
|
3
|
+
const process = globalThis.process;
|
|
4
|
+
if (typeof process?.getuid !== "function" || typeof process.getgid !== "function") return;
|
|
5
|
+
return `${process.getuid()}:${process.getgid()}`;
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { hostBindMountOwner };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=host-bind-mount-owner.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-bind-mount-owner.mjs","names":[],"sources":["../../../src/substrate/runtime/host-bind-mount-owner.ts"],"sourcesContent":["// Host UID:GID accessor for bind-mount ownership.\n//\n// When the substrate runs a container with a host bind mount, the\n// container's writes land on the host filesystem as the container's\n// effective UID. On native Linux Docker that defaults to root, which\n// makes the resulting files un-deletable from the developer's shell\n// without `sudo`. Plugins thread `${UID}:${GID}` into the container's\n// `--user` flag (or via `DEVSTACK_HOST_UID_GID` for entrypoints that\n// re-exec) so the writes land owned by the invoking user.\n//\n// Returns `undefined` on platforms where `process.getuid/getgid` are\n// not available (Windows, browser). Callers omit the user/env stamp in\n// that case — Docker Desktop handles the ownership mapping itself.\n\nexport const hostBindMountOwner = (): string | undefined => {\n\tconst process = (\n\t\tglobalThis as {\n\t\t\tprocess?: { getuid?: () => number; getgid?: () => number };\n\t\t}\n\t).process;\n\tif (typeof process?.getuid !== 'function' || typeof process.getgid !== 'function') {\n\t\treturn undefined;\n\t}\n\treturn `${process.getuid()}:${process.getgid()}`;\n};\n"],"mappings":";AAcA,MAAa,2BAA+C;CAC3D,MAAM,UACL,WAGC;AACF,KAAI,OAAO,SAAS,WAAW,cAAc,OAAO,QAAQ,WAAW,WACtE;AAED,QAAO,GAAG,QAAQ,QAAQ,CAAC,GAAG,QAAQ,QAAQ"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/substrate/runtime/host-gateway.ts
|
|
2
|
+
/** Container `extraHosts` mapping that resolves
|
|
3
|
+
* `host.docker.internal` to the host gateway on native Linux
|
|
4
|
+
* Docker. Pass as `extraHosts:` on container specs that need to
|
|
5
|
+
* dial host-bound services. */
|
|
6
|
+
const HOST_GATEWAY_EXTRA_HOSTS = { "host.docker.internal": "host-gateway" };
|
|
7
|
+
/** Same fact as `HOST_GATEWAY_EXTRA_HOSTS`, encoded for the
|
|
8
|
+
* `docker run --add-host <host>:<gateway>` CLI flag (the value
|
|
9
|
+
* passed as the argv after `--add-host`). Derived from the record
|
|
10
|
+
* so a future change to the alias set lives in one place.
|
|
11
|
+
*
|
|
12
|
+
* If the record ever grows past one entry, the router's traefik
|
|
13
|
+
* bootstrap will need to emit one `--add-host <pair>` per entry;
|
|
14
|
+
* today it stamps exactly one pair, matching this record's single
|
|
15
|
+
* member. */
|
|
16
|
+
const HOST_GATEWAY_ADD_HOST_FLAGS = Object.entries(HOST_GATEWAY_EXTRA_HOSTS).map(([host, gateway]) => `${host}:${gateway}`);
|
|
17
|
+
//#endregion
|
|
18
|
+
export { HOST_GATEWAY_ADD_HOST_FLAGS, HOST_GATEWAY_EXTRA_HOSTS };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=host-gateway.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-gateway.mjs","names":[],"sources":["../../../src/substrate/runtime/host-gateway.ts"],"sourcesContent":["// Host-gateway `extraHosts` — ONE canonical constant.\n//\n// A container that needs to reach a service on the host's loopback\n// (e.g. Docker-published ports from a sibling container, or a host\n// process bound to `127.0.0.1`) does it by name via\n// `host.docker.internal`. Docker Desktop on macOS/Windows wires this\n// automatically; native Linux Docker does NOT and requires an\n// explicit `--add-host host.docker.internal:host-gateway` per\n// container (the literal string `host-gateway` is a Docker token\n// that resolves to the gateway IP of the container's network).\n//\n// This constant is the `extraHosts:` value passed to\n// `EnsureContainerSpec` / `runOneShot`. Use it instead of inlining\n// `{ 'host.docker.internal': 'host-gateway' }` at every dial-host\n// container site so a future change (e.g. additional aliases, or\n// platform-conditional mapping) lives in one place.\n\n/** Container `extraHosts` mapping that resolves\n * `host.docker.internal` to the host gateway on native Linux\n * Docker. Pass as `extraHosts:` on container specs that need to\n * dial host-bound services. */\nexport const HOST_GATEWAY_EXTRA_HOSTS: Readonly<Record<string, string>> = {\n\t'host.docker.internal': 'host-gateway',\n};\n\n/** Same fact as `HOST_GATEWAY_EXTRA_HOSTS`, encoded for the\n * `docker run --add-host <host>:<gateway>` CLI flag (the value\n * passed as the argv after `--add-host`). Derived from the record\n * so a future change to the alias set lives in one place.\n *\n * If the record ever grows past one entry, the router's traefik\n * bootstrap will need to emit one `--add-host <pair>` per entry;\n * today it stamps exactly one pair, matching this record's single\n * member. */\nexport const HOST_GATEWAY_ADD_HOST_FLAGS: ReadonlyArray<string> = Object.entries(\n\tHOST_GATEWAY_EXTRA_HOSTS,\n).map(([host, gateway]) => `${host}:${gateway}`);\n"],"mappings":";;;;;AAqBA,MAAa,2BAA6D,EACzE,wBAAwB,gBACxB;;;;;;;;;;AAWD,MAAa,8BAAqD,OAAO,QACxE,yBACA,CAAC,KAAK,CAAC,MAAM,aAAa,GAAG,KAAK,GAAG,UAAU"}
|
|
@@ -41,6 +41,42 @@ const tarExitError = (operation, status, stderrChunks) => new HostTreeTarError({
|
|
|
41
41
|
detail: `'tar' exited with ${describeProcessExitStatus(status)}`,
|
|
42
42
|
stderrTail: collectStderrTail(stderrChunks)
|
|
43
43
|
});
|
|
44
|
+
/** Spawn `tar` with a bounded stderr ring buffer and a SIGKILL-on-
|
|
45
|
+
* scope-close finalizer. Both `tarHostTree` and `untarHostTree` share
|
|
46
|
+
* this body verbatim — only the args, stdio direction (which pipe is
|
|
47
|
+
* the data channel), and the `operation` tag differ. The data pipe
|
|
48
|
+
* (`stdout` for tar, `stdin` for untar) is read off the returned
|
|
49
|
+
* `child` by the caller; stderr is always piped so the exit-code
|
|
50
|
+
* failure can carry the captured tail.
|
|
51
|
+
*
|
|
52
|
+
* The stderr listener is removed and a live child is killed on scope
|
|
53
|
+
* close — the finalizer binds to the surrounding Scope (the stream's
|
|
54
|
+
* lifecycle for tar, the consuming Effect's for untar). */
|
|
55
|
+
const spawnTar = (operation, args, stdio) => Effect.gen(function* () {
|
|
56
|
+
const child = yield* Effect.try({
|
|
57
|
+
try: () => spawn("tar", [...args], { stdio }),
|
|
58
|
+
catch: (cause) => new HostTreeTarError({
|
|
59
|
+
stage: "spawn",
|
|
60
|
+
operation,
|
|
61
|
+
detail: `failed to spawn 'tar ${args.join(" ")}'`,
|
|
62
|
+
cause
|
|
63
|
+
})
|
|
64
|
+
});
|
|
65
|
+
const stderrChunks = [];
|
|
66
|
+
const onStderr = (chunk) => {
|
|
67
|
+
stderrChunks.push(chunk);
|
|
68
|
+
if (stderrChunks.length > 256) stderrChunks.splice(0, stderrChunks.length - 256);
|
|
69
|
+
};
|
|
70
|
+
child.stderr?.on("data", onStderr);
|
|
71
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
72
|
+
child.stderr?.off("data", onStderr);
|
|
73
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
74
|
+
}));
|
|
75
|
+
return {
|
|
76
|
+
child,
|
|
77
|
+
stderrChunks
|
|
78
|
+
};
|
|
79
|
+
});
|
|
44
80
|
const TAR_BLOCK_SIZE = 512;
|
|
45
81
|
const MAX_TAR_EXTENDED_PATH_BYTES = 1024 * 1024;
|
|
46
82
|
const bytesToString = (bytes) => {
|
|
@@ -123,6 +159,10 @@ const applyExtendedContent = (state, content) => {
|
|
|
123
159
|
return null;
|
|
124
160
|
};
|
|
125
161
|
const processTarValidationChunk = (state, chunk) => {
|
|
162
|
+
if (state.content === null && state.skipRemaining >= chunk.length && state.buffer.length === 0) {
|
|
163
|
+
state.skipRemaining -= chunk.length;
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
126
166
|
state.buffer = concatBytes(state.buffer, chunk);
|
|
127
167
|
while (state.buffer.length > 0) {
|
|
128
168
|
if (state.content !== null) {
|
|
@@ -220,7 +260,13 @@ const tarHostTree = (spec) => Stream.unwrap(Effect.gen(function* () {
|
|
|
220
260
|
operation: "tar",
|
|
221
261
|
detail: "tarHostTree called with empty relPaths list"
|
|
222
262
|
}));
|
|
223
|
-
const
|
|
263
|
+
const unsafe = spec.relPaths.find((relPath) => !isSafeArchivePath(relPath));
|
|
264
|
+
if (unsafe !== void 0) return Stream.fail(new HostTreeTarError({
|
|
265
|
+
stage: "entry-validation",
|
|
266
|
+
operation: "tar",
|
|
267
|
+
detail: `unsafe tar relPath (absolute or contains '..'): ${unsafe}`
|
|
268
|
+
}));
|
|
269
|
+
const { child, stderrChunks } = yield* spawnTar("tar", [
|
|
224
270
|
"-c",
|
|
225
271
|
"-f",
|
|
226
272
|
"-",
|
|
@@ -228,30 +274,11 @@ const tarHostTree = (spec) => Stream.unwrap(Effect.gen(function* () {
|
|
|
228
274
|
spec.parentDir,
|
|
229
275
|
"-p",
|
|
230
276
|
...spec.relPaths
|
|
231
|
-
]
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
"pipe"
|
|
237
|
-
] }),
|
|
238
|
-
catch: (cause) => new HostTreeTarError({
|
|
239
|
-
stage: "spawn",
|
|
240
|
-
operation: "tar",
|
|
241
|
-
detail: `failed to spawn 'tar ${args.join(" ")}'`,
|
|
242
|
-
cause
|
|
243
|
-
})
|
|
244
|
-
});
|
|
245
|
-
const stderrChunks = [];
|
|
246
|
-
const onStderr = (chunk) => {
|
|
247
|
-
stderrChunks.push(chunk);
|
|
248
|
-
if (stderrChunks.length > 256) stderrChunks.splice(0, stderrChunks.length - 256);
|
|
249
|
-
};
|
|
250
|
-
child.stderr?.on("data", onStderr);
|
|
251
|
-
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
252
|
-
child.stderr?.off("data", onStderr);
|
|
253
|
-
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
254
|
-
}));
|
|
277
|
+
], [
|
|
278
|
+
"ignore",
|
|
279
|
+
"pipe",
|
|
280
|
+
"pipe"
|
|
281
|
+
]);
|
|
255
282
|
const stdout = child.stdout;
|
|
256
283
|
if (stdout === null) return Stream.fail(new HostTreeTarError({
|
|
257
284
|
stage: "spawn",
|
|
@@ -278,37 +305,18 @@ const tarHostTree = (spec) => Stream.unwrap(Effect.gen(function* () {
|
|
|
278
305
|
* exits cleanly (code 0).
|
|
279
306
|
*/
|
|
280
307
|
const untarHostTree = (stream, spec) => Effect.gen(function* () {
|
|
281
|
-
const
|
|
308
|
+
const { child, stderrChunks } = yield* spawnTar("untar", [
|
|
282
309
|
"-x",
|
|
283
310
|
"-f",
|
|
284
311
|
"-",
|
|
285
312
|
"-C",
|
|
286
313
|
spec.target,
|
|
287
314
|
"-p"
|
|
288
|
-
]
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
"pipe"
|
|
294
|
-
] }),
|
|
295
|
-
catch: (cause) => new HostTreeTarError({
|
|
296
|
-
stage: "spawn",
|
|
297
|
-
operation: "untar",
|
|
298
|
-
detail: `failed to spawn 'tar ${args.join(" ")}'`,
|
|
299
|
-
cause
|
|
300
|
-
})
|
|
301
|
-
});
|
|
302
|
-
const stderrChunks = [];
|
|
303
|
-
const onStderr = (chunk) => {
|
|
304
|
-
stderrChunks.push(chunk);
|
|
305
|
-
if (stderrChunks.length > 256) stderrChunks.splice(0, stderrChunks.length - 256);
|
|
306
|
-
};
|
|
307
|
-
child.stderr?.on("data", onStderr);
|
|
308
|
-
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
309
|
-
child.stderr?.off("data", onStderr);
|
|
310
|
-
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
311
|
-
}));
|
|
315
|
+
], [
|
|
316
|
+
"pipe",
|
|
317
|
+
"ignore",
|
|
318
|
+
"pipe"
|
|
319
|
+
]);
|
|
312
320
|
const stdin = child.stdin;
|
|
313
321
|
if (stdin === null) return yield* Effect.fail(new HostTreeTarError({
|
|
314
322
|
stage: "spawn",
|
|
@@ -341,6 +349,9 @@ const untarHostTree = (stream, spec) => Effect.gen(function* () {
|
|
|
341
349
|
})));
|
|
342
350
|
else settle(Effect.void);
|
|
343
351
|
})) stdin.once("drain", onDrain);
|
|
352
|
+
return Effect.sync(() => {
|
|
353
|
+
stdin.off("drain", onDrain);
|
|
354
|
+
});
|
|
344
355
|
});
|
|
345
356
|
yield* Stream.runForEach(stream, (chunk) => Effect.gen(function* () {
|
|
346
357
|
const validationError = processTarValidationChunk(validationState, chunk);
|