@treeseed/sdk 0.12.62 → 0.13.0-rc.10
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/LICENSE +13 -0
- package/README.md +33 -359
- package/dist/accounts/account-contracts.d.ts +170 -0
- package/dist/accounts/account-contracts.js +33 -0
- package/dist/agent-capacity/allocation.d.ts +186 -0
- package/dist/agent-capacity/allocation.js +226 -0
- package/dist/agent-capacity/artifact-mutation-receipt.d.ts +49 -0
- package/dist/agent-capacity/artifact-mutation-receipt.js +36 -0
- package/dist/agent-capacity/artifacts.d.ts +107 -0
- package/dist/agent-capacity/artifacts.js +39 -0
- package/dist/agent-capacity/authority/agent-authority-presets.d.ts +13 -0
- package/dist/agent-capacity/authority/agent-authority-presets.js +106 -0
- package/dist/agent-capacity/contracts/capacity/assignments/assignment-records.d.ts +269 -0
- package/dist/agent-capacity/contracts/capacity/assignments/assignment-records.js +10 -0
- package/dist/agent-capacity/contracts/capacity/communication/communication-records.d.ts +101 -0
- package/dist/agent-capacity/contracts/capacity/communication/communication-records.js +9 -0
- package/dist/agent-capacity/contracts/capacity/providers/supply-policy.d.ts +36 -0
- package/dist/agent-capacity/contracts/capacity/providers/supply-policy.js +4 -0
- package/dist/agent-capacity/contracts/capacity/workdays/workday-records.d.ts +188 -0
- package/dist/agent-capacity/contracts/configuration/configuration.d.ts +11 -0
- package/dist/agent-capacity/contracts/configuration/configuration.js +20 -0
- package/dist/agent-capacity/contracts/operations/research-workflow.d.ts +59 -0
- package/dist/agent-capacity/contracts/operations/research-workflow.js +16 -0
- package/dist/agent-capacity/contracts/operations/workflow-promotion.d.ts +24 -0
- package/dist/agent-capacity/contracts/projects/agents/project-agent-class.d.ts +37 -0
- package/dist/agent-capacity/contracts/runtime/runtime-observability.d.ts +335 -0
- package/dist/agent-capacity/contracts/support/agent-atlas.d.ts +185 -0
- package/dist/agent-capacity/contracts/support/agent-atlas.js +47 -0
- package/dist/agent-capacity/contracts/support/agent-lab-monitoring.d.ts +96 -0
- package/dist/agent-capacity/contracts/support/agent-lab-monitoring.js +14 -0
- package/dist/agent-capacity/contracts/support/authority/execution-mode.d.ts +10 -0
- package/dist/agent-capacity/contracts/support/authority/execution-mode.js +17 -0
- package/dist/agent-capacity/contracts/support/decision-work.d.ts +251 -0
- package/dist/agent-capacity/contracts/support/failure-records.d.ts +9 -0
- package/dist/agent-capacity/contracts/support/failure-records.js +0 -0
- package/dist/agent-capacity/contracts/support/financial-records.d.ts +124 -0
- package/dist/agent-capacity/contracts/support/financial-records.js +0 -0
- package/dist/agent-capacity/contracts/support/planning-records.d.ts +121 -0
- package/dist/agent-capacity/contracts/support/planning-records.js +0 -0
- package/dist/agent-capacity/contracts/support/research-citation.d.ts +15 -0
- package/dist/agent-capacity/contracts/support/research-citation.js +0 -0
- package/dist/agent-capacity/contracts/support/research-source-policy.d.ts +8 -0
- package/dist/agent-capacity/contracts/support/research-source-policy.js +0 -0
- package/dist/agent-capacity/contracts/support/time-capacity.d.ts +163 -0
- package/dist/agent-capacity/contracts/support/time-capacity.js +63 -0
- package/dist/agent-capacity/policy/capacity/allocations/allocation-hierarchy.d.ts +29 -0
- package/dist/agent-capacity/policy/capacity/allocations/allocation-hierarchy.js +110 -0
- package/dist/agent-capacity/validation/activity-profile.d.ts +12 -0
- package/dist/agent-capacity/validation/activity-profile.js +264 -0
- package/dist/agent-capacity/validation/agent-definition-schema.d.ts +9140 -0
- package/dist/agent-capacity/validation/agent-definition-schema.js +198 -0
- package/dist/agent-capacity/validation/agent-lab-forensics-schema.d.ts +234 -0
- package/dist/agent-capacity/validation/agent-lab-forensics-schema.js +104 -0
- package/dist/agent-capacity/validation/agent-signal.d.ts +30 -0
- package/dist/agent-capacity/validation/agent-signal.js +31 -0
- package/dist/agent-capacity/validation/allocation.d.ts +12 -0
- package/dist/agent-capacity/validation/allocation.js +115 -0
- package/dist/agent-capacity/validation/assignment-records.d.ts +16 -0
- package/dist/agent-capacity/validation/assignment-records.js +92 -0
- package/dist/agent-capacity/validation/configuration.d.ts +14 -0
- package/dist/agent-capacity/validation/configuration.js +23 -0
- package/dist/agent-capacity/validation/index.d.ts +11 -0
- package/dist/agent-capacity/validation/index.js +11 -0
- package/dist/agent-capacity/validation/proposal-type.d.ts +22 -0
- package/dist/agent-capacity/validation/proposal-type.js +21 -0
- package/dist/agent-capacity/validation/research/citation.d.ts +16 -0
- package/dist/agent-capacity/validation/research/citation.js +47 -0
- package/dist/agent-capacity/validation/research/source-policy.d.ts +11 -0
- package/dist/agent-capacity/validation/research/source-policy.js +26 -0
- package/dist/agent-capacity/validation/research/workflow.d.ts +10 -0
- package/dist/agent-capacity/validation/research/workflow.js +31 -0
- package/dist/agent-capacity/validation/workflow-promotion.d.ts +2 -0
- package/dist/agent-capacity/validation/workflow-promotion.js +34 -0
- package/dist/agent-capacity/workday.d.ts +52 -0
- package/dist/agent-capacity/workday.js +105 -0
- package/dist/ai-appliance/contracts.d.ts +90 -0
- package/dist/ai-appliance/contracts.js +16 -0
- package/dist/ai-appliance/validation.d.ts +4 -0
- package/dist/ai-appliance/validation.js +62 -0
- package/dist/capacity/agents/agent-capacity.d.ts +21 -0
- package/dist/capacity/agents/agent-capacity.js +20 -0
- package/dist/capacity/capacity-core/capacity-pagination.d.ts +18 -0
- package/dist/capacity/capacity-core/capacity-pagination.js +33 -0
- package/dist/capacity/providers/capacity-provider.d.ts +1 -0
- package/dist/capacity/providers/capacity-provider.js +1 -0
- package/dist/capacity-provider/client.d.ts +56 -0
- package/dist/capacity-provider/client.js +163 -0
- package/dist/capacity-provider/contracts/capacity-facts.d.ts +64 -0
- package/dist/capacity-provider/contracts/capacity-facts.js +0 -0
- package/dist/capacity-provider/contracts/governance.d.ts +399 -0
- package/dist/capacity-provider/contracts/governance.js +18 -0
- package/dist/capacity-provider/contracts/index.d.ts +2 -0
- package/dist/capacity-provider/contracts/index.js +2 -0
- package/dist/capacity-provider/index.d.ts +3 -0
- package/dist/capacity-provider/index.js +3 -0
- package/dist/capacity-provider/validation.d.ts +27 -0
- package/dist/capacity-provider/validation.js +184 -0
- package/dist/configuration/secrets-capability.d.ts +4 -0
- package/dist/configuration/secrets-capability.js +4 -0
- package/dist/content/contracts.d.ts +12 -0
- package/dist/content/contracts.js +0 -0
- package/dist/content/validation/agent-operational-content-schemas.d.ts +1351 -0
- package/dist/content/validation/agent-operational-content-schemas.js +146 -0
- package/dist/content/validation/auxiliary-content-schemas.d.ts +1715 -0
- package/dist/content/validation/auxiliary-content-schemas.js +173 -0
- package/dist/content/validation/content-model-schemas.d.ts +21728 -0
- package/dist/content/validation/content-model-schemas.js +271 -0
- package/dist/content/validation/index.d.ts +3 -0
- package/dist/content/validation/index.js +3 -0
- package/dist/content/validation/portable-content-data.d.ts +2101 -0
- package/dist/content/validation/portable-content-data.js +49 -0
- package/dist/entrypoints/clients/control-plane-client.d.ts +112 -0
- package/dist/entrypoints/clients/control-plane-client.js +180 -0
- package/dist/entrypoints/models/field-aliases.d.ts +11 -0
- package/dist/entrypoints/models/model-registry.d.ts +3 -0
- package/dist/entrypoints/models/model-registry.js +3 -0
- package/dist/entrypoints/models/sdk-fields.d.ts +11 -0
- package/dist/entrypoints/models/sdk-types.d.ts +10 -0
- package/dist/entrypoints/models/sdk-types.js +10 -0
- package/dist/feedback/contracts.d.ts +142 -0
- package/dist/feedback/contracts.js +18 -0
- package/dist/feedback/index.d.ts +1 -0
- package/dist/feedback/index.js +1 -0
- package/dist/governance/groups/contracts.d.ts +102 -0
- package/dist/governance/groups/contracts.js +6 -0
- package/dist/graph/context-query-contracts.d.ts +19 -1
- package/dist/graph/context-query-contracts.js +49 -8
- package/dist/knowledge/contracts.d.ts +173 -0
- package/dist/knowledge/contracts.js +16 -0
- package/dist/knowledge/index.d.ts +1 -0
- package/dist/knowledge/index.js +1 -0
- package/dist/model-registry/agent-operational-models.d.ts +4 -0
- package/dist/model-registry/agent-operational-models.js +38 -0
- package/dist/model-registry/build-builtin-model-registry.d.ts +2 -0
- package/dist/model-registry/build-builtin-model-registry.js +426 -0
- package/dist/model-registry/content-root.d.ts +9 -0
- package/dist/model-registry/content-root.js +33 -0
- package/dist/model-registry/discussion-models.d.ts +4 -0
- package/dist/model-registry/discussion-models.js +84 -0
- package/dist/model-registry/group-models.d.ts +2 -0
- package/dist/model-registry/group-models.js +52 -0
- package/dist/model-registry/knowledge-models.d.ts +2 -0
- package/dist/model-registry/knowledge-models.js +89 -0
- package/dist/model-registry/normalize-definition.d.ts +8 -0
- package/dist/model-registry/normalize-definition.js +69 -0
- package/dist/operator-contracts/canonical-command-tree.d.ts +3 -0
- package/dist/operator-contracts/canonical-command-tree.js +81 -0
- package/dist/operator-contracts/command-tree.d.ts +79 -0
- package/dist/operator-contracts/command-tree.js +53 -0
- package/dist/operator-contracts/control-plane-operation.d.ts +80 -0
- package/dist/operator-contracts/control-plane-operation.js +75 -0
- package/dist/operator-contracts/control-plane-operations.d.ts +546 -0
- package/dist/operator-contracts/control-plane-operations.js +352 -0
- package/dist/operator-contracts/index.d.ts +8 -0
- package/dist/operator-contracts/index.js +8 -0
- package/dist/operator-contracts/mcp.d.ts +78 -0
- package/dist/operator-contracts/mcp.js +23 -0
- package/dist/operator-contracts/oauth.d.ts +46 -0
- package/dist/operator-contracts/oauth.js +0 -0
- package/dist/operator-contracts/workday-lifecycle.d.ts +157 -0
- package/dist/operator-contracts/workday-lifecycle.js +65 -0
- package/dist/operator-contracts/workday-profile.d.ts +72 -0
- package/dist/operator-contracts/workday-profile.js +106 -0
- package/dist/projects/projects-core/project-workflow.d.ts +179 -0
- package/dist/projects/projects-core/project-workflow.js +66 -0
- package/dist/sdk-types/configuration/sdk-dispatch-credential-source.d.ts +201 -0
- package/dist/sdk-types/configuration/sdk-dispatch-credential-source.js +0 -0
- package/dist/sdk-types/control-plane/resource-requests.d.ts +124 -0
- package/dist/sdk-types/control-plane/resource-requests.js +0 -0
- package/dist/sdk-types/governance/commons/commons-question-input.d.ts +61 -0
- package/dist/sdk-types/governance/commons/commons-question-input.js +0 -0
- package/dist/sdk-types/support/planning-policy.d.ts +210 -0
- package/dist/sdk-types/support/planning-policy.js +0 -0
- package/dist/sdk-types/support/platform-contracts.d.ts +178 -0
- package/dist/sdk-types/support/platform-contracts.js +15 -0
- package/dist/sdk-types/support/sdk-model-names.d.ts +44 -0
- package/dist/sdk-types/support/sdk-model-names.js +80 -0
- package/dist/sdk-types/support/sdk-search-request.d.ts +182 -0
- package/dist/sdk-types/support/sdk-search-request.js +0 -0
- package/dist/sdk-types/support/task-planning.d.ts +189 -0
- package/dist/sdk-types/support/task-planning.js +0 -0
- package/dist/sdk-types/treedx/graph/sdk-graph-query-request.d.ts +157 -0
- package/dist/sdk-types/treedx/graph/sdk-graph-query-request.js +0 -0
- package/dist/secrets-capability/provider-operation-contracts.d.ts +90 -0
- package/dist/secrets-capability/provider-operation-contracts.js +10 -0
- package/dist/secrets-capability/service-provider-contracts.d.ts +92 -0
- package/dist/secrets-capability/service-provider-contracts.js +183 -0
- package/dist/secrets-capability/service-vault-contracts.d.ts +61 -0
- package/dist/secrets-capability/service-vault-contracts.js +39 -0
- package/dist/secrets-capability/workflow-operation-contracts.d.ts +95 -0
- package/dist/secrets-capability/workflow-operation-contracts.js +0 -0
- package/dist/standards/canonicalize.d.ts +10 -0
- package/dist/standards/canonicalize.js +43 -0
- package/dist/standards/compatibility-attestation.d.ts +23 -0
- package/dist/standards/compatibility-attestation.js +40 -0
- package/dist/standards/composition.d.ts +15 -0
- package/dist/standards/composition.js +14 -0
- package/dist/standards/contract-bundle.d.ts +8 -0
- package/dist/standards/contract-bundle.js +23 -0
- package/dist/standards/contracts.d.ts +39 -0
- package/dist/standards/contracts.js +0 -0
- package/dist/standards/errors.d.ts +6 -0
- package/dist/standards/errors.js +13 -0
- package/dist/standards/index.d.ts +8 -0
- package/dist/standards/index.js +8 -0
- package/dist/standards/mcp/compare.d.ts +2 -0
- package/dist/standards/mcp/compare.js +38 -0
- package/dist/standards/mcp/contracts.d.ts +34 -0
- package/dist/standards/mcp/contracts.js +0 -0
- package/dist/standards/mcp/index.d.ts +3 -0
- package/dist/standards/mcp/index.js +3 -0
- package/dist/standards/mcp/normalize.d.ts +4 -0
- package/dist/standards/mcp/normalize.js +30 -0
- package/dist/standards/openapi/compare.d.ts +2 -0
- package/dist/standards/openapi/compare.js +112 -0
- package/dist/standards/openapi/contracts.d.ts +24 -0
- package/dist/standards/openapi/contracts.js +0 -0
- package/dist/standards/openapi/index.d.ts +3 -0
- package/dist/standards/openapi/index.js +3 -0
- package/dist/standards/openapi/normalize.d.ts +2 -0
- package/dist/standards/openapi/normalize.js +57 -0
- package/dist/standards/package-manifest.d.ts +37 -0
- package/dist/standards/package-manifest.js +89 -0
- package/dist/standards/portfolio-entry.d.ts +12 -0
- package/dist/standards/portfolio-entry.js +11 -0
- package/dist/standards/typescript/compare.d.ts +2 -0
- package/dist/standards/typescript/compare.js +88 -0
- package/dist/standards/typescript/contracts.d.ts +44 -0
- package/dist/standards/typescript/contracts.js +0 -0
- package/dist/standards/typescript/extract.d.ts +10 -0
- package/dist/standards/typescript/extract.js +244 -0
- package/dist/standards/typescript/index.d.ts +3 -0
- package/dist/standards/typescript/index.js +3 -0
- package/dist/treedx/accounts/auth.d.ts +17 -0
- package/dist/treedx/accounts/auth.js +48 -0
- package/dist/treedx/client/accounts/contracts/auth-mode.d.ts +3 -0
- package/dist/treedx/client/accounts/contracts/auth-mode.js +6 -0
- package/dist/treedx/client/build/creation/build-context.d.ts +3 -0
- package/dist/treedx/client/build/creation/build-context.js +7 -0
- package/dist/treedx/client/build/creation/build-snapshot.d.ts +3 -0
- package/dist/treedx/client/build/creation/build-snapshot.js +8 -0
- package/dist/treedx/client/capacity/allocations/contracts/put-capability-grant.d.ts +3 -0
- package/dist/treedx/client/capacity/allocations/contracts/put-capability-grant.js +7 -0
- package/dist/treedx/client/capacity/allocations/queries/list-capability-grants.d.ts +6 -0
- package/dist/treedx/client/capacity/allocations/queries/list-capability-grants.js +10 -0
- package/dist/treedx/client/methods.d.ts +102 -0
- package/dist/treedx/client/methods.js +209 -0
- package/dist/treedx/client/persistence/contracts/restore-storage.d.ts +3 -0
- package/dist/treedx/client/persistence/contracts/restore-storage.js +6 -0
- package/dist/treedx/client/persistence/lifecycle/verify-storage-restore.d.ts +3 -0
- package/dist/treedx/client/persistence/lifecycle/verify-storage-restore.js +6 -0
- package/dist/treedx/client/repositories/creation/register-repository.d.ts +3 -0
- package/dist/treedx/client/repositories/creation/register-repository.js +7 -0
- package/dist/treedx/client/repositories/queries/get-repository.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/get-repository.js +7 -0
- package/dist/treedx/client/repositories/queries/list-repository-paths.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/list-repository-paths.js +7 -0
- package/dist/treedx/client/repositories/queries/list-repository-refs.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/list-repository-refs.js +12 -0
- package/dist/treedx/client/repositories/queries/query-repository.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/query-repository.js +7 -0
- package/dist/treedx/client/repositories/queries/read-repository-file.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/read-repository-file.js +6 -0
- package/dist/treedx/client/repositories/queries/read-repository-files.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/read-repository-files.js +7 -0
- package/dist/treedx/client/repositories/queries/search-repository-files.d.ts +3 -0
- package/dist/treedx/client/repositories/queries/search-repository-files.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/artifacts/contracts/download-artifact.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/artifacts/contracts/download-artifact.js +9 -0
- package/dist/treedx/client/support/contracts/treedx/authorization/contracts/whoami.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/authorization/contracts/whoami.js +6 -0
- package/dist/treedx/client/support/contracts/treedx/graph/contracts/parse-context-dsl.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/graph/contracts/parse-context-dsl.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/deep-health.d.ts +5 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/deep-health.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/health.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/health.js +6 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/metrics.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/metrics.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/prometheus-metrics.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/prometheus-metrics.js +19 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/ready.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/ready.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/status.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/health/contracts/status.js +6 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/commit.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/commit.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/diff.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/diff.js +6 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/fetch-remote.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/fetch-remote.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/push.d.ts +6 -0
- package/dist/treedx/client/support/contracts/treedx/repositories/contracts/push.js +23 -0
- package/dist/treedx/client/support/contracts/treedx/search/contracts/compact-search-index.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/search/contracts/compact-search-index.js +8 -0
- package/dist/treedx/client/support/contracts/treedx/search/contracts/search.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/search/contracts/search.js +7 -0
- package/dist/treedx/client/support/contracts/treedx/storage/contracts/backup-storage.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/storage/contracts/backup-storage.js +8 -0
- package/dist/treedx/client/support/contracts/treedx/storage/contracts/compact-storage.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/storage/contracts/compact-storage.js +8 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/assert-binary-ok.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/assert-binary-ok.js +15 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/fetch-with-timeout.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/fetch-with-timeout.js +38 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/headers.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/headers.js +15 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/parse-json-response.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/parse-json-response.js +15 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/query.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/query.js +14 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/repo-id.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/repo-id.js +14 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/request.d.ts +6 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/request.js +41 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/throw-api-error.d.ts +2 -0
- package/dist/treedx/client/support/contracts/treedx/transport/contracts/throw-api-error.js +14 -0
- package/dist/treedx/client/support/contracts/treedx/workspaces/contracts/exec.d.ts +3 -0
- package/dist/treedx/client/support/contracts/treedx/workspaces/contracts/exec.js +7 -0
- package/dist/treedx/client/support/creation/apply-changeset.d.ts +3 -0
- package/dist/treedx/client/support/creation/apply-changeset.js +10 -0
- package/dist/treedx/client/support/creation/apply-storage-migration.d.ts +3 -0
- package/dist/treedx/client/support/creation/apply-storage-migration.js +7 -0
- package/dist/treedx/client/support/creation/create-migration.d.ts +6 -0
- package/dist/treedx/client/support/creation/create-migration.js +7 -0
- package/dist/treedx/client/support/creation/export-artifact.d.ts +3 -0
- package/dist/treedx/client/support/creation/export-artifact.js +8 -0
- package/dist/treedx/client/support/creation/plan-storage-migration.d.ts +3 -0
- package/dist/treedx/client/support/creation/plan-storage-migration.js +7 -0
- package/dist/treedx/client/support/creation/request-binary.d.ts +6 -0
- package/dist/treedx/client/support/creation/request-binary.js +42 -0
- package/dist/treedx/client/support/creation/write-file.d.ts +3 -0
- package/dist/treedx/client/support/creation/write-file.js +10 -0
- package/dist/treedx/client/support/creation/write-files.d.ts +3 -0
- package/dist/treedx/client/support/creation/write-files.js +12 -0
- package/dist/treedx/client/support/queries/treedx/artifacts/queries/get-artifact.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/artifacts/queries/get-artifact.js +7 -0
- package/dist/treedx/client/support/queries/treedx/artifacts/queries/list-artifacts.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/artifacts/queries/list-artifacts.js +7 -0
- package/dist/treedx/client/support/queries/treedx/authorization/queries/effective-scope.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/authorization/queries/effective-scope.js +9 -0
- package/dist/treedx/client/support/queries/treedx/authorization/queries/list-capabilities.d.ts +4 -0
- package/dist/treedx/client/support/queries/treedx/authorization/queries/list-capabilities.js +6 -0
- package/dist/treedx/client/support/queries/treedx/federation/queries/get-placement.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/federation/queries/get-placement.js +7 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/get-node.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/get-node.js +7 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/get-related.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/get-related.js +7 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/list-nodes.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/graph/queries/list-nodes.js +7 -0
- package/dist/treedx/client/support/queries/treedx/repositories/queries/list-tree.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/repositories/queries/list-tree.js +10 -0
- package/dist/treedx/client/support/queries/treedx/repositories/queries/read-file.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/repositories/queries/read-file.js +9 -0
- package/dist/treedx/client/support/queries/treedx/search/queries/get-search-index-status.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/search/queries/get-search-index-status.js +7 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/get-migration.d.ts +6 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/get-migration.js +7 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/get-snapshot.d.ts +6 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/get-snapshot.js +7 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/list-storage-migrations.d.ts +6 -0
- package/dist/treedx/client/support/queries/treedx/storage/queries/list-storage-migrations.js +6 -0
- package/dist/treedx/client/support/queries/treedx/transport/queries/list-audit-events.d.ts +15 -0
- package/dist/treedx/client/support/queries/treedx/transport/queries/list-audit-events.js +6 -0
- package/dist/treedx/client/support/queries/treedx/transport/queries/list-repositories.d.ts +3 -0
- package/dist/treedx/client/support/queries/treedx/transport/queries/list-repositories.js +7 -0
- package/dist/treedx/client/support/retirement/cleanup-artifacts.d.ts +3 -0
- package/dist/treedx/client/support/retirement/cleanup-artifacts.js +7 -0
- package/dist/treedx/client/support/retirement/delete-artifact.d.ts +3 -0
- package/dist/treedx/client/support/retirement/delete-artifact.js +7 -0
- package/dist/treedx/client/support/retirement/delete-file.d.ts +3 -0
- package/dist/treedx/client/support/retirement/delete-file.js +10 -0
- package/dist/treedx/client/support/retirement/rollback-storage-migration.d.ts +3 -0
- package/dist/treedx/client/support/retirement/rollback-storage-migration.js +7 -0
- package/dist/treedx/client/support/updates/patch-file.d.ts +3 -0
- package/dist/treedx/client/support/updates/patch-file.js +10 -0
- package/dist/treedx/client/support/updates/refresh-search-index.d.ts +3 -0
- package/dist/treedx/client/support/updates/refresh-search-index.js +8 -0
- package/dist/treedx/client/treedx/blobs/contracts/download-blob.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/contracts/download-blob.js +6 -0
- package/dist/treedx/client/treedx/blobs/creation/create-blob-upload.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/create-blob-upload.js +8 -0
- package/dist/treedx/client/treedx/blobs/creation/request-blob-download.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/request-blob-download.js +27 -0
- package/dist/treedx/client/treedx/blobs/creation/request-blob-upload.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/request-blob-upload.js +41 -0
- package/dist/treedx/client/treedx/blobs/creation/upload-blob-part.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/upload-blob-part.js +27 -0
- package/dist/treedx/client/treedx/blobs/creation/upload-blob.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/upload-blob.js +6 -0
- package/dist/treedx/client/treedx/blobs/creation/write-blob.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/creation/write-blob.js +8 -0
- package/dist/treedx/client/treedx/blobs/lifecycle/complete-blob-upload.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/lifecycle/complete-blob-upload.js +8 -0
- package/dist/treedx/client/treedx/blobs/queries/read-blob.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/queries/read-blob.js +10 -0
- package/dist/treedx/client/treedx/blobs/retirement/abort-blob-upload.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/retirement/abort-blob-upload.js +7 -0
- package/dist/treedx/client/treedx/blobs/retirement/delete-blob.d.ts +3 -0
- package/dist/treedx/client/treedx/blobs/retirement/delete-blob.js +8 -0
- package/dist/treedx/client/treedx/federation/contracts/federated-query.d.ts +3 -0
- package/dist/treedx/client/treedx/federation/contracts/federated-query.js +7 -0
- package/dist/treedx/client/treedx/federation/creation/plan-federated-query.d.ts +3 -0
- package/dist/treedx/client/treedx/federation/creation/plan-federated-query.js +6 -0
- package/dist/treedx/client/treedx/federation/queries/check-mirror-health.d.ts +3 -0
- package/dist/treedx/client/treedx/federation/queries/check-mirror-health.js +7 -0
- package/dist/treedx/client/treedx/federation/updates/promote-mirror.d.ts +3 -0
- package/dist/treedx/client/treedx/federation/updates/promote-mirror.js +7 -0
- package/dist/treedx/client/treedx/federation/updates/sync-mirror.d.ts +3 -0
- package/dist/treedx/client/treedx/federation/updates/sync-mirror.js +7 -0
- package/dist/treedx/client/treedx/graph/contracts/federated-context.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/contracts/federated-context.js +7 -0
- package/dist/treedx/client/treedx/graph/contracts/federated-graph.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/contracts/federated-graph.js +7 -0
- package/dist/treedx/client/treedx/graph/contracts/graph-search.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/contracts/graph-search.js +8 -0
- package/dist/treedx/client/treedx/graph/queries/get-graph-node.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/get-graph-node.js +10 -0
- package/dist/treedx/client/treedx/graph/queries/get-graph-refresh-job.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/get-graph-refresh-job.js +7 -0
- package/dist/treedx/client/treedx/graph/queries/get-subgraph.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/get-subgraph.js +7 -0
- package/dist/treedx/client/treedx/graph/queries/query-graph.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/query-graph.js +7 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-entities.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-entities.js +6 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-files.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-files.js +6 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-sections.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/queries/search-graph-sections.js +6 -0
- package/dist/treedx/client/treedx/graph/updates/refresh-graph.d.ts +3 -0
- package/dist/treedx/client/treedx/graph/updates/refresh-graph.js +7 -0
- package/dist/treedx/client/treedx/search/contracts/federated-search.d.ts +3 -0
- package/dist/treedx/client/treedx/search/contracts/federated-search.js +7 -0
- package/dist/treedx/client/treedx/workspaces/creation/create-workspace.d.ts +3 -0
- package/dist/treedx/client/treedx/workspaces/creation/create-workspace.js +7 -0
- package/dist/treedx/client/treedx/workspaces/retirement/abandon-workspace.d.ts +3 -0
- package/dist/treedx/client/treedx/workspaces/retirement/abandon-workspace.js +12 -0
- package/dist/treedx/client/treedx/workspaces/retirement/close-workspace.d.ts +2 -0
- package/dist/treedx/client/treedx/workspaces/retirement/close-workspace.js +6 -0
- package/dist/treedx/generated/openapi-types.d.ts +104 -0
- package/dist/treedx/support/client.d.ts +251 -0
- package/dist/treedx/support/client.js +252 -0
- package/dist/treedx/support/errors.d.ts +13 -0
- package/dist/treedx/types/tree-dx-actor.d.ts +209 -0
- package/dist/treedx/types/tree-dx-actor.js +8 -0
- package/dist/treedx/types/tree-dx-artifact-list-response.d.ts +13 -0
- package/dist/treedx/types/tree-dx-artifact-list-response.js +0 -0
- package/dist/treedx/types/tree-dx-changeset.d.ts +51 -0
- package/dist/treedx/types/tree-dx-changeset.js +6 -0
- package/dist/treedx/types/tree-dx-context-request.d.ts +245 -0
- package/dist/treedx/types/tree-dx-context-request.js +0 -0
- package/dist/treedx/types/tree-dx-exec-result.d.ts +249 -0
- package/dist/treedx/types/tree-dx-exec-result.js +0 -0
- package/dist/treedx/types/tree-dx-list-tree-request.d.ts +222 -0
- package/dist/treedx/types/tree-dx-list-tree-request.js +0 -0
- package/dist/treedx/types/tree-dx-push-request.d.ts +229 -0
- package/dist/treedx/types/tree-dx-push-request.js +0 -0
- package/dist/treedx/types.d.ts +7 -1084
- package/dist/treedx/types.js +7 -8
- package/dist/types/agents/agent-activity-profile.d.ts +267 -0
- package/dist/types/agents/agent-activity-profile.js +0 -0
- package/dist/types/agents/agent-trigger-kinds.d.ts +189 -0
- package/dist/types/agents/agent-trigger-kinds.js +107 -0
- package/dist/types/agents/execution-run-ref.d.ts +136 -0
- package/dist/types/agents/execution-run-ref.js +0 -0
- package/dist/types/agents.d.ts +3 -493
- package/dist/types/agents.js +3 -95
- package/package.json +77 -362
- package/dist/agent-capacity.d.ts +0 -1100
- package/dist/agent-capacity.js +0 -1822
- package/dist/agent-tools.d.ts +0 -37
- package/dist/agent-tools.js +0 -301
- package/dist/api/app.d.ts +0 -8
- package/dist/api/app.js +0 -404
- package/dist/api/auth/d1-database.d.ts +0 -3
- package/dist/api/auth/d1-database.js +0 -20
- package/dist/api/auth/d1-provider.d.ts +0 -85
- package/dist/api/auth/d1-provider.js +0 -95
- package/dist/api/auth/d1-store.d.ts +0 -121
- package/dist/api/auth/d1-store.js +0 -990
- package/dist/api/auth/memory-provider.d.ts +0 -82
- package/dist/api/auth/memory-provider.js +0 -297
- package/dist/api/auth/rbac.d.ts +0 -22
- package/dist/api/auth/rbac.js +0 -162
- package/dist/api/auth/tokens.d.ts +0 -18
- package/dist/api/auth/tokens.js +0 -56
- package/dist/api/config.d.ts +0 -4
- package/dist/api/config.js +0 -149
- package/dist/api/http.d.ts +0 -28
- package/dist/api/http.js +0 -51
- package/dist/api/index.d.ts +0 -10
- package/dist/api/index.js +0 -29
- package/dist/api/operations-routes.d.ts +0 -11
- package/dist/api/operations-routes.js +0 -39
- package/dist/api/operations.d.ts +0 -3
- package/dist/api/operations.js +0 -26
- package/dist/api/providers.d.ts +0 -2
- package/dist/api/providers.js +0 -85
- package/dist/api/railway.d.ts +0 -53
- package/dist/api/railway.js +0 -71
- package/dist/api/sdk-dispatch.d.ts +0 -6
- package/dist/api/sdk-dispatch.js +0 -14
- package/dist/api/sdk-routes.d.ts +0 -11
- package/dist/api/sdk-routes.js +0 -29
- package/dist/api/templates.d.ts +0 -3
- package/dist/api/templates.js +0 -31
- package/dist/api/types.d.ts +0 -238
- package/dist/capacity-provider.d.ts +0 -494
- package/dist/capacity-provider.js +0 -727
- package/dist/capacity.d.ts +0 -483
- package/dist/capacity.js +0 -2331
- package/dist/cli-tools.d.ts +0 -3
- package/dist/cli-tools.js +0 -46
- package/dist/content-operations.d.ts +0 -97
- package/dist/content-operations.js +0 -250
- package/dist/content-store.d.ts +0 -25
- package/dist/content-store.js +0 -269
- package/dist/control-plane-client.d.ts +0 -203
- package/dist/control-plane-client.js +0 -540
- package/dist/control-plane.d.ts +0 -101
- package/dist/control-plane.js +0 -201
- package/dist/copilot.d.ts +0 -18
- package/dist/copilot.js +0 -90
- package/dist/d1-http.d.ts +0 -38
- package/dist/d1-http.js +0 -95
- package/dist/d1-store.d.ts +0 -336
- package/dist/d1-store.js +0 -1431
- package/dist/db/d1.d.ts +0 -246
- package/dist/db/d1.js +0 -8
- package/dist/db/index.d.ts +0 -3
- package/dist/db/index.js +0 -3
- package/dist/db/market-schema.d.ts +0 -83651
- package/dist/db/market-schema.js +0 -3725
- package/dist/db/node-sqlite.d.ts +0 -297
- package/dist/db/node-sqlite.js +0 -133
- package/dist/db/schema.d.ts +0 -483
- package/dist/db/schema.js +0 -29
- package/dist/dispatch.d.ts +0 -4
- package/dist/dispatch.js +0 -174
- package/dist/field-aliases.d.ts +0 -11
- package/dist/fixture-support.d.ts +0 -24
- package/dist/fixture-support.js +0 -385
- package/dist/frontmatter.d.ts +0 -6
- package/dist/frontmatter.js +0 -33
- package/dist/git-runtime.d.ts +0 -16
- package/dist/git-runtime.js +0 -69
- package/dist/governance.d.ts +0 -133
- package/dist/governance.js +0 -285
- package/dist/graph/build.d.ts +0 -19
- package/dist/graph/build.js +0 -949
- package/dist/graph/dsl.d.ts +0 -2
- package/dist/graph/dsl.js +0 -243
- package/dist/graph/query.d.ts +0 -47
- package/dist/graph/query.js +0 -447
- package/dist/graph/ranking.d.ts +0 -3
- package/dist/graph/ranking.js +0 -483
- package/dist/graph/schema.d.ts +0 -142
- package/dist/graph/schema.js +0 -137
- package/dist/graph.d.ts +0 -52
- package/dist/graph.js +0 -133
- package/dist/guarantees/index.d.ts +0 -512
- package/dist/guarantees/index.js +0 -1782
- package/dist/hosting/apps.d.ts +0 -12
- package/dist/hosting/apps.js +0 -107
- package/dist/hosting/builtins.d.ts +0 -25
- package/dist/hosting/builtins.js +0 -685
- package/dist/hosting/contracts.d.ts +0 -190
- package/dist/hosting/graph.d.ts +0 -110
- package/dist/hosting/graph.js +0 -993
- package/dist/hosting/index.d.ts +0 -4
- package/dist/hosting/index.js +0 -4
- package/dist/index.d.ts +0 -88
- package/dist/index.js +0 -757
- package/dist/local-dev/managed-dev.d.ts +0 -85
- package/dist/local-dev/managed-dev.js +0 -431
- package/dist/managed-dependencies.d.ts +0 -91
- package/dist/managed-dependencies.js +0 -1186
- package/dist/market-client.d.ts +0 -831
- package/dist/market-client.js +0 -1219
- package/dist/model-registry.d.ts +0 -8
- package/dist/model-registry.js +0 -674
- package/dist/operations/agent-tools.d.ts +0 -105
- package/dist/operations/agent-tools.js +0 -171
- package/dist/operations/providers/default.d.ts +0 -10
- package/dist/operations/providers/default.js +0 -1245
- package/dist/operations/repository-operations.d.ts +0 -158
- package/dist/operations/repository-operations.js +0 -851
- package/dist/operations/runtime.d.ts +0 -7
- package/dist/operations/runtime.js +0 -61
- package/dist/operations/services/bootstrap-runner.d.ts +0 -39
- package/dist/operations/services/bootstrap-runner.js +0 -191
- package/dist/operations/services/build-warning-policy.d.ts +0 -34
- package/dist/operations/services/build-warning-policy.js +0 -91
- package/dist/operations/services/commit-message-provider.d.ts +0 -65
- package/dist/operations/services/commit-message-provider.js +0 -496
- package/dist/operations/services/config-runtime.d.ts +0 -558
- package/dist/operations/services/config-runtime.js +0 -3494
- package/dist/operations/services/d1-migration.d.ts +0 -6
- package/dist/operations/services/d1-migration.js +0 -120
- package/dist/operations/services/deploy.d.ts +0 -645
- package/dist/operations/services/deploy.js +0 -3826
- package/dist/operations/services/deployment-readiness.d.ts +0 -30
- package/dist/operations/services/deployment-readiness.js +0 -217
- package/dist/operations/services/export-runtime.d.ts +0 -18
- package/dist/operations/services/export-runtime.js +0 -140
- package/dist/operations/services/git-remote-policy.d.ts +0 -9
- package/dist/operations/services/git-remote-policy.js +0 -63
- package/dist/operations/services/git-runner.d.ts +0 -68
- package/dist/operations/services/git-runner.js +0 -500
- package/dist/operations/services/git-workflow.d.ts +0 -255
- package/dist/operations/services/git-workflow.js +0 -845
- package/dist/operations/services/github-actions-verification.d.ts +0 -148
- package/dist/operations/services/github-actions-verification.js +0 -589
- package/dist/operations/services/github-api.d.ts +0 -279
- package/dist/operations/services/github-api.js +0 -936
- package/dist/operations/services/github-automation.d.ts +0 -230
- package/dist/operations/services/github-automation.js +0 -520
- package/dist/operations/services/github-credentials.d.ts +0 -13
- package/dist/operations/services/github-credentials.js +0 -59
- package/dist/operations/services/hosted-service-checks.d.ts +0 -82
- package/dist/operations/services/hosted-service-checks.js +0 -527
- package/dist/operations/services/hosting-audit.d.ts +0 -69
- package/dist/operations/services/hosting-audit.js +0 -638
- package/dist/operations/services/hub-launch.d.ts +0 -182
- package/dist/operations/services/hub-launch.js +0 -309
- package/dist/operations/services/hub-provider-launch.d.ts +0 -160
- package/dist/operations/services/hub-provider-launch.js +0 -1056
- package/dist/operations/services/key-agent.d.ts +0 -137
- package/dist/operations/services/key-agent.js +0 -641
- package/dist/operations/services/live-hosted-service-checks.d.ts +0 -30
- package/dist/operations/services/live-hosted-service-checks.js +0 -611
- package/dist/operations/services/local-cleanup.d.ts +0 -30
- package/dist/operations/services/local-cleanup.js +0 -77
- package/dist/operations/services/local-dev.d.ts +0 -8
- package/dist/operations/services/local-dev.js +0 -97
- package/dist/operations/services/managed-host-security.d.ts +0 -13
- package/dist/operations/services/managed-host-security.js +0 -56
- package/dist/operations/services/managed-repositories.d.ts +0 -37
- package/dist/operations/services/managed-repositories.js +0 -177
- package/dist/operations/services/market-packaging.d.ts +0 -59
- package/dist/operations/services/market-packaging.js +0 -234
- package/dist/operations/services/operations-runner-smoke.d.ts +0 -31
- package/dist/operations/services/operations-runner-smoke.js +0 -212
- package/dist/operations/services/package-adapters.d.ts +0 -184
- package/dist/operations/services/package-adapters.js +0 -912
- package/dist/operations/services/package-artifacts.d.ts +0 -37
- package/dist/operations/services/package-artifacts.js +0 -99
- package/dist/operations/services/package-reference-policy.d.ts +0 -64
- package/dist/operations/services/package-reference-policy.js +0 -364
- package/dist/operations/services/project-host-operations.d.ts +0 -153
- package/dist/operations/services/project-host-operations.js +0 -365
- package/dist/operations/services/project-platform.d.ts +0 -727
- package/dist/operations/services/project-platform.js +0 -1603
- package/dist/operations/services/project-web-monitor.d.ts +0 -15
- package/dist/operations/services/project-web-monitor.js +0 -377
- package/dist/operations/services/railway-api.d.ts +0 -494
- package/dist/operations/services/railway-api.js +0 -1874
- package/dist/operations/services/railway-cli.d.ts +0 -47
- package/dist/operations/services/railway-cli.js +0 -142
- package/dist/operations/services/railway-deploy.d.ts +0 -161
- package/dist/operations/services/railway-deploy.js +0 -1808
- package/dist/operations/services/railway-source-policy.d.ts +0 -25
- package/dist/operations/services/railway-source-policy.js +0 -111
- package/dist/operations/services/release-history.d.ts +0 -59
- package/dist/operations/services/release-history.js +0 -158
- package/dist/operations/services/release-proof-ledger.d.ts +0 -13
- package/dist/operations/services/release-proof-ledger.js +0 -81
- package/dist/operations/services/release-proof-planner.d.ts +0 -48
- package/dist/operations/services/release-proof-planner.js +0 -195
- package/dist/operations/services/release-proof-runner.d.ts +0 -16
- package/dist/operations/services/release-proof-runner.js +0 -169
- package/dist/operations/services/release-proof.d.ts +0 -81
- package/dist/operations/services/release-proof.js +0 -79
- package/dist/operations/services/repository-save-orchestrator.d.ts +0 -193
- package/dist/operations/services/repository-save-orchestrator.js +0 -2110
- package/dist/operations/services/runtime-paths.d.ts +0 -19
- package/dist/operations/services/runtime-paths.js +0 -54
- package/dist/operations/services/runtime-tools.d.ts +0 -35
- package/dist/operations/services/runtime-tools.js +0 -212
- package/dist/operations/services/save-deploy-preflight.d.ts +0 -32
- package/dist/operations/services/save-deploy-preflight.js +0 -82
- package/dist/operations/services/template-host-bindings.d.ts +0 -68
- package/dist/operations/services/template-host-bindings.js +0 -401
- package/dist/operations/services/template-registry.d.ts +0 -110
- package/dist/operations/services/template-registry.js +0 -595
- package/dist/operations/services/template-secret-sync.d.ts +0 -97
- package/dist/operations/services/template-secret-sync.js +0 -292
- package/dist/operations/services/verification-cache.d.ts +0 -25
- package/dist/operations/services/verification-cache.js +0 -71
- package/dist/operations/services/watch-dev.d.ts +0 -21
- package/dist/operations/services/watch-dev.js +0 -282
- package/dist/operations/services/workflow-timing.d.ts +0 -25
- package/dist/operations/services/workflow-timing.js +0 -79
- package/dist/operations/services/workspace-dependency-mode.d.ts +0 -74
- package/dist/operations/services/workspace-dependency-mode.js +0 -478
- package/dist/operations/services/workspace-preflight.d.ts +0 -40
- package/dist/operations/services/workspace-preflight.js +0 -172
- package/dist/operations/services/workspace-save-script.d.ts +0 -26
- package/dist/operations/services/workspace-save-script.js +0 -50
- package/dist/operations/services/workspace-save.d.ts +0 -76
- package/dist/operations/services/workspace-save.js +0 -453
- package/dist/operations/services/workspace-tools.d.ts +0 -17
- package/dist/operations/services/workspace-tools.js +0 -320
- package/dist/operations-registry.d.ts +0 -5
- package/dist/operations-registry.js +0 -98
- package/dist/operations-types.d.ts +0 -75
- package/dist/operations-types.js +0 -17
- package/dist/operations.d.ts +0 -17
- package/dist/operations.js +0 -114
- package/dist/platform/book-export.d.ts +0 -78
- package/dist/platform/book-export.js +0 -451
- package/dist/platform/books-data.d.ts +0 -28
- package/dist/platform/books-data.js +0 -111
- package/dist/platform/config.d.ts +0 -27
- package/dist/platform/config.js +0 -100
- package/dist/platform/content-runtime-source.d.ts +0 -54
- package/dist/platform/content-runtime-source.js +0 -169
- package/dist/platform/contracts.d.ts +0 -361
- package/dist/platform/deploy-config.d.ts +0 -17
- package/dist/platform/deploy-config.js +0 -731
- package/dist/platform/deploy-runtime.d.ts +0 -22
- package/dist/platform/deploy-runtime.js +0 -93
- package/dist/platform/desired-state.d.ts +0 -66
- package/dist/platform/desired-state.js +0 -1071
- package/dist/platform/env.yaml +0 -1126
- package/dist/platform/environment.d.ts +0 -159
- package/dist/platform/environment.js +0 -778
- package/dist/platform/local-content-materialization.d.ts +0 -9
- package/dist/platform/local-content-materialization.js +0 -212
- package/dist/platform/plugin.d.ts +0 -103
- package/dist/platform/plugin.js +0 -6
- package/dist/platform/plugins/constants.d.ts +0 -26
- package/dist/platform/plugins/constants.js +0 -33
- package/dist/platform/plugins/runtime.d.ts +0 -41
- package/dist/platform/plugins/runtime.js +0 -257
- package/dist/platform/plugins.d.ts +0 -6
- package/dist/platform/plugins.js +0 -42
- package/dist/platform/published-content-pipeline.d.ts +0 -84
- package/dist/platform/published-content-pipeline.js +0 -547
- package/dist/platform/published-content.d.ts +0 -224
- package/dist/platform/published-content.js +0 -611
- package/dist/platform/site-config-schema.d.ts +0 -1
- package/dist/platform/site-config-schema.js +0 -4
- package/dist/platform/tenant/runtime-config.d.ts +0 -154
- package/dist/platform/tenant/runtime-config.js +0 -69
- package/dist/platform/tenant-config.d.ts +0 -8
- package/dist/platform/tenant-config.js +0 -207
- package/dist/platform/utils/site-config-schema.d.ts +0 -154
- package/dist/platform/utils/site-config-schema.js +0 -450
- package/dist/platform-operation-store.d.ts +0 -94
- package/dist/platform-operation-store.js +0 -531
- package/dist/platform-operations.d.ts +0 -273
- package/dist/platform-operations.js +0 -440
- package/dist/plugin-default.d.ts +0 -28
- package/dist/plugin-default.js +0 -55
- package/dist/project-import.d.ts +0 -91
- package/dist/project-import.js +0 -257
- package/dist/project-workflow.d.ts +0 -233
- package/dist/project-workflow.js +0 -82
- package/dist/reconcile/bootstrap-systems.d.ts +0 -32
- package/dist/reconcile/bootstrap-systems.js +0 -175
- package/dist/reconcile/builtin-adapters.d.ts +0 -9
- package/dist/reconcile/builtin-adapters.js +0 -5545
- package/dist/reconcile/contracts.d.ts +0 -172
- package/dist/reconcile/desired-state.d.ts +0 -10
- package/dist/reconcile/desired-state.js +0 -322
- package/dist/reconcile/engine.d.ts +0 -118
- package/dist/reconcile/engine.js +0 -645
- package/dist/reconcile/errors.d.ts +0 -5
- package/dist/reconcile/errors.js +0 -13
- package/dist/reconcile/index.d.ts +0 -10
- package/dist/reconcile/index.js +0 -10
- package/dist/reconcile/live-acceptance.d.ts +0 -100
- package/dist/reconcile/live-acceptance.js +0 -2018
- package/dist/reconcile/platform.d.ts +0 -104
- package/dist/reconcile/platform.js +0 -100
- package/dist/reconcile/providers/docker-private.d.ts +0 -54
- package/dist/reconcile/providers/docker-private.js +0 -101
- package/dist/reconcile/providers/github-private.d.ts +0 -69
- package/dist/reconcile/providers/github-private.js +0 -145
- package/dist/reconcile/providers/local-private.d.ts +0 -28
- package/dist/reconcile/providers/local-private.js +0 -75
- package/dist/reconcile/providers/railway-iac.d.ts +0 -220
- package/dist/reconcile/providers/railway-iac.js +0 -621
- package/dist/reconcile/providers/release-private.d.ts +0 -89
- package/dist/reconcile/providers/release-private.js +0 -140
- package/dist/reconcile/registry.d.ts +0 -7
- package/dist/reconcile/registry.js +0 -79
- package/dist/reconcile/state.d.ts +0 -7
- package/dist/reconcile/state.js +0 -270
- package/dist/reconcile/units.d.ts +0 -6
- package/dist/reconcile/units.js +0 -72
- package/dist/remote.d.ts +0 -244
- package/dist/remote.js +0 -284
- package/dist/scenes/artifacts.d.ts +0 -16
- package/dist/scenes/artifacts.js +0 -61
- package/dist/scenes/auth.d.ts +0 -2
- package/dist/scenes/auth.js +0 -28
- package/dist/scenes/base-url.d.ts +0 -12
- package/dist/scenes/base-url.js +0 -123
- package/dist/scenes/builtin-plugins.d.ts +0 -2
- package/dist/scenes/builtin-plugins.js +0 -499
- package/dist/scenes/checkpoints.d.ts +0 -14
- package/dist/scenes/checkpoints.js +0 -33
- package/dist/scenes/device-matrix.d.ts +0 -2
- package/dist/scenes/device-matrix.js +0 -100
- package/dist/scenes/devices.d.ts +0 -11
- package/dist/scenes/devices.js +0 -40
- package/dist/scenes/diagnostics.d.ts +0 -5
- package/dist/scenes/diagnostics.js +0 -22
- package/dist/scenes/diagram-providers.d.ts +0 -2
- package/dist/scenes/diagram-providers.js +0 -181
- package/dist/scenes/diagram-validation.d.ts +0 -22
- package/dist/scenes/diagram-validation.js +0 -74
- package/dist/scenes/environment.d.ts +0 -2
- package/dist/scenes/environment.js +0 -65
- package/dist/scenes/evidence-bundle.d.ts +0 -5
- package/dist/scenes/evidence-bundle.js +0 -42
- package/dist/scenes/evidence-report.d.ts +0 -10
- package/dist/scenes/evidence-report.js +0 -73
- package/dist/scenes/evidence.d.ts +0 -16
- package/dist/scenes/evidence.js +0 -277
- package/dist/scenes/index.d.ts +0 -48
- package/dist/scenes/index.js +0 -312
- package/dist/scenes/inspect.d.ts +0 -9
- package/dist/scenes/inspect.js +0 -64
- package/dist/scenes/loader.d.ts +0 -3
- package/dist/scenes/loader.js +0 -38
- package/dist/scenes/logs.d.ts +0 -2
- package/dist/scenes/logs.js +0 -47
- package/dist/scenes/operations.d.ts +0 -3
- package/dist/scenes/operations.js +0 -128
- package/dist/scenes/phase0.d.ts +0 -9
- package/dist/scenes/phase0.js +0 -637
- package/dist/scenes/planner.d.ts +0 -12
- package/dist/scenes/planner.js +0 -166
- package/dist/scenes/playwright-adapter.d.ts +0 -2
- package/dist/scenes/playwright-adapter.js +0 -63
- package/dist/scenes/plugins.d.ts +0 -5
- package/dist/scenes/plugins.js +0 -115
- package/dist/scenes/progress.d.ts +0 -17
- package/dist/scenes/progress.js +0 -24
- package/dist/scenes/publish-destinations.d.ts +0 -12
- package/dist/scenes/publish-destinations.js +0 -99
- package/dist/scenes/publish-plan-report.d.ts +0 -10
- package/dist/scenes/publish-plan-report.js +0 -70
- package/dist/scenes/publish-plan.d.ts +0 -15
- package/dist/scenes/publish-plan.js +0 -248
- package/dist/scenes/publish-redaction.d.ts +0 -16
- package/dist/scenes/publish-redaction.js +0 -148
- package/dist/scenes/publish-release-record.d.ts +0 -13
- package/dist/scenes/publish-release-record.js +0 -34
- package/dist/scenes/publish-report.d.ts +0 -10
- package/dist/scenes/publish-report.js +0 -67
- package/dist/scenes/publish.d.ts +0 -16
- package/dist/scenes/publish.js +0 -298
- package/dist/scenes/registry.d.ts +0 -12
- package/dist/scenes/registry.js +0 -65
- package/dist/scenes/remotion-adapter.d.ts +0 -3
- package/dist/scenes/remotion-adapter.js +0 -94
- package/dist/scenes/remotion-composition-registry.d.ts +0 -2
- package/dist/scenes/remotion-composition-registry.js +0 -12
- package/dist/scenes/remotion-compositions.d.ts +0 -15
- package/dist/scenes/remotion-compositions.js +0 -464
- package/dist/scenes/remotion-diagrams.d.ts +0 -9
- package/dist/scenes/remotion-diagrams.js +0 -188
- package/dist/scenes/remotion-input.d.ts +0 -13
- package/dist/scenes/remotion-input.js +0 -291
- package/dist/scenes/remotion-visuals.d.ts +0 -29
- package/dist/scenes/remotion-visuals.js +0 -198
- package/dist/scenes/render-media-assets.d.ts +0 -9
- package/dist/scenes/render-media-assets.js +0 -117
- package/dist/scenes/render-report.d.ts +0 -10
- package/dist/scenes/render-report.js +0 -27
- package/dist/scenes/render.d.ts +0 -2
- package/dist/scenes/render.js +0 -218
- package/dist/scenes/reporter.d.ts +0 -3
- package/dist/scenes/reporter.js +0 -101
- package/dist/scenes/resume.d.ts +0 -2
- package/dist/scenes/resume.js +0 -79
- package/dist/scenes/runner.d.ts +0 -2
- package/dist/scenes/runner.js +0 -687
- package/dist/scenes/schema.d.ts +0 -6
- package/dist/scenes/schema.js +0 -1023
- package/dist/scenes/seed.d.ts +0 -2
- package/dist/scenes/seed.js +0 -83
- package/dist/scenes/segments.d.ts +0 -15
- package/dist/scenes/segments.js +0 -79
- package/dist/scenes/selectors.d.ts +0 -3
- package/dist/scenes/selectors.js +0 -21
- package/dist/scenes/timeline.d.ts +0 -11
- package/dist/scenes/timeline.js +0 -26
- package/dist/scenes/timeouts.d.ts +0 -6
- package/dist/scenes/timeouts.js +0 -17
- package/dist/scenes/training-captions.d.ts +0 -14
- package/dist/scenes/training-captions.js +0 -98
- package/dist/scenes/training-report.d.ts +0 -10
- package/dist/scenes/training-report.js +0 -78
- package/dist/scenes/training-transcript.d.ts +0 -24
- package/dist/scenes/training-transcript.js +0 -204
- package/dist/scenes/training.d.ts +0 -10
- package/dist/scenes/training.js +0 -134
- package/dist/scenes/types.d.ts +0 -1900
- package/dist/scenes/types.js +0 -8
- package/dist/scenes/visual-audit-contact-sheets.d.ts +0 -6
- package/dist/scenes/visual-audit-contact-sheets.js +0 -119
- package/dist/scenes/visual-audit-fixtures.d.ts +0 -25
- package/dist/scenes/visual-audit-fixtures.js +0 -247
- package/dist/scenes/visual-audit-report.d.ts +0 -11
- package/dist/scenes/visual-audit-report.js +0 -85
- package/dist/scenes/visual-audit-review.d.ts +0 -23
- package/dist/scenes/visual-audit-review.js +0 -829
- package/dist/scenes/visual-audit-routes.d.ts +0 -13
- package/dist/scenes/visual-audit-routes.js +0 -308
- package/dist/scenes/visual-audit.d.ts +0 -3
- package/dist/scenes/visual-audit.js +0 -596
- package/dist/scripts/aggregate-book.js +0 -16
- package/dist/scripts/assert-release-tag-version.js +0 -23
- package/dist/scripts/build-dist.js +0 -294
- package/dist/scripts/build-tenant-worker.js +0 -36
- package/dist/scripts/check-build-warnings.js +0 -51
- package/dist/scripts/cleanup-markdown.js +0 -377
- package/dist/scripts/cli-test-fixtures.js +0 -44
- package/dist/scripts/config-treeseed.js +0 -131
- package/dist/scripts/deployment-readiness.js +0 -20
- package/dist/scripts/generate-treedx-openapi-types.js +0 -186
- package/dist/scripts/install-managed-dependencies.js +0 -12
- package/dist/scripts/key-agent.js +0 -165
- package/dist/scripts/local-dev.js +0 -129
- package/dist/scripts/migrate-journey-csv-to-guarantees.js +0 -140
- package/dist/scripts/operations-runner-smoke.js +0 -16
- package/dist/scripts/package-tools.js +0 -85
- package/dist/scripts/patch-starlight-content-path.js +0 -173
- package/dist/scripts/prepare.js +0 -10
- package/dist/scripts/publish-package.js +0 -29
- package/dist/scripts/release-verify.js +0 -105
- package/dist/scripts/run-fixture-astro-command.js +0 -18
- package/dist/scripts/scaffold-site.js +0 -66
- package/dist/scripts/sync-template.js +0 -20
- package/dist/scripts/template-catalog.test.js +0 -86
- package/dist/scripts/template-command.js +0 -31
- package/dist/scripts/tenant-astro-command.js +0 -3
- package/dist/scripts/tenant-build.js +0 -48
- package/dist/scripts/tenant-check.js +0 -10
- package/dist/scripts/tenant-d1-migrate-local.js +0 -15
- package/dist/scripts/tenant-deploy.js +0 -220
- package/dist/scripts/tenant-destroy.js +0 -112
- package/dist/scripts/tenant-dev.js +0 -160
- package/dist/scripts/tenant-lint.js +0 -4
- package/dist/scripts/tenant-test.js +0 -4
- package/dist/scripts/tenant-workflow-action.js +0 -101
- package/dist/scripts/test-cloudflare-local.js +0 -209
- package/dist/scripts/test-scaffold.js +0 -300
- package/dist/scripts/test-smoke.js +0 -137
- package/dist/scripts/treeseed-assert-release-tag-version.js +0 -21
- package/dist/scripts/treeseed-build-dist.js +0 -131
- package/dist/scripts/treeseed-publish-package.js +0 -19
- package/dist/scripts/treeseed-release-verify.js +0 -131
- package/dist/scripts/validate-templates.js +0 -6
- package/dist/scripts/verify-driver.js +0 -45
- package/dist/scripts/workflow-commands.test.js +0 -31
- package/dist/scripts/workspace-command-e2e.js +0 -718
- package/dist/scripts/workspace-lint.js +0 -15
- package/dist/scripts/workspace-preflight.js +0 -22
- package/dist/scripts/workspace-publish-changed-packages.js +0 -16
- package/dist/scripts/workspace-release-test.js +0 -104
- package/dist/scripts/workspace-release-verify.js +0 -81
- package/dist/scripts/workspace-save.js +0 -64
- package/dist/scripts/workspace-start-warning.js +0 -3
- package/dist/scripts/workspace-test-unit.js +0 -4
- package/dist/scripts/workspace-test.js +0 -17
- package/dist/sdk-dispatch.d.ts +0 -12
- package/dist/sdk-dispatch.js +0 -126
- package/dist/sdk-fields.d.ts +0 -11
- package/dist/sdk-filters.d.ts +0 -4
- package/dist/sdk-filters.js +0 -74
- package/dist/sdk-types.d.ts +0 -3868
- package/dist/sdk-types.js +0 -542
- package/dist/sdk-version.d.ts +0 -2
- package/dist/sdk-version.js +0 -42
- package/dist/sdk.d.ts +0 -266
- package/dist/sdk.js +0 -825
- package/dist/secrets-capability.d.ts +0 -421
- package/dist/secrets-capability.js +0 -530
- package/dist/seeds/errors.d.ts +0 -5
- package/dist/seeds/errors.js +0 -22
- package/dist/seeds/index.d.ts +0 -20
- package/dist/seeds/index.js +0 -53
- package/dist/seeds/loader.d.ts +0 -8
- package/dist/seeds/loader.js +0 -32
- package/dist/seeds/normalize.d.ts +0 -13
- package/dist/seeds/normalize.js +0 -282
- package/dist/seeds/planner.d.ts +0 -10
- package/dist/seeds/planner.js +0 -149
- package/dist/seeds/schema.d.ts +0 -2
- package/dist/seeds/schema.js +0 -893
- package/dist/seeds/types.d.ts +0 -312
- package/dist/seeds/types.js +0 -12
- package/dist/service-credentials.d.ts +0 -54
- package/dist/service-credentials.js +0 -112
- package/dist/stores/cursor-store.d.ts +0 -10
- package/dist/stores/cursor-store.js +0 -164
- package/dist/stores/envelopes.d.ts +0 -116
- package/dist/stores/envelopes.js +0 -219
- package/dist/stores/helpers.d.ts +0 -12
- package/dist/stores/helpers.js +0 -42
- package/dist/stores/lease-store.d.ts +0 -18
- package/dist/stores/lease-store.js +0 -189
- package/dist/stores/message-store.d.ts +0 -12
- package/dist/stores/message-store.js +0 -273
- package/dist/stores/operational-store.d.ts +0 -55
- package/dist/stores/operational-store.js +0 -861
- package/dist/stores/project-workflow-store.d.ts +0 -56
- package/dist/stores/project-workflow-store.js +0 -482
- package/dist/stores/run-store.d.ts +0 -9
- package/dist/stores/run-store.js +0 -75
- package/dist/stores/subscription-store.d.ts +0 -8
- package/dist/stores/subscription-store.js +0 -73
- package/dist/template-catalog.d.ts +0 -13
- package/dist/template-catalog.js +0 -151
- package/dist/template-launch-requirements.d.ts +0 -118
- package/dist/template-launch-requirements.js +0 -768
- package/dist/template-launch-ui.d.ts +0 -85
- package/dist/template-launch-ui.js +0 -189
- package/dist/timing.d.ts +0 -20
- package/dist/timing.js +0 -73
- package/dist/treedx/adapters.d.ts +0 -6
- package/dist/treedx/adapters.js +0 -36
- package/dist/treedx/client.d.ts +0 -222
- package/dist/treedx/client.js +0 -871
- package/dist/treedx/errors.d.ts +0 -13
- package/dist/treedx/federated-client.d.ts +0 -27
- package/dist/treedx/federated-client.js +0 -158
- package/dist/treedx/graph-adapter.d.ts +0 -33
- package/dist/treedx/graph-adapter.js +0 -156
- package/dist/treedx/index.d.ts +0 -14
- package/dist/treedx/index.js +0 -48
- package/dist/treedx/market-integration.d.ts +0 -27
- package/dist/treedx/market-integration.js +0 -131
- package/dist/treedx/ports.d.ts +0 -166
- package/dist/treedx/ports.js +0 -231
- package/dist/treedx/query-adapter.d.ts +0 -19
- package/dist/treedx/query-adapter.js +0 -62
- package/dist/treedx/registry-client.d.ts +0 -11
- package/dist/treedx/registry-client.js +0 -19
- package/dist/treedx/repository-adapter.d.ts +0 -45
- package/dist/treedx/repository-adapter.js +0 -319
- package/dist/treedx/sdk-integration.d.ts +0 -27
- package/dist/treedx/sdk-integration.js +0 -63
- package/dist/treedx/workspace-adapter.d.ts +0 -27
- package/dist/treedx/workspace-adapter.js +0 -65
- package/dist/treedx-backends.d.ts +0 -220
- package/dist/treedx-backends.js +0 -634
- package/dist/treedx-client.d.ts +0 -89
- package/dist/treedx-client.js +0 -184
- package/dist/treeseed/services/compose.yml +0 -6
- package/dist/treeseed/template-catalog/catalog.fixture.json +0 -707
- package/dist/types/cloudflare.d.ts +0 -54
- package/dist/verification.d.ts +0 -31
- package/dist/verification.js +0 -430
- package/dist/workflow/operations.d.ts +0 -1516
- package/dist/workflow/operations.js +0 -7001
- package/dist/workflow/policy.d.ts +0 -12
- package/dist/workflow/policy.js +0 -70
- package/dist/workflow/runs.d.ts +0 -143
- package/dist/workflow/runs.js +0 -720
- package/dist/workflow/session.d.ts +0 -34
- package/dist/workflow/session.js +0 -160
- package/dist/workflow/worktrees.d.ts +0 -43
- package/dist/workflow/worktrees.js +0 -307
- package/dist/workflow-state.d.ts +0 -250
- package/dist/workflow-state.js +0 -844
- package/dist/workflow-support.d.ts +0 -28
- package/dist/workflow-support.js +0 -390
- package/dist/workflow.d.ts +0 -327
- package/dist/workflow.js +0 -140
- package/drizzle/d1/0000_treeseed_d1.sql +0 -19
- package/drizzle/market/0000_market_control_plane.sql +0 -3223
- package/drizzle/market/0001_capacity_budget_mode_default.sql +0 -4
- package/drizzle/market/0002_user_email_addresses.sql +0 -26
- package/drizzle/market/0003_project_team_slug_unique.sql +0 -4
- package/drizzle/market/0004_treedx_market_integration.sql +0 -99
- package/drizzle/market/0005_agent_capacity_coordination.sql +0 -145
- package/drizzle/market/0006_provider_assignment_lifecycle.sql +0 -9
- package/drizzle/market/0007_agent_architecture_gap_closure.sql +0 -157
- package/drizzle/market/0008_agent_capacity_operational_closure.sql +0 -27
- package/drizzle/market/0009_agent_capacity_runtime_hardening.sql +0 -29
- package/drizzle/market/0010_secrets_capability_persistence.sql +0 -130
- package/drizzle/market/0011_github_app_adapter.sql +0 -47
- package/drizzle/market/0012_workday_test_runs.sql +0 -44
- package/drizzle/market/0013_runtime_task_lifecycle.sql +0 -39
- package/drizzle/market/0014_governance_pipeline.sql +0 -189
- package/drizzle/market/0015_agent_decision_assignment_graphs.sql +0 -60
- package/scripts/verify-driver.ts +0 -51
- package/templates/github/deploy-web.workflow.yml +0 -168
- package/templates/github/hosted-project.workflow.yml +0 -76
- /package/dist/{api/types.js → agent-capacity/contracts/capacity/workdays/workday-records.js} +0 -0
- /package/dist/{hosting/contracts.js → agent-capacity/contracts/operations/workflow-promotion.js} +0 -0
- /package/dist/{platform/contracts.js → agent-capacity/contracts/projects/agents/project-agent-class.js} +0 -0
- /package/dist/{reconcile/contracts.js → agent-capacity/contracts/runtime/runtime-observability.js} +0 -0
- /package/dist/{types/cloudflare.js → agent-capacity/contracts/support/decision-work.js} +0 -0
- /package/dist/{field-aliases.js → entrypoints/models/field-aliases.js} +0 -0
- /package/dist/{sdk-fields.js → entrypoints/models/sdk-fields.js} +0 -0
- /package/dist/{runtime.d.ts → runtime/runtime.d.ts} +0 -0
- /package/dist/{runtime.js → runtime/runtime.js} +0 -0
- /package/dist/treedx/{errors.js → support/errors.js} +0 -0
|
@@ -1,3494 +0,0 @@
|
|
|
1
|
-
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { dirname, resolve } from "node:path";
|
|
5
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
6
|
-
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
7
|
-
import {
|
|
8
|
-
getTreeseedEnvironmentSuggestedValues,
|
|
9
|
-
isTreeseedEnvironmentEntryRelevant,
|
|
10
|
-
isTreeseedEnvironmentEntryRequired,
|
|
11
|
-
resolveTreeseedEnvironmentRegistry,
|
|
12
|
-
TREESEED_ENVIRONMENT_SCOPES,
|
|
13
|
-
validateTreeseedEnvironmentValues
|
|
14
|
-
} from "../../platform/environment.js";
|
|
15
|
-
import { loadTreeseedManifest } from "../../platform/tenant-config.js";
|
|
16
|
-
import {
|
|
17
|
-
buildProvisioningSummary,
|
|
18
|
-
createPersistentDeployTarget,
|
|
19
|
-
ensureGeneratedWranglerConfig,
|
|
20
|
-
loadDeployState,
|
|
21
|
-
syncCloudflareSecrets
|
|
22
|
-
} from "./deploy.js";
|
|
23
|
-
import {
|
|
24
|
-
collectTreeseedReconcileStatus,
|
|
25
|
-
reconcileTreeseedTarget,
|
|
26
|
-
resolveTreeseedBootstrapSelection
|
|
27
|
-
} from "../../reconcile/index.js";
|
|
28
|
-
import {
|
|
29
|
-
ensureGitHubBootstrapRepository,
|
|
30
|
-
maybeResolveGitHubRepositorySlug
|
|
31
|
-
} from "./github-automation.js";
|
|
32
|
-
import {
|
|
33
|
-
buildRailwayCommandEnv,
|
|
34
|
-
configuredRailwayServices
|
|
35
|
-
} from "./railway-deploy.js";
|
|
36
|
-
import {
|
|
37
|
-
ensureRailwayEnvironment,
|
|
38
|
-
ensureRailwayProject,
|
|
39
|
-
ensureRailwayService,
|
|
40
|
-
normalizeRailwayEnvironmentName,
|
|
41
|
-
resolveRailwayWorkspace,
|
|
42
|
-
resolveRailwayWorkspaceContext,
|
|
43
|
-
upsertRailwayVariables
|
|
44
|
-
} from "./railway-api.js";
|
|
45
|
-
import { discoverTreeseedApplications } from "../../hosting/apps.js";
|
|
46
|
-
import {
|
|
47
|
-
createGitHubApiClient,
|
|
48
|
-
ensureGitHubBranchFromBase
|
|
49
|
-
} from "./github-api.js";
|
|
50
|
-
import { resolveGitHubCredentialForRepository } from "./github-credentials.js";
|
|
51
|
-
import { loadCliDeployConfig, packageDistScriptRoot, packageScriptPath, resolveWranglerBin, withProcessCwd } from "./runtime-tools.js";
|
|
52
|
-
import { PRODUCTION_BRANCH, STAGING_BRANCH } from "./git-workflow.js";
|
|
53
|
-
import {
|
|
54
|
-
createTreeseedManagedToolEnv,
|
|
55
|
-
resolveTreeseedToolBinary,
|
|
56
|
-
resolveTreeseedToolCommand
|
|
57
|
-
} from "../../managed-dependencies.js";
|
|
58
|
-
import { resolveTreeseedGitHubToken, withTreeseedServiceCredentialEnv } from "../../service-credentials.js";
|
|
59
|
-
import {
|
|
60
|
-
filterManagedHostGitHubEnvironment,
|
|
61
|
-
usesManagedHostOperationRequests
|
|
62
|
-
} from "./managed-host-security.js";
|
|
63
|
-
import {
|
|
64
|
-
assertTreeseedKeyAgentResponse,
|
|
65
|
-
getTreeseedKeyAgentPaths,
|
|
66
|
-
inspectTreeseedKeyAgentDiagnostics,
|
|
67
|
-
readWrappedMachineKeyFile,
|
|
68
|
-
replaceWrappedMachineKey,
|
|
69
|
-
rotateWrappedMachineKeyPassphrase,
|
|
70
|
-
TREESEED_KEY_AGENT_IDLE_TIMEOUT_MS,
|
|
71
|
-
TREESEED_MACHINE_KEY_PASSPHRASE_ENV,
|
|
72
|
-
TreeseedKeyAgentError,
|
|
73
|
-
unwrapMachineKey
|
|
74
|
-
} from "./key-agent.js";
|
|
75
|
-
import { TREESEED_MACHINE_KEY_PASSPHRASE_ENV as TREESEED_MACHINE_KEY_PASSPHRASE_ENV2, TreeseedKeyAgentError as TreeseedKeyAgentError2 } from "./key-agent.js";
|
|
76
|
-
const MACHINE_CONFIG_RELATIVE_PATH = ".treeseed/config/machine.yaml";
|
|
77
|
-
const MACHINE_KEY_HOME_RELATIVE_PATH = ".treeseed/config/machine.key";
|
|
78
|
-
const LEGACY_MACHINE_KEY_RELATIVE_PATH = ".treeseed/config/machine.key";
|
|
79
|
-
const REMOTE_AUTH_RELATIVE_PATH = ".treeseed/config/remote-auth.json";
|
|
80
|
-
const WORKTREE_METADATA_RELATIVE_PATH = ".treeseed/worktree.json";
|
|
81
|
-
const TEMPLATE_CATALOG_CACHE_RELATIVE_PATH = "treeseed/cache/template-catalog.json";
|
|
82
|
-
const TENANT_ENVIRONMENT_OVERLAY_PATH = "src/env.yaml";
|
|
83
|
-
const CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER = "replace-with-cloudflare-account-id";
|
|
84
|
-
const TREESEED_KEY_AGENT_AUTOPROMPT_ENV = "TREESEED_KEY_AGENT_AUTOPROMPT";
|
|
85
|
-
const KEY_AGENT_COMMAND_TIMEOUT_MS = 1e4;
|
|
86
|
-
const DEFAULT_TREESEED_API_BASE_URL = "https://api.treeseed.dev";
|
|
87
|
-
const DEFAULT_TEMPLATE_CATALOG_URL = "https://api.treeseed.dev/search/templates";
|
|
88
|
-
const TREESEED_TEMPLATE_CATALOG_URL_ENV = "TREESEED_TEMPLATE_CATALOG_URL";
|
|
89
|
-
const TREESEED_API_BASE_URL_ENV = "TREESEED_API_BASE_URL";
|
|
90
|
-
const CLI_CHECK_TIMEOUT_MS = 5e3;
|
|
91
|
-
const DEPRECATED_LOCAL_ENV_FILES = [".env.local", ".dev.vars"];
|
|
92
|
-
const PROVIDER_CONTROL_ENV_KEYS = [
|
|
93
|
-
"TREESEED_GITHUB_TOKEN",
|
|
94
|
-
"TREESEED_CLOUDFLARE_API_TOKEN",
|
|
95
|
-
"TREESEED_CLOUDFLARE_ACCOUNT_ID",
|
|
96
|
-
"CLOUDFLARE_ZONE_ID",
|
|
97
|
-
"TREESEED_RAILWAY_API_TOKEN",
|
|
98
|
-
"TREESEED_RAILWAY_WORKSPACE"
|
|
99
|
-
];
|
|
100
|
-
const warnedDeprecatedLocalEnvRoots = /* @__PURE__ */ new Set();
|
|
101
|
-
const inlineTreeseedSecretSessions = /* @__PURE__ */ new Map();
|
|
102
|
-
const machineKeyHomeRootsByConfigRoot = /* @__PURE__ */ new Map();
|
|
103
|
-
const railwayConnectionCheckCache = /* @__PURE__ */ new Map();
|
|
104
|
-
function filterEnvironmentValuesByRegistry(values, registry, scope, purpose = "config") {
|
|
105
|
-
const registeredKeys = new Set(registry.entries.filter((entry) => isTreeseedEnvironmentEntryRelevant(entry, registry.context, scope, purpose)).map((entry) => entry.id));
|
|
106
|
-
return Object.fromEntries(
|
|
107
|
-
Object.entries(values).filter(([key]) => registeredKeys.has(key))
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
function inspectTreeseedPassphraseEnvDiagnostic(env = process.env) {
|
|
111
|
-
const configured = typeof env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV] === "string" && env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV].trim().length > 0;
|
|
112
|
-
return {
|
|
113
|
-
envVar: TREESEED_MACHINE_KEY_PASSPHRASE_ENV,
|
|
114
|
-
configured,
|
|
115
|
-
recommendedLaunch: `Export ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV} in a shell and launch \`code .\` from that shell before starting the Codex session.`
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
async function inspectTreeseedKeyAgentTransportDiagnostic() {
|
|
119
|
-
const { socketPath, pidPath } = getTreeseedKeyAgentPaths();
|
|
120
|
-
const diagnostics = await inspectTreeseedKeyAgentDiagnostics(socketPath);
|
|
121
|
-
return {
|
|
122
|
-
socketPath,
|
|
123
|
-
pidPath,
|
|
124
|
-
...diagnostics
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
function createDefaultRemoteHost() {
|
|
128
|
-
return {
|
|
129
|
-
id: "official",
|
|
130
|
-
label: "TreeSeed Official API",
|
|
131
|
-
baseUrl: DEFAULT_TREESEED_API_BASE_URL,
|
|
132
|
-
official: true
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
function createDefaultRemoteSettings() {
|
|
136
|
-
return {
|
|
137
|
-
activeHostId: "official",
|
|
138
|
-
executionMode: "prefer-local",
|
|
139
|
-
hosts: [createDefaultRemoteHost()]
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
function normalizeRemoteSettings(value) {
|
|
143
|
-
const record = value && typeof value === "object" ? value : {};
|
|
144
|
-
const hosts = Array.isArray(record.hosts) ? record.hosts.filter((entry) => entry && typeof entry === "object").map((entry) => ({
|
|
145
|
-
id: typeof entry.id === "string" && entry.id.trim() ? entry.id.trim() : "official",
|
|
146
|
-
label: typeof entry.label === "string" && entry.label.trim() ? entry.label.trim() : void 0,
|
|
147
|
-
baseUrl: typeof entry.baseUrl === "string" && entry.baseUrl.trim() ? entry.baseUrl.trim().replace(/\/$/u, "") : DEFAULT_TREESEED_API_BASE_URL,
|
|
148
|
-
official: entry.official === true
|
|
149
|
-
})) : [createDefaultRemoteHost()];
|
|
150
|
-
return {
|
|
151
|
-
activeHostId: typeof record.activeHostId === "string" && record.activeHostId.trim() ? record.activeHostId.trim() : hosts[0]?.id ?? "official",
|
|
152
|
-
executionMode: record.executionMode === "prefer-remote" || record.executionMode === "remote-only" ? record.executionMode : "prefer-local",
|
|
153
|
-
hosts
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
function createDefaultServiceSettings() {
|
|
157
|
-
return {
|
|
158
|
-
railway: {
|
|
159
|
-
projectId: "",
|
|
160
|
-
projectName: "",
|
|
161
|
-
apiServiceId: "",
|
|
162
|
-
apiServiceName: ""
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
function normalizeServiceSettings(value) {
|
|
167
|
-
const record = value && typeof value === "object" ? value : {};
|
|
168
|
-
const railway = record.railway && typeof record.railway === "object" ? record.railway : {};
|
|
169
|
-
return {
|
|
170
|
-
railway: {
|
|
171
|
-
projectId: typeof railway.projectId === "string" ? railway.projectId : "",
|
|
172
|
-
projectName: typeof railway.projectName === "string" ? railway.projectName : "",
|
|
173
|
-
apiServiceId: typeof railway.apiServiceId === "string" ? railway.apiServiceId : "",
|
|
174
|
-
apiServiceName: typeof railway.apiServiceName === "string" ? railway.apiServiceName : ""
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
function ensureParent(filePath) {
|
|
179
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
180
|
-
}
|
|
181
|
-
function listDeprecatedTreeseedLocalEnvFiles(tenantRoot) {
|
|
182
|
-
return DEPRECATED_LOCAL_ENV_FILES.map((fileName) => resolve(tenantRoot, fileName)).filter((filePath) => existsSync(filePath));
|
|
183
|
-
}
|
|
184
|
-
function warnDeprecatedTreeseedLocalEnvFiles(tenantRoot, write = (line) => console.warn(line)) {
|
|
185
|
-
const existing = listDeprecatedTreeseedLocalEnvFiles(tenantRoot);
|
|
186
|
-
if (existing.length === 0 || warnedDeprecatedLocalEnvRoots.has(tenantRoot)) {
|
|
187
|
-
return existing;
|
|
188
|
-
}
|
|
189
|
-
warnedDeprecatedLocalEnvRoots.add(tenantRoot);
|
|
190
|
-
write(
|
|
191
|
-
`Treeseed ignores deprecated local env files: ${existing.map((filePath) => filePath.replace(`${tenantRoot}/`, "")).join(", ")}. Delete them and rely on .treeseed/config/machine.yaml plus Treeseed-launched commands.`
|
|
192
|
-
);
|
|
193
|
-
return existing;
|
|
194
|
-
}
|
|
195
|
-
function maskValue(value) {
|
|
196
|
-
if (!value) {
|
|
197
|
-
return "(unset)";
|
|
198
|
-
}
|
|
199
|
-
if (value.length <= 8) {
|
|
200
|
-
return "********";
|
|
201
|
-
}
|
|
202
|
-
return `${value.slice(0, 3)}...${value.slice(-3)}`;
|
|
203
|
-
}
|
|
204
|
-
function writeDeploySummary(write, summary) {
|
|
205
|
-
write("Treeseed deployment summary");
|
|
206
|
-
write(` Target: ${summary.target}`);
|
|
207
|
-
write(` Worker: ${summary.workerName}`);
|
|
208
|
-
write(` Site URL: ${summary.siteUrl}`);
|
|
209
|
-
write(` Account ID: ${summary.accountId}`);
|
|
210
|
-
write(` D1: ${summary.siteDataDb.databaseName} (${summary.siteDataDb.databaseId})`);
|
|
211
|
-
write(` KV FORM_GUARD_KV: ${summary.formGuardKv.id}`);
|
|
212
|
-
}
|
|
213
|
-
function syncManagedServiceSettingsFromDeployConfig(tenantRoot) {
|
|
214
|
-
const config = loadTreeseedMachineConfig(tenantRoot);
|
|
215
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
216
|
-
const railway = config.settings.services.railway;
|
|
217
|
-
railway.projectId = deployConfig.services?.api?.railway?.projectId ?? railway.projectId;
|
|
218
|
-
railway.projectName = deployConfig.services?.api?.railway?.projectName ?? railway.projectName;
|
|
219
|
-
railway.apiServiceId = deployConfig.services?.api?.railway?.serviceId ?? railway.apiServiceId;
|
|
220
|
-
railway.apiServiceName = deployConfig.services?.api?.railway?.serviceName ?? railway.apiServiceName;
|
|
221
|
-
const remote = normalizeRemoteSettings(config.settings.remote);
|
|
222
|
-
const defaultHostBaseUrl = process.env[TREESEED_API_BASE_URL_ENV] ?? deployConfig.services?.api?.environments?.prod?.baseUrl ?? deployConfig.services?.api?.publicBaseUrl ?? remote.hosts[0]?.baseUrl ?? DEFAULT_TREESEED_API_BASE_URL;
|
|
223
|
-
const officialHost = remote.hosts.find((entry) => entry.id === "official");
|
|
224
|
-
if (officialHost) {
|
|
225
|
-
officialHost.baseUrl = defaultHostBaseUrl.replace(/\/$/u, "");
|
|
226
|
-
} else {
|
|
227
|
-
remote.hosts.unshift({
|
|
228
|
-
id: "official",
|
|
229
|
-
label: "TreeSeed Official API",
|
|
230
|
-
baseUrl: defaultHostBaseUrl.replace(/\/$/u, ""),
|
|
231
|
-
official: true
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
config.settings.remote = remote;
|
|
235
|
-
writeTreeseedMachineConfig(tenantRoot, config);
|
|
236
|
-
return config;
|
|
237
|
-
}
|
|
238
|
-
function loadTenantDeployConfig(tenantRoot) {
|
|
239
|
-
try {
|
|
240
|
-
return loadCliDeployConfig(tenantRoot);
|
|
241
|
-
} catch (error) {
|
|
242
|
-
const packageJsonPath = resolve(tenantRoot, "package.json");
|
|
243
|
-
if (!existsSync(packageJsonPath)) {
|
|
244
|
-
throw error;
|
|
245
|
-
}
|
|
246
|
-
try {
|
|
247
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
248
|
-
if (packageJson?.name !== "@treeseed/agent") {
|
|
249
|
-
throw error;
|
|
250
|
-
}
|
|
251
|
-
const deployConfig = {
|
|
252
|
-
name: "TreeSeed Agent Capacity Provider",
|
|
253
|
-
slug: "treeseed-agent-capacity-provider",
|
|
254
|
-
siteUrl: "http://127.0.0.1:3100",
|
|
255
|
-
contactEmail: "hello@treeseed.ai",
|
|
256
|
-
hosting: { kind: "self_hosted_project", registration: "none" },
|
|
257
|
-
hub: { mode: "treeseed_hosted" },
|
|
258
|
-
runtime: { mode: "byo_attached", registration: "none" },
|
|
259
|
-
surfaces: {
|
|
260
|
-
web: { enabled: false },
|
|
261
|
-
api: { enabled: true, provider: "local", rootDir: ".", localBaseUrl: "http://127.0.0.1:3100" }
|
|
262
|
-
},
|
|
263
|
-
services: {
|
|
264
|
-
api: { enabled: true, provider: "local", rootDir: "." }
|
|
265
|
-
},
|
|
266
|
-
processing: { mode: "local" },
|
|
267
|
-
providers: {
|
|
268
|
-
agents: {
|
|
269
|
-
execution: "codex",
|
|
270
|
-
mutation: "local_branch",
|
|
271
|
-
repository: "git",
|
|
272
|
-
verification: "local",
|
|
273
|
-
notification: "sdk_message",
|
|
274
|
-
research: "project_graph"
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
plugins: []
|
|
278
|
-
};
|
|
279
|
-
Object.defineProperty(deployConfig, "__tenantRoot", {
|
|
280
|
-
value: tenantRoot,
|
|
281
|
-
enumerable: false
|
|
282
|
-
});
|
|
283
|
-
return deployConfig;
|
|
284
|
-
} catch (fallbackError) {
|
|
285
|
-
if (fallbackError === error) throw error;
|
|
286
|
-
throw fallbackError;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
function loadOptionalTenantManifest(tenantRoot) {
|
|
291
|
-
try {
|
|
292
|
-
return withProcessCwd(tenantRoot, () => loadTreeseedManifest());
|
|
293
|
-
} catch {
|
|
294
|
-
return void 0;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
function findNearestTreeseedMachineConfig(startRoot = process.cwd()) {
|
|
298
|
-
let current = resolve(startRoot);
|
|
299
|
-
while (true) {
|
|
300
|
-
const configPath = resolve(current, MACHINE_CONFIG_RELATIVE_PATH);
|
|
301
|
-
if (existsSync(configPath)) {
|
|
302
|
-
return configPath;
|
|
303
|
-
}
|
|
304
|
-
const parent = resolve(current, "..");
|
|
305
|
-
if (parent === current) {
|
|
306
|
-
break;
|
|
307
|
-
}
|
|
308
|
-
current = parent;
|
|
309
|
-
}
|
|
310
|
-
return null;
|
|
311
|
-
}
|
|
312
|
-
function getTreeseedMachineConfigPaths(tenantRoot) {
|
|
313
|
-
const configRoot = resolveManagedWorktreeMachineConfigRoot(tenantRoot);
|
|
314
|
-
const defaultHomeRoot = process.env.VITEST === "true" ? configRoot : process.env.HOME && process.env.HOME.trim().length > 0 ? process.env.HOME : homedir();
|
|
315
|
-
const homeRoot = machineKeyHomeRootsByConfigRoot.get(configRoot) ?? defaultHomeRoot;
|
|
316
|
-
machineKeyHomeRootsByConfigRoot.set(configRoot, homeRoot);
|
|
317
|
-
return {
|
|
318
|
-
configPath: resolve(configRoot, MACHINE_CONFIG_RELATIVE_PATH),
|
|
319
|
-
authPath: resolve(configRoot, REMOTE_AUTH_RELATIVE_PATH),
|
|
320
|
-
keyPath: resolve(homeRoot, MACHINE_KEY_HOME_RELATIVE_PATH),
|
|
321
|
-
legacyKeyPath: resolve(configRoot, LEGACY_MACHINE_KEY_RELATIVE_PATH)
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
function resolveManagedWorktreeMachineConfigRoot(tenantRoot) {
|
|
325
|
-
if (existsSync(resolve(tenantRoot, MACHINE_CONFIG_RELATIVE_PATH))) {
|
|
326
|
-
return tenantRoot;
|
|
327
|
-
}
|
|
328
|
-
const metadataPath = resolve(tenantRoot, WORKTREE_METADATA_RELATIVE_PATH);
|
|
329
|
-
if (!existsSync(metadataPath)) return tenantRoot;
|
|
330
|
-
try {
|
|
331
|
-
const metadata = JSON.parse(readFileSync(metadataPath, "utf8")) ?? {};
|
|
332
|
-
const primaryRoot = metadata.kind === "treeseed.workflow.worktree" && typeof metadata.primaryRoot === "string" ? metadata.primaryRoot : null;
|
|
333
|
-
return primaryRoot && existsSync(resolve(primaryRoot, MACHINE_CONFIG_RELATIVE_PATH)) ? primaryRoot : tenantRoot;
|
|
334
|
-
} catch {
|
|
335
|
-
return tenantRoot;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
function keyAgentScriptPath() {
|
|
339
|
-
const distScriptPath = resolve(packageDistScriptRoot, "key-agent.js");
|
|
340
|
-
return existsSync(distScriptPath) ? distScriptPath : packageScriptPath("key-agent.ts");
|
|
341
|
-
}
|
|
342
|
-
function keyAgentNodeArgs() {
|
|
343
|
-
const scriptPath = keyAgentScriptPath();
|
|
344
|
-
return scriptPath.endsWith(".ts") ? ["--import", "tsx", scriptPath] : [scriptPath];
|
|
345
|
-
}
|
|
346
|
-
function keyAgentScriptCwd() {
|
|
347
|
-
return dirname(dirname(keyAgentScriptPath()));
|
|
348
|
-
}
|
|
349
|
-
function sleepMs(milliseconds) {
|
|
350
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
351
|
-
}
|
|
352
|
-
function shellQuote(value) {
|
|
353
|
-
return `'${value.replace(/'/gu, `'\\''`)}'`;
|
|
354
|
-
}
|
|
355
|
-
function keyAgentAutoPromptEnabled() {
|
|
356
|
-
const value = String(process.env[TREESEED_KEY_AGENT_AUTOPROMPT_ENV] ?? "").trim().toLowerCase();
|
|
357
|
-
if (value === "0" || value === "false" || value === "off") {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
return process.stdin.isTTY && process.stdout.isTTY;
|
|
361
|
-
}
|
|
362
|
-
function useInlineKeyAgentTransport() {
|
|
363
|
-
return process.env.VITEST === "true" || process.env.TREESEED_KEY_AGENT_TRANSPORT === "inline";
|
|
364
|
-
}
|
|
365
|
-
function withTreeseedKeyAgentAutopromptDisabled(action) {
|
|
366
|
-
const previous = process.env[TREESEED_KEY_AGENT_AUTOPROMPT_ENV];
|
|
367
|
-
process.env[TREESEED_KEY_AGENT_AUTOPROMPT_ENV] = "0";
|
|
368
|
-
try {
|
|
369
|
-
return action();
|
|
370
|
-
} finally {
|
|
371
|
-
if (previous === void 0) {
|
|
372
|
-
delete process.env[TREESEED_KEY_AGENT_AUTOPROMPT_ENV];
|
|
373
|
-
} else {
|
|
374
|
-
process.env[TREESEED_KEY_AGENT_AUTOPROMPT_ENV] = previous;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
function startTreeseedKeyAgentDaemon(tenantRoot) {
|
|
379
|
-
const { keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
380
|
-
const { socketPath } = getTreeseedKeyAgentPaths();
|
|
381
|
-
const scriptArgs = keyAgentNodeArgs();
|
|
382
|
-
const command = [
|
|
383
|
-
shellQuote(process.execPath),
|
|
384
|
-
...scriptArgs.map((arg) => shellQuote(arg)),
|
|
385
|
-
"serve",
|
|
386
|
-
"--key-path",
|
|
387
|
-
shellQuote(keyPath),
|
|
388
|
-
"--socket-path",
|
|
389
|
-
shellQuote(socketPath),
|
|
390
|
-
"--idle-timeout-ms",
|
|
391
|
-
shellQuote(String(TREESEED_KEY_AGENT_IDLE_TIMEOUT_MS)),
|
|
392
|
-
">/dev/null",
|
|
393
|
-
"2>/dev/null",
|
|
394
|
-
"&"
|
|
395
|
-
].join(" ");
|
|
396
|
-
const child = spawn("bash", ["-lc", command], {
|
|
397
|
-
cwd: keyAgentScriptCwd(),
|
|
398
|
-
stdio: "ignore"
|
|
399
|
-
});
|
|
400
|
-
child.unref();
|
|
401
|
-
}
|
|
402
|
-
function runTreeseedKeyAgentCommand(args, options = {}) {
|
|
403
|
-
const result = spawnSync(process.execPath, [
|
|
404
|
-
...keyAgentNodeArgs(),
|
|
405
|
-
...args
|
|
406
|
-
], {
|
|
407
|
-
cwd: keyAgentScriptCwd(),
|
|
408
|
-
encoding: "utf8",
|
|
409
|
-
env: {
|
|
410
|
-
...process.env,
|
|
411
|
-
...options.env ?? {}
|
|
412
|
-
},
|
|
413
|
-
stdio: options.input !== void 0 ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"],
|
|
414
|
-
input: options.input,
|
|
415
|
-
timeout: KEY_AGENT_COMMAND_TIMEOUT_MS,
|
|
416
|
-
killSignal: "SIGTERM"
|
|
417
|
-
});
|
|
418
|
-
if (result.error) {
|
|
419
|
-
const timedOut = result.error.code === "ETIMEDOUT";
|
|
420
|
-
return {
|
|
421
|
-
ok: false,
|
|
422
|
-
code: "daemon_unavailable",
|
|
423
|
-
message: timedOut ? `Treeseed key-agent command timed out after ${KEY_AGENT_COMMAND_TIMEOUT_MS}ms.` : result.error.message || "Treeseed key-agent command failed."
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
if (result.status !== 0 && (!result.stdout || result.stdout.trim().length === 0)) {
|
|
427
|
-
return {
|
|
428
|
-
ok: false,
|
|
429
|
-
code: "daemon_unavailable",
|
|
430
|
-
message: result.stderr?.trim() || "Treeseed key-agent command failed."
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
try {
|
|
434
|
-
return JSON.parse(result.stdout.trim() || "{}");
|
|
435
|
-
} catch {
|
|
436
|
-
throw new TreeseedKeyAgentError(
|
|
437
|
-
"daemon_unavailable",
|
|
438
|
-
result.stderr?.trim() || "Treeseed key-agent command returned an invalid response."
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
function requestTreeseedKeyAgent(tenantRoot, payload, { ensureRunning = false, env } = {}) {
|
|
443
|
-
const invoke = () => runTreeseedKeyAgentCommand([
|
|
444
|
-
"request",
|
|
445
|
-
JSON.stringify(payload)
|
|
446
|
-
], { env });
|
|
447
|
-
let response = invoke();
|
|
448
|
-
if (response.code !== "daemon_unavailable" || !ensureRunning) {
|
|
449
|
-
return response;
|
|
450
|
-
}
|
|
451
|
-
startTreeseedKeyAgentDaemon(tenantRoot);
|
|
452
|
-
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
453
|
-
response = invoke();
|
|
454
|
-
if (response.code !== "daemon_unavailable") {
|
|
455
|
-
return response;
|
|
456
|
-
}
|
|
457
|
-
sleepMs(25);
|
|
458
|
-
}
|
|
459
|
-
return response;
|
|
460
|
-
}
|
|
461
|
-
function inspectTreeseedKeyAgentStatus(tenantRoot) {
|
|
462
|
-
const { keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
463
|
-
const { socketPath } = getTreeseedKeyAgentPaths();
|
|
464
|
-
const wrapped = readWrappedMachineKeyFile(keyPath);
|
|
465
|
-
if (useInlineKeyAgentTransport()) {
|
|
466
|
-
const session = inlineTreeseedSecretSessions.get(keyPath) ?? { machineKey: null, lastTouchedAt: 0, idleTimeoutMs: TREESEED_KEY_AGENT_IDLE_TIMEOUT_MS };
|
|
467
|
-
const idleRemainingMs = session.machineKey ? Math.max(0, session.idleTimeoutMs - (Date.now() - session.lastTouchedAt)) : 0;
|
|
468
|
-
if (idleRemainingMs === 0) {
|
|
469
|
-
session.machineKey = null;
|
|
470
|
-
}
|
|
471
|
-
inlineTreeseedSecretSessions.set(keyPath, session);
|
|
472
|
-
return {
|
|
473
|
-
running: true,
|
|
474
|
-
unlocked: Boolean(session.machineKey) && idleRemainingMs > 0,
|
|
475
|
-
wrappedKeyPresent: wrapped.exists && Boolean(wrapped.wrapped),
|
|
476
|
-
migrationRequired: wrapped.migrationRequired,
|
|
477
|
-
keyPath,
|
|
478
|
-
socketPath,
|
|
479
|
-
idleTimeoutMs: session.idleTimeoutMs,
|
|
480
|
-
idleRemainingMs
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
const response = requestTreeseedKeyAgent(tenantRoot, {
|
|
484
|
-
command: "status",
|
|
485
|
-
keyPath,
|
|
486
|
-
socketPath,
|
|
487
|
-
idleTimeoutMs: TREESEED_KEY_AGENT_IDLE_TIMEOUT_MS
|
|
488
|
-
});
|
|
489
|
-
if (response.ok && response.status) {
|
|
490
|
-
return response.status;
|
|
491
|
-
}
|
|
492
|
-
return {
|
|
493
|
-
running: false,
|
|
494
|
-
unlocked: false,
|
|
495
|
-
wrappedKeyPresent: wrapped.exists && Boolean(wrapped.wrapped),
|
|
496
|
-
migrationRequired: wrapped.migrationRequired,
|
|
497
|
-
keyPath,
|
|
498
|
-
socketPath,
|
|
499
|
-
idleTimeoutMs: TREESEED_KEY_AGENT_IDLE_TIMEOUT_MS,
|
|
500
|
-
idleRemainingMs: 0
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
function unlockTreeseedSecretSessionInteractive(tenantRoot) {
|
|
504
|
-
if (useInlineKeyAgentTransport()) {
|
|
505
|
-
throw new TreeseedKeyAgentError("interactive_required", "Inline test transport does not support interactive unlock.");
|
|
506
|
-
}
|
|
507
|
-
const { keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
508
|
-
const { socketPath } = getTreeseedKeyAgentPaths();
|
|
509
|
-
startTreeseedKeyAgentDaemon(tenantRoot);
|
|
510
|
-
let response = { ok: false, code: "daemon_unavailable", message: "Treeseed key agent is unavailable." };
|
|
511
|
-
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
512
|
-
response = runTreeseedKeyAgentCommand([
|
|
513
|
-
"unlock-interactive",
|
|
514
|
-
"--key-path",
|
|
515
|
-
keyPath,
|
|
516
|
-
"--socket-path",
|
|
517
|
-
socketPath,
|
|
518
|
-
"--idle-timeout-ms",
|
|
519
|
-
String(TREESEED_KEY_AGENT_IDLE_TIMEOUT_MS),
|
|
520
|
-
"--allow-migration",
|
|
521
|
-
"--create-if-missing"
|
|
522
|
-
]);
|
|
523
|
-
if (response.code !== "daemon_unavailable") {
|
|
524
|
-
break;
|
|
525
|
-
}
|
|
526
|
-
sleepMs(25);
|
|
527
|
-
}
|
|
528
|
-
assertTreeseedKeyAgentResponse(response, "Unable to unlock the Treeseed secret session.");
|
|
529
|
-
return response.status;
|
|
530
|
-
}
|
|
531
|
-
function unlockTreeseedSecretSessionFromEnv(tenantRoot, options = {}) {
|
|
532
|
-
const { keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
533
|
-
const { socketPath } = getTreeseedKeyAgentPaths();
|
|
534
|
-
if (useInlineKeyAgentTransport()) {
|
|
535
|
-
const passphrase = String(process.env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV] ?? "").trim();
|
|
536
|
-
if (!passphrase) {
|
|
537
|
-
throw new TreeseedKeyAgentError(
|
|
538
|
-
"interactive_required",
|
|
539
|
-
`Set ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV} before unlocking the Treeseed secret session.`
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
const wrapped = readWrappedMachineKeyFile(keyPath);
|
|
543
|
-
const machineKey = wrapped.wrapped ? (() => {
|
|
544
|
-
try {
|
|
545
|
-
return unwrapMachineKey(wrapped.wrapped, passphrase);
|
|
546
|
-
} catch (error) {
|
|
547
|
-
if (process.env.VITEST === "true" && options.createIfMissing !== false) {
|
|
548
|
-
const createdKey = randomBytes(32);
|
|
549
|
-
replaceWrappedMachineKey(keyPath, createdKey, passphrase);
|
|
550
|
-
return createdKey;
|
|
551
|
-
}
|
|
552
|
-
throw error;
|
|
553
|
-
}
|
|
554
|
-
})() : wrapped.plaintextLegacy ? (() => {
|
|
555
|
-
if (options.allowMigration === false) {
|
|
556
|
-
throw new TreeseedKeyAgentError("wrapped_key_migration_required", "Wrap the legacy machine key before unlocking it.");
|
|
557
|
-
}
|
|
558
|
-
replaceWrappedMachineKey(keyPath, wrapped.plaintextLegacy, passphrase);
|
|
559
|
-
return wrapped.plaintextLegacy;
|
|
560
|
-
})() : (() => {
|
|
561
|
-
if (options.createIfMissing === false) {
|
|
562
|
-
throw new TreeseedKeyAgentError("wrapped_key_missing", "No wrapped Treeseed machine key exists yet.");
|
|
563
|
-
}
|
|
564
|
-
const createdKey = randomBytes(32);
|
|
565
|
-
replaceWrappedMachineKey(keyPath, createdKey, passphrase);
|
|
566
|
-
return createdKey;
|
|
567
|
-
})();
|
|
568
|
-
inlineTreeseedSecretSessions.set(keyPath, {
|
|
569
|
-
machineKey,
|
|
570
|
-
lastTouchedAt: Date.now(),
|
|
571
|
-
idleTimeoutMs: TREESEED_KEY_AGENT_IDLE_TIMEOUT_MS
|
|
572
|
-
});
|
|
573
|
-
return inspectTreeseedKeyAgentStatus(tenantRoot);
|
|
574
|
-
}
|
|
575
|
-
startTreeseedKeyAgentDaemon(tenantRoot);
|
|
576
|
-
let response = { ok: false, code: "daemon_unavailable", message: "Treeseed key agent is unavailable." };
|
|
577
|
-
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
578
|
-
try {
|
|
579
|
-
const parsed = runTreeseedKeyAgentCommand([
|
|
580
|
-
"unlock-from-env",
|
|
581
|
-
"--key-path",
|
|
582
|
-
keyPath,
|
|
583
|
-
"--socket-path",
|
|
584
|
-
socketPath,
|
|
585
|
-
"--idle-timeout-ms",
|
|
586
|
-
String(TREESEED_KEY_AGENT_IDLE_TIMEOUT_MS),
|
|
587
|
-
...options.allowMigration === false ? [] : ["--allow-migration"],
|
|
588
|
-
...options.createIfMissing === false ? [] : ["--create-if-missing"]
|
|
589
|
-
]);
|
|
590
|
-
assertTreeseedKeyAgentResponse(parsed, `Unable to unlock the Treeseed secret session from ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV}.`);
|
|
591
|
-
return parsed.status;
|
|
592
|
-
} catch (error) {
|
|
593
|
-
if (attempt === 19) {
|
|
594
|
-
throw error;
|
|
595
|
-
}
|
|
596
|
-
sleepMs(25);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
assertTreeseedKeyAgentResponse(
|
|
600
|
-
response,
|
|
601
|
-
`Unable to unlock the Treeseed secret session from ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV}.`
|
|
602
|
-
);
|
|
603
|
-
return response.status;
|
|
604
|
-
}
|
|
605
|
-
function unlockTreeseedSecretSessionWithPassphrase(tenantRoot, passphrase, options = {}) {
|
|
606
|
-
const normalizedPassphrase = String(passphrase ?? "").trim();
|
|
607
|
-
if (!normalizedPassphrase) {
|
|
608
|
-
throw new TreeseedKeyAgentError(
|
|
609
|
-
"interactive_required",
|
|
610
|
-
`Set ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV} before unlocking the Treeseed secret session.`
|
|
611
|
-
);
|
|
612
|
-
}
|
|
613
|
-
const previousPassphrase = process.env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV];
|
|
614
|
-
process.env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV] = normalizedPassphrase;
|
|
615
|
-
try {
|
|
616
|
-
if (useInlineKeyAgentTransport()) {
|
|
617
|
-
return unlockTreeseedSecretSessionFromEnv(tenantRoot, options);
|
|
618
|
-
}
|
|
619
|
-
const { keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
620
|
-
const { socketPath } = getTreeseedKeyAgentPaths();
|
|
621
|
-
startTreeseedKeyAgentDaemon(tenantRoot);
|
|
622
|
-
let response = { ok: false, code: "daemon_unavailable", message: "Treeseed key agent is unavailable." };
|
|
623
|
-
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
624
|
-
response = runTreeseedKeyAgentCommand([
|
|
625
|
-
"unlock-from-env",
|
|
626
|
-
"--key-path",
|
|
627
|
-
keyPath,
|
|
628
|
-
"--socket-path",
|
|
629
|
-
socketPath,
|
|
630
|
-
"--idle-timeout-ms",
|
|
631
|
-
String(TREESEED_KEY_AGENT_IDLE_TIMEOUT_MS),
|
|
632
|
-
...options.allowMigration === false ? [] : ["--allow-migration"],
|
|
633
|
-
...options.createIfMissing === false ? [] : ["--create-if-missing"]
|
|
634
|
-
], {
|
|
635
|
-
env: {
|
|
636
|
-
[TREESEED_MACHINE_KEY_PASSPHRASE_ENV]: normalizedPassphrase
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
if (response.code !== "daemon_unavailable") {
|
|
640
|
-
break;
|
|
641
|
-
}
|
|
642
|
-
sleepMs(25);
|
|
643
|
-
}
|
|
644
|
-
assertTreeseedKeyAgentResponse(
|
|
645
|
-
response,
|
|
646
|
-
`Unable to unlock the Treeseed secret session from ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV}.`
|
|
647
|
-
);
|
|
648
|
-
return response.status;
|
|
649
|
-
} finally {
|
|
650
|
-
if (previousPassphrase === void 0) {
|
|
651
|
-
delete process.env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV];
|
|
652
|
-
} else {
|
|
653
|
-
process.env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV] = previousPassphrase;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
async function ensureTreeseedSecretSessionForConfig({
|
|
658
|
-
tenantRoot,
|
|
659
|
-
interactive = false,
|
|
660
|
-
env = process.env,
|
|
661
|
-
createIfMissing = true,
|
|
662
|
-
allowMigration = true,
|
|
663
|
-
promptForPassphrase,
|
|
664
|
-
promptForNewPassphrase
|
|
665
|
-
}) {
|
|
666
|
-
const status = inspectTreeseedKeyAgentStatus(tenantRoot);
|
|
667
|
-
if (status.unlocked) {
|
|
668
|
-
return {
|
|
669
|
-
status,
|
|
670
|
-
createdWrappedKey: false,
|
|
671
|
-
migratedWrappedKey: false,
|
|
672
|
-
unlockSource: "existing-session"
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
|
-
const wrappedBefore = readWrappedMachineKeyFile(status.keyPath);
|
|
676
|
-
const envPassphrase = String(env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV] ?? "").trim();
|
|
677
|
-
let unlockSource = "existing-session";
|
|
678
|
-
let nextStatus;
|
|
679
|
-
if (envPassphrase) {
|
|
680
|
-
nextStatus = unlockTreeseedSecretSessionWithPassphrase(tenantRoot, envPassphrase, {
|
|
681
|
-
createIfMissing,
|
|
682
|
-
allowMigration
|
|
683
|
-
});
|
|
684
|
-
unlockSource = "env";
|
|
685
|
-
} else if (interactive && status.migrationRequired) {
|
|
686
|
-
if (!promptForNewPassphrase) {
|
|
687
|
-
throw new TreeseedKeyAgentError("interactive_required", "A passphrase prompt is required to migrate the Treeseed machine key.");
|
|
688
|
-
}
|
|
689
|
-
nextStatus = unlockTreeseedSecretSessionWithPassphrase(tenantRoot, await promptForNewPassphrase(), {
|
|
690
|
-
createIfMissing: false,
|
|
691
|
-
allowMigration: true
|
|
692
|
-
});
|
|
693
|
-
unlockSource = "interactive";
|
|
694
|
-
} else if (interactive && !status.wrappedKeyPresent) {
|
|
695
|
-
if (!promptForNewPassphrase) {
|
|
696
|
-
throw new TreeseedKeyAgentError("interactive_required", "A passphrase prompt is required to create the Treeseed machine key.");
|
|
697
|
-
}
|
|
698
|
-
nextStatus = unlockTreeseedSecretSessionWithPassphrase(tenantRoot, await promptForNewPassphrase(), {
|
|
699
|
-
createIfMissing: true,
|
|
700
|
-
allowMigration: false
|
|
701
|
-
});
|
|
702
|
-
unlockSource = "interactive";
|
|
703
|
-
} else if (interactive) {
|
|
704
|
-
if (!promptForPassphrase) {
|
|
705
|
-
throw new TreeseedKeyAgentError("interactive_required", "A passphrase prompt is required to unlock the Treeseed machine key.");
|
|
706
|
-
}
|
|
707
|
-
nextStatus = unlockTreeseedSecretSessionWithPassphrase(tenantRoot, await promptForPassphrase(), {
|
|
708
|
-
createIfMissing: false,
|
|
709
|
-
allowMigration: false
|
|
710
|
-
});
|
|
711
|
-
unlockSource = "interactive";
|
|
712
|
-
} else if (status.migrationRequired) {
|
|
713
|
-
throw new TreeseedKeyAgentError(
|
|
714
|
-
"wrapped_key_migration_required",
|
|
715
|
-
`Set ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV} before running treeseed config non-interactively so Treeseed can wrap the legacy machine key.`,
|
|
716
|
-
{ keyPath: status.keyPath }
|
|
717
|
-
);
|
|
718
|
-
} else if (!status.wrappedKeyPresent) {
|
|
719
|
-
throw new TreeseedKeyAgentError(
|
|
720
|
-
"wrapped_key_missing",
|
|
721
|
-
`Set ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV} before running treeseed config non-interactively so Treeseed can create the wrapped machine key.`,
|
|
722
|
-
{ keyPath: status.keyPath }
|
|
723
|
-
);
|
|
724
|
-
} else {
|
|
725
|
-
throw new TreeseedKeyAgentError(
|
|
726
|
-
"locked",
|
|
727
|
-
`Set ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV} before running treeseed config non-interactively so Treeseed can unlock the wrapped machine key.`,
|
|
728
|
-
{ keyPath: status.keyPath }
|
|
729
|
-
);
|
|
730
|
-
}
|
|
731
|
-
const wrappedAfter = readWrappedMachineKeyFile(status.keyPath);
|
|
732
|
-
return {
|
|
733
|
-
status: nextStatus,
|
|
734
|
-
createdWrappedKey: !wrappedBefore.wrapped && Boolean(wrappedAfter.wrapped) && !wrappedBefore.migrationRequired,
|
|
735
|
-
migratedWrappedKey: wrappedBefore.migrationRequired && Boolean(wrappedAfter.wrapped),
|
|
736
|
-
unlockSource
|
|
737
|
-
};
|
|
738
|
-
}
|
|
739
|
-
function lockTreeseedSecretSession(tenantRoot) {
|
|
740
|
-
const { keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
741
|
-
if (useInlineKeyAgentTransport()) {
|
|
742
|
-
inlineTreeseedSecretSessions.set(keyPath, {
|
|
743
|
-
machineKey: null,
|
|
744
|
-
lastTouchedAt: 0,
|
|
745
|
-
idleTimeoutMs: TREESEED_KEY_AGENT_IDLE_TIMEOUT_MS
|
|
746
|
-
});
|
|
747
|
-
return inspectTreeseedKeyAgentStatus(tenantRoot);
|
|
748
|
-
}
|
|
749
|
-
const status = inspectTreeseedKeyAgentStatus(tenantRoot);
|
|
750
|
-
if (!status.running) {
|
|
751
|
-
return status;
|
|
752
|
-
}
|
|
753
|
-
const response = requestTreeseedKeyAgent(tenantRoot, {
|
|
754
|
-
command: "lock",
|
|
755
|
-
keyPath: status.keyPath,
|
|
756
|
-
socketPath: status.socketPath,
|
|
757
|
-
idleTimeoutMs: status.idleTimeoutMs
|
|
758
|
-
});
|
|
759
|
-
assertTreeseedKeyAgentResponse(response, "Unable to lock the Treeseed secret session.");
|
|
760
|
-
return response.status;
|
|
761
|
-
}
|
|
762
|
-
function resolveUnlockedMachineKey(tenantRoot) {
|
|
763
|
-
const status = inspectTreeseedKeyAgentStatus(tenantRoot);
|
|
764
|
-
if (!status.unlocked) {
|
|
765
|
-
const envPassphrase = String(process.env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV] ?? "").trim();
|
|
766
|
-
if (envPassphrase) {
|
|
767
|
-
unlockTreeseedSecretSessionFromEnv(tenantRoot);
|
|
768
|
-
} else if (keyAgentAutoPromptEnabled()) {
|
|
769
|
-
unlockTreeseedSecretSessionInteractive(tenantRoot);
|
|
770
|
-
} else if (status.migrationRequired) {
|
|
771
|
-
throw new TreeseedKeyAgentError(
|
|
772
|
-
"wrapped_key_migration_required",
|
|
773
|
-
"The Treeseed machine key is still stored in the legacy plaintext format. Run `treeseed secrets:migrate-key` or unlock it from an interactive session first.",
|
|
774
|
-
{ keyPath: status.keyPath }
|
|
775
|
-
);
|
|
776
|
-
} else if (!status.wrappedKeyPresent) {
|
|
777
|
-
throw new TreeseedKeyAgentError(
|
|
778
|
-
"wrapped_key_missing",
|
|
779
|
-
`No wrapped Treeseed machine key exists yet. Run \`treeseed config\` or \`treeseed secrets:unlock\` from an interactive shell, or set ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV} for the startup unlock path.`,
|
|
780
|
-
{ keyPath: status.keyPath }
|
|
781
|
-
);
|
|
782
|
-
} else {
|
|
783
|
-
throw new TreeseedKeyAgentError(
|
|
784
|
-
"locked",
|
|
785
|
-
`Treeseed secrets are locked. Run \`treeseed secrets:unlock\`, unlock from an interactive session, or set ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV} for the startup unlock path before using secret-backed commands.`,
|
|
786
|
-
{ keyPath: status.keyPath }
|
|
787
|
-
);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
if (useInlineKeyAgentTransport()) {
|
|
791
|
-
const session = inlineTreeseedSecretSessions.get(status.keyPath);
|
|
792
|
-
if (!session?.machineKey) {
|
|
793
|
-
throw new TreeseedKeyAgentError("locked", "Treeseed secrets are locked.");
|
|
794
|
-
}
|
|
795
|
-
session.lastTouchedAt = Date.now();
|
|
796
|
-
return session.machineKey;
|
|
797
|
-
}
|
|
798
|
-
const response = requestTreeseedKeyAgent(tenantRoot, {
|
|
799
|
-
command: "get-machine-key",
|
|
800
|
-
keyPath: status.keyPath,
|
|
801
|
-
socketPath: status.socketPath,
|
|
802
|
-
idleTimeoutMs: status.idleTimeoutMs
|
|
803
|
-
});
|
|
804
|
-
assertTreeseedKeyAgentResponse(response, "Unable to resolve the Treeseed machine key from the local key agent.");
|
|
805
|
-
return Buffer.from(String(response.machineKey ?? ""), "base64");
|
|
806
|
-
}
|
|
807
|
-
function getTreeseedRemoteAuthPaths(tenantRoot) {
|
|
808
|
-
return {
|
|
809
|
-
authPath: getTreeseedMachineConfigPaths(tenantRoot).authPath
|
|
810
|
-
};
|
|
811
|
-
}
|
|
812
|
-
function createDefaultTreeseedMachineConfig({ tenantRoot, deployConfig, tenantConfig }) {
|
|
813
|
-
return {
|
|
814
|
-
version: 1,
|
|
815
|
-
project: {
|
|
816
|
-
tenantRoot,
|
|
817
|
-
tenantId: tenantConfig?.id ?? deployConfig.slug,
|
|
818
|
-
slug: deployConfig.slug,
|
|
819
|
-
name: deployConfig.name,
|
|
820
|
-
siteUrl: deployConfig.siteUrl,
|
|
821
|
-
overlayPath: resolve(tenantRoot, TENANT_ENVIRONMENT_OVERLAY_PATH)
|
|
822
|
-
},
|
|
823
|
-
settings: {
|
|
824
|
-
sync: {
|
|
825
|
-
github: true,
|
|
826
|
-
cloudflare: true
|
|
827
|
-
},
|
|
828
|
-
templates: {
|
|
829
|
-
catalogEndpoint: DEFAULT_TEMPLATE_CATALOG_URL
|
|
830
|
-
},
|
|
831
|
-
remote: createDefaultRemoteSettings(),
|
|
832
|
-
services: createDefaultServiceSettings()
|
|
833
|
-
},
|
|
834
|
-
shared: {
|
|
835
|
-
values: {},
|
|
836
|
-
secrets: {}
|
|
837
|
-
},
|
|
838
|
-
environments: Object.fromEntries(
|
|
839
|
-
TREESEED_ENVIRONMENT_SCOPES.map((scope) => [
|
|
840
|
-
scope,
|
|
841
|
-
{
|
|
842
|
-
values: {},
|
|
843
|
-
secrets: {}
|
|
844
|
-
}
|
|
845
|
-
])
|
|
846
|
-
)
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
function loadLegacyMachineKey(tenantRoot) {
|
|
850
|
-
const { legacyKeyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
851
|
-
if (!existsSync(legacyKeyPath)) {
|
|
852
|
-
return null;
|
|
853
|
-
}
|
|
854
|
-
try {
|
|
855
|
-
const raw = readFileSync(legacyKeyPath, "utf8").trim();
|
|
856
|
-
if (!raw || raw.startsWith("{")) {
|
|
857
|
-
return null;
|
|
858
|
-
}
|
|
859
|
-
const key = Buffer.from(raw, "base64");
|
|
860
|
-
return key.length === 32 ? key : null;
|
|
861
|
-
} catch {
|
|
862
|
-
return null;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
function createDefaultRemoteAuthState() {
|
|
866
|
-
return {
|
|
867
|
-
version: 1,
|
|
868
|
-
sessions: {}
|
|
869
|
-
};
|
|
870
|
-
}
|
|
871
|
-
function encryptValue(value, key) {
|
|
872
|
-
const iv = randomBytes(12);
|
|
873
|
-
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
874
|
-
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
875
|
-
const tag = cipher.getAuthTag();
|
|
876
|
-
return {
|
|
877
|
-
algorithm: "aes-256-gcm",
|
|
878
|
-
iv: iv.toString("base64"),
|
|
879
|
-
tag: tag.toString("base64"),
|
|
880
|
-
ciphertext: ciphertext.toString("base64")
|
|
881
|
-
};
|
|
882
|
-
}
|
|
883
|
-
function decryptValue(payload, key) {
|
|
884
|
-
if (!payload || typeof payload !== "object") {
|
|
885
|
-
return "";
|
|
886
|
-
}
|
|
887
|
-
if (!Buffer.isBuffer(key) || key.length !== 32) {
|
|
888
|
-
throw new TreeseedKeyAgentError(
|
|
889
|
-
"invalid_machine_key",
|
|
890
|
-
"The Treeseed machine key is invalid or corrupt."
|
|
891
|
-
);
|
|
892
|
-
}
|
|
893
|
-
const decipher = createDecipheriv(
|
|
894
|
-
"aes-256-gcm",
|
|
895
|
-
key,
|
|
896
|
-
Buffer.from(String(payload.iv ?? ""), "base64")
|
|
897
|
-
);
|
|
898
|
-
decipher.setAuthTag(Buffer.from(String(payload.tag ?? ""), "base64"));
|
|
899
|
-
const decrypted = Buffer.concat([
|
|
900
|
-
decipher.update(Buffer.from(String(payload.ciphertext ?? ""), "base64")),
|
|
901
|
-
decipher.final()
|
|
902
|
-
]);
|
|
903
|
-
return decrypted.toString("utf8");
|
|
904
|
-
}
|
|
905
|
-
function decryptMachineConfigSecrets(config, key) {
|
|
906
|
-
const secrets = {
|
|
907
|
-
shared: {}
|
|
908
|
-
};
|
|
909
|
-
for (const [entryId, payload] of Object.entries(config.shared?.secrets ?? {})) {
|
|
910
|
-
secrets.shared[entryId] = decryptValue(payload, key);
|
|
911
|
-
}
|
|
912
|
-
for (const scope of TREESEED_ENVIRONMENT_SCOPES) {
|
|
913
|
-
secrets[scope] = {};
|
|
914
|
-
for (const [entryId, payload] of Object.entries(config.environments?.[scope]?.secrets ?? {})) {
|
|
915
|
-
secrets[scope][entryId] = decryptValue(payload, key);
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
return secrets;
|
|
919
|
-
}
|
|
920
|
-
function applyMachineConfigSecrets(config, secrets, key) {
|
|
921
|
-
for (const [entryId, value] of Object.entries(secrets.shared ?? {})) {
|
|
922
|
-
config.shared.secrets[entryId] = encryptValue(value, key);
|
|
923
|
-
}
|
|
924
|
-
for (const scope of TREESEED_ENVIRONMENT_SCOPES) {
|
|
925
|
-
const scoped = config.environments?.[scope];
|
|
926
|
-
if (!scoped) {
|
|
927
|
-
continue;
|
|
928
|
-
}
|
|
929
|
-
for (const [entryId, value] of Object.entries(secrets[scope] ?? {})) {
|
|
930
|
-
scoped.secrets[entryId] = encryptValue(value, key);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
return config;
|
|
934
|
-
}
|
|
935
|
-
function decryptRemoteAuthSessions(payload, key) {
|
|
936
|
-
return Object.fromEntries(
|
|
937
|
-
Object.entries(payload.sessions ?? {}).map(([hostId, entry]) => [
|
|
938
|
-
hostId,
|
|
939
|
-
{
|
|
940
|
-
accessToken: decryptValue(entry.accessToken, key),
|
|
941
|
-
refreshToken: decryptValue(entry.refreshToken, key),
|
|
942
|
-
expiresAt: typeof entry.expiresAt === "string" ? entry.expiresAt : "",
|
|
943
|
-
principal: entry.principal ?? null
|
|
944
|
-
}
|
|
945
|
-
])
|
|
946
|
-
);
|
|
947
|
-
}
|
|
948
|
-
function encryptRemoteAuthSessions(sessions, key) {
|
|
949
|
-
return Object.fromEntries(
|
|
950
|
-
Object.entries(sessions ?? {}).map(([hostId, entry]) => [
|
|
951
|
-
hostId,
|
|
952
|
-
{
|
|
953
|
-
accessToken: entry.accessToken ? encryptValue(entry.accessToken, key) : null,
|
|
954
|
-
refreshToken: entry.refreshToken ? encryptValue(entry.refreshToken, key) : null,
|
|
955
|
-
expiresAt: entry.expiresAt ?? "",
|
|
956
|
-
principal: entry.principal ?? null
|
|
957
|
-
}
|
|
958
|
-
])
|
|
959
|
-
);
|
|
960
|
-
}
|
|
961
|
-
function loadRemoteAuthPayload(tenantRoot) {
|
|
962
|
-
const { authPath } = getTreeseedRemoteAuthPaths(tenantRoot);
|
|
963
|
-
if (!existsSync(authPath)) {
|
|
964
|
-
return createDefaultRemoteAuthState();
|
|
965
|
-
}
|
|
966
|
-
try {
|
|
967
|
-
const raw = JSON.parse(readFileSync(authPath, "utf8"));
|
|
968
|
-
return raw && typeof raw === "object" ? raw : createDefaultRemoteAuthState();
|
|
969
|
-
} catch {
|
|
970
|
-
return createDefaultRemoteAuthState();
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
function writeRemoteAuthPayload(tenantRoot, payload) {
|
|
974
|
-
const { authPath } = getTreeseedRemoteAuthPaths(tenantRoot);
|
|
975
|
-
ensureParent(authPath);
|
|
976
|
-
writeFileSync(authPath, `${JSON.stringify(payload, null, 2)}
|
|
977
|
-
`, "utf8");
|
|
978
|
-
}
|
|
979
|
-
function removeLegacyMachineKeyIfSafe(tenantRoot) {
|
|
980
|
-
const { legacyKeyPath, keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
981
|
-
if (legacyKeyPath !== keyPath && existsSync(legacyKeyPath)) {
|
|
982
|
-
rmSync(legacyKeyPath, { force: true });
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
function reencryptTreeseedEncryptedState(tenantRoot, oldKey, newKey) {
|
|
986
|
-
const machineConfig = loadTreeseedMachineConfig(tenantRoot);
|
|
987
|
-
const machineSecrets = decryptMachineConfigSecrets(machineConfig, oldKey);
|
|
988
|
-
const { authPath } = getTreeseedRemoteAuthPaths(tenantRoot);
|
|
989
|
-
const remoteAuthExists = existsSync(authPath);
|
|
990
|
-
const remoteAuthPayload = loadRemoteAuthPayload(tenantRoot);
|
|
991
|
-
const remoteSessions = decryptRemoteAuthSessions(remoteAuthPayload, oldKey);
|
|
992
|
-
writeTreeseedMachineConfig(tenantRoot, applyMachineConfigSecrets(machineConfig, machineSecrets, newKey));
|
|
993
|
-
if (remoteAuthExists || Object.keys(remoteSessions).length > 0) {
|
|
994
|
-
writeRemoteAuthPayload(tenantRoot, {
|
|
995
|
-
version: 1,
|
|
996
|
-
sessions: encryptRemoteAuthSessions(remoteSessions, newKey)
|
|
997
|
-
});
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
function loadMachineKey(tenantRoot) {
|
|
1001
|
-
return resolveUnlockedMachineKey(tenantRoot);
|
|
1002
|
-
}
|
|
1003
|
-
function decryptValueWithMachineKey(tenantRoot, payload, key) {
|
|
1004
|
-
try {
|
|
1005
|
-
return decryptValue(payload, key);
|
|
1006
|
-
} catch (error) {
|
|
1007
|
-
const legacyKey = loadLegacyMachineKey(tenantRoot);
|
|
1008
|
-
if (!legacyKey) {
|
|
1009
|
-
throw error;
|
|
1010
|
-
}
|
|
1011
|
-
const value = decryptValue(payload, legacyKey);
|
|
1012
|
-
reencryptTreeseedEncryptedState(tenantRoot, legacyKey, key);
|
|
1013
|
-
removeLegacyMachineKeyIfSafe(tenantRoot);
|
|
1014
|
-
return value;
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
function rotateTreeseedMachineKey(tenantRoot) {
|
|
1018
|
-
const { keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
1019
|
-
const oldKey = loadMachineKey(tenantRoot);
|
|
1020
|
-
const newKey = randomBytes(32);
|
|
1021
|
-
reencryptTreeseedEncryptedState(tenantRoot, oldKey, newKey);
|
|
1022
|
-
const status = inspectTreeseedKeyAgentStatus(tenantRoot);
|
|
1023
|
-
if (!status.unlocked) {
|
|
1024
|
-
throw new TreeseedKeyAgentError("locked", "Treeseed secrets must be unlocked before rotating the machine key.", { keyPath });
|
|
1025
|
-
}
|
|
1026
|
-
const wrapped = readWrappedMachineKeyFile(keyPath);
|
|
1027
|
-
if (!wrapped.wrapped) {
|
|
1028
|
-
throw new TreeseedKeyAgentError(
|
|
1029
|
-
wrapped.migrationRequired ? "wrapped_key_migration_required" : "wrapped_key_missing",
|
|
1030
|
-
wrapped.migrationRequired ? "Wrap the Treeseed machine key before rotating it." : "Create and unlock the Treeseed machine key before rotating it.",
|
|
1031
|
-
{ keyPath }
|
|
1032
|
-
);
|
|
1033
|
-
}
|
|
1034
|
-
const passphrase = String(process.env[TREESEED_MACHINE_KEY_PASSPHRASE_ENV] ?? "").trim();
|
|
1035
|
-
if (!passphrase) {
|
|
1036
|
-
throw new TreeseedKeyAgentError(
|
|
1037
|
-
"interactive_required",
|
|
1038
|
-
`Set ${TREESEED_MACHINE_KEY_PASSPHRASE_ENV} when rotating the machine key non-interactively, or use \`treeseed secrets:rotate-machine-key\` from an interactive shell.`,
|
|
1039
|
-
{ keyPath }
|
|
1040
|
-
);
|
|
1041
|
-
}
|
|
1042
|
-
replaceWrappedMachineKey(keyPath, newKey, passphrase);
|
|
1043
|
-
unlockTreeseedSecretSessionFromEnv(tenantRoot, { allowMigration: false, createIfMissing: false });
|
|
1044
|
-
removeLegacyMachineKeyIfSafe(tenantRoot);
|
|
1045
|
-
return {
|
|
1046
|
-
keyPath,
|
|
1047
|
-
rotated: true
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
function rotateTreeseedMachineKeyPassphrase(tenantRoot, passphrase) {
|
|
1051
|
-
const { keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
1052
|
-
const machineKey = loadMachineKey(tenantRoot);
|
|
1053
|
-
rotateWrappedMachineKeyPassphrase(keyPath, machineKey, passphrase);
|
|
1054
|
-
return {
|
|
1055
|
-
keyPath,
|
|
1056
|
-
rotated: true
|
|
1057
|
-
};
|
|
1058
|
-
}
|
|
1059
|
-
function migrateTreeseedMachineKeyToWrapped(tenantRoot, passphrase) {
|
|
1060
|
-
const { keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
1061
|
-
const wrapped = readWrappedMachineKeyFile(keyPath);
|
|
1062
|
-
if (wrapped.wrapped) {
|
|
1063
|
-
return {
|
|
1064
|
-
keyPath,
|
|
1065
|
-
migrated: false,
|
|
1066
|
-
alreadyWrapped: true
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
if (wrapped.plaintextLegacy) {
|
|
1070
|
-
replaceWrappedMachineKey(keyPath, wrapped.plaintextLegacy, passphrase);
|
|
1071
|
-
} else {
|
|
1072
|
-
const legacyKey = loadLegacyMachineKey(tenantRoot);
|
|
1073
|
-
if (!legacyKey) {
|
|
1074
|
-
throw new TreeseedKeyAgentError(
|
|
1075
|
-
"wrapped_key_missing",
|
|
1076
|
-
"No existing machine key was found to migrate.",
|
|
1077
|
-
{ keyPath }
|
|
1078
|
-
);
|
|
1079
|
-
}
|
|
1080
|
-
replaceWrappedMachineKey(keyPath, legacyKey, passphrase);
|
|
1081
|
-
removeLegacyMachineKeyIfSafe(tenantRoot);
|
|
1082
|
-
}
|
|
1083
|
-
return {
|
|
1084
|
-
keyPath,
|
|
1085
|
-
migrated: true,
|
|
1086
|
-
alreadyWrapped: false
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
function loadTreeseedRemoteAuthState(tenantRoot) {
|
|
1090
|
-
const key = loadMachineKey(tenantRoot);
|
|
1091
|
-
const payload = loadRemoteAuthPayload(tenantRoot);
|
|
1092
|
-
let sessions;
|
|
1093
|
-
try {
|
|
1094
|
-
sessions = decryptRemoteAuthSessions(payload, key);
|
|
1095
|
-
} catch (error) {
|
|
1096
|
-
const legacyKey = loadLegacyMachineKey(tenantRoot);
|
|
1097
|
-
if (!legacyKey) {
|
|
1098
|
-
throw error;
|
|
1099
|
-
}
|
|
1100
|
-
sessions = decryptRemoteAuthSessions(payload, legacyKey);
|
|
1101
|
-
reencryptTreeseedEncryptedState(tenantRoot, legacyKey, key);
|
|
1102
|
-
removeLegacyMachineKeyIfSafe(tenantRoot);
|
|
1103
|
-
}
|
|
1104
|
-
return {
|
|
1105
|
-
version: 1,
|
|
1106
|
-
sessions
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
function writeTreeseedRemoteAuthState(tenantRoot, state) {
|
|
1110
|
-
const key = loadMachineKey(tenantRoot);
|
|
1111
|
-
writeRemoteAuthPayload(tenantRoot, {
|
|
1112
|
-
version: 1,
|
|
1113
|
-
sessions: encryptRemoteAuthSessions(state.sessions, key)
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1116
|
-
function setTreeseedRemoteSession(tenantRoot, { hostId, accessToken, refreshToken, expiresAt, principal }) {
|
|
1117
|
-
const state = loadTreeseedRemoteAuthState(tenantRoot);
|
|
1118
|
-
state.sessions[hostId] = {
|
|
1119
|
-
accessToken,
|
|
1120
|
-
refreshToken,
|
|
1121
|
-
expiresAt,
|
|
1122
|
-
principal: principal ?? null
|
|
1123
|
-
};
|
|
1124
|
-
writeTreeseedRemoteAuthState(tenantRoot, state);
|
|
1125
|
-
return state.sessions[hostId];
|
|
1126
|
-
}
|
|
1127
|
-
function clearTreeseedRemoteSession(tenantRoot, hostId) {
|
|
1128
|
-
const state = loadTreeseedRemoteAuthState(tenantRoot);
|
|
1129
|
-
if (hostId) {
|
|
1130
|
-
delete state.sessions[hostId];
|
|
1131
|
-
} else {
|
|
1132
|
-
state.sessions = {};
|
|
1133
|
-
}
|
|
1134
|
-
writeTreeseedRemoteAuthState(tenantRoot, state);
|
|
1135
|
-
return state;
|
|
1136
|
-
}
|
|
1137
|
-
function resolveTreeseedRemoteSession(tenantRoot, hostId) {
|
|
1138
|
-
const { authPath } = getTreeseedRemoteAuthPaths(tenantRoot);
|
|
1139
|
-
if (!existsSync(authPath)) {
|
|
1140
|
-
return null;
|
|
1141
|
-
}
|
|
1142
|
-
const sessionState = loadTreeseedRemoteAuthState(tenantRoot);
|
|
1143
|
-
const { configPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
1144
|
-
const selectedHostId = hostId ?? (existsSync(configPath) ? loadTreeseedMachineConfig(tenantRoot).settings?.remote?.activeHostId : "official") ?? "official";
|
|
1145
|
-
return sessionState.sessions?.[selectedHostId] ?? null;
|
|
1146
|
-
}
|
|
1147
|
-
function loadTreeseedMachineConfig(tenantRoot) {
|
|
1148
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1149
|
-
const tenantConfig = loadOptionalTenantManifest(tenantRoot);
|
|
1150
|
-
const defaults = createDefaultTreeseedMachineConfig({ tenantRoot, deployConfig, tenantConfig });
|
|
1151
|
-
const { configPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
1152
|
-
if (!existsSync(configPath)) {
|
|
1153
|
-
return defaults;
|
|
1154
|
-
}
|
|
1155
|
-
const raw = parseYaml(readFileSync(configPath, "utf8")) ?? {};
|
|
1156
|
-
const parsed = raw && typeof raw === "object" ? raw : {};
|
|
1157
|
-
return {
|
|
1158
|
-
...defaults,
|
|
1159
|
-
...parsed,
|
|
1160
|
-
project: {
|
|
1161
|
-
...defaults.project,
|
|
1162
|
-
...parsed.project ?? {}
|
|
1163
|
-
},
|
|
1164
|
-
settings: {
|
|
1165
|
-
...defaults.settings,
|
|
1166
|
-
...parsed.settings ?? {},
|
|
1167
|
-
sync: {
|
|
1168
|
-
...defaults.settings.sync,
|
|
1169
|
-
...parsed.settings?.sync ?? {}
|
|
1170
|
-
},
|
|
1171
|
-
templates: {
|
|
1172
|
-
...defaults.settings.templates ?? {},
|
|
1173
|
-
...parsed.settings?.templates ?? {}
|
|
1174
|
-
},
|
|
1175
|
-
remote: normalizeRemoteSettings({
|
|
1176
|
-
...defaults.settings.remote ?? {},
|
|
1177
|
-
...parsed.settings?.remote ?? {}
|
|
1178
|
-
}),
|
|
1179
|
-
services: normalizeServiceSettings({
|
|
1180
|
-
...defaults.settings.services ?? {},
|
|
1181
|
-
...parsed.settings?.services ?? {}
|
|
1182
|
-
})
|
|
1183
|
-
},
|
|
1184
|
-
shared: {
|
|
1185
|
-
values: {
|
|
1186
|
-
...defaults.shared?.values ?? {},
|
|
1187
|
-
...parsed.shared?.values ?? {}
|
|
1188
|
-
},
|
|
1189
|
-
secrets: {
|
|
1190
|
-
...defaults.shared?.secrets ?? {},
|
|
1191
|
-
...parsed.shared?.secrets ?? {}
|
|
1192
|
-
}
|
|
1193
|
-
},
|
|
1194
|
-
environments: Object.fromEntries(
|
|
1195
|
-
TREESEED_ENVIRONMENT_SCOPES.map((scope) => [
|
|
1196
|
-
scope,
|
|
1197
|
-
{
|
|
1198
|
-
values: {
|
|
1199
|
-
...defaults.environments?.[scope]?.values ?? {},
|
|
1200
|
-
...parsed.environments?.[scope]?.values ?? {}
|
|
1201
|
-
},
|
|
1202
|
-
secrets: {
|
|
1203
|
-
...defaults.environments?.[scope]?.secrets ?? {},
|
|
1204
|
-
...parsed.environments?.[scope]?.secrets ?? {}
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
])
|
|
1208
|
-
)
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
function writeTreeseedMachineConfig(tenantRoot, config) {
|
|
1212
|
-
const { configPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
1213
|
-
ensureParent(configPath);
|
|
1214
|
-
writeFileSync(configPath, stringifyYaml(config), "utf8");
|
|
1215
|
-
}
|
|
1216
|
-
function updateTreeseedDeployConfigFeatureToggles(tenantRoot, toggles) {
|
|
1217
|
-
const configPath = resolve(tenantRoot, "treeseed.site.yaml");
|
|
1218
|
-
const current = parseYaml(readFileSync(configPath, "utf8")) ?? {};
|
|
1219
|
-
const next = { ...current };
|
|
1220
|
-
if ("smtp" in toggles) {
|
|
1221
|
-
next.smtp = {
|
|
1222
|
-
...current.smtp ?? {},
|
|
1223
|
-
enabled: toggles.smtp === true
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
|
-
if ("turnstile" in toggles) {
|
|
1227
|
-
next.turnstile = {
|
|
1228
|
-
...current.turnstile ?? {},
|
|
1229
|
-
enabled: toggles.turnstile === true
|
|
1230
|
-
};
|
|
1231
|
-
}
|
|
1232
|
-
writeFileSync(configPath, stringifyYaml(next), "utf8");
|
|
1233
|
-
}
|
|
1234
|
-
function resolveTreeseedRemoteConfig(startRoot = process.cwd(), env = process.env) {
|
|
1235
|
-
const machineConfigPath = findNearestTreeseedMachineConfig(startRoot);
|
|
1236
|
-
const tenantRoot = machineConfigPath ? resolve(dirname(dirname(machineConfigPath)), "..") : startRoot;
|
|
1237
|
-
const deployConfig = existsSync(resolve(tenantRoot, "treeseed.site.yaml")) ? loadTenantDeployConfig(tenantRoot) : null;
|
|
1238
|
-
const machineConfig = machineConfigPath ? loadTreeseedMachineConfig(tenantRoot) : createDefaultTreeseedMachineConfig({
|
|
1239
|
-
tenantRoot: startRoot,
|
|
1240
|
-
deployConfig: {
|
|
1241
|
-
name: "TreeSeed",
|
|
1242
|
-
slug: "treeseed",
|
|
1243
|
-
siteUrl: DEFAULT_TREESEED_API_BASE_URL,
|
|
1244
|
-
contactEmail: "hello@treeseed.ai"
|
|
1245
|
-
},
|
|
1246
|
-
tenantConfig: void 0
|
|
1247
|
-
});
|
|
1248
|
-
const settings = normalizeRemoteSettings(machineConfig.settings?.remote);
|
|
1249
|
-
const deployBaseUrl = env[TREESEED_API_BASE_URL_ENV] ?? deployConfig?.services?.api?.environments?.prod?.baseUrl ?? deployConfig?.services?.api?.publicBaseUrl ?? null;
|
|
1250
|
-
if (deployBaseUrl) {
|
|
1251
|
-
const officialHost = settings.hosts.find((entry) => entry.id === "official");
|
|
1252
|
-
if (officialHost) {
|
|
1253
|
-
officialHost.baseUrl = deployBaseUrl.replace(/\/$/u, "");
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
const envBaseUrl = env[TREESEED_API_BASE_URL_ENV];
|
|
1257
|
-
const hosts = envBaseUrl && envBaseUrl.trim().length > 0 ? [
|
|
1258
|
-
{
|
|
1259
|
-
id: "env",
|
|
1260
|
-
label: "Environment override",
|
|
1261
|
-
baseUrl: envBaseUrl.trim().replace(/\/$/u, "")
|
|
1262
|
-
},
|
|
1263
|
-
...settings.hosts.filter((entry) => entry.id !== "env")
|
|
1264
|
-
] : settings.hosts;
|
|
1265
|
-
const activeHostId = envBaseUrl && envBaseUrl.trim().length > 0 ? "env" : settings.activeHostId;
|
|
1266
|
-
const auth = resolveTreeseedRemoteSession(tenantRoot, activeHostId);
|
|
1267
|
-
return {
|
|
1268
|
-
hosts,
|
|
1269
|
-
activeHostId,
|
|
1270
|
-
executionMode: settings.executionMode,
|
|
1271
|
-
auth: auth ? {
|
|
1272
|
-
accessToken: auth.accessToken,
|
|
1273
|
-
refreshToken: auth.refreshToken,
|
|
1274
|
-
expiresAt: auth.expiresAt,
|
|
1275
|
-
principal: auth.principal ?? null
|
|
1276
|
-
} : void 0
|
|
1277
|
-
};
|
|
1278
|
-
}
|
|
1279
|
-
function resolveTreeseedTemplateCatalogEndpoint(startRoot = process.cwd(), env = process.env) {
|
|
1280
|
-
const envValue = env[TREESEED_TEMPLATE_CATALOG_URL_ENV];
|
|
1281
|
-
if (typeof envValue === "string" && envValue.trim().length > 0) {
|
|
1282
|
-
return envValue.trim();
|
|
1283
|
-
}
|
|
1284
|
-
const machineConfigPath = findNearestTreeseedMachineConfig(startRoot);
|
|
1285
|
-
if (!machineConfigPath) {
|
|
1286
|
-
return DEFAULT_TEMPLATE_CATALOG_URL;
|
|
1287
|
-
}
|
|
1288
|
-
const raw = parseYaml(readFileSync(machineConfigPath, "utf8")) ?? {};
|
|
1289
|
-
const parsed = raw && typeof raw === "object" ? raw : {};
|
|
1290
|
-
const configuredEndpoint = parsed.settings?.templates?.catalogEndpoint;
|
|
1291
|
-
return typeof configuredEndpoint === "string" && configuredEndpoint.trim().length > 0 ? configuredEndpoint.trim() : DEFAULT_TEMPLATE_CATALOG_URL;
|
|
1292
|
-
}
|
|
1293
|
-
function resolveTreeseedTemplateCatalogCachePath(startRoot = process.cwd()) {
|
|
1294
|
-
const machineConfigPath = findNearestTreeseedMachineConfig(startRoot);
|
|
1295
|
-
if (machineConfigPath) {
|
|
1296
|
-
return resolve(dirname(dirname(machineConfigPath)), "cache", "template-catalog.json");
|
|
1297
|
-
}
|
|
1298
|
-
return resolve(startRoot, ".treeseed", "cache", TEMPLATE_CATALOG_CACHE_RELATIVE_PATH);
|
|
1299
|
-
}
|
|
1300
|
-
function ensureTreeseedGitignoreEntries(tenantRoot) {
|
|
1301
|
-
const gitignorePath = resolve(tenantRoot, ".gitignore");
|
|
1302
|
-
const requiredEntries = [".treeseed/"];
|
|
1303
|
-
const current = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf8") : "";
|
|
1304
|
-
const lines = current.split(/\r?\n/);
|
|
1305
|
-
let changed = false;
|
|
1306
|
-
for (const entry of requiredEntries) {
|
|
1307
|
-
if (!lines.includes(entry)) {
|
|
1308
|
-
lines.push(entry);
|
|
1309
|
-
changed = true;
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
if (changed || !existsSync(gitignorePath)) {
|
|
1313
|
-
writeFileSync(gitignorePath, `${lines.filter(Boolean).join("\n")}
|
|
1314
|
-
`, "utf8");
|
|
1315
|
-
}
|
|
1316
|
-
return gitignorePath;
|
|
1317
|
-
}
|
|
1318
|
-
function ensureTreeseedRailwayIgnoreEntries(tenantRoot) {
|
|
1319
|
-
const railwayIgnorePath = resolve(tenantRoot, ".railwayignore");
|
|
1320
|
-
const requiredEntries = [
|
|
1321
|
-
".astro/",
|
|
1322
|
-
".codex/",
|
|
1323
|
-
".dev.vars",
|
|
1324
|
-
".env.local",
|
|
1325
|
-
".git/",
|
|
1326
|
-
".treeseed/",
|
|
1327
|
-
".wrangler/",
|
|
1328
|
-
"coverage/",
|
|
1329
|
-
"node_modules/",
|
|
1330
|
-
"npm-debug.log*",
|
|
1331
|
-
"packages/*/.git/",
|
|
1332
|
-
"packages/*/node_modules/",
|
|
1333
|
-
"public/__treeseed/*.json",
|
|
1334
|
-
"public/books/*.json",
|
|
1335
|
-
"public/books/*.md",
|
|
1336
|
-
"scripts/.ts-run-*.mjs",
|
|
1337
|
-
"tmp/",
|
|
1338
|
-
"*.log",
|
|
1339
|
-
"*.tgz"
|
|
1340
|
-
];
|
|
1341
|
-
const removedEntries = /* @__PURE__ */ new Set([
|
|
1342
|
-
"dist/",
|
|
1343
|
-
"**/dist/",
|
|
1344
|
-
"packages/*/dist/"
|
|
1345
|
-
]);
|
|
1346
|
-
const current = existsSync(railwayIgnorePath) ? readFileSync(railwayIgnorePath, "utf8") : "";
|
|
1347
|
-
let changed = false;
|
|
1348
|
-
const lines = current.split(/\r?\n/).filter((line) => {
|
|
1349
|
-
const keep = !removedEntries.has(line.trim());
|
|
1350
|
-
if (!keep) {
|
|
1351
|
-
changed = true;
|
|
1352
|
-
}
|
|
1353
|
-
return keep;
|
|
1354
|
-
});
|
|
1355
|
-
for (const entry of requiredEntries) {
|
|
1356
|
-
if (!lines.includes(entry)) {
|
|
1357
|
-
lines.push(entry);
|
|
1358
|
-
changed = true;
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
if (changed || !existsSync(railwayIgnorePath)) {
|
|
1362
|
-
writeFileSync(railwayIgnorePath, `${lines.filter(Boolean).join("\n")}
|
|
1363
|
-
`, "utf8");
|
|
1364
|
-
}
|
|
1365
|
-
return railwayIgnorePath;
|
|
1366
|
-
}
|
|
1367
|
-
function dedupeRepairActions(actions) {
|
|
1368
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1369
|
-
return actions.filter((action) => {
|
|
1370
|
-
if (seen.has(action.id)) {
|
|
1371
|
-
return false;
|
|
1372
|
-
}
|
|
1373
|
-
seen.add(action.id);
|
|
1374
|
-
return true;
|
|
1375
|
-
});
|
|
1376
|
-
}
|
|
1377
|
-
function applyTreeseedSafeRepairs(tenantRoot) {
|
|
1378
|
-
const actions = [];
|
|
1379
|
-
ensureTreeseedGitignoreEntries(tenantRoot);
|
|
1380
|
-
actions.push({ id: "gitignore", detail: "Ensured Treeseed gitignore entries are present." });
|
|
1381
|
-
ensureTreeseedRailwayIgnoreEntries(tenantRoot);
|
|
1382
|
-
actions.push({ id: "railwayignore", detail: "Ensured Railway deploy ignore entries are present." });
|
|
1383
|
-
const deprecatedFiles = warnDeprecatedTreeseedLocalEnvFiles(tenantRoot);
|
|
1384
|
-
if (deprecatedFiles.length > 0) {
|
|
1385
|
-
actions.push({ id: "deprecated-local-env", detail: "Detected deprecated .env.local/.dev.vars files that Treeseed now ignores." });
|
|
1386
|
-
}
|
|
1387
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1388
|
-
const { configPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
1389
|
-
if (!existsSync(configPath)) {
|
|
1390
|
-
const machineConfig2 = createDefaultTreeseedMachineConfig({
|
|
1391
|
-
tenantRoot,
|
|
1392
|
-
deployConfig,
|
|
1393
|
-
tenantConfig: void 0
|
|
1394
|
-
});
|
|
1395
|
-
writeTreeseedMachineConfig(tenantRoot, machineConfig2);
|
|
1396
|
-
actions.push({ id: "machine-config", detail: "Created the default Treeseed machine config." });
|
|
1397
|
-
}
|
|
1398
|
-
const keyStatus = inspectTreeseedKeyAgentStatus(tenantRoot);
|
|
1399
|
-
if (!keyStatus.wrappedKeyPresent && !keyStatus.migrationRequired) {
|
|
1400
|
-
actions.push({ id: "machine-key", detail: "Treeseed will create a wrapped machine key the first time the secret session is unlocked." });
|
|
1401
|
-
} else if (keyStatus.migrationRequired) {
|
|
1402
|
-
actions.push({ id: "machine-key-migration", detail: "Detected a legacy plaintext machine key that must be wrapped on the next unlock." });
|
|
1403
|
-
}
|
|
1404
|
-
const machineConfig = loadTreeseedMachineConfig(tenantRoot);
|
|
1405
|
-
writeTreeseedMachineConfig(tenantRoot, machineConfig);
|
|
1406
|
-
for (const scope of TREESEED_ENVIRONMENT_SCOPES) {
|
|
1407
|
-
const target = createPersistentDeployTarget(scope);
|
|
1408
|
-
const state = loadDeployState(tenantRoot, deployConfig, { target });
|
|
1409
|
-
if (state.readiness?.initialized || scope === "local") {
|
|
1410
|
-
ensureGeneratedWranglerConfig(tenantRoot, { target });
|
|
1411
|
-
actions.push({ id: `wrangler-${scope}`, detail: `Regenerated the ${scope} generated Wrangler config.` });
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
return dedupeRepairActions(actions);
|
|
1415
|
-
}
|
|
1416
|
-
function decryptMachineEnvironmentBucket(tenantRoot, config, key, bucket) {
|
|
1417
|
-
const values = {
|
|
1418
|
-
...bucket?.values ?? {}
|
|
1419
|
-
};
|
|
1420
|
-
for (const [entryId, payload] of Object.entries(bucket?.secrets ?? {})) {
|
|
1421
|
-
if (!key) {
|
|
1422
|
-
continue;
|
|
1423
|
-
}
|
|
1424
|
-
values[entryId] = decryptValueWithMachineKey(tenantRoot, payload, key);
|
|
1425
|
-
}
|
|
1426
|
-
return values;
|
|
1427
|
-
}
|
|
1428
|
-
function readMachineBucketEntryValue(tenantRoot, key, bucket, entry) {
|
|
1429
|
-
if (entry.sensitivity === "secret") {
|
|
1430
|
-
const payload = bucket?.secrets?.[entry.id];
|
|
1431
|
-
return typeof payload === "string" && payload.length > 0 ? decryptValueWithMachineKey(tenantRoot, payload, key) : "";
|
|
1432
|
-
}
|
|
1433
|
-
return typeof bucket?.values?.[entry.id] === "string" ? bucket.values[entry.id] : "";
|
|
1434
|
-
}
|
|
1435
|
-
function writeMachineBucketEntryValue(target, entry, value, key) {
|
|
1436
|
-
if (entry.sensitivity === "secret") {
|
|
1437
|
-
delete target.values[entry.id];
|
|
1438
|
-
if (value) {
|
|
1439
|
-
target.secrets[entry.id] = encryptValue(value, key);
|
|
1440
|
-
} else {
|
|
1441
|
-
delete target.secrets[entry.id];
|
|
1442
|
-
}
|
|
1443
|
-
return;
|
|
1444
|
-
}
|
|
1445
|
-
delete target.secrets[entry.id];
|
|
1446
|
-
if (value) {
|
|
1447
|
-
target.values[entry.id] = value;
|
|
1448
|
-
} else {
|
|
1449
|
-
delete target.values[entry.id];
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
function migrateLegacyScopedSharedEntries(tenantRoot, config, registryEntries, key) {
|
|
1453
|
-
const notices = [];
|
|
1454
|
-
let changed = false;
|
|
1455
|
-
for (const entry of registryEntries) {
|
|
1456
|
-
if (entry.storage !== "shared") {
|
|
1457
|
-
continue;
|
|
1458
|
-
}
|
|
1459
|
-
const sharedValue = readMachineBucketEntryValue(tenantRoot, key, config.shared, entry);
|
|
1460
|
-
if (sharedValue.length > 0) {
|
|
1461
|
-
continue;
|
|
1462
|
-
}
|
|
1463
|
-
const scopedValues = TREESEED_ENVIRONMENT_SCOPES.map((scope) => ({
|
|
1464
|
-
scope,
|
|
1465
|
-
value: readMachineBucketEntryValue(tenantRoot, key, config.environments?.[scope], entry)
|
|
1466
|
-
})).filter((candidate) => candidate.value.length > 0);
|
|
1467
|
-
if (scopedValues.length === 0) {
|
|
1468
|
-
continue;
|
|
1469
|
-
}
|
|
1470
|
-
const promotedFrom = (scopedValues.find((candidate) => candidate.scope === "staging") ?? scopedValues.find((candidate) => candidate.scope === "prod") ?? scopedValues[0]).scope;
|
|
1471
|
-
const promotedValue = scopedValues.find((candidate) => candidate.scope === promotedFrom)?.value ?? "";
|
|
1472
|
-
const hadConflicts = new Set(scopedValues.map((candidate) => candidate.value)).size > 1;
|
|
1473
|
-
writeMachineBucketEntryValue(config.shared, entry, promotedValue, key);
|
|
1474
|
-
for (const candidateScope of TREESEED_ENVIRONMENT_SCOPES) {
|
|
1475
|
-
delete config.environments[candidateScope].values[entry.id];
|
|
1476
|
-
delete config.environments[candidateScope].secrets[entry.id];
|
|
1477
|
-
}
|
|
1478
|
-
notices.push({
|
|
1479
|
-
entryId: entry.id,
|
|
1480
|
-
label: entry.label,
|
|
1481
|
-
promotedFrom,
|
|
1482
|
-
consolidatedScopes: scopedValues.map((candidate) => candidate.scope),
|
|
1483
|
-
hadConflicts
|
|
1484
|
-
});
|
|
1485
|
-
changed = true;
|
|
1486
|
-
}
|
|
1487
|
-
if (changed) {
|
|
1488
|
-
writeTreeseedMachineConfig(tenantRoot, config);
|
|
1489
|
-
}
|
|
1490
|
-
return notices;
|
|
1491
|
-
}
|
|
1492
|
-
function resolveEntryValueFromBuckets(entry, entryId, scope, bucketValuesByScope) {
|
|
1493
|
-
if (!entry) {
|
|
1494
|
-
return bucketValuesByScope[scope]?.[entryId] ?? bucketValuesByScope.shared?.[entryId] ?? "";
|
|
1495
|
-
}
|
|
1496
|
-
if (entry?.storage === "shared") {
|
|
1497
|
-
const sharedValue = bucketValuesByScope.shared?.[entryId];
|
|
1498
|
-
if (typeof sharedValue === "string" && sharedValue.length > 0) {
|
|
1499
|
-
return sharedValue;
|
|
1500
|
-
}
|
|
1501
|
-
const searchScopes = [scope, ...TREESEED_ENVIRONMENT_SCOPES.filter((candidate) => candidate !== scope)];
|
|
1502
|
-
for (const candidateScope of searchScopes) {
|
|
1503
|
-
const candidateValue = bucketValuesByScope[candidateScope]?.[entryId];
|
|
1504
|
-
if (typeof candidateValue === "string" && candidateValue.length > 0) {
|
|
1505
|
-
return candidateValue;
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
return "";
|
|
1509
|
-
}
|
|
1510
|
-
return bucketValuesByScope[scope]?.[entryId] ?? "";
|
|
1511
|
-
}
|
|
1512
|
-
const LEGACY_ENV_KEY_ALIASES = {
|
|
1513
|
-
TREESEED_RAILWAY_API_TOKEN: ["RAILWAY_API_TOKEN"],
|
|
1514
|
-
TREESEED_CLOUDFLARE_API_TOKEN: ["CLOUDFLARE_API_TOKEN"],
|
|
1515
|
-
TREESEED_DOCKERHUB_TOKEN: ["DOCKERHUB_TOKEN"],
|
|
1516
|
-
TREESEED_DOCKERHUB_USERNAME: ["DOCKERHUB_USERNAME"],
|
|
1517
|
-
TREESEED_GITHUB_TOKEN: ["GH_TOKEN", "GITHUB_TOKEN", "TREESEED_HOSTED_HUBS_GITHUB_TOKEN"]
|
|
1518
|
-
};
|
|
1519
|
-
function resolveLegacyEntryValueFromBuckets(entryId, scope, bucketValuesByScope) {
|
|
1520
|
-
const aliases = LEGACY_ENV_KEY_ALIASES[entryId] ?? [];
|
|
1521
|
-
for (const alias of aliases) {
|
|
1522
|
-
const resolved = resolveEntryValueFromBuckets(null, alias, scope, bucketValuesByScope);
|
|
1523
|
-
if (typeof resolved === "string" && resolved.length > 0) {
|
|
1524
|
-
return resolved;
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
return "";
|
|
1528
|
-
}
|
|
1529
|
-
function resolveTreeseedMachineEnvironmentValues(tenantRoot, scope, additionalKeys = []) {
|
|
1530
|
-
const config = loadTreeseedMachineConfig(tenantRoot);
|
|
1531
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
1532
|
-
let key = null;
|
|
1533
|
-
try {
|
|
1534
|
-
key = loadMachineKey(tenantRoot);
|
|
1535
|
-
} catch {
|
|
1536
|
-
key = null;
|
|
1537
|
-
}
|
|
1538
|
-
const bucketValuesByScope = {
|
|
1539
|
-
shared: decryptMachineEnvironmentBucket(tenantRoot, config, key, config.shared),
|
|
1540
|
-
...Object.fromEntries(
|
|
1541
|
-
TREESEED_ENVIRONMENT_SCOPES.map((candidateScope) => [
|
|
1542
|
-
candidateScope,
|
|
1543
|
-
decryptMachineEnvironmentBucket(tenantRoot, config, key, config.environments?.[candidateScope])
|
|
1544
|
-
])
|
|
1545
|
-
)
|
|
1546
|
-
};
|
|
1547
|
-
const entryById = new Map(registry.entries.map((entry) => [entry.id, entry]));
|
|
1548
|
-
const values = {};
|
|
1549
|
-
const knownKeys = /* @__PURE__ */ new Set([
|
|
1550
|
-
...registry.entries.map((entry) => entry.id),
|
|
1551
|
-
...additionalKeys
|
|
1552
|
-
]);
|
|
1553
|
-
for (const entryId of knownKeys) {
|
|
1554
|
-
const resolved = resolveEntryValueFromBuckets(entryById.get(entryId), entryId, scope, bucketValuesByScope) || resolveLegacyEntryValueFromBuckets(entryId, scope, bucketValuesByScope);
|
|
1555
|
-
if (typeof resolved === "string" && resolved.length > 0) {
|
|
1556
|
-
values[entryId] = resolved;
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
return values;
|
|
1560
|
-
}
|
|
1561
|
-
function setTreeseedMachineEnvironmentValue(tenantRoot, scope, entry, value) {
|
|
1562
|
-
const key = loadMachineKey(tenantRoot);
|
|
1563
|
-
const config = loadTreeseedMachineConfig(tenantRoot);
|
|
1564
|
-
const target = entry.storage === "shared" ? config.shared : config.environments[scope];
|
|
1565
|
-
if (entry.storage === "shared") {
|
|
1566
|
-
for (const candidateScope of TREESEED_ENVIRONMENT_SCOPES) {
|
|
1567
|
-
delete config.environments[candidateScope].values[entry.id];
|
|
1568
|
-
delete config.environments[candidateScope].secrets[entry.id];
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
if (entry.sensitivity === "secret") {
|
|
1572
|
-
delete target.values[entry.id];
|
|
1573
|
-
if (value) {
|
|
1574
|
-
target.secrets[entry.id] = encryptValue(value, key);
|
|
1575
|
-
} else {
|
|
1576
|
-
delete target.secrets[entry.id];
|
|
1577
|
-
}
|
|
1578
|
-
} else {
|
|
1579
|
-
delete target.secrets[entry.id];
|
|
1580
|
-
if (value) {
|
|
1581
|
-
target.values[entry.id] = value;
|
|
1582
|
-
} else {
|
|
1583
|
-
delete target.values[entry.id];
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
writeTreeseedMachineConfig(tenantRoot, config);
|
|
1587
|
-
return config;
|
|
1588
|
-
}
|
|
1589
|
-
function collectTreeseedEnvironmentContext(tenantRoot) {
|
|
1590
|
-
const deployConfig = loadTenantDeployConfig(tenantRoot);
|
|
1591
|
-
const tenantConfig = loadOptionalTenantManifest(tenantRoot);
|
|
1592
|
-
return resolveTreeseedEnvironmentRegistry({
|
|
1593
|
-
deployConfig,
|
|
1594
|
-
tenantConfig
|
|
1595
|
-
});
|
|
1596
|
-
}
|
|
1597
|
-
function collectTreeseedConfigSeedValues(tenantRoot, scope, env = process.env, valuesOverlay = {}) {
|
|
1598
|
-
warnDeprecatedTreeseedLocalEnvFiles(tenantRoot);
|
|
1599
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
1600
|
-
let machineValues = {};
|
|
1601
|
-
let localMachineValues = {};
|
|
1602
|
-
try {
|
|
1603
|
-
machineValues = resolveTreeseedMachineEnvironmentValues(tenantRoot, scope);
|
|
1604
|
-
if (scope !== "local") {
|
|
1605
|
-
localMachineValues = resolveTreeseedMachineEnvironmentValues(tenantRoot, "local");
|
|
1606
|
-
}
|
|
1607
|
-
} catch (error) {
|
|
1608
|
-
if (!(error instanceof TreeseedKeyAgentError)) {
|
|
1609
|
-
throw error;
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
const providerCredentialFallbacks = Object.fromEntries(
|
|
1613
|
-
PROVIDER_CONTROL_ENV_KEYS.map((key) => [key, localMachineValues[key]]).filter(([, value]) => typeof value === "string" && value.length > 0)
|
|
1614
|
-
);
|
|
1615
|
-
const values = {
|
|
1616
|
-
...providerCredentialFallbacks,
|
|
1617
|
-
...machineValues,
|
|
1618
|
-
...nonEmptyEnvironmentValues(env),
|
|
1619
|
-
...nonEmptyEnvironmentValues(valuesOverlay)
|
|
1620
|
-
};
|
|
1621
|
-
const providerControlValues = Object.fromEntries(
|
|
1622
|
-
PROVIDER_CONTROL_ENV_KEYS.map((key) => [key, values[key]]).filter(([, value]) => typeof value === "string" && value.length > 0)
|
|
1623
|
-
);
|
|
1624
|
-
return {
|
|
1625
|
-
...providerControlValues,
|
|
1626
|
-
...filterEnvironmentValuesByRegistry(values, registry, scope)
|
|
1627
|
-
};
|
|
1628
|
-
}
|
|
1629
|
-
function workspaceBootstrapDeployConfig(tenantRoot, deployConfig) {
|
|
1630
|
-
const appConfigs = discoverTreeseedApplications(tenantRoot).map((application) => application.config).filter((config) => config && config !== deployConfig);
|
|
1631
|
-
if (appConfigs.length === 0) {
|
|
1632
|
-
return deployConfig;
|
|
1633
|
-
}
|
|
1634
|
-
const merged = {
|
|
1635
|
-
...deployConfig,
|
|
1636
|
-
runtime: { ...deployConfig.runtime ?? {} },
|
|
1637
|
-
surfaces: { ...deployConfig.surfaces ?? {} },
|
|
1638
|
-
services: { ...deployConfig.services ?? {} }
|
|
1639
|
-
};
|
|
1640
|
-
for (const config of appConfigs) {
|
|
1641
|
-
if (config.runtime?.mode && merged.runtime?.mode !== "treeseed_managed") {
|
|
1642
|
-
merged.runtime = { ...merged.runtime, ...config.runtime };
|
|
1643
|
-
}
|
|
1644
|
-
merged.surfaces = { ...merged.surfaces, ...config.surfaces ?? {} };
|
|
1645
|
-
merged.services = { ...merged.services, ...config.services ?? {} };
|
|
1646
|
-
}
|
|
1647
|
-
return merged;
|
|
1648
|
-
}
|
|
1649
|
-
function configuredMarketDatabaseService(tenantRoot, deployConfig) {
|
|
1650
|
-
if (deployConfig.services?.treeseedDatabase) {
|
|
1651
|
-
return treeseedDatabaseDescriptor(deployConfig.services.treeseedDatabase, deployConfig.slug);
|
|
1652
|
-
}
|
|
1653
|
-
for (const application of discoverTreeseedApplications(tenantRoot)) {
|
|
1654
|
-
const service = application.config.services?.treeseedDatabase;
|
|
1655
|
-
if (service) {
|
|
1656
|
-
return treeseedDatabaseDescriptor(service, application.config.slug);
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
return null;
|
|
1660
|
-
}
|
|
1661
|
-
function treeseedDatabaseDescriptor(service, slug) {
|
|
1662
|
-
return {
|
|
1663
|
-
service,
|
|
1664
|
-
serviceName: typeof service.railway?.serviceName === "string" && service.railway.serviceName.trim() ? service.railway.serviceName.trim() : `${slug ?? "treeseed-api"}-postgres`
|
|
1665
|
-
};
|
|
1666
|
-
}
|
|
1667
|
-
function nonEmptyEnvironmentValues(env = process.env) {
|
|
1668
|
-
return Object.fromEntries(
|
|
1669
|
-
Object.entries(env).filter(([, value]) => typeof value === "string" && value.length > 0)
|
|
1670
|
-
);
|
|
1671
|
-
}
|
|
1672
|
-
function collectTreeseedConfigSeedValueSources(tenantRoot, scope, env = process.env) {
|
|
1673
|
-
warnDeprecatedTreeseedLocalEnvFiles(tenantRoot);
|
|
1674
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
1675
|
-
const registeredKeys = new Set(registry.entries.filter((entry) => isTreeseedEnvironmentEntryRelevant(entry, registry.context, scope, "config")).map((entry) => entry.id));
|
|
1676
|
-
const values = {};
|
|
1677
|
-
const sources = {};
|
|
1678
|
-
const merge = (source, entries) => {
|
|
1679
|
-
for (const [key, value] of Object.entries(entries)) {
|
|
1680
|
-
if (!registeredKeys.has(key)) {
|
|
1681
|
-
continue;
|
|
1682
|
-
}
|
|
1683
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
1684
|
-
continue;
|
|
1685
|
-
}
|
|
1686
|
-
values[key] = value;
|
|
1687
|
-
sources[key] = source;
|
|
1688
|
-
}
|
|
1689
|
-
};
|
|
1690
|
-
try {
|
|
1691
|
-
merge("machine-config", resolveTreeseedMachineEnvironmentValues(tenantRoot, scope));
|
|
1692
|
-
} catch (error) {
|
|
1693
|
-
if (!(error instanceof TreeseedKeyAgentError)) {
|
|
1694
|
-
throw error;
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
const processValues = Object.fromEntries(Object.entries(env).map(([key, value]) => [key, value ?? void 0]));
|
|
1698
|
-
merge("process.env", processValues);
|
|
1699
|
-
return { values, sources };
|
|
1700
|
-
}
|
|
1701
|
-
function resolveTreeseedLaunchEnvironment({
|
|
1702
|
-
tenantRoot,
|
|
1703
|
-
scope,
|
|
1704
|
-
baseEnv = process.env,
|
|
1705
|
-
overrides = {}
|
|
1706
|
-
}) {
|
|
1707
|
-
warnDeprecatedTreeseedLocalEnvFiles(tenantRoot);
|
|
1708
|
-
let machineValues = {};
|
|
1709
|
-
try {
|
|
1710
|
-
machineValues = resolveTreeseedMachineEnvironmentValues(tenantRoot, scope);
|
|
1711
|
-
} catch (error) {
|
|
1712
|
-
if (!(error instanceof TreeseedKeyAgentError)) {
|
|
1713
|
-
throw error;
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
1717
|
-
const baseValues = nonEmptyEnvironmentValues(baseEnv);
|
|
1718
|
-
const seedValues = scope === "local" ? { ...baseValues, ...machineValues } : { ...machineValues, ...baseValues };
|
|
1719
|
-
const suggestedValues = getTreeseedEnvironmentSuggestedValues({
|
|
1720
|
-
scope,
|
|
1721
|
-
purpose: "deploy",
|
|
1722
|
-
deployConfig: registry.context.deployConfig,
|
|
1723
|
-
tenantConfig: registry.context.tenantConfig,
|
|
1724
|
-
plugins: registry.context.plugins,
|
|
1725
|
-
values: seedValues
|
|
1726
|
-
});
|
|
1727
|
-
const nonSecretSuggestedValues = Object.fromEntries(
|
|
1728
|
-
registry.entries.filter((entry) => entry.sensitivity !== "secret" && typeof suggestedValues[entry.id] === "string" && suggestedValues[entry.id].length > 0).map((entry) => [entry.id, suggestedValues[entry.id]])
|
|
1729
|
-
);
|
|
1730
|
-
const systemSecretSuggestedValues = Object.fromEntries(
|
|
1731
|
-
registry.entries.filter(
|
|
1732
|
-
(entry) => entry.sensitivity === "secret" && entry.visibility === "system" && typeof entry.defaultValue === "function" && typeof suggestedValues[entry.id] === "string" && suggestedValues[entry.id].length > 0
|
|
1733
|
-
).map((entry) => [entry.id, suggestedValues[entry.id]])
|
|
1734
|
-
);
|
|
1735
|
-
const scopedValues = scope === "local" ? { ...nonSecretSuggestedValues, ...systemSecretSuggestedValues, ...baseValues, ...machineValues } : { ...nonSecretSuggestedValues, ...systemSecretSuggestedValues, ...machineValues, ...baseValues };
|
|
1736
|
-
return withTreeseedServiceCredentialEnv({
|
|
1737
|
-
...scopedValues,
|
|
1738
|
-
...overrides
|
|
1739
|
-
});
|
|
1740
|
-
}
|
|
1741
|
-
function formatTreeseedConfigEnvironmentReport({ tenantRoot, scope, env = process.env, revealSecrets = false }) {
|
|
1742
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
1743
|
-
const { values, sources } = collectTreeseedConfigSeedValueSources(tenantRoot, scope, env);
|
|
1744
|
-
const lines = [
|
|
1745
|
-
formatConfigSectionTitle(`Resolved environment values for ${scope}`),
|
|
1746
|
-
revealSecrets ? "Secrets are shown because --show-secrets was provided." : "Secret values are masked. Re-run with --show-secrets to print full values."
|
|
1747
|
-
];
|
|
1748
|
-
for (const entry of listRelevantTreeseedConfigEntries(registry, scope)) {
|
|
1749
|
-
const value = values[entry.id];
|
|
1750
|
-
const displayValue = typeof value === "string" && value.length > 0 ? entry.sensitivity === "secret" && !revealSecrets ? maskValue(value) : value : "(unset)";
|
|
1751
|
-
lines.push(`${entry.id}=${displayValue} (${sources[entry.id] ?? "unset"})`);
|
|
1752
|
-
}
|
|
1753
|
-
return lines.join("\n");
|
|
1754
|
-
}
|
|
1755
|
-
function applyTreeseedEnvironmentToProcess({ tenantRoot, scope, override = false }) {
|
|
1756
|
-
let resolvedValues = {};
|
|
1757
|
-
try {
|
|
1758
|
-
resolvedValues = resolveTreeseedLaunchEnvironment({ tenantRoot, scope });
|
|
1759
|
-
} catch (error) {
|
|
1760
|
-
if (!(error instanceof TreeseedKeyAgentError)) {
|
|
1761
|
-
throw error;
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
for (const [key, value] of Object.entries(resolvedValues)) {
|
|
1765
|
-
const currentValue = process.env[key] ?? "";
|
|
1766
|
-
const shouldReplacePlaceholder = key === "CLOUDFLARE_ACCOUNT_ID" && currentValue === CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER;
|
|
1767
|
-
if ((override || currentValue.length === 0 || shouldReplacePlaceholder) && typeof value === "string" && value.length > 0) {
|
|
1768
|
-
process.env[key] = value;
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
return resolvedValues;
|
|
1772
|
-
}
|
|
1773
|
-
function validateTreeseedCommandEnvironment({ tenantRoot, scope, purpose }) {
|
|
1774
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
1775
|
-
const values = resolveTreeseedLaunchEnvironment({ tenantRoot, scope });
|
|
1776
|
-
const validation = filterValidationByWorkflowPlane(validateTreeseedEnvironmentValues({
|
|
1777
|
-
values,
|
|
1778
|
-
scope,
|
|
1779
|
-
purpose,
|
|
1780
|
-
deployConfig: registry.context.deployConfig,
|
|
1781
|
-
tenantConfig: registry.context.tenantConfig,
|
|
1782
|
-
plugins: registry.context.plugins
|
|
1783
|
-
}));
|
|
1784
|
-
return {
|
|
1785
|
-
registry,
|
|
1786
|
-
values,
|
|
1787
|
-
validation
|
|
1788
|
-
};
|
|
1789
|
-
}
|
|
1790
|
-
function filterValidationByWorkflowPlane(validation) {
|
|
1791
|
-
const plane = process.env.TREESEED_WORKFLOW_PLANE;
|
|
1792
|
-
if (plane !== "web" && plane !== "processing") {
|
|
1793
|
-
return validation;
|
|
1794
|
-
}
|
|
1795
|
-
const problemApplies = (problem) => doesEntryApplyToWorkflowPlane(problem.entry, plane);
|
|
1796
|
-
const missing = validation.missing.filter(problemApplies);
|
|
1797
|
-
const invalid = validation.invalid.filter(problemApplies);
|
|
1798
|
-
const entries = validation.entries.filter((entry) => doesEntryApplyToWorkflowPlane(entry, plane));
|
|
1799
|
-
const required = validation.required.filter((entry) => doesEntryApplyToWorkflowPlane(entry, plane));
|
|
1800
|
-
return {
|
|
1801
|
-
...validation,
|
|
1802
|
-
ok: missing.length === 0 && invalid.length === 0,
|
|
1803
|
-
entries,
|
|
1804
|
-
required,
|
|
1805
|
-
missing,
|
|
1806
|
-
invalid
|
|
1807
|
-
};
|
|
1808
|
-
}
|
|
1809
|
-
function doesEntryApplyToWorkflowPlane(entry, plane) {
|
|
1810
|
-
const targets = new Set(entry.targets ?? []);
|
|
1811
|
-
const hasProcessingTarget = targets.has("railway-secret") || targets.has("railway-var");
|
|
1812
|
-
const hasWebTarget = targets.has("cloudflare-secret") || targets.has("cloudflare-var") || targets.has("local-cloudflare");
|
|
1813
|
-
const hasWorkflowTarget = targets.has("github-secret") || targets.has("github-variable");
|
|
1814
|
-
if (plane === "web") {
|
|
1815
|
-
return !hasProcessingTarget || hasWebTarget || hasWorkflowTarget;
|
|
1816
|
-
}
|
|
1817
|
-
if (plane === "processing") {
|
|
1818
|
-
return !hasWebTarget || hasProcessingTarget || hasWorkflowTarget;
|
|
1819
|
-
}
|
|
1820
|
-
return true;
|
|
1821
|
-
}
|
|
1822
|
-
function assertTreeseedCommandEnvironment({ tenantRoot, scope, purpose }) {
|
|
1823
|
-
const report = validateTreeseedCommandEnvironment({ tenantRoot, scope, purpose });
|
|
1824
|
-
if (report.validation.ok) {
|
|
1825
|
-
return report;
|
|
1826
|
-
}
|
|
1827
|
-
const lines = [
|
|
1828
|
-
`Treeseed environment is not ready for ${purpose} (${scope}).`,
|
|
1829
|
-
"Run `treeseed config` to fill in the missing values, or export them in the current shell."
|
|
1830
|
-
];
|
|
1831
|
-
for (const problem of [...report.validation.missing, ...report.validation.invalid]) {
|
|
1832
|
-
lines.push(`- ${problem.message}`);
|
|
1833
|
-
}
|
|
1834
|
-
const error = new Error(lines.join("\n"));
|
|
1835
|
-
error.kind = report.validation.missing.length > 0 ? "missing_config" : "invalid_config";
|
|
1836
|
-
error.details = report.validation;
|
|
1837
|
-
throw error;
|
|
1838
|
-
}
|
|
1839
|
-
function runGh(args, { cwd, planOnly = false, input, env } = {}) {
|
|
1840
|
-
if (planOnly) {
|
|
1841
|
-
return { status: 0, stdout: "", stderr: "" };
|
|
1842
|
-
}
|
|
1843
|
-
const gh = resolveTreeseedToolBinary("gh", { env });
|
|
1844
|
-
if (!gh) {
|
|
1845
|
-
throw new Error("GitHub CLI `gh` is not installed.");
|
|
1846
|
-
}
|
|
1847
|
-
const result = spawnSync(gh, args, {
|
|
1848
|
-
cwd,
|
|
1849
|
-
stdio: input !== void 0 ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"],
|
|
1850
|
-
encoding: "utf8",
|
|
1851
|
-
input,
|
|
1852
|
-
timeout: 15e3,
|
|
1853
|
-
env: createTreeseedManagedToolEnv({ ...process.env, ...env ?? {} })
|
|
1854
|
-
});
|
|
1855
|
-
if (result.error?.code === "ETIMEDOUT") {
|
|
1856
|
-
throw new Error(`gh ${args.join(" ")} timed out`);
|
|
1857
|
-
}
|
|
1858
|
-
if (result.status !== 0) {
|
|
1859
|
-
throw new Error(result.stderr?.trim() || result.stdout?.trim() || `gh ${args.join(" ")} failed`);
|
|
1860
|
-
}
|
|
1861
|
-
return result;
|
|
1862
|
-
}
|
|
1863
|
-
function commandAvailable(command) {
|
|
1864
|
-
if (command === "gh" || command === "wrangler" || command === "railway" || command === "copilot") {
|
|
1865
|
-
return Boolean(resolveTreeseedToolBinary(command));
|
|
1866
|
-
}
|
|
1867
|
-
const result = spawnSync("bash", ["-lc", `command -v ${command}`], {
|
|
1868
|
-
stdio: "pipe",
|
|
1869
|
-
encoding: "utf8"
|
|
1870
|
-
});
|
|
1871
|
-
return result.status === 0;
|
|
1872
|
-
}
|
|
1873
|
-
function checkCommand(command, args, { cwd, env } = {}) {
|
|
1874
|
-
const result = spawnSync(command, args, {
|
|
1875
|
-
cwd,
|
|
1876
|
-
stdio: "pipe",
|
|
1877
|
-
encoding: "utf8",
|
|
1878
|
-
env: { ...process.env, ...env ?? {} },
|
|
1879
|
-
timeout: CLI_CHECK_TIMEOUT_MS
|
|
1880
|
-
});
|
|
1881
|
-
const timedOut = result.error && "code" in result.error && result.error.code === "ETIMEDOUT";
|
|
1882
|
-
const detail = timedOut ? `Command timed out after ${CLI_CHECK_TIMEOUT_MS}ms: ${command} ${args.join(" ")}` : `${result.stderr ?? ""}
|
|
1883
|
-
${result.stdout ?? ""}`.trim();
|
|
1884
|
-
return {
|
|
1885
|
-
ok: result.status === 0,
|
|
1886
|
-
status: result.status ?? 1,
|
|
1887
|
-
stdout: result.stdout?.trim() ?? "",
|
|
1888
|
-
stderr: result.stderr?.trim() ?? "",
|
|
1889
|
-
detail
|
|
1890
|
-
};
|
|
1891
|
-
}
|
|
1892
|
-
function toolStatus(name, available, detail, extra = {}) {
|
|
1893
|
-
return {
|
|
1894
|
-
name,
|
|
1895
|
-
available,
|
|
1896
|
-
detail,
|
|
1897
|
-
...extra
|
|
1898
|
-
};
|
|
1899
|
-
}
|
|
1900
|
-
function ensureTreeseedActVerificationTooling({ tenantRoot = process.cwd(), installIfMissing = true, env = process.env, write } = {}) {
|
|
1901
|
-
const managedEnv = createTreeseedManagedToolEnv(env);
|
|
1902
|
-
const ghBinary = resolveTreeseedToolBinary("gh", { env: managedEnv });
|
|
1903
|
-
const githubCli = !ghBinary ? toolStatus("githubCli", false, "GitHub CLI `gh` is not installed.") : (() => {
|
|
1904
|
-
const check = checkCommand(ghBinary, ["--version"], { cwd: tenantRoot, env: managedEnv });
|
|
1905
|
-
return toolStatus("githubCli", check.ok, check.ok ? check.stdout.split("\n")[0] ?? "GitHub CLI detected." : check.detail || "GitHub CLI check failed.");
|
|
1906
|
-
})();
|
|
1907
|
-
let ghActExtension = toolStatus("ghActExtension", false, "GitHub CLI extension `gh-act` is not installed.", {
|
|
1908
|
-
attemptedInstall: false,
|
|
1909
|
-
installedDuringConfig: false
|
|
1910
|
-
});
|
|
1911
|
-
const wranglerCli = (() => {
|
|
1912
|
-
try {
|
|
1913
|
-
const wranglerCheck = checkCommand(process.execPath, [resolveWranglerBin(), "--version"], { cwd: tenantRoot, env });
|
|
1914
|
-
return toolStatus(
|
|
1915
|
-
"wranglerCli",
|
|
1916
|
-
wranglerCheck.ok,
|
|
1917
|
-
wranglerCheck.ok ? wranglerCheck.stdout.split("\n")[0] ?? "Wrangler CLI detected." : wranglerCheck.detail || "Wrangler CLI is unavailable."
|
|
1918
|
-
);
|
|
1919
|
-
} catch (error) {
|
|
1920
|
-
return toolStatus(
|
|
1921
|
-
"wranglerCli",
|
|
1922
|
-
false,
|
|
1923
|
-
error instanceof Error && error.message ? error.message : "Wrangler CLI is unavailable."
|
|
1924
|
-
);
|
|
1925
|
-
}
|
|
1926
|
-
})();
|
|
1927
|
-
const railwayCommand = resolveTreeseedToolCommand("railway", { env });
|
|
1928
|
-
const railwayCheck = railwayCommand ? checkCommand(railwayCommand.command, [...railwayCommand.argsPrefix, "--version"], { cwd: tenantRoot, env }) : { ok: false, stdout: "", detail: "Railway CLI is unavailable." };
|
|
1929
|
-
const railwayCli = toolStatus(
|
|
1930
|
-
"railwayCli",
|
|
1931
|
-
railwayCheck.ok,
|
|
1932
|
-
railwayCheck.ok ? railwayCheck.stdout.split("\n")[0] ?? "Railway CLI detected." : railwayCheck.detail || "Railway CLI is unavailable."
|
|
1933
|
-
);
|
|
1934
|
-
if (githubCli.available && ghBinary) {
|
|
1935
|
-
const check = checkCommand(ghBinary, ["act", "--version"], { cwd: tenantRoot, env: managedEnv });
|
|
1936
|
-
if (check.ok) {
|
|
1937
|
-
ghActExtension = toolStatus("ghActExtension", true, check.stdout.split("\n")[0] ?? "gh-act is installed.", {
|
|
1938
|
-
attemptedInstall: false,
|
|
1939
|
-
installedDuringConfig: false
|
|
1940
|
-
});
|
|
1941
|
-
} else if (installIfMissing && commandAvailable("docker")) {
|
|
1942
|
-
write?.("Installing GitHub CLI extension `gh-act`...");
|
|
1943
|
-
const install = checkCommand(ghBinary, ["extension", "install", "https://github.com/nektos/gh-act"], { cwd: tenantRoot, env: managedEnv });
|
|
1944
|
-
const postInstall = checkCommand(ghBinary, ["act", "--version"], { cwd: tenantRoot, env: managedEnv });
|
|
1945
|
-
ghActExtension = toolStatus(
|
|
1946
|
-
"ghActExtension",
|
|
1947
|
-
postInstall.ok,
|
|
1948
|
-
postInstall.ok ? postInstall.stdout.split("\n")[0] ?? "gh-act is installed." : install.detail || postInstall.detail || "Unable to install the gh-act extension.",
|
|
1949
|
-
{
|
|
1950
|
-
attemptedInstall: true,
|
|
1951
|
-
installedDuringConfig: postInstall.ok,
|
|
1952
|
-
installStatus: install.status
|
|
1953
|
-
}
|
|
1954
|
-
);
|
|
1955
|
-
} else if (installIfMissing) {
|
|
1956
|
-
ghActExtension = toolStatus("ghActExtension", false, "Docker is not on PATH, so gh-act installation was skipped.", {
|
|
1957
|
-
attemptedInstall: false,
|
|
1958
|
-
installedDuringConfig: false
|
|
1959
|
-
});
|
|
1960
|
-
} else {
|
|
1961
|
-
ghActExtension = toolStatus("ghActExtension", false, check.detail || "GitHub CLI extension `gh-act` is not installed.", {
|
|
1962
|
-
attemptedInstall: false,
|
|
1963
|
-
installedDuringConfig: false
|
|
1964
|
-
});
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
const dockerCheck = checkCommand("docker", ["info"], { cwd: tenantRoot, env });
|
|
1968
|
-
const dockerDaemon = toolStatus(
|
|
1969
|
-
"dockerDaemon",
|
|
1970
|
-
dockerCheck.ok,
|
|
1971
|
-
dockerCheck.ok ? dockerCheck.stdout.split("\n")[0] ?? "Docker daemon is available." : dockerCheck.detail || "Docker daemon is unavailable."
|
|
1972
|
-
);
|
|
1973
|
-
const remediation = [];
|
|
1974
|
-
if (!githubCli.available) {
|
|
1975
|
-
remediation.push("Install GitHub CLI from https://cli.github.com/ and rerun `treeseed config`.");
|
|
1976
|
-
}
|
|
1977
|
-
if (githubCli.available && !ghActExtension.available) {
|
|
1978
|
-
remediation.push("Run `gh extension install https://github.com/nektos/gh-act` and rerun `treeseed config`.");
|
|
1979
|
-
}
|
|
1980
|
-
if (!dockerDaemon.available) {
|
|
1981
|
-
remediation.push("Start Docker Desktop or another local Docker daemon, then rerun `treeseed config`.");
|
|
1982
|
-
}
|
|
1983
|
-
if (!wranglerCli.available) {
|
|
1984
|
-
remediation.push("Install Wrangler or ensure the packaged Wrangler dependency is runnable, then rerun `treeseed config`.");
|
|
1985
|
-
}
|
|
1986
|
-
if (!railwayCli.available) {
|
|
1987
|
-
remediation.push("Install Railway CLI if you plan to manage Railway services from this machine.");
|
|
1988
|
-
}
|
|
1989
|
-
return {
|
|
1990
|
-
githubCli,
|
|
1991
|
-
ghActExtension,
|
|
1992
|
-
dockerDaemon,
|
|
1993
|
-
wranglerCli,
|
|
1994
|
-
railwayCli,
|
|
1995
|
-
actVerificationReady: githubCli.available && ghActExtension.available && dockerDaemon.available,
|
|
1996
|
-
remediation
|
|
1997
|
-
};
|
|
1998
|
-
}
|
|
1999
|
-
function formatCheckOutput(result) {
|
|
2000
|
-
return `${result.stderr ?? ""}
|
|
2001
|
-
${result.stdout ?? ""}`.trim();
|
|
2002
|
-
}
|
|
2003
|
-
function providerConnectionResult(provider, ready, detail, extra = {}) {
|
|
2004
|
-
return {
|
|
2005
|
-
provider,
|
|
2006
|
-
ready,
|
|
2007
|
-
detail,
|
|
2008
|
-
...extra
|
|
2009
|
-
};
|
|
2010
|
-
}
|
|
2011
|
-
function isTransientProviderConnectionError(detail) {
|
|
2012
|
-
return /fetch failed|failed to fetch|timed out|etimedout|econnreset|enetunreach|temporarily unavailable|aborted|api check failed|rate.?limit|too many requests|429/iu.test(detail || "");
|
|
2013
|
-
}
|
|
2014
|
-
function checkGitHubConnection({ tenantRoot, env }) {
|
|
2015
|
-
const identityMode = env.TREESEED_GITHUB_IDENTITY_MODE === "account" ? "account" : "repository";
|
|
2016
|
-
const repository = identityMode === "repository" ? maybeResolveGitHubRepositorySlug(tenantRoot) : null;
|
|
2017
|
-
const credential = repository ? resolveGitHubCredentialForRepository(repository, { values: env, env }) : null;
|
|
2018
|
-
const token = credential?.token ?? resolveTreeseedGitHubToken(env);
|
|
2019
|
-
if (!token) {
|
|
2020
|
-
return providerConnectionResult(
|
|
2021
|
-
"github",
|
|
2022
|
-
false,
|
|
2023
|
-
credential ? `${credential.envName} or TREESEED_GITHUB_TOKEN is not configured.` : "TREESEED_GITHUB_TOKEN is not configured.",
|
|
2024
|
-
{ skipped: true }
|
|
2025
|
-
);
|
|
2026
|
-
}
|
|
2027
|
-
const gh = resolveTreeseedToolBinary("gh", { env });
|
|
2028
|
-
if (!gh) {
|
|
2029
|
-
return providerConnectionResult("github", false, "GitHub CLI `gh` is not installed.");
|
|
2030
|
-
}
|
|
2031
|
-
const owner = typeof env.TREESEED_HOSTED_HUBS_GITHUB_OWNER === "string" ? env.TREESEED_HOSTED_HUBS_GITHUB_OWNER.trim() : "";
|
|
2032
|
-
const toolEnv = createTreeseedManagedToolEnv({ ...process.env, ...env, TREESEED_GITHUB_TOKEN: token });
|
|
2033
|
-
const commandCandidates = repository ? [{
|
|
2034
|
-
args: ["repo", "view", repository, "--json", "nameWithOwner", "--jq", ".nameWithOwner"],
|
|
2035
|
-
successMessage: (resolved) => `GitHub token can access ${resolved || repository}.`
|
|
2036
|
-
}] : owner ? [
|
|
2037
|
-
{
|
|
2038
|
-
args: ["api", `orgs/${owner}`, "--jq", ".login"],
|
|
2039
|
-
successMessage: (resolved) => `GitHub token can access organization ${resolved || owner}.`,
|
|
2040
|
-
optional: true
|
|
2041
|
-
},
|
|
2042
|
-
{
|
|
2043
|
-
args: ["api", `users/${owner}`, "--jq", ".login"],
|
|
2044
|
-
successMessage: (resolved) => `GitHub token can access user ${resolved || owner}.`,
|
|
2045
|
-
optional: true
|
|
2046
|
-
}
|
|
2047
|
-
] : [
|
|
2048
|
-
{
|
|
2049
|
-
args: ["api", "user", "--jq", ".login"],
|
|
2050
|
-
successMessage: (resolved) => resolved ? `Authenticated as ${resolved}.` : "GitHub API check succeeded."
|
|
2051
|
-
}
|
|
2052
|
-
];
|
|
2053
|
-
let lastDetail = "";
|
|
2054
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
2055
|
-
for (const candidate of commandCandidates) {
|
|
2056
|
-
const result = spawnSync(gh, candidate.args, {
|
|
2057
|
-
cwd: tenantRoot,
|
|
2058
|
-
stdio: "pipe",
|
|
2059
|
-
encoding: "utf8",
|
|
2060
|
-
env: toolEnv,
|
|
2061
|
-
timeout: CLI_CHECK_TIMEOUT_MS
|
|
2062
|
-
});
|
|
2063
|
-
if (result.status === 0) {
|
|
2064
|
-
return providerConnectionResult("github", true, candidate.successMessage(result.stdout.trim()));
|
|
2065
|
-
}
|
|
2066
|
-
lastDetail = formatCheckOutput(result) || "GitHub API check failed.";
|
|
2067
|
-
if (candidate.optional && !isTransientProviderConnectionError(lastDetail)) {
|
|
2068
|
-
continue;
|
|
2069
|
-
}
|
|
2070
|
-
break;
|
|
2071
|
-
}
|
|
2072
|
-
if (attempt >= 2 || !isTransientProviderConnectionError(lastDetail)) {
|
|
2073
|
-
return providerConnectionResult("github", false, lastDetail || "GitHub API check failed.");
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
return providerConnectionResult("github", false, lastDetail || "GitHub API check failed.");
|
|
2077
|
-
}
|
|
2078
|
-
function checkCloudflareConnection({ tenantRoot, env }) {
|
|
2079
|
-
if (!env.TREESEED_CLOUDFLARE_API_TOKEN) {
|
|
2080
|
-
return providerConnectionResult("cloudflare", false, "TREESEED_CLOUDFLARE_API_TOKEN is not configured.", { skipped: true });
|
|
2081
|
-
}
|
|
2082
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
2083
|
-
try {
|
|
2084
|
-
const result = spawnSync(process.execPath, [resolveWranglerBin(), "whoami"], {
|
|
2085
|
-
cwd: tenantRoot,
|
|
2086
|
-
stdio: "pipe",
|
|
2087
|
-
encoding: "utf8",
|
|
2088
|
-
env: createTreeseedManagedToolEnv({ ...process.env, ...env }),
|
|
2089
|
-
timeout: CLI_CHECK_TIMEOUT_MS
|
|
2090
|
-
});
|
|
2091
|
-
if (result.status === 0) {
|
|
2092
|
-
return providerConnectionResult("cloudflare", true, "Wrangler authenticated with TREESEED_CLOUDFLARE_API_TOKEN.");
|
|
2093
|
-
}
|
|
2094
|
-
const detail = formatCheckOutput(result) || "Cloudflare Wrangler check failed.";
|
|
2095
|
-
if (attempt >= 2 || !isTransientProviderConnectionError(detail)) {
|
|
2096
|
-
return providerConnectionResult("cloudflare", false, detail);
|
|
2097
|
-
}
|
|
2098
|
-
} catch (error) {
|
|
2099
|
-
const detail = error instanceof Error ? error.message : "Cloudflare Wrangler check failed.";
|
|
2100
|
-
if (attempt >= 2 || !isTransientProviderConnectionError(detail)) {
|
|
2101
|
-
return providerConnectionResult("cloudflare", false, detail);
|
|
2102
|
-
}
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2105
|
-
return providerConnectionResult(
|
|
2106
|
-
"cloudflare",
|
|
2107
|
-
false,
|
|
2108
|
-
"Cloudflare connectivity preflight hit transient fetch failures; bootstrap will continue and rely on live reconcile verification.",
|
|
2109
|
-
{ skipped: true, warning: true, transient: true }
|
|
2110
|
-
);
|
|
2111
|
-
}
|
|
2112
|
-
async function checkRailwayConnection({ tenantRoot, env }) {
|
|
2113
|
-
if (!env.TREESEED_RAILWAY_API_TOKEN) {
|
|
2114
|
-
return providerConnectionResult("railway", false, "TREESEED_RAILWAY_API_TOKEN is not configured.", { skipped: true });
|
|
2115
|
-
}
|
|
2116
|
-
const workspaceName = env.TREESEED_RAILWAY_WORKSPACE || resolveRailwayWorkspace(env);
|
|
2117
|
-
const cacheKey = JSON.stringify({
|
|
2118
|
-
tenantRoot,
|
|
2119
|
-
token: env.TREESEED_RAILWAY_API_TOKEN,
|
|
2120
|
-
workspaceName
|
|
2121
|
-
});
|
|
2122
|
-
const cached = railwayConnectionCheckCache.get(cacheKey);
|
|
2123
|
-
if (cached) {
|
|
2124
|
-
return await cached;
|
|
2125
|
-
}
|
|
2126
|
-
const checkPromise = (async () => {
|
|
2127
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
2128
|
-
try {
|
|
2129
|
-
const workspace = await resolveRailwayWorkspaceContext({ env, workspace: workspaceName });
|
|
2130
|
-
return providerConnectionResult("railway", true, `Railway API token can access workspace ${workspace.name}. Project and service existence will be reconciled during bootstrap.`);
|
|
2131
|
-
} catch (error) {
|
|
2132
|
-
const detail = error instanceof Error ? error.message : "Railway API check failed.";
|
|
2133
|
-
if (/rate.?limit|too many requests|429/iu.test(detail || "")) {
|
|
2134
|
-
return providerConnectionResult(
|
|
2135
|
-
"railway",
|
|
2136
|
-
false,
|
|
2137
|
-
"Railway connectivity preflight was rate-limited; bootstrap will continue and rely on API-backed reconcile verification.",
|
|
2138
|
-
{ skipped: true, warning: true, rateLimited: true }
|
|
2139
|
-
);
|
|
2140
|
-
}
|
|
2141
|
-
if (attempt >= 2 && isTransientProviderConnectionError(detail)) {
|
|
2142
|
-
return providerConnectionResult(
|
|
2143
|
-
"railway",
|
|
2144
|
-
false,
|
|
2145
|
-
"Railway connectivity preflight hit transient API failures; bootstrap will continue and rely on API-backed reconcile verification.",
|
|
2146
|
-
{ skipped: true, warning: true, transient: true }
|
|
2147
|
-
);
|
|
2148
|
-
}
|
|
2149
|
-
if (attempt >= 2 || !isTransientProviderConnectionError(detail)) {
|
|
2150
|
-
return providerConnectionResult("railway", false, detail);
|
|
2151
|
-
}
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
return providerConnectionResult("railway", false, "Railway API check failed.");
|
|
2155
|
-
})();
|
|
2156
|
-
railwayConnectionCheckCache.set(cacheKey, checkPromise);
|
|
2157
|
-
try {
|
|
2158
|
-
return await checkPromise;
|
|
2159
|
-
} catch (error) {
|
|
2160
|
-
railwayConnectionCheckCache.delete(cacheKey);
|
|
2161
|
-
throw error;
|
|
2162
|
-
}
|
|
2163
|
-
}
|
|
2164
|
-
async function checkTreeseedProviderConnections({ tenantRoot, scope = "prod", env = process.env, valuesOverlay = {} } = {}) {
|
|
2165
|
-
const values = collectTreeseedConfigSeedValues(tenantRoot, scope, env, valuesOverlay);
|
|
2166
|
-
const passthroughValue = (key) => {
|
|
2167
|
-
const overlayValue = valuesOverlay?.[key];
|
|
2168
|
-
if (typeof overlayValue === "string" && overlayValue.trim()) {
|
|
2169
|
-
return overlayValue.trim();
|
|
2170
|
-
}
|
|
2171
|
-
const envValue = env?.[key];
|
|
2172
|
-
if (typeof envValue === "string" && envValue.trim()) {
|
|
2173
|
-
return envValue.trim();
|
|
2174
|
-
}
|
|
2175
|
-
const resolvedValue = values?.[key];
|
|
2176
|
-
return typeof resolvedValue === "string" && resolvedValue.trim() ? resolvedValue.trim() : void 0;
|
|
2177
|
-
};
|
|
2178
|
-
const githubCredentialValues = Object.fromEntries(
|
|
2179
|
-
Object.entries(values).filter(([key, value]) => key.startsWith("TREESEED_GITHUB_TOKEN_") && typeof value === "string" && value.trim())
|
|
2180
|
-
);
|
|
2181
|
-
const rawCommandEnv = {
|
|
2182
|
-
...githubCredentialValues,
|
|
2183
|
-
TREESEED_GITHUB_TOKEN: resolveTreeseedGitHubToken(values),
|
|
2184
|
-
TREESEED_GITHUB_IDENTITY_MODE: passthroughValue("TREESEED_GITHUB_IDENTITY_MODE"),
|
|
2185
|
-
TREESEED_HOSTED_HUBS_GITHUB_OWNER: passthroughValue("TREESEED_HOSTED_HUBS_GITHUB_OWNER"),
|
|
2186
|
-
TREESEED_CLOUDFLARE_API_TOKEN: values.TREESEED_CLOUDFLARE_API_TOKEN,
|
|
2187
|
-
TREESEED_CLOUDFLARE_ACCOUNT_ID: values.TREESEED_CLOUDFLARE_ACCOUNT_ID,
|
|
2188
|
-
TREESEED_RAILWAY_API_TOKEN: values.TREESEED_RAILWAY_API_TOKEN,
|
|
2189
|
-
TREESEED_RAILWAY_WORKSPACE: values.TREESEED_RAILWAY_WORKSPACE || resolveRailwayWorkspace(values)
|
|
2190
|
-
};
|
|
2191
|
-
const commandEnv = buildRailwayCommandEnv(createTreeseedManagedToolEnv(rawCommandEnv));
|
|
2192
|
-
const checks = [
|
|
2193
|
-
checkGitHubConnection({ tenantRoot, env: commandEnv }),
|
|
2194
|
-
checkCloudflareConnection({ tenantRoot, env: commandEnv })
|
|
2195
|
-
];
|
|
2196
|
-
const railwayCheck = await checkRailwayConnection({ tenantRoot, env: commandEnv });
|
|
2197
|
-
checks.push(railwayCheck);
|
|
2198
|
-
return {
|
|
2199
|
-
scope,
|
|
2200
|
-
ok: checks.every((check) => check.ready || check.skipped),
|
|
2201
|
-
checks,
|
|
2202
|
-
issues: checks.filter((check) => !check.ready && !check.skipped).map((check) => check.detail)
|
|
2203
|
-
};
|
|
2204
|
-
}
|
|
2205
|
-
function formatTreeseedProviderConnectionReport(report) {
|
|
2206
|
-
const lines = [formatConfigSectionTitle(`Provider connection checks for ${report.scope}`)];
|
|
2207
|
-
for (const check of report.checks) {
|
|
2208
|
-
const label = check.provider[0].toUpperCase() + check.provider.slice(1);
|
|
2209
|
-
const status = check.ready ? colorize("ready", "32") : check.skipped ? colorize("skipped", "33") : colorize("failed", "31");
|
|
2210
|
-
lines.push(`${label}: ${status} - ${check.detail}`);
|
|
2211
|
-
}
|
|
2212
|
-
return lines.join("\n");
|
|
2213
|
-
}
|
|
2214
|
-
function formatTreeseedProviderConnectionFailures(reports) {
|
|
2215
|
-
const failing = reports.filter((report) => report.checks.some((check) => !check.ready && !check.skipped));
|
|
2216
|
-
if (failing.length === 0) {
|
|
2217
|
-
return "";
|
|
2218
|
-
}
|
|
2219
|
-
return [
|
|
2220
|
-
"Treeseed provider connection checks failed.",
|
|
2221
|
-
...failing.map((report) => formatTreeseedProviderConnectionReport(report))
|
|
2222
|
-
].join("\n");
|
|
2223
|
-
}
|
|
2224
|
-
function writeProviderConnectionReport(write, report) {
|
|
2225
|
-
write(formatTreeseedProviderConnectionReport(report));
|
|
2226
|
-
}
|
|
2227
|
-
async function runBounded(items, limit, worker) {
|
|
2228
|
-
const concurrency = Math.max(1, Math.min(limit, items.length || 1));
|
|
2229
|
-
let nextIndex = 0;
|
|
2230
|
-
const workers = Array.from({ length: concurrency }, async () => {
|
|
2231
|
-
for (; ; ) {
|
|
2232
|
-
const index = nextIndex;
|
|
2233
|
-
nextIndex += 1;
|
|
2234
|
-
if (index >= items.length) {
|
|
2235
|
-
return;
|
|
2236
|
-
}
|
|
2237
|
-
await worker(items[index], index);
|
|
2238
|
-
}
|
|
2239
|
-
});
|
|
2240
|
-
await Promise.all(workers);
|
|
2241
|
-
}
|
|
2242
|
-
function repositorySlugFromPackageJson(root) {
|
|
2243
|
-
try {
|
|
2244
|
-
const packageJson = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"));
|
|
2245
|
-
const repository = packageJson.repository;
|
|
2246
|
-
const raw = typeof repository === "string" ? repository : repository && typeof repository === "object" && !Array.isArray(repository) ? repository.url : null;
|
|
2247
|
-
if (typeof raw !== "string") return null;
|
|
2248
|
-
const normalized = raw.trim().replace(/^git\+/u, "").replace(/^ssh:\/\/git@github\.com[:/]/u, "").replace(/^git@github\.com:/u, "").replace(/^https:\/\/github\.com\//u, "").replace(/\.git$/u, "").replace(/\/$/u, "");
|
|
2249
|
-
return /^[^/\s]+\/[^/\s]+$/u.test(normalized) ? normalized : null;
|
|
2250
|
-
} catch {
|
|
2251
|
-
return null;
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
function resolveGitHubRepositorySlugForPath(root) {
|
|
2255
|
-
try {
|
|
2256
|
-
return maybeResolveGitHubRepositorySlug(root);
|
|
2257
|
-
} catch {
|
|
2258
|
-
return null;
|
|
2259
|
-
}
|
|
2260
|
-
}
|
|
2261
|
-
function discoverGitHubEnvironmentSyncTargets(tenantRoot, explicitRepository) {
|
|
2262
|
-
const targets = /* @__PURE__ */ new Map();
|
|
2263
|
-
const add = (repository, managedHostMode) => {
|
|
2264
|
-
if (!repository || !repository.trim()) return;
|
|
2265
|
-
const normalized = repository.trim();
|
|
2266
|
-
const existing = targets.get(normalized);
|
|
2267
|
-
if (!existing || existing.managedHostMode === "managed" && managedHostMode === "direct") {
|
|
2268
|
-
targets.set(normalized, { repository: normalized, managedHostMode });
|
|
2269
|
-
}
|
|
2270
|
-
};
|
|
2271
|
-
add(explicitRepository ?? resolveGitHubRepositorySlugForPath(tenantRoot), "auto");
|
|
2272
|
-
for (const application of discoverTreeseedApplications(tenantRoot)) {
|
|
2273
|
-
const repository = resolveGitHubRepositorySlugForPath(application.root) ?? repositorySlugFromPackageJson(application.root);
|
|
2274
|
-
const managedHostMode = usesManagedHostOperationRequests(application.config) ? "managed" : "direct";
|
|
2275
|
-
add(repository, managedHostMode);
|
|
2276
|
-
}
|
|
2277
|
-
return [...targets.values()];
|
|
2278
|
-
}
|
|
2279
|
-
function withGitHubEnvironmentCredentialContext(error, repository, credential) {
|
|
2280
|
-
const message = error instanceof Error && error.message.trim() ? error.message.trim() : String(error ?? "GitHub environment sync failed.");
|
|
2281
|
-
if (credential.fallbackUsed && /authentication failed|resource not accessible|403/iu.test(message)) {
|
|
2282
|
-
return new Error(`${message} Configure ${credential.envName} with Actions environment secrets and variables permissions for ${repository}; the fallback TREESEED_GITHUB_TOKEN is not sufficient for this repository.`);
|
|
2283
|
-
}
|
|
2284
|
-
return error;
|
|
2285
|
-
}
|
|
2286
|
-
function githubConfigSyncUnitId(value) {
|
|
2287
|
-
return String(value || "unknown").replace(/[^A-Za-z0-9:._/-]+/gu, "-");
|
|
2288
|
-
}
|
|
2289
|
-
function createGitHubConfigSyncUnits({
|
|
2290
|
-
scope,
|
|
2291
|
-
repository,
|
|
2292
|
-
environment,
|
|
2293
|
-
items
|
|
2294
|
-
}) {
|
|
2295
|
-
const identity = {
|
|
2296
|
-
teamId: "config",
|
|
2297
|
-
projectId: "config",
|
|
2298
|
-
slug: "config",
|
|
2299
|
-
environment: scope,
|
|
2300
|
-
deploymentKey: `config:${scope}`,
|
|
2301
|
-
environmentKey: scope
|
|
2302
|
-
};
|
|
2303
|
-
const repositoryId = githubConfigSyncUnitId(repository);
|
|
2304
|
-
const environmentId = githubConfigSyncUnitId(environment);
|
|
2305
|
-
const environmentUnitId = `github-environment:config:${scope}:${repositoryId}:${environmentId}`;
|
|
2306
|
-
const target = { kind: "persistent", scope };
|
|
2307
|
-
const environmentUnit = {
|
|
2308
|
-
unitId: environmentUnitId,
|
|
2309
|
-
unitType: "github-environment",
|
|
2310
|
-
provider: "github",
|
|
2311
|
-
identity,
|
|
2312
|
-
target,
|
|
2313
|
-
logicalName: `${repository} ${environment}`,
|
|
2314
|
-
dependencies: [],
|
|
2315
|
-
spec: {
|
|
2316
|
-
repository,
|
|
2317
|
-
environment
|
|
2318
|
-
},
|
|
2319
|
-
secrets: {},
|
|
2320
|
-
metadata: {
|
|
2321
|
-
source: { type: "config-sync" },
|
|
2322
|
-
resourceKind: "github-environment"
|
|
2323
|
-
}
|
|
2324
|
-
};
|
|
2325
|
-
const bindingUnits = items.flatMap((item) => {
|
|
2326
|
-
if (item.kind === "secret") {
|
|
2327
|
-
return [{
|
|
2328
|
-
unitId: `github-secret-binding:config:${scope}:${repositoryId}:${environmentId}:${githubConfigSyncUnitId(item.name)}`,
|
|
2329
|
-
unitType: "github-secret-binding",
|
|
2330
|
-
provider: "github",
|
|
2331
|
-
identity,
|
|
2332
|
-
target,
|
|
2333
|
-
logicalName: `${repository} ${environment} ${item.name}`,
|
|
2334
|
-
dependencies: [environmentUnitId],
|
|
2335
|
-
spec: {
|
|
2336
|
-
repository,
|
|
2337
|
-
environment,
|
|
2338
|
-
secretName: item.name,
|
|
2339
|
-
envName: item.name
|
|
2340
|
-
},
|
|
2341
|
-
secrets: {},
|
|
2342
|
-
metadata: {
|
|
2343
|
-
source: { type: "config-sync" },
|
|
2344
|
-
resourceKind: "github-secret-binding"
|
|
2345
|
-
}
|
|
2346
|
-
}];
|
|
2347
|
-
}
|
|
2348
|
-
if (item.kind === "variable") {
|
|
2349
|
-
return [{
|
|
2350
|
-
unitId: `github-variable-binding:config:${scope}:${repositoryId}:${environmentId}:${githubConfigSyncUnitId(item.name)}`,
|
|
2351
|
-
unitType: "github-variable-binding",
|
|
2352
|
-
provider: "github",
|
|
2353
|
-
identity,
|
|
2354
|
-
target,
|
|
2355
|
-
logicalName: `${repository} ${environment} ${item.name}`,
|
|
2356
|
-
dependencies: [environmentUnitId],
|
|
2357
|
-
spec: {
|
|
2358
|
-
repository,
|
|
2359
|
-
environment,
|
|
2360
|
-
variableName: item.name,
|
|
2361
|
-
envName: item.name
|
|
2362
|
-
},
|
|
2363
|
-
secrets: {},
|
|
2364
|
-
metadata: {
|
|
2365
|
-
source: { type: "config-sync" },
|
|
2366
|
-
resourceKind: "github-variable-binding"
|
|
2367
|
-
}
|
|
2368
|
-
}];
|
|
2369
|
-
}
|
|
2370
|
-
return [];
|
|
2371
|
-
});
|
|
2372
|
-
return bindingUnits.length > 0 ? [environmentUnit, ...bindingUnits] : [];
|
|
2373
|
-
}
|
|
2374
|
-
async function syncTreeseedGitHubEnvironment({
|
|
2375
|
-
tenantRoot,
|
|
2376
|
-
scope = "prod",
|
|
2377
|
-
planOnly = false,
|
|
2378
|
-
repository: repositoryInput,
|
|
2379
|
-
valuesOverlay = {},
|
|
2380
|
-
entryIds,
|
|
2381
|
-
managedHostMode = "auto",
|
|
2382
|
-
execution = "parallel",
|
|
2383
|
-
concurrency = 4,
|
|
2384
|
-
onProgress
|
|
2385
|
-
}) {
|
|
2386
|
-
const repository = repositoryInput ?? maybeResolveGitHubRepositorySlug(tenantRoot);
|
|
2387
|
-
if (!repository) {
|
|
2388
|
-
throw new Error("Unable to determine the GitHub repository from the origin remote.");
|
|
2389
|
-
}
|
|
2390
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
2391
|
-
const values = {
|
|
2392
|
-
...resolveTreeseedMachineEnvironmentValues(tenantRoot, scope),
|
|
2393
|
-
...nonEmptyEnvironmentValues(valuesOverlay)
|
|
2394
|
-
};
|
|
2395
|
-
const deployConfig = loadCliDeployConfig(tenantRoot);
|
|
2396
|
-
const managedBoundary = managedHostMode === "managed" || managedHostMode === "auto" && usesManagedHostOperationRequests(deployConfig);
|
|
2397
|
-
const allowed = managedBoundary ? filterManagedHostGitHubEnvironment({
|
|
2398
|
-
secrets: registry.entries.filter((entry) => entry.scopes.includes(scope) && entry.targets.includes("github-secret")).map((entry) => entry.id),
|
|
2399
|
-
variables: registry.entries.filter((entry) => entry.scopes.includes(scope) && entry.targets.includes("github-variable")).map((entry) => entry.id)
|
|
2400
|
-
}) : null;
|
|
2401
|
-
const allowedSecrets = allowed ? new Set(allowed.secrets) : null;
|
|
2402
|
-
const allowedVariables = allowed ? new Set(allowed.variables) : null;
|
|
2403
|
-
const entryFilter = Array.isArray(entryIds) && entryIds.length > 0 ? new Set(entryIds) : null;
|
|
2404
|
-
const relevant = registry.entries.filter((entry) => {
|
|
2405
|
-
if (!entry.scopes.includes(scope)) return false;
|
|
2406
|
-
if (entryFilter && !entryFilter.has(entry.id)) return false;
|
|
2407
|
-
if (!managedBoundary) return true;
|
|
2408
|
-
if (entry.sensitivity === "secret") {
|
|
2409
|
-
return Boolean(entry.targets.includes("github-secret") && allowedSecrets?.has(entry.id));
|
|
2410
|
-
}
|
|
2411
|
-
return Boolean(entry.targets.includes("github-variable") && allowedVariables?.has(entry.id));
|
|
2412
|
-
});
|
|
2413
|
-
const credential = resolveGitHubCredentialForRepository(repository, { values, env: process.env });
|
|
2414
|
-
const environment = scope === "prod" ? "production" : scope;
|
|
2415
|
-
const progress = (message, stream = "stdout") => onProgress?.(message, stream);
|
|
2416
|
-
progress(`[${scope}][github][sync] Loading existing GitHub secrets and variables...`);
|
|
2417
|
-
const [secretNames, variableNames] = [/* @__PURE__ */ new Set(), /* @__PURE__ */ new Set()];
|
|
2418
|
-
const synced = {
|
|
2419
|
-
secrets: [],
|
|
2420
|
-
variables: []
|
|
2421
|
-
};
|
|
2422
|
-
const items = [];
|
|
2423
|
-
for (const entry of relevant) {
|
|
2424
|
-
const value = values[entry.id];
|
|
2425
|
-
if (!value) {
|
|
2426
|
-
continue;
|
|
2427
|
-
}
|
|
2428
|
-
if (entry.sensitivity === "secret") {
|
|
2429
|
-
items.push({ kind: "secret", name: entry.id, value, existed: secretNames.has(entry.id) });
|
|
2430
|
-
} else {
|
|
2431
|
-
items.push({ kind: "variable", name: entry.id, value, existed: variableNames.has(entry.id) });
|
|
2432
|
-
}
|
|
2433
|
-
}
|
|
2434
|
-
let completed = 0;
|
|
2435
|
-
const total = items.length;
|
|
2436
|
-
if (!planOnly) {
|
|
2437
|
-
const units = createGitHubConfigSyncUnits({ scope, repository, environment, items });
|
|
2438
|
-
if (units.length === 0) {
|
|
2439
|
-
progress(`[${scope}][github][sync] Complete: 0 secrets, 0 variables, 0 total.`);
|
|
2440
|
-
return {
|
|
2441
|
-
repository,
|
|
2442
|
-
scope,
|
|
2443
|
-
environment,
|
|
2444
|
-
secrets: synced.secrets,
|
|
2445
|
-
variables: synced.variables
|
|
2446
|
-
};
|
|
2447
|
-
}
|
|
2448
|
-
try {
|
|
2449
|
-
const result = await reconcileTreeseedTarget({
|
|
2450
|
-
tenantRoot,
|
|
2451
|
-
target: createPersistentDeployTarget(scope),
|
|
2452
|
-
env: {
|
|
2453
|
-
...process.env,
|
|
2454
|
-
...values,
|
|
2455
|
-
...valuesOverlay
|
|
2456
|
-
},
|
|
2457
|
-
units,
|
|
2458
|
-
write: (line) => progress(`[${scope}][github][reconcile] ${line}`)
|
|
2459
|
-
});
|
|
2460
|
-
const plansById = new Map(result.plans.map((entry) => [entry.unit.unitId, entry]));
|
|
2461
|
-
for (const unit of units) {
|
|
2462
|
-
if (unit.unitType !== "github-secret-binding" && unit.unitType !== "github-variable-binding") continue;
|
|
2463
|
-
const planEntry = plansById.get(unit.unitId);
|
|
2464
|
-
const name = String(unit.spec.secretName ?? unit.spec.variableName ?? unit.spec.envName ?? "");
|
|
2465
|
-
if (!name) continue;
|
|
2466
|
-
const existed = Boolean(planEntry?.observed.exists);
|
|
2467
|
-
if (unit.unitType === "github-secret-binding") {
|
|
2468
|
-
synced.secrets.push({ name, existed });
|
|
2469
|
-
} else {
|
|
2470
|
-
synced.variables.push({ name, existed });
|
|
2471
|
-
}
|
|
2472
|
-
completed += 1;
|
|
2473
|
-
progress(`[${scope}][github][${unit.unitType === "github-secret-binding" ? "secret" : "variable"}] reconciled ${name} (${completed}/${total})`);
|
|
2474
|
-
}
|
|
2475
|
-
} catch (error) {
|
|
2476
|
-
throw withGitHubEnvironmentCredentialContext(error, repository, credential);
|
|
2477
|
-
}
|
|
2478
|
-
progress(`[${scope}][github][sync] Complete: ${synced.secrets.length} secrets, ${synced.variables.length} variables, ${total} total.`);
|
|
2479
|
-
return {
|
|
2480
|
-
repository,
|
|
2481
|
-
scope,
|
|
2482
|
-
environment,
|
|
2483
|
-
secrets: synced.secrets,
|
|
2484
|
-
variables: synced.variables
|
|
2485
|
-
};
|
|
2486
|
-
}
|
|
2487
|
-
progress(`[${scope}][github][sync] Syncing GitHub environment ${environment}: 0/${total} items...`);
|
|
2488
|
-
const limit = execution === "sequential" ? 1 : concurrency;
|
|
2489
|
-
await runBounded(items, limit, async (item) => {
|
|
2490
|
-
completed += 1;
|
|
2491
|
-
const action = item.existed ? "would update" : "would create";
|
|
2492
|
-
progress(`[${scope}][github][${item.kind}] ${action} ${item.name} (${completed}/${total})`);
|
|
2493
|
-
if (item.kind === "secret") {
|
|
2494
|
-
synced.secrets.push({ name: item.name, existed: item.existed });
|
|
2495
|
-
} else {
|
|
2496
|
-
synced.variables.push({ name: item.name, existed: item.existed });
|
|
2497
|
-
}
|
|
2498
|
-
});
|
|
2499
|
-
progress(`[${scope}][github][sync] Complete: ${synced.secrets.length} secrets, ${synced.variables.length} variables, ${total} total.`);
|
|
2500
|
-
return {
|
|
2501
|
-
repository,
|
|
2502
|
-
scope,
|
|
2503
|
-
environment,
|
|
2504
|
-
credential: {
|
|
2505
|
-
repository: credential.repository,
|
|
2506
|
-
envName: credential.envName,
|
|
2507
|
-
configured: credential.configured,
|
|
2508
|
-
source: credential.source,
|
|
2509
|
-
fallbackUsed: credential.fallbackUsed
|
|
2510
|
-
},
|
|
2511
|
-
entryIds: entryFilter ? [...entryFilter] : void 0,
|
|
2512
|
-
...synced
|
|
2513
|
-
};
|
|
2514
|
-
}
|
|
2515
|
-
function syncTreeseedCloudflareEnvironment({
|
|
2516
|
-
tenantRoot,
|
|
2517
|
-
scope = "prod",
|
|
2518
|
-
planOnly = false,
|
|
2519
|
-
valuesOverlay = {},
|
|
2520
|
-
entryIds,
|
|
2521
|
-
onProgress
|
|
2522
|
-
}) {
|
|
2523
|
-
const values = {
|
|
2524
|
-
...resolveTreeseedMachineEnvironmentValues(tenantRoot, scope),
|
|
2525
|
-
...nonEmptyEnvironmentValues(valuesOverlay)
|
|
2526
|
-
};
|
|
2527
|
-
const target = createPersistentDeployTarget(scope);
|
|
2528
|
-
const progress = (message, stream = "stdout") => onProgress?.(message, stream);
|
|
2529
|
-
for (const [key, value] of Object.entries(values)) {
|
|
2530
|
-
if (typeof value === "string" && value.length > 0) {
|
|
2531
|
-
process.env[key] = value;
|
|
2532
|
-
}
|
|
2533
|
-
}
|
|
2534
|
-
progress(`[${scope}][cloudflare][config] Generating Wrangler config...`);
|
|
2535
|
-
const { wranglerPath } = ensureGeneratedWranglerConfig(tenantRoot, { target });
|
|
2536
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
2537
|
-
const entryFilter = Array.isArray(entryIds) && entryIds.length > 0 ? new Set(entryIds) : null;
|
|
2538
|
-
const cloudflareSecrets = Object.fromEntries(registry.entries.filter((entry) => entry.scopes.includes(scope) && entry.targets.includes("cloudflare-secret") && (!entryFilter || entryFilter.has(entry.id))).map((entry) => [entry.id, values[entry.id]]).filter(([, value]) => typeof value === "string" && value.length > 0));
|
|
2539
|
-
progress(`[${scope}][cloudflare][sync] Syncing Cloudflare secrets...`);
|
|
2540
|
-
const syncedSecrets = syncCloudflareSecrets(tenantRoot, {
|
|
2541
|
-
planOnly,
|
|
2542
|
-
target,
|
|
2543
|
-
extraSecrets: cloudflareSecrets,
|
|
2544
|
-
entryIds
|
|
2545
|
-
});
|
|
2546
|
-
const cloudflareVars = registry.entries.filter((entry) => entry.scopes.includes(scope) && entry.targets.includes("cloudflare-var") && (!entryFilter || entryFilter.has(entry.id))).map((entry) => entry.id).filter((key) => typeof values[key] === "string" && values[key].length > 0);
|
|
2547
|
-
progress(`[${scope}][cloudflare][sync] Complete: ${syncedSecrets.length} secrets, ${cloudflareVars.length} vars.`);
|
|
2548
|
-
return {
|
|
2549
|
-
scope,
|
|
2550
|
-
target,
|
|
2551
|
-
wranglerPath,
|
|
2552
|
-
entryIds: entryFilter ? [...entryFilter] : void 0,
|
|
2553
|
-
secrets: syncedSecrets,
|
|
2554
|
-
varsManagedByWranglerConfig: cloudflareVars
|
|
2555
|
-
};
|
|
2556
|
-
}
|
|
2557
|
-
function environmentEntryTargetsService(entry, serviceKey) {
|
|
2558
|
-
const targets = Array.isArray(entry.serviceTargets) ? entry.serviceTargets.map((value) => String(value).trim()).filter(Boolean) : [];
|
|
2559
|
-
return targets.length === 0 || targets.includes(serviceKey);
|
|
2560
|
-
}
|
|
2561
|
-
function railwayEnvironmentEntryIdsForService(registry, values, scope, target, serviceKey, entryFilter = null) {
|
|
2562
|
-
return registry.entries.filter((entry) => entry.scopes.includes(scope) && entry.targets.includes(target) && (!entryFilter || entryFilter.has(entry.id)) && environmentEntryTargetsService(entry, serviceKey)).map((entry) => entry.id).filter((key) => typeof values[key] === "string" && values[key].length > 0);
|
|
2563
|
-
}
|
|
2564
|
-
async function syncTreeseedRailwayEnvironment({
|
|
2565
|
-
tenantRoot,
|
|
2566
|
-
scope = "prod",
|
|
2567
|
-
planOnly = false,
|
|
2568
|
-
valuesOverlay = {},
|
|
2569
|
-
entryIds,
|
|
2570
|
-
onProgress
|
|
2571
|
-
}) {
|
|
2572
|
-
const config = syncManagedServiceSettingsFromDeployConfig(tenantRoot);
|
|
2573
|
-
const values = {
|
|
2574
|
-
...resolveTreeseedMachineEnvironmentValues(tenantRoot, scope),
|
|
2575
|
-
...nonEmptyEnvironmentValues(valuesOverlay)
|
|
2576
|
-
};
|
|
2577
|
-
const deployConfig = loadCliDeployConfig(tenantRoot);
|
|
2578
|
-
const treeseedDatabase = configuredMarketDatabaseService(tenantRoot, deployConfig);
|
|
2579
|
-
const treeseedDatabaseService = treeseedDatabase?.service;
|
|
2580
|
-
const treeseedDatabaseServiceName = treeseedDatabase?.serviceName ?? "";
|
|
2581
|
-
const treeseedDatabaseUrl = typeof values.TREESEED_DATABASE_URL === "string" && values.TREESEED_DATABASE_URL.length > 0 ? values.TREESEED_DATABASE_URL : treeseedDatabaseService?.enabled !== false && treeseedDatabaseService?.provider === "railway" ? `\${{${treeseedDatabaseServiceName}.DATABASE_URL}}` : "";
|
|
2582
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
2583
|
-
const entryFilter = Array.isArray(entryIds) && entryIds.length > 0 ? new Set(entryIds) : null;
|
|
2584
|
-
const progress = (message, stream = "stdout") => onProgress?.(message, stream);
|
|
2585
|
-
const serviceValuesByName = /* @__PURE__ */ new Map();
|
|
2586
|
-
const services = configuredRailwayServices(tenantRoot, scope).map((service) => {
|
|
2587
|
-
const fallbackServiceName = service.key === "api" ? config.settings.services.railway.apiServiceName : service.serviceName;
|
|
2588
|
-
const environmentName = normalizeRailwayEnvironmentName(service.railwayEnvironment ?? scope);
|
|
2589
|
-
const serviceValues = {
|
|
2590
|
-
...values,
|
|
2591
|
-
...service.key === "operationsRunner" ? {
|
|
2592
|
-
TREESEED_PLATFORM_RUNNER_ID: service.runnerId ?? service.serviceName,
|
|
2593
|
-
TREESEED_PLATFORM_RUNNER_DATA_DIR: service.volumeMountPath ?? values.TREESEED_PLATFORM_RUNNER_DATA_DIR,
|
|
2594
|
-
TREESEED_PLATFORM_RUNNER_ENVIRONMENT: scope === "prod" ? "production" : scope
|
|
2595
|
-
} : {},
|
|
2596
|
-
...treeseedDatabaseUrl && ["api", "operationsRunner"].includes(service.key) ? { TREESEED_DATABASE_URL: treeseedDatabaseUrl } : {}
|
|
2597
|
-
};
|
|
2598
|
-
const serviceName = service.serviceName ?? fallbackServiceName;
|
|
2599
|
-
serviceValuesByName.set(serviceName || service.serviceId || service.instanceKey || service.key, serviceValues);
|
|
2600
|
-
return {
|
|
2601
|
-
service: service.key,
|
|
2602
|
-
instanceKey: service.instanceKey ?? service.key,
|
|
2603
|
-
projectName: service.projectName ?? config.settings.services.railway.projectName,
|
|
2604
|
-
serviceName,
|
|
2605
|
-
serviceId: service.serviceId ?? "",
|
|
2606
|
-
rootDir: service.rootDir,
|
|
2607
|
-
baseUrl: service.publicBaseUrl ?? "(unset)",
|
|
2608
|
-
environmentName,
|
|
2609
|
-
secrets: railwayEnvironmentEntryIdsForService(registry, serviceValues, scope, "railway-secret", service.key, entryFilter),
|
|
2610
|
-
variables: railwayEnvironmentEntryIdsForService(registry, serviceValues, scope, "railway-var", service.key, entryFilter),
|
|
2611
|
-
planOnly
|
|
2612
|
-
};
|
|
2613
|
-
}).filter(Boolean);
|
|
2614
|
-
for (const service of services) {
|
|
2615
|
-
const serviceValues = serviceValuesByName.get(service.serviceName || service.serviceId || service.instanceKey || service.service) ?? values;
|
|
2616
|
-
progress(`[${scope}][railway][${service.service}] Syncing ${service.secrets.length} secrets and ${service.variables.length} variables...`);
|
|
2617
|
-
if (!planOnly) {
|
|
2618
|
-
const railwayEnv = { ...process.env, ...values };
|
|
2619
|
-
const project = (await ensureRailwayProject({
|
|
2620
|
-
projectId: "",
|
|
2621
|
-
projectName: service.projectName,
|
|
2622
|
-
env: railwayEnv
|
|
2623
|
-
})).project;
|
|
2624
|
-
const environment = (await ensureRailwayEnvironment({
|
|
2625
|
-
projectId: project.id,
|
|
2626
|
-
environmentName: service.environmentName,
|
|
2627
|
-
env: railwayEnv
|
|
2628
|
-
})).environment;
|
|
2629
|
-
const railwayService = (await ensureRailwayService({
|
|
2630
|
-
projectId: project.id,
|
|
2631
|
-
serviceId: service.serviceId,
|
|
2632
|
-
serviceName: service.serviceName,
|
|
2633
|
-
env: railwayEnv
|
|
2634
|
-
})).service;
|
|
2635
|
-
const variableValues = Object.fromEntries(
|
|
2636
|
-
[...service.secrets, ...service.variables].map((key) => [key, serviceValues[key]]).filter(([, value]) => typeof value === "string" && value.length > 0)
|
|
2637
|
-
);
|
|
2638
|
-
if (Object.keys(variableValues).length > 0) {
|
|
2639
|
-
await upsertRailwayVariables({
|
|
2640
|
-
projectId: project.id,
|
|
2641
|
-
environmentId: environment.id,
|
|
2642
|
-
serviceId: railwayService.id,
|
|
2643
|
-
variables: variableValues,
|
|
2644
|
-
env: railwayEnv
|
|
2645
|
-
});
|
|
2646
|
-
}
|
|
2647
|
-
}
|
|
2648
|
-
progress(`[${scope}][railway][${service.service}] Complete.`);
|
|
2649
|
-
}
|
|
2650
|
-
return {
|
|
2651
|
-
scope,
|
|
2652
|
-
entryIds: entryFilter ? [...entryFilter] : void 0,
|
|
2653
|
-
services
|
|
2654
|
-
};
|
|
2655
|
-
}
|
|
2656
|
-
async function initializeTreeseedPersistentEnvironment({ tenantRoot, scope = "prod", planOnly = false } = {}) {
|
|
2657
|
-
const normalizedScope = scope === "prod" ? "prod" : scope;
|
|
2658
|
-
const target = createPersistentDeployTarget(normalizedScope);
|
|
2659
|
-
const summary = await reconcileTreeseedTarget({
|
|
2660
|
-
tenantRoot,
|
|
2661
|
-
target,
|
|
2662
|
-
env: process.env
|
|
2663
|
-
});
|
|
2664
|
-
return {
|
|
2665
|
-
scope: normalizedScope,
|
|
2666
|
-
target,
|
|
2667
|
-
summary,
|
|
2668
|
-
secrets: summary.results.filter((result) => result.unit.provider === "cloudflare").flatMap((result) => Object.keys(result.resourceLocators ?? {}))
|
|
2669
|
-
};
|
|
2670
|
-
}
|
|
2671
|
-
async function summarizePersistentReadiness(tenantRoot, scope, validation, connectionChecks, env = process.env, { includeReconcileStatus = true, systems } = {}) {
|
|
2672
|
-
const validationProblems = [...validation.missing, ...validation.invalid];
|
|
2673
|
-
const validationBlockers = validationProblems.map((problem) => problem.message);
|
|
2674
|
-
const connectionReady = connectionChecks.every((check) => check.ready || check.skipped);
|
|
2675
|
-
const connectionIssues = connectionChecks.filter((check) => !check.ready && !check.skipped).map((check) => `${check.provider}: ${check.detail}`);
|
|
2676
|
-
const connectionWarnings = connectionChecks.filter((check) => check.skipped).map((check) => `${check.provider}: ${check.detail}`);
|
|
2677
|
-
if (scope === "local") {
|
|
2678
|
-
return {
|
|
2679
|
-
configured: validation.ok,
|
|
2680
|
-
provisioned: true,
|
|
2681
|
-
deployable: validation.ok && connectionReady,
|
|
2682
|
-
phase: validation.ok ? "code_ready" : "config_incomplete",
|
|
2683
|
-
blockers: [
|
|
2684
|
-
...validationBlockers,
|
|
2685
|
-
...connectionIssues
|
|
2686
|
-
],
|
|
2687
|
-
warnings: connectionWarnings,
|
|
2688
|
-
checks: {
|
|
2689
|
-
validation: validation.ok,
|
|
2690
|
-
connections: connectionReady
|
|
2691
|
-
}
|
|
2692
|
-
};
|
|
2693
|
-
}
|
|
2694
|
-
if (!validation.ok) {
|
|
2695
|
-
return {
|
|
2696
|
-
configured: false,
|
|
2697
|
-
provisioned: false,
|
|
2698
|
-
deployable: false,
|
|
2699
|
-
phase: "config_incomplete",
|
|
2700
|
-
blockers: [
|
|
2701
|
-
...validationBlockers,
|
|
2702
|
-
...connectionIssues
|
|
2703
|
-
],
|
|
2704
|
-
warnings: connectionWarnings,
|
|
2705
|
-
checks: {
|
|
2706
|
-
validation: false,
|
|
2707
|
-
connections: connectionReady,
|
|
2708
|
-
cloudflare: null,
|
|
2709
|
-
railway: false
|
|
2710
|
-
}
|
|
2711
|
-
};
|
|
2712
|
-
}
|
|
2713
|
-
const configured = validation.ok;
|
|
2714
|
-
if (!includeReconcileStatus) {
|
|
2715
|
-
return {
|
|
2716
|
-
configured,
|
|
2717
|
-
provisioned: false,
|
|
2718
|
-
deployable: false,
|
|
2719
|
-
phase: "config_complete",
|
|
2720
|
-
blockers: [...connectionIssues],
|
|
2721
|
-
warnings: connectionWarnings,
|
|
2722
|
-
checks: {
|
|
2723
|
-
validation: validation.ok,
|
|
2724
|
-
connections: connectionReady,
|
|
2725
|
-
reconcile: "deferred"
|
|
2726
|
-
}
|
|
2727
|
-
};
|
|
2728
|
-
}
|
|
2729
|
-
const reconcile = await collectTreeseedReconcileStatus({
|
|
2730
|
-
tenantRoot,
|
|
2731
|
-
target: createPersistentDeployTarget(scope),
|
|
2732
|
-
env,
|
|
2733
|
-
systems
|
|
2734
|
-
});
|
|
2735
|
-
const provisioned = reconcile.ready;
|
|
2736
|
-
const deployable = configured && provisioned && connectionReady;
|
|
2737
|
-
const blockers = [...connectionIssues, ...reconcile.blockers];
|
|
2738
|
-
return {
|
|
2739
|
-
configured,
|
|
2740
|
-
provisioned,
|
|
2741
|
-
deployable,
|
|
2742
|
-
phase: provisioned ? "provisioned" : "config_complete",
|
|
2743
|
-
blockers,
|
|
2744
|
-
warnings: [...connectionWarnings, ...reconcile.warnings],
|
|
2745
|
-
checks: {
|
|
2746
|
-
validation: validation.ok,
|
|
2747
|
-
connections: connectionReady,
|
|
2748
|
-
reconcile: reconcile.units
|
|
2749
|
-
}
|
|
2750
|
-
};
|
|
2751
|
-
}
|
|
2752
|
-
function summarizeReconciledPersistentReadiness(scope, validation, connectionChecks, reconciled) {
|
|
2753
|
-
const validationProblems = [...validation.missing, ...validation.invalid];
|
|
2754
|
-
const validationBlockers = validationProblems.map((problem) => problem.message);
|
|
2755
|
-
const connectionReady = connectionChecks.every((check) => check.ready || check.skipped);
|
|
2756
|
-
const connectionIssues = connectionChecks.filter((check) => !check.ready && !check.skipped).map((check) => `${check.provider}: ${check.detail}`);
|
|
2757
|
-
const connectionWarnings = connectionChecks.filter((check) => check.skipped).map((check) => `${check.provider}: ${check.detail}`);
|
|
2758
|
-
if (scope === "local") {
|
|
2759
|
-
return {
|
|
2760
|
-
configured: validation.ok,
|
|
2761
|
-
provisioned: true,
|
|
2762
|
-
deployable: validation.ok && connectionReady,
|
|
2763
|
-
phase: validation.ok ? "code_ready" : "config_incomplete",
|
|
2764
|
-
blockers: [
|
|
2765
|
-
...validationBlockers,
|
|
2766
|
-
...connectionIssues
|
|
2767
|
-
],
|
|
2768
|
-
warnings: connectionWarnings,
|
|
2769
|
-
checks: {
|
|
2770
|
-
validation: validation.ok,
|
|
2771
|
-
connections: connectionReady
|
|
2772
|
-
}
|
|
2773
|
-
};
|
|
2774
|
-
}
|
|
2775
|
-
if (!validation.ok) {
|
|
2776
|
-
return {
|
|
2777
|
-
configured: false,
|
|
2778
|
-
provisioned: false,
|
|
2779
|
-
deployable: false,
|
|
2780
|
-
phase: "config_incomplete",
|
|
2781
|
-
blockers: [
|
|
2782
|
-
...validationBlockers,
|
|
2783
|
-
...connectionIssues
|
|
2784
|
-
],
|
|
2785
|
-
warnings: connectionWarnings,
|
|
2786
|
-
checks: {
|
|
2787
|
-
validation: false,
|
|
2788
|
-
connections: connectionReady,
|
|
2789
|
-
reconcile: []
|
|
2790
|
-
}
|
|
2791
|
-
};
|
|
2792
|
-
}
|
|
2793
|
-
const actions = reconciled?.actions ?? [];
|
|
2794
|
-
const blockers = actions.filter((action) => action.verified !== true).flatMap((action) => [
|
|
2795
|
-
...action.missing.map((entry) => `${action.provider}:${action.unitType}: ${entry}`),
|
|
2796
|
-
...action.drifted.map((entry) => `${action.provider}:${action.unitType}: ${entry}`)
|
|
2797
|
-
]);
|
|
2798
|
-
const provisioned = blockers.length === 0 && actions.length > 0;
|
|
2799
|
-
return {
|
|
2800
|
-
configured: true,
|
|
2801
|
-
provisioned,
|
|
2802
|
-
deployable: provisioned && connectionReady,
|
|
2803
|
-
phase: provisioned ? "provisioned" : "config_complete",
|
|
2804
|
-
blockers: [
|
|
2805
|
-
...connectionIssues,
|
|
2806
|
-
...blockers
|
|
2807
|
-
],
|
|
2808
|
-
warnings: connectionWarnings,
|
|
2809
|
-
checks: {
|
|
2810
|
-
validation: true,
|
|
2811
|
-
connections: connectionReady,
|
|
2812
|
-
reconcile: actions
|
|
2813
|
-
}
|
|
2814
|
-
};
|
|
2815
|
-
}
|
|
2816
|
-
function formatTreeseedConfigValidationFailure(validations, scopes) {
|
|
2817
|
-
const lines = ["Treeseed config validation failed."];
|
|
2818
|
-
for (const scope of scopes) {
|
|
2819
|
-
const validation = validations[scope];
|
|
2820
|
-
if (!validation || validation.ok) {
|
|
2821
|
-
continue;
|
|
2822
|
-
}
|
|
2823
|
-
lines.push("");
|
|
2824
|
-
lines.push(`${scope}:`);
|
|
2825
|
-
for (const problem of [...validation.missing, ...validation.invalid]) {
|
|
2826
|
-
const targets = problem.entry.targets.length > 0 ? ` Targets: ${problem.entry.targets.join(", ")}.` : "";
|
|
2827
|
-
const source = problem.entry.sourceRequirement ? ` Source: ${problem.entry.sourceRequirement}${problem.entry.sourceProvider ? ` (${problem.entry.sourceProvider})` : ""}.` : "";
|
|
2828
|
-
lines.push(`- ${problem.id}: ${problem.message}${targets}${source}`);
|
|
2829
|
-
}
|
|
2830
|
-
}
|
|
2831
|
-
return lines.join("\n");
|
|
2832
|
-
}
|
|
2833
|
-
function colorize(value, code) {
|
|
2834
|
-
return `\x1B[${code}m${value}\x1B[0m`;
|
|
2835
|
-
}
|
|
2836
|
-
function formatConfigSectionTitle(label) {
|
|
2837
|
-
return colorize(`
|
|
2838
|
-
== ${label}`, "1;36");
|
|
2839
|
-
}
|
|
2840
|
-
function hasConfigValue(values, key) {
|
|
2841
|
-
return typeof values[key] === "string" && values[key].trim().length > 0;
|
|
2842
|
-
}
|
|
2843
|
-
function createConfigReadiness(values, validation) {
|
|
2844
|
-
const invalidIds = /* @__PURE__ */ new Set([
|
|
2845
|
-
...(validation?.invalid ?? []).map((problem) => problem.id)
|
|
2846
|
-
]);
|
|
2847
|
-
const validConfigValue = (key) => hasConfigValue(values, key) && !invalidIds.has(key);
|
|
2848
|
-
const configProblems = [
|
|
2849
|
-
...validation?.missing ?? [],
|
|
2850
|
-
...validation?.invalid ?? []
|
|
2851
|
-
];
|
|
2852
|
-
const providerIssues = (provider) => configProblems.filter((problem) => {
|
|
2853
|
-
if (provider === "github") {
|
|
2854
|
-
return problem.id === "TREESEED_GITHUB_TOKEN" || problem.entry.group === "github";
|
|
2855
|
-
}
|
|
2856
|
-
if (provider === "cloudflare") {
|
|
2857
|
-
return problem.id.startsWith("TREESEED_CLOUDFLARE_") || problem.id.startsWith("CLOUDFLARE_") || problem.id.includes("TURNSTILE") || problem.entry.group === "cloudflare";
|
|
2858
|
-
}
|
|
2859
|
-
return problem.id.startsWith("TREESEED_RAILWAY_") || problem.entry.group === "railway";
|
|
2860
|
-
});
|
|
2861
|
-
const localDevelopmentIssues = [
|
|
2862
|
-
...configProblems
|
|
2863
|
-
].filter((problem) => problem.entry.group === "local-development");
|
|
2864
|
-
return {
|
|
2865
|
-
github: {
|
|
2866
|
-
configured: Boolean(resolveTreeseedGitHubToken(values)) && providerIssues("github").length === 0
|
|
2867
|
-
},
|
|
2868
|
-
cloudflare: {
|
|
2869
|
-
configured: providerIssues("cloudflare").length === 0
|
|
2870
|
-
},
|
|
2871
|
-
railway: {
|
|
2872
|
-
configured: validConfigValue("TREESEED_RAILWAY_API_TOKEN") && providerIssues("railway").length === 0
|
|
2873
|
-
},
|
|
2874
|
-
localDevelopment: {
|
|
2875
|
-
configured: localDevelopmentIssues.length === 0
|
|
2876
|
-
}
|
|
2877
|
-
};
|
|
2878
|
-
}
|
|
2879
|
-
const CONFIG_GROUP_ORDER = ["auth", "github", "cloudflare", "railway", "local-development", "forms", "smtp"];
|
|
2880
|
-
function configGroupRank(group) {
|
|
2881
|
-
const index = CONFIG_GROUP_ORDER.indexOf(group);
|
|
2882
|
-
return index === -1 ? CONFIG_GROUP_ORDER.length : index;
|
|
2883
|
-
}
|
|
2884
|
-
function listRelevantTreeseedConfigEntries(registry, scope) {
|
|
2885
|
-
return registry.entries.filter(
|
|
2886
|
-
(entry) => entry.visibility !== "system" && entry.scopes.includes(scope) && (!entry.isRelevant || entry.isRelevant(registry.context, scope, "config") || Boolean(entry.onboardingFeature))
|
|
2887
|
-
).sort((left, right) => {
|
|
2888
|
-
const leftRequired = isTreeseedEnvironmentEntryRequired(left, registry.context, scope, "config");
|
|
2889
|
-
const rightRequired = isTreeseedEnvironmentEntryRequired(right, registry.context, scope, "config");
|
|
2890
|
-
if (leftRequired !== rightRequired) {
|
|
2891
|
-
return leftRequired ? -1 : 1;
|
|
2892
|
-
}
|
|
2893
|
-
if (left.purposes.length !== right.purposes.length) {
|
|
2894
|
-
return right.purposes.length - left.purposes.length;
|
|
2895
|
-
}
|
|
2896
|
-
if (configGroupRank(left.group) !== configGroupRank(right.group)) {
|
|
2897
|
-
return configGroupRank(left.group) - configGroupRank(right.group);
|
|
2898
|
-
}
|
|
2899
|
-
return left.label.localeCompare(right.label);
|
|
2900
|
-
});
|
|
2901
|
-
}
|
|
2902
|
-
function buildConfigEntrySnapshot(scope, entry, currentValue, suggestedValue) {
|
|
2903
|
-
const currentValueValid = (() => {
|
|
2904
|
-
if (!currentValue || !entry.validation) {
|
|
2905
|
-
return currentValue.length > 0;
|
|
2906
|
-
}
|
|
2907
|
-
switch (entry.validation.kind) {
|
|
2908
|
-
case "string":
|
|
2909
|
-
case "nonempty":
|
|
2910
|
-
return currentValue.trim().length > 0 && (typeof entry.validation.minLength !== "number" || currentValue.trim().length >= entry.validation.minLength);
|
|
2911
|
-
case "boolean":
|
|
2912
|
-
return /^(true|false|1|0)$/i.test(currentValue);
|
|
2913
|
-
case "number":
|
|
2914
|
-
return Number.isFinite(Number(currentValue));
|
|
2915
|
-
case "url":
|
|
2916
|
-
try {
|
|
2917
|
-
new URL(currentValue);
|
|
2918
|
-
return true;
|
|
2919
|
-
} catch {
|
|
2920
|
-
return false;
|
|
2921
|
-
}
|
|
2922
|
-
case "email":
|
|
2923
|
-
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(currentValue);
|
|
2924
|
-
case "enum":
|
|
2925
|
-
return entry.validation.values.includes(currentValue);
|
|
2926
|
-
default:
|
|
2927
|
-
return true;
|
|
2928
|
-
}
|
|
2929
|
-
})();
|
|
2930
|
-
const allowGeneratedSecretDefault = ["TREESEED_PLATFORM_RUNNER_SECRET", "TREESEED_WEB_SERVICE_SECRET", "TREESEED_API_WEB_SERVICE_SECRET", "TREESEED_CREDENTIAL_SESSION_SECRET"].includes(entry.id);
|
|
2931
|
-
const allowSuggestedDefault = allowGeneratedSecretDefault || !(entry.sensitivity === "secret" && entry.requirement !== "optional");
|
|
2932
|
-
const effectiveValue = currentValueValid ? currentValue || (allowSuggestedDefault ? suggestedValue : "") || "" : (allowSuggestedDefault ? suggestedValue : "") || currentValue || "";
|
|
2933
|
-
return {
|
|
2934
|
-
id: entry.id,
|
|
2935
|
-
label: entry.label,
|
|
2936
|
-
group: entry.group,
|
|
2937
|
-
cluster: entry.cluster ?? `${entry.group}:${entry.id}`,
|
|
2938
|
-
startupProfile: entry.startupProfile ?? "advanced",
|
|
2939
|
-
requirement: entry.requirement,
|
|
2940
|
-
description: entry.description,
|
|
2941
|
-
howToGet: entry.howToGet,
|
|
2942
|
-
sensitivity: entry.sensitivity,
|
|
2943
|
-
targets: [...entry.targets],
|
|
2944
|
-
purposes: [...entry.purposes],
|
|
2945
|
-
storage: entry.storage ?? "scoped",
|
|
2946
|
-
validation: entry.validation,
|
|
2947
|
-
sourceRequirement: entry.sourceRequirement,
|
|
2948
|
-
sourceHostType: entry.sourceHostType ?? null,
|
|
2949
|
-
sourceProvider: entry.sourceProvider ?? null,
|
|
2950
|
-
scope,
|
|
2951
|
-
sharedScopes: entry.storage === "shared" ? [...entry.scopes] : [scope],
|
|
2952
|
-
required: false,
|
|
2953
|
-
currentValue,
|
|
2954
|
-
suggestedValue,
|
|
2955
|
-
effectiveValue
|
|
2956
|
-
};
|
|
2957
|
-
}
|
|
2958
|
-
function collectTreeseedConfigContext({
|
|
2959
|
-
tenantRoot,
|
|
2960
|
-
scopes = [...TREESEED_ENVIRONMENT_SCOPES],
|
|
2961
|
-
env = process.env
|
|
2962
|
-
}) {
|
|
2963
|
-
ensureTreeseedGitignoreEntries(tenantRoot);
|
|
2964
|
-
ensureTreeseedRailwayIgnoreEntries(tenantRoot);
|
|
2965
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
2966
|
-
const { configPath, keyPath } = getTreeseedMachineConfigPaths(tenantRoot);
|
|
2967
|
-
const valuesByScope = Object.fromEntries(
|
|
2968
|
-
scopes.map((scope) => [scope, collectTreeseedConfigSeedValues(tenantRoot, scope, env)])
|
|
2969
|
-
);
|
|
2970
|
-
const suggestedValuesByScope = Object.fromEntries(
|
|
2971
|
-
scopes.map((scope) => [scope, getTreeseedEnvironmentSuggestedValues({
|
|
2972
|
-
scope,
|
|
2973
|
-
purpose: "config",
|
|
2974
|
-
deployConfig: registry.context.deployConfig,
|
|
2975
|
-
tenantConfig: registry.context.tenantConfig,
|
|
2976
|
-
plugins: registry.context.plugins,
|
|
2977
|
-
values: valuesByScope[scope]
|
|
2978
|
-
})])
|
|
2979
|
-
);
|
|
2980
|
-
const validationByScope = Object.fromEntries(
|
|
2981
|
-
scopes.map((scope) => [scope, validateTreeseedEnvironmentValues({
|
|
2982
|
-
values: {
|
|
2983
|
-
...suggestedValuesByScope[scope],
|
|
2984
|
-
...valuesByScope[scope]
|
|
2985
|
-
},
|
|
2986
|
-
scope,
|
|
2987
|
-
purpose: "config",
|
|
2988
|
-
deployConfig: registry.context.deployConfig,
|
|
2989
|
-
tenantConfig: registry.context.tenantConfig,
|
|
2990
|
-
plugins: registry.context.plugins
|
|
2991
|
-
})])
|
|
2992
|
-
);
|
|
2993
|
-
const configReadinessByScope = Object.fromEntries(
|
|
2994
|
-
scopes.map((scope) => [scope, createConfigReadiness(valuesByScope[scope], validationByScope[scope])])
|
|
2995
|
-
);
|
|
2996
|
-
const entriesByScope = Object.fromEntries(
|
|
2997
|
-
scopes.map((scope) => [scope, listRelevantTreeseedConfigEntries(registry, scope).map((entry) => ({
|
|
2998
|
-
...buildConfigEntrySnapshot(
|
|
2999
|
-
scope,
|
|
3000
|
-
entry,
|
|
3001
|
-
valuesByScope[scope][entry.id] ?? "",
|
|
3002
|
-
suggestedValuesByScope[scope][entry.id] ?? ""
|
|
3003
|
-
),
|
|
3004
|
-
required: isTreeseedEnvironmentEntryRequired(entry, registry.context, scope, "config")
|
|
3005
|
-
}))])
|
|
3006
|
-
);
|
|
3007
|
-
return {
|
|
3008
|
-
tenantRoot,
|
|
3009
|
-
scopes,
|
|
3010
|
-
project: {
|
|
3011
|
-
name: registry.context.deployConfig.name,
|
|
3012
|
-
slug: registry.context.deployConfig.slug,
|
|
3013
|
-
siteUrl: registry.context.deployConfig.siteUrl
|
|
3014
|
-
},
|
|
3015
|
-
configPath,
|
|
3016
|
-
keyPath,
|
|
3017
|
-
entriesByScope,
|
|
3018
|
-
valuesByScope,
|
|
3019
|
-
suggestedValuesByScope,
|
|
3020
|
-
configReadinessByScope,
|
|
3021
|
-
validationByScope,
|
|
3022
|
-
sharedStorageMigrations: [],
|
|
3023
|
-
registry
|
|
3024
|
-
};
|
|
3025
|
-
}
|
|
3026
|
-
function applyTreeseedConfigValues({
|
|
3027
|
-
tenantRoot,
|
|
3028
|
-
updates,
|
|
3029
|
-
applyLocalEnvironment = true
|
|
3030
|
-
}) {
|
|
3031
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
3032
|
-
const key = loadMachineKey(tenantRoot);
|
|
3033
|
-
const machineConfig = loadTreeseedMachineConfig(tenantRoot);
|
|
3034
|
-
const sharedStorageMigrations = migrateLegacyScopedSharedEntries(tenantRoot, machineConfig, registry.entries, key);
|
|
3035
|
-
const entryById = new Map(registry.entries.map((entry) => [entry.id, entry]));
|
|
3036
|
-
const applied = [];
|
|
3037
|
-
for (const update of updates) {
|
|
3038
|
-
const entry = entryById.get(update.entryId);
|
|
3039
|
-
if (!entry) {
|
|
3040
|
-
throw new Error(`Unknown Treeseed config entry "${update.entryId}".`);
|
|
3041
|
-
}
|
|
3042
|
-
if (!entry.scopes.includes(update.scope)) {
|
|
3043
|
-
throw new Error(`Treeseed config entry "${update.entryId}" does not apply to ${update.scope}.`);
|
|
3044
|
-
}
|
|
3045
|
-
setTreeseedMachineEnvironmentValue(tenantRoot, update.scope, entry, update.value);
|
|
3046
|
-
applied.push({
|
|
3047
|
-
scope: entry.storage === "shared" ? "shared" : update.scope,
|
|
3048
|
-
id: entry.id,
|
|
3049
|
-
reused: update.reused === true,
|
|
3050
|
-
cleared: update.value.length === 0
|
|
3051
|
-
});
|
|
3052
|
-
}
|
|
3053
|
-
if (applyLocalEnvironment) {
|
|
3054
|
-
applyTreeseedEnvironmentToProcess({ tenantRoot, scope: "local", override: true });
|
|
3055
|
-
}
|
|
3056
|
-
return {
|
|
3057
|
-
updated: applied,
|
|
3058
|
-
sharedStorageMigrations
|
|
3059
|
-
};
|
|
3060
|
-
}
|
|
3061
|
-
function configProblemBootstrapSystems(problem) {
|
|
3062
|
-
switch (problem?.id) {
|
|
3063
|
-
case "TREESEED_GITHUB_TOKEN":
|
|
3064
|
-
return ["github"];
|
|
3065
|
-
case "TREESEED_CLOUDFLARE_API_TOKEN":
|
|
3066
|
-
case "TREESEED_CLOUDFLARE_ACCOUNT_ID":
|
|
3067
|
-
case "CLOUDFLARE_ZONE_ID":
|
|
3068
|
-
return ["data", "web"];
|
|
3069
|
-
case "TREESEED_RAILWAY_API_TOKEN":
|
|
3070
|
-
case "TREESEED_RAILWAY_WORKSPACE":
|
|
3071
|
-
return ["api", "agents"];
|
|
3072
|
-
default:
|
|
3073
|
-
return null;
|
|
3074
|
-
}
|
|
3075
|
-
}
|
|
3076
|
-
function filterValidationForBootstrapSystems(validation, runnableSystems) {
|
|
3077
|
-
const runnable = new Set(runnableSystems);
|
|
3078
|
-
const keepProblem = (problem) => {
|
|
3079
|
-
const systems = configProblemBootstrapSystems(problem);
|
|
3080
|
-
return !systems || systems.some((system) => runnable.has(system));
|
|
3081
|
-
};
|
|
3082
|
-
const missing = validation.missing.filter(keepProblem);
|
|
3083
|
-
const invalid = validation.invalid.filter(keepProblem);
|
|
3084
|
-
return {
|
|
3085
|
-
...validation,
|
|
3086
|
-
ok: missing.length === 0 && invalid.length === 0,
|
|
3087
|
-
missing,
|
|
3088
|
-
invalid
|
|
3089
|
-
};
|
|
3090
|
-
}
|
|
3091
|
-
async function finalizeTreeseedConfig({
|
|
3092
|
-
tenantRoot,
|
|
3093
|
-
scopes = [...TREESEED_ENVIRONMENT_SCOPES],
|
|
3094
|
-
sync = "all",
|
|
3095
|
-
env = process.env,
|
|
3096
|
-
checkConnections = true,
|
|
3097
|
-
initializePersistent = true,
|
|
3098
|
-
systems,
|
|
3099
|
-
skipUnavailable,
|
|
3100
|
-
bootstrapExecution = "parallel",
|
|
3101
|
-
onProgress
|
|
3102
|
-
}) {
|
|
3103
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
3104
|
-
const summary = {
|
|
3105
|
-
scopes,
|
|
3106
|
-
synced: {},
|
|
3107
|
-
reconciled: [],
|
|
3108
|
-
deployed: [],
|
|
3109
|
-
resourceInventoryByScope: {},
|
|
3110
|
-
connectionChecks: [],
|
|
3111
|
-
validationByScope: {},
|
|
3112
|
-
bootstrapSystemsByScope: {},
|
|
3113
|
-
githubRepository: null,
|
|
3114
|
-
readinessByScope: {},
|
|
3115
|
-
bootstrapExecution
|
|
3116
|
-
};
|
|
3117
|
-
const progress = (message, stream = "stdout") => {
|
|
3118
|
-
if (typeof onProgress === "function") {
|
|
3119
|
-
onProgress(message, stream);
|
|
3120
|
-
}
|
|
3121
|
-
};
|
|
3122
|
-
progress(`Validating configuration for ${scopes.join(", ")}...`);
|
|
3123
|
-
const rawScopeSeedValues = Object.fromEntries(
|
|
3124
|
-
scopes.map((scope) => [scope, collectTreeseedConfigSeedValues(tenantRoot, scope, env)])
|
|
3125
|
-
);
|
|
3126
|
-
const scopeSeedValues = Object.fromEntries(
|
|
3127
|
-
scopes.map((scope) => {
|
|
3128
|
-
const suggestedValues = getTreeseedEnvironmentSuggestedValues({
|
|
3129
|
-
scope,
|
|
3130
|
-
purpose: "config",
|
|
3131
|
-
deployConfig: registry.context.deployConfig,
|
|
3132
|
-
tenantConfig: registry.context.tenantConfig,
|
|
3133
|
-
plugins: registry.context.plugins,
|
|
3134
|
-
values: rawScopeSeedValues[scope]
|
|
3135
|
-
});
|
|
3136
|
-
return [scope, {
|
|
3137
|
-
...suggestedValues,
|
|
3138
|
-
...rawScopeSeedValues[scope]
|
|
3139
|
-
}];
|
|
3140
|
-
})
|
|
3141
|
-
);
|
|
3142
|
-
for (const scope of scopes) {
|
|
3143
|
-
const selection = resolveTreeseedBootstrapSelection({
|
|
3144
|
-
deployConfig: workspaceBootstrapDeployConfig(tenantRoot, registry.context.deployConfig),
|
|
3145
|
-
env: scopeSeedValues[scope],
|
|
3146
|
-
systems: scope === "local" ? ["github"] : systems,
|
|
3147
|
-
skipUnavailable: scope === "local" ? true : skipUnavailable
|
|
3148
|
-
});
|
|
3149
|
-
summary.bootstrapSystemsByScope[scope] = selection;
|
|
3150
|
-
const strictUnavailable = selection.unavailable.filter(
|
|
3151
|
-
(status) => !selection.skipped.some((skipped) => skipped.system === status.system && skipped.reason === status.reason)
|
|
3152
|
-
);
|
|
3153
|
-
if (initializePersistent && strictUnavailable.length > 0) {
|
|
3154
|
-
throw new Error(`Treeseed bootstrap cannot run the selected systems for ${scope}:
|
|
3155
|
-
- ${strictUnavailable.map((status) => `${status.system}: ${status.reason}`).join("\n- ")}`);
|
|
3156
|
-
}
|
|
3157
|
-
for (const skipped of selection.skipped) {
|
|
3158
|
-
progress(`[${scope}][${skipped.system}][skip] ${skipped.reason}`);
|
|
3159
|
-
}
|
|
3160
|
-
}
|
|
3161
|
-
for (const scope of scopes) {
|
|
3162
|
-
const seedValues = scopeSeedValues[scope];
|
|
3163
|
-
const suggestedValues = getTreeseedEnvironmentSuggestedValues({
|
|
3164
|
-
scope,
|
|
3165
|
-
purpose: "config",
|
|
3166
|
-
deployConfig: registry.context.deployConfig,
|
|
3167
|
-
tenantConfig: registry.context.tenantConfig,
|
|
3168
|
-
plugins: registry.context.plugins,
|
|
3169
|
-
values: seedValues
|
|
3170
|
-
});
|
|
3171
|
-
const validation = validateTreeseedEnvironmentValues({
|
|
3172
|
-
values: {
|
|
3173
|
-
...suggestedValues,
|
|
3174
|
-
...seedValues
|
|
3175
|
-
},
|
|
3176
|
-
scope,
|
|
3177
|
-
purpose: "config",
|
|
3178
|
-
deployConfig: registry.context.deployConfig,
|
|
3179
|
-
tenantConfig: registry.context.tenantConfig,
|
|
3180
|
-
plugins: registry.context.plugins
|
|
3181
|
-
});
|
|
3182
|
-
summary.validationByScope[scope] = initializePersistent ? filterValidationForBootstrapSystems(validation, summary.bootstrapSystemsByScope[scope].runnable) : validation;
|
|
3183
|
-
if (checkConnections) {
|
|
3184
|
-
progress(`Checking provider connectivity for ${scope}...`);
|
|
3185
|
-
summary.connectionChecks.push(await checkTreeseedProviderConnections({ tenantRoot, scope, env: seedValues }));
|
|
3186
|
-
}
|
|
3187
|
-
}
|
|
3188
|
-
for (const scope of scopes) {
|
|
3189
|
-
if (scope !== "local") {
|
|
3190
|
-
const target = createPersistentDeployTarget(scope);
|
|
3191
|
-
const deployState = loadDeployState(tenantRoot, registry.context.deployConfig, { target });
|
|
3192
|
-
const inventory = buildProvisioningSummary(registry.context.deployConfig, deployState, target);
|
|
3193
|
-
const railwayWorkspace = resolveRailwayWorkspace(scopeSeedValues[scope]);
|
|
3194
|
-
summary.resourceInventoryByScope[scope] = inventory;
|
|
3195
|
-
progress(
|
|
3196
|
-
`Resolved ${scope} resources: deployment=${inventory.identity?.deploymentKey}, pages=${inventory.resources?.pagesProject}, web-domain=${inventory.resources?.webDomain ?? "(none)"}, api-domain=${inventory.resources?.apiDomain ?? "(none)"}, r2=${inventory.resources?.contentBucket}, queue=${inventory.resources?.queue}, d1=${inventory.resources?.database}, railway=${inventory.resources?.railwayProject}, workspace=${railwayWorkspace}.`
|
|
3197
|
-
);
|
|
3198
|
-
}
|
|
3199
|
-
summary.readinessByScope[scope] = await summarizePersistentReadiness(
|
|
3200
|
-
tenantRoot,
|
|
3201
|
-
scope,
|
|
3202
|
-
summary.validationByScope[scope],
|
|
3203
|
-
summary.connectionChecks.find((report) => report.scope === scope)?.checks ?? [],
|
|
3204
|
-
scopeSeedValues[scope],
|
|
3205
|
-
{
|
|
3206
|
-
includeReconcileStatus: initializePersistent && summary.bootstrapSystemsByScope[scope].runnable.some((system) => system !== "github"),
|
|
3207
|
-
systems: summary.bootstrapSystemsByScope[scope].runnable.filter((system) => system !== "github")
|
|
3208
|
-
}
|
|
3209
|
-
);
|
|
3210
|
-
}
|
|
3211
|
-
const invalidScopes = scopes.filter((scope) => summary.validationByScope[scope]?.ok !== true);
|
|
3212
|
-
if (invalidScopes.length > 0) {
|
|
3213
|
-
throw new Error(formatTreeseedConfigValidationFailure(summary.validationByScope, scopes));
|
|
3214
|
-
}
|
|
3215
|
-
const failingConnectionReports = summary.connectionChecks.filter((report) => report.ok !== true);
|
|
3216
|
-
if (failingConnectionReports.length > 0) {
|
|
3217
|
-
throw new Error(formatTreeseedProviderConnectionFailures(failingConnectionReports));
|
|
3218
|
-
}
|
|
3219
|
-
progress("Syncing managed service settings from treeseed.site.yaml...");
|
|
3220
|
-
syncManagedServiceSettingsFromDeployConfig(tenantRoot);
|
|
3221
|
-
const githubSelected = scopes.some((scope) => scope !== "local" && summary.bootstrapSystemsByScope[scope].runnable.includes("github"));
|
|
3222
|
-
let githubRepository = maybeResolveGitHubRepositorySlug(tenantRoot);
|
|
3223
|
-
if (githubSelected && initializePersistent) {
|
|
3224
|
-
const localSeedValues = collectTreeseedConfigSeedValues(tenantRoot, "local", env);
|
|
3225
|
-
const localSuggestedValues = getTreeseedEnvironmentSuggestedValues({
|
|
3226
|
-
scope: "local",
|
|
3227
|
-
purpose: "config",
|
|
3228
|
-
deployConfig: registry.context.deployConfig,
|
|
3229
|
-
tenantConfig: registry.context.tenantConfig,
|
|
3230
|
-
plugins: registry.context.plugins,
|
|
3231
|
-
values: localSeedValues
|
|
3232
|
-
});
|
|
3233
|
-
const repositoryBootstrap = await ensureGitHubBootstrapRepository(tenantRoot, {
|
|
3234
|
-
values: {
|
|
3235
|
-
...localSuggestedValues,
|
|
3236
|
-
...localSeedValues
|
|
3237
|
-
},
|
|
3238
|
-
defaultName: registry.context.deployConfig.slug,
|
|
3239
|
-
onProgress: (line) => progress(line)
|
|
3240
|
-
});
|
|
3241
|
-
summary.githubRepository = repositoryBootstrap;
|
|
3242
|
-
githubRepository = repositoryBootstrap.repository;
|
|
3243
|
-
}
|
|
3244
|
-
if (initializePersistent) {
|
|
3245
|
-
for (const scope of scopes) {
|
|
3246
|
-
if (scope === "local") {
|
|
3247
|
-
continue;
|
|
3248
|
-
}
|
|
3249
|
-
const selection = summary.bootstrapSystemsByScope[scope];
|
|
3250
|
-
const reconcileSystems = selection.runnable.filter((system) => system !== "github");
|
|
3251
|
-
if (reconcileSystems.length > 0) {
|
|
3252
|
-
progress(`[${scope}][bootstrap][plan] Deriving desired units for ${reconcileSystems.join(", ")}...`);
|
|
3253
|
-
const initialized = await reconcileTreeseedTarget({
|
|
3254
|
-
tenantRoot,
|
|
3255
|
-
target: createPersistentDeployTarget(scope),
|
|
3256
|
-
env: scopeSeedValues[scope],
|
|
3257
|
-
systems: reconcileSystems,
|
|
3258
|
-
write: (line) => progress(`[${scope}][reconcile] ${line}`)
|
|
3259
|
-
});
|
|
3260
|
-
summary.reconciled.push({
|
|
3261
|
-
scope,
|
|
3262
|
-
target: scope,
|
|
3263
|
-
units: initialized.units.length,
|
|
3264
|
-
actions: initialized.results.map((result) => ({
|
|
3265
|
-
unitId: result.unit.unitId,
|
|
3266
|
-
unitType: result.unit.unitType,
|
|
3267
|
-
provider: result.unit.provider,
|
|
3268
|
-
action: result.action,
|
|
3269
|
-
verified: result.verification?.verified === true,
|
|
3270
|
-
missing: result.verification?.missing ?? [],
|
|
3271
|
-
drifted: result.verification?.drifted ?? []
|
|
3272
|
-
}))
|
|
3273
|
-
});
|
|
3274
|
-
}
|
|
3275
|
-
if (scope === "staging" && selection.runnable.includes("github")) {
|
|
3276
|
-
progress(`[${scope}][github][branch] Ensuring ${STAGING_BRANCH} exists on origin from ${PRODUCTION_BRANCH}...`);
|
|
3277
|
-
if (!githubRepository) {
|
|
3278
|
-
throw new Error("Unable to determine the GitHub repository from the origin remote for staging branch bootstrap.");
|
|
3279
|
-
}
|
|
3280
|
-
const branchBootstrap = await ensureGitHubBranchFromBase(githubRepository, STAGING_BRANCH, {
|
|
3281
|
-
baseBranch: PRODUCTION_BRANCH,
|
|
3282
|
-
client: createGitHubApiClient({
|
|
3283
|
-
env: scopeSeedValues[scope]
|
|
3284
|
-
})
|
|
3285
|
-
});
|
|
3286
|
-
summary.deployed.push({
|
|
3287
|
-
scope,
|
|
3288
|
-
branchBootstrap,
|
|
3289
|
-
result: {}
|
|
3290
|
-
});
|
|
3291
|
-
}
|
|
3292
|
-
const deploySystems = selection.runnable.filter((system) => system === "data" || system === "web" || system === "api" || system === "agents");
|
|
3293
|
-
if (deploySystems.length > 0) {
|
|
3294
|
-
progress(`[${scope}][bootstrap][deploy] Deploying ${deploySystems.join(", ")}...`);
|
|
3295
|
-
applyTreeseedEnvironmentToProcess({ tenantRoot, scope, override: true });
|
|
3296
|
-
process.env.TREESEED_RAILWAY_WORKSPACE = process.env.TREESEED_RAILWAY_WORKSPACE || scopeSeedValues[scope].TREESEED_RAILWAY_WORKSPACE || resolveRailwayWorkspace(scopeSeedValues[scope]);
|
|
3297
|
-
const { deployProjectPlatform } = await import("./project-platform.js");
|
|
3298
|
-
const deployResult = await deployProjectPlatform({
|
|
3299
|
-
tenantRoot,
|
|
3300
|
-
scope,
|
|
3301
|
-
skipProvision: true,
|
|
3302
|
-
bootstrapSystems: deploySystems,
|
|
3303
|
-
bootstrapExecution,
|
|
3304
|
-
env: scopeSeedValues[scope],
|
|
3305
|
-
write: (line, stream) => progress(line, stream)
|
|
3306
|
-
});
|
|
3307
|
-
const deployEntry = summary.deployed.find((entry) => entry.scope === scope);
|
|
3308
|
-
if (deployEntry) {
|
|
3309
|
-
deployEntry.result = deployResult;
|
|
3310
|
-
} else {
|
|
3311
|
-
summary.deployed.push({
|
|
3312
|
-
scope,
|
|
3313
|
-
branchBootstrap: null,
|
|
3314
|
-
result: deployResult
|
|
3315
|
-
});
|
|
3316
|
-
}
|
|
3317
|
-
progress(`[${scope}][bootstrap][verify] Re-verifying after deployment...`);
|
|
3318
|
-
const finalized = await reconcileTreeseedTarget({
|
|
3319
|
-
tenantRoot,
|
|
3320
|
-
target: createPersistentDeployTarget(scope),
|
|
3321
|
-
env: scopeSeedValues[scope],
|
|
3322
|
-
systems: reconcileSystems,
|
|
3323
|
-
write: (line) => progress(`[${scope}][verify] ${line}`)
|
|
3324
|
-
});
|
|
3325
|
-
const index = summary.reconciled.findIndex((entry) => entry.scope === scope);
|
|
3326
|
-
const nextSummary = {
|
|
3327
|
-
scope,
|
|
3328
|
-
target: scope,
|
|
3329
|
-
units: finalized.units.length,
|
|
3330
|
-
actions: finalized.results.map((result) => ({
|
|
3331
|
-
unitId: result.unit.unitId,
|
|
3332
|
-
unitType: result.unit.unitType,
|
|
3333
|
-
provider: result.unit.provider,
|
|
3334
|
-
action: result.action,
|
|
3335
|
-
verified: result.verification?.verified === true,
|
|
3336
|
-
missing: result.verification?.missing ?? [],
|
|
3337
|
-
drifted: result.verification?.drifted ?? []
|
|
3338
|
-
}))
|
|
3339
|
-
};
|
|
3340
|
-
if (index >= 0) {
|
|
3341
|
-
summary.reconciled[index] = nextSummary;
|
|
3342
|
-
} else {
|
|
3343
|
-
summary.reconciled.push(nextSummary);
|
|
3344
|
-
}
|
|
3345
|
-
}
|
|
3346
|
-
}
|
|
3347
|
-
}
|
|
3348
|
-
if (sync === "github" || sync === "all") {
|
|
3349
|
-
const githubScopes = scopes.filter((scope) => scope !== "local" && summary.bootstrapSystemsByScope[scope].runnable.includes("github"));
|
|
3350
|
-
const githubTargets = discoverGitHubEnvironmentSyncTargets(tenantRoot, githubRepository);
|
|
3351
|
-
if (githubTargets.length === 0 && githubScopes.length > 0) {
|
|
3352
|
-
throw new Error("Unable to determine the GitHub repository from the origin remote.");
|
|
3353
|
-
}
|
|
3354
|
-
const syncScope = async (scope, target) => {
|
|
3355
|
-
progress(`[${scope}][github][sync] Syncing GitHub environment for ${target.repository}...`);
|
|
3356
|
-
return await syncTreeseedGitHubEnvironment({
|
|
3357
|
-
tenantRoot,
|
|
3358
|
-
scope,
|
|
3359
|
-
repository: target.repository,
|
|
3360
|
-
managedHostMode: target.managedHostMode,
|
|
3361
|
-
execution: bootstrapExecution,
|
|
3362
|
-
onProgress: progress
|
|
3363
|
-
});
|
|
3364
|
-
};
|
|
3365
|
-
const githubResults = [];
|
|
3366
|
-
if (bootstrapExecution === "sequential") {
|
|
3367
|
-
for (const target of githubTargets) {
|
|
3368
|
-
for (const scope of githubScopes) {
|
|
3369
|
-
githubResults.push(await syncScope(scope, target));
|
|
3370
|
-
}
|
|
3371
|
-
}
|
|
3372
|
-
} else {
|
|
3373
|
-
githubResults.push(...await Promise.all(githubTargets.flatMap(
|
|
3374
|
-
(target) => githubScopes.map((scope) => syncScope(scope, target))
|
|
3375
|
-
)));
|
|
3376
|
-
}
|
|
3377
|
-
summary.synced.github = {
|
|
3378
|
-
scopes: githubResults,
|
|
3379
|
-
repository: githubResults[0]?.repository ?? githubRepository ?? maybeResolveGitHubRepositorySlug(tenantRoot),
|
|
3380
|
-
repositories: githubResults.map((entry) => entry.repository).filter((repository, index, all) => all.indexOf(repository) === index),
|
|
3381
|
-
secrets: githubResults.flatMap((entry) => entry.secrets),
|
|
3382
|
-
variables: githubResults.flatMap((entry) => entry.variables)
|
|
3383
|
-
};
|
|
3384
|
-
}
|
|
3385
|
-
for (const scope of scopes) {
|
|
3386
|
-
const reconciled = summary.reconciled.find((entry) => entry.scope === scope) ?? null;
|
|
3387
|
-
summary.readinessByScope[scope] = initializePersistent && reconciled ? summarizeReconciledPersistentReadiness(
|
|
3388
|
-
scope,
|
|
3389
|
-
summary.validationByScope[scope],
|
|
3390
|
-
summary.connectionChecks.find((report) => report.scope === scope)?.checks ?? [],
|
|
3391
|
-
reconciled
|
|
3392
|
-
) : await summarizePersistentReadiness(
|
|
3393
|
-
tenantRoot,
|
|
3394
|
-
scope,
|
|
3395
|
-
summary.validationByScope[scope],
|
|
3396
|
-
summary.connectionChecks.find((report) => report.scope === scope)?.checks ?? [],
|
|
3397
|
-
scopeSeedValues[scope],
|
|
3398
|
-
{
|
|
3399
|
-
includeReconcileStatus: initializePersistent && summary.bootstrapSystemsByScope[scope].runnable.some((system) => system !== "github"),
|
|
3400
|
-
systems: summary.bootstrapSystemsByScope[scope].runnable.filter((system) => system !== "github")
|
|
3401
|
-
}
|
|
3402
|
-
);
|
|
3403
|
-
}
|
|
3404
|
-
return summary;
|
|
3405
|
-
}
|
|
3406
|
-
function collectTreeseedPrintEnvReport({
|
|
3407
|
-
tenantRoot,
|
|
3408
|
-
scope,
|
|
3409
|
-
env = process.env,
|
|
3410
|
-
revealSecrets = false
|
|
3411
|
-
}) {
|
|
3412
|
-
const registry = collectTreeseedEnvironmentContext(tenantRoot);
|
|
3413
|
-
const { values, sources } = collectTreeseedConfigSeedValueSources(tenantRoot, scope, env);
|
|
3414
|
-
return {
|
|
3415
|
-
scope,
|
|
3416
|
-
revealSecrets,
|
|
3417
|
-
entries: listRelevantTreeseedConfigEntries(registry, scope).map((entry) => {
|
|
3418
|
-
const rawValue = values[entry.id] ?? "";
|
|
3419
|
-
const canRevealValue = entry.sensitivity !== "secret" || revealSecrets;
|
|
3420
|
-
return {
|
|
3421
|
-
id: entry.id,
|
|
3422
|
-
label: entry.label,
|
|
3423
|
-
sensitivity: entry.sensitivity,
|
|
3424
|
-
value: canRevealValue ? rawValue : "",
|
|
3425
|
-
displayValue: rawValue ? entry.sensitivity === "secret" && !revealSecrets ? maskValue(rawValue) : rawValue : "(unset)",
|
|
3426
|
-
source: sources[entry.id] ?? "unset",
|
|
3427
|
-
sourceRequirement: entry.sourceRequirement,
|
|
3428
|
-
sourceHostType: entry.sourceHostType ?? null,
|
|
3429
|
-
sourceProvider: entry.sourceProvider ?? null
|
|
3430
|
-
};
|
|
3431
|
-
})
|
|
3432
|
-
};
|
|
3433
|
-
}
|
|
3434
|
-
export {
|
|
3435
|
-
DEFAULT_TEMPLATE_CATALOG_URL,
|
|
3436
|
-
DEFAULT_TREESEED_API_BASE_URL,
|
|
3437
|
-
TREESEED_API_BASE_URL_ENV,
|
|
3438
|
-
TREESEED_MACHINE_KEY_PASSPHRASE_ENV2 as TREESEED_MACHINE_KEY_PASSPHRASE_ENV,
|
|
3439
|
-
TREESEED_TEMPLATE_CATALOG_URL_ENV,
|
|
3440
|
-
TreeseedKeyAgentError2 as TreeseedKeyAgentError,
|
|
3441
|
-
applyTreeseedConfigValues,
|
|
3442
|
-
applyTreeseedEnvironmentToProcess,
|
|
3443
|
-
applyTreeseedSafeRepairs,
|
|
3444
|
-
assertTreeseedCommandEnvironment,
|
|
3445
|
-
checkTreeseedProviderConnections,
|
|
3446
|
-
clearTreeseedRemoteSession,
|
|
3447
|
-
collectTreeseedConfigContext,
|
|
3448
|
-
collectTreeseedConfigSeedValues,
|
|
3449
|
-
collectTreeseedEnvironmentContext,
|
|
3450
|
-
collectTreeseedPrintEnvReport,
|
|
3451
|
-
configGroupRank,
|
|
3452
|
-
createDefaultTreeseedMachineConfig,
|
|
3453
|
-
ensureTreeseedActVerificationTooling,
|
|
3454
|
-
ensureTreeseedGitignoreEntries,
|
|
3455
|
-
ensureTreeseedRailwayIgnoreEntries,
|
|
3456
|
-
ensureTreeseedSecretSessionForConfig,
|
|
3457
|
-
finalizeTreeseedConfig,
|
|
3458
|
-
formatTreeseedConfigEnvironmentReport,
|
|
3459
|
-
formatTreeseedProviderConnectionReport,
|
|
3460
|
-
getTreeseedMachineConfigPaths,
|
|
3461
|
-
getTreeseedRemoteAuthPaths,
|
|
3462
|
-
initializeTreeseedPersistentEnvironment,
|
|
3463
|
-
inspectTreeseedKeyAgentStatus,
|
|
3464
|
-
inspectTreeseedKeyAgentTransportDiagnostic,
|
|
3465
|
-
inspectTreeseedPassphraseEnvDiagnostic,
|
|
3466
|
-
listDeprecatedTreeseedLocalEnvFiles,
|
|
3467
|
-
listRelevantTreeseedConfigEntries,
|
|
3468
|
-
loadTreeseedMachineConfig,
|
|
3469
|
-
loadTreeseedRemoteAuthState,
|
|
3470
|
-
lockTreeseedSecretSession,
|
|
3471
|
-
migrateTreeseedMachineKeyToWrapped,
|
|
3472
|
-
resolveTreeseedLaunchEnvironment,
|
|
3473
|
-
resolveTreeseedMachineEnvironmentValues,
|
|
3474
|
-
resolveTreeseedRemoteConfig,
|
|
3475
|
-
resolveTreeseedRemoteSession,
|
|
3476
|
-
resolveTreeseedTemplateCatalogCachePath,
|
|
3477
|
-
resolveTreeseedTemplateCatalogEndpoint,
|
|
3478
|
-
rotateTreeseedMachineKey,
|
|
3479
|
-
rotateTreeseedMachineKeyPassphrase,
|
|
3480
|
-
setTreeseedMachineEnvironmentValue,
|
|
3481
|
-
setTreeseedRemoteSession,
|
|
3482
|
-
syncTreeseedCloudflareEnvironment,
|
|
3483
|
-
syncTreeseedGitHubEnvironment,
|
|
3484
|
-
syncTreeseedRailwayEnvironment,
|
|
3485
|
-
unlockTreeseedSecretSessionFromEnv,
|
|
3486
|
-
unlockTreeseedSecretSessionInteractive,
|
|
3487
|
-
unlockTreeseedSecretSessionWithPassphrase,
|
|
3488
|
-
updateTreeseedDeployConfigFeatureToggles,
|
|
3489
|
-
validateTreeseedCommandEnvironment,
|
|
3490
|
-
warnDeprecatedTreeseedLocalEnvFiles,
|
|
3491
|
-
withTreeseedKeyAgentAutopromptDisabled,
|
|
3492
|
-
writeTreeseedMachineConfig,
|
|
3493
|
-
writeTreeseedRemoteAuthState
|
|
3494
|
-
};
|