@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,15 +1,26 @@
|
|
|
1
1
|
import { decodeJsonTextSync } from "../../substrate/runtime/runtime-decode.mjs";
|
|
2
2
|
import { CacheService } from "../../substrate/runtime/cache/service.mjs";
|
|
3
|
+
import "../../substrate/runtime/cache/index.mjs";
|
|
3
4
|
import { DockerHost, DockerSpawner, dockerCommand, dockerRun, dockerRunOk } from "./client.mjs";
|
|
4
5
|
import { ImageLoadFailed, ImageNotFound, ImageRemoveFailed, ImageSaveFailed, ImageTagFailed } from "./errors.mjs";
|
|
5
|
-
import { isImageNotFoundStderr, wrapBuildError, wrapGeneric, wrapPullError } from "./wrap.mjs";
|
|
6
|
+
import { isImageNotFoundStderr, isMissingImageStderr, wrapBuildError, wrapGeneric, wrapPullError } from "./wrap.mjs";
|
|
6
7
|
import { Effect, Fiber, Schema, Stream } from "effect";
|
|
7
8
|
//#region src/runtime/docker/image.ts
|
|
8
9
|
const SNAPSHOT_TEMP_TAG_PREFIX = "devstack-snapshot:";
|
|
9
10
|
const isSnapshotTempTag = (ref) => ref.startsWith(SNAPSHOT_TEMP_TAG_PREFIX);
|
|
11
|
+
/** Return the trailing `max` chars of `text`, prefixed with an ellipsis
|
|
12
|
+
* when truncation occurred. Used to surface the actual stdout shape in
|
|
13
|
+
* parse-failure errors without ballooning the error message. */
|
|
14
|
+
const tailBytes = (text, max) => {
|
|
15
|
+
if (text.length <= max) return text;
|
|
16
|
+
return `…${text.slice(-max)}`;
|
|
17
|
+
};
|
|
10
18
|
const cleanupSnapshotTempTag = (ref) => {
|
|
11
19
|
if (!isSnapshotTempTag(ref)) return Effect.void;
|
|
12
|
-
return dockerRunOk("image", ["rm", ref]).pipe(Effect.
|
|
20
|
+
return dockerRunOk("image", ["rm", ref]).pipe(Effect.tapCause((cause) => Effect.logDebug("docker image rm (snapshot temp tag) failed", {
|
|
21
|
+
ref,
|
|
22
|
+
cause
|
|
23
|
+
})), Effect.catch(() => Effect.void), Effect.asVoid);
|
|
13
24
|
};
|
|
14
25
|
/** Encode/decode the cache entry — we store the resolved image digest
|
|
15
26
|
* as plain UTF-8 bytes so the cache primitive's byte shape works. */
|
|
@@ -66,6 +77,7 @@ const build = (opts) => Effect.gen(function* () {
|
|
|
66
77
|
args.push("-f", resolved);
|
|
67
78
|
}
|
|
68
79
|
if (opts.buildArgs) for (const [k, v] of Object.entries(opts.buildArgs)) args.push("--build-arg", `${k}=${v}`);
|
|
80
|
+
if (opts.labels) for (const [k, v] of Object.entries(opts.labels)) args.push("--label", `${k}=${v}`);
|
|
69
81
|
args.push(opts.contextPath);
|
|
70
82
|
yield* dockerRun("build", args, {
|
|
71
83
|
onStdoutLine: opts.onLine,
|
|
@@ -90,7 +102,6 @@ const tagImage = (src, dst, opts = {}) => Effect.gen(function* () {
|
|
|
90
102
|
}
|
|
91
103
|
if (opts.removeSourceAfterTag === true && src !== dst) yield* cleanupSnapshotTempTag(src);
|
|
92
104
|
}).pipe(Effect.withSpan("runtime.docker.image.tag"));
|
|
93
|
-
const isMissingImageStderr = (stderr) => /no such image|not found|reference does not exist/i.test(stderr);
|
|
94
105
|
const removeImage = (ref) => Effect.gen(function* () {
|
|
95
106
|
const res = yield* dockerRunOk("image", [
|
|
96
107
|
"rm",
|
|
@@ -120,12 +131,12 @@ const saveImages = (refs, opts = {}) => {
|
|
|
120
131
|
const spawner = yield* DockerSpawner;
|
|
121
132
|
const cmd = dockerCommand(host, "save", refs);
|
|
122
133
|
const handle = yield* spawner.spawn(cmd).pipe(Effect.mapError(mapSpawnError));
|
|
123
|
-
const stderrFiber = yield* Effect.
|
|
134
|
+
const stderrFiber = yield* Effect.forkScoped(Stream.mkString(Stream.decodeText(handle.stderr)).pipe(Effect.mapError((cause) => new ImageSaveFailed({
|
|
124
135
|
ref: refs.join(" "),
|
|
125
136
|
detail: "docker save stderr drain failed",
|
|
126
137
|
cause
|
|
127
138
|
}))));
|
|
128
|
-
const exitFiber = yield* Effect.
|
|
139
|
+
const exitFiber = yield* Effect.forkScoped(handle.exitCode.pipe(Effect.mapError((cause) => new ImageSaveFailed({
|
|
129
140
|
ref: refs.join(" "),
|
|
130
141
|
detail: "docker save exit failed",
|
|
131
142
|
cause
|
|
@@ -215,10 +226,13 @@ const loadImage = (tar) => Effect.gen(function* () {
|
|
|
215
226
|
stderr: stderrText
|
|
216
227
|
}));
|
|
217
228
|
const parsed = parseLoadedRefs(stdoutText);
|
|
218
|
-
if (parsed.length === 0)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
229
|
+
if (parsed.length === 0) {
|
|
230
|
+
const stdoutTail = tailBytes(stdoutText, 500);
|
|
231
|
+
return yield* Effect.fail(new ImageLoadFailed({
|
|
232
|
+
detail: `docker load succeeded but no Loaded image lines found; stdout tail: ${stdoutTail}`,
|
|
233
|
+
stderr: stderrText
|
|
234
|
+
}));
|
|
235
|
+
}
|
|
222
236
|
return { refs: yield* Effect.forEach(parsed, (ref) => Effect.gen(function* () {
|
|
223
237
|
if (ref.digest !== void 0) return refOf(ref.digest, ref.tag);
|
|
224
238
|
const digest = yield* imageExists(ref.tag);
|
|
@@ -244,7 +258,10 @@ const ensureImageCached = (opts, key) => Effect.gen(function* () {
|
|
|
244
258
|
namespace: key.namespace,
|
|
245
259
|
chain: key.chain,
|
|
246
260
|
contentHash: key.contentHash
|
|
247
|
-
}).pipe(Effect.
|
|
261
|
+
}).pipe(Effect.tapCause((cause) => Effect.logDebug("image cache lookup failed; rebuilding", {
|
|
262
|
+
key,
|
|
263
|
+
cause
|
|
264
|
+
})), Effect.catch(() => Effect.succeed(null)));
|
|
248
265
|
if (hit !== null) {
|
|
249
266
|
if (decodeDigest(hit.bytes) !== null) {
|
|
250
267
|
const existing = yield* imageExists(opts.tag);
|
|
@@ -257,7 +274,10 @@ const ensureImageCached = (opts, key) => Effect.gen(function* () {
|
|
|
257
274
|
namespace: key.namespace,
|
|
258
275
|
chain: key.chain,
|
|
259
276
|
contentHash: key.contentHash
|
|
260
|
-
}, encodeDigest(onHost)).pipe(Effect.
|
|
277
|
+
}, encodeDigest(onHost)).pipe(Effect.tapCause((cause) => Effect.logDebug("image cache write (on-host hit) failed", {
|
|
278
|
+
key,
|
|
279
|
+
cause
|
|
280
|
+
})), Effect.catch(() => Effect.void));
|
|
261
281
|
return onHost;
|
|
262
282
|
}
|
|
263
283
|
const digest = yield* build(opts);
|
|
@@ -265,7 +285,10 @@ const ensureImageCached = (opts, key) => Effect.gen(function* () {
|
|
|
265
285
|
namespace: key.namespace,
|
|
266
286
|
chain: key.chain,
|
|
267
287
|
contentHash: key.contentHash
|
|
268
|
-
}, encodeDigest(digest)).pipe(Effect.
|
|
288
|
+
}, encodeDigest(digest)).pipe(Effect.tapCause((cause) => Effect.logDebug("image cache write (post-build) failed", {
|
|
289
|
+
key,
|
|
290
|
+
cause
|
|
291
|
+
})), Effect.catch(() => Effect.void));
|
|
269
292
|
return digest;
|
|
270
293
|
}).pipe(Effect.withSpan("runtime.docker.image.ensureCached"));
|
|
271
294
|
const refOf = (digest, tag) => tag !== void 0 ? {
|
|
@@ -273,6 +296,6 @@ const refOf = (digest, tag) => tag !== void 0 ? {
|
|
|
273
296
|
tag
|
|
274
297
|
} : { digest };
|
|
275
298
|
//#endregion
|
|
276
|
-
export { ensureImageCached, loadImage, pull, refOf, removeImage, saveImage, saveImages, tagImage };
|
|
299
|
+
export { build, ensureImageCached, imageExists, inspectDigest, loadImage, pull, refOf, removeImage, saveImage, saveImages, tagImage };
|
|
277
300
|
|
|
278
301
|
//# sourceMappingURL=image.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.mjs","names":[],"sources":["../../../src/runtime/docker/image.ts"],"sourcesContent":["// Docker image lifecycle.\n//\n// Architecture § Docker backend § Images:\n// - Bring-your-own-image vs build is a CLOSED SUM (architecture §8):\n// either the caller supplies an image ref (`docker pull`) or a\n// `ContainerBuildContext` (`docker build`); no third option.\n// - Content-addressed build cache: the substrate's L0 cache holds\n// the `(namespace, chainId, contentHash) → imageDigest` mapping.\n// When the cache hits we short-circuit the build. The `docker\n// build` itself is then a no-op when the resulting tag already\n// exists on the host (defense-in-depth — cache miss but on-host\n// tag-exists still skips the build).\n// - Pull progress narration is wired through `onStdoutLine` /\n// `onStderrLine` per-line callbacks on the L0 capture; the L1\n// observability sink promotes WARN/ERROR markers.\n//\n// Surface:\n// - `imageExists(ref)` — `docker inspect` short-circuit\n// - `pull(ref)` — `docker pull`; surfaces ImageNotFound /\n// ImagePullFailed via wrap.ts\n// - `build(ctx, tag)` — `docker build -t <tag> <ctx>`; surfaces\n// BuildFailed via wrap.ts; integrates with\n// the L0 cache\n// - `tag(src, dst)` — `docker tag`\n// - `inspectDigest` — read the on-host digest of a tag\n\nimport { Effect, Fiber, Schema, Stream } from 'effect';\n\nimport type { ChainId, ContentHash } from '../../substrate/brand.ts';\nimport { CacheService } from '../../substrate/runtime/cache/index.ts';\nimport { decodeJsonTextSync } from '../../substrate/runtime/runtime-decode.ts';\nimport type { ImageRef, LoadedImageBundle } from '../../contracts/container-runtime.ts';\nimport { DockerHost, DockerSpawner, dockerCommand, dockerRun, dockerRunOk } from './client.ts';\nimport type { DockerRuntimeError } from './errors.ts';\nimport {\n\tImageLoadFailed,\n\tImageNotFound,\n\tImageRemoveFailed,\n\tImageSaveFailed,\n\tImageTagFailed,\n} from './errors.ts';\nimport { isImageNotFoundStderr, wrapBuildError, wrapGeneric, wrapPullError } from './wrap.ts';\n\n// -----------------------------------------------------------------------------\n// Helpers\n// -----------------------------------------------------------------------------\n\nconst SNAPSHOT_TEMP_TAG_PREFIX = 'devstack-snapshot:';\n\nconst isSnapshotTempTag = (ref: string): boolean => ref.startsWith(SNAPSHOT_TEMP_TAG_PREFIX);\n\nconst cleanupSnapshotTempTag = (\n\tref: string,\n): Effect.Effect<void, never, DockerHost | DockerSpawner> => {\n\tif (!isSnapshotTempTag(ref)) return Effect.void;\n\treturn dockerRunOk('image', ['rm', ref]).pipe(\n\t\tEffect.catch(() => Effect.void),\n\t\tEffect.asVoid,\n\t);\n};\n\nexport interface SaveImageOptions {\n\treadonly removeAfterSave?: boolean;\n}\n\nexport interface TagImageOptions {\n\treadonly removeSourceAfterTag?: boolean;\n}\n\n/** Encode/decode the cache entry — we store the resolved image digest\n * as plain UTF-8 bytes so the cache primitive's byte shape works. */\nconst encodeDigest = (digest: string): Uint8Array =>\n\tnew TextEncoder().encode(JSON.stringify({ digest } satisfies { digest: string }));\n\nconst DigestDoc = Schema.Struct({ digest: Schema.String });\n\nconst decodeDigest = (bytes: Uint8Array): string | null => {\n\ttry {\n\t\treturn decodeJsonTextSync(DigestDoc, new TextDecoder().decode(bytes), {\n\t\t\tsource: 'docker image digest cache',\n\t\t\tmkError: (issue) => issue,\n\t\t}).digest;\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Exists / inspect\n// -----------------------------------------------------------------------------\n\n/** `docker image inspect <ref>` — returns the digest, or null on miss. */\nexport const imageExists = (\n\tref: string,\n): Effect.Effect<string | null, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('image', ['inspect', '--format', '{{.Id}}', ref]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.image.inspect')),\n\t\t);\n\t\tif (res.exitCode !== 0) return null;\n\t\tconst id = res.stdout.trim();\n\t\treturn id.length > 0 ? id : null;\n\t}).pipe(Effect.withSpan('runtime.docker.image.exists'));\n\n/** Strict variant — surfaces `ImageNotFound` if the inspect misses. */\nexport const inspectDigest = (\n\tref: string,\n): Effect.Effect<string, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst id = yield* imageExists(ref);\n\t\tif (id === null) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew ImageNotFound({ ref, detail: 'docker image inspect returned empty' }),\n\t\t\t);\n\t\t}\n\t\treturn id;\n\t}).pipe(Effect.withSpan('runtime.docker.image.inspectDigest'));\n\n// -----------------------------------------------------------------------------\n// Pull\n// -----------------------------------------------------------------------------\n\n/** `docker pull <ref>` — surfaces ImageNotFound / ImagePullFailed.\n * Per-line progress emission opt-in via `onLine`. */\nexport const pull = (\n\tref: string,\n\tonLine?: (line: string) => Effect.Effect<void>,\n): Effect.Effect<string, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tyield* dockerRun('pull', [ref], { onStdoutLine: onLine }).pipe(\n\t\t\tEffect.mapError(wrapPullError(ref)),\n\t\t);\n\t\treturn yield* inspectDigest(ref);\n\t}).pipe(Effect.withSpan('runtime.docker.image.pull'));\n\n// -----------------------------------------------------------------------------\n// Build (content-addressed)\n// -----------------------------------------------------------------------------\n\nexport interface BuildOptions {\n\treadonly contextPath: string;\n\treadonly dockerfile?: string;\n\treadonly platform?: string;\n\treadonly buildArgs?: Readonly<Record<string, string>>;\n\treadonly tag: string;\n\treadonly onLine?: (line: string) => Effect.Effect<void>;\n}\n\n/** `docker build -t <tag> [-f Dockerfile] <ctx>`. On exit-0 the\n * resulting tag is on-host; we read its digest and return.\n *\n * NOTE: the content-addressed cache integration is in `ensureImageCached`\n * below — this verb is the unconditional build, used when the cache\n * has missed AND the on-host tag-exists probe also missed. */\nexport const build = (\n\topts: BuildOptions,\n): Effect.Effect<string, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst args: Array<string> = ['-t', opts.tag];\n\t\tif (opts.platform !== undefined) {\n\t\t\targs.push('--platform', opts.platform);\n\t\t}\n\t\t// `-f` is resolved relative to the docker CLI's CWD, NOT the\n\t\t// context directory. Skip `-f` entirely when the dockerfile is\n\t\t// the canonical default name (`Dockerfile`) and the context is a\n\t\t// directory — docker buildx then auto-discovers `<ctx>/Dockerfile`.\n\t\t// When the user names a non-default dockerfile, resolve it against\n\t\t// the context so `docker build -f a/b/Dockerfile.x .` works\n\t\t// regardless of CWD.\n\t\tif (opts.dockerfile && opts.dockerfile !== 'Dockerfile') {\n\t\t\tconst isAbs = opts.dockerfile.startsWith('/') || /^[A-Za-z]:/.test(opts.dockerfile);\n\t\t\tconst resolved = isAbs\n\t\t\t\t? opts.dockerfile\n\t\t\t\t: `${opts.contextPath.replace(/\\/$/, '')}/${opts.dockerfile}`;\n\t\t\targs.push('-f', resolved);\n\t\t}\n\t\tif (opts.buildArgs) {\n\t\t\tfor (const [k, v] of Object.entries(opts.buildArgs)) {\n\t\t\t\targs.push('--build-arg', `${k}=${v}`);\n\t\t\t}\n\t\t}\n\t\targs.push(opts.contextPath);\n\t\tyield* dockerRun('build', args, {\n\t\t\tonStdoutLine: opts.onLine,\n\t\t\tonStderrLine: opts.onLine,\n\t\t}).pipe(Effect.mapError(wrapBuildError(opts.contextPath, opts.dockerfile)));\n\t\treturn yield* inspectDigest(opts.tag);\n\t}).pipe(Effect.withSpan('runtime.docker.image.build'));\n\n// -----------------------------------------------------------------------------\n// Tag\n// -----------------------------------------------------------------------------\n\n/** `docker tag <src> <dst>` — used by snapshot restore to alias a\n * loaded image back to its original tag. */\nexport const tagImage = (\n\tsrc: string,\n\tdst: string,\n\topts: TagImageOptions = {},\n): Effect.Effect<void, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('tag', [src, dst]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.tag')),\n\t\t);\n\t\tif (res.exitCode !== 0) {\n\t\t\tif (isImageNotFoundStderr(res.stderr)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew ImageNotFound({ ref: src, detail: `tag source missing: ${res.stderr}` }),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn yield* Effect.fail(new ImageTagFailed({ src, dst, stderr: res.stderr }));\n\t\t}\n\t\tif (opts.removeSourceAfterTag === true && src !== dst) {\n\t\t\tyield* cleanupSnapshotTempTag(src);\n\t\t}\n\t}).pipe(Effect.withSpan('runtime.docker.image.tag'));\n\nconst isMissingImageStderr = (stderr: string): boolean =>\n\t/no such image|not found|reference does not exist/i.test(stderr);\n\nexport const removeImage = (\n\tref: string,\n): Effect.Effect<void, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('image', ['rm', '-f', ref]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.image.rm')),\n\t\t);\n\t\tif (res.exitCode === 0 || isMissingImageStderr(res.stderr)) return;\n\t\treturn yield* Effect.fail(\n\t\t\tnew ImageRemoveFailed({\n\t\t\t\tref,\n\t\t\t\tstderr: res.stderr,\n\t\t\t\texitCode: res.exitCode,\n\t\t\t}),\n\t\t);\n\t}).pipe(Effect.withSpan('runtime.docker.image.remove'));\n\n// -----------------------------------------------------------------------------\n// Save (image → tar stream)\n// -----------------------------------------------------------------------------\n\n/** Stream the bytes of `docker save <ref>` to the consumer. Stdout\n * stays streaming so large images never materialise in memory; stderr\n * and exit are drained concurrently so a missing image cannot look\n * like a successful empty tar. */\nexport const saveImages = (\n\trefs: ReadonlyArray<string>,\n\topts: SaveImageOptions = {},\n): Stream.Stream<Uint8Array, DockerRuntimeError, DockerHost | DockerSpawner> => {\n\tconst mapSpawnError = (cause: unknown): DockerRuntimeError =>\n\t\tnew ImageSaveFailed({\n\t\t\tref: refs.join(' '),\n\t\t\tdetail:\n\t\t\t\trefs.length === 0 ? 'docker save called with no image refs' : 'docker save spawn failed',\n\t\t\tcause,\n\t\t});\n\tif (refs.length === 0) {\n\t\treturn Stream.fail(mapSpawnError(new Error('empty docker save ref list')));\n\t}\n\treturn Stream.unwrap(\n\t\tEffect.gen(function* () {\n\t\t\tconst host = yield* DockerHost;\n\t\t\tconst spawner = yield* DockerSpawner;\n\t\t\tconst cmd = dockerCommand(host, 'save', refs);\n\t\t\tconst handle = yield* spawner.spawn(cmd).pipe(Effect.mapError(mapSpawnError));\n\t\t\tconst stderrFiber = yield* Effect.forkChild(\n\t\t\t\tStream.mkString(Stream.decodeText(handle.stderr)).pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageSaveFailed({\n\t\t\t\t\t\t\t\tref: refs.join(' '),\n\t\t\t\t\t\t\t\tdetail: 'docker save stderr drain failed',\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst exitFiber = yield* Effect.forkChild(\n\t\t\t\thandle.exitCode.pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageSaveFailed({\n\t\t\t\t\t\t\t\tref: refs.join(' '),\n\t\t\t\t\t\t\t\tdetail: 'docker save exit failed',\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst checkExit = Effect.gen(function* () {\n\t\t\t\tconst [stderrText, exitCode] = yield* Effect.all(\n\t\t\t\t\t[Fiber.join(stderrFiber), Fiber.join(exitFiber)] as const,\n\t\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t\t);\n\t\t\t\tif (exitCode !== 0) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew ImageSaveFailed({\n\t\t\t\t\t\t\tref: refs.join(' '),\n\t\t\t\t\t\t\tdetail: `docker save exited ${exitCode}${\n\t\t\t\t\t\t\t\tstderrText.length > 0 ? `: ${stderrText}` : ''\n\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\t\t\tconst cleanupTag =\n\t\t\t\topts.removeAfterSave === true\n\t\t\t\t\t? Effect.forEach(refs, cleanupSnapshotTempTag, { concurrency: 'unbounded' }).pipe(\n\t\t\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\t\t)\n\t\t\t\t\t: Effect.void;\n\t\t\tconst cleanup = Effect.all(\n\t\t\t\t[cleanupTag, Fiber.interrupt(stderrFiber), Fiber.interrupt(exitFiber)],\n\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t).pipe(Effect.asVoid);\n\t\t\treturn handle.stdout.pipe(\n\t\t\t\tStream.mapError(\n\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\tnew ImageSaveFailed({\n\t\t\t\t\t\t\tref: refs.join(' '),\n\t\t\t\t\t\t\tdetail: 'docker save stdout pipe failed',\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\tStream.onEnd(checkExit),\n\t\t\t\tStream.ensuring(cleanup),\n\t\t\t);\n\t\t}),\n\t);\n};\n\nexport const saveImage = (\n\tref: string,\n\topts: SaveImageOptions = {},\n): Stream.Stream<Uint8Array, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tsaveImages([ref], opts);\n\n// -----------------------------------------------------------------------------\n// Load (tar stream → image)\n// -----------------------------------------------------------------------------\n\n/** Parse `docker load` stdout lines to extract the loaded image refs.\n * Docker's output is one of:\n * - `Loaded image: <tag>`\n * - `Loaded image ID: sha256:<digest>`\n * Exported for direct testing without a docker dependency. */\nexport const parseLoadedRefs = (\n\tstdout: string,\n): ReadonlyArray<{ readonly tag?: string; readonly digest?: string }> => {\n\tconst refs: Array<{ tag?: string; digest?: string }> = [];\n\tfor (const rawLine of stdout.split(/\\r?\\n/)) {\n\t\tconst line = rawLine.trim();\n\t\tconst taggedMatch = /^Loaded image: (\\S+)$/.exec(line);\n\t\tif (taggedMatch?.[1]) {\n\t\t\trefs.push({ tag: taggedMatch[1] });\n\t\t\tcontinue;\n\t\t}\n\t\tconst digestMatch = /^Loaded image ID: (sha256:\\S+)$/.exec(line);\n\t\tif (digestMatch?.[1]) {\n\t\t\trefs.push({ digest: digestMatch[1] });\n\t\t}\n\t}\n\treturn refs;\n};\n\n/** `docker load < <tar-stream>`. Returns every freshly-loaded ref\n * Docker reported on stdout. Symmetric with `saveImage`/`saveImages`.\n * Upstream stream errors (e.g. file-read failures from a snapshot\n * tar) are projected to `ImageLoadFailed` so callers see one shape.\n *\n * Stdin lifetime: the supplied stream is fed in via the spawner's\n * `stdin` sink under the same scope as the spawn itself; both close\n * when the child exits. */\nexport const loadImage = (\n\ttar: Stream.Stream<Uint8Array, unknown>,\n): Effect.Effect<LoadedImageBundle, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst host = yield* DockerHost;\n\t\tconst spawner = yield* DockerSpawner;\n\t\tconst cmd = dockerCommand(host, 'load', []);\n\t\tconst mapSpawnError = (cause: unknown): DockerRuntimeError =>\n\t\t\tnew ImageLoadFailed({ detail: 'docker load spawn failed', cause });\n\t\treturn yield* Effect.scoped(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst handle = yield* spawner.spawn(cmd).pipe(Effect.mapError(mapSpawnError));\n\t\t\t\t// Concurrently:\n\t\t\t\t// - pump the supplied tar into stdin\n\t\t\t\t// - drain stdout (the \"Loaded image: …\" lines)\n\t\t\t\t// - drain stderr\n\t\t\t\t// - wait for exit\n\t\t\t\tconst writeStdin = Stream.run(tar, handle.stdin).pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause: unknown): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageLoadFailed({ detail: 'docker load stdin pipe failed', cause }),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tconst stdoutDrain = Stream.mkString(Stream.decodeText(handle.stdout)).pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageLoadFailed({ detail: 'docker load stdout drain failed', cause }),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tconst stderrDrain = Stream.mkString(Stream.decodeText(handle.stderr)).pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageLoadFailed({ detail: 'docker load stderr drain failed', cause }),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tconst exit = handle.exitCode.pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageLoadFailed({ detail: 'docker load exit failed', cause }),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tconst [, stdoutText, stderrText, exitCode] = yield* Effect.all(\n\t\t\t\t\t[writeStdin, stdoutDrain, stderrDrain, exit] as const,\n\t\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t\t);\n\t\t\t\tif (exitCode !== 0) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew ImageLoadFailed({\n\t\t\t\t\t\t\tdetail: `docker load exited ${exitCode}`,\n\t\t\t\t\t\t\tstderr: stderrText,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst parsed = parseLoadedRefs(stdoutText);\n\t\t\t\tif (parsed.length === 0) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew ImageLoadFailed({\n\t\t\t\t\t\t\tdetail: 'docker load succeeded but no Loaded image lines found',\n\t\t\t\t\t\t\tstderr: stdoutText,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst refs = yield* Effect.forEach(\n\t\t\t\t\tparsed,\n\t\t\t\t\t(ref) =>\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (ref.digest !== undefined) {\n\t\t\t\t\t\t\t\treturn refOf(ref.digest, ref.tag);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst digest = yield* imageExists(ref.tag!);\n\t\t\t\t\t\t\tif (digest === null) {\n\t\t\t\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\t\t\t\tnew ImageLoadFailed({\n\t\t\t\t\t\t\t\t\t\tdetail: `loaded image '${ref.tag}' not visible to inspect`,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn refOf(digest, ref.tag);\n\t\t\t\t\t\t}),\n\t\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t\t);\n\t\t\t\treturn { refs };\n\t\t\t}),\n\t\t);\n\t}).pipe(Effect.withSpan('runtime.docker.image.load'));\n\n// -----------------------------------------------------------------------------\n// Cache integration\n// -----------------------------------------------------------------------------\n\nexport interface CachedBuildKey {\n\treadonly namespace: string;\n\treadonly chain: ChainId;\n\treadonly contentHash: ContentHash;\n}\n\n/** Content-addressed image build:\n *\n * 1. cache lookup keyed by (namespace, chainId, contentHash)\n * 2. on hit, verify the on-host tag still resolves; if so return\n * 3. on miss, run `docker build`, then write the resolved digest\n * to the cache (best-effort — write failure does not roll back\n * the on-host build).\n *\n * Caller-supplied tag is the content-addressed name; the cache key\n * is independent so an unrelated invalidation (e.g. base-image\n * bump) can drop the cache without disturbing the tag scheme. */\nexport const ensureImageCached = (\n\topts: BuildOptions,\n\tkey: CachedBuildKey,\n): Effect.Effect<string, DockerRuntimeError, CacheService | DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst cache = yield* CacheService;\n\t\tconst hit = yield* cache\n\t\t\t.lookup({ namespace: key.namespace, chain: key.chain, contentHash: key.contentHash })\n\t\t\t.pipe(\n\t\t\t\t// Cache-side failures collapse to MISS — the contract is\n\t\t\t\t// best-effort. We still build.\n\t\t\t\tEffect.catch(() => Effect.succeed(null)),\n\t\t\t);\n\t\tif (hit !== null) {\n\t\t\tconst cached = decodeDigest(hit.bytes);\n\t\t\tif (cached !== null) {\n\t\t\t\t// Verify the on-host tag still resolves to something —\n\t\t\t\t// the cached digest may have been pruned out from under us.\n\t\t\t\tconst existing = yield* imageExists(opts.tag);\n\t\t\t\tif (existing !== null) {\n\t\t\t\t\treturn existing;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// On-host tag-exists short-circuit (defence-in-depth).\n\t\tconst onHost = yield* imageExists(opts.tag);\n\t\tif (onHost !== null) {\n\t\t\tyield* cache\n\t\t\t\t.write(\n\t\t\t\t\t{ namespace: key.namespace, chain: key.chain, contentHash: key.contentHash },\n\t\t\t\t\tencodeDigest(onHost),\n\t\t\t\t)\n\t\t\t\t.pipe(Effect.catch(() => Effect.void));\n\t\t\treturn onHost;\n\t\t}\n\t\tconst digest = yield* build(opts);\n\t\tyield* cache\n\t\t\t.write(\n\t\t\t\t{ namespace: key.namespace, chain: key.chain, contentHash: key.contentHash },\n\t\t\t\tencodeDigest(digest),\n\t\t\t)\n\t\t\t.pipe(Effect.catch(() => Effect.void));\n\t\treturn digest;\n\t}).pipe(Effect.withSpan('runtime.docker.image.ensureCached'));\n\n// -----------------------------------------------------------------------------\n// Contract-shaped ImageRef helpers\n// -----------------------------------------------------------------------------\n\nexport const refOf = (digest: string, tag?: string): ImageRef =>\n\ttag !== undefined ? { digest, tag } : { digest };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"image.mjs","names":[],"sources":["../../../src/runtime/docker/image.ts"],"sourcesContent":["// Docker image lifecycle.\n//\n// Architecture § Docker backend § Images:\n// - Bring-your-own-image vs build is a CLOSED SUM (architecture §8):\n// either the caller supplies an image ref (`docker pull`) or a\n// `ContainerBuildContext` (`docker build`); no third option.\n// - Content-addressed build cache: the substrate's L0 cache holds\n// the `(namespace, chainId, contentHash) → imageDigest` mapping.\n// When the cache hits we short-circuit the build. The `docker\n// build` itself is then a no-op when the resulting tag already\n// exists on the host (defense-in-depth — cache miss but on-host\n// tag-exists still skips the build).\n// - Pull progress narration is wired through `onStdoutLine` /\n// `onStderrLine` per-line callbacks on the L0 capture; the L1\n// observability sink promotes WARN/ERROR markers.\n//\n// Surface:\n// - `imageExists(ref)` — `docker inspect` short-circuit\n// - `pull(ref)` — `docker pull`; surfaces ImageNotFound /\n// ImagePullFailed via wrap.ts\n// - `build(ctx, tag)` — `docker build -t <tag> <ctx>`; surfaces\n// BuildFailed via wrap.ts; integrates with\n// the L0 cache\n// - `tag(src, dst)` — `docker tag`\n// - `inspectDigest` — read the on-host digest of a tag\n\nimport { Effect, Fiber, Schema, Stream } from 'effect';\n\nimport type { ChainId, ContentHash } from '../../substrate/brand.ts';\nimport { CacheService } from '../../substrate/runtime/cache/index.ts';\nimport { decodeJsonTextSync } from '../../substrate/runtime/runtime-decode.ts';\nimport type { ImageRef, LoadedImageBundle } from '../../contracts/container-runtime.ts';\nimport { DockerHost, DockerSpawner, dockerCommand, dockerRun, dockerRunOk } from './client.ts';\nimport type { DockerRuntimeError } from './errors.ts';\nimport {\n\tImageLoadFailed,\n\tImageNotFound,\n\tImageRemoveFailed,\n\tImageSaveFailed,\n\tImageTagFailed,\n} from './errors.ts';\nimport {\n\tisImageNotFoundStderr,\n\tisMissingImageStderr,\n\twrapBuildError,\n\twrapGeneric,\n\twrapPullError,\n} from './wrap.ts';\n\n// -----------------------------------------------------------------------------\n// Helpers\n// -----------------------------------------------------------------------------\n\nconst SNAPSHOT_TEMP_TAG_PREFIX = 'devstack-snapshot:';\n\nconst isSnapshotTempTag = (ref: string): boolean => ref.startsWith(SNAPSHOT_TEMP_TAG_PREFIX);\n\n/** Return the trailing `max` chars of `text`, prefixed with an ellipsis\n * when truncation occurred. Used to surface the actual stdout shape in\n * parse-failure errors without ballooning the error message. */\nconst tailBytes = (text: string, max: number): string => {\n\tif (text.length <= max) return text;\n\treturn `…${text.slice(-max)}`;\n};\n\nconst cleanupSnapshotTempTag = (\n\tref: string,\n): Effect.Effect<void, never, DockerHost | DockerSpawner> => {\n\tif (!isSnapshotTempTag(ref)) return Effect.void;\n\treturn dockerRunOk('image', ['rm', ref]).pipe(\n\t\tEffect.tapCause((cause) =>\n\t\t\tEffect.logDebug('docker image rm (snapshot temp tag) failed', { ref, cause }),\n\t\t),\n\t\tEffect.catch(() => Effect.void),\n\t\tEffect.asVoid,\n\t);\n};\n\nexport interface SaveImageOptions {\n\treadonly removeAfterSave?: boolean;\n}\n\nexport interface TagImageOptions {\n\treadonly removeSourceAfterTag?: boolean;\n}\n\n/** Encode/decode the cache entry — we store the resolved image digest\n * as plain UTF-8 bytes so the cache primitive's byte shape works. */\nconst encodeDigest = (digest: string): Uint8Array =>\n\tnew TextEncoder().encode(JSON.stringify({ digest } satisfies { digest: string }));\n\nconst DigestDoc = Schema.Struct({ digest: Schema.String });\n\nconst decodeDigest = (bytes: Uint8Array): string | null => {\n\ttry {\n\t\treturn decodeJsonTextSync(DigestDoc, new TextDecoder().decode(bytes), {\n\t\t\tsource: 'docker image digest cache',\n\t\t\tmkError: (issue) => issue,\n\t\t}).digest;\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Exists / inspect\n// -----------------------------------------------------------------------------\n\n/** `docker image inspect <ref>` — returns the digest, or null on miss. */\nexport const imageExists = (\n\tref: string,\n): Effect.Effect<string | null, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('image', ['inspect', '--format', '{{.Id}}', ref]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.image.inspect')),\n\t\t);\n\t\tif (res.exitCode !== 0) return null;\n\t\tconst id = res.stdout.trim();\n\t\treturn id.length > 0 ? id : null;\n\t}).pipe(Effect.withSpan('runtime.docker.image.exists'));\n\n/** Strict variant — surfaces `ImageNotFound` if the inspect misses. */\nexport const inspectDigest = (\n\tref: string,\n): Effect.Effect<string, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst id = yield* imageExists(ref);\n\t\tif (id === null) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew ImageNotFound({ ref, detail: 'docker image inspect returned empty' }),\n\t\t\t);\n\t\t}\n\t\treturn id;\n\t}).pipe(Effect.withSpan('runtime.docker.image.inspectDigest'));\n\n// -----------------------------------------------------------------------------\n// Pull\n// -----------------------------------------------------------------------------\n\n/** `docker pull <ref>` — surfaces ImageNotFound / ImagePullFailed.\n * Per-line progress emission opt-in via `onLine`. */\nexport const pull = (\n\tref: string,\n\tonLine?: (line: string) => Effect.Effect<void>,\n): Effect.Effect<string, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tyield* dockerRun('pull', [ref], { onStdoutLine: onLine }).pipe(\n\t\t\tEffect.mapError(wrapPullError(ref)),\n\t\t);\n\t\treturn yield* inspectDigest(ref);\n\t}).pipe(Effect.withSpan('runtime.docker.image.pull'));\n\n// -----------------------------------------------------------------------------\n// Build (content-addressed)\n// -----------------------------------------------------------------------------\n\nexport interface BuildOptions {\n\treadonly contextPath: string;\n\treadonly dockerfile?: string;\n\treadonly platform?: string;\n\treadonly buildArgs?: Readonly<Record<string, string>>;\n\treadonly tag: string;\n\t/** Image labels rendered as `--label key=value` flags. Stamped onto\n\t * the resulting image so label-driven prune can find it. */\n\treadonly labels?: Readonly<Record<string, string>>;\n\treadonly onLine?: (line: string) => Effect.Effect<void>;\n}\n\n/** `docker build -t <tag> [-f Dockerfile] <ctx>`. On exit-0 the\n * resulting tag is on-host; we read its digest and return.\n *\n * NOTE: the content-addressed cache integration is in `ensureImageCached`\n * below — this verb is the unconditional build, used when the cache\n * has missed AND the on-host tag-exists probe also missed. */\nexport const build = (\n\topts: BuildOptions,\n): Effect.Effect<string, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst args: Array<string> = ['-t', opts.tag];\n\t\tif (opts.platform !== undefined) {\n\t\t\targs.push('--platform', opts.platform);\n\t\t}\n\t\t// `-f` is resolved relative to the docker CLI's CWD, NOT the\n\t\t// context directory. Skip `-f` entirely when the dockerfile is\n\t\t// the canonical default name (`Dockerfile`) and the context is a\n\t\t// directory — docker buildx then auto-discovers `<ctx>/Dockerfile`.\n\t\t// When the user names a non-default dockerfile, resolve it against\n\t\t// the context so `docker build -f a/b/Dockerfile.x .` works\n\t\t// regardless of CWD.\n\t\tif (opts.dockerfile && opts.dockerfile !== 'Dockerfile') {\n\t\t\tconst isAbs = opts.dockerfile.startsWith('/') || /^[A-Za-z]:/.test(opts.dockerfile);\n\t\t\tconst resolved = isAbs\n\t\t\t\t? opts.dockerfile\n\t\t\t\t: `${opts.contextPath.replace(/\\/$/, '')}/${opts.dockerfile}`;\n\t\t\targs.push('-f', resolved);\n\t\t}\n\t\tif (opts.buildArgs) {\n\t\t\tfor (const [k, v] of Object.entries(opts.buildArgs)) {\n\t\t\t\targs.push('--build-arg', `${k}=${v}`);\n\t\t\t}\n\t\t}\n\t\tif (opts.labels) {\n\t\t\tfor (const [k, v] of Object.entries(opts.labels)) {\n\t\t\t\targs.push('--label', `${k}=${v}`);\n\t\t\t}\n\t\t}\n\t\targs.push(opts.contextPath);\n\t\tyield* dockerRun('build', args, {\n\t\t\tonStdoutLine: opts.onLine,\n\t\t\tonStderrLine: opts.onLine,\n\t\t}).pipe(Effect.mapError(wrapBuildError(opts.contextPath, opts.dockerfile)));\n\t\treturn yield* inspectDigest(opts.tag);\n\t}).pipe(Effect.withSpan('runtime.docker.image.build'));\n\n// -----------------------------------------------------------------------------\n// Tag\n// -----------------------------------------------------------------------------\n\n/** `docker tag <src> <dst>` — used by snapshot restore to alias a\n * loaded image back to its original tag. */\nexport const tagImage = (\n\tsrc: string,\n\tdst: string,\n\topts: TagImageOptions = {},\n): Effect.Effect<void, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('tag', [src, dst]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.tag')),\n\t\t);\n\t\tif (res.exitCode !== 0) {\n\t\t\tif (isImageNotFoundStderr(res.stderr)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew ImageNotFound({ ref: src, detail: `tag source missing: ${res.stderr}` }),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn yield* Effect.fail(new ImageTagFailed({ src, dst, stderr: res.stderr }));\n\t\t}\n\t\tif (opts.removeSourceAfterTag === true && src !== dst) {\n\t\t\tyield* cleanupSnapshotTempTag(src);\n\t\t}\n\t}).pipe(Effect.withSpan('runtime.docker.image.tag'));\n\nexport const removeImage = (\n\tref: string,\n): Effect.Effect<void, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('image', ['rm', '-f', ref]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.image.rm')),\n\t\t);\n\t\tif (res.exitCode === 0 || isMissingImageStderr(res.stderr)) return;\n\t\treturn yield* Effect.fail(\n\t\t\tnew ImageRemoveFailed({\n\t\t\t\tref,\n\t\t\t\tstderr: res.stderr,\n\t\t\t\texitCode: res.exitCode,\n\t\t\t}),\n\t\t);\n\t}).pipe(Effect.withSpan('runtime.docker.image.remove'));\n\n// -----------------------------------------------------------------------------\n// Save (image → tar stream)\n// -----------------------------------------------------------------------------\n\n/** Stream the bytes of `docker save <ref>` to the consumer. Stdout\n * stays streaming so large images never materialise in memory; stderr\n * and exit are drained concurrently so a missing image cannot look\n * like a successful empty tar. */\nexport const saveImages = (\n\trefs: ReadonlyArray<string>,\n\topts: SaveImageOptions = {},\n): Stream.Stream<Uint8Array, DockerRuntimeError, DockerHost | DockerSpawner> => {\n\tconst mapSpawnError = (cause: unknown): DockerRuntimeError =>\n\t\tnew ImageSaveFailed({\n\t\t\tref: refs.join(' '),\n\t\t\tdetail:\n\t\t\t\trefs.length === 0 ? 'docker save called with no image refs' : 'docker save spawn failed',\n\t\t\tcause,\n\t\t});\n\tif (refs.length === 0) {\n\t\treturn Stream.fail(mapSpawnError(new Error('empty docker save ref list')));\n\t}\n\treturn Stream.unwrap(\n\t\tEffect.gen(function* () {\n\t\t\tconst host = yield* DockerHost;\n\t\t\tconst spawner = yield* DockerSpawner;\n\t\t\tconst cmd = dockerCommand(host, 'save', refs);\n\t\t\tconst handle = yield* spawner.spawn(cmd).pipe(Effect.mapError(mapSpawnError));\n\t\t\t// `Stream.unwrap` IS scope-binding in v4 (STYLE_GUIDE §1): the\n\t\t\t// surrounding Effect.gen runs under the consuming stream's\n\t\t\t// scope, so `forkScoped` ties these helper fibers to that\n\t\t\t// scope. With `forkChild` (unscoped) a caller that errors\n\t\t\t// before draining the returned stream would leak the\n\t\t\t// stderr/exit fibers — `Stream.ensuring(cleanup)` only runs\n\t\t\t// once the stream is actually consumed.\n\t\t\tconst stderrFiber = yield* Effect.forkScoped(\n\t\t\t\tStream.mkString(Stream.decodeText(handle.stderr)).pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageSaveFailed({\n\t\t\t\t\t\t\t\tref: refs.join(' '),\n\t\t\t\t\t\t\t\tdetail: 'docker save stderr drain failed',\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst exitFiber = yield* Effect.forkScoped(\n\t\t\t\thandle.exitCode.pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageSaveFailed({\n\t\t\t\t\t\t\t\tref: refs.join(' '),\n\t\t\t\t\t\t\t\tdetail: 'docker save exit failed',\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst checkExit = Effect.gen(function* () {\n\t\t\t\tconst [stderrText, exitCode] = yield* Effect.all(\n\t\t\t\t\t[Fiber.join(stderrFiber), Fiber.join(exitFiber)] as const,\n\t\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t\t);\n\t\t\t\tif (exitCode !== 0) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew ImageSaveFailed({\n\t\t\t\t\t\t\tref: refs.join(' '),\n\t\t\t\t\t\t\tdetail: `docker save exited ${exitCode}${\n\t\t\t\t\t\t\t\tstderrText.length > 0 ? `: ${stderrText}` : ''\n\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\t\t\tconst cleanupTag =\n\t\t\t\topts.removeAfterSave === true\n\t\t\t\t\t? Effect.forEach(refs, cleanupSnapshotTempTag, { concurrency: 'unbounded' }).pipe(\n\t\t\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\t\t)\n\t\t\t\t\t: Effect.void;\n\t\t\tconst cleanup = Effect.all(\n\t\t\t\t[cleanupTag, Fiber.interrupt(stderrFiber), Fiber.interrupt(exitFiber)],\n\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t).pipe(Effect.asVoid);\n\t\t\treturn handle.stdout.pipe(\n\t\t\t\tStream.mapError(\n\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\tnew ImageSaveFailed({\n\t\t\t\t\t\t\tref: refs.join(' '),\n\t\t\t\t\t\t\tdetail: 'docker save stdout pipe failed',\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t\tStream.onEnd(checkExit),\n\t\t\t\tStream.ensuring(cleanup),\n\t\t\t);\n\t\t}),\n\t);\n};\n\nexport const saveImage = (\n\tref: string,\n\topts: SaveImageOptions = {},\n): Stream.Stream<Uint8Array, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tsaveImages([ref], opts);\n\n// -----------------------------------------------------------------------------\n// Load (tar stream → image)\n// -----------------------------------------------------------------------------\n\n/** Parse `docker load` stdout lines to extract the loaded image refs.\n * Docker's output is one of:\n * - `Loaded image: <tag>`\n * - `Loaded image ID: sha256:<digest>`\n * Exported for direct testing without a docker dependency. */\nexport const parseLoadedRefs = (\n\tstdout: string,\n): ReadonlyArray<{ readonly tag?: string; readonly digest?: string }> => {\n\tconst refs: Array<{ tag?: string; digest?: string }> = [];\n\tfor (const rawLine of stdout.split(/\\r?\\n/)) {\n\t\tconst line = rawLine.trim();\n\t\tconst taggedMatch = /^Loaded image: (\\S+)$/.exec(line);\n\t\tif (taggedMatch?.[1]) {\n\t\t\trefs.push({ tag: taggedMatch[1] });\n\t\t\tcontinue;\n\t\t}\n\t\tconst digestMatch = /^Loaded image ID: (sha256:\\S+)$/.exec(line);\n\t\tif (digestMatch?.[1]) {\n\t\t\trefs.push({ digest: digestMatch[1] });\n\t\t}\n\t}\n\treturn refs;\n};\n\n/** `docker load < <tar-stream>`. Returns every freshly-loaded ref\n * Docker reported on stdout. Symmetric with `saveImage`/`saveImages`.\n * Upstream stream errors (e.g. file-read failures from a snapshot\n * tar) are projected to `ImageLoadFailed` so callers see one shape.\n *\n * Stdin lifetime: the supplied stream is fed in via the spawner's\n * `stdin` sink under the same scope as the spawn itself; both close\n * when the child exits. */\nexport const loadImage = (\n\ttar: Stream.Stream<Uint8Array, unknown>,\n): Effect.Effect<LoadedImageBundle, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst host = yield* DockerHost;\n\t\tconst spawner = yield* DockerSpawner;\n\t\tconst cmd = dockerCommand(host, 'load', []);\n\t\tconst mapSpawnError = (cause: unknown): DockerRuntimeError =>\n\t\t\tnew ImageLoadFailed({ detail: 'docker load spawn failed', cause });\n\t\treturn yield* Effect.scoped(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst handle = yield* spawner.spawn(cmd).pipe(Effect.mapError(mapSpawnError));\n\t\t\t\t// Concurrently:\n\t\t\t\t// - pump the supplied tar into stdin\n\t\t\t\t// - drain stdout (the \"Loaded image: …\" lines)\n\t\t\t\t// - drain stderr\n\t\t\t\t// - wait for exit\n\t\t\t\tconst writeStdin = Stream.run(tar, handle.stdin).pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause: unknown): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageLoadFailed({ detail: 'docker load stdin pipe failed', cause }),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tconst stdoutDrain = Stream.mkString(Stream.decodeText(handle.stdout)).pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageLoadFailed({ detail: 'docker load stdout drain failed', cause }),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tconst stderrDrain = Stream.mkString(Stream.decodeText(handle.stderr)).pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageLoadFailed({ detail: 'docker load stderr drain failed', cause }),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tconst exit = handle.exitCode.pipe(\n\t\t\t\t\tEffect.mapError(\n\t\t\t\t\t\t(cause): DockerRuntimeError =>\n\t\t\t\t\t\t\tnew ImageLoadFailed({ detail: 'docker load exit failed', cause }),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tconst [, stdoutText, stderrText, exitCode] = yield* Effect.all(\n\t\t\t\t\t[writeStdin, stdoutDrain, stderrDrain, exit] as const,\n\t\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t\t);\n\t\t\t\tif (exitCode !== 0) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew ImageLoadFailed({\n\t\t\t\t\t\t\tdetail: `docker load exited ${exitCode}`,\n\t\t\t\t\t\t\tstderr: stderrText,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst parsed = parseLoadedRefs(stdoutText);\n\t\t\t\tif (parsed.length === 0) {\n\t\t\t\t\t// Include the trailing ~500 bytes of stdout so newer\n\t\t\t\t\t// Docker variants that interleave progress markers\n\t\t\t\t\t// (e.g. JSON status lines) surface the actual output\n\t\t\t\t\t// shape instead of an opaque \"no Loaded image lines\n\t\t\t\t\t// found\". The full stderr text is still carried for\n\t\t\t\t\t// the rare case docker emitted a diagnostic there.\n\t\t\t\t\tconst stdoutTail = tailBytes(stdoutText, 500);\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\tnew ImageLoadFailed({\n\t\t\t\t\t\t\tdetail: `docker load succeeded but no Loaded image lines found; stdout tail: ${stdoutTail}`,\n\t\t\t\t\t\t\tstderr: stderrText,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst refs = yield* Effect.forEach(\n\t\t\t\t\tparsed,\n\t\t\t\t\t(ref) =>\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (ref.digest !== undefined) {\n\t\t\t\t\t\t\t\treturn refOf(ref.digest, ref.tag);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst digest = yield* imageExists(ref.tag!);\n\t\t\t\t\t\t\tif (digest === null) {\n\t\t\t\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\t\t\t\tnew ImageLoadFailed({\n\t\t\t\t\t\t\t\t\t\tdetail: `loaded image '${ref.tag}' not visible to inspect`,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn refOf(digest, ref.tag);\n\t\t\t\t\t\t}),\n\t\t\t\t\t{ concurrency: 'unbounded' },\n\t\t\t\t);\n\t\t\t\treturn { refs };\n\t\t\t}),\n\t\t);\n\t}).pipe(Effect.withSpan('runtime.docker.image.load'));\n\n// -----------------------------------------------------------------------------\n// Cache integration\n// -----------------------------------------------------------------------------\n\nexport interface CachedBuildKey {\n\treadonly namespace: string;\n\treadonly chain: ChainId;\n\treadonly contentHash: ContentHash;\n}\n\n/** Content-addressed image build:\n *\n * 1. cache lookup keyed by (namespace, chainId, contentHash)\n * 2. on hit, verify the on-host tag still resolves; if so return\n * 3. on miss, run `docker build`, then write the resolved digest\n * to the cache (best-effort — write failure does not roll back\n * the on-host build).\n *\n * Caller-supplied tag is the content-addressed name; the cache key\n * is independent so an unrelated invalidation (e.g. base-image\n * bump) can drop the cache without disturbing the tag scheme. */\nexport const ensureImageCached = (\n\topts: BuildOptions,\n\tkey: CachedBuildKey,\n): Effect.Effect<string, DockerRuntimeError, CacheService | DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst cache = yield* CacheService;\n\t\tconst hit = yield* cache\n\t\t\t.lookup({ namespace: key.namespace, chain: key.chain, contentHash: key.contentHash })\n\t\t\t.pipe(\n\t\t\t\t// Cache-side failures collapse to MISS — the contract is\n\t\t\t\t// best-effort. We still build, but logDebug so the rare\n\t\t\t\t// cache-read failure is visible.\n\t\t\t\tEffect.tapCause((cause) =>\n\t\t\t\t\tEffect.logDebug('image cache lookup failed; rebuilding', { key, cause }),\n\t\t\t\t),\n\t\t\t\tEffect.catch(() => Effect.succeed(null)),\n\t\t\t);\n\t\tif (hit !== null) {\n\t\t\tconst cached = decodeDigest(hit.bytes);\n\t\t\tif (cached !== null) {\n\t\t\t\t// Verify the on-host tag still resolves to something —\n\t\t\t\t// the cached digest may have been pruned out from under us.\n\t\t\t\tconst existing = yield* imageExists(opts.tag);\n\t\t\t\tif (existing !== null) {\n\t\t\t\t\treturn existing;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// On-host tag-exists short-circuit (defence-in-depth).\n\t\tconst onHost = yield* imageExists(opts.tag);\n\t\tif (onHost !== null) {\n\t\t\tyield* cache\n\t\t\t\t.write(\n\t\t\t\t\t{ namespace: key.namespace, chain: key.chain, contentHash: key.contentHash },\n\t\t\t\t\tencodeDigest(onHost),\n\t\t\t\t)\n\t\t\t\t.pipe(\n\t\t\t\t\tEffect.tapCause((cause) =>\n\t\t\t\t\t\tEffect.logDebug('image cache write (on-host hit) failed', { key, cause }),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.catch(() => Effect.void),\n\t\t\t\t);\n\t\t\treturn onHost;\n\t\t}\n\t\tconst digest = yield* build(opts);\n\t\tyield* cache\n\t\t\t.write(\n\t\t\t\t{ namespace: key.namespace, chain: key.chain, contentHash: key.contentHash },\n\t\t\t\tencodeDigest(digest),\n\t\t\t)\n\t\t\t.pipe(\n\t\t\t\tEffect.tapCause((cause) =>\n\t\t\t\t\tEffect.logDebug('image cache write (post-build) failed', { key, cause }),\n\t\t\t\t),\n\t\t\t\tEffect.catch(() => Effect.void),\n\t\t\t);\n\t\treturn digest;\n\t}).pipe(Effect.withSpan('runtime.docker.image.ensureCached'));\n\n// -----------------------------------------------------------------------------\n// Contract-shaped ImageRef helpers\n// -----------------------------------------------------------------------------\n\nexport const refOf = (digest: string, tag?: string): ImageRef =>\n\ttag !== undefined ? { digest, tag } : { digest };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "./client.mjs";
|
|
2
|
+
import "./errors.mjs";
|
|
3
|
+
import "./wrap.mjs";
|
|
4
|
+
import "./labels.mjs";
|
|
5
|
+
import "./network.mjs";
|
|
6
|
+
import "./container.mjs";
|
|
7
|
+
import "./exec.mjs";
|
|
8
|
+
import "./image.mjs";
|
|
9
|
+
import "./inventory.mjs";
|
|
10
|
+
import "./logs.mjs";
|
|
11
|
+
import "./volume.mjs";
|
|
12
|
+
import "./sweep.mjs";
|
|
13
|
+
import "./service.mjs";
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { decodeJsonArrayElementSync } from "../../substrate/runtime/runtime-decode.mjs";
|
|
2
|
+
import { DaemonUnreachable, DockerInspectDecodeFailed, DockerInspectFailed } from "./errors.mjs";
|
|
3
|
+
import { isDaemonUnreachableStderr } from "./wrap.mjs";
|
|
4
|
+
import { Effect } from "effect";
|
|
5
|
+
//#region src/runtime/docker/inspect-and-decode.ts
|
|
6
|
+
/** Run `docker inspect`, classify the exit code, and decode the
|
|
7
|
+
* JSON-array element. Returns `null` when the resource is missing
|
|
8
|
+
* per the caller-supplied stderr classifier; otherwise returns the
|
|
9
|
+
* decoded schema value. All failure modes surface as
|
|
10
|
+
* `DockerRuntimeError` (`DaemonUnreachable`, `DockerInspectFailed`,
|
|
11
|
+
* or `DockerInspectDecodeFailed`). */
|
|
12
|
+
const dockerInspectAndDecode = (options) => Effect.gen(function* () {
|
|
13
|
+
const res = yield* options.inspectCommand;
|
|
14
|
+
if (res.exitCode !== 0) {
|
|
15
|
+
if (options.isMissingStderr(res.stderr)) return null;
|
|
16
|
+
if (isDaemonUnreachableStderr(res.stderr)) return yield* Effect.fail(new DaemonUnreachable({
|
|
17
|
+
op: options.op,
|
|
18
|
+
detail: "docker daemon unreachable"
|
|
19
|
+
}));
|
|
20
|
+
return yield* Effect.fail(new DockerInspectFailed({
|
|
21
|
+
resource: options.resourceKind,
|
|
22
|
+
name: options.name,
|
|
23
|
+
stderr: res.stderr,
|
|
24
|
+
exitCode: res.exitCode
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
const malformedDetail = options.malformedDetail ?? `inspect returned malformed ${options.resourceKind} JSON`;
|
|
28
|
+
return yield* Effect.try({
|
|
29
|
+
try: () => decodeJsonArrayElementSync(options.schema, res.stdout, {
|
|
30
|
+
source: `docker ${options.resourceKind} inspect ${options.name}`,
|
|
31
|
+
missingMessage: "inspect returned an empty result",
|
|
32
|
+
index: options.index,
|
|
33
|
+
mkError: (issue) => new DockerInspectDecodeFailed({
|
|
34
|
+
resource: options.resourceKind,
|
|
35
|
+
name: options.name,
|
|
36
|
+
detail: issue.message === "inspect returned an empty result" ? issue.message : malformedDetail,
|
|
37
|
+
cause: issue.cause
|
|
38
|
+
})
|
|
39
|
+
}),
|
|
40
|
+
catch: (cause) => cause instanceof DockerInspectDecodeFailed ? cause : new DockerInspectDecodeFailed({
|
|
41
|
+
resource: options.resourceKind,
|
|
42
|
+
name: options.name,
|
|
43
|
+
detail: `unexpected defect while decoding ${options.resourceKind} inspect`,
|
|
44
|
+
cause
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
//#endregion
|
|
49
|
+
export { dockerInspectAndDecode };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=inspect-and-decode.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspect-and-decode.mjs","names":[],"sources":["../../../src/runtime/docker/inspect-and-decode.ts"],"sourcesContent":["// `docker <resource> inspect` shared decode pipeline.\n//\n// Three Docker subsystem files (`container.ts`, `network.ts`) inline the\n// SAME post-`docker inspect` shape:\n//\n// 1. examine `CaptureResult.exitCode`\n// 2. on non-zero stderr, classify into `<missing>` (caller-specific),\n// `DaemonUnreachable`, or `DockerInspectFailed`\n// 3. on zero exit, decode the JSON-array element via\n// `decodeJsonArrayElementSync`, wrapping decode issues in\n// `DockerInspectDecodeFailed`\n//\n// This module centralises that ladder so the three sites stay byte-\n// identical and new resource kinds get the same treatment for free.\n//\n// Image inspect is intentionally NOT a consumer: `imageExists` uses\n// `docker inspect --format '{{.Id}}'` (plain stdout, no JSON array),\n// so it never enters this pipeline.\n\nimport { Effect, Schema } from 'effect';\n\nimport { decodeJsonArrayElementSync } from '../../substrate/runtime/runtime-decode.ts';\nimport type { CaptureResult } from '../../substrate/runtime/observability/subprocess-capture.ts';\nimport {\n\tDaemonUnreachable,\n\tDockerInspectDecodeFailed,\n\tDockerInspectFailed,\n\ttype DockerResourceKind,\n\ttype DockerRuntimeError,\n} from './errors.ts';\nimport { isDaemonUnreachableStderr } from './wrap.ts';\n\nexport interface DockerInspectAndDecodeOptions<\n\tS extends Schema.Decoder<unknown>,\n\tR,\n> {\n\t/** Resource kind, threaded into `DockerInspectFailed` /\n\t * `DockerInspectDecodeFailed` error envelopes. */\n\treadonly resourceKind: DockerResourceKind;\n\t/** Resource name (container, network, volume) being inspected.\n\t * Threaded into the error envelopes and the decode `source`. */\n\treadonly name: string;\n\t/** Op label for the `DaemonUnreachable` envelope.\n\t * e.g. `'docker.network.inspect'`. */\n\treadonly op: string;\n\t/** Pre-wrapped `docker <resource> inspect <name>` invocation. The\n\t * caller is responsible for the `mapError` step (each site has its\n\t * own wrap.ts classifier) so this Effect already produces typed\n\t * `DockerRuntimeError`s for spawn-side failures. */\n\treadonly inspectCommand: Effect.Effect<CaptureResult, DockerRuntimeError, R>;\n\t/** Schema for the JSON-array element returned by `docker inspect`. */\n\treadonly schema: S;\n\t/** Stderr classifier for the resource-specific \"missing\" case (e.g.\n\t * `No such container`). When this returns true on a non-zero exit,\n\t * the helper returns `null` to the caller; the caller decides what\n\t * the contract-level missing return value should be (`null`, `[]`,\n\t * …). */\n\treadonly isMissingStderr: (stderr: string) => boolean;\n\t/** Optional override for the malformed-JSON decode error detail.\n\t * Sites use slightly different wording (e.g. \"malformed container\n\t * JSON\" vs \"malformed network JSON\"); defaults to a generic\n\t * resource-kind-derived string. */\n\treadonly malformedDetail?: string;\n\t/** Optional array index; the universal pattern is `0` (single-\n\t * element array from `docker inspect`). */\n\treadonly index?: number;\n}\n\n/** Run `docker inspect`, classify the exit code, and decode the\n * JSON-array element. Returns `null` when the resource is missing\n * per the caller-supplied stderr classifier; otherwise returns the\n * decoded schema value. All failure modes surface as\n * `DockerRuntimeError` (`DaemonUnreachable`, `DockerInspectFailed`,\n * or `DockerInspectDecodeFailed`). */\nexport const dockerInspectAndDecode = <S extends Schema.Decoder<unknown>, R>(\n\toptions: DockerInspectAndDecodeOptions<S, R>,\n): Effect.Effect<S['Type'] | null, DockerRuntimeError, R> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* options.inspectCommand;\n\t\tif (res.exitCode !== 0) {\n\t\t\tif (options.isMissingStderr(res.stderr)) return null;\n\t\t\tif (isDaemonUnreachableStderr(res.stderr)) {\n\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\tnew DaemonUnreachable({\n\t\t\t\t\t\top: options.op,\n\t\t\t\t\t\tdetail: 'docker daemon unreachable',\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn yield* Effect.fail(\n\t\t\t\tnew DockerInspectFailed({\n\t\t\t\t\tresource: options.resourceKind,\n\t\t\t\t\tname: options.name,\n\t\t\t\t\tstderr: res.stderr,\n\t\t\t\t\texitCode: res.exitCode,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\t// `decodeJsonArrayElementSync`'s `mkError` produces a typed\n\t\t// `DockerInspectDecodeFailed`; we route it through `Effect.try`\n\t\t// so the error channel stays typed. The `catch` narrows on\n\t\t// `instanceof` so that any UNEXPECTED sync defect thrown from\n\t\t// inside the decode body (e.g. a reader helper bug) is wrapped\n\t\t// in a fresh envelope rather than cast-lied as the wrong type.\n\t\tconst malformedDetail =\n\t\t\toptions.malformedDetail ??\n\t\t\t`inspect returned malformed ${options.resourceKind} JSON`;\n\t\treturn yield* Effect.try({\n\t\t\ttry: (): S['Type'] =>\n\t\t\t\tdecodeJsonArrayElementSync(options.schema, res.stdout, {\n\t\t\t\t\tsource: `docker ${options.resourceKind} inspect ${options.name}`,\n\t\t\t\t\tmissingMessage: 'inspect returned an empty result',\n\t\t\t\t\tindex: options.index,\n\t\t\t\t\tmkError: (issue) =>\n\t\t\t\t\t\tnew DockerInspectDecodeFailed({\n\t\t\t\t\t\t\tresource: options.resourceKind,\n\t\t\t\t\t\t\tname: options.name,\n\t\t\t\t\t\t\tdetail:\n\t\t\t\t\t\t\t\tissue.message === 'inspect returned an empty result'\n\t\t\t\t\t\t\t\t\t? issue.message\n\t\t\t\t\t\t\t\t\t: malformedDetail,\n\t\t\t\t\t\t\tcause: issue.cause,\n\t\t\t\t\t\t}),\n\t\t\t\t}),\n\t\t\tcatch: (cause): DockerInspectDecodeFailed =>\n\t\t\t\tcause instanceof DockerInspectDecodeFailed\n\t\t\t\t\t? cause\n\t\t\t\t\t: new DockerInspectDecodeFailed({\n\t\t\t\t\t\t\tresource: options.resourceKind,\n\t\t\t\t\t\t\tname: options.name,\n\t\t\t\t\t\t\tdetail: `unexpected defect while decoding ${options.resourceKind} inspect`,\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t}),\n\t\t});\n\t});\n"],"mappings":";;;;;;;;;;;AA0EA,MAAa,0BACZ,YAEA,OAAO,IAAI,aAAa;CACvB,MAAM,MAAM,OAAO,QAAQ;AAC3B,KAAI,IAAI,aAAa,GAAG;AACvB,MAAI,QAAQ,gBAAgB,IAAI,OAAO,CAAE,QAAO;AAChD,MAAI,0BAA0B,IAAI,OAAO,CACxC,QAAO,OAAO,OAAO,KACpB,IAAI,kBAAkB;GACrB,IAAI,QAAQ;GACZ,QAAQ;GACR,CAAC,CACF;AAEF,SAAO,OAAO,OAAO,KACpB,IAAI,oBAAoB;GACvB,UAAU,QAAQ;GAClB,MAAM,QAAQ;GACd,QAAQ,IAAI;GACZ,UAAU,IAAI;GACd,CAAC,CACF;;CAQF,MAAM,kBACL,QAAQ,mBACR,8BAA8B,QAAQ,aAAa;AACpD,QAAO,OAAO,OAAO,IAAI;EACxB,WACC,2BAA2B,QAAQ,QAAQ,IAAI,QAAQ;GACtD,QAAQ,UAAU,QAAQ,aAAa,WAAW,QAAQ;GAC1D,gBAAgB;GAChB,OAAO,QAAQ;GACf,UAAU,UACT,IAAI,0BAA0B;IAC7B,UAAU,QAAQ;IAClB,MAAM,QAAQ;IACd,QACC,MAAM,YAAY,qCACf,MAAM,UACN;IACJ,OAAO,MAAM;IACb,CAAC;GACH,CAAC;EACH,QAAQ,UACP,iBAAiB,4BACd,QACA,IAAI,0BAA0B;GAC9B,UAAU,QAAQ;GAClB,MAAM,QAAQ;GACd,QAAQ,oCAAoC,QAAQ,aAAa;GACjE;GACA,CAAC;EACL,CAAC;EACD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { decodeJsonTextSync } from "../../substrate/runtime/runtime-decode.mjs";
|
|
2
2
|
import { dockerRunOk } from "./client.mjs";
|
|
3
|
-
import { LabelKey, renderFilterArgs } from "./labels.mjs";
|
|
4
3
|
import { wrapGeneric } from "./wrap.mjs";
|
|
4
|
+
import { LabelKey, renderFilterArgs } from "./labels.mjs";
|
|
5
5
|
import { Effect, Schema } from "effect";
|
|
6
6
|
//#region src/runtime/docker/inventory.ts
|
|
7
7
|
const PsLine = Schema.Struct({
|
|
@@ -85,12 +85,6 @@ const toVolumeSummaries = (stdout) => {
|
|
|
85
85
|
}));
|
|
86
86
|
};
|
|
87
87
|
const devstackAppFilterArgs = ["--filter", `label=${LabelKey.app}`];
|
|
88
|
-
const devstackRouterFilterArgs = [
|
|
89
|
-
"--filter",
|
|
90
|
-
`label=${LabelKey.managed}=true`,
|
|
91
|
-
"--filter",
|
|
92
|
-
`label=${LabelKey.routerMarker}=true`
|
|
93
|
-
];
|
|
94
88
|
const listContainers = (match) => Effect.gen(function* () {
|
|
95
89
|
return toContainerSummaries((yield* dockerRunOk("ps", [
|
|
96
90
|
"-a",
|
|
@@ -107,14 +101,23 @@ const listDevstackContainers = () => Effect.gen(function* () {
|
|
|
107
101
|
...devstackAppFilterArgs
|
|
108
102
|
]).pipe(Effect.mapError(wrapGeneric("docker.ps")))).stdout);
|
|
109
103
|
}).pipe(Effect.withSpan("runtime.docker.inventory.devstackContainers"));
|
|
110
|
-
|
|
104
|
+
/** List devstack-managed containers whose generic `kind` label matches.
|
|
105
|
+
*
|
|
106
|
+
* Orchestrators that own a slot of named containers (e.g. the router
|
|
107
|
+
* orchestrator stamps `kind=router`) use this to enumerate their own
|
|
108
|
+
* fleet without L1 learning the orchestrator's name. The L1 lister
|
|
109
|
+
* takes a STRING — it never branches on the value. */
|
|
110
|
+
const listDevstackContainersByKind = (kind) => Effect.gen(function* () {
|
|
111
111
|
return toContainerSummaries((yield* dockerRunOk("ps", [
|
|
112
112
|
"-a",
|
|
113
113
|
"--format",
|
|
114
114
|
"{{json .}}",
|
|
115
|
-
|
|
115
|
+
"--filter",
|
|
116
|
+
`label=${LabelKey.managed}=true`,
|
|
117
|
+
"--filter",
|
|
118
|
+
`label=${LabelKey.kind}=${kind}`
|
|
116
119
|
]).pipe(Effect.mapError(wrapGeneric("docker.ps")))).stdout);
|
|
117
|
-
}).pipe(Effect.withSpan("runtime.docker.inventory.
|
|
120
|
+
}).pipe(Effect.withSpan("runtime.docker.inventory.devstackContainersByKind"));
|
|
118
121
|
const listImages = (match) => Effect.gen(function* () {
|
|
119
122
|
return toImageSummaries((yield* dockerRunOk("images", [
|
|
120
123
|
"--format",
|
|
@@ -162,6 +165,6 @@ const listDevstackVolumes = () => Effect.gen(function* () {
|
|
|
162
165
|
]).pipe(Effect.mapError(wrapGeneric("docker.volume.ls")))).stdout);
|
|
163
166
|
}).pipe(Effect.withSpan("runtime.docker.inventory.devstackVolumes"));
|
|
164
167
|
//#endregion
|
|
165
|
-
export { listContainers, listDevstackContainers, listDevstackImages, listDevstackNetworks,
|
|
168
|
+
export { listContainers, listDevstackContainers, listDevstackContainersByKind, listDevstackImages, listDevstackNetworks, listDevstackVolumes, listImages, listNetworks, listVolumes };
|
|
166
169
|
|
|
167
170
|
//# sourceMappingURL=inventory.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inventory.mjs","names":[],"sources":["../../../src/runtime/docker/inventory.ts"],"sourcesContent":["// Label-driven inventory.\n//\n// Architecture § Container runtime § Label-driven inventory:\n// inventory NEVER greps stdout for container names; it ALWAYS\n// filters by labels via `--filter label=...`. The output format is\n// the `--format` JSON line shape, which we parse line-by-line.\n//\n// Surface:\n// - `listContainers(match)` → ContainerSummary[]\n// - `listImages(match)` → ImageSummary[]\n// - `listNetworks(match)` → NetworkSummary[]\n// - `listVolumes(match)` → VolumeSummary[]\n\nimport { Effect, Schema } from 'effect';\n\nimport type { ContainerLabelTuple } from '../../contracts/snapshotable.ts';\nimport { decodeJsonTextSync } from '../../substrate/runtime/runtime-decode.ts';\nimport { DockerHost, DockerSpawner, dockerRunOk } from './client.ts';\nimport type { DockerRuntimeError } from './errors.ts';\nimport { LabelKey, renderFilterArgs } from './labels.ts';\nimport { wrapGeneric } from './wrap.ts';\n\n// -----------------------------------------------------------------------------\n// Summary shapes — what the label-filter listers return\n// -----------------------------------------------------------------------------\n\nexport interface ContainerSummary {\n\treadonly id: string;\n\treadonly name: string;\n\treadonly image: string;\n\treadonly status: string;\n\treadonly state: string;\n\treadonly labels: Readonly<Record<string, string>>;\n}\n\nexport interface ImageSummary {\n\treadonly id: string;\n\treadonly tag: string;\n\treadonly labels: Readonly<Record<string, string>>;\n}\n\nexport interface NetworkSummary {\n\treadonly id: string;\n\treadonly name: string;\n\treadonly driver: string;\n\treadonly labels: Readonly<Record<string, string>>;\n}\n\nexport interface VolumeSummary {\n\treadonly name: string;\n\treadonly driver: string;\n\treadonly mountpoint: string;\n\treadonly labels: Readonly<Record<string, string>>;\n}\n\n// -----------------------------------------------------------------------------\n// JSON-line schemas (docker --format '{{json .}}')\n// -----------------------------------------------------------------------------\n\nconst PsLine = Schema.Struct({\n\tID: Schema.String,\n\tNames: Schema.String,\n\tImage: Schema.String,\n\tStatus: Schema.String,\n\tState: Schema.String,\n\tLabels: Schema.optional(Schema.String),\n});\n\nconst ImagesLine = Schema.Struct({\n\tID: Schema.String,\n\tRepository: Schema.String,\n\tTag: Schema.String,\n\tLabels: Schema.optional(Schema.String),\n});\n\nconst NetworksLine = Schema.Struct({\n\tID: Schema.String,\n\tName: Schema.String,\n\tDriver: Schema.String,\n\tLabels: Schema.optional(Schema.String),\n});\n\nconst VolumesLine = Schema.Struct({\n\tName: Schema.String,\n\tDriver: Schema.String,\n\tMountpoint: Schema.String,\n\tLabels: Schema.optional(Schema.String),\n});\n\n// -----------------------------------------------------------------------------\n// Label parsing — docker's `Labels` field is a comma-joined `k=v,k=v`\n// -----------------------------------------------------------------------------\n\nconst parseLabelString = (s: string | undefined): Record<string, string> => {\n\tif (!s) return {};\n\tconst out: Record<string, string> = {};\n\tfor (const part of s.split(',')) {\n\t\tconst i = part.indexOf('=');\n\t\tif (i < 0) continue;\n\t\tout[part.slice(0, i).trim()] = part.slice(i + 1).trim();\n\t}\n\treturn out;\n};\n\nconst parseJsonLines = <S extends Schema.Decoder<unknown>>(\n\tschema: S,\n\tstdout: string,\n): ReadonlyArray<S['Type']> => {\n\tconst lines = stdout.split('\\n').filter((l) => l.trim().length > 0);\n\tconst out: Array<S['Type']> = [];\n\tfor (const [index, line] of lines.entries()) {\n\t\ttry {\n\t\t\tout.push(\n\t\t\t\tdecodeJsonTextSync(schema, line, {\n\t\t\t\t\tsource: `docker inventory:${index + 1}`,\n\t\t\t\t\tmkError: (issue) => issue,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch {\n\t\t\t// Malformed line — skip. Inventory is best-effort; a single\n\t\t\t// torn line shouldn't poison the whole sweep.\n\t\t}\n\t}\n\treturn out;\n};\n\n// -----------------------------------------------------------------------------\n// Listers\n// -----------------------------------------------------------------------------\n\nconst toContainerSummaries = (stdout: string): ReadonlyArray<ContainerSummary> => {\n\tconst lines = parseJsonLines(PsLine, stdout);\n\treturn lines.map(\n\t\t(l): ContainerSummary => ({\n\t\t\tid: l.ID,\n\t\t\tname: l.Names.split(',')[0] ?? l.Names,\n\t\t\timage: l.Image,\n\t\t\tstatus: l.Status,\n\t\t\tstate: l.State,\n\t\t\tlabels: parseLabelString(l.Labels),\n\t\t}),\n\t);\n};\n\nconst toImageSummaries = (stdout: string): ReadonlyArray<ImageSummary> => {\n\tconst lines = parseJsonLines(ImagesLine, stdout);\n\treturn lines.map(\n\t\t(l): ImageSummary => ({\n\t\t\tid: l.ID,\n\t\t\ttag: `${l.Repository}:${l.Tag}`,\n\t\t\tlabels: parseLabelString(l.Labels),\n\t\t}),\n\t);\n};\n\nconst toNetworkSummaries = (stdout: string): ReadonlyArray<NetworkSummary> => {\n\tconst lines = parseJsonLines(NetworksLine, stdout);\n\treturn lines.map(\n\t\t(l): NetworkSummary => ({\n\t\t\tid: l.ID,\n\t\t\tname: l.Name,\n\t\t\tdriver: l.Driver,\n\t\t\tlabels: parseLabelString(l.Labels),\n\t\t}),\n\t);\n};\n\nconst toVolumeSummaries = (stdout: string): ReadonlyArray<VolumeSummary> => {\n\tconst lines = parseJsonLines(VolumesLine, stdout);\n\treturn lines.map(\n\t\t(l): VolumeSummary => ({\n\t\t\tname: l.Name,\n\t\t\tdriver: l.Driver,\n\t\t\tmountpoint: l.Mountpoint,\n\t\t\tlabels: parseLabelString(l.Labels),\n\t\t}),\n\t);\n};\n\nconst devstackAppFilterArgs: ReadonlyArray<string> = ['--filter', `label=${LabelKey.app}`];\nconst devstackRouterFilterArgs: ReadonlyArray<string> = [\n\t'--filter',\n\t`label=${LabelKey.managed}=true`,\n\t'--filter',\n\t`label=${LabelKey.routerMarker}=true`,\n];\n\nexport const listContainers = (\n\tmatch: Partial<ContainerLabelTuple>,\n): Effect.Effect<ReadonlyArray<ContainerSummary>, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst filters = renderFilterArgs(match);\n\t\tconst res = yield* dockerRunOk('ps', ['-a', '--format', '{{json .}}', ...filters]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.ps')),\n\t\t);\n\t\treturn toContainerSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.containers'));\n\nexport const listDevstackContainers = (): Effect.Effect<\n\tReadonlyArray<ContainerSummary>,\n\tDockerRuntimeError,\n\tDockerHost | DockerSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('ps', [\n\t\t\t'-a',\n\t\t\t'--format',\n\t\t\t'{{json .}}',\n\t\t\t...devstackAppFilterArgs,\n\t\t]).pipe(Effect.mapError(wrapGeneric('docker.ps')));\n\t\treturn toContainerSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.devstackContainers'));\n\nexport const listDevstackRouterContainers = (): Effect.Effect<\n\tReadonlyArray<ContainerSummary>,\n\tDockerRuntimeError,\n\tDockerHost | DockerSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('ps', [\n\t\t\t'-a',\n\t\t\t'--format',\n\t\t\t'{{json .}}',\n\t\t\t...devstackRouterFilterArgs,\n\t\t]).pipe(Effect.mapError(wrapGeneric('docker.ps')));\n\t\treturn toContainerSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.devstackRouterContainers'));\n\nexport const listImages = (\n\tmatch: Partial<ContainerLabelTuple>,\n): Effect.Effect<ReadonlyArray<ImageSummary>, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst filters = renderFilterArgs(match);\n\t\tconst res = yield* dockerRunOk('images', ['--format', '{{json .}}', ...filters]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.images')),\n\t\t);\n\t\treturn toImageSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.images'));\n\nexport const listDevstackImages = (): Effect.Effect<\n\tReadonlyArray<ImageSummary>,\n\tDockerRuntimeError,\n\tDockerHost | DockerSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('images', [\n\t\t\t'--format',\n\t\t\t'{{json .}}',\n\t\t\t...devstackAppFilterArgs,\n\t\t]).pipe(Effect.mapError(wrapGeneric('docker.images')));\n\t\treturn toImageSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.devstackImages'));\n\nexport const listNetworks = (\n\tmatch: Partial<ContainerLabelTuple>,\n): Effect.Effect<ReadonlyArray<NetworkSummary>, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst filters = renderFilterArgs(match);\n\t\tconst res = yield* dockerRunOk('network', ['ls', '--format', '{{json .}}', ...filters]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.network.ls')),\n\t\t);\n\t\treturn toNetworkSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.networks'));\n\nexport const listDevstackNetworks = (): Effect.Effect<\n\tReadonlyArray<NetworkSummary>,\n\tDockerRuntimeError,\n\tDockerHost | DockerSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('network', [\n\t\t\t'ls',\n\t\t\t'--format',\n\t\t\t'{{json .}}',\n\t\t\t...devstackAppFilterArgs,\n\t\t]).pipe(Effect.mapError(wrapGeneric('docker.network.ls')));\n\t\treturn toNetworkSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.devstackNetworks'));\n\nexport const listVolumes = (\n\tmatch: Partial<ContainerLabelTuple>,\n): Effect.Effect<ReadonlyArray<VolumeSummary>, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst filters = renderFilterArgs(match);\n\t\tconst res = yield* dockerRunOk('volume', ['ls', '--format', '{{json .}}', ...filters]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.volume.ls')),\n\t\t);\n\t\treturn toVolumeSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.volumes'));\n\nexport const listDevstackVolumes = (): Effect.Effect<\n\tReadonlyArray<VolumeSummary>,\n\tDockerRuntimeError,\n\tDockerHost | DockerSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('volume', [\n\t\t\t'ls',\n\t\t\t'--format',\n\t\t\t'{{json .}}',\n\t\t\t...devstackAppFilterArgs,\n\t\t]).pipe(Effect.mapError(wrapGeneric('docker.volume.ls')));\n\t\treturn toVolumeSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.devstackVolumes'));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"inventory.mjs","names":[],"sources":["../../../src/runtime/docker/inventory.ts"],"sourcesContent":["// Label-driven inventory.\n//\n// Architecture § Container runtime § Label-driven inventory:\n// inventory NEVER greps stdout for container names; it ALWAYS\n// filters by labels via `--filter label=...`. The output format is\n// the `--format` JSON line shape, which we parse line-by-line.\n//\n// Surface:\n// - `listContainers(match)` → ContainerSummary[]\n// - `listImages(match)` → ImageSummary[]\n// - `listNetworks(match)` → NetworkSummary[]\n// - `listVolumes(match)` → VolumeSummary[]\n\nimport { Effect, Schema } from 'effect';\n\nimport type { ContainerLabelTuple } from '../../contracts/snapshotable.ts';\nimport { decodeJsonTextSync } from '../../substrate/runtime/runtime-decode.ts';\nimport { DockerHost, DockerSpawner, dockerRunOk } from './client.ts';\nimport type { DockerRuntimeError } from './errors.ts';\nimport { LabelKey, renderFilterArgs } from './labels.ts';\nimport { wrapGeneric } from './wrap.ts';\n\n// -----------------------------------------------------------------------------\n// Summary shapes — what the label-filter listers return\n// -----------------------------------------------------------------------------\n\nexport interface ContainerSummary {\n\treadonly id: string;\n\treadonly name: string;\n\treadonly image: string;\n\treadonly status: string;\n\treadonly state: string;\n\treadonly labels: Readonly<Record<string, string>>;\n}\n\nexport interface ImageSummary {\n\treadonly id: string;\n\treadonly tag: string;\n\treadonly labels: Readonly<Record<string, string>>;\n}\n\nexport interface NetworkSummary {\n\treadonly id: string;\n\treadonly name: string;\n\treadonly driver: string;\n\treadonly labels: Readonly<Record<string, string>>;\n}\n\nexport interface VolumeSummary {\n\treadonly name: string;\n\treadonly driver: string;\n\treadonly mountpoint: string;\n\treadonly labels: Readonly<Record<string, string>>;\n}\n\n// -----------------------------------------------------------------------------\n// JSON-line schemas (docker --format '{{json .}}')\n// -----------------------------------------------------------------------------\n\nconst PsLine = Schema.Struct({\n\tID: Schema.String,\n\tNames: Schema.String,\n\tImage: Schema.String,\n\tStatus: Schema.String,\n\tState: Schema.String,\n\tLabels: Schema.optional(Schema.String),\n});\n\nconst ImagesLine = Schema.Struct({\n\tID: Schema.String,\n\tRepository: Schema.String,\n\tTag: Schema.String,\n\tLabels: Schema.optional(Schema.String),\n});\n\nconst NetworksLine = Schema.Struct({\n\tID: Schema.String,\n\tName: Schema.String,\n\tDriver: Schema.String,\n\tLabels: Schema.optional(Schema.String),\n});\n\nconst VolumesLine = Schema.Struct({\n\tName: Schema.String,\n\tDriver: Schema.String,\n\tMountpoint: Schema.String,\n\tLabels: Schema.optional(Schema.String),\n});\n\n// -----------------------------------------------------------------------------\n// Label parsing — docker's `Labels` field is a comma-joined `k=v,k=v`\n// -----------------------------------------------------------------------------\n\nconst parseLabelString = (s: string | undefined): Record<string, string> => {\n\tif (!s) return {};\n\tconst out: Record<string, string> = {};\n\tfor (const part of s.split(',')) {\n\t\tconst i = part.indexOf('=');\n\t\tif (i < 0) continue;\n\t\tout[part.slice(0, i).trim()] = part.slice(i + 1).trim();\n\t}\n\treturn out;\n};\n\nconst parseJsonLines = <S extends Schema.Decoder<unknown>>(\n\tschema: S,\n\tstdout: string,\n): ReadonlyArray<S['Type']> => {\n\tconst lines = stdout.split('\\n').filter((l) => l.trim().length > 0);\n\tconst out: Array<S['Type']> = [];\n\tfor (const [index, line] of lines.entries()) {\n\t\ttry {\n\t\t\tout.push(\n\t\t\t\tdecodeJsonTextSync(schema, line, {\n\t\t\t\t\tsource: `docker inventory:${index + 1}`,\n\t\t\t\t\tmkError: (issue) => issue,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch {\n\t\t\t// Malformed line — skip. Inventory is best-effort; a single\n\t\t\t// torn line shouldn't poison the whole sweep.\n\t\t}\n\t}\n\treturn out;\n};\n\n// -----------------------------------------------------------------------------\n// Listers\n// -----------------------------------------------------------------------------\n\nconst toContainerSummaries = (stdout: string): ReadonlyArray<ContainerSummary> => {\n\tconst lines = parseJsonLines(PsLine, stdout);\n\treturn lines.map(\n\t\t(l): ContainerSummary => ({\n\t\t\tid: l.ID,\n\t\t\tname: l.Names.split(',')[0] ?? l.Names,\n\t\t\timage: l.Image,\n\t\t\tstatus: l.Status,\n\t\t\tstate: l.State,\n\t\t\tlabels: parseLabelString(l.Labels),\n\t\t}),\n\t);\n};\n\nconst toImageSummaries = (stdout: string): ReadonlyArray<ImageSummary> => {\n\tconst lines = parseJsonLines(ImagesLine, stdout);\n\treturn lines.map(\n\t\t(l): ImageSummary => ({\n\t\t\tid: l.ID,\n\t\t\ttag: `${l.Repository}:${l.Tag}`,\n\t\t\tlabels: parseLabelString(l.Labels),\n\t\t}),\n\t);\n};\n\nconst toNetworkSummaries = (stdout: string): ReadonlyArray<NetworkSummary> => {\n\tconst lines = parseJsonLines(NetworksLine, stdout);\n\treturn lines.map(\n\t\t(l): NetworkSummary => ({\n\t\t\tid: l.ID,\n\t\t\tname: l.Name,\n\t\t\tdriver: l.Driver,\n\t\t\tlabels: parseLabelString(l.Labels),\n\t\t}),\n\t);\n};\n\nconst toVolumeSummaries = (stdout: string): ReadonlyArray<VolumeSummary> => {\n\tconst lines = parseJsonLines(VolumesLine, stdout);\n\treturn lines.map(\n\t\t(l): VolumeSummary => ({\n\t\t\tname: l.Name,\n\t\t\tdriver: l.Driver,\n\t\t\tmountpoint: l.Mountpoint,\n\t\t\tlabels: parseLabelString(l.Labels),\n\t\t}),\n\t);\n};\n\nconst devstackAppFilterArgs: ReadonlyArray<string> = ['--filter', `label=${LabelKey.app}`];\n\nexport const listContainers = (\n\tmatch: Partial<ContainerLabelTuple>,\n): Effect.Effect<ReadonlyArray<ContainerSummary>, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst filters = renderFilterArgs(match);\n\t\tconst res = yield* dockerRunOk('ps', ['-a', '--format', '{{json .}}', ...filters]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.ps')),\n\t\t);\n\t\treturn toContainerSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.containers'));\n\nexport const listDevstackContainers = (): Effect.Effect<\n\tReadonlyArray<ContainerSummary>,\n\tDockerRuntimeError,\n\tDockerHost | DockerSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('ps', [\n\t\t\t'-a',\n\t\t\t'--format',\n\t\t\t'{{json .}}',\n\t\t\t...devstackAppFilterArgs,\n\t\t]).pipe(Effect.mapError(wrapGeneric('docker.ps')));\n\t\treturn toContainerSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.devstackContainers'));\n\n/** List devstack-managed containers whose generic `kind` label matches.\n *\n * Orchestrators that own a slot of named containers (e.g. the router\n * orchestrator stamps `kind=router`) use this to enumerate their own\n * fleet without L1 learning the orchestrator's name. The L1 lister\n * takes a STRING — it never branches on the value. */\nexport const listDevstackContainersByKind = (\n\tkind: string,\n): Effect.Effect<ReadonlyArray<ContainerSummary>, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('ps', [\n\t\t\t'-a',\n\t\t\t'--format',\n\t\t\t'{{json .}}',\n\t\t\t'--filter',\n\t\t\t`label=${LabelKey.managed}=true`,\n\t\t\t'--filter',\n\t\t\t`label=${LabelKey.kind}=${kind}`,\n\t\t]).pipe(Effect.mapError(wrapGeneric('docker.ps')));\n\t\treturn toContainerSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.devstackContainersByKind'));\n\nexport const listImages = (\n\tmatch: Partial<ContainerLabelTuple>,\n): Effect.Effect<ReadonlyArray<ImageSummary>, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst filters = renderFilterArgs(match);\n\t\tconst res = yield* dockerRunOk('images', ['--format', '{{json .}}', ...filters]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.images')),\n\t\t);\n\t\treturn toImageSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.images'));\n\nexport const listDevstackImages = (): Effect.Effect<\n\tReadonlyArray<ImageSummary>,\n\tDockerRuntimeError,\n\tDockerHost | DockerSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('images', [\n\t\t\t'--format',\n\t\t\t'{{json .}}',\n\t\t\t...devstackAppFilterArgs,\n\t\t]).pipe(Effect.mapError(wrapGeneric('docker.images')));\n\t\treturn toImageSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.devstackImages'));\n\nexport const listNetworks = (\n\tmatch: Partial<ContainerLabelTuple>,\n): Effect.Effect<ReadonlyArray<NetworkSummary>, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst filters = renderFilterArgs(match);\n\t\tconst res = yield* dockerRunOk('network', ['ls', '--format', '{{json .}}', ...filters]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.network.ls')),\n\t\t);\n\t\treturn toNetworkSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.networks'));\n\nexport const listDevstackNetworks = (): Effect.Effect<\n\tReadonlyArray<NetworkSummary>,\n\tDockerRuntimeError,\n\tDockerHost | DockerSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('network', [\n\t\t\t'ls',\n\t\t\t'--format',\n\t\t\t'{{json .}}',\n\t\t\t...devstackAppFilterArgs,\n\t\t]).pipe(Effect.mapError(wrapGeneric('docker.network.ls')));\n\t\treturn toNetworkSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.devstackNetworks'));\n\nexport const listVolumes = (\n\tmatch: Partial<ContainerLabelTuple>,\n): Effect.Effect<ReadonlyArray<VolumeSummary>, DockerRuntimeError, DockerHost | DockerSpawner> =>\n\tEffect.gen(function* () {\n\t\tconst filters = renderFilterArgs(match);\n\t\tconst res = yield* dockerRunOk('volume', ['ls', '--format', '{{json .}}', ...filters]).pipe(\n\t\t\tEffect.mapError(wrapGeneric('docker.volume.ls')),\n\t\t);\n\t\treturn toVolumeSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.volumes'));\n\nexport const listDevstackVolumes = (): Effect.Effect<\n\tReadonlyArray<VolumeSummary>,\n\tDockerRuntimeError,\n\tDockerHost | DockerSpawner\n> =>\n\tEffect.gen(function* () {\n\t\tconst res = yield* dockerRunOk('volume', [\n\t\t\t'ls',\n\t\t\t'--format',\n\t\t\t'{{json .}}',\n\t\t\t...devstackAppFilterArgs,\n\t\t]).pipe(Effect.mapError(wrapGeneric('docker.volume.ls')));\n\t\treturn toVolumeSummaries(res.stdout);\n\t}).pipe(Effect.withSpan('runtime.docker.inventory.devstackVolumes'));\n"],"mappings":"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"}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
//#region src/runtime/docker/labels.ts
|
|
2
|
-
/** Canonical label keys. Single source of truth.
|
|
2
|
+
/** Canonical label keys. Single source of truth.
|
|
3
|
+
*
|
|
4
|
+
* L1 is plugin-blind: it carries the four canonical engine dimensions
|
|
5
|
+
* (`app`/`stack`/`plugin`/`role`) plus generic resource-kind slots
|
|
6
|
+
* (`kind`/`subkind`/`specVersion`). Orchestrators stamp their own
|
|
7
|
+
* values into the generic slots — e.g. the router orchestrator stamps
|
|
8
|
+
* `kind=router`, `subkind=<profile-id>`, `specVersion=<version>` —
|
|
9
|
+
* those become orchestrator-side conventions rather than L1 vocabulary.
|
|
10
|
+
* Per STYLE_GUIDE §7, L1 MUST NOT add orchestrator-named label keys. */
|
|
3
11
|
const LabelKey = {
|
|
4
12
|
app: "devstack.app",
|
|
5
13
|
stack: "devstack.stack",
|
|
@@ -10,9 +18,9 @@ const LabelKey = {
|
|
|
10
18
|
managed: "devstack.managed",
|
|
11
19
|
networkMarker: "devstack.network",
|
|
12
20
|
volumeMarker: "devstack.volume",
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
kind: "devstack.kind",
|
|
22
|
+
subkind: "devstack.subkind",
|
|
23
|
+
specVersion: "devstack.spec-version"
|
|
16
24
|
};
|
|
17
25
|
/** Docker Compose-compatible labels used only for Docker Desktop grouping.
|
|
18
26
|
* Cleanup and inventory remain keyed by `LabelKey` above. */
|
|
@@ -26,6 +34,18 @@ const ComposeLabelKey = {
|
|
|
26
34
|
volume: "com.docker.compose.volume"
|
|
27
35
|
};
|
|
28
36
|
const COMPOSE_UI_VERSION = "2.0.0";
|
|
37
|
+
/** Coerce a `docker inspect` `Labels` field (typed `Schema.Unknown`) into
|
|
38
|
+
* a string→string map, dropping any non-string values. Shared by the
|
|
39
|
+
* container / network / volume inspect readers — `docker inspect` reports
|
|
40
|
+
* labels as an object whose values are always strings in practice, but the
|
|
41
|
+
* schema keeps the field `Unknown` so a malformed daemon response cannot
|
|
42
|
+
* blow up decode; this reader is the defensive narrowing. */
|
|
43
|
+
const readLabels = (raw) => {
|
|
44
|
+
if (raw === null || typeof raw !== "object") return {};
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const [key, value] of Object.entries(raw)) if (typeof value === "string") out[key] = value;
|
|
47
|
+
return out;
|
|
48
|
+
};
|
|
29
49
|
const normalizeComposeSegment = (value) => {
|
|
30
50
|
const normalized = value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
31
51
|
return normalized.length > 0 ? normalized : "unnamed";
|
|
@@ -57,6 +77,19 @@ const expectedNetworkOwnershipLabels = (app, stack) => ({
|
|
|
57
77
|
[LabelKey.app]: app,
|
|
58
78
|
[LabelKey.stack]: stack
|
|
59
79
|
});
|
|
80
|
+
/** Labels stamped on `docker build` outputs so label-driven prune can
|
|
81
|
+
* find the image. `plugin` / `role` are optional — the runtime
|
|
82
|
+
* contract only requires `app` / `stack`. */
|
|
83
|
+
const expectedImageOwnershipLabels = (owner) => {
|
|
84
|
+
const out = {
|
|
85
|
+
[LabelKey.managed]: "true",
|
|
86
|
+
[LabelKey.app]: owner.app,
|
|
87
|
+
[LabelKey.stack]: owner.stack
|
|
88
|
+
};
|
|
89
|
+
if (owner.plugin !== void 0) out[LabelKey.plugin] = owner.plugin;
|
|
90
|
+
if (owner.role !== void 0) out[LabelKey.role] = owner.role;
|
|
91
|
+
return out;
|
|
92
|
+
};
|
|
60
93
|
const ownershipMismatchDetail = (expected, actual) => {
|
|
61
94
|
const mismatches = [];
|
|
62
95
|
for (const [key, expectedValue] of Object.entries(expected)) {
|
|
@@ -92,6 +125,6 @@ const renderNetworkLabels = (name, app, stack, opts = {}) => {
|
|
|
92
125
|
return opts.composeUi === false ? ownership : [...ownership, ...renderComposeNetworkLabels(name, app, stack)];
|
|
93
126
|
};
|
|
94
127
|
//#endregion
|
|
95
|
-
export { ComposeLabelKey, LabelKey, expectedContainerOwnershipLabels, expectedNetworkOwnershipLabels, ownershipMismatchDetail, renderContainerLabels, renderFilterArgs, renderNetworkLabels };
|
|
128
|
+
export { COMPOSE_UI_VERSION, ComposeLabelKey, LabelKey, composeProjectId, composeServiceId, expectedContainerOwnershipLabels, expectedImageOwnershipLabels, expectedNetworkOwnershipLabels, ownershipMismatchDetail, readLabels, renderComposeContainerLabels, renderComposeNetworkLabels, renderContainerLabels, renderFilterArgs, renderNetworkLabels };
|
|
96
129
|
|
|
97
130
|
//# sourceMappingURL=labels.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labels.mjs","names":[],"sources":["../../../src/runtime/docker/labels.ts"],"sourcesContent":["// Docker label contract.\n//\n// Architecture § Container runtime § Label-driven inventory:\n// Containers, images, networks, and volumes are enumerated by the\n// four canonical engine-level dimensions and a small set of\n// well-known role labels. Inventory NEVER greps stdout for names.\n//\n// The four canonical engine dimensions (no service names anywhere):\n//\n// - `devstack.app` — the app/project this resource belongs to\n// - `devstack.stack` — the stack name within that app\n// - `devstack.plugin` — the plugin key (opaque to the runtime)\n// - `devstack.role` — the resource's role within the plugin (also opaque)\n//\n// Plus two protocol-level labels for the lifecycle machinery:\n//\n// - `devstack.cycle` — the engine cycle id that last touched it\n// (sweep filters by this to find orphans)\n// - `devstack.managed` — set to `'true'` on every resource we own;\n// a coarse \"is this ours at all?\" filter\n//\n// Network / image / volume specializations carry only the canonical\n// engine dimensions plus a kind-specific marker.\n\nimport type { ContainerLabelTuple } from '../../contracts/snapshotable.ts';\n\n/** Canonical label keys. Single source of truth. */\nexport const LabelKey = {\n\tapp: 'devstack.app',\n\tstack: 'devstack.stack',\n\tplugin: 'devstack.plugin',\n\trole: 'devstack.role',\n\tcycle: 'devstack.cycle',\n\tconfigHash: 'devstack.config-hash',\n\tmanaged: 'devstack.managed',\n\t// Resource-kind markers — coarse \"what is this?\" classification\n\t// for inventory walks.\n\tnetworkMarker: 'devstack.network',\n\tvolumeMarker: 'devstack.volume',\n\t// Reverse-proxy / routable. Router itself is built outside this\n\t// package, but if it stamps a label, this is the key it uses.\n\trouterMarker: 'devstack.router',\n\trouterProfile: 'devstack.router.profile',\n\trouterSpecVersion: 'devstack.router.spec-version',\n} as const;\n\nexport type LabelKey = (typeof LabelKey)[keyof typeof LabelKey];\n\n/** Docker Compose-compatible labels used only for Docker Desktop grouping.\n * Cleanup and inventory remain keyed by `LabelKey` above. */\nexport const ComposeLabelKey = {\n\tproject: 'com.docker.compose.project',\n\tservice: 'com.docker.compose.service',\n\tcontainerNumber: 'com.docker.compose.container-number',\n\tversion: 'com.docker.compose.version',\n\toneoff: 'com.docker.compose.oneoff',\n\tnetwork: 'com.docker.compose.network',\n\tvolume: 'com.docker.compose.volume',\n} as const;\n\nexport type ComposeLabelKey = (typeof ComposeLabelKey)[keyof typeof ComposeLabelKey];\n\nexport const COMPOSE_UI_VERSION = '2.0.0';\n\nexport type ExpectedOwnershipLabels = Readonly<Record<string, string>>;\n\nconst normalizeComposeSegment = (value: string): string => {\n\tconst normalized = value\n\t\t.trim()\n\t\t.replace(/[^a-zA-Z0-9_-]+/g, '-')\n\t\t.replace(/^-+|-+$/g, '');\n\treturn normalized.length > 0 ? normalized : 'unnamed';\n};\n\nexport const composeProjectId = (app: string, stack: string): string =>\n\t`${normalizeComposeSegment(app)}-${normalizeComposeSegment(stack)}`;\n\nexport const composeServiceId = (tuple: ContainerLabelTuple): string =>\n\t`${normalizeComposeSegment(tuple.plugin)}.${normalizeComposeSegment(tuple.role)}`;\n\nexport const renderComposeContainerLabels = (tuple: ContainerLabelTuple): ReadonlyArray<string> => [\n\t`${ComposeLabelKey.project}=${composeProjectId(tuple.app, tuple.stack)}`,\n\t`${ComposeLabelKey.service}=${composeServiceId(tuple)}`,\n\t`${ComposeLabelKey.containerNumber}=1`,\n\t`${ComposeLabelKey.version}=${COMPOSE_UI_VERSION}`,\n\t`${ComposeLabelKey.oneoff}=False`,\n];\n\nexport const renderComposeNetworkLabels = (\n\tname: string,\n\tapp: string,\n\tstack: string,\n): ReadonlyArray<string> => [\n\t`${ComposeLabelKey.project}=${composeProjectId(app, stack)}`,\n\t`${ComposeLabelKey.network}=${name}`,\n\t`${ComposeLabelKey.version}=${COMPOSE_UI_VERSION}`,\n];\n\nexport const renderComposeVolumeLabels = (\n\tname: string,\n\ttuple: ContainerLabelTuple,\n): ReadonlyArray<string> => [\n\t`${ComposeLabelKey.project}=${composeProjectId(tuple.app, tuple.stack)}`,\n\t`${ComposeLabelKey.volume}=${name}`,\n\t`${ComposeLabelKey.version}=${COMPOSE_UI_VERSION}`,\n];\n\nexport const expectedContainerOwnershipLabels = (\n\ttuple: ContainerLabelTuple,\n): ExpectedOwnershipLabels => ({\n\t[LabelKey.managed]: 'true',\n\t[LabelKey.app]: tuple.app,\n\t[LabelKey.stack]: tuple.stack,\n\t[LabelKey.plugin]: tuple.plugin,\n\t[LabelKey.role]: tuple.role,\n});\n\nexport const expectedNetworkOwnershipLabels = (\n\tapp: string,\n\tstack: string,\n): ExpectedOwnershipLabels => ({\n\t[LabelKey.managed]: 'true',\n\t[LabelKey.networkMarker]: 'true',\n\t[LabelKey.app]: app,\n\t[LabelKey.stack]: stack,\n});\n\nexport const expectedVolumeOwnershipLabels = (\n\ttuple: ContainerLabelTuple,\n): ExpectedOwnershipLabels => ({\n\t[LabelKey.managed]: 'true',\n\t[LabelKey.volumeMarker]: 'true',\n\t[LabelKey.app]: tuple.app,\n\t[LabelKey.stack]: tuple.stack,\n\t[LabelKey.plugin]: tuple.plugin,\n\t[LabelKey.role]: tuple.role,\n});\n\nexport const ownershipMismatchDetail = (\n\texpected: ExpectedOwnershipLabels,\n\tactual: Readonly<Record<string, string>>,\n): string | null => {\n\tconst mismatches: Array<string> = [];\n\tfor (const [key, expectedValue] of Object.entries(expected)) {\n\t\tconst actualValue = actual[key];\n\t\tif (actualValue !== expectedValue) {\n\t\t\tmismatches.push(`${key}: expected ${expectedValue}, found ${actualValue ?? '<missing>'}`);\n\t\t}\n\t}\n\treturn mismatches.length === 0 ? null : mismatches.join('; ');\n};\n\n/** Render a `ContainerLabelTuple` to docker `--label key=value` pairs.\n * The cycle/managed labels are stamped at create time by `container.ts`. */\nexport const renderContainerLabels = (\n\ttuple: ContainerLabelTuple,\n\tcycle: number,\n\textra: Readonly<Record<string, string>> = {},\n): ReadonlyArray<string> => {\n\tconst ownership = [\n\t\t...Object.entries(expectedContainerOwnershipLabels(tuple)).map(\n\t\t\t([key, value]) => `${key}=${value}`,\n\t\t),\n\t\t`${LabelKey.cycle}=${cycle}`,\n\t\t...Object.entries(extra).map(([key, value]) => `${key}=${value}`),\n\t];\n\treturn [...ownership, ...renderComposeContainerLabels(tuple)];\n};\n\n/** Render a label-tuple as `--filter label=key=value` args for `docker ps`,\n * `docker volume ls`, etc. Partial tuples allowed (sweep / inventory). */\nexport const renderFilterArgs = (\n\tmatch: Partial<ContainerLabelTuple>,\n\textra: Readonly<Record<string, string>> = {},\n): ReadonlyArray<string> => {\n\tconst filters: Array<string> = [`label=${LabelKey.managed}=true`];\n\tif (match.app !== undefined) filters.push(`label=${LabelKey.app}=${match.app}`);\n\tif (match.stack !== undefined) filters.push(`label=${LabelKey.stack}=${match.stack}`);\n\tif (match.plugin !== undefined) filters.push(`label=${LabelKey.plugin}=${match.plugin}`);\n\tif (match.role !== undefined) filters.push(`label=${LabelKey.role}=${match.role}`);\n\tfor (const [k, v] of Object.entries(extra)) filters.push(`label=${k}=${v}`);\n\treturn filters.flatMap((f) => ['--filter', f]);\n};\n\n/** Network-specific labels (shared `devstack` network is cross-stack;\n * per-stack networks would stamp the full tuple). */\nexport const renderNetworkLabels = (\n\tname: string,\n\tapp: string,\n\tstack: string,\n\topts: { readonly composeUi?: boolean } = {},\n): ReadonlyArray<string> => {\n\tconst ownership = Object.entries(expectedNetworkOwnershipLabels(app, stack)).map(\n\t\t([key, value]) => `${key}=${value}`,\n\t);\n\treturn opts.composeUi === false\n\t\t? ownership\n\t\t: [...ownership, ...renderComposeNetworkLabels(name, app, stack)];\n};\n\n/** Volume labels — pre-create with these before any `run -v` argv to\n * avoid docker's lazy unlabelled-create. */\nexport const renderVolumeLabels = (\n\tname: string,\n\ttuple: ContainerLabelTuple,\n): ReadonlyArray<string> => {\n\tconst ownership = Object.entries(expectedVolumeOwnershipLabels(tuple)).map(\n\t\t([key, value]) => `${key}=${value}`,\n\t);\n\treturn [...ownership, ...renderComposeVolumeLabels(name, tuple)];\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"labels.mjs","names":[],"sources":["../../../src/runtime/docker/labels.ts"],"sourcesContent":["// Docker label contract.\n//\n// Architecture § Container runtime § Label-driven inventory:\n// Containers, images, networks, and volumes are enumerated by the\n// four canonical engine-level dimensions and a small set of\n// well-known role labels. Inventory NEVER greps stdout for names.\n//\n// The four canonical engine dimensions (no service names anywhere):\n//\n// - `devstack.app` — the app/project this resource belongs to\n// - `devstack.stack` — the stack name within that app\n// - `devstack.plugin` — the plugin key (opaque to the runtime)\n// - `devstack.role` — the resource's role within the plugin (also opaque)\n//\n// Plus two protocol-level labels for the lifecycle machinery:\n//\n// - `devstack.cycle` — the engine cycle id that last touched it\n// (sweep filters by this to find orphans)\n// - `devstack.managed` — set to `'true'` on every resource we own;\n// a coarse \"is this ours at all?\" filter\n//\n// Network / image / volume specializations carry only the canonical\n// engine dimensions plus a kind-specific marker.\n\nimport type { ContainerLabelTuple } from '../../contracts/snapshotable.ts';\n\n/** Canonical label keys. Single source of truth.\n *\n * L1 is plugin-blind: it carries the four canonical engine dimensions\n * (`app`/`stack`/`plugin`/`role`) plus generic resource-kind slots\n * (`kind`/`subkind`/`specVersion`). Orchestrators stamp their own\n * values into the generic slots — e.g. the router orchestrator stamps\n * `kind=router`, `subkind=<profile-id>`, `specVersion=<version>` —\n * those become orchestrator-side conventions rather than L1 vocabulary.\n * Per STYLE_GUIDE §7, L1 MUST NOT add orchestrator-named label keys. */\nexport const LabelKey = {\n\tapp: 'devstack.app',\n\tstack: 'devstack.stack',\n\tplugin: 'devstack.plugin',\n\trole: 'devstack.role',\n\tcycle: 'devstack.cycle',\n\tconfigHash: 'devstack.config-hash',\n\tmanaged: 'devstack.managed',\n\t// Resource-kind markers — coarse \"what is this?\" classification\n\t// for inventory walks.\n\tnetworkMarker: 'devstack.network',\n\tvolumeMarker: 'devstack.volume',\n\t// Generic kind/subkind slots — orchestrators stamp their own values\n\t// (e.g. router stamps `kind=router`, `subkind=<profile-id>`).\n\tkind: 'devstack.kind',\n\tsubkind: 'devstack.subkind',\n\tspecVersion: 'devstack.spec-version',\n} as const;\n\nexport type LabelKey = (typeof LabelKey)[keyof typeof LabelKey];\n\n/** Docker Compose-compatible labels used only for Docker Desktop grouping.\n * Cleanup and inventory remain keyed by `LabelKey` above. */\nexport const ComposeLabelKey = {\n\tproject: 'com.docker.compose.project',\n\tservice: 'com.docker.compose.service',\n\tcontainerNumber: 'com.docker.compose.container-number',\n\tversion: 'com.docker.compose.version',\n\toneoff: 'com.docker.compose.oneoff',\n\tnetwork: 'com.docker.compose.network',\n\tvolume: 'com.docker.compose.volume',\n} as const;\n\nexport type ComposeLabelKey = (typeof ComposeLabelKey)[keyof typeof ComposeLabelKey];\n\nexport const COMPOSE_UI_VERSION = '2.0.0';\n\nexport type ExpectedOwnershipLabels = Readonly<Record<string, string>>;\n\n/** Coerce a `docker inspect` `Labels` field (typed `Schema.Unknown`) into\n * a string→string map, dropping any non-string values. Shared by the\n * container / network / volume inspect readers — `docker inspect` reports\n * labels as an object whose values are always strings in practice, but the\n * schema keeps the field `Unknown` so a malformed daemon response cannot\n * blow up decode; this reader is the defensive narrowing. */\nexport const readLabels = (raw: unknown): Readonly<Record<string, string>> => {\n\tif (raw === null || typeof raw !== 'object') return {};\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(raw)) {\n\t\tif (typeof value === 'string') out[key] = value;\n\t}\n\treturn out;\n};\n\nconst normalizeComposeSegment = (value: string): string => {\n\tconst normalized = value\n\t\t.trim()\n\t\t.replace(/[^a-zA-Z0-9_-]+/g, '-')\n\t\t.replace(/^-+|-+$/g, '');\n\treturn normalized.length > 0 ? normalized : 'unnamed';\n};\n\nexport const composeProjectId = (app: string, stack: string): string =>\n\t`${normalizeComposeSegment(app)}-${normalizeComposeSegment(stack)}`;\n\nexport const composeServiceId = (tuple: ContainerLabelTuple): string =>\n\t`${normalizeComposeSegment(tuple.plugin)}.${normalizeComposeSegment(tuple.role)}`;\n\nexport const renderComposeContainerLabels = (tuple: ContainerLabelTuple): ReadonlyArray<string> => [\n\t`${ComposeLabelKey.project}=${composeProjectId(tuple.app, tuple.stack)}`,\n\t`${ComposeLabelKey.service}=${composeServiceId(tuple)}`,\n\t`${ComposeLabelKey.containerNumber}=1`,\n\t`${ComposeLabelKey.version}=${COMPOSE_UI_VERSION}`,\n\t`${ComposeLabelKey.oneoff}=False`,\n];\n\nexport const renderComposeNetworkLabels = (\n\tname: string,\n\tapp: string,\n\tstack: string,\n): ReadonlyArray<string> => [\n\t`${ComposeLabelKey.project}=${composeProjectId(app, stack)}`,\n\t`${ComposeLabelKey.network}=${name}`,\n\t`${ComposeLabelKey.version}=${COMPOSE_UI_VERSION}`,\n];\n\nexport const renderComposeVolumeLabels = (\n\tname: string,\n\ttuple: ContainerLabelTuple,\n): ReadonlyArray<string> => [\n\t`${ComposeLabelKey.project}=${composeProjectId(tuple.app, tuple.stack)}`,\n\t`${ComposeLabelKey.volume}=${name}`,\n\t`${ComposeLabelKey.version}=${COMPOSE_UI_VERSION}`,\n];\n\nexport const expectedContainerOwnershipLabels = (\n\ttuple: ContainerLabelTuple,\n): ExpectedOwnershipLabels => ({\n\t[LabelKey.managed]: 'true',\n\t[LabelKey.app]: tuple.app,\n\t[LabelKey.stack]: tuple.stack,\n\t[LabelKey.plugin]: tuple.plugin,\n\t[LabelKey.role]: tuple.role,\n});\n\nexport const expectedNetworkOwnershipLabels = (\n\tapp: string,\n\tstack: string,\n): ExpectedOwnershipLabels => ({\n\t[LabelKey.managed]: 'true',\n\t[LabelKey.networkMarker]: 'true',\n\t[LabelKey.app]: app,\n\t[LabelKey.stack]: stack,\n});\n\nexport const expectedVolumeOwnershipLabels = (\n\ttuple: ContainerLabelTuple,\n): ExpectedOwnershipLabels => ({\n\t[LabelKey.managed]: 'true',\n\t[LabelKey.volumeMarker]: 'true',\n\t[LabelKey.app]: tuple.app,\n\t[LabelKey.stack]: tuple.stack,\n\t[LabelKey.plugin]: tuple.plugin,\n\t[LabelKey.role]: tuple.role,\n});\n\n/** Labels stamped on `docker build` outputs so label-driven prune can\n * find the image. `plugin` / `role` are optional — the runtime\n * contract only requires `app` / `stack`. */\nexport const expectedImageOwnershipLabels = (owner: {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly plugin?: string;\n\treadonly role?: string;\n}): ExpectedOwnershipLabels => {\n\tconst out: Record<string, string> = {\n\t\t[LabelKey.managed]: 'true',\n\t\t[LabelKey.app]: owner.app,\n\t\t[LabelKey.stack]: owner.stack,\n\t};\n\tif (owner.plugin !== undefined) out[LabelKey.plugin] = owner.plugin;\n\tif (owner.role !== undefined) out[LabelKey.role] = owner.role;\n\treturn out;\n};\n\nexport const ownershipMismatchDetail = (\n\texpected: ExpectedOwnershipLabels,\n\tactual: Readonly<Record<string, string>>,\n): string | null => {\n\tconst mismatches: Array<string> = [];\n\tfor (const [key, expectedValue] of Object.entries(expected)) {\n\t\tconst actualValue = actual[key];\n\t\tif (actualValue !== expectedValue) {\n\t\t\tmismatches.push(`${key}: expected ${expectedValue}, found ${actualValue ?? '<missing>'}`);\n\t\t}\n\t}\n\treturn mismatches.length === 0 ? null : mismatches.join('; ');\n};\n\n/** Render a `ContainerLabelTuple` to docker `--label key=value` pairs.\n * The cycle/managed labels are stamped at create time by `container.ts`. */\nexport const renderContainerLabels = (\n\ttuple: ContainerLabelTuple,\n\tcycle: number,\n\textra: Readonly<Record<string, string>> = {},\n): ReadonlyArray<string> => {\n\tconst ownership = [\n\t\t...Object.entries(expectedContainerOwnershipLabels(tuple)).map(\n\t\t\t([key, value]) => `${key}=${value}`,\n\t\t),\n\t\t`${LabelKey.cycle}=${cycle}`,\n\t\t...Object.entries(extra).map(([key, value]) => `${key}=${value}`),\n\t];\n\treturn [...ownership, ...renderComposeContainerLabels(tuple)];\n};\n\n/** Render a label-tuple as `--filter label=key=value` args for `docker ps`,\n * `docker volume ls`, etc. Partial tuples allowed (sweep / inventory). */\nexport const renderFilterArgs = (\n\tmatch: Partial<ContainerLabelTuple>,\n\textra: Readonly<Record<string, string>> = {},\n): ReadonlyArray<string> => {\n\tconst filters: Array<string> = [`label=${LabelKey.managed}=true`];\n\tif (match.app !== undefined) filters.push(`label=${LabelKey.app}=${match.app}`);\n\tif (match.stack !== undefined) filters.push(`label=${LabelKey.stack}=${match.stack}`);\n\tif (match.plugin !== undefined) filters.push(`label=${LabelKey.plugin}=${match.plugin}`);\n\tif (match.role !== undefined) filters.push(`label=${LabelKey.role}=${match.role}`);\n\tfor (const [k, v] of Object.entries(extra)) filters.push(`label=${k}=${v}`);\n\treturn filters.flatMap((f) => ['--filter', f]);\n};\n\n/** Network-specific labels (shared `devstack` network is cross-stack;\n * per-stack networks would stamp the full tuple). */\nexport const renderNetworkLabels = (\n\tname: string,\n\tapp: string,\n\tstack: string,\n\topts: { readonly composeUi?: boolean } = {},\n): ReadonlyArray<string> => {\n\tconst ownership = Object.entries(expectedNetworkOwnershipLabels(app, stack)).map(\n\t\t([key, value]) => `${key}=${value}`,\n\t);\n\treturn opts.composeUi === false\n\t\t? ownership\n\t\t: [...ownership, ...renderComposeNetworkLabels(name, app, stack)];\n};\n\n/** Volume labels — pre-create with these before any `run -v` argv to\n * avoid docker's lazy unlabelled-create. */\nexport const renderVolumeLabels = (\n\tname: string,\n\ttuple: ContainerLabelTuple,\n): ReadonlyArray<string> => {\n\tconst ownership = Object.entries(expectedVolumeOwnershipLabels(tuple)).map(\n\t\t([key, value]) => `${key}=${value}`,\n\t);\n\treturn [...ownership, ...renderComposeVolumeLabels(name, tuple)];\n};\n"],"mappings":"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"}
|