@tdk-landscape/tdk-cli-core 1.3.44 → 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.
Files changed (431) hide show
  1. package/.github/workflows/release-binaries.yml +54 -0
  2. package/.pre-commit-config.yaml +57 -0
  3. package/Makefile +92 -0
  4. package/bun.lock +538 -0
  5. package/cli/AGENTS.md +390 -0
  6. package/cli/DESIGN.md +735 -0
  7. package/cli/biome.json +44 -0
  8. package/cli/dependency-graph.svg +1135 -0
  9. package/cli/knip.json +6 -0
  10. package/cli/package.json +77 -0
  11. package/cli/pnpm-lock.yaml +2305 -0
  12. package/cli/pnpm-workspace.yaml +2 -0
  13. package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +23 -0
  14. package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +464 -0
  15. package/cli/shared-platform-engineering/docker-templates/install-deps.sh +152 -0
  16. package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +12 -0
  17. package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +56 -0
  18. package/{dist/cli.js → cli/src/cli.ts} +21 -13
  19. package/cli/src/commands/__tests__/always-enabled-infra.test.ts +115 -0
  20. package/cli/src/commands/__tests__/config.test.ts +146 -0
  21. package/cli/src/commands/__tests__/doctor.test.ts +557 -0
  22. package/cli/src/commands/__tests__/error-handling.test.ts +78 -0
  23. package/cli/src/commands/__tests__/phases-model.test.ts +163 -0
  24. package/cli/src/commands/__tests__/project-resource-features-e2e.test.ts +207 -0
  25. package/cli/src/commands/__tests__/project.test.ts +68 -0
  26. package/cli/src/commands/__tests__/resource.test.ts +226 -0
  27. package/cli/src/commands/__tests__/saas-starter-integration.test.ts +196 -0
  28. package/cli/src/commands/__tests__/stack-features-e2e.test.ts +129 -0
  29. package/cli/src/commands/__tests__/upgrade.test.ts +101 -0
  30. package/{dist/commands/completion.js → cli/src/commands/completion.ts} +78 -72
  31. package/cli/src/commands/config.ts +299 -0
  32. package/cli/src/commands/doctor.ts +790 -0
  33. package/cli/src/commands/down.ts +37 -0
  34. package/cli/src/commands/help.ts +150 -0
  35. package/cli/src/commands/networks-design.md +80 -0
  36. package/cli/src/commands/networks.ts +332 -0
  37. package/cli/src/commands/project.ts +439 -0
  38. package/cli/src/commands/projects.ts +82 -0
  39. package/cli/src/commands/resource.ts +652 -0
  40. package/cli/src/commands/resources.ts +95 -0
  41. package/cli/src/commands/stack.ts +128 -0
  42. package/cli/src/commands/stacks.ts +52 -0
  43. package/cli/src/commands/status.ts +88 -0
  44. package/cli/src/commands/ui.tsx +843 -0
  45. package/cli/src/commands/up.ts +150 -0
  46. package/cli/src/commands/upgrade.ts +570 -0
  47. package/{dist/commands/version.js → cli/src/commands/version.ts} +5 -5
  48. package/cli/src/components/Accessible.tsx +21 -0
  49. package/cli/src/components/BaseTooltip.tsx +66 -0
  50. package/cli/src/components/DetailPanel.tsx +146 -0
  51. package/cli/src/components/FileTree.tsx +89 -0
  52. package/cli/src/components/ResourceSelectInput.tsx +30 -0
  53. package/cli/src/components/ResourceTable.tsx +78 -0
  54. package/cli/src/components/TabBar.tsx +53 -0
  55. package/cli/src/components/Tooltip.tsx +11 -0
  56. package/{dist/components/index.js → cli/src/components/index.ts} +0 -1
  57. package/cli/src/config/platform-standards.test.ts +11 -0
  58. package/cli/src/config/platform-standards.ts +196 -0
  59. package/cli/src/generator/__tests__/extension-fetch.test.ts +220 -0
  60. package/cli/src/generator/__tests__/template-engine.test.ts +70 -0
  61. package/cli/src/generator/extension-fetch.ts +279 -0
  62. package/cli/src/generator/template-engine.ts +754 -0
  63. package/cli/src/index.ts +86 -0
  64. package/cli/src/templates.d.ts +4 -0
  65. package/cli/src/types/index.ts +351 -0
  66. package/cli/src/utils/__tests__/cache.test.ts +98 -0
  67. package/cli/src/utils/__tests__/formatting.test.ts +170 -0
  68. package/cli/src/utils/__tests__/paths.test.ts +180 -0
  69. package/cli/src/utils/__tests__/port-assignment.test.ts +121 -0
  70. package/cli/src/utils/__tests__/service-urls.test.ts +174 -0
  71. package/cli/src/utils/__tests__/services.test.ts +531 -0
  72. package/cli/src/utils/__tests__/tilt.test.ts +143 -0
  73. package/cli/src/utils/__tests__/validation.test.ts +148 -0
  74. package/cli/src/utils/cache.ts +76 -0
  75. package/cli/src/utils/command-helpers.ts +54 -0
  76. package/cli/src/utils/constants.ts +126 -0
  77. package/cli/src/utils/discovery-context.ts +49 -0
  78. package/cli/src/utils/docker.ts +20 -0
  79. package/cli/src/utils/doctor-runtime.ts +604 -0
  80. package/cli/src/utils/env-validator.ts +139 -0
  81. package/cli/src/utils/errors.ts +168 -0
  82. package/cli/src/utils/file-helpers.ts +62 -0
  83. package/cli/src/utils/formatting.ts +215 -0
  84. package/cli/src/utils/paths.ts +53 -0
  85. package/cli/src/utils/port-assignment.ts +106 -0
  86. package/cli/src/utils/project-features.ts +118 -0
  87. package/cli/src/utils/project-templates.ts +28 -0
  88. package/cli/src/utils/prompt.ts +123 -0
  89. package/cli/src/utils/resource-features.ts +209 -0
  90. package/cli/src/utils/service-urls.ts +124 -0
  91. package/cli/src/utils/services.ts +363 -0
  92. package/cli/src/utils/stack-features.ts +30 -0
  93. package/cli/src/utils/tar.ts +24 -0
  94. package/cli/src/utils/tilt.ts +113 -0
  95. package/cli/src/utils/validation.ts +67 -0
  96. package/{templates → cli/templates}/Tiltfile.hbs +1 -1
  97. package/cli/tsconfig.json +25 -0
  98. package/cli/tsconfig.typecheck.json +8 -0
  99. package/docs/FEATURES.md +279 -0
  100. package/engine/topologies/platform/docker/dockerfile/dockerfile.star +3 -3
  101. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +1 -1
  102. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +0 -3
  103. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +1 -1
  104. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +1 -1
  105. package/engine/topologies/platform/docker/secrets/infisical.star +19 -47
  106. package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +1 -1
  107. package/install.sh +242 -0
  108. package/knip.json +10 -0
  109. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/.openspec.yaml +2 -0
  110. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/design.md +102 -0
  111. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/proposal.md +31 -0
  112. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-notifications/spec.md +48 -0
  113. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-orchestration/spec.md +63 -0
  114. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-troubleshooting/spec.md +56 -0
  115. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/tasks.md +61 -0
  116. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/.openspec.yaml +2 -0
  117. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/design.md +114 -0
  118. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/proposal.md +32 -0
  119. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/manifest-field-migration/spec.md +20 -0
  120. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/resource-terminology-standard/spec.md +20 -0
  121. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/stack-terminology-standard/spec.md +20 -0
  122. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/tasks.md +194 -0
  123. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/.openspec.yaml +2 -0
  124. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/design.md +111 -0
  125. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/proposal.md +33 -0
  126. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/function-naming-alignment/spec.md +12 -0
  127. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/optional-parameter-patterns/spec.md +18 -0
  128. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-documentation/spec.md +16 -0
  129. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-naming-consistency/spec.md +12 -0
  130. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-ordering-standard/spec.md +12 -0
  131. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/tasks.md +194 -0
  132. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/.openspec.yaml +2 -0
  133. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/codebase-audit-report.md +62 -0
  134. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/design.md +84 -0
  135. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/proposal.md +31 -0
  136. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/codebase-audit/spec.md +27 -0
  137. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/dead-code-cleanup/spec.md +27 -0
  138. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/test-gap-fill/spec.md +23 -0
  139. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/todo-resolution/spec.md +27 -0
  140. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/tasks.md +39 -0
  141. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/.openspec.yaml +5 -0
  142. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/design.md +94 -0
  143. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/proposal.md +46 -0
  144. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CLEANUP_REPORT_8.md +174 -0
  145. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/ai-slop-cleanup/CRITICAL_ASSESSMENT_8.md +160 -0
  146. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/circular-deps/CRITICAL_ASSESSMENT_4.md +381 -0
  147. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/dead-code/CRITICAL_ASSESSMENT_3.md +316 -0
  148. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/CRITICAL_ASSESSMENT_1.md +290 -0
  149. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/deduplication/IMPLEMENTATION_SUMMARY_1.md +233 -0
  150. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/defensive-cleanup/CRITICAL_ASSESSMENT_6.md +835 -0
  151. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/CRITICAL_ASSESSMENT_7.md +254 -0
  152. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/legacy-cleanup/IMPLEMENTATION_SUMMARY_7.md +187 -0
  153. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/strong-typing/CRITICAL_ASSESSMENT_5.md +269 -0
  154. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/CRITICAL_ASSESSMENT_2.md +312 -0
  155. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/specs/type-consolidation/IMPLEMENTATION_SUMMARY.md +170 -0
  156. package/openspec/changes/archive/2026-05-04-comprehensive-cleanup-final/tasks.md +137 -0
  157. package/openspec/changes/archive/2026-06-25-landscape-consolidation/.openspec.yaml +2 -0
  158. package/openspec/changes/archive/2026-06-25-landscape-consolidation/design.md +80 -0
  159. package/openspec/changes/archive/2026-06-25-landscape-consolidation/proposal.md +40 -0
  160. package/openspec/changes/archive/2026-06-25-landscape-consolidation/spec.md +52 -0
  161. package/openspec/changes/archive/2026-06-25-landscape-consolidation/tasks.md +51 -0
  162. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/.openspec.yaml +2 -0
  163. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/design.md +43 -0
  164. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/proposal.md +29 -0
  165. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/specs/self-contained-generated-projects/spec.md +67 -0
  166. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/tasks.md +27 -0
  167. package/openspec/specs/manifest-field-migration/spec.md +24 -0
  168. package/openspec/specs/resource-terminology-standard/spec.md +24 -0
  169. package/openspec/specs/self-contained-generated-projects/spec.md +67 -0
  170. package/openspec/specs/stack-terminology-standard/spec.md +24 -0
  171. package/package.json +27 -64
  172. package/platform/services/platform/proxy/service.yaml +14 -0
  173. package/scripts/audit-codebase.sh +213 -0
  174. package/scripts/install-git-hooks.sh +14 -0
  175. package/scripts/release-binaries.sh +94 -0
  176. package/scripts/setup-pre-commit.sh +33 -0
  177. package/scripts/stage-npm-engine.sh +36 -0
  178. package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +23 -0
  179. package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +464 -0
  180. package/shared-platform-engineering/docker-templates/install-deps.sh +152 -0
  181. package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +12 -0
  182. package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +56 -0
  183. package/tests/tilt-engine/__init__.py +5 -0
  184. package/tests/tilt-engine/conftest.py +182 -0
  185. package/tests/tilt-engine/fixtures/appointment-frontend-service.json +11 -0
  186. package/tests/tilt-engine/fixtures/identity-backend-service.json +12 -0
  187. package/tests/tilt-engine/fixtures/invalid-service.json +4 -0
  188. package/tests/tilt-engine/test_agents_md_generator.py +341 -0
  189. package/tests/tilt-engine/test_agents_md_integration.py +378 -0
  190. package/tests/tilt-engine/test_cache_operations.py +181 -0
  191. package/tests/tilt-engine/test_cache_persistence.py +142 -0
  192. package/tests/tilt-engine/test_code_executor.py +152 -0
  193. package/tests/tilt-engine/test_code_viewer.py +127 -0
  194. package/tests/tilt-engine/test_compose_port_anchors.py +310 -0
  195. package/tests/tilt-engine/test_config_inspector.py +162 -0
  196. package/tests/tilt-engine/test_daemon_focus_mode.py +89 -0
  197. package/tests/tilt-engine/test_daemon_polling.py +122 -0
  198. package/tests/tilt-engine/test_daemon_registration.py +115 -0
  199. package/tests/tilt-engine/test_daemon_validation.py +107 -0
  200. package/tests/tilt-engine/test_docker_generator_bugs.py +493 -0
  201. package/tests/tilt-engine/test_docker_prune.py +119 -0
  202. package/tests/tilt-engine/test_dockerignore_path_bug.py +82 -0
  203. package/tests/tilt-engine/test_file_browser.py +193 -0
  204. package/tests/tilt-engine/test_frontend_basepath_manifest.py +27 -0
  205. package/tests/tilt-engine/test_health_dependencies.py +130 -0
  206. package/tests/tilt-engine/test_health_endpoints.py +118 -0
  207. package/tests/tilt-engine/test_health_tilt_ui.py +85 -0
  208. package/tests/tilt-engine/test_safety_aliases.py +84 -0
  209. package/tests/tilt-engine/test_snapshot_diff.py +136 -0
  210. package/tests/tilt-engine/test_snapshot_persistence.py +163 -0
  211. package/tests/tilt-engine/test_snapshot_scanning.py +123 -0
  212. package/tests/tilt-engine/test_standalone_project_boot.py +61 -0
  213. package/tests/tilt-engine/test_standalone_traefik.py +108 -0
  214. package/dist/cli.d.ts +0 -3
  215. package/dist/cli.d.ts.map +0 -1
  216. package/dist/cli.js.map +0 -1
  217. package/dist/commands/completion.d.ts +0 -3
  218. package/dist/commands/completion.d.ts.map +0 -1
  219. package/dist/commands/completion.js.map +0 -1
  220. package/dist/commands/config.d.ts +0 -3
  221. package/dist/commands/config.d.ts.map +0 -1
  222. package/dist/commands/config.js +0 -231
  223. package/dist/commands/config.js.map +0 -1
  224. package/dist/commands/doctor.d.ts +0 -7
  225. package/dist/commands/doctor.d.ts.map +0 -1
  226. package/dist/commands/doctor.js +0 -597
  227. package/dist/commands/doctor.js.map +0 -1
  228. package/dist/commands/down.d.ts +0 -3
  229. package/dist/commands/down.d.ts.map +0 -1
  230. package/dist/commands/down.js +0 -32
  231. package/dist/commands/down.js.map +0 -1
  232. package/dist/commands/help.d.ts +0 -2
  233. package/dist/commands/help.d.ts.map +0 -1
  234. package/dist/commands/help.js +0 -130
  235. package/dist/commands/help.js.map +0 -1
  236. package/dist/commands/networks.d.ts +0 -3
  237. package/dist/commands/networks.d.ts.map +0 -1
  238. package/dist/commands/networks.js +0 -260
  239. package/dist/commands/networks.js.map +0 -1
  240. package/dist/commands/project.d.ts +0 -3
  241. package/dist/commands/project.d.ts.map +0 -1
  242. package/dist/commands/project.js +0 -373
  243. package/dist/commands/project.js.map +0 -1
  244. package/dist/commands/projects.d.ts +0 -3
  245. package/dist/commands/projects.d.ts.map +0 -1
  246. package/dist/commands/projects.js +0 -71
  247. package/dist/commands/projects.js.map +0 -1
  248. package/dist/commands/resource.d.ts +0 -69
  249. package/dist/commands/resource.d.ts.map +0 -1
  250. package/dist/commands/resource.js +0 -579
  251. package/dist/commands/resource.js.map +0 -1
  252. package/dist/commands/resources.d.ts +0 -3
  253. package/dist/commands/resources.d.ts.map +0 -1
  254. package/dist/commands/resources.js +0 -73
  255. package/dist/commands/resources.js.map +0 -1
  256. package/dist/commands/stack.d.ts +0 -3
  257. package/dist/commands/stack.d.ts.map +0 -1
  258. package/dist/commands/stack.js +0 -97
  259. package/dist/commands/stack.js.map +0 -1
  260. package/dist/commands/stacks.d.ts +0 -3
  261. package/dist/commands/stacks.d.ts.map +0 -1
  262. package/dist/commands/stacks.js +0 -43
  263. package/dist/commands/stacks.js.map +0 -1
  264. package/dist/commands/status.d.ts +0 -3
  265. package/dist/commands/status.d.ts.map +0 -1
  266. package/dist/commands/status.js +0 -71
  267. package/dist/commands/status.js.map +0 -1
  268. package/dist/commands/ui.d.ts +0 -3
  269. package/dist/commands/ui.d.ts.map +0 -1
  270. package/dist/commands/ui.js +0 -445
  271. package/dist/commands/ui.js.map +0 -1
  272. package/dist/commands/up.d.ts +0 -3
  273. package/dist/commands/up.d.ts.map +0 -1
  274. package/dist/commands/up.js +0 -110
  275. package/dist/commands/up.js.map +0 -1
  276. package/dist/commands/upgrade.d.ts +0 -11
  277. package/dist/commands/upgrade.d.ts.map +0 -1
  278. package/dist/commands/upgrade.js +0 -496
  279. package/dist/commands/upgrade.js.map +0 -1
  280. package/dist/commands/version.d.ts +0 -3
  281. package/dist/commands/version.d.ts.map +0 -1
  282. package/dist/commands/version.js.map +0 -1
  283. package/dist/components/Accessible.d.ts +0 -8
  284. package/dist/components/Accessible.d.ts.map +0 -1
  285. package/dist/components/Accessible.js +0 -10
  286. package/dist/components/Accessible.js.map +0 -1
  287. package/dist/components/BaseTooltip.d.ts +0 -4
  288. package/dist/components/BaseTooltip.d.ts.map +0 -1
  289. package/dist/components/BaseTooltip.js +0 -28
  290. package/dist/components/BaseTooltip.js.map +0 -1
  291. package/dist/components/DetailPanel.d.ts +0 -4
  292. package/dist/components/DetailPanel.d.ts.map +0 -1
  293. package/dist/components/DetailPanel.js +0 -18
  294. package/dist/components/DetailPanel.js.map +0 -1
  295. package/dist/components/FileTree.d.ts +0 -4
  296. package/dist/components/FileTree.d.ts.map +0 -1
  297. package/dist/components/FileTree.js +0 -47
  298. package/dist/components/FileTree.js.map +0 -1
  299. package/dist/components/ResourceSelectInput.d.ts +0 -4
  300. package/dist/components/ResourceSelectInput.d.ts.map +0 -1
  301. package/dist/components/ResourceSelectInput.js +0 -7
  302. package/dist/components/ResourceSelectInput.js.map +0 -1
  303. package/dist/components/ResourceTable.d.ts +0 -4
  304. package/dist/components/ResourceTable.d.ts.map +0 -1
  305. package/dist/components/ResourceTable.js +0 -18
  306. package/dist/components/ResourceTable.js.map +0 -1
  307. package/dist/components/TabBar.d.ts +0 -4
  308. package/dist/components/TabBar.d.ts.map +0 -1
  309. package/dist/components/TabBar.js +0 -17
  310. package/dist/components/TabBar.js.map +0 -1
  311. package/dist/components/Tooltip.d.ts +0 -12
  312. package/dist/components/Tooltip.d.ts.map +0 -1
  313. package/dist/components/Tooltip.js +0 -12
  314. package/dist/components/Tooltip.js.map +0 -1
  315. package/dist/components/index.d.ts +0 -8
  316. package/dist/components/index.d.ts.map +0 -1
  317. package/dist/components/index.js.map +0 -1
  318. package/dist/config/platform-standards.d.ts +0 -159
  319. package/dist/config/platform-standards.d.ts.map +0 -1
  320. package/dist/config/platform-standards.js +0 -181
  321. package/dist/config/platform-standards.js.map +0 -1
  322. package/dist/generator/extension-fetch.d.ts +0 -25
  323. package/dist/generator/extension-fetch.d.ts.map +0 -1
  324. package/dist/generator/extension-fetch.js +0 -252
  325. package/dist/generator/extension-fetch.js.map +0 -1
  326. package/dist/generator/template-engine.d.ts +0 -61
  327. package/dist/generator/template-engine.d.ts.map +0 -1
  328. package/dist/generator/template-engine.js +0 -557
  329. package/dist/generator/template-engine.js.map +0 -1
  330. package/dist/index.d.ts +0 -11
  331. package/dist/index.d.ts.map +0 -1
  332. package/dist/index.js +0 -11
  333. package/dist/index.js.map +0 -1
  334. package/dist/types/index.d.ts +0 -255
  335. package/dist/types/index.d.ts.map +0 -1
  336. package/dist/types/index.js +0 -17
  337. package/dist/types/index.js.map +0 -1
  338. package/dist/utils/cache.d.ts +0 -19
  339. package/dist/utils/cache.d.ts.map +0 -1
  340. package/dist/utils/cache.js +0 -64
  341. package/dist/utils/cache.js.map +0 -1
  342. package/dist/utils/command-helpers.d.ts +0 -17
  343. package/dist/utils/command-helpers.d.ts.map +0 -1
  344. package/dist/utils/command-helpers.js +0 -41
  345. package/dist/utils/command-helpers.js.map +0 -1
  346. package/dist/utils/constants.d.ts +0 -52
  347. package/dist/utils/constants.d.ts.map +0 -1
  348. package/dist/utils/constants.js +0 -112
  349. package/dist/utils/constants.js.map +0 -1
  350. package/dist/utils/discovery-context.d.ts +0 -4
  351. package/dist/utils/discovery-context.d.ts.map +0 -1
  352. package/dist/utils/discovery-context.js +0 -39
  353. package/dist/utils/discovery-context.js.map +0 -1
  354. package/dist/utils/docker.d.ts +0 -4
  355. package/dist/utils/docker.d.ts.map +0 -1
  356. package/dist/utils/docker.js +0 -19
  357. package/dist/utils/docker.js.map +0 -1
  358. package/dist/utils/doctor-runtime.d.ts +0 -66
  359. package/dist/utils/doctor-runtime.d.ts.map +0 -1
  360. package/dist/utils/doctor-runtime.js +0 -469
  361. package/dist/utils/doctor-runtime.js.map +0 -1
  362. package/dist/utils/env-validator.d.ts +0 -15
  363. package/dist/utils/env-validator.d.ts.map +0 -1
  364. package/dist/utils/env-validator.js +0 -109
  365. package/dist/utils/env-validator.js.map +0 -1
  366. package/dist/utils/errors.d.ts +0 -31
  367. package/dist/utils/errors.d.ts.map +0 -1
  368. package/dist/utils/errors.js +0 -140
  369. package/dist/utils/errors.js.map +0 -1
  370. package/dist/utils/file-helpers.d.ts +0 -14
  371. package/dist/utils/file-helpers.d.ts.map +0 -1
  372. package/dist/utils/file-helpers.js +0 -45
  373. package/dist/utils/file-helpers.js.map +0 -1
  374. package/dist/utils/formatting.d.ts +0 -30
  375. package/dist/utils/formatting.d.ts.map +0 -1
  376. package/dist/utils/formatting.js +0 -164
  377. package/dist/utils/formatting.js.map +0 -1
  378. package/dist/utils/paths.d.ts +0 -3
  379. package/dist/utils/paths.d.ts.map +0 -1
  380. package/dist/utils/paths.js +0 -41
  381. package/dist/utils/paths.js.map +0 -1
  382. package/dist/utils/port-assignment.d.ts +0 -6
  383. package/dist/utils/port-assignment.d.ts.map +0 -1
  384. package/dist/utils/port-assignment.js +0 -81
  385. package/dist/utils/port-assignment.js.map +0 -1
  386. package/dist/utils/project-features.d.ts +0 -23
  387. package/dist/utils/project-features.d.ts.map +0 -1
  388. package/dist/utils/project-features.js +0 -98
  389. package/dist/utils/project-features.js.map +0 -1
  390. package/dist/utils/project-templates.d.ts +0 -7
  391. package/dist/utils/project-templates.d.ts.map +0 -1
  392. package/dist/utils/project-templates.js +0 -24
  393. package/dist/utils/project-templates.js.map +0 -1
  394. package/dist/utils/prompt.d.ts +0 -28
  395. package/dist/utils/prompt.d.ts.map +0 -1
  396. package/dist/utils/prompt.js +0 -79
  397. package/dist/utils/prompt.js.map +0 -1
  398. package/dist/utils/resource-features.d.ts +0 -29
  399. package/dist/utils/resource-features.d.ts.map +0 -1
  400. package/dist/utils/resource-features.js +0 -171
  401. package/dist/utils/resource-features.js.map +0 -1
  402. package/dist/utils/service-urls.d.ts +0 -30
  403. package/dist/utils/service-urls.d.ts.map +0 -1
  404. package/dist/utils/service-urls.js +0 -93
  405. package/dist/utils/service-urls.js.map +0 -1
  406. package/dist/utils/services.d.ts +0 -18
  407. package/dist/utils/services.d.ts.map +0 -1
  408. package/dist/utils/services.js +0 -291
  409. package/dist/utils/services.js.map +0 -1
  410. package/dist/utils/stack-features.d.ts +0 -22
  411. package/dist/utils/stack-features.d.ts.map +0 -1
  412. package/dist/utils/stack-features.js +0 -13
  413. package/dist/utils/stack-features.js.map +0 -1
  414. package/dist/utils/tar.d.ts +0 -7
  415. package/dist/utils/tar.d.ts.map +0 -1
  416. package/dist/utils/tar.js +0 -21
  417. package/dist/utils/tar.js.map +0 -1
  418. package/dist/utils/tilt.d.ts +0 -19
  419. package/dist/utils/tilt.d.ts.map +0 -1
  420. package/dist/utils/tilt.js +0 -78
  421. package/dist/utils/tilt.js.map +0 -1
  422. package/dist/utils/validation.d.ts +0 -16
  423. package/dist/utils/validation.d.ts.map +0 -1
  424. package/dist/utils/validation.js +0 -58
  425. package/dist/utils/validation.js.map +0 -1
  426. /package/{README.md → cli/README.md} +0 -0
  427. /package/{bin → cli/bin}/tdk.js +0 -0
  428. /package/{templates → cli/templates}/.tiltignore.hbs +0 -0
  429. /package/{templates → cli/templates}/TILT_RESOURCE_DEFAULTS.star.hbs +0 -0
  430. /package/{templates → cli/templates}/TILT_TECH_STACK.star.hbs +0 -0
  431. /package/{templates → cli/templates}/spec.master.hbs +0 -0
