@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,524 @@
1
+ # =============================================================================
2
+ # 🎯 TILT SDK - MANIFEST-DRIVEN CONFIG GENERATOR
3
+ # =============================================================================
4
+ # Path: .tilt/provisioner/orchestrator/generators/manifest_resource.star
5
+ # Purpose: Per-manifest config generation as Tilt resources
6
+ # =============================================================================
7
+ #
8
+ # Creates individual Tilt resources for each manifest that trigger all
9
+ # config generators (tsconfig, dockerfile, vite, npmrc, etc.) for that
10
+ # specific service.
11
+ #
12
+ # Benefits:
13
+ # - Granular tracking (see "my-service-config-gen" in Tilt UI)
14
+ # - Efficient updates (only regenerate changed manifests)
15
+ # - Better dependency management (explicit resource deps)
16
+ # - Cleaner architecture (manifest → resource → 10 generators)
17
+ # =============================================================================
18
+
19
+ # === INLINED CONSTANTS for pure extension loading ===
20
+ OUTPUT = struct(AUTOGENERATED_FOLDER = ".autogenerated")
21
+ # === END INLINED CONSTANTS ===
22
+
23
+
24
+ load('../../../../../topologies/tilt/common/utils.star', 'Utils')
25
+ load('../../../../../../discovery/loading.star', 'Manifest')
26
+ load('../../../../../topologies/tilt/manifest/constants.star', 'AUTOGENERATED_CONFIG_PATHS', 'PACKAGE_CONFIG_FILENAMES', 'GENERATED_CONFIG_FILENAMES', 'MANIFEST_FILENAME', 'MANIFEST_FILENAME_YAML', 'BASE_PORT_FRONTEND')
27
+ # Load OUTPUT for AUTOGENERATED_FOLDER
28
+ load('../../../generators/vite_config.star', 'Vite')
29
+ load('../../../generators/nginx_conf.star', 'Nginx')
30
+ load('../../../generators/api_gateway.star', 'ApiGateway')
31
+ load('../../../generators/tsconfig.star', 'TSConfig')
32
+ load('../../../../platform/docker/index.star', 'Docker')
33
+ load('../../../generators/npmrc.star', 'PackageConfig')
34
+ load('../../libs.star', 'Libs')
35
+ load('./env.star', 'EnvGenerators')
36
+ load('./frontend.star', 'FrontendGenerators')
37
+ load('../helpers.star', 'OrchestratorHelpers')
38
+
39
+ # =============================================================================
40
+ # NEW: Pre-Flight Validation Generators (v2.0)
41
+ # These generators prevent vibe-coding failures by validating before build
42
+ # =============================================================================
43
+ load('../../../generators/index.star', 'Generators')
44
+
45
+ _generate_env_file = EnvGenerators.generate_env_file
46
+ _generate_frontend_api_files = FrontendGenerators.generate_all
47
+ _get_db_name_for_resource = OrchestratorHelpers.get_db_name
48
+ _get_resource_display_name = OrchestratorHelpers.get_display_name
49
+
50
+
51
+ def _run_pre_flight_validations(resource_name, resource_path, manifest, ctx):
52
+ """
53
+ Run pre-flight validations before generating configs.
54
+
55
+ This catches issues BEFORE they cause build failures:
56
+ - Docker path alias issues
57
+ - Missing dependencies
58
+ - Database config problems
59
+ - Package publishing gaps
60
+
61
+ Returns validation report for logging.
62
+ """
63
+ global_config = ctx.get('global_config', {})
64
+ all_services = ctx.get('all_services', [])
65
+
66
+ # Only run validations if Generators module loaded successfully
67
+ if not Generators:
68
+ return {"status": "SKIPPED", "reason": "Generators module not loaded"}
69
+
70
+ # Run all validations
71
+ report = Generators.run_pre_flight_validations(
72
+ resource_path,
73
+ manifest,
74
+ all_services,
75
+ global_config,
76
+ )
77
+
78
+ # Log summary to Tilt UI
79
+ if report.get("overall_status") == "FAILED":
80
+ print("⚠️ {}: Validation failures detected - check " + OUTPUT.AUTOGENERATED_FOLDER + "/validation-*.json".format(resource_name))
81
+ elif report.get("summary", {}).get("warnings", 0) > 0:
82
+ print("⚡ {}: Validations passed with warnings".format(resource_name))
83
+ else:
84
+ print("✅ {}: All validations passed".format(resource_name))
85
+
86
+ return report
87
+
88
+
89
+ def _escape_for_double_quoted_shell(value):
90
+ escaped = str(value)
91
+ escaped = escaped.replace("\\", "\\\\")
92
+ escaped = escaped.replace('"', '\\"')
93
+ escaped = escaped.replace("$", "\\$")
94
+ escaped = escaped.replace("`", "\\`")
95
+ return escaped
96
+
97
+
98
+ def _get_file_emoji(filename):
99
+ """Return an emoji based on the file type."""
100
+ if 'Dockerfile' in filename:
101
+ return '🐳'
102
+ elif filename.endswith('.ts'):
103
+ return '📜'
104
+ elif filename.endswith('.json'):
105
+ return '📋'
106
+ elif filename.endswith('.toml'):
107
+ return '⚙️'
108
+ elif filename.endswith('.conf'):
109
+ return '🔧'
110
+ elif filename.endswith('.env') or '.env.' in filename:
111
+ return '🔐'
112
+ elif '.npmrc' in filename:
113
+ return '📦'
114
+ elif '.dockerignore' in filename:
115
+ return '🚫'
116
+ elif filename.endswith('.md'):
117
+ return '🤖'
118
+ else:
119
+ return '📄'
120
+
121
+
122
+ def _build_generated_file_list(resource_config, resource_path, manifest):
123
+ """
124
+ Build the expected generated file list for logging in config-gen resources.
125
+ Mirrors _generate_all_configs_for_resource() behavior.
126
+ """
127
+ files = []
128
+ app_type = manifest.get('appType', 'backend')
129
+ is_frontend = resource_config.get('frontend', False) or app_type == 'frontend'
130
+
131
+ # Vite
132
+ if is_frontend:
133
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['vite_frontend'])
134
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['vite_build'])
135
+ elif app_type == 'backend':
136
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['vite_backend'])
137
+ elif app_type == 'sdk':
138
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['vite_sdk'])
139
+
140
+ # Nginx (frontends)
141
+ if is_frontend:
142
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['nginx'])
143
+
144
+ # Env
145
+ if is_frontend:
146
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['env_frontend'])
147
+ else:
148
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['env_backend'])
149
+
150
+ # Dockerfile + dockerignore
151
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['dockerfile_app'])
152
+ dockerfile_rel_path = resource_config.get('dockerfile', '')
153
+ if dockerfile_rel_path:
154
+ dockerfile_path = resource_path.rsplit('/', 1)[0] + "/" + dockerfile_rel_path
155
+ files.append(dockerfile_path + '.dockerignore')
156
+
157
+ # TS config(s)
158
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['tsconfig_docker'])
159
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['tsconfig_local'])
160
+
161
+ # Prisma config (non-frontends)
162
+ if not is_frontend:
163
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['prisma'])
164
+
165
+ # Playwright config (optional)
166
+ if manifest.get('playwright', False):
167
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['playwright'])
168
+
169
+ # Package configs
170
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + PACKAGE_CONFIG_FILENAMES['npmrc_docker_autogenerated'])
171
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + PACKAGE_CONFIG_FILENAMES['bunfig_docker_autogenerated'])
172
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + PACKAGE_CONFIG_FILENAMES['npmrc'])
173
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + PACKAGE_CONFIG_FILENAMES['bunfig'])
174
+
175
+ # AGENTS.md (AI agent guard rail)
176
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['agents_md'])
177
+
178
+ return files
179
+
180
+
181
+ def _build_config_log_cmd(resource_name, generated_files, config_gen_resource_name):
182
+ """Build a tree-structured log command with emojis for generated files.
183
+
184
+ Includes auto-removal of the config-gen resource after logging since
185
+ it's only needed for initial generation and watching for changes.
186
+ """
187
+ cmd_parts = [
188
+ 'echo ""',
189
+ 'echo "╔══════════════════════════════════════════════════════════════════════╗"',
190
+ 'echo "║ ⚡ ' + _escape_for_double_quoted_shell(resource_name) + ' configs generated by Starlark"',
191
+ 'echo "╚══════════════════════════════════════════════════════════════════════╝"',
192
+ 'echo ""',
193
+ ]
194
+
195
+ # Group files by directory
196
+ dirs = {}
197
+ for file_path in generated_files:
198
+ parts = file_path.rsplit('/', 1)
199
+ if len(parts) == 2:
200
+ dir_path, filename = parts
201
+ else:
202
+ dir_path = '.'
203
+ filename = file_path
204
+ if dir_path not in dirs:
205
+ dirs[dir_path] = []
206
+ dirs[dir_path].append(filename)
207
+
208
+ # Sort directories for consistent output
209
+ sorted_dirs = sorted(dirs.keys())
210
+ total_files = len(generated_files)
211
+
212
+ cmd_parts.append('echo "📦 Generated ' + str(total_files) + ' files:"')
213
+ cmd_parts.append('echo ""')
214
+
215
+ for dir_idx, dir_path in enumerate(sorted_dirs):
216
+ is_last_dir = (dir_idx == len(sorted_dirs) - 1)
217
+ dir_prefix = '└── ' if is_last_dir else '├── '
218
+
219
+ cmd_parts.append('echo "' + dir_prefix + '📁 ' + _escape_for_double_quoted_shell(dir_path) + '/"')
220
+
221
+ files_in_dir = sorted(dirs[dir_path])
222
+ for file_idx, filename in enumerate(files_in_dir):
223
+ is_last_file = (file_idx == len(files_in_dir) - 1)
224
+ file_prefix = ' └── ' if is_last_file else ' ├── '
225
+ if not is_last_dir:
226
+ file_prefix = '│ └── ' if is_last_file else '│ ├── '
227
+
228
+ emoji = _get_file_emoji(filename)
229
+ cmd_parts.append('echo "' + file_prefix + emoji + ' ' + _escape_for_double_quoted_shell(filename) + '"')
230
+
231
+ cmd_parts.append('echo ""')
232
+ cmd_parts.append('echo "✅ All configs ready!"')
233
+ cmd_parts.append('echo ""')
234
+
235
+ # Auto-remove this config-gen resource after logging
236
+ # This prevents clutter in the Tilt UI since config-gen is only needed for initial generation
237
+ cmd_parts.append('echo "🧹 Auto-removing config-gen resource..."')
238
+ cmd_parts.append('tilt delete uiresource ' + config_gen_resource_name + ' 2>/dev/null || true')
239
+ cmd_parts.append('echo "✅ Config-gen resource auto-removed"')
240
+ cmd_parts.append('echo ""')
241
+
242
+ return " && ".join(cmd_parts)
243
+
244
+
245
+ def _generate_all_configs_for_resource(
246
+ resource_name,
247
+ resource_config,
248
+ resource_path,
249
+ manifest,
250
+ backend_manifest,
251
+ ctx
252
+ ):
253
+ """
254
+ Generate ALL configuration files for a single resource.
255
+
256
+ This function is the core of the manifest-driven architecture.
257
+ It generates 10+ config files for one service based on its manifest.
258
+
259
+ Args:
260
+ resource_name: Parent service name (e.g., 'my-service')
261
+ resource_config: Resource dict from registry
262
+ resource_path: Full path to resource (e.g., 'services/product/users/user-backend')
263
+ manifest: Loaded manifest for this resource
264
+ backend_manifest: Backend manifest (for frontends only)
265
+ ctx: Tilt context with write_file, should_enable, etc.
266
+ """
267
+ write_file = ctx['write_file']
268
+ should_enable = ctx['should_enable']
269
+ global_config = ctx['global_config']
270
+
271
+ # WORKAROUND: Prevent infinite recursion by checking recursion depth
272
+ # Use a unique key per resource to track depth, avoiding interference between different resources
273
+ recursion_key = '_recursion_depth_' + resource_name
274
+ recursion_depth = ctx.get(recursion_key, 0)
275
+ if recursion_depth > 5:
276
+ print("⚠️ Recursion depth exceeded for {}, skipping config generation".format(resource_name))
277
+ return
278
+ # Create a new context copy to avoid mutating shared state for other resources
279
+ ctx = dict(ctx)
280
+ ctx[recursion_key] = recursion_depth + 1
281
+
282
+ # Add resource_path to manifest so generators can access it
283
+ manifest['_resource_path'] = resource_path
284
+
285
+ # ==========================================================================
286
+ # NEW: PRE-FLIGHT VALIDATIONS (v2.0)
287
+ # Run validations BEFORE generating configs to catch issues early
288
+ # ==========================================================================
289
+ validation_report = _run_pre_flight_validations(
290
+ resource_name,
291
+ resource_path,
292
+ manifest,
293
+ ctx
294
+ )
295
+
296
+ # Store validation report for later reference
297
+ write_file(
298
+ resource_path + "/" + OUTPUT.AUTOGENERATED_FOLDER + "/validation-report.autogenerated.json",
299
+ Utils.encode_json(validation_report)
300
+ )
301
+
302
+ app_type = manifest.get('appType', 'backend')
303
+ is_frontend = resource_config.get('frontend', False) or app_type == 'frontend'
304
+
305
+ # ==========================================================================
306
+ # 1. VITE CONFIG
307
+ # ==========================================================================
308
+ if is_frontend:
309
+ if backend_manifest:
310
+ Vite.for_manifest(manifest, backend_manifest, write_file)
311
+ else:
312
+ Vite.for_manifest(manifest, None, write_file)
313
+ elif app_type == 'backend':
314
+ Vite.for_manifest(manifest, None, write_file)
315
+ elif app_type == 'sdk':
316
+ Vite.for_manifest(manifest, None, write_file)
317
+
318
+ # ==========================================================================
319
+ # 2. NGINX CONFIG (for frontends)
320
+ # ==========================================================================
321
+ if is_frontend:
322
+ # Generate referer map using ApiGateway and pass to Nginx
323
+ referer_map_block = ApiGateway.build_referer_map_block(manifest)
324
+ Nginx.frontend(manifest, write_file, referer_map_block)
325
+
326
+ # ==========================================================================
327
+ # 3. ENV FILE
328
+ # ==========================================================================
329
+ _generate_env_file(manifest, backend_manifest, write_file)
330
+
331
+ # ==========================================================================
332
+ # 3.5 FRONTEND API CLIENT FILES (for frontends only)
333
+ # ==========================================================================
334
+ if is_frontend:
335
+ FrontendGenerators.generate_all(manifest, write_file)
336
+
337
+ # ==========================================================================
338
+ # 4. PLAYWRIGHT CONFIG (if enabled)
339
+ # ==========================================================================
340
+ if manifest.get('playwright', False):
341
+ resource_type = 'frontend' if is_frontend else 'backend'
342
+ TSConfig.playwright(resource_path, resource_type, write_file)
343
+
344
+ # ==========================================================================
345
+ # 5. DOCKERFILE
346
+ # ==========================================================================
347
+ if is_frontend:
348
+ target_path = resource_config.get('target_path', '/usr/share/nginx/html')
349
+ use_nginx = 'nginx' in target_path or target_path == '/usr/share/nginx/html'
350
+ port = resource_config.get('port', manifest.get('port', BASE_PORT_FRONTEND))
351
+ dockerfile_content = Docker.frontend(
352
+ res_path=resource_path,
353
+ resource_name=resource_config['name'],
354
+ port=port,
355
+ target_path=target_path,
356
+ use_nginx=use_nginx,
357
+ use_golden=should_enable('golden-image')
358
+ )
359
+ else:
360
+ dockerfile_content = Docker.backend(
361
+ res_path=resource_path,
362
+ resource_name=resource_name,
363
+ use_golden=should_enable('golden-image')
364
+ )
365
+
366
+ write_file(resource_path + "/" + OUTPUT.AUTOGENERATED_FOLDER + "/" + GENERATED_CONFIG_FILENAMES['dockerfile_app'], dockerfile_content)
367
+
368
+ # ==========================================================================
369
+ # 6. DOCKERIGNORE
370
+ # ==========================================================================
371
+ dockerfile_rel_path = resource_config.get('dockerfile', '')
372
+ if dockerfile_rel_path:
373
+ resource_type = 'frontend' if is_frontend else ('migrator' if 'migrator' in resource_config['name'] else 'backend')
374
+ dockerignore_content = Docker.generate_dockerignore(resource_path, resource_type)
375
+ dockerfile_path = resource_path.rsplit('/', 1)[0] + "/" + dockerfile_rel_path
376
+ dockerignore_path = dockerfile_path + ".dockerignore"
377
+ write_file(dockerignore_path, dockerignore_content)
378
+
379
+ # ==========================================================================
380
+ # 7. TYPESCRIPT CONFIGS
381
+ # ==========================================================================
382
+ internal_deps_map = Libs.get_deps_mapping(ctx, resource_path)
383
+
384
+ if is_frontend:
385
+ # CRITICAL FIX (v2.0): For Docker builds, we MUST NOT pass internal_deps
386
+ # This prevents "Module not found" errors when path aliases point to
387
+ # source files outside the Docker context.
388
+ #
389
+ # Local builds: Use path aliases to source for faster development
390
+ # Docker builds: Resolve from node_modules (packages must be published)
391
+
392
+ # Docker tsconfig: NO path aliases to internal deps
393
+ TSConfig.frontend(resource_path, write_file, internal_deps=None, is_docker=True)
394
+
395
+ # Local tsconfig: CAN use path aliases to internal deps
396
+ TSConfig.frontend(resource_path, write_file, internal_deps=internal_deps_map, is_docker=False)
397
+ else:
398
+ prisma_path = resource_config.get('prisma_client_path', './node_modules/.prisma/client')
399
+ TSConfig.backend(resource_path, prisma_path, write_file, internal_deps=internal_deps_map, is_docker=True)
400
+ TSConfig.backend(resource_path, prisma_path, write_file, internal_deps=internal_deps_map, is_docker=False)
401
+
402
+ # Prisma config
403
+ db_name = _get_db_name_for_resource(resource_name, resource_config['name'])
404
+ resource_display_name = _get_resource_display_name(resource_name, resource_config['name'])
405
+ TSConfig.prisma(resource_path=resource_path, resource_name=resource_display_name, db_name=db_name, write_fn=write_file)
406
+
407
+ # ==========================================================================
408
+ # 8. PACKAGE CONFIGS (npmrc, bunfig)
409
+ # ==========================================================================
410
+ PackageConfig.npmrc(resource_path=resource_path, registry_url=global_config['verdaccio_url_docker'], is_docker=True, write_fn=write_file)
411
+ PackageConfig.bunfig(resource_path=resource_path, registry_url=global_config['verdaccio_url_docker'], is_docker=True, write_fn=write_file)
412
+ PackageConfig.npmrc(resource_path=resource_path, registry_url=global_config['verdaccio_url_local'], is_docker=False, write_fn=write_file)
413
+ PackageConfig.bunfig(resource_path=resource_path, registry_url=global_config['verdaccio_url_local'], is_docker=False, write_fn=write_file)
414
+
415
+ # ==========================================================================
416
+ # 9. YAML MANIFEST (for verification)
417
+ # ==========================================================================
418
+ _generate_yaml_manifest(resource_path, manifest, write_file)
419
+
420
+ # ==========================================================================
421
+ # 10. AGENTS.MD (AI agent guard rail for .autogenerated/ folder)
422
+ # ==========================================================================
423
+ # Generates AGENTS.md in .autogenerated/ to warn AI agents not to modify
424
+ # machine-generated files. Explains single source of truth principle.
425
+ Generators.AgentsMd.generate(manifest, write_file)
426
+
427
+
428
+ def _generate_yaml_manifest(resource_path, manifest, write_file):
429
+ """Generate YAML version of manifest for verification (idempotent)."""
430
+ yaml_path = resource_path + "/" + MANIFEST_FILENAME_YAML
431
+
432
+ # Check if YAML already exists and is up-to-date by comparing with JSON
433
+ json_path = resource_path + "/" + MANIFEST_FILENAME
434
+ json_mtime = local("stat -f %m " + json_path + " 2>/dev/null || stat -c %Y " + json_path + " 2>/dev/null || echo 0", quiet=True)
435
+ yaml_mtime = local("stat -f %m " + yaml_path + " 2>/dev/null || stat -c %Y " + yaml_path + " 2>/dev/null || echo 0", quiet=True)
436
+
437
+ if str(json_mtime).strip() <= str(yaml_mtime).strip() and yaml_mtime != "0":
438
+ # YAML is up-to-date, skip generation
439
+ return
440
+
441
+ # Generate YAML using the json_to_yaml.py converter
442
+ # NOTE: Path relative to TDK CLI installation root (where this file is located)
443
+ # json_to_yaml.py is in engine/topologies/tilt/manifest/ relative to tdk-cli root
444
+ converter_path = "engine/topologies/tilt/manifest/json_to_yaml.py"
445
+ yaml_content = str(local("python3 " + converter_path + " " + json_path, quiet=True)).strip()
446
+ write_file(yaml_path, yaml_content)
447
+
448
+
449
+ # =============================================================================
450
+ # 📦 EXPORTED STRUCT
451
+ # =============================================================================
452
+
453
+ def create_manifest_config_resource(
454
+ resource_name,
455
+ resource_config,
456
+ resource_path,
457
+ manifest,
458
+ backend_manifest,
459
+ ctx
460
+ ):
461
+ """
462
+ Create a Tilt local_resource for manifest-driven config generation.
463
+
464
+ This creates a visible Tilt resource (e.g., "my-service-config-gen")
465
+ that watches the manifest and package.json, regenerating all configs
466
+ when they change.
467
+
468
+ Args:
469
+ resource_name: Parent service name
470
+ resource_config: Resource dict from registry
471
+ resource_path: Base service path
472
+ manifest: Loaded manifest
473
+ backend_manifest: Backend manifest (for frontends)
474
+ ctx: Tilt context
475
+
476
+ Returns:
477
+ Resource name (e.g., "my-service-config-gen")
478
+ """
479
+ resource_name = resource_config['name']
480
+ # resource_path is now correctly passed from apply_manifest_orchestration.star
481
+ # which uses resource.get('_resource_path', ...) to get the actual path
482
+ resource_path = resource_path
483
+ config_gen_resource_name = resource_name + '-config-gen'
484
+ auto_init_config_gen = ctx.get('auto_init_config_gen', True)
485
+
486
+ # Dependencies to watch
487
+ manifest_path = resource_path + "/" + MANIFEST_FILENAME
488
+ package_json_path = resource_path + "/package.json"
489
+
490
+ # Generate configs immediately (synchronously)
491
+ _generate_all_configs_for_resource(
492
+ resource_name,
493
+ resource_config,
494
+ resource_path,
495
+ manifest,
496
+ backend_manifest,
497
+ ctx
498
+ )
499
+ generated_files = _build_generated_file_list(resource_config, resource_path, manifest)
500
+ log_cmd = _build_config_log_cmd(resource_name, generated_files, config_gen_resource_name)
501
+
502
+ # Create a Tilt resource to watch for changes
503
+ # Note: Generation happens in Starlark before this resource runs.
504
+ # We log the expected generated file set for quick visibility in Tilt logs.
505
+ # The real value is in the deps watching and labels.
506
+ # The command auto-removes this resource after completion via tilt delete.
507
+ local_resource(
508
+ name=config_gen_resource_name,
509
+ cmd=log_cmd,
510
+ deps=[manifest_path, package_json_path],
511
+ labels=['config-gen', resource_name],
512
+ allow_parallel=True,
513
+ auto_init=auto_init_config_gen
514
+ )
515
+
516
+ return config_gen_resource_name
517
+
518
+
519
+ ManifestResource = struct(
520
+ generate_all_configs = _generate_all_configs_for_resource,
521
+ create_config_resource = create_manifest_config_resource,
522
+ )
523
+
524
+ VALIDATION = {}
@@ -0,0 +1,33 @@
1
+ # =============================================================================
2
+ # 🧩 TILT SDK - ORCHESTRATOR HELPERS
3
+ # =============================================================================
4
+ # Path: .tilt/provisioner/orchestrator/helpers.star
5
+ # Purpose: Shared helper functions for orchestrator
6
+ # =============================================================================
7
+
8
+ def _get_db_name_for_resource(resource_name, res_name):
9
+ """Get database name from manifest databaseName field dynamically."""
10
+ # Database names now come from service.json databaseName field
11
+ # No hardcoded mappings - all from service.json
12
+ # Default: {project}_{resource_name}
13
+ return resource_name
14
+
15
+
16
+ def _get_resource_display_name(resource_name, res_name):
17
+ """Get human-readable service name for display."""
18
+ display_name = resource_name.replace('-', ' ').title()
19
+
20
+ # Dynamic service type detection from resource name
21
+ # No hardcoded service names - pattern-based detection
22
+ if '-planner' in res_name or 'planner' in res_name:
23
+ return display_name + ' Planner'
24
+ elif '-management' in res_name:
25
+ return display_name + ' Management'
26
+
27
+ return display_name
28
+
29
+
30
+ OrchestratorHelpers = struct(
31
+ get_db_name = _get_db_name_for_resource,
32
+ get_display_name = _get_resource_display_name,
33
+ )
@@ -0,0 +1,54 @@
1
+ # =============================================================================
2
+ # 🎯 TILT SDK - SERVICE ORCHESTRATOR
3
+ # =============================================================================
4
+ # Path: .tilt/provisioner/orchestrator.star
5
+ # Purpose: Application service lifecycle management
6
+ # =============================================================================
7
+ #
8
+ # This module handles the complete lifecycle of loading and configuring
9
+ # application services. It orchestrates:
10
+ # - Manifest loading and validation
11
+ # - Config generation (Vite, TSConfig, Docker)
12
+ # - Resource registration and dependency tracking
13
+ #
14
+ # Key Functions:
15
+ # Orchestrator.apply_service(resource_config, ctx) - Load and configure a service
16
+ # Orchestrator.apply_all(services, ctx) - Load all services
17
+ # =============================================================================
18
+
19
+ load('./orchestrator/apply.star', 'apply_app_service')
20
+ load('./orchestrator/helpers.star', 'OrchestratorHelpers')
21
+ load('./orchestrator/generators/env.star', 'EnvGenerators')
22
+
23
+ _get_db_name_for_resource = OrchestratorHelpers.get_db_name
24
+ _get_resource_display_name = OrchestratorHelpers.get_display_name
25
+ _generate_env_file = EnvGenerators.generate_env_file
26
+
27
+
28
+ def apply_all_services(services, ctx):
29
+ """Apply all services in the list."""
30
+ for resource_config in services:
31
+ # BUG FIX: Skip services without required 'name' field
32
+ if type(resource_config) != "dict":
33
+ print("⚠️ Skipping invalid service config (not a dict): " + str(resource_config))
34
+ continue
35
+ if "name" not in resource_config:
36
+ print("⚠️ Skipping service config without 'name' field: " + str(resource_config))
37
+ continue
38
+ apply_app_service(resource_config, ctx)
39
+
40
+
41
+ # =============================================================================
42
+ # 📦 ORCHESTRATOR STRUCT (Public API)
43
+ # =============================================================================
44
+
45
+ Orchestrator = struct(
46
+ # Main functions
47
+ apply_service = apply_app_service,
48
+ apply_all = apply_all_services,
49
+
50
+ # Helpers (exposed for testing)
51
+ generate_env_file = _generate_env_file,
52
+ get_db_name = _get_db_name_for_resource,
53
+ get_display_name = _get_resource_display_name,
54
+ )
@@ -0,0 +1,3 @@
1
+ load("./infra-loader.star", _Infra = "Infra")
2
+
3
+ Infra = _Infra
@@ -0,0 +1,3 @@
1
+ load("./watchers.star", _Watchers = "Watchers")
2
+
3
+ Watchers = _Watchers