@tdk-landscape/tdk-cli-core 1.3.14 → 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 (277) hide show
  1. package/README.md +3 -3
  2. package/Tiltfile +144 -0
  3. package/discovery/AGENTS.md +362 -0
  4. package/discovery/TILT_DISCOVERY.star +424 -0
  5. package/discovery/Tiltfile +58 -0
  6. package/discovery/config.star +258 -0
  7. package/discovery/constants.star +127 -0
  8. package/discovery/deterministic.star +211 -0
  9. package/discovery/discovery_daemon.star +191 -0
  10. package/discovery/discovery_orchestrator.star +460 -0
  11. package/discovery/filtering.star +5 -0
  12. package/discovery/identity-management-backend/service.yaml +26 -0
  13. package/discovery/identity-management-frontend/service.yaml +29 -0
  14. package/discovery/identity-sdk/service.yaml +9 -0
  15. package/discovery/incremental_discovery.star +213 -0
  16. package/discovery/indexing.star +7 -0
  17. package/discovery/json_manifest_scanner.star +51 -0
  18. package/discovery/libraries.star +46 -0
  19. package/discovery/loading.star +23 -0
  20. package/discovery/manifest/constants.star +104 -0
  21. package/discovery/manifest/loading.star +386 -0
  22. package/discovery/manifest/normalize.star +281 -0
  23. package/discovery/manifest/validation.star +108 -0
  24. package/discovery/normalization.star +11 -0
  25. package/discovery/registry.star +544 -0
  26. package/discovery/resource_snapshot.py +382 -0
  27. package/discovery/resource_snapshot.star +143 -0
  28. package/discovery/schemas.star +1 -0
  29. package/discovery/services/platform/cdc/debezium/nats-connector/service.yaml +9 -0
  30. package/discovery/services/platform/cdc/nats-http-bridge/service.yaml +9 -0
  31. package/discovery/services/platform/orchestrator/orchestrator-glue-backend/service.yaml +26 -0
  32. package/discovery/services/platform/synthetic-monitor/service.yaml +9 -0
  33. package/discovery/test_resource_snapshot.py +251 -0
  34. package/discovery/validation.star +78 -0
  35. package/discovery/versioning.star +1 -0
  36. package/dist/commands/config.d.ts.map +1 -1
  37. package/dist/commands/config.js +11 -0
  38. package/dist/commands/config.js.map +1 -1
  39. package/dist/commands/doctor.d.ts.map +1 -1
  40. package/dist/commands/doctor.js +1 -2
  41. package/dist/commands/doctor.js.map +1 -1
  42. package/dist/commands/project.d.ts.map +1 -1
  43. package/dist/commands/project.js +36 -41
  44. package/dist/commands/project.js.map +1 -1
  45. package/dist/commands/resource.d.ts.map +1 -1
  46. package/dist/commands/resource.js +51 -55
  47. package/dist/commands/resource.js.map +1 -1
  48. package/dist/commands/stack.d.ts.map +1 -1
  49. package/dist/commands/stack.js +22 -24
  50. package/dist/commands/stack.js.map +1 -1
  51. package/dist/commands/ui.d.ts.map +1 -1
  52. package/dist/commands/ui.js +1 -2
  53. package/dist/commands/ui.js.map +1 -1
  54. package/dist/commands/up.d.ts.map +1 -1
  55. package/dist/commands/up.js +1 -2
  56. package/dist/commands/up.js.map +1 -1
  57. package/dist/commands/upgrade.d.ts +8 -0
  58. package/dist/commands/upgrade.d.ts.map +1 -1
  59. package/dist/commands/upgrade.js +62 -21
  60. package/dist/commands/upgrade.js.map +1 -1
  61. package/dist/generator/extension-fetch.d.ts +19 -0
  62. package/dist/generator/extension-fetch.d.ts.map +1 -1
  63. package/dist/generator/extension-fetch.js +71 -5
  64. package/dist/generator/extension-fetch.js.map +1 -1
  65. package/dist/generator/template-engine.d.ts +1 -0
  66. package/dist/generator/template-engine.d.ts.map +1 -1
  67. package/dist/generator/template-engine.js +50 -6
  68. package/dist/generator/template-engine.js.map +1 -1
  69. package/dist/utils/command-helpers.d.ts.map +1 -1
  70. package/dist/utils/command-helpers.js +7 -9
  71. package/dist/utils/command-helpers.js.map +1 -1
  72. package/dist/utils/constants.d.ts +1 -0
  73. package/dist/utils/constants.d.ts.map +1 -1
  74. package/dist/utils/constants.js +1 -0
  75. package/dist/utils/constants.js.map +1 -1
  76. package/dist/utils/errors.d.ts +2 -0
  77. package/dist/utils/errors.d.ts.map +1 -1
  78. package/dist/utils/errors.js +6 -1
  79. package/dist/utils/errors.js.map +1 -1
  80. package/dist/utils/paths.js +1 -1
  81. package/dist/utils/paths.js.map +1 -1
  82. package/dist/utils/project-features.d.ts.map +1 -1
  83. package/dist/utils/project-features.js +1 -1
  84. package/dist/utils/project-features.js.map +1 -1
  85. package/dist/utils/resource-features.d.ts.map +1 -1
  86. package/dist/utils/resource-features.js +2 -2
  87. package/dist/utils/resource-features.js.map +1 -1
  88. package/dist/utils/tar.js +4 -4
  89. package/dist/utils/tar.js.map +1 -1
  90. package/dist/utils/validation.d.ts +7 -0
  91. package/dist/utils/validation.d.ts.map +1 -1
  92. package/dist/utils/validation.js +9 -0
  93. package/dist/utils/validation.js.map +1 -1
  94. package/engine/AGENTS.md +705 -0
  95. package/engine/README.md +29 -0
  96. package/engine/REFACTORING_MANIFEST_SYSTEM.md +974 -0
  97. package/engine/Tiltfile +80 -0
  98. package/engine/assets/docker/golden-layers.Dockerfile +221 -0
  99. package/engine/assets/scripts/cleanup-completed-migrator-images.sh +37 -0
  100. package/engine/assets/scripts/cleanup-tilt-images.sh +120 -0
  101. package/engine/assets/scripts/mac-m1-resource-monitor.sh +121 -0
  102. package/engine/config.star +3 -0
  103. package/engine/docs/AUTODISCOVERY_TEST.md +182 -0
  104. package/engine/docs/C4-ARCHITECTURE-operations.md +396 -0
  105. package/engine/docs/C4-ARCHITECTURE-overview.md +284 -0
  106. package/engine/docs/C4-ARCHITECTURE.md +17 -0
  107. package/engine/docs/GOLDEN_IMAGE_INTEGRATION.md +349 -0
  108. package/engine/docs/MANIFEST_DRIVEN_ARCHITECTURE.md +357 -0
  109. package/engine/docs/README.md +284 -0
  110. package/engine/entrypoint.star +97 -0
  111. package/engine/lifecycle/orchestrator.star +260 -0
  112. package/engine/paths.star +717 -0
  113. package/engine/schemas/manifest-schema.json +301 -0
  114. package/engine/schemas/service-schema.json +301 -0
  115. package/engine/scripts/README.md +89 -0
  116. package/engine/scripts/build-and-publish-lib.sh +205 -0
  117. package/engine/scripts/check-lib-publish.sh +85 -0
  118. package/engine/scripts/debug-container.sh +258 -0
  119. package/engine/scripts/generate-manifest.sh +197 -0
  120. package/engine/spec.master +702 -0
  121. package/engine/spec.master.example +341 -0
  122. package/engine/topologies/README.md +34 -0
  123. package/engine/topologies/platform/docker/AGENTS.md +59 -0
  124. package/engine/topologies/platform/docker/auth/auth.star +167 -0
  125. package/engine/topologies/platform/docker/build/golden_image.star +37 -0
  126. package/engine/topologies/platform/docker/build/golden_image_build.star +82 -0
  127. package/engine/topologies/platform/docker/build/golden_image_constants.star +93 -0
  128. package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +291 -0
  129. package/engine/topologies/platform/docker/compose/compose.star +636 -0
  130. package/engine/topologies/platform/docker/compose/compose_helpers.star +52 -0
  131. package/engine/topologies/platform/docker/compose/traefik_standalone.star +53 -0
  132. package/engine/topologies/platform/docker/config/bunignore.star +142 -0
  133. package/engine/topologies/platform/docker/config/dockerignore.star +150 -0
  134. package/engine/topologies/platform/docker/constants.star +170 -0
  135. package/engine/topologies/platform/docker/dockerfile/dockerfile.star +54 -0
  136. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +73 -0
  137. package/engine/topologies/platform/docker/index.star +27 -0
  138. package/engine/topologies/platform/docker/layers/bun_helpers.star +23 -0
  139. package/engine/topologies/platform/docker/layers/infisical/index.star +40 -0
  140. package/engine/topologies/platform/docker/layers/infisical/infisical_docker.star +262 -0
  141. package/engine/topologies/platform/docker/layers/l1_base_layers.star +87 -0
  142. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +125 -0
  143. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +209 -0
  144. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +214 -0
  145. package/engine/topologies/platform/docker/layers/prisma/constants.star +135 -0
  146. package/engine/topologies/platform/docker/layers/prisma/db_readiness.star +96 -0
  147. package/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +109 -0
  148. package/engine/topologies/platform/docker/layers/prisma/migrate_script.star +98 -0
  149. package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +126 -0
  150. package/engine/topologies/platform/docker/layers/prisma/prisma_runtime.star +161 -0
  151. package/engine/topologies/platform/docker/layers/prisma/secret_resolution.star +119 -0
  152. package/engine/topologies/platform/docker/networking/api_path_constants.star +230 -0
  153. package/engine/topologies/platform/docker/networking/sablier_container_cycle.star +27 -0
  154. package/engine/topologies/platform/docker/networking/traefik.star +232 -0
  155. package/engine/topologies/platform/docker/networking/traefik_constants.star +146 -0
  156. package/engine/topologies/platform/docker/networking/traefik_helpers.star +160 -0
  157. package/engine/topologies/platform/docker/secrets/infisical.star +135 -0
  158. package/engine/topologies/platform/net/proxy.star +4 -0
  159. package/engine/topologies/platform/registries/local.star +4 -0
  160. package/engine/topologies/platform/security/secrets.star +239 -0
  161. package/engine/topologies/platform/state/relational.star +4 -0
  162. package/engine/topologies/tilt/common/AGENTS.md +59 -0
  163. package/engine/topologies/tilt/common/utils.star +364 -0
  164. package/engine/topologies/tilt/common/utils_debug.star +69 -0
  165. package/engine/topologies/tilt/common/utils_deterministic.star +168 -0
  166. package/engine/topologies/tilt/common/utils_docker_networks.star +46 -0
  167. package/engine/topologies/tilt/common/utils_env.star +43 -0
  168. package/engine/topologies/tilt/common/utils_output.star +97 -0
  169. package/engine/topologies/tilt/config/AGENTS.md +68 -0
  170. package/engine/topologies/tilt/config/defaults.star +3 -0
  171. package/engine/topologies/tilt/config/feature_flags.star +10 -0
  172. package/engine/topologies/tilt/config/global.star +55 -0
  173. package/engine/topologies/tilt/config/profiles.star +382 -0
  174. package/engine/topologies/tilt/generators/agents_md.star +62 -0
  175. package/engine/topologies/tilt/generators/api_gateway.star +145 -0
  176. package/engine/topologies/tilt/generators/index.star +124 -0
  177. package/engine/topologies/tilt/generators/infisical/README.md +424 -0
  178. package/engine/topologies/tilt/generators/infisical/examples.star +312 -0
  179. package/engine/topologies/tilt/generators/infisical/index.star +57 -0
  180. package/engine/topologies/tilt/generators/infisical/machine_identity.star +420 -0
  181. package/engine/topologies/tilt/generators/infisical/organization.star +534 -0
  182. package/engine/topologies/tilt/generators/infisical/path_manager.star +476 -0
  183. package/engine/topologies/tilt/generators/infisical/secrets_generator.star +420 -0
  184. package/engine/topologies/tilt/generators/nginx_conf.star +143 -0
  185. package/engine/topologies/tilt/generators/npmrc.star +111 -0
  186. package/engine/topologies/tilt/generators/tsconfig.star +23 -0
  187. package/engine/topologies/tilt/generators/typescript/backend_library_tsconfig.star +71 -0
  188. package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +124 -0
  189. package/engine/topologies/tilt/generators/typescript/common.star +7 -0
  190. package/engine/topologies/tilt/generators/typescript/frontend_library_tsconfig.star +76 -0
  191. package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +110 -0
  192. package/engine/topologies/tilt/generators/typescript/include.star +23 -0
  193. package/engine/topologies/tilt/generators/typescript/playwright_config.star +40 -0
  194. package/engine/topologies/tilt/generators/typescript/prisma_config.star +49 -0
  195. package/engine/topologies/tilt/generators/validators.star +853 -0
  196. package/engine/topologies/tilt/generators/vite/backend.star +78 -0
  197. package/engine/topologies/tilt/generators/vite/frontend.star +98 -0
  198. package/engine/topologies/tilt/generators/vite/generators.star +43 -0
  199. package/engine/topologies/tilt/generators/vite/helpers.star +244 -0
  200. package/engine/topologies/tilt/generators/vite/library.star +65 -0
  201. package/engine/topologies/tilt/generators/vite/sdk.star +73 -0
  202. package/engine/topologies/tilt/generators/vite/templates.star +737 -0
  203. package/engine/topologies/tilt/generators/vite_config.star +22 -0
  204. package/engine/topologies/tilt/manifest/AGENTS.md +61 -0
  205. package/engine/topologies/tilt/manifest/README.md +693 -0
  206. package/engine/topologies/tilt/manifest/__init__.star +218 -0
  207. package/engine/topologies/tilt/manifest/constants.star +252 -0
  208. package/engine/topologies/tilt/manifest/errors.star +266 -0
  209. package/engine/topologies/tilt/manifest/integration.star +339 -0
  210. package/engine/topologies/tilt/manifest/json_to_yaml.py +54 -0
  211. package/engine/topologies/tilt/manifest/loader.star +214 -0
  212. package/engine/topologies/tilt/manifest/mapper.star +227 -0
  213. package/engine/topologies/tilt/manifest/parser.star +400 -0
  214. package/engine/topologies/tilt/manifest/schema.star +462 -0
  215. package/engine/topologies/tilt/manifest/tests/test_integration.star +276 -0
  216. package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +397 -0
  217. package/engine/topologies/tilt/manifest/tests/test_manifest.star +316 -0
  218. package/engine/topologies/tilt/manifest/tests/test_validator.star +470 -0
  219. package/engine/topologies/tilt/manifest/validator.star +717 -0
  220. package/engine/topologies/tilt/manifest/yaml_to_json.py +18 -0
  221. package/engine/topologies/tilt/resources/AGENTS.md +71 -0
  222. package/engine/topologies/tilt/resources/conditions.star +3 -0
  223. package/engine/topologies/tilt/resources/databases.star +244 -0
  224. package/engine/topologies/tilt/resources/declaration.star +3 -0
  225. package/engine/topologies/tilt/resources/deps.star +3 -0
  226. package/engine/topologies/tilt/resources/infra-loader.star +437 -0
  227. package/engine/topologies/tilt/resources/libs.star +30 -0
  228. package/engine/topologies/tilt/resources/libs_discovery.star +102 -0
  229. package/engine/topologies/tilt/resources/libs_paths.star +79 -0
  230. package/engine/topologies/tilt/resources/libs_resource_definition.star +151 -0
  231. package/engine/topologies/tilt/resources/libs_setup.star +164 -0
  232. package/engine/topologies/tilt/resources/libs_tsconfig.star +37 -0
  233. package/engine/topologies/tilt/resources/orchestrator/apply.star +77 -0
  234. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +431 -0
  235. package/engine/topologies/tilt/resources/orchestrator/apply_manifest_orchestration.star +81 -0
  236. package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +148 -0
  237. package/engine/topologies/tilt/resources/orchestrator/apply_resource_validation.star +36 -0
  238. package/engine/topologies/tilt/resources/orchestrator/apply_runtime_flags.star +44 -0
  239. package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +164 -0
  240. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/AGENTS.md +75 -0
  241. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/index.star +21 -0
  242. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +134 -0
  243. package/engine/topologies/tilt/resources/orchestrator/generators/env.star +258 -0
  244. package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +169 -0
  245. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +524 -0
  246. package/engine/topologies/tilt/resources/orchestrator/helpers.star +33 -0
  247. package/engine/topologies/tilt/resources/orchestrator.star +54 -0
  248. package/engine/topologies/tilt/resources/ordering.star +3 -0
  249. package/engine/topologies/tilt/resources/triggers.star +3 -0
  250. package/engine/topologies/tilt/resources/watchers.star +299 -0
  251. package/engine/where-things-live.star +717 -0
  252. package/ext/Tiltfile +66 -0
  253. package/ext/ide-components/README.md +219 -0
  254. package/ext/ide-components/code_executor/server.py +268 -0
  255. package/ext/ide-components/code_viewer/server.py +206 -0
  256. package/ext/ide-components/config_inspector/server.py +1050 -0
  257. package/ext/ide-components/file_browser/server.py +285 -0
  258. package/ext/ide-components/shared/config_service.py +469 -0
  259. package/ext/ide-components/shared/file_utils.py +298 -0
  260. package/ext/ide-components/shared/http_utils.py +232 -0
  261. package/ext/ide-components/shared/static/styles.css +1334 -0
  262. package/ext/ide-components/shared/syntax_highlighter.py +140 -0
  263. package/ext/ide-components/shared/template_engine.py +123 -0
  264. package/ext/ide-components/shared/templates/base.html +121 -0
  265. package/ext/ide-components/shared/test_config_service.py +326 -0
  266. package/ext/ide-components/shared/tilt_integration.py +158 -0
  267. package/ext/ui-enhancements/cron-status-server.sh +49 -0
  268. package/ext/ui-enhancements/cron-status-tracker.sh +146 -0
  269. package/ext/ui-enhancements/extension.yaml +36 -0
  270. package/ext/ui-enhancements/tiltfile.py +99 -0
  271. package/ext/ui-enhancements/ui/cron-jobs-tab.css +396 -0
  272. package/ext/ui-enhancements/ui/help-panel.css +279 -0
  273. package/ext/ui-enhancements/ui/icons.css +125 -0
  274. package/ext/ui-enhancements/ui/tooltips.css +114 -0
  275. package/package.json +13 -9
  276. package/specs/specs/TILT_SERVICE_DEFAULTS.star +205 -0
  277. package/specs/specs/TILT_TECH_STACK.star +159 -0
