@tdk-landscape/tdk-cli-core 1.3.15 → 1.3.22

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 (274) hide show
  1. package/Tiltfile +144 -0
  2. package/discovery/AGENTS.md +362 -0
  3. package/discovery/TILT_DISCOVERY.star +424 -0
  4. package/discovery/Tiltfile +58 -0
  5. package/discovery/config.star +258 -0
  6. package/discovery/constants.star +127 -0
  7. package/discovery/deterministic.star +211 -0
  8. package/discovery/discovery_daemon.star +191 -0
  9. package/discovery/discovery_orchestrator.star +460 -0
  10. package/discovery/filtering.star +5 -0
  11. package/discovery/identity-management-backend/service.yaml +26 -0
  12. package/discovery/identity-management-frontend/service.yaml +29 -0
  13. package/discovery/identity-sdk/service.yaml +9 -0
  14. package/discovery/incremental_discovery.star +213 -0
  15. package/discovery/indexing.star +7 -0
  16. package/discovery/json_manifest_scanner.star +51 -0
  17. package/discovery/libraries.star +46 -0
  18. package/discovery/loading.star +23 -0
  19. package/discovery/manifest/constants.star +104 -0
  20. package/discovery/manifest/loading.star +386 -0
  21. package/discovery/manifest/normalize.star +281 -0
  22. package/discovery/manifest/validation.star +108 -0
  23. package/discovery/normalization.star +11 -0
  24. package/discovery/registry.star +544 -0
  25. package/discovery/resource_snapshot.py +382 -0
  26. package/discovery/resource_snapshot.star +143 -0
  27. package/discovery/schemas.star +1 -0
  28. package/discovery/services/platform/cdc/debezium/nats-connector/service.yaml +9 -0
  29. package/discovery/services/platform/cdc/nats-http-bridge/service.yaml +9 -0
  30. package/discovery/services/platform/orchestrator/orchestrator-glue-backend/service.yaml +26 -0
  31. package/discovery/services/platform/synthetic-monitor/service.yaml +9 -0
  32. package/discovery/test_resource_snapshot.py +251 -0
  33. package/discovery/validation.star +78 -0
  34. package/discovery/versioning.star +1 -0
  35. package/dist/commands/config.d.ts.map +1 -1
  36. package/dist/commands/config.js +11 -0
  37. package/dist/commands/config.js.map +1 -1
  38. package/dist/commands/doctor.d.ts.map +1 -1
  39. package/dist/commands/doctor.js +1 -2
  40. package/dist/commands/doctor.js.map +1 -1
  41. package/dist/commands/project.d.ts.map +1 -1
  42. package/dist/commands/project.js +36 -41
  43. package/dist/commands/project.js.map +1 -1
  44. package/dist/commands/resource.d.ts.map +1 -1
  45. package/dist/commands/resource.js +51 -55
  46. package/dist/commands/resource.js.map +1 -1
  47. package/dist/commands/stack.d.ts.map +1 -1
  48. package/dist/commands/stack.js +22 -24
  49. package/dist/commands/stack.js.map +1 -1
  50. package/dist/commands/ui.d.ts.map +1 -1
  51. package/dist/commands/ui.js +1 -2
  52. package/dist/commands/ui.js.map +1 -1
  53. package/dist/commands/up.d.ts.map +1 -1
  54. package/dist/commands/up.js +1 -2
  55. package/dist/commands/up.js.map +1 -1
  56. package/dist/commands/upgrade.d.ts +8 -0
  57. package/dist/commands/upgrade.d.ts.map +1 -1
  58. package/dist/commands/upgrade.js +59 -18
  59. package/dist/commands/upgrade.js.map +1 -1
  60. package/dist/generator/extension-fetch.d.ts +19 -0
  61. package/dist/generator/extension-fetch.d.ts.map +1 -1
  62. package/dist/generator/extension-fetch.js +71 -5
  63. package/dist/generator/extension-fetch.js.map +1 -1
  64. package/dist/generator/template-engine.d.ts +1 -0
  65. package/dist/generator/template-engine.d.ts.map +1 -1
  66. package/dist/generator/template-engine.js +50 -6
  67. package/dist/generator/template-engine.js.map +1 -1
  68. package/dist/utils/command-helpers.d.ts.map +1 -1
  69. package/dist/utils/command-helpers.js +7 -9
  70. package/dist/utils/command-helpers.js.map +1 -1
  71. package/dist/utils/constants.d.ts +1 -0
  72. package/dist/utils/constants.d.ts.map +1 -1
  73. package/dist/utils/constants.js +1 -0
  74. package/dist/utils/constants.js.map +1 -1
  75. package/dist/utils/errors.d.ts +2 -0
  76. package/dist/utils/errors.d.ts.map +1 -1
  77. package/dist/utils/errors.js +6 -1
  78. package/dist/utils/errors.js.map +1 -1
  79. package/dist/utils/project-features.d.ts.map +1 -1
  80. package/dist/utils/project-features.js +1 -1
  81. package/dist/utils/project-features.js.map +1 -1
  82. package/dist/utils/resource-features.d.ts.map +1 -1
  83. package/dist/utils/resource-features.js +2 -2
  84. package/dist/utils/resource-features.js.map +1 -1
  85. package/dist/utils/tar.js +4 -4
  86. package/dist/utils/tar.js.map +1 -1
  87. package/dist/utils/validation.d.ts +7 -0
  88. package/dist/utils/validation.d.ts.map +1 -1
  89. package/dist/utils/validation.js +9 -0
  90. package/dist/utils/validation.js.map +1 -1
  91. package/engine/AGENTS.md +705 -0
  92. package/engine/README.md +29 -0
  93. package/engine/REFACTORING_MANIFEST_SYSTEM.md +974 -0
  94. package/engine/Tiltfile +80 -0
  95. package/engine/assets/docker/golden-layers.Dockerfile +221 -0
  96. package/engine/assets/scripts/cleanup-completed-migrator-images.sh +37 -0
  97. package/engine/assets/scripts/cleanup-tilt-images.sh +120 -0
  98. package/engine/assets/scripts/mac-m1-resource-monitor.sh +121 -0
  99. package/engine/config.star +3 -0
  100. package/engine/docs/AUTODISCOVERY_TEST.md +182 -0
  101. package/engine/docs/C4-ARCHITECTURE-operations.md +396 -0
  102. package/engine/docs/C4-ARCHITECTURE-overview.md +284 -0
  103. package/engine/docs/C4-ARCHITECTURE.md +17 -0
  104. package/engine/docs/GOLDEN_IMAGE_INTEGRATION.md +349 -0
  105. package/engine/docs/MANIFEST_DRIVEN_ARCHITECTURE.md +357 -0
  106. package/engine/docs/README.md +284 -0
  107. package/engine/entrypoint.star +97 -0
  108. package/engine/lifecycle/orchestrator.star +260 -0
  109. package/engine/paths.star +717 -0
  110. package/engine/schemas/manifest-schema.json +301 -0
  111. package/engine/schemas/service-schema.json +301 -0
  112. package/engine/scripts/README.md +89 -0
  113. package/engine/scripts/build-and-publish-lib.sh +205 -0
  114. package/engine/scripts/check-lib-publish.sh +85 -0
  115. package/engine/scripts/debug-container.sh +258 -0
  116. package/engine/scripts/generate-manifest.sh +197 -0
  117. package/engine/spec.master +702 -0
  118. package/engine/spec.master.example +341 -0
  119. package/engine/topologies/README.md +34 -0
  120. package/engine/topologies/platform/docker/AGENTS.md +59 -0
  121. package/engine/topologies/platform/docker/auth/auth.star +167 -0
  122. package/engine/topologies/platform/docker/build/golden_image.star +37 -0
  123. package/engine/topologies/platform/docker/build/golden_image_build.star +82 -0
  124. package/engine/topologies/platform/docker/build/golden_image_constants.star +93 -0
  125. package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +291 -0
  126. package/engine/topologies/platform/docker/compose/compose.star +636 -0
  127. package/engine/topologies/platform/docker/compose/compose_helpers.star +52 -0
  128. package/engine/topologies/platform/docker/compose/traefik_standalone.star +53 -0
  129. package/engine/topologies/platform/docker/config/bunignore.star +142 -0
  130. package/engine/topologies/platform/docker/config/dockerignore.star +150 -0
  131. package/engine/topologies/platform/docker/constants.star +170 -0
  132. package/engine/topologies/platform/docker/dockerfile/dockerfile.star +54 -0
  133. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +73 -0
  134. package/engine/topologies/platform/docker/index.star +27 -0
  135. package/engine/topologies/platform/docker/layers/bun_helpers.star +23 -0
  136. package/engine/topologies/platform/docker/layers/infisical/index.star +40 -0
  137. package/engine/topologies/platform/docker/layers/infisical/infisical_docker.star +262 -0
  138. package/engine/topologies/platform/docker/layers/l1_base_layers.star +87 -0
  139. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +125 -0
  140. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +209 -0
  141. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +214 -0
  142. package/engine/topologies/platform/docker/layers/prisma/constants.star +135 -0
  143. package/engine/topologies/platform/docker/layers/prisma/db_readiness.star +96 -0
  144. package/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +109 -0
  145. package/engine/topologies/platform/docker/layers/prisma/migrate_script.star +98 -0
  146. package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +126 -0
  147. package/engine/topologies/platform/docker/layers/prisma/prisma_runtime.star +161 -0
  148. package/engine/topologies/platform/docker/layers/prisma/secret_resolution.star +119 -0
  149. package/engine/topologies/platform/docker/networking/api_path_constants.star +230 -0
  150. package/engine/topologies/platform/docker/networking/sablier_container_cycle.star +27 -0
  151. package/engine/topologies/platform/docker/networking/traefik.star +232 -0
  152. package/engine/topologies/platform/docker/networking/traefik_constants.star +146 -0
  153. package/engine/topologies/platform/docker/networking/traefik_helpers.star +160 -0
  154. package/engine/topologies/platform/docker/secrets/infisical.star +135 -0
  155. package/engine/topologies/platform/net/proxy.star +4 -0
  156. package/engine/topologies/platform/registries/local.star +4 -0
  157. package/engine/topologies/platform/security/secrets.star +239 -0
  158. package/engine/topologies/platform/state/relational.star +4 -0
  159. package/engine/topologies/tilt/common/AGENTS.md +59 -0
  160. package/engine/topologies/tilt/common/utils.star +364 -0
  161. package/engine/topologies/tilt/common/utils_debug.star +69 -0
  162. package/engine/topologies/tilt/common/utils_deterministic.star +168 -0
  163. package/engine/topologies/tilt/common/utils_docker_networks.star +46 -0
  164. package/engine/topologies/tilt/common/utils_env.star +43 -0
  165. package/engine/topologies/tilt/common/utils_output.star +97 -0
  166. package/engine/topologies/tilt/config/AGENTS.md +68 -0
  167. package/engine/topologies/tilt/config/defaults.star +3 -0
  168. package/engine/topologies/tilt/config/feature_flags.star +10 -0
  169. package/engine/topologies/tilt/config/global.star +55 -0
  170. package/engine/topologies/tilt/config/profiles.star +382 -0
  171. package/engine/topologies/tilt/generators/agents_md.star +62 -0
  172. package/engine/topologies/tilt/generators/api_gateway.star +145 -0
  173. package/engine/topologies/tilt/generators/index.star +124 -0
  174. package/engine/topologies/tilt/generators/infisical/README.md +424 -0
  175. package/engine/topologies/tilt/generators/infisical/examples.star +312 -0
  176. package/engine/topologies/tilt/generators/infisical/index.star +57 -0
  177. package/engine/topologies/tilt/generators/infisical/machine_identity.star +420 -0
  178. package/engine/topologies/tilt/generators/infisical/organization.star +534 -0
  179. package/engine/topologies/tilt/generators/infisical/path_manager.star +476 -0
  180. package/engine/topologies/tilt/generators/infisical/secrets_generator.star +420 -0
  181. package/engine/topologies/tilt/generators/nginx_conf.star +143 -0
  182. package/engine/topologies/tilt/generators/npmrc.star +111 -0
  183. package/engine/topologies/tilt/generators/tsconfig.star +23 -0
  184. package/engine/topologies/tilt/generators/typescript/backend_library_tsconfig.star +71 -0
  185. package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +124 -0
  186. package/engine/topologies/tilt/generators/typescript/common.star +7 -0
  187. package/engine/topologies/tilt/generators/typescript/frontend_library_tsconfig.star +76 -0
  188. package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +110 -0
  189. package/engine/topologies/tilt/generators/typescript/include.star +23 -0
  190. package/engine/topologies/tilt/generators/typescript/playwright_config.star +40 -0
  191. package/engine/topologies/tilt/generators/typescript/prisma_config.star +49 -0
  192. package/engine/topologies/tilt/generators/validators.star +853 -0
  193. package/engine/topologies/tilt/generators/vite/backend.star +78 -0
  194. package/engine/topologies/tilt/generators/vite/frontend.star +98 -0
  195. package/engine/topologies/tilt/generators/vite/generators.star +43 -0
  196. package/engine/topologies/tilt/generators/vite/helpers.star +244 -0
  197. package/engine/topologies/tilt/generators/vite/library.star +65 -0
  198. package/engine/topologies/tilt/generators/vite/sdk.star +73 -0
  199. package/engine/topologies/tilt/generators/vite/templates.star +737 -0
  200. package/engine/topologies/tilt/generators/vite_config.star +22 -0
  201. package/engine/topologies/tilt/manifest/AGENTS.md +61 -0
  202. package/engine/topologies/tilt/manifest/README.md +693 -0
  203. package/engine/topologies/tilt/manifest/__init__.star +218 -0
  204. package/engine/topologies/tilt/manifest/constants.star +252 -0
  205. package/engine/topologies/tilt/manifest/errors.star +266 -0
  206. package/engine/topologies/tilt/manifest/integration.star +339 -0
  207. package/engine/topologies/tilt/manifest/json_to_yaml.py +54 -0
  208. package/engine/topologies/tilt/manifest/loader.star +214 -0
  209. package/engine/topologies/tilt/manifest/mapper.star +227 -0
  210. package/engine/topologies/tilt/manifest/parser.star +400 -0
  211. package/engine/topologies/tilt/manifest/schema.star +462 -0
  212. package/engine/topologies/tilt/manifest/tests/test_integration.star +276 -0
  213. package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +397 -0
  214. package/engine/topologies/tilt/manifest/tests/test_manifest.star +316 -0
  215. package/engine/topologies/tilt/manifest/tests/test_validator.star +470 -0
  216. package/engine/topologies/tilt/manifest/validator.star +717 -0
  217. package/engine/topologies/tilt/manifest/yaml_to_json.py +18 -0
  218. package/engine/topologies/tilt/resources/AGENTS.md +71 -0
  219. package/engine/topologies/tilt/resources/conditions.star +3 -0
  220. package/engine/topologies/tilt/resources/databases.star +244 -0
  221. package/engine/topologies/tilt/resources/declaration.star +3 -0
  222. package/engine/topologies/tilt/resources/deps.star +3 -0
  223. package/engine/topologies/tilt/resources/infra-loader.star +437 -0
  224. package/engine/topologies/tilt/resources/libs.star +30 -0
  225. package/engine/topologies/tilt/resources/libs_discovery.star +102 -0
  226. package/engine/topologies/tilt/resources/libs_paths.star +79 -0
  227. package/engine/topologies/tilt/resources/libs_resource_definition.star +151 -0
  228. package/engine/topologies/tilt/resources/libs_setup.star +164 -0
  229. package/engine/topologies/tilt/resources/libs_tsconfig.star +37 -0
  230. package/engine/topologies/tilt/resources/orchestrator/apply.star +77 -0
  231. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +431 -0
  232. package/engine/topologies/tilt/resources/orchestrator/apply_manifest_orchestration.star +81 -0
  233. package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +148 -0
  234. package/engine/topologies/tilt/resources/orchestrator/apply_resource_validation.star +36 -0
  235. package/engine/topologies/tilt/resources/orchestrator/apply_runtime_flags.star +44 -0
  236. package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +164 -0
  237. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/AGENTS.md +75 -0
  238. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/index.star +21 -0
  239. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +134 -0
  240. package/engine/topologies/tilt/resources/orchestrator/generators/env.star +258 -0
  241. package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +169 -0
  242. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +524 -0
  243. package/engine/topologies/tilt/resources/orchestrator/helpers.star +33 -0
  244. package/engine/topologies/tilt/resources/orchestrator.star +54 -0
  245. package/engine/topologies/tilt/resources/ordering.star +3 -0
  246. package/engine/topologies/tilt/resources/triggers.star +3 -0
  247. package/engine/topologies/tilt/resources/watchers.star +299 -0
  248. package/engine/where-things-live.star +717 -0
  249. package/ext/Tiltfile +66 -0
  250. package/ext/ide-components/README.md +219 -0
  251. package/ext/ide-components/code_executor/server.py +268 -0
  252. package/ext/ide-components/code_viewer/server.py +206 -0
  253. package/ext/ide-components/config_inspector/server.py +1050 -0
  254. package/ext/ide-components/file_browser/server.py +285 -0
  255. package/ext/ide-components/shared/config_service.py +469 -0
  256. package/ext/ide-components/shared/file_utils.py +298 -0
  257. package/ext/ide-components/shared/http_utils.py +232 -0
  258. package/ext/ide-components/shared/static/styles.css +1334 -0
  259. package/ext/ide-components/shared/syntax_highlighter.py +140 -0
  260. package/ext/ide-components/shared/template_engine.py +123 -0
  261. package/ext/ide-components/shared/templates/base.html +121 -0
  262. package/ext/ide-components/shared/test_config_service.py +326 -0
  263. package/ext/ide-components/shared/tilt_integration.py +158 -0
  264. package/ext/ui-enhancements/cron-status-server.sh +49 -0
  265. package/ext/ui-enhancements/cron-status-tracker.sh +146 -0
  266. package/ext/ui-enhancements/extension.yaml +36 -0
  267. package/ext/ui-enhancements/tiltfile.py +99 -0
  268. package/ext/ui-enhancements/ui/cron-jobs-tab.css +396 -0
  269. package/ext/ui-enhancements/ui/help-panel.css +279 -0
  270. package/ext/ui-enhancements/ui/icons.css +125 -0
  271. package/ext/ui-enhancements/ui/tooltips.css +114 -0
  272. package/package.json +13 -9
  273. package/specs/specs/TILT_SERVICE_DEFAULTS.star +205 -0
  274. package/specs/specs/TILT_TECH_STACK.star +159 -0