@@ -0,0 +1,754 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import Handlebars from "handlebars";
5
+ import { PLATFORM_STANDARDS } from "../config/platform-standards.js";
6
+ import type { JsonValue, ProjectConfig } from "../types/index.js";
7
+ import { writeTextFile } from "../utils/file-helpers.js";
8
+ import { discoverResourcesFromRoot } from "../utils/services.js";
9
+ import { isStackFeatureEnabledInStacks } from "../utils/stack-features.js";
10
+ import { hasSablierLicense, hasVerdaccioLicense } from "./extension-fetch.js";
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+
15
+ // Helper function to load templates with robust path resolution
16
+ export function loadTemplate(filename: string): string {
17
+ // Try multiple possible template directory locations
18
+ const templateDirs = [
19
+ path.join(__dirname, "..", "..", "templates"), // Standard: dist/generator -> templates (also covers src/generator -> ../../templates in dev/test)
20
+ path.join(__dirname, "..", "templates"), // Installed: dist/generator -> dist/templates
21
+ path.join(__dirname, "..", "..", "..", "templates"), // Development: cli/dist/generator -> cli/templates
22
+ path.join(process.cwd(), "node_modules", "@tdk-landscape", "tdk-cli-core", "templates"), // Monorepo root
23
+ // Compiled single-file binary (bun build --compile): __dirname resolves inside the
24
+ // virtual $bunfs filesystem, not a real path, so fall back to the directory the actual
25
+ // executable lives in. release-binaries.sh bundles cli/templates at tdk-cli/cli/templates
26
+ // next to the platform binaries.
27
+ path.join(path.dirname(process.execPath), "tdk-cli", "cli", "templates"),
28
+ ];
29
+
30
+ const triedPaths: string[] = [];
31
+ for (const templateDir of templateDirs) {
32
+ const templatePath = path.join(templateDir, filename);
33
+ triedPaths.push(templatePath);
34
+ try {
35
+ return fs.readFileSync(templatePath, "utf-8");
36
+ } catch (error) {
37
+ // Try next path
38
+ continue;
39
+ }
40
+ }
41
+
42
+ throw new Error(`Failed to load template ${filename} from any of these paths: ${triedPaths.join(", ")}`);
43
+ }
44
+
45
+ // Templates used to be loaded eagerly here, at module initialization. That
46
+ // meant importing this module at all (e.g. via cli.ts -> config.ts/project.ts)
47
+ // threw immediately whenever the bundled templates folder was missing or
48
+ // stale - bricking every single `tdk` command, including `tdk upgrade`, the
49
+ // one command meant to repair a stale install. Load lazily instead, on first
50
+ // actual use, so commands that don't need templates (upgrade, doctor,
51
+ // version, ...) keep working even when the templates folder is absent, and
52
+ // `tdk upgrade` can run to fix it.
53
+ let embeddedTemplatesCache: Record<string, string> | null = null;
54
+ function getEmbeddedTemplates(): Record<string, string> {
55
+ if (!embeddedTemplatesCache) {
56
+ embeddedTemplatesCache = {
57
+ ".tiltignore": loadTemplate(".tiltignore.hbs"),
58
+ "TILT_RESOURCE_DEFAULTS.star": loadTemplate("TILT_RESOURCE_DEFAULTS.star.hbs"),
59
+ "TILT_TECH_STACK.star": loadTemplate("TILT_TECH_STACK.star.hbs"),
60
+ Tiltfile: loadTemplate("Tiltfile.hbs"),
61
+ "spec.master": loadTemplate("spec.master.hbs"),
62
+ };
63
+ }
64
+ return embeddedTemplatesCache;
65
+ }
66
+
67
+ interface GeneratorContext {
68
+ version: string;
69
+ timestamp: string;
70
+ tech: typeof PLATFORM_STANDARDS.tech;
71
+ ports: typeof PLATFORM_STANDARDS.ports;
72
+ health: typeof PLATFORM_STANDARDS.health;
73
+ naming: typeof PLATFORM_STANDARDS.naming;
74
+ traefik: typeof PLATFORM_STANDARDS.traefik;
75
+ filewatchIgnores: string[];
76
+ serviceTypes: typeof PLATFORM_STANDARDS.serviceTypes;
77
+ paths: typeof PLATFORM_STANDARDS.paths;
78
+ discovery: { scanIntervalSeconds: number; maxManifestsPerRoot: number; paths: string[] };
79
+ docker: typeof PLATFORM_STANDARDS.docker;
80
+ runtime: typeof PLATFORM_STANDARDS.runtime;
81
+ project: ProjectConfig["project"];
82
+ phases: ProjectConfig["phases"];
83
+ alwaysEnabledInfra: string[];
84
+ optionalInfra: ProjectConfig["optional_infra"];
85
+ serviceDescriptions: Record<string, string>;
86
+ infraDescriptions: Record<string, string>;
87
+ }
88
+
89
+ // Generic resource descriptions - no hardcoded service names
90
+ // Users define their own service descriptions in project.json
91
+ const RESOURCE_DESCRIPTIONS: Record<string, string> = {
92
+ proxy: "Traefik reverse proxy",
93
+ verdaccio: "Private npm registry",
94
+ "database-management": "PostgreSQL database",
95
+ };
96
+
97
+ const INFRA_DESCRIPTIONS: Record<string, string> = {
98
+ monitoring: "Signoz, SkyWalking (RAM intensive)",
99
+ elk: "Elasticsearch/Logstash/Kibana",
100
+ debezium: "CDC with Kafka/Zookeeper",
101
+ golden_image: "Golden image rebuild (one-time)",
102
+ verdaccio: "Premium local npm registry",
103
+ };
104
+
105
+ /**
106
+ * List of all files generated by the template engine.
107
+ * Used for iteration and type-safe access to generated content.
108
+ */
109
+ const ALL_GENERATED_FILES = [
110
+ "TILT_TECH_STACK.star",
111
+ "TILT_RESOURCE_DEFAULTS.star",
112
+ "spec.master",
113
+ "Tiltfile",
114
+ ".tiltignore",
115
+ ] as const;
116
+
117
+ /**
118
+ * Type representing valid generated file names.
119
+ * Derived from ALL_GENERATED_FILES const array for type safety.
120
+ */
121
+ type GeneratedFileName = (typeof ALL_GENERATED_FILES)[number];
122
+
123
+ export class TemplateEngine {
124
+ private templatesDir: string;
125
+
126
+ constructor(templatesDir?: string) {
127
+ const currentDir = import.meta.dirname || path.dirname(new URL(import.meta.url).pathname);
128
+ this.templatesDir = templatesDir || path.join(currentDir, "..", "..", "templates");
129
+ this.registerHelpers();
130
+ }
131
+
132
+ private formatStarlarkValue(val: JsonValue): string {
133
+ if (val === null || val === undefined) {
134
+ return "None";
135
+ }
136
+ if (typeof val === "string") {
137
+ return `"${val.replace(/"/g, '\\"')}"`;
138
+ }
139
+ if (typeof val === "boolean") {
140
+ return val ? "True" : "False";
141
+ }
142
+ if (typeof val === "number") {
143
+ return String(val);
144
+ }
145
+ if (Array.isArray(val)) {
146
+ const items = val.map((item) => this.formatStarlarkValue(item));
147
+ return `[${items.join(", ")}]`;
148
+ }
149
+ if (typeof val === "object") {
150
+ const entries = Object.entries(val).map(([key, v]) => {
151
+ return `"${key}": ${this.formatStarlarkValue(v)}`;
152
+ });
153
+ return `{${entries.join(", ")}}`;
154
+ }
155
+ return String(val);
156
+ }
157
+
158
+ private registerHelpers(): void {
159
+ Handlebars.registerHelper("starlark", (value: JsonValue): Handlebars.SafeString => {
160
+ return new Handlebars.SafeString(this.formatStarlarkValue(value));
161
+ });
162
+
163
+ Handlebars.registerHelper("starlarkArray", (value: JsonValue[]): Handlebars.SafeString => {
164
+ if (!Array.isArray(value)) return new Handlebars.SafeString("[]");
165
+ const items = value.map((item) => this.formatStarlarkValue(item));
166
+ return new Handlebars.SafeString(`[${items.join(", ")}]`);
167
+ });
168
+
169
+ Handlebars.registerHelper("json", (value: JsonValue): string => {
170
+ return JSON.stringify(value);
171
+ });
172
+ }
173
+
174
+ buildContext(projectConfig: ProjectConfig): GeneratorContext {
175
+ return {
176
+ version: PLATFORM_STANDARDS.version,
177
+ timestamp: new Date().toISOString(),
178
+ tech: PLATFORM_STANDARDS.tech,
179
+ ports: PLATFORM_STANDARDS.ports,
180
+ health: PLATFORM_STANDARDS.health,
181
+ naming: PLATFORM_STANDARDS.naming,
182
+ traefik: PLATFORM_STANDARDS.traefik,
183
+ filewatchIgnores: [...PLATFORM_STANDARDS.filewatchIgnores],
184
+ serviceTypes: PLATFORM_STANDARDS.serviceTypes,
185
+ paths: PLATFORM_STANDARDS.paths,
186
+ discovery: {
187
+ scanIntervalSeconds: PLATFORM_STANDARDS.discovery.scanIntervalSeconds,
188
+ maxManifestsPerRoot: PLATFORM_STANDARDS.discovery.maxManifestsPerRoot,
189
+ paths: projectConfig.discovery.paths,
190
+ },
191
+ docker: PLATFORM_STANDARDS.docker,
192
+ runtime: PLATFORM_STANDARDS.runtime,
193
+ project: projectConfig.project,
194
+ phases: dedupePhaseEnabledStacks(projectConfig.phases),
195
+ alwaysEnabledInfra: projectConfig.always_enabled_infra ?? [
196
+ "database-management",
197
+ "proxy",
198
+ ],
199
+ optionalInfra: projectConfig.optional_infra,
200
+ serviceDescriptions: RESOURCE_DESCRIPTIONS,
201
+ infraDescriptions: INFRA_DESCRIPTIONS,
202
+ };
203
+ }
204
+
205
+ private loadTemplate(templateName: string): HandlebarsTemplateDelegate {
206
+ const templatePath = path.join(this.templatesDir, `${templateName}.hbs`);
207
+ const templateSource = fs.existsSync(templatePath)
208
+ ? fs.readFileSync(templatePath, "utf-8")
209
+ : getEmbeddedTemplates()[templateName];
210
+ if (!templateSource) {
211
+ throw new Error(`Template not found: ${templatePath}`);
212
+ }
213
+ return Handlebars.compile(templateSource);
214
+ }
215
+
216
+ generateTechStack(context: GeneratorContext): string {
217
+ const template = this.loadTemplate("TILT_TECH_STACK.star");
218
+ return template(context);
219
+ }
220
+
221
+ generateServiceDefaults(context: GeneratorContext): string {
222
+ const template = this.loadTemplate("TILT_RESOURCE_DEFAULTS.star");
223
+ return template(context);
224
+ }
225
+
226
+ generateSpecMaster(context: GeneratorContext): string {
227
+ const template = this.loadTemplate("spec.master");
228
+ return template(context);
229
+ }
230
+
231
+ generateTiltfile(context: GeneratorContext): string {
232
+ const template = this.loadTemplate("Tiltfile");
233
+ return template(context);
234
+ }
235
+
236
+ generateTiltIgnore(context: GeneratorContext): string {
237
+ const template = this.loadTemplate(".tiltignore");
238
+ return template(context);
239
+ }
240
+
241
+ generateAll(projectConfig: ProjectConfig): Record<GeneratedFileName, string> {
242
+ const context = this.buildContext(projectConfig);
243
+
244
+ return {
245
+ "TILT_TECH_STACK.star": this.generateTechStack(context),
246
+ "TILT_RESOURCE_DEFAULTS.star": this.generateServiceDefaults(context),
247
+ "spec.master": this.generateSpecMaster(context),
248
+ Tiltfile: this.generateTiltfile(context),
249
+ ".tiltignore": this.generateTiltIgnore(context),
250
+ };
251
+ }
252
+ }
253
+
254
+ // spec.master.hbs emits one Starlark dict entry per enabledStacks[] item, so a
255
+ // duplicate name in project.json (e.g. from the interactive wizard offering
256
+ // the same service as both a core feature and a discovered stack) becomes a
257
+ // duplicate dict key, which fails Starlark parsing at `tdk up`. Dedupe here
258
+ // so a bad project.json can't break generation.
259
+ function dedupePhaseEnabledStacks(phases: ProjectConfig["phases"]): ProjectConfig["phases"] {
260
+ const deduped = { ...phases };
261
+ for (const key of Object.keys(deduped) as (keyof ProjectConfig["phases"])[]) {
262
+ deduped[key] = {
263
+ ...deduped[key],
264
+ enabledStacks: Array.from(new Set(deduped[key].enabledStacks)),
265
+ };
266
+ }
267
+ return deduped;
268
+ }
269
+
270
+ function toComposeProjectName(projectName: string): string {
271
+ return projectName.replace(/-/g, "_");
272
+ }
273
+
274
+ export function generateDatabaseManagementCompose(projectConfig: ProjectConfig): string {
275
+ const name = toComposeProjectName(projectConfig.project.name || "tdk_project");
276
+ return `###############################################################################
277
+ # SYSTEM-GENERATED - DO NOT EDIT
278
+ # Stack feature: database-management
279
+ # Source: .tdk/project.json phases.*.enabledStacks includes "database-management"
280
+ ###############################################################################
281
+
282
+ services:
283
+ postgres:
284
+ image: postgres:16-alpine
285
+ container_name: ${name}_postgres
286
+ restart: unless-stopped
287
+ environment:
288
+ POSTGRES_USER: ${name}
289
+ POSTGRES_PASSWORD: \${DB_PASSWORD}
290
+ POSTGRES_DB: postgres
291
+ ports:
292
+ - "5432:5432"
293
+ volumes:
294
+ - ${name}_postgres_data:/var/lib/postgresql/data
295
+ networks:
296
+ - database
297
+ healthcheck:
298
+ test: ["CMD-SHELL", "pg_isready -U ${name} -d postgres"]
299
+ interval: 5s
300
+ timeout: 5s
301
+ retries: 20
302
+
303
+ networks:
304
+ database:
305
+ name: ${name}_database
306
+ external: true
307
+
308
+ volumes:
309
+ ${name}_postgres_data:
310
+ `;
311
+ }
312
+
313
+ /**
314
+ * Type guard to validate if an unknown value is a valid ProjectConfig.
315
+ *
316
+ * NOTE: The `as` assertions below are INTENTIONAL and SAFE:
317
+ * - We've already verified `typeof value === 'object'` and `value !== null`
318
+ * - TypeScript doesn't automatically narrow `{}` to `Record<string, unknown>`
319
+ * - Each assertion is immediately followed by property type checks
320
+ * - This pattern is standard for deep object validation in type guards
321
+ */
322
+ function hasPhaseShape(value: unknown, fieldName: "enabledStacks" | "services"): boolean {
323
+ if (!value || typeof value !== "object") return false;
324
+ const phases = value as Record<string, unknown>;
325
+ for (const phaseName of ["pre_alpha", "alpha", "beta", "out_of_scope"]) {
326
+ const phase = phases[phaseName];
327
+ if (!phase || typeof phase !== "object") return false;
328
+ if (!Array.isArray((phase as Record<string, unknown>)[fieldName])) return false;
329
+ }
330
+ return true;
331
+ }
332
+
333
+ function normalizeProjectConfig(value: unknown): ProjectConfig | null {
334
+ if (!value || typeof value !== "object") {
335
+ return null;
336
+ }
337
+
338
+ // Safe to assert as Record after null and object type checks
339
+ const config = value as Record<string, unknown>;
340
+
341
+ if (typeof config.version !== "string") {
342
+ return null;
343
+ }
344
+
345
+ if (!config.project || typeof config.project !== "object") {
346
+ return null;
347
+ }
348
+ const project = config.project as Record<string, unknown>;
349
+ if (typeof project.name !== "string" || typeof project.version !== "string") {
350
+ return null;
351
+ }
352
+
353
+ let phases: ProjectConfig["phases"] | null = null;
354
+ if (hasPhaseShape(config.phases, "enabledStacks")) {
355
+ phases = config.phases as ProjectConfig["phases"];
356
+ } else if (hasPhaseShape(config.stacks, "services")) {
357
+ const legacyStacks = config.stacks as NonNullable<ProjectConfig["stacks"]>;
358
+ phases = {
359
+ pre_alpha: {
360
+ name: legacyStacks.pre_alpha.name,
361
+ description: legacyStacks.pre_alpha.description,
362
+ enabledStacks: legacyStacks.pre_alpha.services,
363
+ },
364
+ alpha: {
365
+ name: legacyStacks.alpha.name,
366
+ description: legacyStacks.alpha.description,
367
+ enabledStacks: legacyStacks.alpha.services,
368
+ },
369
+ beta: {
370
+ name: legacyStacks.beta.name,
371
+ description: legacyStacks.beta.description,
372
+ enabledStacks: legacyStacks.beta.services,
373
+ },
374
+ out_of_scope: {
375
+ name: legacyStacks.out_of_scope.name,
376
+ description: legacyStacks.out_of_scope.description,
377
+ enabledStacks: legacyStacks.out_of_scope.services,
378
+ },
379
+ };
380
+ }
381
+
382
+ if (!phases) {
383
+ return null;
384
+ }
385
+
386
+ if (!config.optional_infra || typeof config.optional_infra !== "object") {
387
+ return null;
388
+ }
389
+ const optionalInfra = config.optional_infra as Record<string, unknown>;
390
+ if (
391
+ typeof optionalInfra.monitoring !== "boolean" ||
392
+ typeof optionalInfra.elk !== "boolean" ||
393
+ typeof optionalInfra.debezium !== "boolean" ||
394
+ typeof optionalInfra.golden_image !== "boolean" ||
395
+ typeof optionalInfra.verdaccio !== "boolean"
396
+ ) {
397
+ return null;
398
+ }
399
+
400
+ if (!config.discovery || typeof config.discovery !== "object") {
401
+ return null;
402
+ }
403
+ const discovery = config.discovery as Record<string, unknown>;
404
+ if (!Array.isArray(discovery.paths)) {
405
+ return null;
406
+ }
407
+
408
+ return {
409
+ version: config.version,
410
+ project: config.project as ProjectConfig["project"],
411
+ phases,
412
+ always_enabled_infra: config.always_enabled_infra as string[] | undefined,
413
+ optional_infra: config.optional_infra as ProjectConfig["optional_infra"],
414
+ discovery: config.discovery as ProjectConfig["discovery"],
415
+ overrides: config.overrides as ProjectConfig["overrides"],
416
+ };
417
+ }
418
+
419
+ export function readProjectConfig(projectRoot: string): ProjectConfig {
420
+ const projectJsonPath = path.join(projectRoot, ".tdk", "project.json");
421
+
422
+ if (!fs.existsSync(projectJsonPath)) {
423
+ throw new Error(`Project config not found: ${projectJsonPath}. Run 'tdk project init' first.`);
424
+ }
425
+
426
+ const jsonContent = fs.readFileSync(projectJsonPath, "utf-8");
427
+ const parsed: unknown = JSON.parse(jsonContent);
428
+
429
+ const projectConfig = normalizeProjectConfig(parsed);
430
+ if (!projectConfig) {
431
+ throw new Error(
432
+ "Invalid project.json: missing or invalid required fields. " +
433
+ "Expected: version (string), project (object with name/version), " +
434
+ "phases (object with pre_alpha/alpha/beta/out_of_scope enabledStacks), " +
435
+ "optional_infra (object with boolean flags), " +
436
+ "discovery (object with paths array)",
437
+ );
438
+ }
439
+
440
+ return projectConfig;
441
+ }
442
+
443
+ // Sablier ("sablier: {enable: true}" on a resource's own service.json) is
444
+ // Premium, but unlike Verdaccio below, this is NOT the enforcement - it's a
445
+ // friendly heads-up. The real gate is that engine/.../sablier_container_cycle.star
446
+ // in this (public) repo is a free-tier stub that always returns disabled; the
447
+ // working implementation only exists in the private tdk-cli-extensions/premium/
448
+ // bundle, applied by vendorTdkExtension()'s applyPremiumOverlay() below when a
449
+ // license grants it. Forking this repo and deleting a license check (as you
450
+ // could with Verdaccio/DDD) gets you nothing here, because there's no check to
451
+ // delete - the actual code simply isn't present without the overlay. This just
452
+ // tells a confused free-tier user why their `sablier` block silently does
453
+ // nothing, instead of leaving them to guess.
454
+ async function warnIfSablierUnlicensed(projectRoot: string): Promise<void> {
455
+ const resources = discoverResourcesFromRoot(projectRoot).filter(
456
+ (r) => r.config?.sablier?.enable === true,
457
+ );
458
+ if (resources.length === 0) return;
459
+
460
+ const granted = await hasSablierLicense(projectRoot);
461
+ if (granted) return;
462
+
463
+ console.warn(
464
+ `⚠️ Sablier (on-demand scaling) is a Premium feature - no valid license key found. ` +
465
+ `${resources.length} resource${resources.length === 1 ? " has" : "s have"} a \`sablier\` block, ` +
466
+ "but it will have no effect without a license. Set TDK_LICENSE_KEY to a key that grants it, " +
467
+ "or remove the `sablier` block from the affected service.json file(s) to silence this warning.",
468
+ );
469
+ }
470
+
471
+ function ensureRootWorkspaceManifest(projectRoot: string, projectConfig: ProjectConfig): boolean {
472
+ const packageJsonPath = path.join(projectRoot, "package.json");
473
+ if (fs.existsSync(packageJsonPath)) {
474
+ return false;
475
+ }
476
+
477
+ const manifest = {
478
+ name: projectConfig.project.name || path.basename(projectRoot),
479
+ version: projectConfig.project.version || "1.0.0",
480
+ private: true,
481
+ type: "module",
482
+ workspaces: projectConfig.discovery.paths,
483
+ };
484
+
485
+ writeTextFile(packageJsonPath, `${JSON.stringify(manifest, null, 2)}\n`);
486
+ return true;
487
+ }
488
+
489
+ export async function generateMasterConfigs(projectRoot: string): Promise<void> {
490
+ const projectConfig = readProjectConfig(projectRoot);
491
+
492
+ await warnIfSablierUnlicensed(projectRoot);
493
+
494
+ if (ensureRootWorkspaceManifest(projectRoot, projectConfig)) {
495
+ console.log("✓ Generated: package.json (workspace root)");
496
+ }
497
+
498
+ // Verdaccio is Premium, but unlike the old posture here, this is NOT the
499
+ // enforcement - it's a friendly heads-up. The real gate is that
500
+ // engine/topologies/platform/registries/verdaccio_loader.star in this
501
+ // (public) repo is a free-tier stub that always skips; the working loader
502
+ // only exists in the private tdk-cli-extensions/premium/ bundle, applied
503
+ // by vendorTdkExtension()'s applyPremiumOverlay() below when a license
504
+ // grants it. Forking this repo and deleting a license check gets you
505
+ // nothing, because there's no check left to delete - the actual code
506
+ // simply isn't present without the overlay. This just tells a confused
507
+ // free-tier user why enabling Verdaccio silently did nothing, instead of
508
+ // leaving them to guess.
509
+ const verdaccioInPhase = Object.values(projectConfig.phases).some((phase) =>
510
+ phase.enabledStacks.includes("verdaccio"),
511
+ );
512
+ if (projectConfig.optional_infra.verdaccio || verdaccioInPhase) {
513
+ const granted = await hasVerdaccioLicense(projectRoot);
514
+ if (!granted) {
515
+ console.warn(
516
+ "⚠️ Verdaccio is a Premium feature - no valid license key found. It's enabled in " +
517
+ "this project's config, but will have no effect without a license. Set TDK_LICENSE_KEY " +
518
+ "to a key that grants it, or run `tdk config disable-infra verdaccio` to silence this warning.",
519
+ );
520
+ }
521
+ }
522
+
523
+ const outputDir = path.join(projectRoot, ".tdk", ".tdk-out");
524
+ if (!fs.existsSync(outputDir)) {
525
+ fs.mkdirSync(outputDir, { recursive: true });
526
+ }
527
+
528
+ const engine = new TemplateEngine();
529
+ const files = engine.generateAll(projectConfig);
530
+
531
+ for (const filename of ALL_GENERATED_FILES) {
532
+ const content = files[filename];
533
+ const filePath = path.join(outputDir, filename);
534
+ writeTextFile(filePath, content);
535
+ console.log(`✓ Generated: .tdk/.tdk-out/${filename}`);
536
+ }
537
+
538
+ if (isStackFeatureEnabledInStacks(projectConfig.phases, "database-management")) {
539
+ const composeDir = path.join(projectRoot, "services", "platform", "database-management");
540
+ fs.mkdirSync(composeDir, { recursive: true });
541
+ const composePath = path.join(
542
+ composeDir,
543
+ "docker-compose.yml",
544
+ );
545
+ writeTextFile(composePath, generateDatabaseManagementCompose(projectConfig));
546
+ console.log("✓ Generated: services/platform/database-management/docker-compose.yml");
547
+ }
548
+
549
+ // Copy .tiltignore to project root so Tilt uses it
550
+ const tiltignoreSource = path.join(outputDir, ".tiltignore");
551
+ const tiltignoreTarget = path.join(projectRoot, ".tiltignore");
552
+ if (fs.existsSync(tiltignoreSource)) {
553
+ fs.copyFileSync(tiltignoreSource, tiltignoreTarget);
554
+ console.log(`✓ Copied: .tiltignore → project root`);
555
+ }
556
+
557
+ const copiedAssets = ensureProjectRuntimeAssets(projectRoot);
558
+ for (const asset of copiedAssets) {
559
+ console.log(`✓ Copied: ${asset} → project root`);
560
+ }
561
+
562
+ await vendorTdkExtension(projectRoot);
563
+
564
+ console.log("");
565
+ console.log("💡 To start Tilt: tdk up");
566
+ }
567
+
568
+ // The generated Tiltfile loads the TDK Tilt extension (engine, discovery,
569
+ // specs, ext starlark modules). The extension repo may stay private, so we
570
+ // vendor a working copy into .tdk/.tdk-out/tdk-cli-ext/ at generation time.
571
+ // This makes `tdk up` work offline and on any machine - no hardcoded paths,
572
+ // no dependency on a private GitHub repo.
573
+ const TDK_EXTENSION_DIRS = ["engine", "discovery", "specs", "ext"] as string[];
574
+ const PROJECT_RUNTIME_ASSET_DIRS = [
575
+ ["shared-platform-engineering", "docker-templates"],
576
+ ] as const;
577
+ const REQUIRED_DOCKER_TEMPLATE_FILES = [
578
+ "install-deps.sh",
579
+ "bun-hoisted-symlink-fix.sh",
580
+ "prisma-bun-client-link-fix.sh",
581
+ "prisma-normalize-client.sh",
582
+ ] as const;
583
+
584
+ // Walk up from this module's own location looking for a directory that has
585
+ // both Tiltfile and engine/ next to it - true when cli/ and engine/ ship
586
+ // together in the same repo (the public tdk-cli-core layout, where a plain
587
+ // `git clone` + `bun run` needs zero extra config to find the engine).
588
+ function findSelfContainedEngineRoot(): string | null {
589
+ let dir = import.meta.dirname || process.cwd();
590
+ for (let i = 0; i < 6; i++) {
591
+ if (fs.existsSync(path.join(dir, "Tiltfile")) && fs.existsSync(path.join(dir, "engine"))) {
592
+ return dir;
593
+ }
594
+ const parent = path.dirname(dir);
595
+ if (parent === dir) break;
596
+ dir = parent;
597
+ }
598
+ return null;
599
+ }
600
+
601
+ function findCliAssetRoot(): string | null {
602
+ let dir = import.meta.dirname || process.cwd();
603
+ for (let i = 0; i < 8; i++) {
604
+ if (fs.existsSync(path.join(dir, "shared-platform-engineering", "docker-templates"))) {
605
+ return dir;
606
+ }
607
+ const parent = path.dirname(dir);
608
+ if (parent === dir) break;
609
+ dir = parent;
610
+ }
611
+
612
+ const exeDir = path.dirname(process.execPath);
613
+ const candidates = [
614
+ path.join(exeDir, "tdk-cli"),
615
+ path.join(process.cwd(), "node_modules", "@tdk-landscape", "tdk-cli-core"),
616
+ ];
617
+
618
+ return candidates.find((candidate) =>
619
+ fs.existsSync(path.join(candidate, "shared-platform-engineering", "docker-templates")),
620
+ ) ?? null;
621
+ }
622
+
623
+ export function ensureProjectRuntimeAssets(projectRoot: string): string[] {
624
+ const sourceRoot = findCliAssetRoot();
625
+ const copied: string[] = [];
626
+
627
+ if (!sourceRoot) {
628
+ console.warn(
629
+ "⚠️ TDK runtime assets not found - generated Dockerfiles may fail. " +
630
+ "Reinstall or upgrade TDK, then re-run `tdk project`.",
631
+ );
632
+ return copied;
633
+ }
634
+
635
+ for (const parts of PROJECT_RUNTIME_ASSET_DIRS) {
636
+ const source = path.join(sourceRoot, ...parts);
637
+ const target = path.join(projectRoot, ...parts);
638
+ if (!fs.existsSync(source)) {
639
+ continue;
640
+ }
641
+
642
+ fs.mkdirSync(path.dirname(target), { recursive: true });
643
+ fs.cpSync(source, target, { recursive: true });
644
+ copied.push(parts.join("/"));
645
+ }
646
+
647
+ for (const file of REQUIRED_DOCKER_TEMPLATE_FILES) {
648
+ const filePath = path.join(projectRoot, "shared-platform-engineering", "docker-templates", file);
649
+ if (!fs.existsSync(filePath)) {
650
+ console.warn(`⚠️ Missing runtime asset after copy: ${filePath}`);
651
+ }
652
+ }
653
+
654
+ return copied;
655
+ }
656
+
657
+ async function vendorTdkExtension(projectRoot: string): Promise<void> {
658
+ const outputDir = path.join(projectRoot, ".tdk", ".tdk-out");
659
+ const vendoredDir = path.join(outputDir, "tdk-cli-ext");
660
+
661
+ // Source resolution order:
662
+ // 1. $TDK_EXTENSION_SOURCE (explicit override)
663
+ // 2. Self-contained repo (engine/ ships next to cli/ - public tdk-cli-core)
664
+ // 3. Executable-adjacent checkout (bundled installs)
665
+ // 4. Relative ../tdk-cli sibling (monorepo maintainer layout)
666
+ let sources: string[] = [];
667
+ if (process.env.TDK_EXTENSION_SOURCE) {
668
+ sources.push(process.env.TDK_EXTENSION_SOURCE);
669
+ }
670
+ const selfContainedRoot = findSelfContainedEngineRoot();
671
+ if (selfContainedRoot) {
672
+ sources.push(selfContainedRoot);
673
+ }
674
+ // process.execPath is the compiled binary's own path when running as a
675
+ // standalone executable (what we actually need here). In dev mode it's
676
+ // the bun CLI's own path instead (e.g. ~/.bun/bin/bun) - harmless, that
677
+ // candidate just won't match anything and falls through to the next
678
+ // one. Note: process.executablePath is not a real property on Node or
679
+ // Bun's process object - it was always undefined, silently falling
680
+ // back to cwd() instead of the binary's real location.
681
+ const exeDir = path.dirname(process.execPath);
682
+ sources.push(path.join(exeDir, "tdk-cli"));
683
+ sources.push(path.join(projectRoot, "..", "tdk-cli"));
684
+
685
+ let sourceRoot = "";
686
+ for (const candidate of sources) {
687
+ if (
688
+ candidate &&
689
+ fs.existsSync(path.join(candidate, "Tiltfile")) &&
690
+ fs.existsSync(path.join(candidate, "engine"))
691
+ ) {
692
+ sourceRoot = candidate;
693
+ break;
694
+ }
695
+ }
696
+
697
+ if (!sourceRoot) {
698
+ console.warn(
699
+ "⚠️ TDK extension not found - set TDK_EXTENSION_SOURCE to its checkout path " +
700
+ "and re-run `tdk project` (or `tdk config regenerate`) to vendor it before `tdk up`.",
701
+ );
702
+ return;
703
+ }
704
+
705
+ if (!fs.existsSync(vendoredDir)) {
706
+ fs.mkdirSync(vendoredDir, { recursive: true });
707
+ }
708
+ fs.copyFileSync(path.join(sourceRoot, "Tiltfile"), path.join(vendoredDir, "Tiltfile"));
709
+ for (const dir of TDK_EXTENSION_DIRS) {
710
+ fs.cpSync(path.join(sourceRoot, dir), path.join(vendoredDir, dir), {
711
+ recursive: true,
712
+ });
713
+ }
714
+ console.log(`✓ Vendored TDK extension → .tdk/.tdk-out/tdk-cli-ext/`);
715
+
716
+ // Best-effort: if TDK_LICENSE_KEY is set and grants any paid resource,
717
+ // overlay the premium bundle over the stub files just vendored above.
718
+ // No-ops silently (beyond a warning) if there's no key, the fetch
719
+ // fails, or nothing's granted - the free engine already works fine on
720
+ // its own.
721
+ try {
722
+ const { applyPremiumOverlay } = await import("./extension-fetch.js");
723
+ await applyPremiumOverlay(projectRoot, vendoredDir);
724
+ } catch (err) {
725
+ console.warn(`⚠️ Premium overlay skipped: ${(err as Error).message}`);
726
+ }
727
+ }
728
+
729
+ export function verifyMasterConfigs(projectRoot: string): { valid: boolean; errors: string[] } {
730
+ const errors: string[] = [];
731
+
732
+ const projectConfig = readProjectConfig(projectRoot);
733
+ const engine = new TemplateEngine();
734
+ const expectedFiles = engine.generateAll(projectConfig);
735
+
736
+ const outputDir = path.join(projectRoot, ".tdk", ".tdk-out");
737
+
738
+ for (const filename of ALL_GENERATED_FILES) {
739
+ const expectedContent = expectedFiles[filename];
740
+ const filePath = path.join(outputDir, filename);
741
+
742
+ if (!fs.existsSync(filePath)) {
743
+ errors.push(`Missing file: .tdk/.tdk-out/${filename}`);
744
+ continue;
745
+ }
746
+
747
+ const actualContent = fs.readFileSync(filePath, "utf-8");
748
+ if (actualContent !== expectedContent) {
749
+ errors.push(`Out of sync: .tdk/.tdk-out/${filename} (run 'tdk config regenerate')`);
750
+ }
751
+ }
752
+
753
+ return { valid: errors.length === 0, errors };
754
+ }