@tdk-landscape/tdk-cli-core 1.3.45 → 1.3.48
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/.github/workflows/release-binaries.yml +54 -0
- package/.pre-commit-config.yaml +57 -0
- package/Makefile +92 -0
- package/bun.lock +538 -0
- package/cli/AGENTS.md +390 -0
- package/cli/DESIGN.md +735 -0
- package/cli/biome.json +44 -0
- package/cli/dependency-graph.svg +1135 -0
- package/cli/knip.json +6 -0
- package/cli/package.json +77 -0
- package/cli/pnpm-lock.yaml +2305 -0
- package/cli/pnpm-workspace.yaml +2 -0
- package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +23 -0
- package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +464 -0
- package/cli/shared-platform-engineering/docker-templates/install-deps.sh +152 -0
- package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +12 -0
- package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +56 -0
- package/{dist/cli.js → cli/src/cli.ts} +21 -13
- package/cli/src/commands/__tests__/always-enabled-infra.test.ts +115 -0
- package/cli/src/commands/__tests__/config.test.ts +146 -0
- package/cli/src/commands/__tests__/doctor.test.ts +557 -0
- package/cli/src/commands/__tests__/error-handling.test.ts +78 -0
- package/cli/src/commands/__tests__/phases-model.test.ts +163 -0
- package/cli/src/commands/__tests__/project-resource-features-e2e.test.ts +207 -0
- package/cli/src/commands/__tests__/project.test.ts +68 -0
- package/cli/src/commands/__tests__/resource.test.ts +226 -0
- package/cli/src/commands/__tests__/saas-starter-integration.test.ts +196 -0
- package/cli/src/commands/__tests__/stack-features-e2e.test.ts +129 -0
- package/cli/src/commands/__tests__/upgrade.test.ts +101 -0
- package/{dist/commands/completion.js → cli/src/commands/completion.ts} +78 -72
- package/cli/src/commands/config.ts +299 -0
- package/cli/src/commands/doctor.ts +790 -0
- package/cli/src/commands/down.ts +37 -0
- package/cli/src/commands/help.ts +150 -0
- package/cli/src/commands/networks-design.md +80 -0
- package/cli/src/commands/networks.ts +332 -0
- package/cli/src/commands/project.ts +439 -0
- package/cli/src/commands/projects.ts +82 -0
- package/cli/src/commands/resource.ts +652 -0
- package/cli/src/commands/resources.ts +95 -0
- package/cli/src/commands/stack.ts +128 -0
- package/cli/src/commands/stacks.ts +52 -0
- package/cli/src/commands/status.ts +88 -0
- package/cli/src/commands/ui.tsx +843 -0
- package/cli/src/commands/up.ts +150 -0
- package/cli/src/commands/upgrade.ts +570 -0
- package/{dist/commands/version.js → cli/src/commands/version.ts} +5 -5
- package/cli/src/components/Accessible.tsx +21 -0
- package/cli/src/components/BaseTooltip.tsx +66 -0
- package/cli/src/components/DetailPanel.tsx +146 -0
- package/cli/src/components/FileTree.tsx +89 -0
- package/cli/src/components/ResourceSelectInput.tsx +30 -0
- package/cli/src/components/ResourceTable.tsx +78 -0
- package/cli/src/components/TabBar.tsx +53 -0
- package/cli/src/components/Tooltip.tsx +11 -0
- package/{dist/components/index.js → cli/src/components/index.ts} +0 -1
- package/cli/src/config/platform-standards.test.ts +11 -0
- package/cli/src/config/platform-standards.ts +196 -0
- package/cli/src/generator/__tests__/extension-fetch.test.ts +220 -0
- package/cli/src/generator/__tests__/template-engine.test.ts +70 -0
- package/cli/src/generator/extension-fetch.ts +279 -0
- package/cli/src/generator/template-engine.ts +754 -0
- package/cli/src/index.ts +86 -0
- package/cli/src/templates.d.ts +4 -0
- package/cli/src/types/index.ts +351 -0
- package/cli/src/utils/__tests__/cache.test.ts +98 -0
- package/cli/src/utils/__tests__/formatting.test.ts +170 -0
- package/cli/src/utils/__tests__/paths.test.ts +180 -0
- package/cli/src/utils/__tests__/port-assignment.test.ts +121 -0
- package/cli/src/utils/__tests__/service-urls.test.ts +174 -0
- package/cli/src/utils/__tests__/services.test.ts +531 -0
- package/cli/src/utils/__tests__/tilt.test.ts +143 -0
- package/cli/src/utils/__tests__/validation.test.ts +148 -0
- package/cli/src/utils/cache.ts +76 -0
- package/cli/src/utils/command-helpers.ts +54 -0
- package/cli/src/utils/constants.ts +126 -0
- package/cli/src/utils/discovery-context.ts +49 -0
- package/cli/src/utils/docker.ts +20 -0
- package/cli/src/utils/doctor-runtime.ts +604 -0
- package/cli/src/utils/env-validator.ts +139 -0
- package/cli/src/utils/errors.ts +168 -0
- package/cli/src/utils/file-helpers.ts +62 -0
- package/cli/src/utils/formatting.ts +215 -0
- package/cli/src/utils/paths.ts +53 -0
- package/cli/src/utils/port-assignment.ts +106 -0
- package/cli/src/utils/project-features.ts +118 -0
- package/cli/src/utils/project-templates.ts +28 -0
- package/cli/src/utils/prompt.ts +123 -0
- package/cli/src/utils/resource-features.ts +209 -0
- package/cli/src/utils/service-urls.ts +124 -0
- package/cli/src/utils/services.ts +363 -0
- package/cli/src/utils/stack-features.ts +30 -0
- package/cli/src/utils/tar.ts +24 -0
- package/cli/src/utils/tilt.ts +113 -0
- package/cli/src/utils/validation.ts +67 -0
- package/{templates → cli/templates}/Tiltfile.hbs +1 -1
- package/cli/tsconfig.json +25 -0
- package/cli/tsconfig.typecheck.json +8 -0
- package/docs/FEATURES.md +279 -0
- package/engine/topologies/platform/docker/dockerfile/dockerfile.star +3 -3
- package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +1 -1
- package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +0 -3
- package/engine/topologies/platform/docker/layers/l3_builder_layers.star +1 -1
- package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +1 -1
- package/engine/topologies/platform/docker/secrets/infisical.star +19 -47
- package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +1 -1
- package/install.sh +242 -0
- package/knip.json +10 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/.openspec.yaml +2 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/design.md +102 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/proposal.md +31 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-notifications/spec.md +48 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-orchestration/spec.md +63 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-troubleshooting/spec.md +56 -0
- package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/tasks.md +61 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/design.md +114 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/proposal.md +32 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/manifest-field-migration/spec.md +20 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/resource-terminology-standard/spec.md +20 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/stack-terminology-standard/spec.md +20 -0
- package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/tasks.md +194 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/design.md +111 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/proposal.md +33 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/function-naming-alignment/spec.md +12 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/optional-parameter-patterns/spec.md +18 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-documentation/spec.md +16 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-naming-consistency/spec.md +12 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-ordering-standard/spec.md +12 -0
- package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/tasks.md +194 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/codebase-audit-report.md +62 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/design.md +84 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/proposal.md +31 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/codebase-audit/spec.md +27 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/dead-code-cleanup/spec.md +27 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/test-gap-fill/spec.md +23 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/todo-resolution/spec.md +27 -0
- package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/tasks.md +39 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/.openspec.yaml +5 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/design.md +94 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/proposal.md +46 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CLEANUP_REPORT_8.md +174 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CRITICAL_ASSESSMENT_8.md +160 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/circular-deps/CRITICAL_ASSESSMENT_4.md +381 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/dead-code/CRITICAL_ASSESSMENT_3.md +316 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/CRITICAL_ASSESSMENT_1.md +290 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/IMPLEMENTATION_SUMMARY_1.md +233 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/defensive-cleanup/CRITICAL_ASSESSMENT_6.md +835 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/CRITICAL_ASSESSMENT_7.md +254 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/IMPLEMENTATION_SUMMARY_7.md +187 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/strong-typing/CRITICAL_ASSESSMENT_5.md +269 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/CRITICAL_ASSESSMENT_2.md +312 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/IMPLEMENTATION_SUMMARY.md +170 -0
- package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/tasks.md +137 -0
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/design.md +80 -0
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/proposal.md +40 -0
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/spec.md +52 -0
- package/openspec/changes/archive/2026-06-25-landscape-consolidation/tasks.md +51 -0
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/design.md +43 -0
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/proposal.md +29 -0
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/specs/self-contained-generated-projects/spec.md +67 -0
- package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/tasks.md +27 -0
- package/openspec/specs/manifest-field-migration/spec.md +24 -0
- package/openspec/specs/resource-terminology-standard/spec.md +24 -0
- package/openspec/specs/self-contained-generated-projects/spec.md +67 -0
- package/openspec/specs/stack-terminology-standard/spec.md +24 -0
- package/package.json +27 -64
- package/platform/services/platform/proxy/service.yaml +14 -0
- package/scripts/audit-codebase.sh +213 -0
- package/scripts/install-git-hooks.sh +14 -0
- package/scripts/release-binaries.sh +94 -0
- package/scripts/setup-pre-commit.sh +33 -0
- package/scripts/stage-npm-engine.sh +36 -0
- package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +23 -0
- package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +464 -0
- package/shared-platform-engineering/docker-templates/install-deps.sh +152 -0
- package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +12 -0
- package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +56 -0
- package/tests/tilt-engine/__init__.py +5 -0
- package/tests/tilt-engine/conftest.py +182 -0
- package/tests/tilt-engine/fixtures/appointment-frontend-service.json +11 -0
- package/tests/tilt-engine/fixtures/identity-backend-service.json +12 -0
- package/tests/tilt-engine/fixtures/invalid-service.json +4 -0
- package/tests/tilt-engine/test_agents_md_generator.py +341 -0
- package/tests/tilt-engine/test_agents_md_integration.py +378 -0
- package/tests/tilt-engine/test_cache_operations.py +181 -0
- package/tests/tilt-engine/test_cache_persistence.py +142 -0
- package/tests/tilt-engine/test_code_executor.py +152 -0
- package/tests/tilt-engine/test_code_viewer.py +127 -0
- package/tests/tilt-engine/test_compose_port_anchors.py +310 -0
- package/tests/tilt-engine/test_config_inspector.py +162 -0
- package/tests/tilt-engine/test_daemon_focus_mode.py +89 -0
- package/tests/tilt-engine/test_daemon_polling.py +122 -0
- package/tests/tilt-engine/test_daemon_registration.py +115 -0
- package/tests/tilt-engine/test_daemon_validation.py +107 -0
- package/tests/tilt-engine/test_docker_generator_bugs.py +493 -0
- package/tests/tilt-engine/test_docker_prune.py +119 -0
- package/tests/tilt-engine/test_dockerignore_path_bug.py +82 -0
- package/tests/tilt-engine/test_file_browser.py +193 -0
- package/tests/tilt-engine/test_frontend_basepath_manifest.py +27 -0
- package/tests/tilt-engine/test_health_dependencies.py +130 -0
- package/tests/tilt-engine/test_health_endpoints.py +118 -0
- package/tests/tilt-engine/test_health_tilt_ui.py +85 -0
- package/tests/tilt-engine/test_safety_aliases.py +84 -0
- package/tests/tilt-engine/test_snapshot_diff.py +136 -0
- package/tests/tilt-engine/test_snapshot_persistence.py +163 -0
- package/tests/tilt-engine/test_snapshot_scanning.py +123 -0
- package/tests/tilt-engine/test_standalone_project_boot.py +61 -0
- package/tests/tilt-engine/test_standalone_traefik.py +108 -0
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/completion.d.ts +0 -3
- package/dist/commands/completion.d.ts.map +0 -1
- package/dist/commands/completion.js.map +0 -1
- package/dist/commands/config.d.ts +0 -3
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js +0 -231
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -7
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js +0 -597
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/down.d.ts +0 -3
- package/dist/commands/down.d.ts.map +0 -1
- package/dist/commands/down.js +0 -32
- package/dist/commands/down.js.map +0 -1
- package/dist/commands/help.d.ts +0 -2
- package/dist/commands/help.d.ts.map +0 -1
- package/dist/commands/help.js +0 -130
- package/dist/commands/help.js.map +0 -1
- package/dist/commands/networks.d.ts +0 -3
- package/dist/commands/networks.d.ts.map +0 -1
- package/dist/commands/networks.js +0 -260
- package/dist/commands/networks.js.map +0 -1
- package/dist/commands/project.d.ts +0 -3
- package/dist/commands/project.d.ts.map +0 -1
- package/dist/commands/project.js +0 -373
- package/dist/commands/project.js.map +0 -1
- package/dist/commands/projects.d.ts +0 -3
- package/dist/commands/projects.d.ts.map +0 -1
- package/dist/commands/projects.js +0 -71
- package/dist/commands/projects.js.map +0 -1
- package/dist/commands/resource.d.ts +0 -69
- package/dist/commands/resource.d.ts.map +0 -1
- package/dist/commands/resource.js +0 -579
- package/dist/commands/resource.js.map +0 -1
- package/dist/commands/resources.d.ts +0 -3
- package/dist/commands/resources.d.ts.map +0 -1
- package/dist/commands/resources.js +0 -73
- package/dist/commands/resources.js.map +0 -1
- package/dist/commands/stack.d.ts +0 -3
- package/dist/commands/stack.d.ts.map +0 -1
- package/dist/commands/stack.js +0 -97
- package/dist/commands/stack.js.map +0 -1
- package/dist/commands/stacks.d.ts +0 -3
- package/dist/commands/stacks.d.ts.map +0 -1
- package/dist/commands/stacks.js +0 -43
- package/dist/commands/stacks.js.map +0 -1
- package/dist/commands/status.d.ts +0 -3
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -71
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/ui.d.ts +0 -3
- package/dist/commands/ui.d.ts.map +0 -1
- package/dist/commands/ui.js +0 -445
- package/dist/commands/ui.js.map +0 -1
- package/dist/commands/up.d.ts +0 -3
- package/dist/commands/up.d.ts.map +0 -1
- package/dist/commands/up.js +0 -110
- package/dist/commands/up.js.map +0 -1
- package/dist/commands/upgrade.d.ts +0 -11
- package/dist/commands/upgrade.d.ts.map +0 -1
- package/dist/commands/upgrade.js +0 -496
- package/dist/commands/upgrade.js.map +0 -1
- package/dist/commands/version.d.ts +0 -3
- package/dist/commands/version.d.ts.map +0 -1
- package/dist/commands/version.js.map +0 -1
- package/dist/components/Accessible.d.ts +0 -8
- package/dist/components/Accessible.d.ts.map +0 -1
- package/dist/components/Accessible.js +0 -10
- package/dist/components/Accessible.js.map +0 -1
- package/dist/components/BaseTooltip.d.ts +0 -4
- package/dist/components/BaseTooltip.d.ts.map +0 -1
- package/dist/components/BaseTooltip.js +0 -28
- package/dist/components/BaseTooltip.js.map +0 -1
- package/dist/components/DetailPanel.d.ts +0 -4
- package/dist/components/DetailPanel.d.ts.map +0 -1
- package/dist/components/DetailPanel.js +0 -18
- package/dist/components/DetailPanel.js.map +0 -1
- package/dist/components/FileTree.d.ts +0 -4
- package/dist/components/FileTree.d.ts.map +0 -1
- package/dist/components/FileTree.js +0 -47
- package/dist/components/FileTree.js.map +0 -1
- package/dist/components/ResourceSelectInput.d.ts +0 -4
- package/dist/components/ResourceSelectInput.d.ts.map +0 -1
- package/dist/components/ResourceSelectInput.js +0 -7
- package/dist/components/ResourceSelectInput.js.map +0 -1
- package/dist/components/ResourceTable.d.ts +0 -4
- package/dist/components/ResourceTable.d.ts.map +0 -1
- package/dist/components/ResourceTable.js +0 -18
- package/dist/components/ResourceTable.js.map +0 -1
- package/dist/components/TabBar.d.ts +0 -4
- package/dist/components/TabBar.d.ts.map +0 -1
- package/dist/components/TabBar.js +0 -17
- package/dist/components/TabBar.js.map +0 -1
- package/dist/components/Tooltip.d.ts +0 -12
- package/dist/components/Tooltip.d.ts.map +0 -1
- package/dist/components/Tooltip.js +0 -12
- package/dist/components/Tooltip.js.map +0 -1
- package/dist/components/index.d.ts +0 -8
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/index.js.map +0 -1
- package/dist/config/platform-standards.d.ts +0 -159
- package/dist/config/platform-standards.d.ts.map +0 -1
- package/dist/config/platform-standards.js +0 -181
- package/dist/config/platform-standards.js.map +0 -1
- package/dist/generator/extension-fetch.d.ts +0 -25
- package/dist/generator/extension-fetch.d.ts.map +0 -1
- package/dist/generator/extension-fetch.js +0 -252
- package/dist/generator/extension-fetch.js.map +0 -1
- package/dist/generator/template-engine.d.ts +0 -61
- package/dist/generator/template-engine.d.ts.map +0 -1
- package/dist/generator/template-engine.js +0 -557
- package/dist/generator/template-engine.js.map +0 -1
- package/dist/index.d.ts +0 -11
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -11
- package/dist/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -255
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -17
- package/dist/types/index.js.map +0 -1
- package/dist/utils/cache.d.ts +0 -19
- package/dist/utils/cache.d.ts.map +0 -1
- package/dist/utils/cache.js +0 -64
- package/dist/utils/cache.js.map +0 -1
- package/dist/utils/command-helpers.d.ts +0 -17
- package/dist/utils/command-helpers.d.ts.map +0 -1
- package/dist/utils/command-helpers.js +0 -41
- package/dist/utils/command-helpers.js.map +0 -1
- package/dist/utils/constants.d.ts +0 -52
- package/dist/utils/constants.d.ts.map +0 -1
- package/dist/utils/constants.js +0 -112
- package/dist/utils/constants.js.map +0 -1
- package/dist/utils/discovery-context.d.ts +0 -4
- package/dist/utils/discovery-context.d.ts.map +0 -1
- package/dist/utils/discovery-context.js +0 -39
- package/dist/utils/discovery-context.js.map +0 -1
- package/dist/utils/docker.d.ts +0 -4
- package/dist/utils/docker.d.ts.map +0 -1
- package/dist/utils/docker.js +0 -19
- package/dist/utils/docker.js.map +0 -1
- package/dist/utils/doctor-runtime.d.ts +0 -66
- package/dist/utils/doctor-runtime.d.ts.map +0 -1
- package/dist/utils/doctor-runtime.js +0 -469
- package/dist/utils/doctor-runtime.js.map +0 -1
- package/dist/utils/env-validator.d.ts +0 -15
- package/dist/utils/env-validator.d.ts.map +0 -1
- package/dist/utils/env-validator.js +0 -109
- package/dist/utils/env-validator.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -31
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -140
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/file-helpers.d.ts +0 -14
- package/dist/utils/file-helpers.d.ts.map +0 -1
- package/dist/utils/file-helpers.js +0 -45
- package/dist/utils/file-helpers.js.map +0 -1
- package/dist/utils/formatting.d.ts +0 -30
- package/dist/utils/formatting.d.ts.map +0 -1
- package/dist/utils/formatting.js +0 -164
- package/dist/utils/formatting.js.map +0 -1
- package/dist/utils/paths.d.ts +0 -3
- package/dist/utils/paths.d.ts.map +0 -1
- package/dist/utils/paths.js +0 -41
- package/dist/utils/paths.js.map +0 -1
- package/dist/utils/port-assignment.d.ts +0 -6
- package/dist/utils/port-assignment.d.ts.map +0 -1
- package/dist/utils/port-assignment.js +0 -81
- package/dist/utils/port-assignment.js.map +0 -1
- package/dist/utils/project-features.d.ts +0 -23
- package/dist/utils/project-features.d.ts.map +0 -1
- package/dist/utils/project-features.js +0 -98
- package/dist/utils/project-features.js.map +0 -1
- package/dist/utils/project-templates.d.ts +0 -7
- package/dist/utils/project-templates.d.ts.map +0 -1
- package/dist/utils/project-templates.js +0 -24
- package/dist/utils/project-templates.js.map +0 -1
- package/dist/utils/prompt.d.ts +0 -28
- package/dist/utils/prompt.d.ts.map +0 -1
- package/dist/utils/prompt.js +0 -79
- package/dist/utils/prompt.js.map +0 -1
- package/dist/utils/resource-features.d.ts +0 -29
- package/dist/utils/resource-features.d.ts.map +0 -1
- package/dist/utils/resource-features.js +0 -171
- package/dist/utils/resource-features.js.map +0 -1
- package/dist/utils/service-urls.d.ts +0 -30
- package/dist/utils/service-urls.d.ts.map +0 -1
- package/dist/utils/service-urls.js +0 -93
- package/dist/utils/service-urls.js.map +0 -1
- package/dist/utils/services.d.ts +0 -18
- package/dist/utils/services.d.ts.map +0 -1
- package/dist/utils/services.js +0 -291
- package/dist/utils/services.js.map +0 -1
- package/dist/utils/stack-features.d.ts +0 -22
- package/dist/utils/stack-features.d.ts.map +0 -1
- package/dist/utils/stack-features.js +0 -13
- package/dist/utils/stack-features.js.map +0 -1
- package/dist/utils/tar.d.ts +0 -7
- package/dist/utils/tar.d.ts.map +0 -1
- package/dist/utils/tar.js +0 -21
- package/dist/utils/tar.js.map +0 -1
- package/dist/utils/tilt.d.ts +0 -19
- package/dist/utils/tilt.d.ts.map +0 -1
- package/dist/utils/tilt.js +0 -78
- package/dist/utils/tilt.js.map +0 -1
- package/dist/utils/validation.d.ts +0 -16
- package/dist/utils/validation.d.ts.map +0 -1
- package/dist/utils/validation.js +0 -58
- package/dist/utils/validation.js.map +0 -1
- /package/{README.md → cli/README.md} +0 -0
- /package/{bin → cli/bin}/tdk.js +0 -0
- /package/{templates → cli/templates}/.tiltignore.hbs +0 -0
- /package/{templates → cli/templates}/TILT_RESOURCE_DEFAULTS.star.hbs +0 -0
- /package/{templates → cli/templates}/TILT_TECH_STACK.star.hbs +0 -0
- /package/{templates → cli/templates}/spec.master.hbs +0 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// Covers the exact three scenarios demonstrated manually on the real CLI:
|
|
2
|
+
// 1. no TDK_LICENSE_KEY set -> free tier, zero network calls
|
|
3
|
+
// 2. TDK_LICENSE_KEY set but invalid -> "no premium resources unlocked", free tier
|
|
4
|
+
// 3. TDK_LICENSE_KEY set and valid -> premium files applied over the stubs
|
|
5
|
+
//
|
|
6
|
+
// Only the network boundary (fetch) is mocked - tar extraction, the file
|
|
7
|
+
// overlay, and the on-disk cache all run for real against temp directories,
|
|
8
|
+
// so this exercises the same code path `tdk project` actually runs.
|
|
9
|
+
|
|
10
|
+
import { execFileSync } from "node:child_process";
|
|
11
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, utimesSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
15
|
+
|
|
16
|
+
let projectRoot: string;
|
|
17
|
+
let destDir: string;
|
|
18
|
+
let fakeHome: string;
|
|
19
|
+
let fixtureTarball: Buffer;
|
|
20
|
+
|
|
21
|
+
function buildFixtureTarball(): Buffer {
|
|
22
|
+
const src = mkdtempSync(join(tmpdir(), "tdk-premium-fixture-"));
|
|
23
|
+
mkdirSync(join(src, "typescript"), { recursive: true });
|
|
24
|
+
writeFileSync(
|
|
25
|
+
join(src, "typescript", "playwright_config.star"),
|
|
26
|
+
"# REAL premium playwright config\ndef generate_playwright_config(): pass\n"
|
|
27
|
+
);
|
|
28
|
+
writeFileSync(join(src, "c4_diagram.star"), "# REAL premium c4 diagram generator\n");
|
|
29
|
+
|
|
30
|
+
// Match exactly how the real premium.tar.gz is built (tar -C dir .), so
|
|
31
|
+
// entries get a "./" prefix - extractTarball's --strip-components=1
|
|
32
|
+
// expects that dummy leading component, not a real directory name.
|
|
33
|
+
const tarPath = join(src, "..", "premium-fixture.tar.gz");
|
|
34
|
+
execFileSync("tar", ["-czf", tarPath, "-C", src, "."]);
|
|
35
|
+
const buf = readFileSync(tarPath);
|
|
36
|
+
rmSync(src, { recursive: true, force: true });
|
|
37
|
+
rmSync(tarPath, { force: true });
|
|
38
|
+
return buf;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
projectRoot = mkdtempSync(join(tmpdir(), "tdk-project-"));
|
|
43
|
+
destDir = mkdtempSync(join(tmpdir(), "tdk-dest-"));
|
|
44
|
+
fakeHome = mkdtempSync(join(tmpdir(), "tdk-home-"));
|
|
45
|
+
|
|
46
|
+
// Seed destDir with the free-tier stub, same as vendorTdkExtension does
|
|
47
|
+
// before ever calling applyPremiumOverlay - so we can assert it gets
|
|
48
|
+
// overwritten only when a premium fetch actually succeeds.
|
|
49
|
+
mkdirSync(join(destDir, "engine/topologies/tilt/generators/typescript"), { recursive: true });
|
|
50
|
+
writeFileSync(
|
|
51
|
+
join(destDir, "engine/topologies/tilt/generators/typescript/playwright_config.star"),
|
|
52
|
+
"# unlicensed stub\n"
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
process.env.HOME = fakeHome;
|
|
56
|
+
delete process.env.TDK_LICENSE_KEY;
|
|
57
|
+
delete process.env.TDK_PREMIUM_ENDPOINT;
|
|
58
|
+
vi.restoreAllMocks();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
afterEach(() => {
|
|
62
|
+
rmSync(projectRoot, { recursive: true, force: true });
|
|
63
|
+
rmSync(destDir, { recursive: true, force: true });
|
|
64
|
+
rmSync(fakeHome, { recursive: true, force: true });
|
|
65
|
+
vi.unstubAllGlobals();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("applyPremiumOverlay", () => {
|
|
69
|
+
it("scenario 1: no license key -> returns false, never calls fetch", async () => {
|
|
70
|
+
const fetchSpy = vi.fn();
|
|
71
|
+
vi.stubGlobal("fetch", fetchSpy);
|
|
72
|
+
|
|
73
|
+
const { applyPremiumOverlay } = await import("../extension-fetch.js");
|
|
74
|
+
const applied = await applyPremiumOverlay(projectRoot, destDir);
|
|
75
|
+
|
|
76
|
+
expect(applied).toBe(false);
|
|
77
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
78
|
+
// Stub must be left untouched - zero side effects for free users.
|
|
79
|
+
expect(
|
|
80
|
+
readFileSync(
|
|
81
|
+
join(destDir, "engine/topologies/tilt/generators/typescript/playwright_config.star"),
|
|
82
|
+
"utf-8"
|
|
83
|
+
)
|
|
84
|
+
).toBe("# unlicensed stub\n");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("scenario 2: invalid/ungranted key -> tries every known resource, then falls back to free tier", async () => {
|
|
88
|
+
process.env.TDK_LICENSE_KEY = "tdk-fa411b"; // the mistyped key from the real session
|
|
89
|
+
|
|
90
|
+
const fetchSpy = vi.fn().mockResolvedValue(new Response("key does not grant resource", { status: 403 }));
|
|
91
|
+
vi.stubGlobal("fetch", fetchSpy);
|
|
92
|
+
|
|
93
|
+
const { applyPremiumOverlay } = await import("../extension-fetch.js");
|
|
94
|
+
const applied = await applyPremiumOverlay(projectRoot, destDir);
|
|
95
|
+
|
|
96
|
+
expect(applied).toBe(false);
|
|
97
|
+
// Every known resource name gets a shot before giving up.
|
|
98
|
+
expect(fetchSpy).toHaveBeenCalledTimes(6);
|
|
99
|
+
for (const call of fetchSpy.mock.calls) {
|
|
100
|
+
const url = call[0] as string;
|
|
101
|
+
expect(url).toContain("key=tdk-fa411b");
|
|
102
|
+
}
|
|
103
|
+
expect(
|
|
104
|
+
readFileSync(
|
|
105
|
+
join(destDir, "engine/topologies/tilt/generators/typescript/playwright_config.star"),
|
|
106
|
+
"utf-8"
|
|
107
|
+
)
|
|
108
|
+
).toBe("# unlicensed stub\n");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("scenario 3: valid key granting the first resource -> real files overlay the stubs", async () => {
|
|
112
|
+
process.env.TDK_LICENSE_KEY = "tdk-fa411a"; // the real key from the session, grants playwright
|
|
113
|
+
fixtureTarball ??= buildFixtureTarball();
|
|
114
|
+
|
|
115
|
+
const fetchSpy = vi.fn().mockImplementation((url: string) => {
|
|
116
|
+
if (url.includes("resource=playwright")) {
|
|
117
|
+
return Promise.resolve(new Response(new Uint8Array(fixtureTarball), { status: 200 }));
|
|
118
|
+
}
|
|
119
|
+
return Promise.resolve(new Response("not granted", { status: 403 }));
|
|
120
|
+
});
|
|
121
|
+
vi.stubGlobal("fetch", fetchSpy);
|
|
122
|
+
|
|
123
|
+
const { applyPremiumOverlay } = await import("../extension-fetch.js");
|
|
124
|
+
const applied = await applyPremiumOverlay(projectRoot, destDir);
|
|
125
|
+
|
|
126
|
+
expect(applied).toBe(true);
|
|
127
|
+
// Stops at the first granted resource - doesn't keep trying c4-diagram/logging.
|
|
128
|
+
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
129
|
+
|
|
130
|
+
const playwrightOut = readFileSync(
|
|
131
|
+
join(destDir, "engine/topologies/tilt/generators/typescript/playwright_config.star"),
|
|
132
|
+
"utf-8"
|
|
133
|
+
);
|
|
134
|
+
expect(playwrightOut).toContain("REAL premium playwright config");
|
|
135
|
+
expect(playwrightOut).not.toContain("unlicensed stub");
|
|
136
|
+
|
|
137
|
+
const c4Out = readFileSync(join(destDir, "engine/topologies/tilt/generators/c4_diagram.star"), "utf-8");
|
|
138
|
+
expect(c4Out).toContain("REAL premium c4 diagram generator");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("sends the per-project id and reuses it across calls instead of generating a new one each time", async () => {
|
|
142
|
+
process.env.TDK_LICENSE_KEY = "tdk-fa411b";
|
|
143
|
+
const seenProjectIds: string[] = [];
|
|
144
|
+
const fetchSpy = vi.fn().mockImplementation((url: string) => {
|
|
145
|
+
seenProjectIds.push(new URL(url).searchParams.get("projectId")!);
|
|
146
|
+
return Promise.resolve(new Response("not granted", { status: 403 }));
|
|
147
|
+
});
|
|
148
|
+
vi.stubGlobal("fetch", fetchSpy);
|
|
149
|
+
|
|
150
|
+
const { applyPremiumOverlay } = await import("../extension-fetch.js");
|
|
151
|
+
await applyPremiumOverlay(projectRoot, destDir);
|
|
152
|
+
const idFile = join(projectRoot, ".tdk", ".project-id");
|
|
153
|
+
expect(existsSync(idFile)).toBe(true);
|
|
154
|
+
const persistedId = readFileSync(idFile, "utf-8").trim();
|
|
155
|
+
|
|
156
|
+
expect(new Set(seenProjectIds).size).toBe(1);
|
|
157
|
+
expect(seenProjectIds[0]).toBe(persistedId);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("caches a successful fetch and does not re-fetch within the TTL", async () => {
|
|
161
|
+
process.env.TDK_LICENSE_KEY = "tdk-fa411a";
|
|
162
|
+
fixtureTarball ??= buildFixtureTarball();
|
|
163
|
+
|
|
164
|
+
const fetchSpy = vi.fn().mockResolvedValue(new Response(new Uint8Array(fixtureTarball), { status: 200 }));
|
|
165
|
+
vi.stubGlobal("fetch", fetchSpy);
|
|
166
|
+
|
|
167
|
+
const { applyPremiumOverlay } = await import("../extension-fetch.js");
|
|
168
|
+
await applyPremiumOverlay(projectRoot, destDir);
|
|
169
|
+
await applyPremiumOverlay(projectRoot, destDir);
|
|
170
|
+
|
|
171
|
+
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("re-fetches once the cached bundle is older than the TTL", async () => {
|
|
175
|
+
process.env.TDK_LICENSE_KEY = "tdk-fa411a";
|
|
176
|
+
fixtureTarball ??= buildFixtureTarball();
|
|
177
|
+
|
|
178
|
+
// A Response body can only be read once - mockResolvedValue would
|
|
179
|
+
// reuse the same instance across both calls in this test, so build a
|
|
180
|
+
// fresh Response per invocation instead.
|
|
181
|
+
const fetchSpy = vi.fn().mockImplementation(() => Promise.resolve(new Response(new Uint8Array(fixtureTarball), { status: 200 })));
|
|
182
|
+
vi.stubGlobal("fetch", fetchSpy);
|
|
183
|
+
|
|
184
|
+
const { applyPremiumOverlay } = await import("../extension-fetch.js");
|
|
185
|
+
await applyPremiumOverlay(projectRoot, destDir);
|
|
186
|
+
|
|
187
|
+
const cachedPath = join(fakeHome, ".tdk", "cache", "premium-tdk-fa411a", "premium.tar.gz");
|
|
188
|
+
expect(existsSync(cachedPath)).toBe(true);
|
|
189
|
+
const longAgo = new Date(Date.now() - 13 * 60 * 60 * 1000); // TTL is 12h
|
|
190
|
+
utimesSync(cachedPath, longAgo, longAgo);
|
|
191
|
+
|
|
192
|
+
await applyPremiumOverlay(projectRoot, destDir);
|
|
193
|
+
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("switching keys uses a separate cache instead of serving the other key's bundle", async () => {
|
|
197
|
+
fixtureTarball ??= buildFixtureTarball();
|
|
198
|
+
|
|
199
|
+
process.env.TDK_LICENSE_KEY = "tdk-fa411a";
|
|
200
|
+
vi.stubGlobal("fetch", vi.fn().mockResolvedValue(new Response(new Uint8Array(fixtureTarball), { status: 200 })));
|
|
201
|
+
const { applyPremiumOverlay } = await import("../extension-fetch.js");
|
|
202
|
+
await applyPremiumOverlay(projectRoot, destDir);
|
|
203
|
+
|
|
204
|
+
process.env.TDK_LICENSE_KEY = "tdk-fa411b";
|
|
205
|
+
const secondFetch = vi.fn().mockResolvedValue(new Response("not granted", { status: 403 }));
|
|
206
|
+
vi.stubGlobal("fetch", secondFetch);
|
|
207
|
+
const applied = await applyPremiumOverlay(projectRoot, destDir);
|
|
208
|
+
|
|
209
|
+
expect(applied).toBe(false);
|
|
210
|
+
expect(secondFetch).toHaveBeenCalled();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("a network error is caught, never throws, and falls back to the free tier", async () => {
|
|
214
|
+
process.env.TDK_LICENSE_KEY = "tdk-fa411a";
|
|
215
|
+
vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("network unreachable")));
|
|
216
|
+
|
|
217
|
+
const { applyPremiumOverlay } = await import("../extension-fetch.js");
|
|
218
|
+
await expect(applyPremiumOverlay(projectRoot, destDir)).resolves.toBe(false);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { loadTemplate } from "../template-engine.js";
|
|
6
|
+
|
|
7
|
+
// Real template files, keyed by a substring that only appears in that specific
|
|
8
|
+
// file - regression coverage for the bug where loadTemplate joined a candidate
|
|
9
|
+
// *directory* (not the file) and silently fell through every candidate.
|
|
10
|
+
const REAL_TEMPLATES: Record<string, string> = {
|
|
11
|
+
"TILT_RESOURCE_DEFAULTS.star.hbs": "get_resource_defaults",
|
|
12
|
+
"TILT_TECH_STACK.star.hbs": "get_tech_stack",
|
|
13
|
+
"Tiltfile.hbs": "Tiltfile",
|
|
14
|
+
".tiltignore.hbs": "TILT IGNORE FILE",
|
|
15
|
+
"spec.master.hbs": "spec.master",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
describe("template-engine", () => {
|
|
19
|
+
describe("loadTemplate", () => {
|
|
20
|
+
for (const [filename, marker] of Object.entries(REAL_TEMPLATES)) {
|
|
21
|
+
it(`should load ${filename} and return its own content`, () => {
|
|
22
|
+
const content = loadTemplate(filename);
|
|
23
|
+
expect(typeof content).toBe("string");
|
|
24
|
+
expect(content.length).toBeGreaterThan(0);
|
|
25
|
+
expect(content).toContain(marker);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
it("should return distinct content for each template (not the same file/dir every time)", () => {
|
|
30
|
+
const contents = Object.keys(REAL_TEMPLATES).map((filename) => loadTemplate(filename));
|
|
31
|
+
expect(new Set(contents).size).toBe(contents.length);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should throw (not silently return directory bytes) for non-existent template", () => {
|
|
35
|
+
expect(() => loadTemplate("non-existent.hbs")).toThrow("Failed to load template");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("executable-relative fallback (compiled binary layout)", () => {
|
|
39
|
+
let fakeExecDir: string;
|
|
40
|
+
let originalExecPath: string;
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
// Mirrors what scripts/release-binaries.sh bundles next to the
|
|
44
|
+
// compiled binary: <exeDir>/tdk-cli/cli/templates/<filename>.
|
|
45
|
+
fakeExecDir = fs.mkdtempSync(path.join(os.tmpdir(), "tdk-exec-"));
|
|
46
|
+
const bundledTemplatesDir = path.join(fakeExecDir, "tdk-cli", "cli", "templates");
|
|
47
|
+
fs.mkdirSync(bundledTemplatesDir, { recursive: true });
|
|
48
|
+
fs.writeFileSync(path.join(bundledTemplatesDir, "only-in-bundle.hbs"), "bundled-marker");
|
|
49
|
+
|
|
50
|
+
originalExecPath = process.execPath;
|
|
51
|
+
Object.defineProperty(process, "execPath", {
|
|
52
|
+
value: path.join(fakeExecDir, "tdk"),
|
|
53
|
+
configurable: true,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
Object.defineProperty(process, "execPath", {
|
|
59
|
+
value: originalExecPath,
|
|
60
|
+
configurable: true,
|
|
61
|
+
});
|
|
62
|
+
fs.rmSync(fakeExecDir, { recursive: true, force: true });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("finds a template that only exists next to the executable", () => {
|
|
66
|
+
expect(loadTemplate("only-in-bundle.hbs")).toBe("bundled-marker");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// Fetches the paid-tier "premium" resource bundle (playwright, c4-diagram,
|
|
2
|
+
// logging, agents-md, sablier, verdaccio, and a few other extras - see
|
|
3
|
+
// tdk-cli-extensions/premium/) from the gated distribution worker, when a
|
|
4
|
+
// license key is configured. The free engine never needs this for its own
|
|
5
|
+
// resources - docker-compose, npm, bun, etc. are already public in this
|
|
6
|
+
// repo, no key required.
|
|
7
|
+
//
|
|
8
|
+
// DDD (domain-driven-design scaffolding) is the one paid feature that is
|
|
9
|
+
// NOT a file overlay: it's gated separately by hasDddLicense() below, via
|
|
10
|
+
// the shared hasLiveResourceLicense() helper, since its code stays in this
|
|
11
|
+
// public repo and is turned on/off by a runtime check rather than swapped
|
|
12
|
+
// in - see that helper's own comment for why it can't just be added to
|
|
13
|
+
// KNOWN_RESOURCES. hasVerdaccioLicense()/hasSablierLicense() also use that
|
|
14
|
+
// same helper, but only to print a "this needs a license" warning in
|
|
15
|
+
// template-engine.ts - Verdaccio and Sablier's real gate is the file
|
|
16
|
+
// overlay above (their public files are disabled stubs, not gated code).
|
|
17
|
+
//
|
|
18
|
+
// The worker (tdk-extension-dist) is the real access-control boundary: it
|
|
19
|
+
// checks the key against BUNDLES_REPO/keys/*.json - existence, activation/
|
|
20
|
+
// expiry dates, granted resources, and a per-key project limit - before
|
|
21
|
+
// serving anything. This client just asks and, if granted, unpacks the
|
|
22
|
+
// result over the vendored stub files so the generator pipeline picks up
|
|
23
|
+
// the real implementations transparently. See
|
|
24
|
+
// tdk-extension-dist/src/index.ts for the server-side enforcement and
|
|
25
|
+
// tdk-extension-dist/src/project-limit.ts for the project-limit logic.
|
|
26
|
+
|
|
27
|
+
import { randomUUID } from "node:crypto";
|
|
28
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
29
|
+
import { homedir } from "node:os";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
import { extractTarball } from "../utils/tar.js";
|
|
32
|
+
|
|
33
|
+
const DEFAULT_ENDPOINT = "https://tdk-extension-dist.oranguman.workers.dev/v1/premium/bundle.tar.gz";
|
|
34
|
+
|
|
35
|
+
// Tried in order until one is granted - the worker unlocks the whole
|
|
36
|
+
// bundle on the first resource name a key is found to grant, since
|
|
37
|
+
// premium.tar.gz ships all paid resources together. Keep in sync with
|
|
38
|
+
// what issue-key.yml/update-key.yml accept as `resources` entries.
|
|
39
|
+
const KNOWN_RESOURCES = ["playwright", "c4-diagram", "logging", "agents-md", "sablier", "verdaccio"];
|
|
40
|
+
|
|
41
|
+
// Re-check the license periodically rather than trusting a local cache
|
|
42
|
+
// forever - an expired or revoked key shouldn't keep unlocking premium
|
|
43
|
+
// content indefinitely just because it worked once.
|
|
44
|
+
const CACHE_TTL_MS = 12 * 60 * 60 * 1000;
|
|
45
|
+
|
|
46
|
+
// Maps each file's path inside premium.tar.gz to where it belongs in the
|
|
47
|
+
// vendored engine output, so the generator pipeline finds it in the same
|
|
48
|
+
// place the free stub (or, for the never-wired items, nothing at all)
|
|
49
|
+
// used to be.
|
|
50
|
+
const PREMIUM_PATH_MAP: Record<string, string> = {
|
|
51
|
+
"typescript/playwright_config.star":
|
|
52
|
+
"engine/topologies/tilt/generators/typescript/playwright_config.star",
|
|
53
|
+
"generators/agents_md.star": "engine/topologies/tilt/generators/agents_md.star",
|
|
54
|
+
"c4_diagram.star": "engine/topologies/tilt/generators/c4_diagram.star",
|
|
55
|
+
"observability/logging.star": "engine/topologies/platform/observability/logging.star",
|
|
56
|
+
"generators/database_provisioner.star":
|
|
57
|
+
"engine/topologies/tilt/generators/database_provisioner.star",
|
|
58
|
+
"generators/dependency_manifest.star":
|
|
59
|
+
"engine/topologies/tilt/generators/dependency_manifest.star",
|
|
60
|
+
"generators/fixed_frontend_tsconfig.star":
|
|
61
|
+
"engine/topologies/tilt/generators/typescript/fixed_frontend_tsconfig.star",
|
|
62
|
+
"synthetic_monitor.star": "engine/resources/synthetic_monitor.star",
|
|
63
|
+
"synthetic-monitor-topology.star": "engine/topologies/platform/services/synthetic-monitor.star",
|
|
64
|
+
"database-management-service.yaml":
|
|
65
|
+
"platform/services/platform/database-management/service.yaml",
|
|
66
|
+
"networking/sablier_container_cycle.star":
|
|
67
|
+
"engine/topologies/platform/docker/networking/sablier_container_cycle.star",
|
|
68
|
+
"registries/verdaccio_loader.star": "engine/topologies/platform/registries/verdaccio_loader.star",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
function getLicenseKey(): string | null {
|
|
72
|
+
return process.env.TDK_LICENSE_KEY?.trim() || null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getEndpoint(): string {
|
|
76
|
+
return process.env.TDK_PREMIUM_ENDPOINT?.trim() || DEFAULT_ENDPOINT;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// A stable per-project id for the worker's project-limit tracking - not a
|
|
80
|
+
// device or user id. Generated once and persisted alongside the rest of
|
|
81
|
+
// the project's .tdk/ state.
|
|
82
|
+
function getOrCreateProjectId(projectRoot: string): string {
|
|
83
|
+
const idPath = join(projectRoot, ".tdk", ".project-id");
|
|
84
|
+
if (existsSync(idPath)) {
|
|
85
|
+
return readFileSync(idPath, "utf-8").trim();
|
|
86
|
+
}
|
|
87
|
+
const id = randomUUID();
|
|
88
|
+
mkdirSync(join(projectRoot, ".tdk"), { recursive: true });
|
|
89
|
+
writeFileSync(idPath, id + "\n");
|
|
90
|
+
return id;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function cacheTarballPath(key: string): string {
|
|
94
|
+
// Cache is per-key (not shared across different keys on the same
|
|
95
|
+
// machine) so switching license keys doesn't serve a stale bundle
|
|
96
|
+
// fetched under a different plan.
|
|
97
|
+
const safeName = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
98
|
+
return join(homedir(), ".tdk", "cache", `premium-${safeName}`, "premium.tar.gz");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isCacheFresh(path: string): boolean {
|
|
102
|
+
if (!existsSync(path)) return false;
|
|
103
|
+
return Date.now() - statSync(path).mtimeMs < CACHE_TTL_MS;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function fetchPremiumBundle(key: string, projectId: string): Promise<Buffer | null> {
|
|
107
|
+
const endpoint = getEndpoint();
|
|
108
|
+
for (const resource of KNOWN_RESOURCES) {
|
|
109
|
+
const url =
|
|
110
|
+
`${endpoint}?key=${encodeURIComponent(key)}` +
|
|
111
|
+
`&resource=${encodeURIComponent(resource)}` +
|
|
112
|
+
`&projectId=${encodeURIComponent(projectId)}`;
|
|
113
|
+
|
|
114
|
+
let res: Response;
|
|
115
|
+
try {
|
|
116
|
+
res = await fetch(url);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
console.warn(`⚠️ Premium fetch failed (network error): ${(err as Error).message}`);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (res.ok) {
|
|
123
|
+
return Buffer.from(await res.arrayBuffer());
|
|
124
|
+
}
|
|
125
|
+
if (res.status === 401 || res.status === 403) {
|
|
126
|
+
// Not granted for this specific resource name - try the next one
|
|
127
|
+
// before concluding the key has nothing to offer.
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
// Anything else (404 missing bundle release, 502 upstream failure,
|
|
131
|
+
// etc.) won't be fixed by trying a different resource name.
|
|
132
|
+
console.warn(`⚠️ Premium fetch failed: HTTP ${res.status}`);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Fetches (with a local, time-limited cache) the premium bundle for the
|
|
140
|
+
* configured license key and overlays its files onto the already-vendored
|
|
141
|
+
* free engine at destDir. Best-effort: returns false (never throws) if
|
|
142
|
+
* there's no key configured, the key doesn't grant anything, or the fetch
|
|
143
|
+
* fails - the caller should keep going with the free engine either way.
|
|
144
|
+
*/
|
|
145
|
+
export async function applyPremiumOverlay(projectRoot: string, destDir: string): Promise<boolean> {
|
|
146
|
+
const key = getLicenseKey();
|
|
147
|
+
if (!key) return false;
|
|
148
|
+
|
|
149
|
+
const tarballPath = cacheTarballPath(key);
|
|
150
|
+
|
|
151
|
+
if (!isCacheFresh(tarballPath)) {
|
|
152
|
+
const projectId = getOrCreateProjectId(projectRoot);
|
|
153
|
+
const bundle = await fetchPremiumBundle(key, projectId);
|
|
154
|
+
if (!bundle) {
|
|
155
|
+
console.warn("⚠️ No premium resources unlocked for this key - using free tier only.");
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
mkdirSync(join(tarballPath, ".."), { recursive: true });
|
|
159
|
+
writeFileSync(tarballPath, bundle);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const extractDir = join(tarballPath, "..", "extracted");
|
|
164
|
+
mkdirSync(extractDir, { recursive: true });
|
|
165
|
+
extractTarball(readFileSync(tarballPath), extractDir);
|
|
166
|
+
|
|
167
|
+
let applied = 0;
|
|
168
|
+
for (const [src, dest] of Object.entries(PREMIUM_PATH_MAP)) {
|
|
169
|
+
const from = join(extractDir, src);
|
|
170
|
+
if (!existsSync(from)) continue;
|
|
171
|
+
const to = join(destDir, dest);
|
|
172
|
+
mkdirSync(join(to, ".."), { recursive: true });
|
|
173
|
+
writeFileSync(to, readFileSync(from));
|
|
174
|
+
applied++;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (applied > 0) {
|
|
178
|
+
console.log(`✓ Premium resources unlocked (${applied} file${applied === 1 ? "" : "s"})`);
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
return false;
|
|
182
|
+
} catch (err) {
|
|
183
|
+
console.warn(`⚠️ Failed to apply premium overlay: ${(err as Error).message}`);
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function liveResourceAccessCachePath(key: string, resource: string): string {
|
|
189
|
+
const safeName = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
190
|
+
return join(homedir(), ".tdk", "cache", `premium-${safeName}`, `${resource}-access.json`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Checks whether the configured license key grants a given "live" premium
|
|
195
|
+
* resource. Originally added for resources with nothing to swap on disk
|
|
196
|
+
* (ddd is still exactly that: gated code baked into the free engine, no
|
|
197
|
+
* file overlay). verdaccio and sablier also use this - not because they
|
|
198
|
+
* lack a PREMIUM_PATH_MAP entry (they have one now), but because their
|
|
199
|
+
* public files are already disabled stubs regardless of license, so this
|
|
200
|
+
* check only ever drives a "you need a license for this" warning in
|
|
201
|
+
* template-engine.ts, never an actual enable/disable decision. Can't reuse
|
|
202
|
+
* KNOWN_RESOURCES/fetchPremiumBundle for that warning either: that loop
|
|
203
|
+
* stops at the FIRST resource the key grants and would report false for a
|
|
204
|
+
* key that grants this resource but not, say, playwright (or vice versa).
|
|
205
|
+
*
|
|
206
|
+
* Best-effort: returns false (never throws) if there's no key, the fetch
|
|
207
|
+
* fails, or the worker denies the resource - the caller decides what "not
|
|
208
|
+
* licensed" means. Caches the grant/deny outcome (not the bundle itself,
|
|
209
|
+
* which we never need here) for CACHE_TTL_MS so repeated checks don't
|
|
210
|
+
* re-download the full premium.tar.gz just to read a status code.
|
|
211
|
+
*/
|
|
212
|
+
async function hasLiveResourceLicense(projectRoot: string, resource: string): Promise<boolean> {
|
|
213
|
+
const key = getLicenseKey();
|
|
214
|
+
if (!key) return false;
|
|
215
|
+
|
|
216
|
+
const cachePath = liveResourceAccessCachePath(key, resource);
|
|
217
|
+
if (isCacheFresh(cachePath)) {
|
|
218
|
+
try {
|
|
219
|
+
const cached = JSON.parse(readFileSync(cachePath, "utf-8")) as { granted: boolean };
|
|
220
|
+
return cached.granted === true;
|
|
221
|
+
} catch {
|
|
222
|
+
// Corrupt/unreadable cache - fall through and re-check live.
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const projectId = getOrCreateProjectId(projectRoot);
|
|
227
|
+
const endpoint = getEndpoint();
|
|
228
|
+
const url =
|
|
229
|
+
`${endpoint}?key=${encodeURIComponent(key)}` +
|
|
230
|
+
`&resource=${encodeURIComponent(resource)}&projectId=${encodeURIComponent(projectId)}`;
|
|
231
|
+
|
|
232
|
+
let granted = false;
|
|
233
|
+
try {
|
|
234
|
+
const res = await fetch(url);
|
|
235
|
+
granted = res.ok;
|
|
236
|
+
// Drain the body (a full premium.tar.gz on success) so we don't leave
|
|
237
|
+
// it dangling - we only need the status, not the content.
|
|
238
|
+
if (res.body) {
|
|
239
|
+
await res.arrayBuffer().catch(() => undefined);
|
|
240
|
+
}
|
|
241
|
+
} catch (err) {
|
|
242
|
+
console.warn(`⚠️ ${resource} license check failed (network error): ${(err as Error).message}`);
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
mkdirSync(join(cachePath, ".."), { recursive: true });
|
|
248
|
+
writeFileSync(cachePath, JSON.stringify({ granted, checked: new Date().toISOString() }));
|
|
249
|
+
} catch {
|
|
250
|
+
// Best-effort cache; a write failure just means we re-check next time.
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return granted;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Checks whether the configured license key grants the "verdaccio" resource. */
|
|
257
|
+
export async function hasVerdaccioLicense(projectRoot: string): Promise<boolean> {
|
|
258
|
+
return hasLiveResourceLicense(projectRoot, "verdaccio");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Checks whether the configured license key grants the "ddd" resource
|
|
263
|
+
* (domain-driven-design folder structure + path aliases for backend
|
|
264
|
+
* resources - see generate_backend_path_aliases() in
|
|
265
|
+
* engine/topologies/tilt/generators/vite/helpers.star).
|
|
266
|
+
*/
|
|
267
|
+
export async function hasDddLicense(projectRoot: string): Promise<boolean> {
|
|
268
|
+
return hasLiveResourceLicense(projectRoot, "ddd");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Checks whether the configured license key grants the "sablier" resource
|
|
273
|
+
* (on-demand start/stop for idle resources via a `sablier: {enable: true}`
|
|
274
|
+
* manifest block - see sablier_container_cycle.star in
|
|
275
|
+
* engine/topologies/platform/docker/networking/).
|
|
276
|
+
*/
|
|
277
|
+
export async function hasSablierLicense(projectRoot: string): Promise<boolean> {
|
|
278
|
+
return hasLiveResourceLicense(projectRoot, "sablier");
|
|
279
|
+
}
|