@@ -0,0 +1,386 @@
1
+ # =============================================================================
2
+ # 📋 TILT SDK - MANIFEST LOADING (CEO Review: rename-service-manifest)
3
+ # =============================================================================
4
+ # Path: .tilt/topologies/tilt/discovery/manifest/loading.star
5
+ # Purpose: Load, synthesize, and apply defaults to manifests
6
+ #
7
+ # Manifest loading with synthesis-by-default
8
+ # Priority: service.json → synthesize from path
9
+ # =============================================================================
10
+
11
+ load('../../engine/topologies/tilt/manifest/constants.star',
12
+ 'MANIFEST_DEFAULTS',
13
+ 'DEFAULT_SYNCS',
14
+ 'MANIFEST_FILENAME',
15
+ 'BASE_PORT_FRONTEND',
16
+ 'BASE_PORT_BACKEND',
17
+ 'HEALTH_CHECK_PATH')
18
+ load('../../engine/topologies/tilt/common/utils.star', 'Utils')
19
+ load('../../engine/topologies/platform/docker/constants.star', 'PlatformDockerConstants')
20
+
21
+ def _check_prisma_folder(resource_path):
22
+ """
23
+ Check if prisma/ directory exists in resource path (silent).
24
+ Used to auto-detect has_migrator when not in features.
25
+ """
26
+ check_cmd = "test -d '{path}/prisma' && echo 'yes' || echo 'no'".format(path=resource_path)
27
+ result = str(local(check_cmd, quiet=True, echo_off=True)).strip()
28
+ return result == 'yes'
29
+
30
+
31
+ def check_prisma_folder(resource_path):
32
+ return _check_prisma_folder(resource_path)
33
+
34
+
35
+ def get_default_syncs_for_type(app_type, features=None):
36
+ """
37
+ Returns default sync paths based on app type and features.
38
+ """
39
+ if features == None:
40
+ features = []
41
+
42
+ syncs = list(DEFAULT_SYNCS.get(app_type, ['src', 'package.json']))
43
+
44
+ # Add prisma to syncs if using prisma feature
45
+ if app_type == 'backend' and 'prisma' in features and 'prisma' not in syncs:
46
+ syncs.append('prisma')
47
+
48
+ return syncs
49
+
50
+
51
+ def _extract_stack_from_path(resource_path, app_name):
52
+ """
53
+ Extract stack from resource path or app name.
54
+
55
+ Convention: services/product/{stack}/{resource-name}
56
+ Fallback: First segment of app_name before hyphen
57
+
58
+ Special handling: profile -> identity
59
+ """
60
+ parts = resource_path.rstrip('/').split('/')
61
+ if len(parts) >= 4 and parts[-4] == 'services' and parts[-3] == 'product':
62
+ stack = parts[-2]
63
+ # Normalize profile to identity
64
+ if stack == 'profile':
65
+ return 'identity'
66
+ return stack
67
+ else:
68
+ return app_name.split('-')[0] if '-' in app_name else app_name
69
+
70
+
71
+ def load_manifest(resource_path, persist_to_disk=False):
72
+ """
73
+ Load manifest with synthesis-by-default.
74
+
75
+ Priority order:
76
+ 1. service.json (manifest file)
77
+ 2. Synthesize from directory structure (synthesis-by-default for standard resources)
78
+
79
+ Args:
80
+ resource_path: Path to resource directory
81
+ persist_to_disk: If True, sync _internalDeps back to the physical manifest file
82
+
83
+ Returns:
84
+ Validated manifest dict with all defaults applied
85
+ """
86
+ manifest = None
87
+ manifest_source = None
88
+ manifest_path = None
89
+
90
+ # Prepend project root to relative paths for correct resolution
91
+ project_root = os.environ.get('TDK_PROJECT_ROOT', '')
92
+ if project_root and not resource_path.startswith('/'):
93
+ base_path = project_root + '/' + resource_path
94
+ else:
95
+ base_path = resource_path
96
+
97
+ # Try to load manifest file
98
+ manifest_full_path = base_path + '/' + MANIFEST_FILENAME
99
+ content = read_file(manifest_full_path, default='')
100
+
101
+ if content and str(content).strip():
102
+ manifest = decode_json(content)
103
+ manifest_source = 'service.json'
104
+ manifest_path = manifest_full_path
105
+ else:
106
+ # Synthesize from directory structure if no manifest found
107
+ manifest = synthesize_manifest_from_path(resource_path)
108
+ manifest_source = 'synthesized'
109
+ manifest_path = resource_path # Virtual path
110
+
111
+ # Validate JSON parsed correctly
112
+ if manifest == None and manifest_source != 'synthesized':
113
+ fail("""
114
+ ❌ ═══════════════════════════════════════════════════════════════════
115
+ ❌ INVALID JSON IN MANIFEST FILE
116
+ ❌ ═══════════════════════════════════════════════════════════════════
117
+ 📁 File: {path}
118
+ Source: {source}
119
+
120
+ Common issues:
121
+ • Missing closing quote: "appName": "my-app
122
+ • Trailing comma: "port": 4000,}}
123
+ • Missing comma between properties
124
+ • Single quotes instead of double quotes
125
+
126
+ Fix the JSON syntax and run 'tilt up' again.
127
+ ❌ ═══════════════════════════════════════════════════════════════════
128
+ """.format(path=manifest_path, source=manifest_source))
129
+
130
+ # Track manifest source for debugging
131
+ manifest['_manifestSource'] = manifest_source
132
+ manifest['_manifestPath'] = manifest_path
133
+
134
+ # Apply defaults and compute derived values
135
+ validated = apply_manifest_defaults(manifest, resource_path)
136
+
137
+ return validated
138
+
139
+
140
+ def synthesize_manifest_from_path(resource_path):
141
+ return _synthesize_manifest_from_path(resource_path)
142
+
143
+
144
+ def apply_manifest_defaults(manifest, resource_path):
145
+ return _apply_manifest_defaults(manifest, resource_path)
146
+
147
+
148
+ def _synthesize_manifest_from_path(resource_path):
149
+ """
150
+ Create a manifest based on directory naming conventions.
151
+ Used when manifest.json doesn't exist (backward compatibility).
152
+
153
+ Convention: services/product/{stack}/{stack}-{component}-{type}
154
+ Example: services/product/{stack}/{app-name}-{type}
155
+ -> appName: {app-name}-{type}
156
+ -> appType: {type}
157
+ -> stack: {stack}
158
+ """
159
+ parts = resource_path.rstrip('/').split('/')
160
+ dir_name = parts[-1] if parts else 'unknown-service'
161
+
162
+ # Determine appType from directory name suffix
163
+ app_type = 'backend'
164
+ if dir_name.endswith('-frontend'):
165
+ app_type = 'frontend'
166
+ elif dir_name.endswith('-backend'):
167
+ app_type = 'backend'
168
+ elif dir_name.endswith('-migrator'):
169
+ app_type = 'migrator'
170
+ elif dir_name.endswith('-sdk'):
171
+ app_type = 'sdk'
172
+ elif dir_name.endswith('-infra') or dir_name.endswith('-ui'):
173
+ app_type = 'library'
174
+ elif 'lib-' in dir_name or 'platform-' in dir_name:
175
+ app_type = 'library'
176
+
177
+ return {
178
+ 'appName': dir_name,
179
+ 'appType': app_type,
180
+ '_synthesized': True,
181
+ }
182
+
183
+
184
+ def _apply_manifest_defaults(manifest, resource_path):
185
+ """
186
+ 🎯 SMART DEFAULTS: Expand slim manifest to full config.
187
+
188
+ CEO Review updates (rename-service-manifest):
189
+ - Port is constant per appType (3000 frontend, 4000 backend) — Traefik handles routing
190
+ - Custom dockerfile support via manifest["dockerfile"] field
191
+ - Developer manually provides features (no auto-detection per requirement)
192
+
193
+ Convention over Configuration:
194
+ - features: ["nats", "prisma"] -> usePrisma=True, useNats=True
195
+ - domain: "order" -> databaseName="TDK_order"
196
+ - domain: "order" -> traefik.host="order.backend.{project}.local"
197
+ - domain: "order" -> nats.queueGroup="order_backend_svc"
198
+ """
199
+ result = dict(manifest)
200
+
201
+ # Normalize resource_path to be relative (strip leading / if present)
202
+ # Docker build context requires relative paths
203
+ if resource_path.startswith('/'):
204
+ resource_path = resource_path[1:]
205
+
206
+ # Track overrides for logging (task 4.8: override detection)
207
+ overrides = []
208
+ app_name = result.get('appName', '')
209
+
210
+ # Apply simple defaults
211
+ for key, default_value in MANIFEST_DEFAULTS.items():
212
+ if key not in result or result[key] == None:
213
+ result[key] = default_value
214
+
215
+ # 🎯 CEO REVIEW: Traefik-native port strategy
216
+ # Port is constant per appType, not dynamically assigned
217
+ # Traefik routes by hostname (e.g., booking.backend.{project}.local), not port
218
+ app_type = result.get('appType', 'backend')
219
+
220
+ # Set constant port per appType, but preserve explicit service.json ports.
221
+ computed_port = None
222
+ if app_type == 'frontend':
223
+ computed_port = BASE_PORT_FRONTEND
224
+ elif app_type == 'backend':
225
+ computed_port = BASE_PORT_BACKEND
226
+ elif app_type == 'migrator':
227
+ computed_port = 7000 # Migrator uses different range
228
+
229
+ if computed_port != None and ('port' not in manifest or manifest.get('port') == None):
230
+ result['port'] = computed_port
231
+
232
+ # 🎯 CEO REVIEW: Custom dockerfile support
233
+ # Validate custom dockerfile exists if specified
234
+ if 'dockerfile' in result:
235
+ custom_dockerfile = result['dockerfile']
236
+ dockerfile_path = resource_path + '/' + custom_dockerfile
237
+ # BUG FIX: Run check from project root since Tilt may run from .tdk/.tdk-out/
238
+ project_root = os.environ.get('TDK_PROJECT_ROOT', '')
239
+ if project_root:
240
+ dockerfile_exists = local("cd '{root}' && test -f '{path}' && echo 'yes' || echo 'no'".format(root=project_root, path=dockerfile_path), quiet=True, echo_off=True)
241
+ else:
242
+ dockerfile_exists = local("test -f '{path}' && echo 'yes' || echo 'no'".format(path=dockerfile_path), quiet=True, echo_off=True)
243
+ if str(dockerfile_exists).strip() != 'yes':
244
+ fail("""
245
+ ❌ ═══════════════════════════════════════════════════════════════════
246
+ ❌ CUSTOM DOCKERFILE NOT FOUND
247
+ ❌ ═══════════════════════════════════════════════════════════════════
248
+ 📁 Resource: {resource}
249
+ 🐳 Dockerfile: {dockerfile}
250
+
251
+ Manifest specifies custom Dockerfile but it doesn't exist.
252
+ Either:
253
+ • Create {dockerfile} in resource directory
254
+ • Remove "dockerfile" field to use auto-generated Dockerfile
255
+ ❌ ═══════════════════════════════════════════════════════════════════
256
+ """.format(resource=resource_path.split('/')[-1], dockerfile=custom_dockerfile))
257
+
258
+ # 🎯 FEATURE FLAGS -> BOOLEAN CONFIG
259
+ # NOTE: Developer manually provides features per CEO Review requirement
260
+ # No auto-detection from file existence (e.g., prisma/schema.prisma)
261
+ features = result.get('features', [])
262
+ result['usePrisma'] = 'prisma' in features
263
+ result['useNats'] = 'nats' in features
264
+ result['useTraefik'] = 'traefik' in features or app_type in ['backend', 'sdk']
265
+
266
+ # Extract stack from path if not set
267
+ if 'stack' not in result:
268
+ result['stack'] = _extract_stack_from_path(resource_path, app_name)
269
+
270
+ stack = result['stack']
271
+
272
+ # 🎯 SMART DATABASE NAME with override detection
273
+ computed_db_name = PlatformDockerConstants.get_db_name(stack)
274
+ if 'databaseName' not in result:
275
+ result['databaseName'] = computed_db_name
276
+ elif result['databaseName'] != computed_db_name:
277
+ overrides.append("databaseName: {} (auto: {})".format(result['databaseName'], computed_db_name))
278
+
279
+ # 🎯 SMART TRAEFIK CONFIG (Convention over Configuration)
280
+ # Apply to backends and SDKs that need Traefik routing
281
+ if result['useTraefik'] and app_type in ['backend', 'sdk']:
282
+ existing_traefik = result.get('traefik', {})
283
+ # Use apiPath from root level if available, otherwise use default pattern
284
+ root_api_path = result.get('apiPath')
285
+ default_path_prefix = root_api_path if root_api_path else '/api/' + stack
286
+
287
+ computed_traefik = {
288
+ 'host': stack + '.backend.' + PlatformDockerConstants.PROJECT_NAME + '.local',
289
+ 'pathPrefix': default_path_prefix,
290
+ 'healthCheck': HEALTH_CHECK_PATH,
291
+ }
292
+
293
+ # Check for overrides
294
+ if existing_traefik.get('host') and existing_traefik.get('host') != computed_traefik['host']:
295
+ overrides.append("traefik.host: {} (auto: {})".format(existing_traefik['host'], computed_traefik['host']))
296
+ if existing_traefik.get('pathPrefix') and existing_traefik.get('pathPrefix') != computed_traefik['pathPrefix']:
297
+ overrides.append("traefik.pathPrefix: {} (auto: {})".format(existing_traefik['pathPrefix'], computed_traefik['pathPrefix']))
298
+
299
+ result['traefik'] = {
300
+ 'host': existing_traefik.get('host', computed_traefik['host']),
301
+ 'pathPrefix': existing_traefik.get('pathPrefix', computed_traefik['pathPrefix']),
302
+ 'healthCheck': existing_traefik.get('healthCheck', computed_traefik['healthCheck']),
303
+ }
304
+
305
+ # 🎯 SMART NATS CONFIG
306
+ if result['useNats']:
307
+ existing_nats = result.get('nats', {})
308
+ result['nats'] = {
309
+ 'queueGroup': existing_nats.get('queueGroup', stack + '_' + app_type + '_svc'),
310
+ 'subjects': existing_nats.get('subjects', [stack + '.>']),
311
+ }
312
+
313
+ # Compute backend name for frontends with override detection
314
+ if app_type == 'frontend':
315
+ computed_backend = app_name.replace('-frontend', '-backend')
316
+ if 'backendName' not in result:
317
+ result['backendName'] = computed_backend
318
+ elif result['backendName'] != computed_backend:
319
+ overrides.append("backendName: {} (auto: {})".format(result['backendName'], computed_backend))
320
+
321
+ # 🎯 SMART BASEPATH for frontends (convention: /{stack}s)
322
+ computed_base_path = '/' + stack + 's'
323
+ if 'basePath' not in result:
324
+ result['basePath'] = computed_base_path
325
+ elif result['basePath'] != computed_base_path:
326
+ overrides.append("basePath: {} (auto: {})".format(result['basePath'], computed_base_path))
327
+
328
+ # 🎯 Always set _servicePath for all manifest types
329
+ result['_servicePath'] = resource_path
330
+
331
+ # 🎯 CENTRALIZED: Use Utils.get_internal_deps() for dependency extraction
332
+ internal_deps = Utils.get_internal_deps(resource_path)
333
+ result['_internalDeps'] = sorted(internal_deps)
334
+
335
+ # 🎯 CENTRALIZED: Use Utils.build_deps_mapping() for path resolution
336
+ result['_internalDepsMapping'] = Utils.build_deps_mapping(resource_path, internal_deps)
337
+
338
+ # 🎯 Log overrides if any were detected and verbose mode is enabled
339
+ if overrides and os.environ.get('TILT_LOG_LEVEL') == 'verbose':
340
+ print("📝 Override(s) detected for {}: {}".format(app_name, ', '.join(overrides)))
341
+
342
+ return result
343
+
344
+
345
+ def load_related_manifest(frontend_manifest, backend_suffix='-backend'):
346
+ """
347
+ Load the related backend manifest for a frontend resource.
348
+
349
+ CEO Review update: Supports dual-filename and synthesis-by-default.
350
+
351
+ Args:
352
+ frontend_manifest: The loaded frontend manifest
353
+ backend_suffix: Suffix to replace '-frontend' with
354
+
355
+ Returns:
356
+ Backend manifest dictionary, or None if not found
357
+ """
358
+ if frontend_manifest.get('appType') != 'frontend':
359
+ return None
360
+
361
+ resource_path = frontend_manifest.get('_servicePath', '')
362
+ backend_path = resource_path.replace('-frontend', backend_suffix)
363
+
364
+ # Use load_manifest which handles dual-filename and synthesis
365
+ return load_manifest(backend_path)
366
+
367
+
368
+ def get_manifest_filename(resource_path):
369
+ """
370
+ Determine which manifest filename is being used for a resource.
371
+
372
+ CEO Review addition: Utility for filename detection.
373
+
374
+ Args:
375
+ resource_path: Path to resource directory
376
+
377
+ Returns:
378
+ Filename string or None if neither exists (synthesis will be used)
379
+ """
380
+ # Check for manifest file (service.json)
381
+ manifest_path = resource_path + '/' + MANIFEST_FILENAME
382
+ if local("test -f '{path}' && echo 'yes' || echo 'no'".format(path=manifest_path), quiet=True, echo_off=True) == 'yes':
383
+ return MANIFEST_FILENAME
384
+
385
+ # Manifest not found - synthesis will be used
386
+ return None
@@ -0,0 +1,281 @@
1
+ # =============================================================================
2
+ # 📋 TILT SDK - MANIFEST NORMALIZATION & DISCOVERY
3
+ # =============================================================================
4
+ # Path: .tilt/topologies/tilt/discovery/manifest/normalize.star
5
+ # Purpose: Normalize manifests and provide helper accessors
6
+ # =============================================================================
7
+
8
+ load('./constants.star', 'MANIFEST_DEFAULTS', 'MANIFEST_FILENAME')
9
+ load('./loading.star', 'apply_manifest_defaults', 'check_prisma_folder', 'get_default_syncs_for_type')
10
+ load('./validation.star', 'validate_manifest')
11
+ load('../../engine/topologies/tilt/common/utils.star', 'Utils')
12
+ load('../../engine/topologies/platform/docker/constants.star', 'PlatformDockerConstants')
13
+ load('../../specs/specs/TILT_TECH_STACK.star', 'RUNTIME', 'MESSAGING')
14
+
15
+
16
+ def _load_and_normalize(manifest_path, warn_only=True):
17
+ """
18
+ 🎯 MAIN ENTRY POINT: Load manifest and normalize to full resource config.
19
+
20
+ This function is the bridge between the discovery system (resource_registry.star)
21
+ and the manifest loading system. It:
22
+
23
+ 1. Reads the JSON file
24
+ 2. Validates the structure
25
+ 3. Normalizes with smart defaults
26
+ 4. Computes labels, syncs, has_migrator
27
+ 5. Returns a resource-ready configuration
28
+
29
+ Args:
30
+ manifest_path: Full path to manifest file
31
+ warn_only: If True, return None on error instead of failing
32
+
33
+ Returns:
34
+ Normalized manifest dict or None on error (if warn_only=True)
35
+ """
36
+ # Prepend project root to relative paths for correct resolution
37
+ project_root = os.environ.get('TDK_PROJECT_ROOT', '')
38
+ if project_root and not manifest_path.startswith('/'):
39
+ full_path = project_root + '/' + manifest_path
40
+ else:
41
+ full_path = manifest_path
42
+
43
+ # Read manifest file
44
+ content = read_file(full_path, default='')
45
+
46
+ if not content or not str(content).strip():
47
+ if warn_only:
48
+ print(" ⚠️ Empty or missing manifest: " + manifest_path)
49
+ return None
50
+ fail("Empty or missing manifest: " + manifest_path)
51
+
52
+ # Parse JSON
53
+ manifest = decode_json(content)
54
+ if manifest == None:
55
+ if warn_only:
56
+ print(" ⚠️ Invalid JSON in manifest: " + manifest_path)
57
+ return None
58
+ fail("Invalid JSON in manifest: " + manifest_path)
59
+
60
+ # Validate
61
+ issues = validate_manifest(manifest)
62
+ if issues:
63
+ if warn_only:
64
+ for issue in issues:
65
+ print(" ⚠️ " + manifest_path + ": " + issue)
66
+ return None
67
+ fail("Manifest validation failed:\n" + "\n".join(issues))
68
+
69
+ # Extract service path from manifest path
70
+ resource_path = manifest_path.rsplit('/', 1)[0]
71
+
72
+ # BUG FIX: Normalize various path patterns to project-relative paths
73
+ # Pattern 1: Worktrees - .worktrees/task1-dry/services/platform/...
74
+ # Pattern 2: TDK output - .tdk/.tdk-out/../.. or .tdk/.tdk-out/../../path
75
+
76
+ if resource_path.startswith('.worktrees/'):
77
+ # Extract the part after the worktree name
78
+ parts = resource_path.split('/')
79
+ if len(parts) >= 3:
80
+ resource_path = '/'.join(parts[2:])
81
+ elif '.worktrees/' in resource_path:
82
+ worktree_idx = resource_path.find('.worktrees/')
83
+ after_worktree = resource_path[worktree_idx:]
84
+ parts = after_worktree.split('/')
85
+ if len(parts) >= 3:
86
+ resource_path = '/'.join(parts[2:])
87
+ elif '.tdk/.tdk-out/../..' in resource_path:
88
+ # Handle TDK output paths like: /path/to/.tdk/.tdk-out/../.. or .tdk/.tdk-out/../../services/...
89
+ tdk_idx = resource_path.find('.tdk/.tdk-out/../..')
90
+ if tdk_idx != -1:
91
+ after_tdk = resource_path[tdk_idx + len('.tdk/.tdk-out/../..'):]
92
+ if after_tdk.startswith('/'):
93
+ after_tdk = after_tdk[1:]
94
+ resource_path = after_tdk
95
+ elif resource_path.endswith('.tdk/.tdk-out/../..'):
96
+ # Handle bare TDK output path that ends with just the resolution pattern
97
+ resource_path = ''
98
+
99
+ # Apply smart defaults
100
+ normalized = apply_manifest_defaults(manifest, resource_path)
101
+
102
+ # Compute additional fields for registry compatibility
103
+ app_name = normalized.get('appName', '')
104
+ app_type = normalized.get('appType', 'backend')
105
+ stack = normalized.get('stack', '')
106
+ features = normalized.get('features', [])
107
+
108
+ # 🎯 AUTO-COMPUTE labels from stack
109
+ normalized['labels'] = ['app.' + stack]
110
+
111
+ # 🎯 AUTO-DETECT has_migrator
112
+ has_migrator = 'prisma' in features
113
+ if not has_migrator:
114
+ has_migrator = check_prisma_folder(resource_path)
115
+ normalized['has_migrator'] = has_migrator
116
+
117
+ # 🎯 AUTO-COMPUTE syncs if not specified
118
+ if normalized.get('syncs') == None:
119
+ normalized['syncs'] = get_default_syncs_for_type(app_type, features)
120
+
121
+ # 🎯 COMPUTE dockerfile path
122
+ dockerfile = normalized.get('dockerfile', 'Dockerfile')
123
+ normalized['dockerfile_path'] = app_name + '/' + dockerfile
124
+
125
+ # 🎯 MARK as frontend if applicable
126
+ if app_type == 'frontend':
127
+ normalized['frontend'] = True
128
+
129
+ # 🎯 EXTRACT dependencies for registry
130
+ normalized['serviceDependencies'] = normalized.get('dependsOn', [])
131
+
132
+ return normalized
133
+
134
+
135
+ def _discover_manifests_in_path(root_path):
136
+ """
137
+ Discover all manifest files under a root path.
138
+
139
+ Args:
140
+ root_path: Directory to search (e.g., 'services/product')
141
+
142
+ Returns:
143
+ List of manifest file paths
144
+ """
145
+ find_cmd = "find {root} -name '{filename}' -type f 2>/dev/null | sort".format(
146
+ root=root_path,
147
+ filename=MANIFEST_FILENAME
148
+ )
149
+ result = str(local(find_cmd, quiet=True, echo_off=True))
150
+
151
+ manifests = []
152
+ if result:
153
+ for line in result.strip().split('\n'):
154
+ if line and line.strip():
155
+ manifests.append(line.strip())
156
+
157
+ return manifests
158
+
159
+
160
+ def _load_all_manifests(root_path, warn_only=True):
161
+ """
162
+ Load and normalize all manifests under a root path.
163
+
164
+ Args:
165
+ root_path: Directory to search (e.g., 'services/product')
166
+ warn_only: If True, skip invalid manifests with warnings
167
+
168
+ Returns:
169
+ List of normalized manifest dicts
170
+ """
171
+ manifest_paths = _discover_manifests_in_path(root_path)
172
+ manifests = []
173
+
174
+ for path in manifest_paths:
175
+ normalized = _load_and_normalize(path, warn_only=warn_only)
176
+ if normalized:
177
+ manifests.append(normalized)
178
+
179
+ return manifests
180
+
181
+
182
+ def _get_port(manifest):
183
+ """Get the resource port from manifest."""
184
+ return manifest.get('port', MANIFEST_DEFAULTS['port'])
185
+
186
+
187
+ def _get_database_url(manifest, host='localhost', port=5432):
188
+ """
189
+ Generate DATABASE_URL from manifest.
190
+
191
+ Args:
192
+ manifest: Manifest dictionary
193
+ host: Database host (default: localhost)
194
+ port: Database port (default: 5432)
195
+
196
+ Returns:
197
+ PostgreSQL connection string
198
+ """
199
+ db_name = manifest.get('databaseName', PlatformDockerConstants.get_db_name(manifest.get('stack', 'app')))
200
+ return PlatformDockerConstants.get_tilt_database_url_template().format(
201
+ host=host,
202
+ port=port,
203
+ db=db_name,
204
+ )
205
+
206
+
207
+ def _print_summary(manifests):
208
+ """
209
+ Print formatted summary of loaded manifests.
210
+
211
+ Args:
212
+ manifests: List of manifest dictionaries
213
+ """
214
+ print("")
215
+ print("📋 ═══════════════════════════════════════════════════════════════")
216
+ print("📋 LOADED MANIFESTS")
217
+ print("📋 ═══════════════════════════════════════════════════════════════")
218
+
219
+ for m in manifests:
220
+ status = "✅" if not m.get('_synthesized') else "⚡"
221
+ app_name = m.get('appName', 'unknown')
222
+ app_type = m.get('appType', 'unknown')
223
+ port = m.get('port', 0)
224
+ stack = m.get('stack', 'unknown')
225
+
226
+ print(" {status} {name} | {type} | Port: {port} | Stack: {stack}".format(
227
+ status=status,
228
+ name=app_name,
229
+ type=app_type,
230
+ port=port,
231
+ stack=stack,
232
+ ))
233
+
234
+ print("📋 ═══════════════════════════════════════════════════════════════")
235
+ print("")
236
+
237
+
238
+ def _generate_manifest_template(app_name, stack, app_type='backend', port=4000):
239
+ """
240
+ 🎯 TEMPLATE GENERATOR: Returns a string with a standard manifest.
241
+ """
242
+ template = {
243
+ "$schema": "https://" + PlatformDockerConstants.EMAIL_DOMAIN + "/schemas/manifest-schema.json",
244
+ "appName": app_name,
245
+ "appType": app_type,
246
+ "stack": stack,
247
+ "port": port,
248
+ "replicas": 1,
249
+ "features": [MESSAGING, "infisical"],
250
+ "dependsOn": [],
251
+ "runtime": RUNTIME
252
+ }
253
+ return Utils.encode_json(template)
254
+
255
+
256
+ def load_and_normalize(manifest_path, warn_only=True):
257
+ return _load_and_normalize(manifest_path, warn_only=warn_only)
258
+
259
+
260
+ def discover_manifests_in_path(root_path):
261
+ return _discover_manifests_in_path(root_path)
262
+
263
+
264
+ def load_all_manifests(root_path, warn_only=True):
265
+ return _load_all_manifests(root_path, warn_only=warn_only)
266
+
267
+
268
+ def get_port(manifest):
269
+ return _get_port(manifest)
270
+
271
+
272
+ def get_database_url(manifest, host='localhost', port=5432):
273
+ return _get_database_url(manifest, host=host, port=port)
274
+
275
+
276
+ def print_summary(manifests):
277
+ return _print_summary(manifests)
278
+
279
+
280
+ def generate_manifest_template(app_name, stack, app_type='backend', port=4000):
281
+ return _generate_manifest_template(app_name, stack, app_type=app_type, port=port)