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

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,191 @@
1
+ # =============================================================================
2
+ # 👁️ DISCOVERY DAEMON - Continuous Service Monitoring
3
+ # =============================================================================
4
+ # Monitors filesystem for new resources and triggers incremental registration
5
+ # =============================================================================
6
+
7
+ load("./resource_snapshot.star", "ResourceSnapshot")
8
+ load("./resource_registry.star", "CacheOps")
9
+ load("../engine/topologies/tilt/manifest/loader.star", "ManifestLoader")
10
+ # Discovery config - inlined for unified repo
11
+ def get_discovery_config():
12
+ return {
13
+ "scan_interval_seconds": 5,
14
+ "max_scan_timeout_ms": 5000,
15
+ "initial_scan_delay_seconds": 2,
16
+ "scan_debounce_ms": 100,
17
+ }
18
+
19
+ _DISCOVERY_CONFIG = get_discovery_config()
20
+
21
+ def run_discovery_daemon(
22
+ scan_interval_seconds=_DISCOVERY_CONFIG["scan_interval_seconds"],
23
+ focus_mode=False,
24
+ focus_stacks=[],
25
+ auto_init_new=True,
26
+ on_new_resource=None,
27
+ verbose=False
28
+ ):
29
+ """
30
+ Run the discovery daemon with continuous monitoring loop.
31
+
32
+ This function is designed to be called as a serve_cmd in a local_resource,
33
+ running continuously to monitor for new resources.
34
+
35
+ Args:
36
+ scan_interval_seconds: Seconds between scans (default: from get_discovery_config)
37
+ focus_mode: Whether focus mode is active
38
+ focus_stacks: List of domains to focus on (empty = all)
39
+ auto_init_new: Whether to auto-init new resources
40
+ on_new_resource: Callback function for new resource detection
41
+ verbose: Enable verbose logging
42
+ """
43
+ print("🔍 Discovery daemon starting...")
44
+ print(" └─ Scan interval: {}s".format(scan_interval_seconds))
45
+ print(" └─ Focus mode: {}".format("enabled" if focus_mode else "disabled"))
46
+ print(" └─ Auto-init: {}".format("enabled" if auto_init_new else "disabled"))
47
+
48
+ # Initialize snapshot if it doesn't exist
49
+ if not ResourceSnapshot.exists():
50
+ initial_services = ResourceSnapshot.scan()
51
+ ResourceSnapshot.save(initial_services)
52
+ print("✅ Initial snapshot created: {} services".format(len(initial_services)))
53
+
54
+ # Continuous monitoring loop
55
+ while True:
56
+ start_time = 0
57
+
58
+ # Load previous snapshot
59
+ old_snapshot = ResourceSnapshot.load_from_file()
60
+
61
+ # Scan for current services
62
+ current_services = ResourceSnapshot.scan()
63
+
64
+ # Compare to find changes
65
+ diff = ResourceSnapshot.diff(old_snapshot, current_services)
66
+
67
+ # Handle new resources
68
+ if diff.added_count > 0:
69
+ if verbose:
70
+ print("🔍 Detected {} new resource(s)".format(diff.added_count))
71
+
72
+ for resource_path in diff.added:
73
+ _handle_new_service(
74
+ resource_path,
75
+ focus_mode=focus_mode,
76
+ focus_stacks=focus_stacks,
77
+ auto_init=auto_init_new,
78
+ verbose=verbose,
79
+ on_new_resource=on_new_resource
80
+ )
81
+
82
+ # Handle removed services (optional - just log for now)
83
+ if diff.removed_count > 0 and verbose:
84
+ print("🗑️ Detected {} removed service(s)".format(diff.removed_count))
85
+ for resource_path in diff.removed:
86
+ print(" - {}".format(resource_path))
87
+
88
+ # Update snapshot if there were changes
89
+ if diff.added_count > 0 or diff.removed_count > 0:
90
+ ResourceSnapshot.save(current_services)
91
+ if verbose:
92
+ print("📸 Snapshot updated: {} services".format(len(current_services)))
93
+
94
+ # Calculate sleep time (account for scan duration)
95
+ elapsed = 0 - start_time
96
+ sleep_time = max(0, scan_interval_seconds - elapsed)
97
+
98
+ # Sleep before next scan
99
+ if sleep_time > 0:
100
+ sleep(sleep_time)
101
+
102
+ def _handle_new_service(
103
+ resource_path,
104
+ focus_mode=False,
105
+ focus_stacks=[],
106
+ auto_init=True,
107
+ verbose=False,
108
+ on_new_resource=None
109
+ ):
110
+ """
111
+ Process a newly detected service.
112
+
113
+ Args:
114
+ resource_path: Path to service.json
115
+ focus_mode: Whether to filter by stack
116
+ focus_stacks: Allowed domains
117
+ auto_init: Whether to auto-init the service
118
+ verbose: Verbose logging
119
+ on_new_resource: Optional callback
120
+ """
121
+ # Extract service directory
122
+ resource_dir = resource_path.rsplit("/", 1)[0] if "/" in resource_path else resource_path
123
+
124
+ # Check for package.json (complete service structure)
125
+ package_json_path = resource_dir + "/package.json"
126
+ if not _file_exists(package_json_path):
127
+ print("⏳ Waiting for package.json in {}".format(resource_dir))
128
+ return
129
+
130
+ # Load and validate manifest
131
+ load_result = ManifestLoader.load_from_file(resource_path)
132
+ if load_result.error:
133
+ print("❌ Invalid manifest: {}".format(resource_path))
134
+ print(" └─ Error: {}".format(load_result.error))
135
+ return
136
+
137
+ manifest = load_result.manifest
138
+ if not manifest:
139
+ print("❌ Empty manifest: {}".format(resource_path))
140
+ return
141
+
142
+ # Get service name
143
+ resource_name = manifest.get("appName", "")
144
+ if not resource_name:
145
+ print("❌ Missing appName in: {}".format(resource_path))
146
+ return
147
+
148
+ # Check for duplicates
149
+ if CacheOps.has(resource_name):
150
+ if verbose:
151
+ print("ℹ️ Service already registered: {}".format(resource_name))
152
+ return
153
+
154
+ # Check focus mode
155
+ stack = manifest.get("stack", "")
156
+ if focus_mode and focus_stacks and stack not in focus_stacks:
157
+ print("📋 Focus mode: Skipping {} (stack: {})".format(resource_name, stack))
158
+ return
159
+
160
+ # Log detection
161
+ print("🔍 New resource detected: {}".format(resource_name))
162
+ print(" └─ Path: {}".format(resource_path))
163
+ print(" └─ Stack: {}".format(stack))
164
+ print(" └─ Type: {}".format(manifest.get("appType", "unknown")))
165
+
166
+ # Call callback if provided
167
+ if on_new_resource:
168
+ on_new_resource(resource_name, resource_path, manifest, auto_init)
169
+
170
+ def _file_exists(path):
171
+ """Check if a file exists."""
172
+ result = local(
173
+ "test -f {} && echo 'yes' || echo 'no'".format(path),
174
+ quiet=True,
175
+ echo_off=True
176
+ )
177
+ return str(result).strip() == "yes"
178
+
179
+ def time_now():
180
+ """Get current time in seconds (for timing)."""
181
+ result = local("date +%s", quiet=True, echo_off=True)
182
+ return int(str(result).strip())
183
+
184
+ def sleep(seconds):
185
+ """Sleep for specified seconds."""
186
+ local("sleep {}".format(seconds), quiet=True, echo_off=True)
187
+
188
+ # Export daemon functions
189
+ DiscoveryDaemon = struct(
190
+ run=run_discovery_daemon,
191
+ )
@@ -0,0 +1,460 @@
1
+ load("../engine/topologies/tilt/manifest/parser.star", "extract_stack_from_path")
2
+ load("../engine/topologies/tilt/manifest/loader.star", "ManifestLoader")
3
+ load("./manifest/loading.star", "get_default_syncs_for_type")
4
+
5
+ # Out of scope stacks - defined locally for unified repo
6
+ OUT_OF_SCOPE_STACKS = ["warehouse", "logistics", "accounting"]
7
+
8
+ load("./manifest/constants.star", "MANIFEST_FILENAME", "MANIFEST_FILENAME_YAML")
9
+
10
+ # Service defaults - inlined for unified repo
11
+ def get_default_port(app_type):
12
+ if app_type == "frontend":
13
+ return 3000
14
+ return 4000
15
+
16
+ BASE_PORT_FRONTEND = 3000
17
+ BASE_PORT_BACKEND = 4000
18
+
19
+ # Phase 1: Use json_manifest_scanner for manifest discovery (enables Phase 2 multi-source discovery)
20
+ load("./json_manifest_scanner.star", "discover_json_manifests")
21
+ load("./constants.star",
22
+ "RESOURCES_ROOT",
23
+ "DISCOVERY_SCAN_ROOTS",
24
+ "SCAN_ROOT_PATTERNS",
25
+ "MAX_SCAN_TIMEOUT_MS",
26
+ "MAX_MANIFESTS_PER_ROOT")
27
+
28
+ # Phase 1.5: Validation integration for stability
29
+ load("./validation.star", "validate")
30
+
31
+ # Phase 2: Root overlap validation (prevents duplicate scanning from misconfiguration)
32
+ def _validate_scan_roots(roots):
33
+ """Ensure no root is a subdirectory of another."""
34
+ sorted_roots = sorted(roots, key=len)
35
+ for i, root in enumerate(sorted_roots):
36
+ for other in sorted_roots[i+1:]:
37
+ root_normalized = root.rstrip("/") + "/"
38
+ other_normalized = other.rstrip("/") + "/"
39
+ if other_normalized.startswith(root_normalized):
40
+ print("⚠️ Warning: {} is subdirectory of {}".format(other, root))
41
+
42
+ # Built-in functions available in Tilt (no load needed)
43
+ # read_json, local are built-in
44
+
45
+ # Define locally since parser.star exports this as private
46
+ def _determine_app_type(manifest, resource_path):
47
+ """
48
+ Determine app type from manifest or service path.
49
+ """
50
+ # Check if manifest has explicit appType
51
+ if manifest.get("appType") in ["frontend", "backend", "library", "migrator", "sdk", "worker", "infra"]:
52
+ return manifest["appType"]
53
+
54
+ # Extract from path
55
+ dir_name = resource_path.split("/")[-1] if resource_path else ""
56
+
57
+ if dir_name.endswith("-frontend"):
58
+ return "frontend"
59
+ if dir_name.endswith("-backend"):
60
+ return "backend"
61
+ if dir_name.endswith("-migrator"):
62
+ return "migrator"
63
+ if dir_name.endswith("-sdk"):
64
+ return "sdk"
65
+
66
+ # Default to backend
67
+ return "backend"
68
+
69
+ def _normalize_manifest(manifest, resource_path):
70
+ """
71
+ Normalize manifest to discovery resource format.
72
+ Phase 5: Works with both legacy and new manifest system normalized manifests.
73
+ """
74
+ # If manifest is already normalized by new system, use it directly
75
+ if manifest.get("_normalized") or manifest.get("syncs"):
76
+ # Extract fields from normalized manifest
77
+ app_name = manifest.get("appName", resource_path.split("/")[-1])
78
+ app_type = manifest.get("appType", _determine_app_type(manifest, resource_path))
79
+ stack = manifest.get("stack", extract_stack_from_path(resource_path))
80
+ port = manifest.get("port", BASE_PORT_BACKEND if app_type == "backend" else BASE_PORT_FRONTEND)
81
+ else:
82
+ # Legacy path: compute fields
83
+ app_name = manifest.get("appName", resource_path.split("/")[-1])
84
+ app_type = _determine_app_type(manifest, resource_path)
85
+ stack = manifest.get("stack", extract_stack_from_path(resource_path))
86
+ port = manifest.get("port", BASE_PORT_BACKEND if app_type == "backend" else BASE_PORT_FRONTEND)
87
+
88
+ features = manifest.get("features", [])
89
+ syncs = manifest.get("syncs", get_default_syncs_for_type(app_type, features))
90
+ dockerfile = manifest.get("dockerfile", ".autogenerated/Dockerfile.app.autogenerated")
91
+
92
+ resource = {
93
+ "name": app_name,
94
+ "dockerfile": app_name + "/" + dockerfile,
95
+ "syncs": syncs,
96
+ "port": port,
97
+ "stack": stack,
98
+ "_manifest": manifest.get("_manifest", manifest),
99
+ "_resource_path": resource_path,
100
+ }
101
+
102
+ if app_type == "frontend":
103
+ resource["frontend"] = True
104
+ resource["backendName"] = manifest.get("backendName", app_name.replace("-frontend", "-backend"))
105
+
106
+ if manifest.get("target_path"):
107
+ resource["target_path"] = manifest["target_path"]
108
+ if manifest.get("prisma_client_path"):
109
+ resource["prisma_client_path"] = manifest["prisma_client_path"]
110
+ if manifest.get("registry_host"):
111
+ resource["registry_host"] = manifest["registry_host"]
112
+ if manifest.get("start_command"):
113
+ resource["start_command"] = manifest["start_command"]
114
+
115
+ return resource
116
+
117
+
118
+ def _get_stack_path(stack):
119
+ return "services/product/" + stack
120
+
121
+
122
+ def initialize_discovery(cache, second_pass=False):
123
+ """
124
+ Initialize discovery cache by scanning for manifest files.
125
+
126
+ Args:
127
+ cache: Dictionary to populate with discovery data
128
+ second_pass: If True, this is the second initialization pass (for logging only)
129
+ """
130
+ if cache.get("initialized"):
131
+ return
132
+
133
+ # Scan for resources from JSON manifests (source of truth)
134
+ # YAML files are only for Tilt resource tracking, not data parsing
135
+ if second_pass:
136
+ print(" 1.2 Preparing Tilt resources...")
137
+ else:
138
+ print(" 1.1 Scanning service manifests...")
139
+
140
+ resources = _scan_resources()
141
+
142
+ # Build dependency graph and aliases
143
+ dependencies = {}
144
+ aliases = {}
145
+ path_map = {}
146
+
147
+ for resource in resources:
148
+ name = resource.get("name", "")
149
+ path = resource.get("path", "")
150
+
151
+ # Map name to path
152
+ if name:
153
+ aliases[name] = path
154
+ path_map[path] = name
155
+
156
+ # Extract dependencies from manifest
157
+ manifest = resource.get("_manifest", {})
158
+ deps = manifest.get("dependsOn", [])
159
+ if deps:
160
+ dependencies[name] = deps
161
+
162
+ # Populate cache
163
+ cache["app_resources"] = resources
164
+ cache["resource_dependencies"] = dependencies
165
+ cache["resource_aliases"] = aliases
166
+ cache["resource_path_map"] = path_map
167
+ cache["stack_configs"] = {}
168
+ cache["initialized"] = True
169
+ if second_pass:
170
+ cache["second_pass"] = True
171
+
172
+
173
+ def _scan_resources():
174
+ """
175
+ FIRST PASS: Scan for all resources with JSON manifest files from multiple roots.
176
+ Phase 2: Multi-root discovery with validation, deduplication, and explicit stack extraction.
177
+ Returns list of resource dictionaries.
178
+ """
179
+ resources = []
180
+
181
+ # Phase 1.5: Track validation stats and skipped stacks for discovery summary
182
+ validation_stats = {
183
+ "total_manifests": 0,
184
+ "valid_manifests": 0,
185
+ "manifests_with_warnings": 0,
186
+ "total_warnings": 0,
187
+ }
188
+ skipped_stacks = {} # Track skipped stacks for summary
189
+
190
+ # Phase 2: Validate scan roots don't overlap (prevents duplicate scanning)
191
+ _validate_scan_roots(DISCOVERY_SCAN_ROOTS)
192
+
193
+ # Phase 2: Scan all root directories and collect manifest paths
194
+ all_manifest_paths = [] # Collect from all roots
195
+
196
+ # Scan all root directories (quiet unless verbose)
197
+ total_roots = len(DISCOVERY_SCAN_ROOTS)
198
+
199
+ for root in DISCOVERY_SCAN_ROOTS:
200
+ # Use json_manifest_scanner.discover_json_manifests() to find all manifest files
201
+ paths = discover_json_manifests(root)
202
+
203
+ if paths:
204
+ # Phase 2: Performance guardrails - warn if root is too large
205
+ if len(paths) > MAX_MANIFESTS_PER_ROOT:
206
+ print(" ⚠️ Large root: {} has {} manifests (max: {})".format(
207
+ root, len(paths), MAX_MANIFESTS_PER_ROOT))
208
+
209
+ # Only show in verbose mode
210
+ if os.environ.get('TILT_LOG_LEVEL') == 'verbose':
211
+ print(" 📁 Scanned {} manifests from {}".format(len(paths), root))
212
+ all_manifest_paths.extend(paths)
213
+
214
+ # Phase 2: Deduplicate by path (in case of overlaps between roots)
215
+ seen_paths = {}
216
+ manifest_paths = []
217
+ for path in all_manifest_paths:
218
+ if path not in seen_paths:
219
+ seen_paths[path] = True
220
+ manifest_paths.append(path)
221
+
222
+ # CRITICAL FIX: Filter out tdk-cli internal paths to prevent self-scanning
223
+ # This prevents tdk-cli from discovering its own internal test stubs
224
+ def _is_tdk_cli_internal_path(path):
225
+ """Check if path is within tdk-cli's own directory structure."""
226
+ internal_markers = [
227
+ "tdk-cli/discovery/services/",
228
+ "tdk-cli/discovery/identity-",
229
+ ]
230
+ for marker in internal_markers:
231
+ if marker in path:
232
+ return True
233
+ return False
234
+
235
+ filtered_manifest_paths = []
236
+ for path in manifest_paths:
237
+ if _is_tdk_cli_internal_path(path):
238
+ print("⚠️ Skipping tdk-cli internal stub: {}".format(path))
239
+ continue
240
+ filtered_manifest_paths.append(path)
241
+
242
+ manifest_paths = filtered_manifest_paths
243
+
244
+ if not manifest_paths:
245
+ return resources
246
+
247
+ for manifest_path in manifest_paths:
248
+ # manifest_path already stripped by json_manifest_scanner
249
+
250
+ # Parse manifest using proper loader (handles JSON and YAML)
251
+ # First, extract service path from manifest path
252
+ full_resource_path = manifest_path.rsplit("/", 1)[0]
253
+
254
+ # Normalize path to be relative (strip leading / if present)
255
+ # Docker build context requires relative paths
256
+ if full_resource_path.startswith('/'):
257
+ full_resource_path = full_resource_path[1:]
258
+
259
+ # Use the loader which handles both JSON and YAML and normalizes format
260
+ load_result = ManifestLoader.load_from_file(manifest_path)
261
+ if load_result.error:
262
+ print("Error loading manifest: " + load_result.error)
263
+ continue
264
+
265
+ manifest = load_result.manifest
266
+ if not manifest:
267
+ continue
268
+
269
+ # Phase 1.5: Validate manifest structure and content
270
+ validation_stats["total_manifests"] += 1
271
+ validation_issues = validate(manifest)
272
+ if validation_issues:
273
+ validation_stats["manifests_with_warnings"] += 1
274
+ validation_stats["total_warnings"] += len(validation_issues)
275
+ # Log all validation issues with file path for context
276
+ for issue in validation_issues:
277
+ print("⚠️ Validation: " + manifest_path + ": " + issue)
278
+ # Lenient mode: log warnings but continue discovery
279
+ # (Strict mode could fail here with: continue)
280
+ else:
281
+ validation_stats["valid_manifests"] += 1
282
+
283
+ # Skip out-of-scope stacks for alpha (defined in spec.master)
284
+ stack = manifest.get("stack", "")
285
+ if stack in OUT_OF_SCOPE_STACKS:
286
+ # Track skipped stack for summary instead of printing immediately
287
+ if stack not in skipped_stacks:
288
+ skipped_stacks[stack] = 0
289
+ skipped_stacks[stack] += 1
290
+ continue
291
+
292
+ # Convert relative path to project-root-relative path
293
+ # manifest_path is like: ../../../../services/product/{domain}/{service}/manifest.json
294
+ # We want: services/product/{domain}/{service}
295
+ if manifest_path.startswith("../../../../"):
296
+ project_relative_path = manifest_path[12:] # Remove "../../../../" prefix
297
+ else:
298
+ project_relative_path = manifest_path
299
+
300
+ # Extract service info from the project-relative path
301
+ # Format: services/product/{domain}/{service}/" + MANIFEST_FILENAME,
302
+ path_parts = project_relative_path.split("/")
303
+ if len(path_parts) < 2:
304
+ continue
305
+
306
+ resource_dir = path_parts[-2] # e.g., "user-management-frontend"
307
+
308
+ # Phase 2: Use explicit stack extraction patterns
309
+ # Determine which root this manifest came from
310
+ matching_root = None
311
+ for root in DISCOVERY_SCAN_ROOTS:
312
+ if manifest_path.startswith(root):
313
+ matching_root = root
314
+ break
315
+
316
+ # Use configured pattern or default to parent folder
317
+ pattern = SCAN_ROOT_PATTERNS.get(matching_root, {"stack_depth": -2})
318
+ depth = pattern["stack_depth"]
319
+
320
+ # Extract stack based on configured depth
321
+ if abs(depth) <= len(path_parts):
322
+ stack_dir = path_parts[depth]
323
+ else:
324
+ stack_dir = path_parts[-2] if len(path_parts) >= 2 else "" # Fallback to parent
325
+
326
+ # Build paths relative to project root (for use by other modules)
327
+ # Handle both nested paths (services/product/stack/service) and flat paths (identity-*)
328
+ if len(path_parts) >= 3:
329
+ base_path = "/".join(path_parts[:-2]) # e.g., "services/product/users"
330
+ elif len(path_parts) == 2:
331
+ base_path = path_parts[0] # e.g., "identity-management-backend"
332
+ else:
333
+ base_path = path_parts[0] if path_parts else ""
334
+ resource_path = base_path # This is the DOMAIN path
335
+ full_resource_path = project_relative_path.rsplit("/", 1)[0] # Full path to service
336
+
337
+ # Normalize path to be relative (strip leading / if present)
338
+ # Docker build context requires relative paths
339
+ if full_resource_path.startswith('/'):
340
+ full_resource_path = full_resource_path[1:]
341
+
342
+ # BUG FIX: Normalize worktree paths to project-relative paths
343
+ # Worktrees create paths like .worktrees/task1-dry/services/platform/...
344
+ # These need to be converted to services/platform/... for correct Dockerfile resolution
345
+ if full_resource_path.startswith('.worktrees/'):
346
+ # Extract the part after the worktree name
347
+ parts = full_resource_path.split('/')
348
+ if len(parts) >= 3:
349
+ # Skip .worktrees/ and the worktree name
350
+ full_resource_path = '/'.join(parts[2:])
351
+ elif '.worktrees/' in full_resource_path:
352
+ # Handle paths that include worktree somewhere in the middle
353
+ worktree_idx = full_resource_path.find('.worktrees/')
354
+ after_worktree = full_resource_path[worktree_idx:]
355
+ parts = after_worktree.split('/')
356
+ if len(parts) >= 3:
357
+ full_resource_path = '/'.join(parts[2:])
358
+
359
+ # Also normalize resource_path (base_path) if it has worktree
360
+ if resource_path.startswith('.worktrees/'):
361
+ parts = resource_path.split('/')
362
+ if len(parts) >= 3:
363
+ resource_path = '/'.join(parts[2:])
364
+ elif '.worktrees/' in resource_path:
365
+ worktree_idx = resource_path.find('.worktrees/')
366
+ after_worktree = resource_path[worktree_idx:]
367
+ parts = after_worktree.split('/')
368
+ if len(parts) >= 3:
369
+ resource_path = '/'.join(parts[2:])
370
+
371
+ app_name = manifest.get("appName", resource_dir)
372
+ app_type = manifest.get("appType", "backend")
373
+ stack = manifest.get("stack", stack_dir)
374
+ port = manifest.get("port", get_default_port(app_type))
375
+ features = manifest.get("features", [])
376
+
377
+ # Build resource entry
378
+ # dockerfile path is relative to the resource directory (prepended with res_path by caller)
379
+ # Use custom dockerfile from manifest if specified, otherwise use autogenerated default
380
+ custom_dockerfile = manifest.get("dockerfile")
381
+ if custom_dockerfile:
382
+ dockerfile_path = custom_dockerfile
383
+ else:
384
+ dockerfile_path = ".autogenerated/Dockerfile.app.autogenerated"
385
+
386
+ resource = {
387
+ "name": app_name,
388
+ "dockerfile": dockerfile_path,
389
+ "syncs": get_default_syncs_for_type(app_type, features),
390
+ "port": port,
391
+ "stack": stack,
392
+ "_manifest": manifest,
393
+ "_resource_path": full_resource_path,
394
+ }
395
+
396
+ # Add frontend-specific fields
397
+ if app_type == "frontend":
398
+ resource["frontend"] = True
399
+ resource["backendName"] = manifest.get("backendName", app_name.replace("-frontend", "-backend"))
400
+
401
+ # Build resource entry
402
+ resource_key = stack if stack else app_name.replace("-" + app_type, "")
403
+
404
+ # Check if app resource already exists (merge resources for same stack)
405
+ existing_app_resource = None
406
+ for app_res in resources:
407
+ if app_res["name"] == resource_key:
408
+ existing_app_resource = app_res
409
+ break
410
+
411
+ if existing_app_resource:
412
+ # Check if resource with same name already exists (prevent duplicates from multiple scan roots)
413
+ resource_exists = False
414
+ for existing_res in existing_app_resource["resources"]:
415
+ if existing_res.get("name") == resource["name"]:
416
+ resource_exists = True
417
+ print(" ⚠️ Skipping duplicate resource: " + app_name + " (from " + resource_path + ")")
418
+ break
419
+
420
+ if not resource_exists:
421
+ # Add resource to existing app resource
422
+ existing_app_resource["resources"].append(resource)
423
+ existing_app_resource["labels"].append("app." + app_name)
424
+ pass # Service merged into existing domain
425
+ else:
426
+ # Create new app resource entry
427
+ # Use resource_path (domain path) for the app_resource - docker-compose goes here
428
+ # Individual resources have _resource_path for their specific paths
429
+ app_resource = {
430
+ "name": resource_key,
431
+ "appName": resource_key,
432
+ "path": resource_path,
433
+ "labels": ["app." + app_name] if app_name else [],
434
+ "resources": [resource],
435
+ }
436
+ pass # New domain group created
437
+ resources.append(app_resource)
438
+
439
+ # Phase 1.5: Print consolidated discovery summary
440
+ if validation_stats["total_manifests"] > 0:
441
+ # Calculate total skipped manually (sum() not available in Starlark)
442
+ total_skipped = 0
443
+ for count in skipped_stacks.values():
444
+ total_skipped += count
445
+ print("✅ {} resources loaded ({} valid, {} skipped)".format(
446
+ validation_stats["total_manifests"],
447
+ validation_stats["valid_manifests"],
448
+ total_skipped))
449
+
450
+ if validation_stats["manifests_with_warnings"] > 0:
451
+ print(" ⚠️ {} manifest(s) with {} validation warning(s)".format(
452
+ validation_stats["manifests_with_warnings"],
453
+ validation_stats["total_warnings"]))
454
+
455
+ if skipped_stacks and os.environ.get('TILT_LOG_LEVEL') == 'verbose':
456
+ skipped_list = ", ".join(["{} ({})".format(d, c) for d, c in skipped_stacks.items()])
457
+ print(" 📋 Focus mode skipped: {}".format(skipped_list))
458
+
459
+
460
+ return resources
@@ -0,0 +1,5 @@
1
+ def by_app_type(manifests, app_type):
2
+ return [m for m in manifests if m.get("appType") == app_type]
3
+
4
+ def by_stack(manifests, stack):
5
+ return [m for m in manifests if m.get("stack") == stack]
@@ -0,0 +1,26 @@
1
+ appName: identity-management-backend
2
+ appType: backend
3
+ stack: identity
4
+ port: 4004
5
+ apiPath: /api/identity-management
6
+ features:
7
+ - nats
8
+ - prisma
9
+ healthCheck: True
10
+ healthCheckPath: /api/v1/health
11
+ params:
12
+ BASE_URL: http://identity.backend.beauty.local
13
+ EMAIL_FROM: noreply@beauty-crm.local
14
+ UPSTASH_REDIS_REST_URL: http://redis:6379
15
+ path: identity-management-backend
16
+ playwright: True
17
+ prisma_client_path: ./node_modules/.prisma/client/client
18
+ replicas: 1
19
+ runtime: bun
20
+ secrets:
21
+ optional:
22
+ required:
23
+ - JWT_REFRESH_SECRET
24
+ - JWT_SECRET
25
+ - PASSWORD_PEPPER
26
+ - UPSTASH_REDIS_REST_TOKEN