@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,382 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Service Snapshot Utility
4
+
5
+ Manages the snapshot file used for incremental service discovery.
6
+ The snapshot tracks which services have been discovered to detect
7
+ new services added while Tilt is running.
8
+ """
9
+
10
+ import json
11
+ import os
12
+ import sys
13
+ import hashlib
14
+ import logging
15
+ from pathlib import Path
16
+ from typing import List, Dict, Set, Tuple, Optional
17
+ from datetime import datetime, timezone
18
+
19
+ # Configure logging
20
+ logging.basicConfig(level=logging.WARNING, format='%(levelname)s: %(message)s')
21
+ logger = logging.getLogger(__name__)
22
+
23
+ SNAPSHOT_FILE = Path(".tdk/.tdk-out/snapshots/resource-snapshot.json")
24
+
25
+
26
+ def compute_resource_hash(resource_path: str) -> Optional[str]:
27
+ """
28
+ Compute MD5 hash of a service.json file for efficient change detection.
29
+
30
+ Args:
31
+ resource_path: Path to service.json file
32
+
33
+ Returns:
34
+ MD5 hash string or None if file cannot be read
35
+ """
36
+ try:
37
+ with open(resource_path, 'rb') as f:
38
+ content = f.read()
39
+ return hashlib.md5(content).hexdigest()[:16] # First 16 chars sufficient
40
+ except (IOError, OSError) as e:
41
+ logger.warning(f"Could not compute hash for {resource_path}: {e}")
42
+ return None
43
+
44
+
45
+ def compute_services_hash(services: List[str]) -> str:
46
+ """
47
+ Compute aggregate hash of all services for quick comparison.
48
+
49
+ Args:
50
+ services: List of service paths
51
+
52
+ Returns:
53
+ Aggregate hash string
54
+ """
55
+ hasher = hashlib.md5()
56
+ for svc in sorted(services):
57
+ hasher.update(svc.encode())
58
+ svc_hash = compute_resource_hash(svc)
59
+ if svc_hash:
60
+ hasher.update(svc_hash.encode())
61
+ return hasher.hexdigest()[:16]
62
+
63
+
64
+ def save_snapshot(services: List[str], include_hashes: bool = True) -> None:
65
+ """
66
+ Save current service paths to snapshot file.
67
+
68
+ Args:
69
+ services: List of service.json file paths
70
+ include_hashes: Whether to include content hashes for efficient comparison
71
+ """
72
+ snapshot = {
73
+ "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
74
+ "services": sorted(services),
75
+ "count": len(services)
76
+ }
77
+
78
+ # Include aggregate hash for quick comparison
79
+ if services:
80
+ snapshot["hash"] = compute_services_hash(services)
81
+
82
+ # Include individual service hashes for change detection
83
+ if include_hashes and services:
84
+ resource_hashes = {}
85
+ for svc in services:
86
+ svc_hash = compute_resource_hash(svc)
87
+ if svc_hash:
88
+ resource_hashes[svc] = svc_hash
89
+ snapshot["resource_hashes"] = resource_hashes
90
+
91
+ # Ensure directory exists
92
+ SNAPSHOT_FILE.parent.mkdir(parents=True, exist_ok=True)
93
+
94
+ with open(SNAPSHOT_FILE, 'w') as f:
95
+ json.dump(snapshot, f, indent=2)
96
+
97
+
98
+ def load_snapshot() -> Dict:
99
+ """
100
+ Load previous snapshot from file.
101
+
102
+ Returns:
103
+ Snapshot dict with 'timestamp', 'services', 'count'.
104
+ Returns empty snapshot if file doesn't exist.
105
+ """
106
+ if not SNAPSHOT_FILE.exists():
107
+ return {
108
+ "timestamp": None,
109
+ "services": [],
110
+ "count": 0
111
+ }
112
+
113
+ try:
114
+ with open(SNAPSHOT_FILE, 'r') as f:
115
+ return json.load(f)
116
+ except json.JSONDecodeError as e:
117
+ logger.warning(f"Corrupted snapshot file {SNAPSHOT_FILE}: {e}")
118
+ return {
119
+ "timestamp": None,
120
+ "services": [],
121
+ "count": 0
122
+ }
123
+ except IOError as e:
124
+ logger.warning(f"Could not read snapshot file {SNAPSHOT_FILE}: {e}")
125
+ return {
126
+ "timestamp": None,
127
+ "services": [],
128
+ "count": 0
129
+ }
130
+
131
+
132
+ def diff_snapshots(old: Dict, new: List[str], use_hashes: bool = True) -> Tuple[Set[str], Set[str], Set[str]]:
133
+ """
134
+ Compare old snapshot with current services to find changes.
135
+
136
+ Uses hash-based comparison for efficient change detection when enabled.
137
+ Returns services that changed content even if path stayed the same.
138
+
139
+ Args:
140
+ old: Previous snapshot dict
141
+ new: Current list of service paths
142
+ use_hashes: Whether to use hash-based comparison for efficiency
143
+
144
+ Returns:
145
+ Tuple of (added_services, removed_services, modified_services) as sets
146
+ """
147
+ old_services = set(old.get("services", []))
148
+ new_services = set(new)
149
+
150
+ added = new_services - old_services
151
+ removed = old_services - new_services
152
+ modified = set()
153
+
154
+ # Quick hash-based check - if aggregate hash matches, no changes
155
+ if use_hashes and "hash" in old and new:
156
+ current_hash = compute_services_hash(new)
157
+ if old["hash"] == current_hash:
158
+ return set(), set(), set() # No changes at all
159
+
160
+ # Check for modified services (same path, different content)
161
+ if use_hashes and "resource_hashes" in old:
162
+ old_hashes = old["resource_hashes"]
163
+ for svc in new_services & old_services: # Intersection - services in both
164
+ old_hash = old_hashes.get(svc)
165
+ new_hash = compute_resource_hash(svc)
166
+ if old_hash and new_hash and old_hash != new_hash:
167
+ modified.add(svc)
168
+
169
+ return added, removed, modified
170
+
171
+
172
+ def get_current_services(scan_root: str = "services/product") -> List[str]:
173
+ """
174
+ Scan filesystem for current service.json files.
175
+
176
+ Args:
177
+ scan_root: Root directory to scan
178
+
179
+ Returns:
180
+ List of service.json file paths
181
+ """
182
+ import subprocess
183
+
184
+ try:
185
+ result = subprocess.run(
186
+ ["find", scan_root, "-type", "f", "-name", "service.json"],
187
+ capture_output=True,
188
+ text=True,
189
+ timeout=5
190
+ )
191
+
192
+ if result.returncode == 0:
193
+ services = [line.strip() for line in result.stdout.split('\n') if line.strip()]
194
+ return sorted(services)
195
+ except subprocess.TimeoutExpired:
196
+ logger.warning(f"Service scan timed out for {scan_root}")
197
+ except subprocess.SubprocessError as e:
198
+ logger.warning(f"Service scan failed for {scan_root}: {e}")
199
+
200
+ return []
201
+
202
+
203
+ def main():
204
+ """CLI interface for snapshot operations."""
205
+ if len(sys.argv) < 2:
206
+ print("Usage: resource_snapshot.py <command> [args]")
207
+ print("Commands:")
208
+ print(" save - Save current service snapshot")
209
+ print(" load - Load and display snapshot")
210
+ print(" diff - Compare current state with snapshot (uses hash-based comparison)")
211
+ print(" scan - Scan for services without saving")
212
+ print(" profile - Profile performance and resource usage (Task 10.5)")
213
+ print(" watch [interval] - Continuous monitoring with hash-based change detection")
214
+ print()
215
+ print("Examples:")
216
+ print(" python3 resource_snapshot.py scan")
217
+ print(" python3 resource_snapshot.py diff")
218
+ print(" python3 resource_snapshot.py profile")
219
+ print(" python3 resource_snapshot.py watch 5")
220
+ return
221
+
222
+ command = sys.argv[1]
223
+
224
+ if command == "save":
225
+ services = get_current_services()
226
+ save_snapshot(services)
227
+ print(f"✅ Snapshot saved: {len(services)} services")
228
+
229
+ elif command == "load":
230
+ snapshot = load_snapshot()
231
+ print(json.dumps(snapshot, indent=2))
232
+
233
+ elif command == "diff":
234
+ import time
235
+ start = time.time()
236
+
237
+ old = load_snapshot()
238
+ new = get_current_services()
239
+ added, removed, modified = diff_snapshots(old, new, use_hashes=True)
240
+
241
+ elapsed = (time.time() - start) * 1000 # Convert to ms
242
+
243
+ print(f"📊 Snapshot diff (computed in {elapsed:.1f}ms):")
244
+ print(f" Previous: {old.get('count', 0)} services")
245
+ print(f" Current: {len(new)} services")
246
+ print(f" Added: {len(added)}")
247
+ print(f" Removed: {len(removed)}")
248
+ print(f" Modified: {len(modified)}")
249
+
250
+ if added:
251
+ print("\n New services:")
252
+ for svc in sorted(added):
253
+ print(f" + {svc}")
254
+
255
+ if removed:
256
+ print("\n Removed services:")
257
+ for svc in sorted(removed):
258
+ print(f" - {svc}")
259
+
260
+ if modified:
261
+ print("\n Modified services (content changed):")
262
+ for svc in sorted(modified):
263
+ print(f" ~ {svc}")
264
+
265
+ print(f"\n ⚡ Hash-based comparison: enabled")
266
+ if old.get('hash'):
267
+ print(f" 🔐 Aggregate hash: {old['hash'][:8]}...")
268
+
269
+ elif command == "scan":
270
+ services = get_current_services()
271
+ print(f"🔍 Found {len(services)} services:")
272
+ for svc in services:
273
+ print(f" - {svc}")
274
+
275
+ elif command == "profile":
276
+ """Profile resource usage and performance metrics."""
277
+ import time
278
+ import resource
279
+
280
+ print("🔬 Profiling snapshot operations...")
281
+ print()
282
+
283
+ # Memory baseline
284
+ baseline_mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024 # KB to MB
285
+
286
+ # Time the scan operation
287
+ start = time.time()
288
+ services = get_current_services()
289
+ scan_time = (time.time() - start) * 1000
290
+
291
+ # Time hash computation
292
+ start = time.time()
293
+ if services:
294
+ svc_hash = compute_services_hash(services)
295
+ hash_time = (time.time() - start) * 1000 if services else 0
296
+
297
+ # Time snapshot save
298
+ start = time.time()
299
+ save_snapshot(services, include_hashes=True)
300
+ save_time = (time.time() - start) * 1000
301
+
302
+ # Memory after operations
303
+ final_mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024
304
+ memory_used = final_mem - baseline_mem
305
+
306
+ print("📊 Performance Profile:")
307
+ print(f" Services scanned: {len(services)}")
308
+ print(f" Scan time: {scan_time:.2f}ms")
309
+ print(f" Hash computation: {hash_time:.2f}ms")
310
+ print(f" Snapshot save: {save_time:.2f}ms")
311
+ print(f" Total time: {scan_time + hash_time + save_time:.2f}ms")
312
+ print(f" Memory used: {memory_used:.2f} MB")
313
+ print()
314
+
315
+ # Performance targets
316
+ print("🎯 Performance Targets:")
317
+ print(f" Scan < 500ms: {'✅ PASS' if scan_time < 500 else '⚠️ FAIL'}")
318
+ print(f" Memory < 10MB: {'✅ PASS' if memory_used < 10 else '⚠️ FAIL'}")
319
+ print()
320
+
321
+ if services:
322
+ print(f"🔐 Aggregate hash: {svc_hash[:16]}...")
323
+
324
+ elif command == "watch":
325
+ """Continuous monitoring mode for daemon operation."""
326
+ import time
327
+
328
+ scan_interval = int(sys.argv[2]) if len(sys.argv) > 2 else 5
329
+
330
+ print(f"👁️ Starting continuous monitoring (interval: {scan_interval}s)")
331
+ print(" Press Ctrl+C to stop")
332
+ print()
333
+
334
+ # Initialize snapshot if needed
335
+ if not SNAPSHOT_FILE.exists():
336
+ services = get_current_services()
337
+ save_snapshot(services)
338
+ print(f"✅ Initial snapshot: {len(services)} services")
339
+
340
+ try:
341
+ while True:
342
+ old = load_snapshot()
343
+ current = get_current_services()
344
+ added, removed, modified = diff_snapshots(old, current, use_hashes=True)
345
+
346
+ if added or removed or modified:
347
+ print(f"🔍 Changes detected at {datetime.now(timezone.utc).strftime('%H:%M:%S')}:")
348
+ if added:
349
+ print(f" + {len(added)} new service(s)")
350
+ for svc in added:
351
+ print(f" + {svc}")
352
+ if removed:
353
+ print(f" - {len(removed)} removed service(s)")
354
+ for svc in removed:
355
+ print(f" - {svc}")
356
+ if modified:
357
+ print(f" ~ {len(modified)} modified service(s)")
358
+ for svc in modified:
359
+ print(f" ~ {svc}")
360
+
361
+ # Update snapshot
362
+ save_snapshot(current)
363
+ print(f"✅ Snapshot updated: {len(current)} services")
364
+
365
+ time.sleep(scan_interval)
366
+
367
+ except KeyboardInterrupt:
368
+ print("\n👋 Monitoring stopped")
369
+
370
+ else:
371
+ print(f"Unknown command: {command}")
372
+ print("\nAvailable commands:")
373
+ print(" save - Save current snapshot")
374
+ print(" load - Load and display snapshot")
375
+ print(" diff - Compare current state with snapshot")
376
+ print(" scan - Scan for services without saving")
377
+ print(" profile - Profile performance and resource usage")
378
+ print(" watch [N] - Continuous monitoring with N-second interval (default: 5)")
379
+
380
+
381
+ if __name__ == "__main__":
382
+ main()
@@ -0,0 +1,143 @@
1
+ # =============================================================================
2
+ # 📸 RESOURCE SNAPSHOT - Starlark Interface
3
+ # =============================================================================
4
+ # Provides Starlark functions for snapshot-based incremental resource discovery
5
+ # =============================================================================
6
+
7
+ # Python script path (relative to Tiltfile)
8
+ _SNAPSHOT_SCRIPT = ".tdk/.tdk-out/snapshots/resource_snapshot.py"
9
+
10
+ def _run_snapshot_command(cmd):
11
+ """Run a snapshot command via Python script."""
12
+ result = local(
13
+ "python3 " + _SNAPSHOT_SCRIPT + " " + cmd,
14
+ quiet=True,
15
+ echo_off=True
16
+ )
17
+ return str(result)
18
+
19
+ def save_snapshot(resources):
20
+ """
21
+ Save current resource list to snapshot file.
22
+
23
+ Args:
24
+ resources: List of service.json file paths
25
+ """
26
+ # Use Python to save snapshot directly
27
+ result = local(
28
+ "cd " + config.main_dir + " && python3 " + _SNAPSHOT_SCRIPT + " save",
29
+ quiet=True,
30
+ echo_off=True
31
+ )
32
+
33
+ return str(result)
34
+
35
+ def load_from_file():
36
+ """
37
+ Load previous snapshot from file.
38
+
39
+ Returns:
40
+ Dict with 'timestamp', 'resources', 'count'
41
+ """
42
+ result = _run_snapshot_command("load")
43
+
44
+ # Parse JSON result
45
+ if result and result.strip():
46
+ # In Starlark, we can't use try/except, so we just try to decode
47
+ parsed = decode_json(result)
48
+ if parsed:
49
+ return parsed
50
+
51
+ # Return empty snapshot on error
52
+ return {
53
+ "timestamp": None,
54
+ "services": [],
55
+ "count": 0
56
+ }
57
+
58
+ def diff_snapshots(old_snapshot, current_resources):
59
+ """
60
+ Compare old snapshot with current resources.
61
+
62
+ Args:
63
+ old_snapshot: Previous snapshot dict
64
+ current_resources: Current list of resource paths
65
+
66
+ Returns:
67
+ Struct with 'added' and 'removed' lists
68
+ """
69
+ old_set = {}
70
+ for resource in old_snapshot.get("resources", []):
71
+ old_set[resource] = True
72
+
73
+ current_set = {}
74
+ for resource in current_resources:
75
+ current_set[resource] = True
76
+
77
+ # Find added resources
78
+ added = []
79
+ for resource in current_resources:
80
+ if resource not in old_set:
81
+ added.append(resource)
82
+
83
+ # Find removed resources
84
+ removed = []
85
+ for resource in old_snapshot.get("resources", []):
86
+ if resource not in current_set:
87
+ removed.append(resource)
88
+
89
+ return struct(
90
+ added=added,
91
+ removed=removed,
92
+ added_count=len(added),
93
+ removed_count=len(removed)
94
+ )
95
+
96
+ def get_current_resources(scan_roots=["services/product"]):
97
+ """
98
+ Scan filesystem for current service.json files.
99
+
100
+ Args:
101
+ scan_roots: List of root directories to scan
102
+
103
+ Returns:
104
+ List of service.json file paths (relative to project root)
105
+ """
106
+ all_resources = []
107
+
108
+ for root in scan_roots:
109
+ # Use find command to locate service.json files
110
+ cmd = "find " + root + " -type f -name 'service.json' 2>/dev/null | sort"
111
+ result = local(cmd, quiet=True, echo_off=True)
112
+
113
+ if result:
114
+ lines = str(result).strip().split("\n")
115
+ for line in lines:
116
+ line = line.strip()
117
+ if line:
118
+ all_resources.append(line)
119
+
120
+ return all_resources
121
+
122
+ def snapshot_exists():
123
+ """Check if snapshot file exists."""
124
+ result = local(
125
+ "test -f .tdk/.tdk-out/snapshots/resource-snapshot.json && echo 'yes' || echo 'no'",
126
+ quiet=True,
127
+ echo_off=True
128
+ )
129
+ return str(result).strip() == "yes"
130
+
131
+ # Get the path to the resource snapshot file
132
+ def get_resource_snapshot_path():
133
+ """Return the path to the resource snapshot JSON file."""
134
+ return ".tdk/.tdk-out/snapshots/resource-snapshot.json"
135
+
136
+ # Export public API
137
+ ResourceSnapshot = struct(
138
+ save=save_snapshot,
139
+ load_from_file=load_from_file,
140
+ diff=diff_snapshots,
141
+ scan=get_current_resources,
142
+ exists=snapshot_exists,
143
+ )
@@ -0,0 +1 @@
1
+ MANIFEST_SCHEMA_PATH = ".tilt/schemas/manifest-schema.json"
@@ -0,0 +1,9 @@
1
+ appName: nats-connector
2
+ appType: backend
3
+ stack: platform
4
+ port: 4103
5
+ features:
6
+ - nats
7
+ dependsOn:
8
+ replicas: 1
9
+ runtime: node
@@ -0,0 +1,9 @@
1
+ appName: nats-http-bridge
2
+ appType: backend
3
+ stack: platform
4
+ port: 4102
5
+ features:
6
+ - nats
7
+ dependsOn:
8
+ replicas: 1
9
+ runtime: node
@@ -0,0 +1,26 @@
1
+ appName: orchestrator-glue-backend
2
+ appType: backend
3
+ stack: platform
4
+ port: 5001
5
+ databaseName: beauty_crm_orchestrator
6
+ features:
7
+ - nats
8
+ - prisma
9
+ healthCheck:
10
+ port: 5001
11
+ interval: 30
12
+ path: /health
13
+ infisicalSecretPath: /services/orchestrator-glue
14
+ dependsOn:
15
+ replicas: 1
16
+ resources:
17
+ limits:
18
+ cpu: 500m
19
+ memory: 512Mi
20
+ requests:
21
+ cpu: 100m
22
+ memory: 256Mi
23
+ runtime: bun
24
+ traefik:
25
+ host: orchestrator.backend.beauty.local
26
+ pathPrefix: /api/orchestrator
@@ -0,0 +1,9 @@
1
+ appName: synthetic-monitor
2
+ appType: backend
3
+ stack: platform
4
+ port: 4100
5
+ features:
6
+ - nats
7
+ dependsOn:
8
+ replicas: 1
9
+ runtime: bun