@treeseed/sdk 0.10.28 → 0.12.4
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 +213 -7
- package/dist/agent-capacity.d.ts +916 -0
- package/dist/agent-capacity.js +1600 -0
- package/dist/agent-tools.d.ts +37 -0
- package/dist/agent-tools.js +301 -0
- package/dist/api/app.d.ts +1 -1
- package/dist/api/auth/d1-database.d.ts +2 -2
- package/dist/api/auth/d1-provider.d.ts +4 -4
- package/dist/api/auth/d1-store.d.ts +2 -2
- package/dist/api/auth/memory-provider.d.ts +1 -1
- package/dist/api/auth/tokens.d.ts +1 -1
- package/dist/api/config.d.ts +3 -1
- package/dist/api/config.js +34 -10
- package/dist/api/http.d.ts +2 -2
- package/dist/api/index.d.ts +10 -10
- package/dist/api/index.js +4 -2
- package/dist/api/operations-routes.d.ts +3 -3
- package/dist/api/operations.d.ts +1 -1
- package/dist/api/providers.d.ts +1 -1
- package/dist/api/providers.js +21 -4
- package/dist/api/railway.d.ts +3 -2
- package/dist/api/sdk-dispatch.d.ts +4 -4
- package/dist/api/sdk-routes.d.ts +2 -2
- package/dist/api/templates.d.ts +2 -2
- package/dist/api/types.d.ts +3 -2
- package/dist/capacity-provider.d.ts +118 -59
- package/dist/capacity-provider.js +235 -43
- package/dist/capacity.d.ts +2 -2
- package/dist/cli-tools.d.ts +1 -1
- package/dist/content-operations.d.ts +97 -0
- package/dist/content-operations.js +250 -0
- package/dist/content-store.d.ts +4 -4
- package/dist/control-plane-client.d.ts +13 -18
- package/dist/control-plane-client.js +8 -86
- package/dist/control-plane.d.ts +3 -2
- package/dist/control-plane.js +38 -13
- package/dist/copilot.d.ts +3 -0
- package/dist/copilot.js +16 -1
- package/dist/d1-http.d.ts +1 -1
- package/dist/d1-store.d.ts +32 -57
- package/dist/d1-store.js +115 -318
- package/dist/db/d1.d.ts +1 -232
- package/dist/db/index.d.ts +3 -3
- package/dist/db/market-schema.d.ts +50900 -11018
- package/dist/db/market-schema.js +2313 -467
- package/dist/db/node-sqlite.d.ts +1 -232
- package/dist/db/node-sqlite.js +7 -2
- package/dist/db/schema.d.ts +0 -462
- package/dist/db/schema.js +2 -22
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +0 -11
- package/dist/fixture-support.js +52 -4
- package/dist/governance.d.ts +133 -0
- package/dist/governance.js +285 -0
- package/dist/graph/build.d.ts +2 -2
- package/dist/graph/context-query-contracts.d.ts +1 -1
- package/dist/graph/dsl.d.ts +1 -1
- package/dist/graph/query.d.ts +4 -4
- package/dist/graph/ranking.d.ts +1 -1
- package/dist/graph/schema.d.ts +1 -1
- package/dist/graph.d.ts +11 -11
- package/dist/guarantees/index.d.ts +425 -0
- package/dist/guarantees/index.js +1146 -0
- package/dist/hosting/apps.d.ts +12 -0
- package/dist/hosting/apps.js +107 -0
- package/dist/hosting/builtins.d.ts +25 -0
- package/dist/hosting/builtins.js +602 -0
- package/dist/hosting/contracts.d.ts +208 -0
- package/dist/hosting/contracts.js +0 -0
- package/dist/hosting/graph.d.ts +195 -0
- package/dist/hosting/graph.js +1092 -0
- package/dist/hosting/index.d.ts +4 -0
- package/dist/hosting/index.js +4 -0
- package/dist/index.d.ts +88 -67
- package/dist/index.js +142 -14
- package/dist/local-dev/managed-dev.d.ts +85 -0
- package/dist/local-dev/managed-dev.js +368 -0
- package/dist/managed-dependencies.d.ts +9 -0
- package/dist/managed-dependencies.js +10 -9
- package/dist/market-client.d.ts +327 -9
- package/dist/market-client.js +464 -20
- package/dist/model-registry.d.ts +1 -1
- package/dist/model-registry.js +19 -19
- package/dist/operations/agent-tools.d.ts +2 -11
- package/dist/operations/agent-tools.js +2 -14
- package/dist/operations/providers/default.d.ts +1 -1
- package/dist/operations/providers/default.js +20 -37
- package/dist/operations/repository-operations.d.ts +26 -2
- package/dist/operations/repository-operations.js +159 -1
- package/dist/operations/runtime.d.ts +3 -3
- package/dist/operations/services/bootstrap-runner.d.ts +4 -2
- package/dist/operations/services/bootstrap-runner.js +22 -2
- package/dist/operations/services/build-warning-policy.d.ts +34 -0
- package/dist/operations/services/build-warning-policy.js +81 -82
- package/dist/operations/services/commit-message-provider.js +4 -4
- package/dist/operations/services/config-runtime.d.ts +83 -24
- package/dist/operations/services/config-runtime.js +449 -130
- package/dist/operations/services/deploy.d.ts +106 -624
- package/dist/operations/services/deploy.js +746 -263
- package/dist/operations/services/deployment-readiness.d.ts +30 -0
- package/dist/operations/services/deployment-readiness.js +206 -0
- package/dist/operations/services/git-remote-policy.js +12 -4
- package/dist/operations/services/git-runner.d.ts +66 -0
- package/dist/operations/services/git-runner.js +479 -0
- package/dist/operations/services/git-workflow.d.ts +45 -6
- package/dist/operations/services/git-workflow.js +251 -23
- package/dist/operations/services/github-actions-verification.d.ts +4 -1
- package/dist/operations/services/github-actions-verification.js +12 -3
- package/dist/operations/services/github-api.js +22 -6
- package/dist/operations/services/github-automation.d.ts +4 -4
- package/dist/operations/services/github-automation.js +19 -21
- package/dist/operations/services/github-credentials.d.ts +13 -0
- package/dist/operations/services/github-credentials.js +59 -0
- package/dist/operations/services/hosted-service-checks.d.ts +81 -0
- package/dist/operations/services/hosted-service-checks.js +478 -0
- package/dist/operations/services/hosting-audit.d.ts +2 -2
- package/dist/operations/services/hosting-audit.js +7 -7
- package/dist/operations/services/hub-launch.d.ts +1 -1
- package/dist/operations/services/hub-provider-launch.d.ts +5 -5
- package/dist/operations/services/hub-provider-launch.js +22 -17
- package/dist/operations/services/live-hosted-service-checks.d.ts +26 -0
- package/dist/operations/services/live-hosted-service-checks.js +463 -0
- package/dist/operations/services/local-dev.js +0 -1
- package/dist/operations/services/managed-host-security.d.ts +1 -1
- package/dist/operations/services/managed-host-security.js +1 -1
- package/dist/operations/services/managed-repositories.d.ts +37 -0
- package/dist/operations/services/managed-repositories.js +177 -0
- package/dist/operations/services/operations-runner-smoke.d.ts +31 -0
- package/dist/operations/services/operations-runner-smoke.js +198 -0
- package/dist/operations/services/package-adapters.d.ts +170 -0
- package/dist/operations/services/package-adapters.js +859 -0
- package/dist/operations/services/package-reference-policy.d.ts +13 -88
- package/dist/operations/services/package-reference-policy.js +96 -215
- package/dist/operations/services/project-host-operations.d.ts +4 -4
- package/dist/operations/services/project-platform.d.ts +57 -173
- package/dist/operations/services/project-platform.js +80 -111
- package/dist/operations/services/project-web-monitor.d.ts +2 -2
- package/dist/operations/services/project-web-monitor.js +122 -5
- package/dist/operations/services/railway-api.d.ts +117 -5
- package/dist/operations/services/railway-api.js +915 -93
- package/dist/operations/services/railway-deploy.d.ts +30 -112
- package/dist/operations/services/railway-deploy.js +521 -961
- package/dist/operations/services/release-history.js +3 -4
- package/dist/operations/services/release-proof-ledger.d.ts +13 -0
- package/dist/operations/services/release-proof-ledger.js +81 -0
- package/dist/operations/services/release-proof-planner.d.ts +48 -0
- package/dist/operations/services/release-proof-planner.js +195 -0
- package/dist/operations/services/release-proof-runner.d.ts +16 -0
- package/dist/operations/services/release-proof-runner.js +169 -0
- package/dist/operations/services/release-proof.d.ts +81 -0
- package/dist/operations/services/release-proof.js +79 -0
- package/dist/operations/services/repository-save-orchestrator.d.ts +8 -7
- package/dist/operations/services/repository-save-orchestrator.js +604 -212
- package/dist/operations/services/runtime-paths.d.ts +1 -3
- package/dist/operations/services/runtime-paths.js +0 -4
- package/dist/operations/services/runtime-tools.d.ts +1 -191
- package/dist/operations/services/runtime-tools.js +4 -443
- package/dist/operations/services/save-deploy-preflight.js +9 -1
- package/dist/operations/services/template-host-bindings.d.ts +2 -2
- package/dist/operations/services/template-host-bindings.js +3 -2
- package/dist/operations/services/template-registry.d.ts +6 -6
- package/dist/operations/services/template-registry.js +33 -14
- package/dist/operations/services/template-secret-sync.d.ts +3 -3
- package/dist/operations/services/verification-cache.d.ts +25 -0
- package/dist/operations/services/verification-cache.js +71 -0
- package/dist/operations/services/workflow-timing.d.ts +25 -0
- package/dist/operations/services/workflow-timing.js +79 -0
- package/dist/operations/services/workspace-dependency-mode.js +50 -5
- package/dist/operations/services/workspace-preflight.js +8 -8
- package/dist/operations/services/workspace-save-script.d.ts +4 -4
- package/dist/operations/services/workspace-save.d.ts +1 -0
- package/dist/operations/services/workspace-save.js +42 -19
- package/dist/operations/services/workspace-tools.js +42 -7
- package/dist/operations-registry.d.ts +1 -1
- package/dist/operations-registry.js +5 -6
- package/dist/operations-types.d.ts +1 -1
- package/dist/operations.d.ts +17 -11
- package/dist/operations.js +57 -3
- package/dist/platform/book-export.js +2 -0
- package/dist/platform/books-data.d.ts +1 -1
- package/dist/platform/config.d.ts +27 -0
- package/dist/platform/config.js +100 -0
- package/dist/platform/content-runtime-source.d.ts +54 -0
- package/dist/platform/content-runtime-source.js +169 -0
- package/dist/platform/contracts.d.ts +38 -2
- package/dist/platform/deploy-config.d.ts +1 -1
- package/dist/platform/deploy-config.js +80 -9
- package/dist/platform/deploy-runtime.d.ts +1 -1
- package/dist/platform/desired-state.d.ts +63 -0
- package/dist/platform/desired-state.js +1021 -0
- package/dist/platform/env.yaml +568 -45
- package/dist/platform/environment.d.ts +3 -2
- package/dist/platform/environment.js +122 -21
- package/dist/platform/local-content-materialization.d.ts +9 -0
- package/dist/platform/local-content-materialization.js +212 -0
- package/dist/platform/plugin.d.ts +13 -4
- package/dist/platform/plugins/runtime.d.ts +3 -3
- package/dist/platform/plugins/runtime.js +62 -1
- package/dist/platform/plugins.d.ts +6 -6
- package/dist/platform/published-content-pipeline.d.ts +3 -3
- package/dist/platform/published-content-pipeline.js +5 -1
- package/dist/platform/published-content.d.ts +4 -3
- package/dist/platform/site-config-schema.d.ts +1 -0
- package/dist/platform/site-config-schema.js +4 -1
- package/dist/platform/tenant/runtime-config.d.ts +152 -2
- package/dist/platform/tenant-config.d.ts +1 -1
- package/dist/platform/utils/site-config-schema.d.ts +154 -0
- package/dist/platform/utils/site-config-schema.js +400 -461
- package/dist/platform-operation-store.d.ts +1 -1
- package/dist/platform-operation-store.js +2 -2
- package/dist/platform-operations.js +1 -1
- package/dist/plugin-default.d.ts +1 -1
- package/dist/plugin-default.js +27 -12
- package/dist/project-import.d.ts +91 -0
- package/dist/project-import.js +257 -0
- package/dist/project-workflow.d.ts +1 -1
- package/dist/reconcile/bootstrap-systems.d.ts +2 -2
- package/dist/reconcile/bootstrap-systems.js +5 -5
- package/dist/reconcile/builtin-adapters.d.ts +7 -1
- package/dist/reconcile/builtin-adapters.js +3190 -429
- package/dist/reconcile/contracts.d.ts +23 -6
- package/dist/reconcile/desired-state.d.ts +2 -192
- package/dist/reconcile/desired-state.js +29 -38
- package/dist/reconcile/engine.d.ts +61 -395
- package/dist/reconcile/engine.js +229 -44
- package/dist/reconcile/index.d.ts +10 -8
- package/dist/reconcile/index.js +2 -0
- package/dist/reconcile/live-acceptance.d.ts +100 -0
- package/dist/reconcile/live-acceptance.js +2027 -0
- package/dist/reconcile/platform.d.ts +104 -0
- package/dist/reconcile/platform.js +100 -0
- package/dist/reconcile/providers/docker-private.d.ts +54 -0
- package/dist/reconcile/providers/docker-private.js +101 -0
- package/dist/reconcile/providers/github-private.d.ts +65 -0
- package/dist/reconcile/providers/github-private.js +119 -0
- package/dist/reconcile/providers/local-private.d.ts +28 -0
- package/dist/reconcile/providers/local-private.js +75 -0
- package/dist/reconcile/providers/railway-iac.d.ts +66 -0
- package/dist/reconcile/providers/railway-iac.js +275 -0
- package/dist/reconcile/providers/release-private.d.ts +92 -0
- package/dist/reconcile/providers/release-private.js +152 -0
- package/dist/reconcile/registry.d.ts +2 -2
- package/dist/reconcile/registry.js +16 -1
- package/dist/reconcile/state.d.ts +1 -1
- package/dist/reconcile/state.js +7 -44
- package/dist/reconcile/units.d.ts +1 -1
- package/dist/reconcile/units.js +9 -3
- package/dist/remote.d.ts +6 -6
- package/dist/scenes/artifacts.d.ts +16 -0
- package/dist/scenes/artifacts.js +61 -0
- package/dist/scenes/auth.d.ts +2 -0
- package/dist/scenes/auth.js +28 -0
- package/dist/scenes/base-url.d.ts +12 -0
- package/dist/scenes/base-url.js +123 -0
- package/dist/scenes/builtin-plugins.d.ts +2 -0
- package/dist/scenes/builtin-plugins.js +447 -0
- package/dist/scenes/checkpoints.d.ts +14 -0
- package/dist/scenes/checkpoints.js +33 -0
- package/dist/scenes/device-matrix.d.ts +2 -0
- package/dist/scenes/device-matrix.js +97 -0
- package/dist/scenes/devices.d.ts +11 -0
- package/dist/scenes/devices.js +22 -0
- package/dist/scenes/diagnostics.d.ts +5 -0
- package/dist/scenes/diagnostics.js +22 -0
- package/dist/scenes/diagram-providers.d.ts +2 -0
- package/dist/scenes/diagram-providers.js +181 -0
- package/dist/scenes/diagram-validation.d.ts +22 -0
- package/dist/scenes/diagram-validation.js +74 -0
- package/dist/scenes/environment.d.ts +2 -0
- package/dist/scenes/environment.js +65 -0
- package/dist/scenes/evidence-bundle.d.ts +5 -0
- package/dist/scenes/evidence-bundle.js +42 -0
- package/dist/scenes/evidence-report.d.ts +10 -0
- package/dist/scenes/evidence-report.js +73 -0
- package/dist/scenes/evidence.d.ts +16 -0
- package/dist/scenes/evidence.js +277 -0
- package/dist/scenes/index.d.ts +48 -0
- package/dist/scenes/index.js +312 -0
- package/dist/scenes/inspect.d.ts +9 -0
- package/dist/scenes/inspect.js +64 -0
- package/dist/scenes/loader.d.ts +3 -0
- package/dist/scenes/loader.js +38 -0
- package/dist/scenes/logs.d.ts +2 -0
- package/dist/scenes/logs.js +47 -0
- package/dist/scenes/operations.d.ts +3 -0
- package/dist/scenes/operations.js +128 -0
- package/dist/scenes/phase0.d.ts +9 -0
- package/dist/scenes/phase0.js +637 -0
- package/dist/scenes/planner.d.ts +12 -0
- package/dist/scenes/planner.js +166 -0
- package/dist/scenes/playwright-adapter.d.ts +2 -0
- package/dist/scenes/playwright-adapter.js +63 -0
- package/dist/scenes/plugins.d.ts +5 -0
- package/dist/scenes/plugins.js +115 -0
- package/dist/scenes/progress.d.ts +17 -0
- package/dist/scenes/progress.js +24 -0
- package/dist/scenes/publish-destinations.d.ts +12 -0
- package/dist/scenes/publish-destinations.js +99 -0
- package/dist/scenes/publish-plan-report.d.ts +10 -0
- package/dist/scenes/publish-plan-report.js +70 -0
- package/dist/scenes/publish-plan.d.ts +15 -0
- package/dist/scenes/publish-plan.js +248 -0
- package/dist/scenes/publish-redaction.d.ts +16 -0
- package/dist/scenes/publish-redaction.js +148 -0
- package/dist/scenes/publish-release-record.d.ts +13 -0
- package/dist/scenes/publish-release-record.js +34 -0
- package/dist/scenes/publish-report.d.ts +10 -0
- package/dist/scenes/publish-report.js +67 -0
- package/dist/scenes/publish.d.ts +16 -0
- package/dist/scenes/publish.js +298 -0
- package/dist/scenes/registry.d.ts +12 -0
- package/dist/scenes/registry.js +65 -0
- package/dist/scenes/remotion-adapter.d.ts +3 -0
- package/dist/scenes/remotion-adapter.js +94 -0
- package/dist/scenes/remotion-composition-registry.d.ts +2 -0
- package/dist/scenes/remotion-composition-registry.js +12 -0
- package/dist/scenes/remotion-compositions.d.ts +15 -0
- package/dist/scenes/remotion-compositions.js +464 -0
- package/dist/scenes/remotion-diagrams.d.ts +9 -0
- package/dist/scenes/remotion-diagrams.js +188 -0
- package/dist/scenes/remotion-input.d.ts +13 -0
- package/dist/scenes/remotion-input.js +291 -0
- package/dist/scenes/remotion-visuals.d.ts +29 -0
- package/dist/scenes/remotion-visuals.js +198 -0
- package/dist/scenes/render-media-assets.d.ts +9 -0
- package/dist/scenes/render-media-assets.js +117 -0
- package/dist/scenes/render-report.d.ts +10 -0
- package/dist/scenes/render-report.js +27 -0
- package/dist/scenes/render.d.ts +2 -0
- package/dist/scenes/render.js +218 -0
- package/dist/scenes/reporter.d.ts +3 -0
- package/dist/scenes/reporter.js +101 -0
- package/dist/scenes/resume.d.ts +2 -0
- package/dist/scenes/resume.js +79 -0
- package/dist/scenes/runner.d.ts +2 -0
- package/dist/scenes/runner.js +674 -0
- package/dist/scenes/schema.d.ts +6 -0
- package/dist/scenes/schema.js +994 -0
- package/dist/scenes/seed.d.ts +2 -0
- package/dist/scenes/seed.js +83 -0
- package/dist/scenes/segments.d.ts +15 -0
- package/dist/scenes/segments.js +79 -0
- package/dist/scenes/selectors.d.ts +3 -0
- package/dist/scenes/selectors.js +21 -0
- package/dist/scenes/timeline.d.ts +11 -0
- package/dist/scenes/timeline.js +26 -0
- package/dist/scenes/timeouts.d.ts +6 -0
- package/dist/scenes/timeouts.js +17 -0
- package/dist/scenes/training-captions.d.ts +14 -0
- package/dist/scenes/training-captions.js +98 -0
- package/dist/scenes/training-report.d.ts +10 -0
- package/dist/scenes/training-report.js +78 -0
- package/dist/scenes/training-transcript.d.ts +24 -0
- package/dist/scenes/training-transcript.js +204 -0
- package/dist/scenes/training.d.ts +10 -0
- package/dist/scenes/training.js +134 -0
- package/dist/scenes/types.d.ts +1872 -0
- package/dist/scenes/types.js +8 -0
- package/dist/scenes/visual-audit-contact-sheets.d.ts +6 -0
- package/dist/scenes/visual-audit-contact-sheets.js +119 -0
- package/dist/scenes/visual-audit-fixtures.d.ts +25 -0
- package/dist/scenes/visual-audit-fixtures.js +210 -0
- package/dist/scenes/visual-audit-report.d.ts +11 -0
- package/dist/scenes/visual-audit-report.js +85 -0
- package/dist/scenes/visual-audit-review.d.ts +23 -0
- package/dist/scenes/visual-audit-review.js +829 -0
- package/dist/scenes/visual-audit-routes.d.ts +13 -0
- package/dist/scenes/visual-audit-routes.js +308 -0
- package/dist/scenes/visual-audit.d.ts +3 -0
- package/dist/scenes/visual-audit.js +596 -0
- package/dist/scripts/build-dist.js +154 -40
- package/dist/scripts/check-build-warnings.js +42 -53
- package/dist/scripts/cleanup-markdown.js +1 -1
- package/dist/scripts/cli-test-fixtures.js +24 -28
- package/dist/scripts/deployment-readiness.js +20 -0
- package/dist/scripts/generate-treedx-openapi-types.js +186 -0
- package/dist/scripts/migrate-journey-csv-to-guarantees.js +140 -0
- package/dist/scripts/operations-runner-smoke.js +16 -0
- package/dist/scripts/package-tools.js +12 -1
- package/dist/scripts/prepare.js +3 -7
- package/dist/scripts/release-verify.js +6 -6
- package/dist/scripts/run-fixture-astro-command.js +1 -1
- package/dist/scripts/template-catalog.test.js +68 -82
- package/dist/scripts/tenant-dev.js +0 -10
- package/dist/scripts/tenant-workflow-action.js +10 -1
- package/dist/scripts/test-cloudflare-local.js +1 -1
- package/dist/scripts/test-scaffold.js +0 -1
- package/dist/scripts/test-smoke.js +3 -3
- package/dist/scripts/treeseed-build-dist.js +4 -7
- package/dist/scripts/treeseed-release-verify.js +1 -1
- package/dist/scripts/verify-driver.js +30 -21
- package/dist/scripts/workflow-commands.test.js +18 -27
- package/dist/scripts/workspace-command-e2e.js +1 -1
- package/dist/scripts/workspace-lint.js +6 -0
- package/dist/scripts/workspace-release-test.js +90 -1
- package/dist/scripts/workspace-test.js +6 -0
- package/dist/sdk-dispatch.d.ts +1 -1
- package/dist/sdk-dispatch.js +0 -16
- package/dist/sdk-fields.d.ts +1 -1
- package/dist/sdk-filters.d.ts +1 -1
- package/dist/sdk-types.d.ts +1352 -116
- package/dist/sdk-types.js +413 -2
- package/dist/sdk.d.ts +106 -100
- package/dist/sdk.js +211 -51
- package/dist/secrets-capability.d.ts +421 -0
- package/dist/secrets-capability.js +530 -0
- package/dist/seeds/normalize.js +1 -0
- package/dist/seeds/planner.js +38 -0
- package/dist/seeds/schema.js +293 -4
- package/dist/seeds/types.d.ts +60 -0
- package/dist/seeds/types.js +9 -1
- package/dist/service-credentials.d.ts +54 -0
- package/dist/service-credentials.js +112 -0
- package/dist/stores/cursor-store.d.ts +2 -2
- package/dist/stores/envelopes.d.ts +1 -1
- package/dist/stores/helpers.d.ts +1 -1
- package/dist/stores/lease-store.d.ts +2 -2
- package/dist/stores/message-store.d.ts +2 -2
- package/dist/stores/operational-store.d.ts +3 -26
- package/dist/stores/operational-store.js +0 -225
- package/dist/stores/project-workflow-store.d.ts +2 -2
- package/dist/stores/run-store.d.ts +2 -3
- package/dist/stores/run-store.js +0 -98
- package/dist/stores/subscription-store.d.ts +2 -3
- package/dist/stores/subscription-store.js +0 -100
- package/dist/template-catalog.d.ts +1 -1
- package/dist/template-launch-requirements.d.ts +2 -2
- package/dist/template-launch-requirements.js +9 -0
- package/dist/template-launch-ui.d.ts +2 -2
- package/dist/treedx/adapters.d.ts +6 -0
- package/dist/treedx/adapters.js +36 -0
- package/dist/treedx/client.d.ts +222 -0
- package/dist/treedx/client.js +871 -0
- package/dist/treedx/errors.d.ts +13 -0
- package/dist/treedx/errors.js +17 -0
- package/dist/treedx/federated-client.d.ts +27 -0
- package/dist/treedx/federated-client.js +158 -0
- package/dist/treedx/generated/openapi-types.d.ts +3558 -0
- package/dist/treedx/generated/openapi-types.js +0 -0
- package/dist/treedx/graph-adapter.d.ts +33 -0
- package/dist/treedx/graph-adapter.js +156 -0
- package/dist/treedx/index.d.ts +14 -0
- package/dist/treedx/index.js +48 -0
- package/dist/treedx/market-integration.d.ts +27 -0
- package/dist/treedx/market-integration.js +131 -0
- package/dist/treedx/ports.d.ts +166 -0
- package/dist/treedx/ports.js +231 -0
- package/dist/treedx/query-adapter.d.ts +19 -0
- package/dist/treedx/query-adapter.js +62 -0
- package/dist/treedx/registry-client.d.ts +11 -0
- package/dist/treedx/registry-client.js +19 -0
- package/dist/treedx/repository-adapter.d.ts +45 -0
- package/dist/treedx/repository-adapter.js +319 -0
- package/dist/treedx/sdk-integration.d.ts +27 -0
- package/dist/treedx/sdk-integration.js +63 -0
- package/dist/treedx/types.d.ts +1084 -0
- package/dist/treedx/types.js +8 -0
- package/dist/treedx/workspace-adapter.d.ts +27 -0
- package/dist/treedx/workspace-adapter.js +65 -0
- package/dist/treedx-backends.d.ts +220 -0
- package/dist/treedx-backends.js +634 -0
- package/dist/treedx-client.d.ts +89 -0
- package/dist/treedx-client.js +184 -0
- package/dist/treeseed/services/compose.yml +2 -3
- package/dist/treeseed/template-catalog/catalog.fixture.json +27 -27
- package/dist/types/agents.d.ts +256 -0
- package/dist/types/agents.js +42 -1
- package/dist/verification.d.ts +1 -0
- package/dist/verification.js +143 -14
- package/dist/workflow/operations.d.ts +580 -523
- package/dist/workflow/operations.js +2084 -1279
- package/dist/workflow/policy.js +19 -7
- package/dist/workflow/runs.d.ts +9 -3
- package/dist/workflow/runs.js +66 -26
- package/dist/workflow/session.d.ts +4 -1
- package/dist/workflow/session.js +50 -10
- package/dist/workflow/worktrees.d.ts +6 -2
- package/dist/workflow/worktrees.js +98 -15
- package/dist/workflow-state.d.ts +17 -4
- package/dist/workflow-state.js +68 -41
- package/dist/workflow-support.d.ts +26 -17
- package/dist/workflow-support.js +84 -24
- package/dist/workflow.d.ts +51 -16
- package/dist/workflow.js +19 -7
- package/drizzle/d1/0000_treeseed_d1.sql +0 -18
- package/drizzle/market/0000_market_control_plane.sql +1793 -1499
- package/drizzle/market/0004_treedx_market_integration.sql +99 -0
- package/drizzle/market/0005_agent_capacity_coordination.sql +145 -0
- package/drizzle/market/0006_provider_assignment_lifecycle.sql +9 -0
- package/drizzle/market/0007_agent_architecture_gap_closure.sql +157 -0
- package/drizzle/market/0008_agent_capacity_operational_closure.sql +27 -0
- package/drizzle/market/0009_agent_capacity_runtime_hardening.sql +29 -0
- package/drizzle/market/0010_secrets_capability_persistence.sql +130 -0
- package/drizzle/market/0011_github_app_adapter.sql +47 -0
- package/drizzle/market/0012_workday_test_runs.sql +44 -0
- package/drizzle/market/0013_runtime_task_lifecycle.sql +39 -0
- package/drizzle/market/0014_governance_pipeline.sql +189 -0
- package/package.json +106 -18
- package/scripts/{verify-driver.mjs → verify-driver.ts} +19 -4
- package/templates/github/deploy-web.workflow.yml +49 -12
- package/dist/operations/services/mailpit-runtime.d.ts +0 -20
- package/dist/operations/services/mailpit-runtime.js +0 -67
- package/dist/operations/services/release-candidate.d.ts +0 -41
- package/dist/operations/services/release-candidate.js +0 -616
- package/dist/scripts/ensure-mailpit.js +0 -48
- package/dist/scripts/logs-mailpit.js +0 -2
- package/dist/scripts/run-ts.js +0 -45
- package/dist/scripts/stop-mailpit.js +0 -5
- package/dist/scripts/treeseed-run-ts.js +0 -45
|
@@ -7,7 +7,6 @@ import { resolveTreeseedWebCachePolicy } from "../../platform/deploy-config.js";
|
|
|
7
7
|
import {
|
|
8
8
|
deleteRailwayCustomDomain,
|
|
9
9
|
deleteRailwayEnvironment,
|
|
10
|
-
deleteRailwayProject,
|
|
11
10
|
deleteRailwayVolume,
|
|
12
11
|
getRailwayServiceInstance,
|
|
13
12
|
listRailwayCustomDomains,
|
|
@@ -23,7 +22,7 @@ import { loadCliDeployConfig, resolveWranglerBin } from "./runtime-tools.js";
|
|
|
23
22
|
import { sdkD1MigrationsRoot } from "./runtime-paths.js";
|
|
24
23
|
const DEFAULT_COMPATIBILITY_DATE = "2026-04-05";
|
|
25
24
|
const DEFAULT_COMPATIBILITY_FLAGS = ["nodejs_compat"];
|
|
26
|
-
const DEFAULT_TREESEED_MARKET_BASE_URL = "https://api.treeseed.
|
|
25
|
+
const DEFAULT_TREESEED_MARKET_BASE_URL = "https://api.treeseed.dev";
|
|
27
26
|
const GENERATED_ROOT = ".treeseed/generated";
|
|
28
27
|
const STATE_ROOT = ".treeseed/state";
|
|
29
28
|
const WORKTREE_METADATA_RELATIVE_PATH = ".treeseed/worktree.json";
|
|
@@ -146,6 +145,24 @@ function deriveApiDomainFromWebDomain(domain) {
|
|
|
146
145
|
}
|
|
147
146
|
return domain.startsWith("api.") ? domain : `api.${domain}`;
|
|
148
147
|
}
|
|
148
|
+
function configuredApiConnectionDomain(deployConfig, scope) {
|
|
149
|
+
const apiConnection = deployConfig.connections?.api?.environments?.[scope];
|
|
150
|
+
const domain = apiConnection?.domain?.trim();
|
|
151
|
+
if (domain) {
|
|
152
|
+
return domain;
|
|
153
|
+
}
|
|
154
|
+
const connectionBaseUrl = primaryHost(apiConnection?.baseUrl);
|
|
155
|
+
if (connectionBaseUrl) {
|
|
156
|
+
return connectionBaseUrl;
|
|
157
|
+
}
|
|
158
|
+
const serviceDomain = deployConfig.services?.api?.environments?.[scope]?.domain?.trim();
|
|
159
|
+
if (serviceDomain) {
|
|
160
|
+
return serviceDomain;
|
|
161
|
+
}
|
|
162
|
+
return primaryHost(
|
|
163
|
+
deployConfig.services?.api?.environments?.[scope]?.baseUrl ?? deployConfig.services?.api?.publicBaseUrl
|
|
164
|
+
);
|
|
165
|
+
}
|
|
149
166
|
function resolveConfiguredSurfaceDomain(deployConfig, target, surface) {
|
|
150
167
|
if (target.kind !== "persistent") {
|
|
151
168
|
return null;
|
|
@@ -155,6 +172,12 @@ function resolveConfiguredSurfaceDomain(deployConfig, target, surface) {
|
|
|
155
172
|
if (configured) {
|
|
156
173
|
return configured;
|
|
157
174
|
}
|
|
175
|
+
if (surface === "api") {
|
|
176
|
+
const apiConnectionDomain = configuredApiConnectionDomain(deployConfig, scope);
|
|
177
|
+
if (apiConnectionDomain) {
|
|
178
|
+
return apiConnectionDomain;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
158
181
|
if (scope === "staging") {
|
|
159
182
|
return deriveTreeseedStagingSurfaceDomain(
|
|
160
183
|
deployConfig,
|
|
@@ -171,6 +194,14 @@ function resolveConfiguredSurfaceDomain(deployConfig, target, surface) {
|
|
|
171
194
|
return deriveApiDomainFromWebDomain(resolveConfiguredSurfaceDomain(deployConfig, target, "web"));
|
|
172
195
|
}
|
|
173
196
|
function resolveConfiguredSurfaceBaseUrl(deployConfig, target, surface) {
|
|
197
|
+
if (surface === "api") {
|
|
198
|
+
const scope = scopeFromTarget(target);
|
|
199
|
+
const apiConnection = deployConfig.connections?.api?.environments?.[scope];
|
|
200
|
+
const connectionBaseUrl = apiConnection?.baseUrl ?? (apiConnection?.domain ? `https://${apiConnection.domain}` : null);
|
|
201
|
+
if (connectionBaseUrl) {
|
|
202
|
+
return connectionBaseUrl;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
174
205
|
const configuredDomain = resolveConfiguredSurfaceDomain(deployConfig, target, surface);
|
|
175
206
|
if (configuredDomain) {
|
|
176
207
|
return `https://${configuredDomain}`;
|
|
@@ -301,6 +332,7 @@ function resolveContentServingMode(deployConfig, options = {}) {
|
|
|
301
332
|
return deployConfig.providers?.content?.serving ?? "local_collections";
|
|
302
333
|
}
|
|
303
334
|
function buildPublicVars(deployConfig, options = {}) {
|
|
335
|
+
const target = options.target ? normalizeTarget(options.target) : createPersistentDeployTarget("prod");
|
|
304
336
|
const identity = resolveTreeseedResourceIdentity(deployConfig, createPersistentDeployTarget("prod"));
|
|
305
337
|
const contentRuntimeProvider = deployConfig.providers?.content?.runtime ?? "team_scoped_r2_overlay";
|
|
306
338
|
const contentPublishProvider = deployConfig.providers?.content?.publish ?? contentRuntimeProvider;
|
|
@@ -312,20 +344,23 @@ function buildPublicVars(deployConfig, options = {}) {
|
|
|
312
344
|
const managedRuntime = deployConfig.runtime?.mode === "treeseed_managed";
|
|
313
345
|
const workerRailway = deployConfig.services?.worker?.railway ?? {};
|
|
314
346
|
const webCachePolicy = resolveTreeseedWebCachePolicy(deployConfig);
|
|
347
|
+
const projectDomain = target.kind === "persistent" ? resolveConfiguredSurfaceDomain(deployConfig, target, "web") : primaryHost(deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl);
|
|
315
348
|
return {
|
|
316
349
|
TREESEED_HOSTING_KIND: deployConfig.hosting?.kind ?? "self_hosted_project",
|
|
317
350
|
TREESEED_HOSTING_REGISTRATION: deployConfig.hosting?.registration ?? "none",
|
|
318
351
|
TREESEED_HUB_MODE: deployConfig.hub?.mode ?? "treeseed_hosted",
|
|
319
352
|
TREESEED_RUNTIME_MODE: deployConfig.runtime?.mode ?? "none",
|
|
320
353
|
TREESEED_RUNTIME_REGISTRATION: deployConfig.runtime?.registration ?? "none",
|
|
321
|
-
TREESEED_CENTRAL_MARKET_API_BASE_URL:
|
|
322
|
-
TREESEED_MARKET_API_BASE_URL: resolveConfiguredMarketBaseUrl(deployConfig),
|
|
323
|
-
|
|
354
|
+
TREESEED_CENTRAL_MARKET_API_BASE_URL: resolveConfiguredCentralMarketBaseUrl(deployConfig, target),
|
|
355
|
+
TREESEED_MARKET_API_BASE_URL: resolveConfiguredMarketBaseUrl(deployConfig, target),
|
|
356
|
+
TREESEED_API_BASE_URL: resolveConfiguredMarketBaseUrl(deployConfig, target),
|
|
357
|
+
TREESEED_CATALOG_MARKET_API_BASE_URLS: resolveConfiguredMarketBaseUrl(deployConfig, target) ?? envOrNull("TREESEED_CATALOG_MARKET_API_BASE_URLS"),
|
|
324
358
|
TREESEED_HOSTING_TEAM_ID: contentDefaultTeamId,
|
|
359
|
+
TREESEED_PROJECT_DOMAINS: projectDomain ?? "",
|
|
325
360
|
TREESEED_PROJECT_ID: identity.projectId,
|
|
326
|
-
TREESEED_AGENT_EXECUTION_PROVIDER: deployConfig.providers?.agents?.execution ?? "
|
|
327
|
-
TREESEED_AGENT_REPOSITORY_PROVIDER: deployConfig.providers?.agents?.repository ?? "
|
|
328
|
-
TREESEED_AGENT_VERIFICATION_PROVIDER: deployConfig.providers?.agents?.verification ?? "
|
|
361
|
+
TREESEED_AGENT_EXECUTION_PROVIDER: deployConfig.providers?.agents?.execution ?? "codex",
|
|
362
|
+
TREESEED_AGENT_REPOSITORY_PROVIDER: deployConfig.providers?.agents?.repository ?? "git",
|
|
363
|
+
TREESEED_AGENT_VERIFICATION_PROVIDER: deployConfig.providers?.agents?.verification ?? "local",
|
|
329
364
|
TREESEED_CONTENT_RUNTIME_PROVIDER: contentRuntimeProvider,
|
|
330
365
|
TREESEED_CONTENT_PUBLISH_PROVIDER: contentPublishProvider,
|
|
331
366
|
TREESEED_CONTENT_SERVING_MODE: contentServingMode,
|
|
@@ -380,7 +415,7 @@ function envValue(env, key) {
|
|
|
380
415
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
381
416
|
}
|
|
382
417
|
const LOCAL_RUNTIME_AUTH_ENV_KEYS = [
|
|
383
|
-
"
|
|
418
|
+
"TREESEED_BETTER_AUTH_URL",
|
|
384
419
|
"TREESEED_SITE_URL",
|
|
385
420
|
"TREESEED_AUTH_MODE",
|
|
386
421
|
"TREESEED_AUTH_INTERNAL_SIGNUP",
|
|
@@ -478,15 +513,7 @@ function defaultStateFromConfig(deployConfig, target) {
|
|
|
478
513
|
previewDatabaseId: `dryrun-${suffix}-site-data-preview`
|
|
479
514
|
}
|
|
480
515
|
},
|
|
481
|
-
queues: {
|
|
482
|
-
agentWork: {
|
|
483
|
-
name: environmentScopedIdentityName(identity, deployConfig.cloudflare.queueName ?? "agent-work", target),
|
|
484
|
-
dlqName: environmentScopedIdentityName(identity, deployConfig.cloudflare.dlqName ?? "agent-work-dlq", target),
|
|
485
|
-
binding: deployConfig.cloudflare.queueBinding ?? "AGENT_WORK_QUEUE",
|
|
486
|
-
queueId: null,
|
|
487
|
-
dlqId: null
|
|
488
|
-
}
|
|
489
|
-
},
|
|
516
|
+
queues: {},
|
|
490
517
|
pages: {
|
|
491
518
|
projectName: resolveConfiguredPagesProjectName(deployConfig),
|
|
492
519
|
productionBranch: deployConfig.cloudflare.pages?.productionBranch ?? "main",
|
|
@@ -655,19 +682,7 @@ function loadDeployState(tenantRoot, deployConfig, options = {}) {
|
|
|
655
682
|
binding: defaults.d1Databases.SITE_DATA_DB.binding
|
|
656
683
|
}
|
|
657
684
|
},
|
|
658
|
-
queues: {
|
|
659
|
-
...defaults.queues ?? {},
|
|
660
|
-
...persisted.queues ?? {},
|
|
661
|
-
agentWork: {
|
|
662
|
-
...defaults.queues?.agentWork ?? {},
|
|
663
|
-
...persisted.queues?.agentWork ?? {},
|
|
664
|
-
name: defaults.queues?.agentWork?.name ?? persisted.queues?.agentWork?.name ?? "agent-work",
|
|
665
|
-
dlqName: defaults.queues?.agentWork?.dlqName ?? persisted.queues?.agentWork?.dlqName ?? "agent-work-dlq",
|
|
666
|
-
binding: defaults.queues?.agentWork?.binding ?? persisted.queues?.agentWork?.binding ?? "AGENT_WORK_QUEUE",
|
|
667
|
-
queueId: persisted.queues?.agentWork?.queueId ?? defaults.queues?.agentWork?.queueId ?? null,
|
|
668
|
-
dlqId: persisted.queues?.agentWork?.dlqId ?? defaults.queues?.agentWork?.dlqId ?? null
|
|
669
|
-
}
|
|
670
|
-
},
|
|
685
|
+
queues: {},
|
|
671
686
|
generatedSecrets: {
|
|
672
687
|
...defaults.generatedSecrets ?? {},
|
|
673
688
|
...persisted.generatedSecrets ?? {}
|
|
@@ -906,7 +921,7 @@ function runWrangler(args, { cwd, allowFailure = false, json = false, capture =
|
|
|
906
921
|
output || `Wrangler command failed: ${args.join(" ")}`,
|
|
907
922
|
"",
|
|
908
923
|
"Treeseed Cloudflare authentication failed. Check that CLOUDFLARE_API_TOKEN is an account-level token scoped to the target account and domain.",
|
|
909
|
-
"Required Cloudflare permissions:
|
|
924
|
+
"Required Cloudflare permissions: account Pages Write, Workers Scripts Write, Workers KV Storage Write, Workers R2 Storage Write, D1 Write, Queues Write, Turnstile Sites Write, Account Rulesets Write, and Account Rule Lists Write; target zone Zone Read, DNS Write, Cache Settings Write, and SSL and Certificates Write."
|
|
910
925
|
].join("\n"));
|
|
911
926
|
}
|
|
912
927
|
throw new Error(output || `Wrangler command failed: ${args.join(" ")}`);
|
|
@@ -925,7 +940,7 @@ function isWranglerAlreadyExistsError(error, matchers) {
|
|
|
925
940
|
return matchers.some((matcher) => matcher.test(message));
|
|
926
941
|
}
|
|
927
942
|
function listKvNamespaces(tenantRoot, env) {
|
|
928
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
943
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
929
944
|
if (!accountId) {
|
|
930
945
|
return [];
|
|
931
946
|
}
|
|
@@ -936,7 +951,7 @@ function listKvNamespaces(tenantRoot, env) {
|
|
|
936
951
|
return Array.isArray(payload?.result) ? payload.result : [];
|
|
937
952
|
}
|
|
938
953
|
function listD1Databases(tenantRoot, env) {
|
|
939
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
954
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
940
955
|
if (!accountId) {
|
|
941
956
|
return [];
|
|
942
957
|
}
|
|
@@ -947,7 +962,7 @@ function listD1Databases(tenantRoot, env) {
|
|
|
947
962
|
return Array.isArray(payload?.result) ? payload.result : [];
|
|
948
963
|
}
|
|
949
964
|
function listQueues(tenantRoot, env) {
|
|
950
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
965
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
951
966
|
if (!accountId) {
|
|
952
967
|
return [];
|
|
953
968
|
}
|
|
@@ -958,7 +973,7 @@ function listQueues(tenantRoot, env) {
|
|
|
958
973
|
return Array.isArray(payload?.result) ? payload.result : [];
|
|
959
974
|
}
|
|
960
975
|
function listR2Buckets(tenantRoot, env) {
|
|
961
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
976
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
962
977
|
if (!accountId) {
|
|
963
978
|
return [];
|
|
964
979
|
}
|
|
@@ -969,7 +984,7 @@ function listR2Buckets(tenantRoot, env) {
|
|
|
969
984
|
return Array.isArray(payload?.result?.buckets) ? payload.result.buckets : [];
|
|
970
985
|
}
|
|
971
986
|
function listPagesProjects(tenantRoot, env) {
|
|
972
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
987
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
973
988
|
if (!accountId) {
|
|
974
989
|
return [];
|
|
975
990
|
}
|
|
@@ -980,7 +995,7 @@ function listPagesProjects(tenantRoot, env) {
|
|
|
980
995
|
return Array.isArray(payload?.result) ? payload.result : [];
|
|
981
996
|
}
|
|
982
997
|
function listTurnstileWidgets(tenantRoot, env) {
|
|
983
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
998
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
984
999
|
if (!accountId) {
|
|
985
1000
|
return [];
|
|
986
1001
|
}
|
|
@@ -990,8 +1005,50 @@ function listTurnstileWidgets(tenantRoot, env) {
|
|
|
990
1005
|
});
|
|
991
1006
|
return Array.isArray(payload?.result) ? payload.result : [];
|
|
992
1007
|
}
|
|
1008
|
+
function listWorkers(tenantRoot, env) {
|
|
1009
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1010
|
+
if (!accountId) {
|
|
1011
|
+
return [];
|
|
1012
|
+
}
|
|
1013
|
+
const payload = cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/workers/services?per_page=100`, {
|
|
1014
|
+
env,
|
|
1015
|
+
allowFailure: true
|
|
1016
|
+
});
|
|
1017
|
+
return Array.isArray(payload?.result) ? payload.result : [];
|
|
1018
|
+
}
|
|
1019
|
+
function listDnsZones(env) {
|
|
1020
|
+
const payload = cloudflareApiRequest("/zones?per_page=100", {
|
|
1021
|
+
env,
|
|
1022
|
+
allowFailure: true
|
|
1023
|
+
});
|
|
1024
|
+
return Array.isArray(payload?.result) ? payload.result : [];
|
|
1025
|
+
}
|
|
1026
|
+
function listDnsRecords(zoneId, env) {
|
|
1027
|
+
if (!zoneId) {
|
|
1028
|
+
return [];
|
|
1029
|
+
}
|
|
1030
|
+
const records = [];
|
|
1031
|
+
let page = 1;
|
|
1032
|
+
let totalPages = 1;
|
|
1033
|
+
while (page <= totalPages && page <= 50) {
|
|
1034
|
+
const payload = cloudflareApiRequest(
|
|
1035
|
+
`/zones/${encodeURIComponent(zoneId)}/dns_records?per_page=100&page=${page}`,
|
|
1036
|
+
{ env, allowFailure: true }
|
|
1037
|
+
);
|
|
1038
|
+
if (payload?.success === false) {
|
|
1039
|
+
break;
|
|
1040
|
+
}
|
|
1041
|
+
if (Array.isArray(payload?.result)) {
|
|
1042
|
+
records.push(...payload.result);
|
|
1043
|
+
}
|
|
1044
|
+
const reportedTotal = Number(payload?.result_info?.total_pages);
|
|
1045
|
+
totalPages = Number.isFinite(reportedTotal) && reportedTotal > 0 ? reportedTotal : page;
|
|
1046
|
+
page += 1;
|
|
1047
|
+
}
|
|
1048
|
+
return records;
|
|
1049
|
+
}
|
|
993
1050
|
function getTurnstileWidget(env, sitekey) {
|
|
994
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
1051
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
995
1052
|
if (!accountId || !sitekey) {
|
|
996
1053
|
return null;
|
|
997
1054
|
}
|
|
@@ -1002,7 +1059,7 @@ function getTurnstileWidget(env, sitekey) {
|
|
|
1002
1059
|
return payload?.result ?? null;
|
|
1003
1060
|
}
|
|
1004
1061
|
function createTurnstileWidget(env, input) {
|
|
1005
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
1062
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1006
1063
|
if (!accountId) {
|
|
1007
1064
|
throw new Error("Configure CLOUDFLARE_ACCOUNT_ID before creating Turnstile widgets.");
|
|
1008
1065
|
}
|
|
@@ -1022,7 +1079,7 @@ function createTurnstileWidget(env, input) {
|
|
|
1022
1079
|
}
|
|
1023
1080
|
}
|
|
1024
1081
|
function updateTurnstileWidget(env, sitekey, input) {
|
|
1025
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
1082
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1026
1083
|
if (!accountId || !sitekey) {
|
|
1027
1084
|
throw new Error("Configure CLOUDFLARE_ACCOUNT_ID and sitekey before updating Turnstile widgets.");
|
|
1028
1085
|
}
|
|
@@ -1125,13 +1182,10 @@ function buildProvisioningSummary(deployConfig, state, target) {
|
|
|
1125
1182
|
formGuardKv: state.kvNamespaces.FORM_GUARD_KV,
|
|
1126
1183
|
sessionKv: state.kvNamespaces.SESSION ?? null,
|
|
1127
1184
|
siteDataDb: state.d1Databases.SITE_DATA_DB,
|
|
1128
|
-
queue: state.queues?.agentWork ?? null,
|
|
1129
1185
|
content: state.content ?? null,
|
|
1130
1186
|
resources: {
|
|
1131
1187
|
pagesProject: state.pages?.projectName ?? null,
|
|
1132
1188
|
contentBucket: state.content?.bucketName ?? null,
|
|
1133
|
-
queue: state.queues?.agentWork?.name ?? null,
|
|
1134
|
-
dlq: state.queues?.agentWork?.dlqName ?? null,
|
|
1135
1189
|
database: state.d1Databases?.SITE_DATA_DB?.databaseName ?? null,
|
|
1136
1190
|
turnstileWidget: state.turnstileWidgets?.formGuard?.name ?? null,
|
|
1137
1191
|
formGuardKv: state.kvNamespaces?.FORM_GUARD_KV?.name ?? null,
|
|
@@ -1199,6 +1253,13 @@ function shouldManageCloudflareWebCacheRules(deployConfig, target) {
|
|
|
1199
1253
|
return Boolean(webTarget?.host && !webTarget.host.endsWith(".workers.dev") && !webTarget.host.endsWith(".pages.dev"));
|
|
1200
1254
|
}
|
|
1201
1255
|
function cloudflareApiRequest(path, { method = "GET", body, env, allowFailure = false } = {}) {
|
|
1256
|
+
const token = env?.TREESEED_CLOUDFLARE_API_TOKEN ?? env?.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? "";
|
|
1257
|
+
if (!token) {
|
|
1258
|
+
if (allowFailure) {
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
throw new Error(`Cloudflare API token is required: ${method} ${path}`);
|
|
1262
|
+
}
|
|
1202
1263
|
const requestScript = `import { readFileSync } from 'node:fs';
|
|
1203
1264
|
import { request } from 'node:https';
|
|
1204
1265
|
const input = JSON.parse(readFileSync(0, 'utf8') || '{}');
|
|
@@ -1262,9 +1323,13 @@ try {
|
|
|
1262
1323
|
method,
|
|
1263
1324
|
body,
|
|
1264
1325
|
timeoutMs: 12e3,
|
|
1265
|
-
token
|
|
1326
|
+
token
|
|
1266
1327
|
});
|
|
1267
|
-
const isTransient = (text) => /fetch failed|timed out|etimedout|econnreset|enetunreach|temporarily unavailable|aborted/iu.test(text || "");
|
|
1328
|
+
const isTransient = (text) => /fetch failed|timed out|etimedout|econnreset|enetunreach|temporarily unavailable|aborted|rate limit|too many requests|throttl|please wait/iu.test(text || "");
|
|
1329
|
+
const retryDelay = (text, currentAttempt) => {
|
|
1330
|
+
const base = /rate limit|too many requests|throttl|please wait/iu.test(text || "") ? 2500 : 500;
|
|
1331
|
+
return base * (currentAttempt + 1);
|
|
1332
|
+
};
|
|
1268
1333
|
const formatPayloadErrors = (payload) => Array.isArray(payload?.errors) ? payload.errors.map((entry) => entry?.message ?? JSON.stringify(entry)).join("; ") : "";
|
|
1269
1334
|
const summarizeChildError = (text) => {
|
|
1270
1335
|
const lines = String(text || "").split("\n").map((line) => line.trim()).filter(Boolean);
|
|
@@ -1288,9 +1353,9 @@ try {
|
|
|
1288
1353
|
}
|
|
1289
1354
|
);
|
|
1290
1355
|
if (response.error?.code === "ETIMEDOUT") {
|
|
1291
|
-
if (attempt <
|
|
1356
|
+
if (attempt < 7) {
|
|
1292
1357
|
attempt += 1;
|
|
1293
|
-
sleepSync(
|
|
1358
|
+
sleepSync(retryDelay("timed out", attempt));
|
|
1294
1359
|
continue;
|
|
1295
1360
|
}
|
|
1296
1361
|
if (!allowFailure) {
|
|
@@ -1300,9 +1365,9 @@ try {
|
|
|
1300
1365
|
}
|
|
1301
1366
|
const stderr = response.stderr?.trim() || "";
|
|
1302
1367
|
if (response.status !== 0) {
|
|
1303
|
-
if (attempt <
|
|
1368
|
+
if (attempt < 7 && isTransient(stderr)) {
|
|
1304
1369
|
attempt += 1;
|
|
1305
|
-
sleepSync(
|
|
1370
|
+
sleepSync(retryDelay(stderr, attempt));
|
|
1306
1371
|
continue;
|
|
1307
1372
|
}
|
|
1308
1373
|
if (!allowFailure) {
|
|
@@ -1323,9 +1388,9 @@ try {
|
|
|
1323
1388
|
};
|
|
1324
1389
|
}
|
|
1325
1390
|
const details = formatPayloadErrors(parsed.payload);
|
|
1326
|
-
if (!parsed.ok &&
|
|
1391
|
+
if (!parsed.ok && isTransient(details) && attempt < 7) {
|
|
1327
1392
|
attempt += 1;
|
|
1328
|
-
sleepSync(
|
|
1393
|
+
sleepSync(retryDelay(details, attempt));
|
|
1329
1394
|
continue;
|
|
1330
1395
|
}
|
|
1331
1396
|
if (!parsed.ok && !allowFailure) {
|
|
@@ -1352,6 +1417,13 @@ function listCloudflareZoneRulesets(zoneId, env) {
|
|
|
1352
1417
|
const result = cloudflareApiRequest(`/zones/${zoneId}/rulesets`, { env, allowFailure: true });
|
|
1353
1418
|
return Array.isArray(result?.result) ? result.result : [];
|
|
1354
1419
|
}
|
|
1420
|
+
function joinCloudflareAndExpression(clauses) {
|
|
1421
|
+
const parts = clauses.map((clause) => typeof clause === "string" ? clause.trim() : "").filter((clause) => clause.length > 0).map((clause) => clause.startsWith("(") && clause.endsWith(")") ? clause : `(${clause})`);
|
|
1422
|
+
if (parts.length === 0) {
|
|
1423
|
+
throw new Error("Cannot build a Cloudflare expression without predicates.");
|
|
1424
|
+
}
|
|
1425
|
+
return `(${parts.join(" and ")})`;
|
|
1426
|
+
}
|
|
1355
1427
|
function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kind) {
|
|
1356
1428
|
if (!cacheTarget?.host) {
|
|
1357
1429
|
return [];
|
|
@@ -1359,12 +1431,16 @@ function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kin
|
|
|
1359
1431
|
const policy = resolveTreeseedWebCachePolicy(deployConfig);
|
|
1360
1432
|
const cachePolicy = kind === "web" ? policy.contentPages : policy.r2PublishedObjects;
|
|
1361
1433
|
const hostExpression = `(http.host eq "${cacheTarget.host}")`;
|
|
1362
|
-
const pathExpression = cacheTarget.pathPrefix ? `(starts_with(http.request.uri.path, "${cacheTarget.pathPrefix}/") or (http.request.uri.path eq "${cacheTarget.pathPrefix}"))` :
|
|
1434
|
+
const pathExpression = cacheTarget.pathPrefix ? `(starts_with(http.request.uri.path, "${cacheTarget.pathPrefix}/") or (http.request.uri.path eq "${cacheTarget.pathPrefix}"))` : null;
|
|
1363
1435
|
if (kind === "content") {
|
|
1364
1436
|
return [
|
|
1365
1437
|
{
|
|
1366
1438
|
description: "treeseed-managed: cache public r2 objects",
|
|
1367
|
-
expression:
|
|
1439
|
+
expression: joinCloudflareAndExpression([
|
|
1440
|
+
hostExpression,
|
|
1441
|
+
pathExpression,
|
|
1442
|
+
'(http.request.method in {"GET" "HEAD"})'
|
|
1443
|
+
]),
|
|
1368
1444
|
action: "set_cache_settings",
|
|
1369
1445
|
action_parameters: {
|
|
1370
1446
|
cache: true,
|
|
@@ -1382,21 +1458,31 @@ function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kin
|
|
|
1382
1458
|
];
|
|
1383
1459
|
}
|
|
1384
1460
|
const sourcePaths = policy.sourcePages.paths.map((path) => path === "/" ? "/" : path.replace(/\/+$/u, ""));
|
|
1385
|
-
const sourcePathExpression = sourcePaths.length > 0 ? `(${sourcePaths.map((path) => `(http.request.uri.path eq "${path}")`).join(" or ")})` :
|
|
1386
|
-
const notSourcePathExpression = sourcePaths.length > 0 ? `not ${sourcePathExpression}` :
|
|
1387
|
-
|
|
1461
|
+
const sourcePathExpression = sourcePaths.length > 0 ? `(${sourcePaths.map((path) => `(http.request.uri.path eq "${path}")`).join(" or ")})` : null;
|
|
1462
|
+
const notSourcePathExpression = sourcePaths.length > 0 ? `not ${sourcePathExpression}` : null;
|
|
1463
|
+
const rules = [
|
|
1388
1464
|
{
|
|
1389
1465
|
description: "treeseed-managed: bypass preview and dynamic routes",
|
|
1390
|
-
expression:
|
|
1466
|
+
expression: joinCloudflareAndExpression([
|
|
1467
|
+
hostExpression,
|
|
1468
|
+
'((starts_with(http.request.uri.path, "/api/")) or (http.request.uri.path eq "/api") or (starts_with(http.request.uri.path, "/auth")) or (starts_with(http.request.uri.path, "/admin")) or (starts_with(http.request.uri.path, "/app")) or (starts_with(http.request.uri.path, "/internal")) or (http.request.uri.query contains "preview=") or (http.cookie contains "treeseed-content-preview="))'
|
|
1469
|
+
]),
|
|
1391
1470
|
action: "set_cache_settings",
|
|
1392
1471
|
action_parameters: {
|
|
1393
1472
|
cache: false
|
|
1394
1473
|
},
|
|
1395
1474
|
enabled: true
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1475
|
+
}
|
|
1476
|
+
];
|
|
1477
|
+
if (sourcePathExpression) {
|
|
1478
|
+
rules.push({
|
|
1398
1479
|
description: "treeseed-managed: cache source html routes",
|
|
1399
|
-
expression:
|
|
1480
|
+
expression: joinCloudflareAndExpression([
|
|
1481
|
+
hostExpression,
|
|
1482
|
+
pathExpression,
|
|
1483
|
+
sourcePathExpression,
|
|
1484
|
+
'(http.request.method in {"GET" "HEAD"})'
|
|
1485
|
+
]),
|
|
1400
1486
|
action: "set_cache_settings",
|
|
1401
1487
|
action_parameters: {
|
|
1402
1488
|
cache: true,
|
|
@@ -1410,10 +1496,26 @@ function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kin
|
|
|
1410
1496
|
}
|
|
1411
1497
|
},
|
|
1412
1498
|
enabled: true
|
|
1413
|
-
}
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
rules.push(
|
|
1414
1502
|
{
|
|
1415
1503
|
description: "treeseed-managed: cache content html routes",
|
|
1416
|
-
expression:
|
|
1504
|
+
expression: joinCloudflareAndExpression([
|
|
1505
|
+
hostExpression,
|
|
1506
|
+
pathExpression,
|
|
1507
|
+
notSourcePathExpression,
|
|
1508
|
+
'(http.request.method in {"GET" "HEAD"})',
|
|
1509
|
+
'(http.request.uri.path.extension eq "")',
|
|
1510
|
+
'not (starts_with(http.request.uri.path, "/api/"))',
|
|
1511
|
+
'not (http.request.uri.path eq "/api")',
|
|
1512
|
+
'not (starts_with(http.request.uri.path, "/auth"))',
|
|
1513
|
+
'not (starts_with(http.request.uri.path, "/admin"))',
|
|
1514
|
+
'not (starts_with(http.request.uri.path, "/app"))',
|
|
1515
|
+
'not (starts_with(http.request.uri.path, "/internal"))',
|
|
1516
|
+
'not (http.request.uri.query contains "preview=")',
|
|
1517
|
+
'not (http.cookie contains "treeseed-content-preview=")'
|
|
1518
|
+
]),
|
|
1417
1519
|
action: "set_cache_settings",
|
|
1418
1520
|
action_parameters: {
|
|
1419
1521
|
cache: true,
|
|
@@ -1428,7 +1530,8 @@ function buildTreeseedManagedCloudflareCacheRules(deployConfig, cacheTarget, kin
|
|
|
1428
1530
|
},
|
|
1429
1531
|
enabled: true
|
|
1430
1532
|
}
|
|
1431
|
-
|
|
1533
|
+
);
|
|
1534
|
+
return rules;
|
|
1432
1535
|
}
|
|
1433
1536
|
function reconcileCloudflareCacheRulesForTarget(role, deployConfig, state, cacheTarget, env, { dryRun = false } = {}) {
|
|
1434
1537
|
const roleKey = role === "web" ? "Web" : "Content";
|
|
@@ -1482,7 +1585,7 @@ function reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, targe
|
|
|
1482
1585
|
return { managed: false, skipped: true, reason: "unsupported_target_or_host" };
|
|
1483
1586
|
}
|
|
1484
1587
|
const env = {
|
|
1485
|
-
CLOUDFLARE_API_TOKEN: providedEnv?.CLOUDFLARE_API_TOKEN ?? process.env.
|
|
1588
|
+
CLOUDFLARE_API_TOKEN: providedEnv?.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? ""
|
|
1486
1589
|
};
|
|
1487
1590
|
if (!env.CLOUDFLARE_API_TOKEN) {
|
|
1488
1591
|
state.webCache.webHost = resolvePublicWebCacheTarget(deployConfig)?.host ?? null;
|
|
@@ -1555,7 +1658,7 @@ function queueId(entry) {
|
|
|
1555
1658
|
}
|
|
1556
1659
|
function hasProvisionedCloudflareResources(state) {
|
|
1557
1660
|
return Boolean(
|
|
1558
|
-
state?.pages?.projectName && state?.pages?.url && state?.d1Databases?.SITE_DATA_DB?.databaseId && state?.kvNamespaces?.FORM_GUARD_KV?.id && state?.
|
|
1661
|
+
state?.pages?.projectName && state?.pages?.url && state?.d1Databases?.SITE_DATA_DB?.databaseId && state?.kvNamespaces?.FORM_GUARD_KV?.id && state?.content?.bucketName
|
|
1559
1662
|
);
|
|
1560
1663
|
}
|
|
1561
1664
|
function absoluteUrlForPath(baseUrl, path) {
|
|
@@ -1585,14 +1688,14 @@ function purgeSourcePageCaches(tenantRoot, options = {}) {
|
|
|
1585
1688
|
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1586
1689
|
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
1587
1690
|
const urls = resolveSourcePagePurgeUrls(deployConfig);
|
|
1588
|
-
if ((options.dryRun ?? false) || urls.length === 0 || !process.env.
|
|
1691
|
+
if ((options.dryRun ?? false) || urls.length === 0 || !process.env.TREESEED_CLOUDFLARE_API_TOKEN) {
|
|
1589
1692
|
recordCachePurgeResult(state.webCache.deployPurge, urls.map((url) => ({ count: url ? 1 : 0 })));
|
|
1590
1693
|
writeDeployState(tenantRoot, state, { target });
|
|
1591
1694
|
return { skipped: options.dryRun ?? false, urls, results: [] };
|
|
1592
1695
|
}
|
|
1593
1696
|
try {
|
|
1594
1697
|
const results = purgeCloudflareCacheByUrls(urls, deployConfig, {
|
|
1595
|
-
env: { CLOUDFLARE_API_TOKEN: process.env.
|
|
1698
|
+
env: { CLOUDFLARE_API_TOKEN: process.env.TREESEED_CLOUDFLARE_API_TOKEN }
|
|
1596
1699
|
});
|
|
1597
1700
|
recordCachePurgeResult(state.webCache.deployPurge, results);
|
|
1598
1701
|
writeDeployState(tenantRoot, state, { target });
|
|
@@ -1607,14 +1710,14 @@ function purgePublishedContentCaches(tenantRoot, urls, options = {}) {
|
|
|
1607
1710
|
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
1608
1711
|
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1609
1712
|
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
1610
|
-
if ((options.dryRun ?? false) || !urls?.length || !process.env.
|
|
1713
|
+
if ((options.dryRun ?? false) || !urls?.length || !process.env.TREESEED_CLOUDFLARE_API_TOKEN) {
|
|
1611
1714
|
recordCachePurgeResult(state.webCache.contentPurge, (urls ?? []).map((url) => ({ count: url ? 1 : 0 })));
|
|
1612
1715
|
writeDeployState(tenantRoot, state, { target });
|
|
1613
1716
|
return { skipped: options.dryRun ?? false, urls: urls ?? [], results: [] };
|
|
1614
1717
|
}
|
|
1615
1718
|
try {
|
|
1616
1719
|
const results = purgeCloudflareCacheByUrls(urls, deployConfig, {
|
|
1617
|
-
env: { CLOUDFLARE_API_TOKEN: process.env.
|
|
1720
|
+
env: { CLOUDFLARE_API_TOKEN: process.env.TREESEED_CLOUDFLARE_API_TOKEN }
|
|
1618
1721
|
});
|
|
1619
1722
|
recordCachePurgeResult(state.webCache.contentPurge, results);
|
|
1620
1723
|
writeDeployState(tenantRoot, state, { target });
|
|
@@ -1634,8 +1737,41 @@ function isPlaceholderAccountId(value) {
|
|
|
1634
1737
|
function resolveConfiguredCloudflareAccountId(deployConfig) {
|
|
1635
1738
|
return envOrNull("CLOUDFLARE_ACCOUNT_ID") ?? deployConfig.cloudflare.accountId;
|
|
1636
1739
|
}
|
|
1637
|
-
function
|
|
1638
|
-
|
|
1740
|
+
function normalizeConfiguredBaseUrl(value) {
|
|
1741
|
+
const raw = typeof value === "string" ? value.trim() : "";
|
|
1742
|
+
if (!raw) {
|
|
1743
|
+
return null;
|
|
1744
|
+
}
|
|
1745
|
+
return raw.replace(/\/+$/u, "");
|
|
1746
|
+
}
|
|
1747
|
+
function domainBaseUrl(domain) {
|
|
1748
|
+
const raw = typeof domain === "string" ? domain.trim() : "";
|
|
1749
|
+
if (!raw) {
|
|
1750
|
+
return null;
|
|
1751
|
+
}
|
|
1752
|
+
if (/^https?:\/\//iu.test(raw)) {
|
|
1753
|
+
return normalizeConfiguredBaseUrl(raw);
|
|
1754
|
+
}
|
|
1755
|
+
return `https://${raw.replace(/^\/+|\/+$/gu, "")}`;
|
|
1756
|
+
}
|
|
1757
|
+
function targetEnvironmentKey(target) {
|
|
1758
|
+
if (target?.kind === "persistent") {
|
|
1759
|
+
return target.scope;
|
|
1760
|
+
}
|
|
1761
|
+
return "staging";
|
|
1762
|
+
}
|
|
1763
|
+
function resolveConfiguredApiConnectionBaseUrl(deployConfig, target) {
|
|
1764
|
+
const scope = targetEnvironmentKey(target);
|
|
1765
|
+
if (scope === "local") {
|
|
1766
|
+
return normalizeConfiguredBaseUrl(deployConfig.connections?.api?.localBaseUrl) ?? normalizeConfiguredBaseUrl(deployConfig.connections?.api?.environments?.local?.baseUrl) ?? domainBaseUrl(deployConfig.connections?.api?.environments?.local?.domain);
|
|
1767
|
+
}
|
|
1768
|
+
return normalizeConfiguredBaseUrl(deployConfig.connections?.api?.environments?.[scope]?.baseUrl) ?? domainBaseUrl(deployConfig.connections?.api?.environments?.[scope]?.domain);
|
|
1769
|
+
}
|
|
1770
|
+
function resolveConfiguredMarketBaseUrl(deployConfig, target) {
|
|
1771
|
+
return resolveConfiguredApiConnectionBaseUrl(deployConfig, target) ?? envOrNull("TREESEED_API_BASE_URL") ?? deployConfig.runtime?.marketBaseUrl ?? deployConfig.hosting?.marketBaseUrl ?? envOrNull("TREESEED_CENTRAL_MARKET_API_BASE_URL") ?? DEFAULT_TREESEED_MARKET_BASE_URL;
|
|
1772
|
+
}
|
|
1773
|
+
function resolveConfiguredCentralMarketBaseUrl(deployConfig, target) {
|
|
1774
|
+
return resolveConfiguredApiConnectionBaseUrl(deployConfig, target) ?? envOrNull("TREESEED_CENTRAL_MARKET_API_BASE_URL") ?? DEFAULT_TREESEED_MARKET_BASE_URL;
|
|
1639
1775
|
}
|
|
1640
1776
|
function resolveConfiguredPagesProjectName(deployConfig) {
|
|
1641
1777
|
return sharedDeploymentName(resolveTreeseedResourceIdentity(deployConfig, createPersistentDeployTarget("prod")));
|
|
@@ -1803,7 +1939,7 @@ function resolveExistingD1ByName(d1Databases, expectedName, current) {
|
|
|
1803
1939
|
};
|
|
1804
1940
|
}
|
|
1805
1941
|
function looksLikeMissingResource(output) {
|
|
1806
|
-
return /not found|does not exist|could(?: not|n't) find|couldnt find/i.test(output);
|
|
1942
|
+
return /not found|does not exist|could(?: not|n't) find|couldnt find|already deleted|deleted widget|access a deleted/i.test(output);
|
|
1807
1943
|
}
|
|
1808
1944
|
function deleteKvNamespace(tenantRoot, namespaceId, { env, dryRun, preview = false }) {
|
|
1809
1945
|
if (!namespaceId || isPlaceholderResourceId(namespaceId)) {
|
|
@@ -1812,7 +1948,7 @@ function deleteKvNamespace(tenantRoot, namespaceId, { env, dryRun, preview = fal
|
|
|
1812
1948
|
if (dryRun) {
|
|
1813
1949
|
return { status: "planned", id: namespaceId, preview };
|
|
1814
1950
|
}
|
|
1815
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
1951
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1816
1952
|
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/storage/kv/namespaces/${encodeURIComponent(namespaceId)}` : null;
|
|
1817
1953
|
const deleted = deleteCloudflareApiResource(path, { env, dryRun: false, name: namespaceId, type: "kv-namespace" });
|
|
1818
1954
|
return { status: deleted.status, id: namespaceId, preview };
|
|
@@ -1824,7 +1960,7 @@ function deleteTurnstileWidget(sitekey, { env, dryRun, name = null }) {
|
|
|
1824
1960
|
if (dryRun) {
|
|
1825
1961
|
return { status: "planned", sitekey, name };
|
|
1826
1962
|
}
|
|
1827
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
1963
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1828
1964
|
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/challenges/widgets/${encodeURIComponent(sitekey)}` : null;
|
|
1829
1965
|
let deleted;
|
|
1830
1966
|
try {
|
|
@@ -1842,7 +1978,7 @@ function deleteD1Database(tenantRoot, databaseName, { env, dryRun }) {
|
|
|
1842
1978
|
if (dryRun) {
|
|
1843
1979
|
return { status: "planned", name: databaseName };
|
|
1844
1980
|
}
|
|
1845
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
1981
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1846
1982
|
const database = accountId ? listD1Databases(tenantRoot, env).find((entry) => entry?.name === databaseName) : null;
|
|
1847
1983
|
const databaseId = database?.uuid ?? database?.id ?? null;
|
|
1848
1984
|
const path = accountId && databaseId ? `/accounts/${encodeURIComponent(accountId)}/d1/database/${encodeURIComponent(databaseId)}` : null;
|
|
@@ -1856,7 +1992,7 @@ function deleteWorker(tenantRoot, workerName, { env, dryRun, force = false }) {
|
|
|
1856
1992
|
if (dryRun) {
|
|
1857
1993
|
return { status: "planned", name: workerName };
|
|
1858
1994
|
}
|
|
1859
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
1995
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1860
1996
|
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/workers/services/${encodeURIComponent(workerName)}` : null;
|
|
1861
1997
|
const deleted = deleteCloudflareApiResource(path, { env, dryRun: false, name: workerName, type: "worker" });
|
|
1862
1998
|
return { status: deleted.status, name: workerName };
|
|
@@ -1895,7 +2031,7 @@ function deleteQueueByName(tenantRoot, queue, { env, dryRun }) {
|
|
|
1895
2031
|
if (dryRun) {
|
|
1896
2032
|
return resourceOperation("cloudflare", "queue", name, "planned", { id });
|
|
1897
2033
|
}
|
|
1898
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
2034
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1899
2035
|
if (!id && accountId) {
|
|
1900
2036
|
const live = listQueues(tenantRoot, env).find((entry) => queueName(entry) === name);
|
|
1901
2037
|
id = queueId(live);
|
|
@@ -1912,25 +2048,6 @@ function deleteQueueByName(tenantRoot, queue, { env, dryRun }) {
|
|
|
1912
2048
|
}
|
|
1913
2049
|
throw new Error(`Failed to delete queue ${name}: CLOUDFLARE_ACCOUNT_ID is not configured.`);
|
|
1914
2050
|
}
|
|
1915
|
-
function isLegacyTreeseedQueueName(name, scope) {
|
|
1916
|
-
if (!name || !scope) {
|
|
1917
|
-
return false;
|
|
1918
|
-
}
|
|
1919
|
-
const environmentNames = scope === "prod" ? ["prod", "production"] : [scope];
|
|
1920
|
-
return environmentNames.some(
|
|
1921
|
-
(environmentName) => name === `agent-work-${environmentName}` || name === `agent-work-dlq-${environmentName}`
|
|
1922
|
-
);
|
|
1923
|
-
}
|
|
1924
|
-
function legacyQueueDestroyOperations(tenantRoot, queues, target, knownNames, { env, dryRun }) {
|
|
1925
|
-
const scope = target.kind === "persistent" ? target.scope : target.branchName;
|
|
1926
|
-
return queues.filter((queue) => {
|
|
1927
|
-
const name = queueName(queue);
|
|
1928
|
-
return name && !knownNames.has(name) && isLegacyTreeseedQueueName(name, scope);
|
|
1929
|
-
}).map((queue) => {
|
|
1930
|
-
const deleted = deleteQueueByName(tenantRoot, queue, { env, dryRun });
|
|
1931
|
-
return { ...deleted, legacy: true };
|
|
1932
|
-
});
|
|
1933
|
-
}
|
|
1934
2051
|
function deleteR2Bucket(tenantRoot, bucketName, { env, dryRun, deleteData }) {
|
|
1935
2052
|
if (!bucketName) {
|
|
1936
2053
|
return resourceOperation("cloudflare", "r2-bucket", bucketName, "missing");
|
|
@@ -1942,7 +2059,7 @@ function deleteR2Bucket(tenantRoot, bucketName, { env, dryRun, deleteData }) {
|
|
|
1942
2059
|
return resourceOperation("cloudflare", "r2-bucket", bucketName, "planned");
|
|
1943
2060
|
}
|
|
1944
2061
|
const drained = drainR2Bucket(bucketName, { env });
|
|
1945
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
2062
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1946
2063
|
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}` : null;
|
|
1947
2064
|
const deleted = deleteCloudflareApiResource(path, { env, dryRun: false, name: bucketName, type: "r2-bucket" });
|
|
1948
2065
|
return resourceOperation("cloudflare", "r2-bucket", bucketName, deleted.status, drained);
|
|
@@ -1951,15 +2068,15 @@ function r2ObjectKey(entry) {
|
|
|
1951
2068
|
return typeof entry?.key === "string" ? entry.key : typeof entry?.name === "string" ? entry.name : "";
|
|
1952
2069
|
}
|
|
1953
2070
|
function listR2Objects(bucketName, { env }) {
|
|
1954
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
2071
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1955
2072
|
if (!accountId || !bucketName) {
|
|
1956
2073
|
return [];
|
|
1957
2074
|
}
|
|
1958
2075
|
const objects = [];
|
|
1959
2076
|
let cursor = "";
|
|
1960
|
-
for (let page = 0; page <
|
|
2077
|
+
for (let page = 0; page < 20; page += 1) {
|
|
1961
2078
|
const payload = cloudflareApiRequest(
|
|
1962
|
-
`/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}/objects?per_page=
|
|
2079
|
+
`/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}/objects?per_page=200${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`,
|
|
1963
2080
|
{ env, allowFailure: true }
|
|
1964
2081
|
);
|
|
1965
2082
|
if (payload?.success === false) {
|
|
@@ -1972,45 +2089,156 @@ function listR2Objects(bucketName, { env }) {
|
|
|
1972
2089
|
break;
|
|
1973
2090
|
}
|
|
1974
2091
|
cursor = nextCursor;
|
|
2092
|
+
if (objects.length >= 200) {
|
|
2093
|
+
break;
|
|
2094
|
+
}
|
|
1975
2095
|
}
|
|
1976
2096
|
return objects;
|
|
1977
2097
|
}
|
|
1978
2098
|
function drainR2Bucket(bucketName, { env }) {
|
|
1979
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
2099
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1980
2100
|
if (!accountId || !bucketName) {
|
|
1981
|
-
return { objectsDeleted: 0 };
|
|
2101
|
+
return { objectsDeleted: 0, objectsMissing: 0, objectsDeferred: 0 };
|
|
1982
2102
|
}
|
|
1983
2103
|
let objectsDeleted = 0;
|
|
2104
|
+
let objectsMissing = 0;
|
|
2105
|
+
let objectsDeferred = 0;
|
|
1984
2106
|
for (let batch = 0; batch < 100; batch += 1) {
|
|
1985
2107
|
const objects = listR2Objects(bucketName, { env });
|
|
1986
2108
|
if (objects.length === 0) {
|
|
1987
2109
|
break;
|
|
1988
2110
|
}
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2111
|
+
const keys = objects.map((object) => r2ObjectKey(object)).filter(Boolean);
|
|
2112
|
+
const deleted = deleteR2ObjectsBatch(bucketName, keys, { env });
|
|
2113
|
+
objectsDeleted += deleted.objectsDeleted;
|
|
2114
|
+
objectsMissing += deleted.objectsMissing;
|
|
2115
|
+
objectsDeferred += deleted.objectsDeferred;
|
|
2116
|
+
const batchDeleted = deleted.objectsDeleted + deleted.objectsMissing;
|
|
2117
|
+
if (batchDeleted === 0) {
|
|
2118
|
+
if (deleted.objectsDeferred > 0) {
|
|
2119
|
+
sleepSync(3e3);
|
|
1993
2120
|
continue;
|
|
1994
2121
|
}
|
|
1995
|
-
const result = cloudflareApiRequest(
|
|
1996
|
-
`/accounts/${encodeURIComponent(accountId)}/r2/buckets/${encodeURIComponent(bucketName)}/objects/${encodeURIComponent(key)}`,
|
|
1997
|
-
{ method: "DELETE", env, allowFailure: true }
|
|
1998
|
-
);
|
|
1999
|
-
if (result?.success === false) {
|
|
2000
|
-
const message = formatCloudflareErrors(result);
|
|
2001
|
-
if (looksLikeMissingResource(message)) {
|
|
2002
|
-
continue;
|
|
2003
|
-
}
|
|
2004
|
-
throw new Error(message || `Failed to delete R2 object ${key}.`);
|
|
2005
|
-
}
|
|
2006
|
-
objectsDeleted += 1;
|
|
2007
|
-
batchDeleted += 1;
|
|
2008
|
-
}
|
|
2009
|
-
if (batchDeleted === 0) {
|
|
2010
2122
|
break;
|
|
2011
2123
|
}
|
|
2124
|
+
if (deleted.objectsDeferred > 0) {
|
|
2125
|
+
sleepSync(1500);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return { objectsDeleted, objectsMissing, objectsDeferred };
|
|
2129
|
+
}
|
|
2130
|
+
function deleteR2ObjectsBatch(bucketName, keys, { env }) {
|
|
2131
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2132
|
+
const token = env?.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? "";
|
|
2133
|
+
const uniqueKeys = [...new Set((keys ?? []).filter(Boolean))];
|
|
2134
|
+
if (!accountId || !bucketName || uniqueKeys.length === 0) {
|
|
2135
|
+
return { objectsDeleted: 0, objectsMissing: 0, objectsDeferred: 0 };
|
|
2136
|
+
}
|
|
2137
|
+
const script = `
|
|
2138
|
+
const input = JSON.parse(await new Promise((resolve) => {
|
|
2139
|
+
let body = '';
|
|
2140
|
+
process.stdin.setEncoding('utf8');
|
|
2141
|
+
process.stdin.on('data', (chunk) => { body += chunk; });
|
|
2142
|
+
process.stdin.on('end', () => resolve(body || '{}'));
|
|
2143
|
+
}));
|
|
2144
|
+
let index = 0;
|
|
2145
|
+
let deleted = 0;
|
|
2146
|
+
let missing = 0;
|
|
2147
|
+
let deferred = 0;
|
|
2148
|
+
const failed = [];
|
|
2149
|
+
async function removeKey(key) {
|
|
2150
|
+
function encodeObjectKey(value) {
|
|
2151
|
+
return String(value).split('/').map((part) => encodeURIComponent(part)).join('/');
|
|
2152
|
+
}
|
|
2153
|
+
const url = 'https://api.cloudflare.com/client/v4/accounts/'
|
|
2154
|
+
+ encodeURIComponent(input.accountId)
|
|
2155
|
+
+ '/r2/buckets/'
|
|
2156
|
+
+ encodeURIComponent(input.bucketName)
|
|
2157
|
+
+ '/objects/'
|
|
2158
|
+
+ encodeObjectKey(key);
|
|
2159
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
2160
|
+
const controller = new AbortController();
|
|
2161
|
+
const timeout = setTimeout(() => controller.abort(), input.timeoutMs || 15000);
|
|
2162
|
+
try {
|
|
2163
|
+
const response = await fetch(url, {
|
|
2164
|
+
method: 'DELETE',
|
|
2165
|
+
headers: { authorization: 'Bearer ' + input.token },
|
|
2166
|
+
signal: controller.signal,
|
|
2167
|
+
});
|
|
2168
|
+
const text = await response.text();
|
|
2169
|
+
let payload = {};
|
|
2170
|
+
try { payload = text ? JSON.parse(text) : {}; } catch { payload = { errors: [{ message: text }] }; }
|
|
2171
|
+
if (response.ok && payload.success !== false) {
|
|
2172
|
+
deleted += 1;
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
const message = Array.isArray(payload.errors) ? payload.errors.map((entry) => entry?.message || JSON.stringify(entry)).join('; ') : text;
|
|
2176
|
+
if (/not found|does not exist|deleted|missing/i.test(message || '')) {
|
|
2177
|
+
missing += 1;
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
if (response.status === 429 || /rate limit|too many requests/i.test(message || '')) {
|
|
2181
|
+
await new Promise((resolve) => setTimeout(resolve, 750 * (attempt + 1)));
|
|
2182
|
+
continue;
|
|
2183
|
+
}
|
|
2184
|
+
failed.push({ key, message: message || \`delete failed with status \${response.status}\` });
|
|
2185
|
+
return;
|
|
2186
|
+
} catch (error) {
|
|
2187
|
+
if (attempt < 5 && /aborted|timed out|fetch failed|econnreset/i.test(error instanceof Error ? error.message : String(error))) {
|
|
2188
|
+
await new Promise((resolve) => setTimeout(resolve, 750 * (attempt + 1)));
|
|
2189
|
+
continue;
|
|
2190
|
+
}
|
|
2191
|
+
failed.push({ key, message: error instanceof Error ? error.message : String(error) });
|
|
2192
|
+
return;
|
|
2193
|
+
} finally {
|
|
2194
|
+
clearTimeout(timeout);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
deferred += 1;
|
|
2198
|
+
}
|
|
2199
|
+
async function worker() {
|
|
2200
|
+
for (;;) {
|
|
2201
|
+
const current = index;
|
|
2202
|
+
index += 1;
|
|
2203
|
+
if (current >= input.keys.length) return;
|
|
2204
|
+
await removeKey(input.keys[current]);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
await Promise.all(Array.from({ length: Math.min(input.concurrency || 4, input.keys.length) }, () => worker()));
|
|
2208
|
+
process.stdout.write(JSON.stringify({ deleted, missing, deferred, failed }));
|
|
2209
|
+
`.trim();
|
|
2210
|
+
const result = spawnSync(process.execPath, ["--input-type=module", "-e", script], {
|
|
2211
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
2212
|
+
encoding: "utf8",
|
|
2213
|
+
env: { ...process.env, ...env ?? {} },
|
|
2214
|
+
input: JSON.stringify({
|
|
2215
|
+
accountId,
|
|
2216
|
+
bucketName,
|
|
2217
|
+
keys: uniqueKeys,
|
|
2218
|
+
token,
|
|
2219
|
+
concurrency: 4,
|
|
2220
|
+
timeoutMs: 12e3
|
|
2221
|
+
}),
|
|
2222
|
+
timeout: 12e4
|
|
2223
|
+
});
|
|
2224
|
+
if (result.status !== 0 || result.error) {
|
|
2225
|
+
throw new Error(result.stderr?.trim() || result.error?.message || `Failed to delete R2 object batch for ${bucketName}.`);
|
|
2226
|
+
}
|
|
2227
|
+
let parsed;
|
|
2228
|
+
try {
|
|
2229
|
+
parsed = JSON.parse(result.stdout || "{}");
|
|
2230
|
+
} catch {
|
|
2231
|
+
throw new Error(`R2 object batch delete returned invalid JSON for ${bucketName}.`);
|
|
2012
2232
|
}
|
|
2013
|
-
|
|
2233
|
+
if (Array.isArray(parsed.failed) && parsed.failed.length > 0) {
|
|
2234
|
+
const first = parsed.failed[0];
|
|
2235
|
+
throw new Error(`Failed to delete ${parsed.failed.length} R2 objects from ${bucketName}: ${first?.message ?? first?.key ?? "unknown error"}`);
|
|
2236
|
+
}
|
|
2237
|
+
return {
|
|
2238
|
+
objectsDeleted: Number(parsed.deleted) || 0,
|
|
2239
|
+
objectsMissing: Number(parsed.missing) || 0,
|
|
2240
|
+
objectsDeferred: Number(parsed.deferred) || 0
|
|
2241
|
+
};
|
|
2014
2242
|
}
|
|
2015
2243
|
function deleteD1DatabaseForDestroy(tenantRoot, databaseName, { env, dryRun, deleteData }) {
|
|
2016
2244
|
if (!deleteData) {
|
|
@@ -2023,7 +2251,7 @@ function pagesDomainName(domain) {
|
|
|
2023
2251
|
return typeof domain?.name === "string" ? domain.name : typeof domain?.domain === "string" ? domain.domain : typeof domain?.hostname === "string" ? domain.hostname : "";
|
|
2024
2252
|
}
|
|
2025
2253
|
function listPagesCustomDomains(projectName, { env }) {
|
|
2026
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
2254
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2027
2255
|
if (!projectName || !accountId) {
|
|
2028
2256
|
return [];
|
|
2029
2257
|
}
|
|
@@ -2066,20 +2294,20 @@ function listPagesCustomDomainsWithWrangler(tenantRoot, projectName, { env }) {
|
|
|
2066
2294
|
return [];
|
|
2067
2295
|
}
|
|
2068
2296
|
}
|
|
2069
|
-
function deletePagesCustomDomains(tenantRoot, projectName, knownNames, { env, dryRun }) {
|
|
2297
|
+
function deletePagesCustomDomains(tenantRoot, projectName, knownNames, { env, dryRun, knownOnly = false }) {
|
|
2070
2298
|
if (!projectName) {
|
|
2071
2299
|
return [resourceOperation("cloudflare", "pages-custom-domain", projectName, "missing")];
|
|
2072
2300
|
}
|
|
2073
2301
|
const desiredNames = [...new Set((knownNames ?? []).filter(Boolean))];
|
|
2074
2302
|
if (dryRun) {
|
|
2075
|
-
return desiredNames.length > 0 ? desiredNames.map((name) => resourceOperation("cloudflare", "pages-custom-domain", name, "planned", { projectName })) : [resourceOperation("cloudflare", "pages-custom-domain", projectName, "planned", { reason: "project_delete_prerequisite" })];
|
|
2303
|
+
return desiredNames.length > 0 ? desiredNames.map((name) => resourceOperation("cloudflare", "pages-custom-domain", name, "planned", { projectName, knownOnly })) : [resourceOperation("cloudflare", "pages-custom-domain", projectName, knownOnly ? "skipped" : "planned", { reason: knownOnly ? "no_target_scoped_domain" : "project_delete_prerequisite" })];
|
|
2076
2304
|
}
|
|
2077
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
2305
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2078
2306
|
if (!accountId) {
|
|
2079
2307
|
return desiredNames.length > 0 ? desiredNames.map((name) => resourceOperation("cloudflare", "pages-custom-domain", name, "blocked", { projectName, reason: "missing_cloudflare_account_id" })) : [resourceOperation("cloudflare", "pages-custom-domain", projectName, "blocked", { reason: "missing_cloudflare_account_id" })];
|
|
2080
2308
|
}
|
|
2081
|
-
const listedNames = listPagesCustomDomains(projectName, { env }).map(pagesDomainName).filter(Boolean);
|
|
2082
|
-
const wranglerNames = listPagesCustomDomainsWithWrangler(tenantRoot, projectName, { env });
|
|
2309
|
+
const listedNames = knownOnly ? [] : listPagesCustomDomains(projectName, { env }).map(pagesDomainName).filter(Boolean);
|
|
2310
|
+
const wranglerNames = knownOnly ? [] : listPagesCustomDomainsWithWrangler(tenantRoot, projectName, { env });
|
|
2083
2311
|
const domainNames = [.../* @__PURE__ */ new Set([...desiredNames, ...listedNames, ...wranglerNames])];
|
|
2084
2312
|
if (domainNames.length === 0) {
|
|
2085
2313
|
return [resourceOperation("cloudflare", "pages-custom-domain", projectName, "missing", { projectName })];
|
|
@@ -2095,13 +2323,16 @@ function normalizePagesDeploymentId(deployment) {
|
|
|
2095
2323
|
function normalizePagesDeployments(value) {
|
|
2096
2324
|
return (Array.isArray(value) ? value : Array.isArray(value?.result) ? value.result : []).filter((entry) => normalizePagesDeploymentId(entry));
|
|
2097
2325
|
}
|
|
2098
|
-
function
|
|
2099
|
-
|
|
2326
|
+
function pagesDeploymentEnvironments(environment = "all") {
|
|
2327
|
+
return environment === "preview" ? ["preview"] : environment === "production" ? ["production"] : ["preview", "production"];
|
|
2328
|
+
}
|
|
2329
|
+
function listPagesDeploymentsWithApi(projectName, { env, environment = "all" }) {
|
|
2330
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2100
2331
|
if (!projectName || !accountId) {
|
|
2101
2332
|
return [];
|
|
2102
2333
|
}
|
|
2103
2334
|
const deployments = [];
|
|
2104
|
-
for (const pagesEnvironment of
|
|
2335
|
+
for (const pagesEnvironment of pagesDeploymentEnvironments(environment)) {
|
|
2105
2336
|
let page = 1;
|
|
2106
2337
|
let totalPages = 1;
|
|
2107
2338
|
while (page <= totalPages && page <= 50) {
|
|
@@ -2120,9 +2351,9 @@ function listPagesDeploymentsWithApi(projectName, { env }) {
|
|
|
2120
2351
|
}
|
|
2121
2352
|
return deployments;
|
|
2122
2353
|
}
|
|
2123
|
-
function listPagesDeployments(tenantRoot, projectName, { env }) {
|
|
2354
|
+
function listPagesDeployments(tenantRoot, projectName, { env, environment = "all" }) {
|
|
2124
2355
|
const deployments = [];
|
|
2125
|
-
for (const pagesEnvironment of
|
|
2356
|
+
for (const pagesEnvironment of pagesDeploymentEnvironments(environment)) {
|
|
2126
2357
|
const result = runWrangler(["pages", "deployment", "list", "--project-name", projectName, "--environment", pagesEnvironment, "--json"], {
|
|
2127
2358
|
cwd: tenantRoot,
|
|
2128
2359
|
allowFailure: true,
|
|
@@ -2141,16 +2372,16 @@ function listPagesDeployments(tenantRoot, projectName, { env }) {
|
|
|
2141
2372
|
const byId = new Map(deployments.map((deployment) => [normalizePagesDeploymentId(deployment), deployment]));
|
|
2142
2373
|
return [...byId.values()];
|
|
2143
2374
|
}
|
|
2144
|
-
return listPagesDeploymentsWithApi(projectName, { env });
|
|
2375
|
+
return listPagesDeploymentsWithApi(projectName, { env, environment });
|
|
2145
2376
|
}
|
|
2146
|
-
function deletePagesDeployments(tenantRoot, projectName, { env, dryRun }) {
|
|
2377
|
+
function deletePagesDeployments(tenantRoot, projectName, { env, dryRun, environment = "all" }) {
|
|
2147
2378
|
if (!projectName) {
|
|
2148
2379
|
return resourceOperation("cloudflare", "pages-deployments", projectName, "missing");
|
|
2149
2380
|
}
|
|
2150
2381
|
if (dryRun) {
|
|
2151
|
-
return resourceOperation("cloudflare", "pages-deployments", projectName, "planned", { reason: "project_delete_prerequisite" });
|
|
2382
|
+
return resourceOperation("cloudflare", "pages-deployments", projectName, "planned", { reason: "project_delete_prerequisite", environment });
|
|
2152
2383
|
}
|
|
2153
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
2384
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2154
2385
|
if (!accountId) {
|
|
2155
2386
|
return resourceOperation("cloudflare", "pages-deployments", projectName, "blocked", { reason: "missing_cloudflare_account_id" });
|
|
2156
2387
|
}
|
|
@@ -2158,7 +2389,7 @@ function deletePagesDeployments(tenantRoot, projectName, { env, dryRun }) {
|
|
|
2158
2389
|
let skipped = 0;
|
|
2159
2390
|
let total = 0;
|
|
2160
2391
|
for (let batch = 0; batch < 100; batch += 1) {
|
|
2161
|
-
const deployments = listPagesDeployments(tenantRoot, projectName, { env });
|
|
2392
|
+
const deployments = listPagesDeployments(tenantRoot, projectName, { env, environment });
|
|
2162
2393
|
if (deployments.length === 0) {
|
|
2163
2394
|
return resourceOperation("cloudflare", "pages-deployments", projectName, deleted > 0 ? "deleted" : "missing", {
|
|
2164
2395
|
deleted,
|
|
@@ -2204,7 +2435,7 @@ function deletePagesDeployments(tenantRoot, projectName, { env, dryRun }) {
|
|
|
2204
2435
|
});
|
|
2205
2436
|
}
|
|
2206
2437
|
function deletePagesProject(projectName, { env, dryRun }) {
|
|
2207
|
-
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
2438
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
2208
2439
|
if (!projectName || !accountId) {
|
|
2209
2440
|
return resourceOperation("cloudflare", "pages-project", projectName, "missing");
|
|
2210
2441
|
}
|
|
@@ -2286,31 +2517,31 @@ function configuredRailwayDestroyTargets(tenantRoot, deployConfig, scope) {
|
|
|
2286
2517
|
identity = { deploymentKey: deployConfig.slug ?? deployConfig.name ?? "treeseed" };
|
|
2287
2518
|
}
|
|
2288
2519
|
const services = [];
|
|
2289
|
-
for (const serviceKey of ["api", "
|
|
2520
|
+
for (const serviceKey of ["api", "operationsRunner"]) {
|
|
2290
2521
|
const service = deployConfig.services?.[serviceKey];
|
|
2291
2522
|
if (!service || service.enabled === false || (service.provider ?? "railway") !== "railway") {
|
|
2292
2523
|
continue;
|
|
2293
2524
|
}
|
|
2294
|
-
const baseServiceName = service.railway?.serviceName ?? `${identity.deploymentKey}-${serviceKey === "
|
|
2295
|
-
const runnerPool = serviceKey === "
|
|
2296
|
-
const count = serviceKey === "
|
|
2525
|
+
const baseServiceName = service.railway?.serviceName ?? `${identity.deploymentKey}-${serviceKey === "operationsRunner" ? "operations-runner" : serviceKey}`;
|
|
2526
|
+
const runnerPool = serviceKey === "operationsRunner" && service.railway?.runnerPool && typeof service.railway.runnerPool === "object" ? service.railway.runnerPool : null;
|
|
2527
|
+
const count = serviceKey === "operationsRunner" ? Math.max(1, Number.parseInt(String(runnerPool?.bootstrapCount ?? 1), 10) || 1) : 1;
|
|
2297
2528
|
for (let index = 1; index <= count; index += 1) {
|
|
2298
|
-
const serviceName = serviceKey === "
|
|
2529
|
+
const serviceName = serviceKey === "operationsRunner" ? `${String(baseServiceName).replace(/-\d+$/u, "").replace(/-\d{2}$/u, "")}-${String(index).padStart(2, "0")}` : baseServiceName;
|
|
2299
2530
|
services.push({
|
|
2300
2531
|
key: serviceKey,
|
|
2301
2532
|
projectName: service.railway?.projectName ?? identity.deploymentKey,
|
|
2302
2533
|
serviceName,
|
|
2303
2534
|
railwayEnvironment: normalizeRailwayEnvironmentName(service.environments?.[normalizedScope]?.railwayEnvironment ?? normalizedScope),
|
|
2304
2535
|
domain: service.environments?.[normalizedScope]?.domain ?? null,
|
|
2305
|
-
volumeMountPath: serviceKey === "
|
|
2536
|
+
volumeMountPath: serviceKey === "operationsRunner" ? service.railway?.volumeMountPath ?? runnerPool?.volumeMountPath ?? "/data" : null
|
|
2306
2537
|
});
|
|
2307
2538
|
}
|
|
2308
2539
|
}
|
|
2309
|
-
const
|
|
2310
|
-
if (
|
|
2311
|
-
const baseName = typeof
|
|
2540
|
+
const treeseedDatabase = deployConfig.services?.treeseedDatabase;
|
|
2541
|
+
if (treeseedDatabase?.enabled !== false && treeseedDatabase?.provider === "railway" && treeseedDatabase?.railway?.resourceType === "postgres") {
|
|
2542
|
+
const baseName = typeof treeseedDatabase.railway?.serviceName === "string" && treeseedDatabase.railway.serviceName.trim() ? treeseedDatabase.railway.serviceName.trim() : `${deployConfig.slug ?? "treeseed-market"}-postgres`;
|
|
2312
2543
|
services.push({
|
|
2313
|
-
key: "
|
|
2544
|
+
key: "treeseedDatabase",
|
|
2314
2545
|
projectName: deployConfig.services?.api?.railway?.projectName ?? identity.deploymentKey,
|
|
2315
2546
|
serviceName: `${baseName.replace(/-(staging|prod|production)$/u, "")}-${normalizedScope === "prod" ? "prod" : normalizedScope}`,
|
|
2316
2547
|
railwayEnvironment: normalizeRailwayEnvironmentName(normalizedScope),
|
|
@@ -2396,17 +2627,19 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
|
|
|
2396
2627
|
}
|
|
2397
2628
|
}
|
|
2398
2629
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
const
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2630
|
+
if (deleteData) {
|
|
2631
|
+
const volumes = await listRailwayVolumes({ projectId: project.id, env });
|
|
2632
|
+
for (const volume of volumes) {
|
|
2633
|
+
const matchingInstance = volume.instances.find((instance) => instance.environmentId === environment?.id);
|
|
2634
|
+
if (!matchingInstance) {
|
|
2635
|
+
continue;
|
|
2636
|
+
}
|
|
2637
|
+
if (dryRun) {
|
|
2638
|
+
operations.push(resourceOperation("railway", "volume", volume.name, "planned", { id: volume.id, projectId: project.id }));
|
|
2639
|
+
} else {
|
|
2640
|
+
const result = await deleteRailwayVolume({ volumeId: volume.id, env });
|
|
2641
|
+
operations.push(resourceOperation("railway", "volume", volume.name, result.status, { id: volume.id, projectId: project.id }));
|
|
2642
|
+
}
|
|
2410
2643
|
}
|
|
2411
2644
|
}
|
|
2412
2645
|
const shouldDeleteProject = shouldDeleteRailwayProjectAfterEnvironmentDestroy(project, scope, deleteData, environment?.id ?? null);
|
|
@@ -2417,11 +2650,7 @@ async function destroyRailwayResources(tenantRoot, deployConfig, target, { dryRu
|
|
|
2417
2650
|
reason: scope === "prod" ? "prod_delete_data_cleanup" : "no_managed_persistent_environments"
|
|
2418
2651
|
}));
|
|
2419
2652
|
} else {
|
|
2420
|
-
|
|
2421
|
-
operations.push(resourceOperation("railway", "project", project.name, result.status, {
|
|
2422
|
-
id: project.id,
|
|
2423
|
-
reason: scope === "prod" ? "prod_delete_data_cleanup" : "no_managed_persistent_environments"
|
|
2424
|
-
}));
|
|
2653
|
+
throw new Error("Railway project deletion is reconciler-owned. Use trsd reconcile destroy or live acceptance cleanup for project-scoped deletion.");
|
|
2425
2654
|
}
|
|
2426
2655
|
} else if (environment) {
|
|
2427
2656
|
if (dryRun) {
|
|
@@ -2465,6 +2694,87 @@ function killPidFromFile(filePath, { dryRun }) {
|
|
|
2465
2694
|
}
|
|
2466
2695
|
return resourceOperation("local", "dev-process", String(pid), "deleted", { pidFile: filePath });
|
|
2467
2696
|
}
|
|
2697
|
+
const LOCAL_DOCKER_RESOURCE_PATTERN = /(?:^|[-_.])(?:treeseed|treedx|treedb)(?:[-_.]|$)|(?:treeseed|treedx|treedb)/iu;
|
|
2698
|
+
let destroyDockerRunnerForTests = null;
|
|
2699
|
+
function setDestroyDockerRunnerForTests(runner) {
|
|
2700
|
+
destroyDockerRunnerForTests = runner;
|
|
2701
|
+
}
|
|
2702
|
+
function runDestroyDocker(args) {
|
|
2703
|
+
if (destroyDockerRunnerForTests) {
|
|
2704
|
+
return destroyDockerRunnerForTests(args);
|
|
2705
|
+
}
|
|
2706
|
+
return spawnSync("docker", args, { encoding: "utf8", stdio: "pipe" });
|
|
2707
|
+
}
|
|
2708
|
+
function dockerAvailable() {
|
|
2709
|
+
const result = runDestroyDocker(["info"]);
|
|
2710
|
+
return result.status === 0;
|
|
2711
|
+
}
|
|
2712
|
+
function dockerList(formatArgs) {
|
|
2713
|
+
const result = runDestroyDocker(formatArgs);
|
|
2714
|
+
if (result.status !== 0) {
|
|
2715
|
+
return [];
|
|
2716
|
+
}
|
|
2717
|
+
return result.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
2718
|
+
}
|
|
2719
|
+
function matchingDockerEntries(lines, parser) {
|
|
2720
|
+
return lines.map(parser).filter((entry) => entry && LOCAL_DOCKER_RESOURCE_PATTERN.test(`${entry.name} ${entry.image ?? ""}`));
|
|
2721
|
+
}
|
|
2722
|
+
function removeDockerResource(kind, id, name) {
|
|
2723
|
+
const args = kind === "container" ? ["rm", "-f", id] : kind === "volume" ? ["volume", "rm", "-f", id] : ["network", "rm", id];
|
|
2724
|
+
const result = runDestroyDocker(args);
|
|
2725
|
+
if (result.status === 0) {
|
|
2726
|
+
return resourceOperation("local", `docker-${kind}`, name, "deleted", { id });
|
|
2727
|
+
}
|
|
2728
|
+
return resourceOperation("local", `docker-${kind}`, name, "blocked", {
|
|
2729
|
+
id,
|
|
2730
|
+
reason: result.stderr?.trim() || result.stdout?.trim() || "docker_remove_failed"
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2733
|
+
function dockerLocalRuntimeResourceOperations({ dryRun = false } = {}) {
|
|
2734
|
+
if (!dockerAvailable()) {
|
|
2735
|
+
return [resourceOperation("local", "docker-cleanup", "docker", "skipped", { reason: "docker_unavailable" })];
|
|
2736
|
+
}
|
|
2737
|
+
const containers = matchingDockerEntries(
|
|
2738
|
+
dockerList(["ps", "-a", "--format", "{{.ID}} {{.Names}} {{.Image}}"]),
|
|
2739
|
+
(line) => {
|
|
2740
|
+
const [id, name, image] = line.split(" ");
|
|
2741
|
+
return id && name ? { id, name, image } : null;
|
|
2742
|
+
}
|
|
2743
|
+
);
|
|
2744
|
+
const volumes = matchingDockerEntries(
|
|
2745
|
+
dockerList(["volume", "ls", "--format", "{{.Name}}"]),
|
|
2746
|
+
(line) => ({ id: line, name: line })
|
|
2747
|
+
);
|
|
2748
|
+
const networks = matchingDockerEntries(
|
|
2749
|
+
dockerList(["network", "ls", "--format", "{{.ID}} {{.Name}}"]),
|
|
2750
|
+
(line) => {
|
|
2751
|
+
const [id, name] = line.split(" ");
|
|
2752
|
+
return id && name ? { id, name } : null;
|
|
2753
|
+
}
|
|
2754
|
+
).filter((entry) => !["bridge", "host", "none"].includes(entry.name));
|
|
2755
|
+
if (dryRun) {
|
|
2756
|
+
return [
|
|
2757
|
+
...containers.map((entry) => resourceOperation("local", "docker-container", entry.name, "planned", { id: entry.id })),
|
|
2758
|
+
...volumes.map((entry) => resourceOperation("local", "docker-volume", entry.name, "planned", { id: entry.id })),
|
|
2759
|
+
...networks.map((entry) => resourceOperation("local", "docker-network", entry.name, "planned", { id: entry.id })),
|
|
2760
|
+
...containers.length || volumes.length || networks.length ? [] : [resourceOperation("local", "docker-cleanup", "docker", "missing", { reason: "no_matching_resources" })]
|
|
2761
|
+
];
|
|
2762
|
+
}
|
|
2763
|
+
const operations = [];
|
|
2764
|
+
for (const entry of containers) {
|
|
2765
|
+
operations.push(removeDockerResource("container", entry.id, entry.name));
|
|
2766
|
+
}
|
|
2767
|
+
for (const entry of volumes) {
|
|
2768
|
+
operations.push(removeDockerResource("volume", entry.id, entry.name));
|
|
2769
|
+
}
|
|
2770
|
+
for (const entry of networks) {
|
|
2771
|
+
operations.push(removeDockerResource("network", entry.id, entry.name));
|
|
2772
|
+
}
|
|
2773
|
+
if (!operations.length) {
|
|
2774
|
+
operations.push(resourceOperation("local", "docker-cleanup", "docker", "missing", { reason: "no_matching_resources" }));
|
|
2775
|
+
}
|
|
2776
|
+
return operations;
|
|
2777
|
+
}
|
|
2468
2778
|
function destroyLocalRuntimeResources(tenantRoot, { dryRun = false, deleteData = false } = {}) {
|
|
2469
2779
|
const operations = [];
|
|
2470
2780
|
const pidDir = resolve(tenantRoot, ".treeseed/dev-pids");
|
|
@@ -2481,7 +2791,7 @@ function destroyLocalRuntimeResources(tenantRoot, { dryRun = false, deleteData =
|
|
|
2481
2791
|
for (const relativePath of [
|
|
2482
2792
|
".treeseed/generated/environments/local",
|
|
2483
2793
|
".treeseed/generated/dev",
|
|
2484
|
-
".treeseed/
|
|
2794
|
+
".treeseed/operations-runner",
|
|
2485
2795
|
".treeseed/local-capacity-provider/data"
|
|
2486
2796
|
]) {
|
|
2487
2797
|
const absolutePath = resolve(tenantRoot, relativePath);
|
|
@@ -2496,11 +2806,243 @@ function destroyLocalRuntimeResources(tenantRoot, { dryRun = false, deleteData =
|
|
|
2496
2806
|
rmSync(absolutePath, { recursive: true, force: true });
|
|
2497
2807
|
operations.push(resourceOperation("local", "data-path", relativePath, "deleted"));
|
|
2498
2808
|
}
|
|
2809
|
+
operations.push(...dockerLocalRuntimeResourceOperations({ dryRun }));
|
|
2499
2810
|
} else {
|
|
2500
2811
|
operations.push(resourceOperation("local", "data-path", ".treeseed/generated/environments/local", "skipped", { reason: "data_preserved" }));
|
|
2501
2812
|
}
|
|
2502
2813
|
return { operations };
|
|
2503
2814
|
}
|
|
2815
|
+
function treeSeedSweepTokens(deployConfig, state) {
|
|
2816
|
+
const configuredHosts = [
|
|
2817
|
+
deployConfig.siteUrl,
|
|
2818
|
+
deployConfig.surfaces?.web?.publicBaseUrl,
|
|
2819
|
+
deployConfig.surfaces?.web?.environments?.staging?.domain,
|
|
2820
|
+
deployConfig.surfaces?.web?.environments?.prod?.domain,
|
|
2821
|
+
deployConfig.surfaces?.api?.environments?.staging?.domain,
|
|
2822
|
+
deployConfig.surfaces?.api?.environments?.prod?.domain,
|
|
2823
|
+
deployConfig.services?.api?.environments?.staging?.domain,
|
|
2824
|
+
deployConfig.services?.api?.environments?.prod?.domain
|
|
2825
|
+
].map((value) => primaryHost(value) ?? value);
|
|
2826
|
+
return [...new Set([
|
|
2827
|
+
"treeseed",
|
|
2828
|
+
deployConfig.slug,
|
|
2829
|
+
deployConfig.name,
|
|
2830
|
+
state.identity?.deploymentKey,
|
|
2831
|
+
state.identity?.environmentKey,
|
|
2832
|
+
state.pages?.projectName,
|
|
2833
|
+
state.workerName,
|
|
2834
|
+
state.content?.bucketName,
|
|
2835
|
+
state.kvNamespaces?.FORM_GUARD_KV?.name,
|
|
2836
|
+
state.kvNamespaces?.SESSION?.name,
|
|
2837
|
+
state.d1Databases?.SITE_DATA_DB?.databaseName,
|
|
2838
|
+
...configuredHosts
|
|
2839
|
+
].map((value) => String(value ?? "").trim().toLowerCase()).filter((value) => value.length >= 4))];
|
|
2840
|
+
}
|
|
2841
|
+
function isProtectedAiIntegrationResource(value) {
|
|
2842
|
+
return /(?:^|[-_.])(?:ai-gateway|workers-ai|ai-integration|openai|anthropic)(?:[-_.]|$)/iu.test(String(value ?? ""));
|
|
2843
|
+
}
|
|
2844
|
+
function matchesTreeSeedSweep(value, tokens) {
|
|
2845
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
2846
|
+
if (!normalized || isProtectedAiIntegrationResource(normalized)) {
|
|
2847
|
+
return false;
|
|
2848
|
+
}
|
|
2849
|
+
return tokens.some((token) => normalized === token || normalized.includes(token));
|
|
2850
|
+
}
|
|
2851
|
+
function cloudflareNameCandidates(entry) {
|
|
2852
|
+
return [
|
|
2853
|
+
entry?.name,
|
|
2854
|
+
entry?.title,
|
|
2855
|
+
entry?.id,
|
|
2856
|
+
entry?.queue_name,
|
|
2857
|
+
entry?.script,
|
|
2858
|
+
entry?.domain,
|
|
2859
|
+
entry?.hostname,
|
|
2860
|
+
entry?.content,
|
|
2861
|
+
entry?.comment,
|
|
2862
|
+
...Array.isArray(entry?.domains) ? entry.domains : [],
|
|
2863
|
+
...Array.isArray(entry?.tags) ? entry.tags : []
|
|
2864
|
+
].filter(Boolean);
|
|
2865
|
+
}
|
|
2866
|
+
function cloudflareEntryMatchesTreeSeed(entry, tokens) {
|
|
2867
|
+
return cloudflareNameCandidates(entry).some((candidate) => matchesTreeSeedSweep(candidate, tokens));
|
|
2868
|
+
}
|
|
2869
|
+
function deleteDnsRecord(zoneId, record, { env, dryRun }) {
|
|
2870
|
+
const name = record?.name ?? record?.content ?? record?.id ?? null;
|
|
2871
|
+
if (!zoneId || !record?.id) {
|
|
2872
|
+
return resourceOperation("cloudflare", "dns-record", name, "missing", { zoneId });
|
|
2873
|
+
}
|
|
2874
|
+
if (dryRun) {
|
|
2875
|
+
return resourceOperation("cloudflare", "dns-record", name, "planned", {
|
|
2876
|
+
zoneId,
|
|
2877
|
+
id: record.id,
|
|
2878
|
+
content: record.content ?? null,
|
|
2879
|
+
recordType: record.type ?? null
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
return deleteCloudflareApiResource(
|
|
2883
|
+
`/zones/${encodeURIComponent(zoneId)}/dns_records/${encodeURIComponent(record.id)}`,
|
|
2884
|
+
{ env, dryRun: false, name, type: "dns-record" }
|
|
2885
|
+
);
|
|
2886
|
+
}
|
|
2887
|
+
function sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env, dryRun, deleteData }) {
|
|
2888
|
+
const tokens = treeSeedSweepTokens(deployConfig, state);
|
|
2889
|
+
const operations = [];
|
|
2890
|
+
const pagesProjects = listPagesProjects(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens));
|
|
2891
|
+
for (const project of pagesProjects) {
|
|
2892
|
+
const projectName = project?.name ?? project?.id ?? null;
|
|
2893
|
+
operations.push(...deletePagesCustomDomains(tenantRoot, projectName, [], { env, dryRun, knownOnly: false }));
|
|
2894
|
+
operations.push(deletePagesDeployments(tenantRoot, projectName, { env, dryRun, environment: "all" }));
|
|
2895
|
+
operations.push(deletePagesProject(projectName, { env, dryRun }));
|
|
2896
|
+
}
|
|
2897
|
+
for (const worker of listWorkers(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2898
|
+
const name = worker?.id ?? worker?.name ?? worker?.script ?? null;
|
|
2899
|
+
const deleted = deleteWorker(tenantRoot, name, { env, dryRun, force: true });
|
|
2900
|
+
operations.push(resourceOperation("cloudflare", "worker", name, deleted.status, { ...deleted, sweep: true }));
|
|
2901
|
+
}
|
|
2902
|
+
for (const namespace of listKvNamespaces(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2903
|
+
const deleted = deleteKvNamespace(tenantRoot, namespace.id, { env, dryRun });
|
|
2904
|
+
operations.push(resourceOperation("cloudflare", "kv-namespace", namespace.title ?? namespace.id, deleted.status, { ...deleted, sweep: true }));
|
|
2905
|
+
}
|
|
2906
|
+
for (const queue of listQueues(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2907
|
+
operations.push({ ...deleteQueueByName(tenantRoot, queue, { env, dryRun }), sweep: true });
|
|
2908
|
+
}
|
|
2909
|
+
for (const database of listD1Databases(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2910
|
+
const name = database?.name ?? database?.uuid ?? database?.id ?? null;
|
|
2911
|
+
const deleted = deleteData ? deleteD1Database(tenantRoot, name, { env, dryRun }) : null;
|
|
2912
|
+
operations.push(resourceOperation("cloudflare", "d1-database", name, deleteData ? deleted?.status : "skipped", {
|
|
2913
|
+
...deleteData ? deleted : { reason: "data_preserved" },
|
|
2914
|
+
sweep: true
|
|
2915
|
+
}));
|
|
2916
|
+
}
|
|
2917
|
+
for (const bucket of listR2Buckets(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2918
|
+
operations.push({ ...deleteR2Bucket(tenantRoot, bucket.name, { env, dryRun, deleteData }), sweep: true });
|
|
2919
|
+
}
|
|
2920
|
+
for (const widget of listTurnstileWidgets(tenantRoot, env).filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens))) {
|
|
2921
|
+
const deleted = deleteTurnstileWidget(widget.sitekey, { env, dryRun, name: widget.name });
|
|
2922
|
+
operations.push(resourceOperation("cloudflare", "turnstile-widget", widget.name ?? widget.sitekey, deleted.status, { ...deleted, sweep: true }));
|
|
2923
|
+
}
|
|
2924
|
+
for (const zone of listDnsZones(env)) {
|
|
2925
|
+
const zoneId = zone?.id ?? null;
|
|
2926
|
+
for (const record of listDnsRecords(zoneId, env)) {
|
|
2927
|
+
if (record?.type === "SOA" || record?.type === "NS") {
|
|
2928
|
+
continue;
|
|
2929
|
+
}
|
|
2930
|
+
if (!cloudflareEntryMatchesTreeSeed(record, tokens)) {
|
|
2931
|
+
continue;
|
|
2932
|
+
}
|
|
2933
|
+
operations.push({ ...deleteDnsRecord(zoneId, record, { env, dryRun }), zoneName: zone?.name ?? null, sweep: true });
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
return operations.length > 0 ? operations : [resourceOperation("cloudflare", "treeseed-sweep", "cloudflare", "missing", { reason: "no_matching_resources" })];
|
|
2937
|
+
}
|
|
2938
|
+
function countMatchingCloudflareEntries(entries, tokens) {
|
|
2939
|
+
return entries.filter((entry) => cloudflareEntryMatchesTreeSeed(entry, tokens)).length;
|
|
2940
|
+
}
|
|
2941
|
+
function cloudflareDestroyVerification(tenantRoot, deployConfig, state, env) {
|
|
2942
|
+
const tokens = treeSeedSweepTokens(deployConfig, state);
|
|
2943
|
+
const zoneIds = /* @__PURE__ */ new Set([
|
|
2944
|
+
deployConfig.cloudflare?.zoneId,
|
|
2945
|
+
state.webCache?.webZoneId,
|
|
2946
|
+
state.webCache?.contentZoneId
|
|
2947
|
+
]);
|
|
2948
|
+
for (const zone of listDnsZones(env)) {
|
|
2949
|
+
if (zone?.id) {
|
|
2950
|
+
zoneIds.add(zone.id);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
const dnsRecords = [];
|
|
2954
|
+
for (const zoneId of [...zoneIds].filter(Boolean)) {
|
|
2955
|
+
dnsRecords.push(...listDnsRecords(zoneId, env));
|
|
2956
|
+
}
|
|
2957
|
+
const remaining = {
|
|
2958
|
+
pages: countMatchingCloudflareEntries(listPagesProjects(tenantRoot, env), tokens),
|
|
2959
|
+
workers: countMatchingCloudflareEntries(listWorkers(tenantRoot, env), tokens),
|
|
2960
|
+
kvNamespaces: countMatchingCloudflareEntries(listKvNamespaces(tenantRoot, env), tokens),
|
|
2961
|
+
queues: countMatchingCloudflareEntries(listQueues(tenantRoot, env), tokens),
|
|
2962
|
+
d1Databases: countMatchingCloudflareEntries(listD1Databases(tenantRoot, env), tokens),
|
|
2963
|
+
r2Buckets: countMatchingCloudflareEntries(listR2Buckets(tenantRoot, env), tokens),
|
|
2964
|
+
turnstileWidgets: countMatchingCloudflareEntries(listTurnstileWidgets(tenantRoot, env), tokens),
|
|
2965
|
+
dnsRecords: countMatchingCloudflareEntries(
|
|
2966
|
+
dnsRecords.filter((record) => record?.type !== "SOA" && record?.type !== "NS"),
|
|
2967
|
+
tokens
|
|
2968
|
+
)
|
|
2969
|
+
};
|
|
2970
|
+
const totalRemaining = Object.values(remaining).reduce((sum, value) => sum + value, 0);
|
|
2971
|
+
return {
|
|
2972
|
+
provider: "cloudflare",
|
|
2973
|
+
method: "cloudflare-api",
|
|
2974
|
+
status: totalRemaining === 0 ? "clean" : "remaining",
|
|
2975
|
+
remaining,
|
|
2976
|
+
totalRemaining
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
function localDockerDestroyVerification() {
|
|
2980
|
+
if (!dockerAvailable()) {
|
|
2981
|
+
return {
|
|
2982
|
+
provider: "local-docker",
|
|
2983
|
+
method: "docker-cli",
|
|
2984
|
+
status: "skipped",
|
|
2985
|
+
reason: "docker_unavailable",
|
|
2986
|
+
remaining: {
|
|
2987
|
+
containers: 0,
|
|
2988
|
+
volumes: 0,
|
|
2989
|
+
networks: 0
|
|
2990
|
+
},
|
|
2991
|
+
totalRemaining: 0
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
const containers = matchingDockerEntries(
|
|
2995
|
+
dockerList(["ps", "-a", "--format", "{{.ID}} {{.Names}} {{.Image}}"]),
|
|
2996
|
+
(line) => {
|
|
2997
|
+
const [id, name, image] = line.split(" ");
|
|
2998
|
+
return id && name ? { id, name, image } : null;
|
|
2999
|
+
}
|
|
3000
|
+
).length;
|
|
3001
|
+
const volumes = matchingDockerEntries(
|
|
3002
|
+
dockerList(["volume", "ls", "--format", "{{.Name}}"]),
|
|
3003
|
+
(line) => ({ id: line, name: line })
|
|
3004
|
+
).length;
|
|
3005
|
+
const networks = matchingDockerEntries(
|
|
3006
|
+
dockerList(["network", "ls", "--format", "{{.ID}} {{.Name}}"]),
|
|
3007
|
+
(line) => {
|
|
3008
|
+
const [id, name] = line.split(" ");
|
|
3009
|
+
return id && name ? { id, name } : null;
|
|
3010
|
+
}
|
|
3011
|
+
).filter((entry) => !["bridge", "host", "none"].includes(entry.name)).length;
|
|
3012
|
+
const remaining = { containers, volumes, networks };
|
|
3013
|
+
const totalRemaining = containers + volumes + networks;
|
|
3014
|
+
return {
|
|
3015
|
+
provider: "local-docker",
|
|
3016
|
+
method: "docker-cli",
|
|
3017
|
+
status: totalRemaining === 0 ? "clean" : "remaining",
|
|
3018
|
+
remaining,
|
|
3019
|
+
totalRemaining
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
3022
|
+
async function sweepTreeSeedRailwayResources(deployConfig, state, { env, dryRun }) {
|
|
3023
|
+
if (!resolveRailwayApiToken(env)) {
|
|
3024
|
+
return [resourceOperation("railway", "treeseed-sweep", "railway", "blocked", { reason: "missing_railway_api_token" })];
|
|
3025
|
+
}
|
|
3026
|
+
const tokens = treeSeedSweepTokens(deployConfig, state);
|
|
3027
|
+
const workspace = await resolveRailwayWorkspaceContext({ env, workspace: resolveRailwayWorkspace(env) });
|
|
3028
|
+
const projects = await listRailwayProjects({ env, workspaceId: workspace.id });
|
|
3029
|
+
const operations = [];
|
|
3030
|
+
for (const project of projects) {
|
|
3031
|
+
if (project.deletedAt || !matchesTreeSeedSweep(project.name, tokens)) {
|
|
3032
|
+
continue;
|
|
3033
|
+
}
|
|
3034
|
+
if (dryRun) {
|
|
3035
|
+
operations.push(resourceOperation("railway", "project", project.name, "planned", {
|
|
3036
|
+
id: project.id,
|
|
3037
|
+
workspaceId: workspace.id,
|
|
3038
|
+
sweep: true
|
|
3039
|
+
}));
|
|
3040
|
+
} else {
|
|
3041
|
+
throw new Error("Railway project sweep deletion is reconciler-owned. Use trsd reconcile test-live --mode cleanup for isolated cleanup.");
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
return operations.length > 0 ? operations : [resourceOperation("railway", "treeseed-sweep", "railway", "missing", { reason: "no_matching_projects" })];
|
|
3045
|
+
}
|
|
2504
3046
|
async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
2505
3047
|
const target = normalizeTarget(options.scope ?? options.target ?? "prod");
|
|
2506
3048
|
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
@@ -2512,6 +3054,8 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
2512
3054
|
const dryRun = options.dryRun ?? false;
|
|
2513
3055
|
const deleteData = options.deleteData === true;
|
|
2514
3056
|
const force = options.force ?? false;
|
|
3057
|
+
const sweepTreeseed = options.sweepTreeseed === true;
|
|
3058
|
+
const destroysSharedWebSurface = target.kind === "persistent" && target.scope === "prod" && deleteData;
|
|
2515
3059
|
const kvNamespaces = dryRun ? [] : listKvNamespaces(tenantRoot, env);
|
|
2516
3060
|
const d1Databases = dryRun ? [] : listD1Databases(tenantRoot, env);
|
|
2517
3061
|
const queues = dryRun ? [] : listQueues(tenantRoot, env);
|
|
@@ -2538,8 +3082,6 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
2538
3082
|
state.turnstileWidgets.formGuard = resolveExistingTurnstileWidget(turnstileWidgets, state.turnstileWidgets?.formGuard);
|
|
2539
3083
|
const pagesProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
2540
3084
|
const bucket = buckets.find((entry) => entry?.name === state.content?.bucketName);
|
|
2541
|
-
const queue = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.name);
|
|
2542
|
-
const dlq = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.dlqName);
|
|
2543
3085
|
const workerResult = deleteWorker(tenantRoot, state.workerName, { env, dryRun, force });
|
|
2544
3086
|
const turnstileWidget = deleteTurnstileWidget(state.turnstileWidgets?.formGuard?.sitekey, {
|
|
2545
3087
|
env,
|
|
@@ -2566,13 +3108,6 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
2566
3108
|
return resourceOperation("cloudflare", "kv-namespace", namespace.title, result.status, { ...result, legacy: true });
|
|
2567
3109
|
});
|
|
2568
3110
|
const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env, dryRun, deleteData });
|
|
2569
|
-
const deletedQueue = deleteQueueByName(tenantRoot, queue ?? { name: state.queues?.agentWork?.name }, { env, dryRun });
|
|
2570
|
-
const deletedDlq = state.queues?.agentWork?.dlqName ? deleteQueueByName(tenantRoot, dlq ?? { name: state.queues.agentWork.dlqName }, { env, dryRun }) : null;
|
|
2571
|
-
const knownQueueNames = new Set([
|
|
2572
|
-
state.queues?.agentWork?.name,
|
|
2573
|
-
state.queues?.agentWork?.dlqName
|
|
2574
|
-
].filter(Boolean));
|
|
2575
|
-
const legacyQueues = legacyQueueDestroyOperations(tenantRoot, queues, target, knownQueueNames, { env, dryRun });
|
|
2576
3111
|
const r2Bucket = bucket || dryRun ? deleteR2Bucket(tenantRoot, state.content?.bucketName, { env, dryRun, deleteData }) : resourceOperation("cloudflare", "r2-bucket", state.content?.bucketName, "missing");
|
|
2577
3112
|
const pageDnsNames = [
|
|
2578
3113
|
state.pages?.customDomain,
|
|
@@ -2585,11 +3120,21 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
2585
3120
|
].filter(Boolean);
|
|
2586
3121
|
const dnsRecords = [.../* @__PURE__ */ new Set([...pageDnsNames, ...apiDnsNames])].flatMap((name) => deleteDnsRecordsForName(deployConfig, name, { env, dryRun }));
|
|
2587
3122
|
const cacheRules = deleteTreeseedCacheRules(deployConfig, state, { env, dryRun });
|
|
2588
|
-
const pageCustomDomains = pagesProject || dryRun ? deletePagesCustomDomains(tenantRoot, state.pages?.projectName, pageDnsNames, { env, dryRun }) : [resourceOperation("cloudflare", "pages-custom-domain", state.pages?.projectName, "missing")];
|
|
2589
|
-
const pageDeployments = pagesProject || dryRun ? deletePagesDeployments(tenantRoot, state.pages?.projectName, {
|
|
2590
|
-
|
|
3123
|
+
const pageCustomDomains = pagesProject || dryRun ? deletePagesCustomDomains(tenantRoot, state.pages?.projectName, pageDnsNames, { env, dryRun, knownOnly: !destroysSharedWebSurface }) : [resourceOperation("cloudflare", "pages-custom-domain", state.pages?.projectName, "missing")];
|
|
3124
|
+
const pageDeployments = pagesProject || dryRun ? deletePagesDeployments(tenantRoot, state.pages?.projectName, {
|
|
3125
|
+
env,
|
|
3126
|
+
dryRun,
|
|
3127
|
+
environment: destroysSharedWebSurface ? "all" : "preview"
|
|
3128
|
+
}) : resourceOperation("cloudflare", "pages-deployments", state.pages?.projectName, "missing");
|
|
3129
|
+
const pages = destroysSharedWebSurface && (pagesProject || dryRun) ? deletePagesProject(state.pages?.projectName, { env, dryRun }) : resourceOperation("cloudflare", "pages-project", state.pages?.projectName, "skipped", {
|
|
3130
|
+
reason: target.scope === "prod" ? "delete_data_required" : "shared_web_surface"
|
|
3131
|
+
});
|
|
2591
3132
|
const local = target.kind === "persistent" && target.scope === "local" ? destroyLocalRuntimeResources(tenantRoot, { dryRun, deleteData }) : { operations: [] };
|
|
2592
3133
|
const railway = await destroyRailwayResources(tenantRoot, deployConfig, target, { dryRun, deleteData, env: process.env });
|
|
3134
|
+
const sweep = sweepTreeseed ? {
|
|
3135
|
+
cloudflare: sweepTreeSeedCloudflareResources(tenantRoot, deployConfig, state, { env, dryRun, deleteData }),
|
|
3136
|
+
railway: await sweepTreeSeedRailwayResources(deployConfig, state, { env: process.env, dryRun })
|
|
3137
|
+
} : { cloudflare: [], railway: [] };
|
|
2593
3138
|
const operations = {
|
|
2594
3139
|
cloudflare: [
|
|
2595
3140
|
resourceOperation("cloudflare", "worker", state.workerName, workerResult.status, workerResult),
|
|
@@ -2600,24 +3145,31 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
2600
3145
|
...sessionPreview ? [resourceOperation("cloudflare", "kv-namespace-preview", state.kvNamespaces.SESSION.name, sessionPreview.status, sessionPreview)] : [],
|
|
2601
3146
|
...legacyKvNamespaces,
|
|
2602
3147
|
database,
|
|
2603
|
-
deletedQueue,
|
|
2604
|
-
...deletedDlq ? [deletedDlq] : [],
|
|
2605
|
-
...legacyQueues,
|
|
2606
3148
|
r2Bucket,
|
|
2607
3149
|
...pageCustomDomains,
|
|
2608
3150
|
pageDeployments,
|
|
2609
3151
|
pages,
|
|
2610
3152
|
...dnsRecords,
|
|
2611
|
-
...cacheRules
|
|
3153
|
+
...cacheRules,
|
|
3154
|
+
...sweep.cloudflare
|
|
3155
|
+
],
|
|
3156
|
+
railway: [
|
|
3157
|
+
...railway.operations,
|
|
3158
|
+
...sweep.railway
|
|
2612
3159
|
],
|
|
2613
|
-
railway: railway.operations,
|
|
2614
3160
|
local: local.operations
|
|
2615
3161
|
};
|
|
3162
|
+
const verification = dryRun ? null : {
|
|
3163
|
+
cloudflare: cloudflareDestroyVerification(tenantRoot, deployConfig, state, env),
|
|
3164
|
+
...target.kind === "persistent" && target.scope === "local" ? { localDocker: localDockerDestroyVerification() } : {}
|
|
3165
|
+
};
|
|
2616
3166
|
return {
|
|
2617
3167
|
target,
|
|
2618
3168
|
deleteData,
|
|
3169
|
+
sweepTreeseed,
|
|
2619
3170
|
summary: buildDestroySummary(deployConfig, state, target),
|
|
2620
|
-
operations
|
|
3171
|
+
operations,
|
|
3172
|
+
verification
|
|
2621
3173
|
};
|
|
2622
3174
|
}
|
|
2623
3175
|
function destroyCloudflareResources(tenantRoot, options = {}) {
|
|
@@ -2648,8 +3200,6 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
|
|
|
2648
3200
|
state.d1Databases.SITE_DATA_DB
|
|
2649
3201
|
);
|
|
2650
3202
|
state.turnstileWidgets.formGuard = resolveExistingTurnstileWidget(turnstileWidgets, state.turnstileWidgets?.formGuard);
|
|
2651
|
-
const queue = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.name);
|
|
2652
|
-
const dlq = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.dlqName);
|
|
2653
3203
|
const bucket = buckets.find((entry) => entry?.name === state.content?.bucketName);
|
|
2654
3204
|
const pagesProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
2655
3205
|
const worker = deleteWorker(tenantRoot, state.workerName, { env, dryRun, force });
|
|
@@ -2660,13 +3210,6 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
|
|
|
2660
3210
|
});
|
|
2661
3211
|
const formGuard = deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.id, { env, dryRun });
|
|
2662
3212
|
const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env, dryRun, deleteData });
|
|
2663
|
-
const deletedQueue = deleteQueueByName(tenantRoot, queue ?? { name: state.queues?.agentWork?.name }, { env, dryRun });
|
|
2664
|
-
const deletedDlq = state.queues?.agentWork?.dlqName ? deleteQueueByName(tenantRoot, dlq ?? { name: state.queues.agentWork.dlqName }, { env, dryRun }) : null;
|
|
2665
|
-
const knownQueueNames = new Set([
|
|
2666
|
-
state.queues?.agentWork?.name,
|
|
2667
|
-
state.queues?.agentWork?.dlqName
|
|
2668
|
-
].filter(Boolean));
|
|
2669
|
-
const legacyQueues = legacyQueueDestroyOperations(tenantRoot, queues, target, knownQueueNames, { env, dryRun });
|
|
2670
3213
|
const r2Bucket = bucket || dryRun ? deleteR2Bucket(tenantRoot, state.content?.bucketName, { env, dryRun, deleteData }) : resourceOperation("cloudflare", "r2-bucket", state.content?.bucketName, "missing");
|
|
2671
3214
|
const pages = pagesProject || dryRun ? deletePagesProject(state.pages?.projectName, { env, dryRun }) : resourceOperation("cloudflare", "pages-project", state.pages?.projectName, "missing");
|
|
2672
3215
|
const operations = {
|
|
@@ -2674,9 +3217,6 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
|
|
|
2674
3217
|
turnstileWidget,
|
|
2675
3218
|
formGuard,
|
|
2676
3219
|
database,
|
|
2677
|
-
queue: deletedQueue,
|
|
2678
|
-
dlq: deletedDlq,
|
|
2679
|
-
legacyQueues,
|
|
2680
3220
|
r2Bucket,
|
|
2681
3221
|
pages
|
|
2682
3222
|
};
|
|
@@ -2773,61 +3313,6 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
|
|
|
2773
3313
|
current.databaseId = created.uuid;
|
|
2774
3314
|
current.previewDatabaseId = created.previewDatabaseUuid ?? created.uuid;
|
|
2775
3315
|
};
|
|
2776
|
-
const ensureQueue = () => {
|
|
2777
|
-
const current = state.queues?.agentWork;
|
|
2778
|
-
if (!current?.name) {
|
|
2779
|
-
return;
|
|
2780
|
-
}
|
|
2781
|
-
let refreshedQueues = queues;
|
|
2782
|
-
const exists = refreshedQueues.find((entry) => queueName(entry) === current.name);
|
|
2783
|
-
if (exists) {
|
|
2784
|
-
current.queueId = queueId(exists);
|
|
2785
|
-
const currentDlq = current.dlqName ? refreshedQueues.find((entry) => queueName(entry) === current.dlqName) : null;
|
|
2786
|
-
current.dlqId = queueId(currentDlq);
|
|
2787
|
-
return;
|
|
2788
|
-
}
|
|
2789
|
-
if (dryRun) {
|
|
2790
|
-
current.queueId = `dryrun-${current.name}`;
|
|
2791
|
-
current.dlqId = current.dlqName ? `dryrun-${current.dlqName}` : null;
|
|
2792
|
-
return;
|
|
2793
|
-
}
|
|
2794
|
-
try {
|
|
2795
|
-
runWrangler(["queues", "create", current.name], {
|
|
2796
|
-
cwd: tenantRoot,
|
|
2797
|
-
capture: true,
|
|
2798
|
-
env
|
|
2799
|
-
});
|
|
2800
|
-
} catch (error) {
|
|
2801
|
-
if (!isWranglerAlreadyExistsError(error, [/Queue name .* is already taken/i, /\[code:\s*11009\]/i])) {
|
|
2802
|
-
throw error;
|
|
2803
|
-
}
|
|
2804
|
-
}
|
|
2805
|
-
refreshedQueues = listQueues(tenantRoot, env);
|
|
2806
|
-
if (current.dlqName && !refreshedQueues.find((entry) => queueName(entry) === current.dlqName)) {
|
|
2807
|
-
try {
|
|
2808
|
-
runWrangler(["queues", "create", current.dlqName], {
|
|
2809
|
-
cwd: tenantRoot,
|
|
2810
|
-
capture: true,
|
|
2811
|
-
env
|
|
2812
|
-
});
|
|
2813
|
-
} catch (error) {
|
|
2814
|
-
if (!isWranglerAlreadyExistsError(error, [/Queue name .* is already taken/i, /\[code:\s*11009\]/i])) {
|
|
2815
|
-
throw error;
|
|
2816
|
-
}
|
|
2817
|
-
}
|
|
2818
|
-
}
|
|
2819
|
-
refreshedQueues = listQueues(tenantRoot, env);
|
|
2820
|
-
const created = refreshedQueues.find((entry) => queueName(entry) === current.name);
|
|
2821
|
-
if (!created) {
|
|
2822
|
-
throw new Error(`Unable to resolve Cloudflare queue ${current.name} after reconciliation.`);
|
|
2823
|
-
}
|
|
2824
|
-
current.queueId = queueId(created);
|
|
2825
|
-
const createdDlq = current.dlqName ? refreshedQueues.find((entry) => queueName(entry) === current.dlqName) : null;
|
|
2826
|
-
if (current.dlqName && !createdDlq) {
|
|
2827
|
-
throw new Error(`Unable to resolve Cloudflare dead-letter queue ${current.dlqName} after reconciliation.`);
|
|
2828
|
-
}
|
|
2829
|
-
current.dlqId = queueId(createdDlq);
|
|
2830
|
-
};
|
|
2831
3316
|
const ensureR2Bucket = () => {
|
|
2832
3317
|
const bucketName = state.content?.bucketName;
|
|
2833
3318
|
if (!bucketName) {
|
|
@@ -2865,7 +3350,7 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
|
|
|
2865
3350
|
const exists = pagesProjects.find((entry) => entry?.name === current.projectName);
|
|
2866
3351
|
if (exists) {
|
|
2867
3352
|
current.url = exists.subdomain ? `https://${exists.subdomain}` : current.url ?? `https://${current.projectName}.pages.dev`;
|
|
2868
|
-
ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
3353
|
+
ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "", current.projectName, env, exists, { state, target });
|
|
2869
3354
|
return;
|
|
2870
3355
|
}
|
|
2871
3356
|
if (dryRun) {
|
|
@@ -2884,12 +3369,11 @@ function provisionCloudflareResources(tenantRoot, options = {}) {
|
|
|
2884
3369
|
capture: true,
|
|
2885
3370
|
env
|
|
2886
3371
|
});
|
|
2887
|
-
ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.
|
|
3372
|
+
ensurePagesProjectCompatibility(env.CLOUDFLARE_ACCOUNT_ID ?? process.env.TREESEED_CLOUDFLARE_ACCOUNT_ID ?? "", current.projectName, env, null, { state, target });
|
|
2888
3373
|
current.url = `https://${current.projectName}.pages.dev`;
|
|
2889
3374
|
};
|
|
2890
3375
|
ensureKv("FORM_GUARD_KV");
|
|
2891
3376
|
ensureD1();
|
|
2892
|
-
ensureQueue();
|
|
2893
3377
|
ensureR2Bucket();
|
|
2894
3378
|
ensurePagesProject();
|
|
2895
3379
|
reconcileCloudflareWebCacheRules(tenantRoot, deployConfig, state, target, { dryRun });
|
|
@@ -2979,8 +3463,6 @@ function verifyProvisionedCloudflareResources(tenantRoot, options = {}) {
|
|
|
2979
3463
|
pages: Boolean(state.pages?.projectName && (livePages || pagesProject?.name === state.pages.projectName)),
|
|
2980
3464
|
formGuardKv: Boolean(state.kvNamespaces?.FORM_GUARD_KV?.name && kvNamespaces.find((entry) => entry?.title === state.kvNamespaces.FORM_GUARD_KV.name)),
|
|
2981
3465
|
d1: Boolean(state.d1Databases?.SITE_DATA_DB?.databaseName && d1Databases.find((entry) => entry?.name === state.d1Databases.SITE_DATA_DB.databaseName)),
|
|
2982
|
-
queue: Boolean(state.queues?.agentWork?.name && queues.find((entry) => queueName(entry) === state.queues.agentWork.name)),
|
|
2983
|
-
dlq: !state.queues?.agentWork?.dlqName || Boolean(queues.find((entry) => queueName(entry) === state.queues.agentWork.dlqName)),
|
|
2984
3466
|
r2: Boolean(state.content?.bucketName && buckets.find((entry) => entry?.name === state.content.bucketName)),
|
|
2985
3467
|
pagesFormGuardKvBinding: !pagesBindings.kv_namespaces?.FORM_GUARD_KV || pageBindingConfigured("kv_namespaces", "FORM_GUARD_KV", pagesBindings.kv_namespaces.FORM_GUARD_KV),
|
|
2986
3468
|
pagesD1Binding: !pagesBindings.d1_databases?.SITE_DATA_DB || pageBindingConfigured("d1_databases", "SITE_DATA_DB", pagesBindings.d1_databases.SITE_DATA_DB),
|
|
@@ -2996,12 +3478,6 @@ function verifyProvisionedCloudflareResources(tenantRoot, options = {}) {
|
|
|
2996
3478
|
state.readiness.blockers = [];
|
|
2997
3479
|
state.readiness.warnings = [];
|
|
2998
3480
|
state.readiness.lastValidationSummary = checks;
|
|
2999
|
-
const liveQueue = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.name);
|
|
3000
|
-
if (state.queues?.agentWork) {
|
|
3001
|
-
state.queues.agentWork.queueId = queueId(liveQueue) ?? state.queues.agentWork.queueId ?? null;
|
|
3002
|
-
const liveDlq = queues.find((entry) => queueName(entry) === state.queues.agentWork.dlqName);
|
|
3003
|
-
state.queues.agentWork.dlqId = queueId(liveDlq) ?? state.queues.agentWork.dlqId ?? null;
|
|
3004
|
-
}
|
|
3005
3481
|
if (state.pages) {
|
|
3006
3482
|
const configuredWebUrl = resolveConfiguredSurfaceBaseUrl(deployConfig, target, "web");
|
|
3007
3483
|
if (configuredWebUrl) {
|
|
@@ -3240,9 +3716,11 @@ export {
|
|
|
3240
3716
|
buildProvisioningSummary,
|
|
3241
3717
|
buildPublicVars,
|
|
3242
3718
|
buildSecretMap,
|
|
3719
|
+
buildTreeseedManagedCloudflareCacheRules,
|
|
3243
3720
|
buildWranglerConfigContents,
|
|
3244
3721
|
cleanupDestroyedState,
|
|
3245
3722
|
cloudflareApiRequest,
|
|
3723
|
+
cloudflareDestroyVerification,
|
|
3246
3724
|
collectMissingDeployInputs,
|
|
3247
3725
|
createBranchPreviewDeployTarget,
|
|
3248
3726
|
createPersistentDeployTarget,
|
|
@@ -3251,17 +3729,21 @@ export {
|
|
|
3251
3729
|
deriveTreeseedStagingSurfaceDomain,
|
|
3252
3730
|
destroyCloudflareResources,
|
|
3253
3731
|
destroyTreeseedEnvironmentResources,
|
|
3732
|
+
dockerLocalRuntimeResourceOperations,
|
|
3254
3733
|
ensureGeneratedWranglerConfig,
|
|
3255
3734
|
finalizeDeploymentState,
|
|
3256
3735
|
getTurnstileWidget,
|
|
3257
3736
|
hasProvisionedCloudflareResources,
|
|
3258
3737
|
isWranglerAlreadyExistsError,
|
|
3259
3738
|
listD1Databases,
|
|
3739
|
+
listDnsRecords,
|
|
3740
|
+
listDnsZones,
|
|
3260
3741
|
listKvNamespaces,
|
|
3261
3742
|
listPagesProjects,
|
|
3262
3743
|
listQueues,
|
|
3263
3744
|
listR2Buckets,
|
|
3264
3745
|
listTurnstileWidgets,
|
|
3746
|
+
listWorkers,
|
|
3265
3747
|
loadDeployState,
|
|
3266
3748
|
markDeploymentInitialized,
|
|
3267
3749
|
markManagedServicesInitialized,
|
|
@@ -3286,6 +3768,7 @@ export {
|
|
|
3286
3768
|
runRemoteD1Migrations,
|
|
3287
3769
|
runWrangler,
|
|
3288
3770
|
scopeFromTarget,
|
|
3771
|
+
setDestroyDockerRunnerForTests,
|
|
3289
3772
|
shouldDeleteRailwayProjectAfterEnvironmentDestroy,
|
|
3290
3773
|
syncCloudflareSecrets,
|
|
3291
3774
|
updateTurnstileWidget,
|