@@ -0,0 +1,298 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Shared file utilities for Tilt IDE components
4
+ """
5
+
6
+ import os
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Optional, List, Tuple
10
+
11
+ # TDK CLI root directory (5 levels up from this file: ext/ide-components/shared/)
12
+ PROJECT_ROOT = Path(__file__).parent.parent.parent.parent.parent
13
+ MAX_FILE_SIZE = 1024 * 1024 # 1MB
14
+
15
+
16
+ class FileUtils:
17
+ """Safe file operations for IDE components"""
18
+
19
+ @staticmethod
20
+ def validate_path(path: str) -> Optional[Path]:
21
+ """
22
+ Validate and sanitize a file path.
23
+ Returns absolute Path if valid, None if invalid.
24
+
25
+ Security: Prevents path traversal attacks by ensuring
26
+ the resolved path is within the project root.
27
+ """
28
+ try:
29
+ # Handle relative paths
30
+ if not path.startswith('/'):
31
+ target = PROJECT_ROOT / path
32
+ else:
33
+ target = Path(path)
34
+
35
+ # Resolve to absolute, removing .. and symlinks
36
+ resolved = target.resolve()
37
+
38
+ # Ensure within project root
39
+ try:
40
+ resolved.relative_to(PROJECT_ROOT.resolve())
41
+ except ValueError:
42
+ return None
43
+
44
+ return resolved
45
+ except (ValueError, OSError, RuntimeError):
46
+ return None
47
+
48
+ @staticmethod
49
+ def get_file_info(file_path: Path) -> dict:
50
+ """Get file metadata for display"""
51
+ stat = file_path.stat()
52
+ size = stat.st_size
53
+
54
+ # Human-readable size
55
+ if size < 1024:
56
+ size_str = f"{size} B"
57
+ elif size < 1024 * 1024:
58
+ size_str = f"{size / 1024:.1f} KB"
59
+ else:
60
+ size_str = f"{size / (1024 * 1024):.1f} MB"
61
+
62
+ # Modified time
63
+ from datetime import datetime
64
+ mtime = datetime.fromtimestamp(stat.st_mtime)
65
+ mtime_str = mtime.strftime("%Y-%m-%d %H:%M")
66
+
67
+ # File type icon
68
+ ext = file_path.suffix.lower()
69
+ icon = FileUtils.get_file_icon(ext, file_path.is_dir(), file_path.name)
70
+
71
+ return {
72
+ 'name': file_path.name,
73
+ 'path': str(file_path.relative_to(PROJECT_ROOT)),
74
+ 'size': size,
75
+ 'size_str': size_str,
76
+ 'modified': mtime_str,
77
+ 'is_dir': file_path.is_dir(),
78
+ 'icon': icon,
79
+ 'extension': ext,
80
+ }
81
+
82
+ @staticmethod
83
+ def get_file_icon(ext: str, is_dir: bool, filename: str = '') -> str:
84
+ """Get emoji icon for file type"""
85
+ if is_dir:
86
+ if filename.startswith('.'):
87
+ return '🔒'
88
+ if filename in ('node_modules', 'vendor', '__pycache__'):
89
+ return '📦'
90
+ return '📁'
91
+
92
+ # Check special filenames first (exact match)
93
+ special_icons = {
94
+ 'Makefile': '🔨',
95
+ 'README': '📖',
96
+ 'LICENSE': '📜',
97
+ 'Dockerfile': '🐳',
98
+ 'docker-compose.yml': '🐳',
99
+ 'docker-compose.yaml': '🐳',
100
+ '.gitignore': '🚫',
101
+ 'go.mod': '🔵',
102
+ 'go.sum': '🔵',
103
+ 'Cargo.toml': '🦀',
104
+ 'Cargo.lock': '🦀',
105
+ 'package.json': '⬡',
106
+ 'package-lock.json': '⬡',
107
+ 'requirements.txt': '🐍',
108
+ 'setup.py': '🐍',
109
+ }
110
+
111
+ if filename in special_icons:
112
+ return special_icons[filename]
113
+
114
+ icons = {
115
+ '.ts': '📘', '.tsx': '📘',
116
+ '.js': '📒', '.jsx': '📒',
117
+ '.mjs': '📒', '.cjs': '📒',
118
+ '.json': '📋',
119
+ '.yaml': '📋', '.yml': '📋',
120
+ '.star': '⭐',
121
+ '.py': '🐍',
122
+ '.pyc': '🐍',
123
+ '.pyo': '🐍',
124
+ '.ipynb': '📓',
125
+ '.md': '📝',
126
+ '.mdx': '📝',
127
+ '.txt': '📄',
128
+ '.prisma': '🗃️',
129
+ '.dockerfile': '🐳', '.docker': '🐳',
130
+ '.sh': '🔧',
131
+ '.bash': '🔧',
132
+ '.zsh': '🔧',
133
+ '.fish': '🔧',
134
+ '.css': '🎨', '.scss': '🎨', '.sass': '🎨', '.less': '🎨',
135
+ '.html': '🌐', '.htm': '🌐', '.xhtml': '🌐',
136
+ '.vue': '💚',
137
+ '.svelte': '🧡',
138
+ '.conf': '⚙️', '.config': '⚙️', '.cfg': '⚙️',
139
+ '.ini': '⚙️',
140
+ '.toml': '⚙️',
141
+ '.env': '🔐',
142
+ '.env.local': '🔐', '.env.production': '🔐', '.env.development': '🔐',
143
+ '.sql': '🗄️',
144
+ '.go': '🔵',
145
+ '.rs': '🦀',
146
+ '.java': '☕',
147
+ '.class': '☕',
148
+ '.jar': '☕',
149
+ '.kt': '💜',
150
+ '.swift': '🦅',
151
+ '.rb': '💎',
152
+ '.php': '🐘',
153
+ '.c': '🇨',
154
+ '.cpp': '🇨🇵',
155
+ '.cc': '🇨🇵',
156
+ '.h': '📋',
157
+ '.hpp': '📋',
158
+ '.zig': '⚡',
159
+ '.lua': '🌙',
160
+ '.r': '📊',
161
+ '.R': '📊',
162
+ '.lock': '🔒',
163
+ }
164
+
165
+ return icons.get(ext, '📄')
166
+
167
+ @staticmethod
168
+ def read_file_safe(file_path: str, max_size: int = MAX_FILE_SIZE) -> Tuple[Optional[str], Optional[str]]:
169
+ """
170
+ Safely read a file with size limit.
171
+
172
+ Returns: (content, error_message)
173
+ - If success: (content, None)
174
+ - If error: (None, error_message)
175
+ """
176
+ resolved = FileUtils.validate_path(file_path)
177
+
178
+ if not resolved:
179
+ return None, "Invalid path or access denied"
180
+
181
+ if not resolved.exists():
182
+ return None, f"File not found: {file_path}"
183
+
184
+ if resolved.is_dir():
185
+ return None, "Path is a directory, not a file"
186
+
187
+ # Check size
188
+ size = resolved.stat().st_size
189
+ if size > max_size:
190
+ return None, f"File too large ({size / (1024*1024):.1f} MB > {max_size / (1024*1024):.1f} MB limit)"
191
+
192
+ try:
193
+ with open(resolved, 'r', encoding='utf-8', errors='replace') as f:
194
+ return f.read(), None
195
+ except Exception as e:
196
+ return None, f"Error reading file: {str(e)}"
197
+
198
+ @staticmethod
199
+ def list_directory(dir_path: str) -> Tuple[Optional[List[dict]], Optional[str]]:
200
+ """
201
+ List directory contents safely.
202
+
203
+ Returns: (items, error_message)
204
+ """
205
+ resolved = FileUtils.validate_path(dir_path)
206
+
207
+ if not resolved:
208
+ return None, "Invalid path or access denied"
209
+
210
+ if not resolved.exists():
211
+ return None, f"Directory not found: {dir_path}"
212
+
213
+ if not resolved.is_dir():
214
+ return None, "Path is not a directory"
215
+
216
+ try:
217
+ items = []
218
+ for entry in sorted(resolved.iterdir()):
219
+ # Skip hidden files
220
+ if entry.name.startswith('.'):
221
+ continue
222
+ items.append(FileUtils.get_file_info(entry))
223
+ return items, None
224
+ except Exception as e:
225
+ return None, f"Error reading directory: {str(e)}"
226
+
227
+ @staticmethod
228
+ def search_files(query: str, max_results: int = 50) -> List[dict]:
229
+ """
230
+ Search for files by name across the project.
231
+
232
+ Returns list of matching file info dicts.
233
+ """
234
+ results = []
235
+ query_lower = query.lower()
236
+
237
+ try:
238
+ for root, dirs, files in os.walk(PROJECT_ROOT):
239
+ # Skip hidden directories and common ignores
240
+ dirs[:] = [d for d in dirs if not d.startswith('.') and
241
+ d not in ('node_modules', 'dist', 'build', '.git', '__pycache__', 'coverage')]
242
+
243
+ for name in files:
244
+ if query_lower in name.lower():
245
+ file_path = Path(root) / name
246
+ try:
247
+ info = FileUtils.get_file_info(file_path)
248
+ results.append(info)
249
+ if len(results) >= max_results:
250
+ return results
251
+ except (OSError, PermissionError):
252
+ continue
253
+
254
+ except Exception:
255
+ pass
256
+
257
+ return results
258
+
259
+ @staticmethod
260
+ def get_language_from_ext(ext: str) -> str:
261
+ """Get language identifier from file extension"""
262
+ languages = {
263
+ '.ts': 'typescript', '.tsx': 'typescript',
264
+ '.js': 'javascript', '.jsx': 'javascript',
265
+ '.json': 'json',
266
+ '.yaml': 'yaml', '.yml': 'yaml',
267
+ '.star': 'python', # Starlark is Python-like
268
+ '.py': 'python',
269
+ '.md': 'markdown',
270
+ '.prisma': 'prisma',
271
+ '.dockerfile': 'dockerfile',
272
+ '.sh': 'bash',
273
+ '.css': 'css', '.scss': 'scss', '.less': 'less',
274
+ '.html': 'html', '.htm': 'html',
275
+ '.sql': 'sql',
276
+ '.go': 'go',
277
+ '.rs': 'rust',
278
+ '.java': 'java',
279
+ }
280
+
281
+ return languages.get(ext, 'text')
282
+
283
+
284
+ if __name__ == '__main__':
285
+ # Quick test
286
+ print(f"Project root: {PROJECT_ROOT}")
287
+
288
+ # Test validate_path
289
+ test_paths = [
290
+ '.',
291
+ 'services/product/staff',
292
+ '../outside', # Should fail
293
+ '/etc/passwd', # Should fail
294
+ ]
295
+
296
+ for path in test_paths:
297
+ result = FileUtils.validate_path(path)
298
+ print(f"Path '{path}': {result}")
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Shared HTTP handler utilities for Tilt IDE components.
4
+ Provides a base handler class with common HTTP functionality.
5
+ """
6
+
7
+ import json
8
+ from http.server import BaseHTTPRequestHandler
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+
13
+ class BaseIDEHandler(BaseHTTPRequestHandler):
14
+ """
15
+ Base HTTP handler for Tilt IDE components.
16
+
17
+ Provides common functionality:
18
+ - JSON/HTML response helpers
19
+ - HTML escaping for XSS prevention
20
+ - Page rendering with shared template
21
+ - Suppressed logging
22
+ """
23
+
24
+ # Shared template fallback (used when base.html is not found)
25
+ DEFAULT_TEMPLATE = '''<!DOCTYPE html>
26
+ <html lang="en">
27
+ <head>
28
+ <meta charset="UTF-8">
29
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
30
+ <title>{title}</title>
31
+ <link rel="stylesheet" href="/static/styles.css">
32
+ </head>
33
+ <body>
34
+ <header class="header">
35
+ <h1>{icon} {title}</h1>
36
+ {controls}
37
+ </header>
38
+ <div class="container">
39
+ {sidebar}
40
+ <main class="content">{content}</main>
41
+ </div>
42
+ {scripts}
43
+ </body>
44
+ </html>'''
45
+
46
+ def log_message(self, format, *args):
47
+ """Suppress default request logging."""
48
+ pass
49
+
50
+ def send_json_response(self, data: dict, status: int = 200) -> None:
51
+ """
52
+ Send a JSON response with CORS headers.
53
+
54
+ Args:
55
+ data: Response data to serialize as JSON
56
+ status: HTTP status code (default: 200)
57
+ """
58
+ self.send_response(status)
59
+ self.send_header('Content-Type', 'application/json')
60
+ self.send_header('Access-Control-Allow-Origin', '*')
61
+ self.end_headers()
62
+ self.wfile.write(json.dumps(data).encode())
63
+
64
+ def send_html_response(self, html: str, status: int = 200) -> None:
65
+ """
66
+ Send an HTML response with CORS headers.
67
+
68
+ Args:
69
+ html: HTML content to send
70
+ status: HTTP status code (default: 200)
71
+ """
72
+ self.send_response(status)
73
+ self.send_header('Content-Type', 'text/html; charset=utf-8')
74
+ self.send_header('Access-Control-Allow-Origin', '*')
75
+ self.end_headers()
76
+ self.wfile.write(html.encode())
77
+
78
+ def send_css_response(self, css: str) -> None:
79
+ """
80
+ Send a CSS response.
81
+
82
+ Args:
83
+ css: CSS content to send
84
+ """
85
+ self.send_response(200)
86
+ self.send_header('Content-Type', 'text/css')
87
+ self.end_headers()
88
+ self.wfile.write(css.encode())
89
+
90
+ def send_error_response(self, status: int, message: str) -> None:
91
+ """
92
+ Send an error response.
93
+
94
+ Args:
95
+ status: HTTP status code
96
+ message: Error message
97
+ """
98
+ self.send_response(status)
99
+ self.send_header('Content-Type', 'text/plain')
100
+ self.end_headers()
101
+ self.wfile.write(message.encode())
102
+
103
+ @staticmethod
104
+ def escape_html(text: str) -> str:
105
+ """
106
+ Escape HTML special characters to prevent XSS attacks.
107
+
108
+ Args:
109
+ text: Raw text to escape
110
+
111
+ Returns:
112
+ Escaped text safe for HTML insertion
113
+ """
114
+ return (text
115
+ .replace('&', '&amp;')
116
+ .replace('<', '&lt;')
117
+ .replace('>', '&gt;')
118
+ .replace('"', '&quot;')
119
+ .replace("'", '&#x27;'))
120
+
121
+ def render_page(
122
+ self,
123
+ title: str,
124
+ content: str,
125
+ icon: str = '📄',
126
+ sidebar: str = '',
127
+ controls: str = '',
128
+ scripts: str = ''
129
+ ) -> str:
130
+ """
131
+ Render a full HTML page using the base template.
132
+
133
+ Args:
134
+ title: Page title
135
+ content: Main content HTML
136
+ icon: Emoji icon for the page header
137
+ sidebar: Sidebar HTML (optional)
138
+ controls: Header controls HTML (optional)
139
+ scripts: Scripts to include before </body> (optional)
140
+
141
+ Returns:
142
+ Complete HTML page as string
143
+ """
144
+ # Try to load template from file
145
+ template = self._load_template()
146
+
147
+ # Build sidebar wrapper if content provided
148
+ sidebar_html = f'<aside class="sidebar">{sidebar}</aside>' if sidebar else ''
149
+
150
+ # Use string replacement to avoid conflicts with JS braces
151
+ result = template.replace('{title}', self.escape_html(title))
152
+ result = result.replace('{icon}', icon)
153
+ result = result.replace('{favicon}', icon)
154
+ result = result.replace('{controls}', controls)
155
+ result = result.replace('{sidebar}', sidebar_html)
156
+ result = result.replace('{content}', content)
157
+ result = result.replace('{scripts}', scripts)
158
+
159
+ return result
160
+
161
+ def _load_template(self) -> str:
162
+ """Load base template from file or return default."""
163
+ try:
164
+ template_path = self._get_template_path()
165
+ if template_path and template_path.exists():
166
+ with open(template_path, 'r', encoding='utf-8') as f:
167
+ return f.read()
168
+ except Exception:
169
+ pass
170
+ return self.DEFAULT_TEMPLATE
171
+
172
+ def _get_template_path(self) -> Optional[Path]:
173
+ """Get path to base template. Override in subclasses if needed."""
174
+ # Default: look in shared/templates/ relative to this file
175
+ shared_dir = Path(__file__).parent
176
+ return shared_dir / 'templates' / 'base.html'
177
+
178
+ def handle_health_check(self, service_name: str, features: Optional[list] = None) -> None:
179
+ """
180
+ Handle a standard health check request.
181
+
182
+ Args:
183
+ service_name: Name of the service for the response
184
+ features: Optional list of service features/capabilities
185
+ """
186
+ response = {
187
+ 'status': 'ok',
188
+ 'service': service_name
189
+ }
190
+ if features:
191
+ response['features'] = features
192
+ self.send_json_response(response)
193
+
194
+
195
+ def run_server(server_class, port: int, service_name: str, service_description: str) -> None:
196
+ """
197
+ Run an HTTP server with standard configuration.
198
+
199
+ Args:
200
+ server_class: The HTTP handler class to use
201
+ port: Port number to listen on
202
+ service_name: Name of the service for startup message
203
+ service_description: Brief description of the service
204
+ """
205
+ from http.server import HTTPServer
206
+
207
+ server = HTTPServer(('127.0.0.1', port), server_class)
208
+ print(f"{service_name} running at http://localhost:{port}")
209
+ if service_description:
210
+ print(f" {service_description}")
211
+
212
+ try:
213
+ server.serve_forever()
214
+ except KeyboardInterrupt:
215
+ print("\nShutting down...")
216
+ server.shutdown()
217
+
218
+
219
+ # Backwards compatibility: standalone functions that delegate to class methods
220
+ def send_json_response(handler: BaseHTTPRequestHandler, data: dict, status: int = 200) -> None:
221
+ """Standalone function for sending JSON responses (legacy compatibility)."""
222
+ BaseIDEHandler.send_json_response(handler, data, status)
223
+
224
+
225
+ def send_html_response(handler: BaseHTTPRequestHandler, html: str, status: int = 200) -> None:
226
+ """Standalone function for sending HTML responses (legacy compatibility)."""
227
+ BaseIDEHandler.send_html_response(handler, html, status)
228
+
229
+
230
+ def escape_html(text: str) -> str:
231
+ """Standalone function for HTML escaping (legacy compatibility)."""
232
+ return BaseIDEHandler.escape_html(text)