@flisk-sh/flisk 0.0.0-stage → 1.3.190

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 (440) hide show
  1. package/CHANGELOG.md +492 -0
  2. package/LICENSE +21 -0
  3. package/README.md +479 -2
  4. package/Tiltfile +151 -0
  5. package/cli/bin/flisk.js +42 -0
  6. package/cli/dist/agent-runtimes/custom.js +335 -0
  7. package/cli/dist/agent-runtimes/registry.js +34 -0
  8. package/cli/dist/backend-frameworks/elysia.js +38 -0
  9. package/cli/dist/backend-frameworks/express.js +49 -0
  10. package/cli/dist/backend-frameworks/fastify.js +43 -0
  11. package/cli/dist/backend-frameworks/h3.js +37 -0
  12. package/cli/dist/backend-frameworks/koa.js +47 -0
  13. package/cli/dist/backend-frameworks/mcp.js +86 -0
  14. package/cli/dist/backend-frameworks/nestjs.js +55 -0
  15. package/cli/dist/backend-frameworks/registry.js +134 -0
  16. package/cli/dist/backend-frameworks/types.js +1 -0
  17. package/cli/dist/backend-languages/bun.js +76 -0
  18. package/cli/dist/backend-languages/go.js +125 -0
  19. package/cli/dist/backend-languages/node.js +122 -0
  20. package/cli/dist/backend-languages/python.js +94 -0
  21. package/cli/dist/backend-languages/registry.js +37 -0
  22. package/cli/dist/backend-languages/rust.js +82 -0
  23. package/cli/dist/backend-languages/types.js +1 -0
  24. package/cli/dist/cli.js +29 -0
  25. package/cli/dist/commands/__tests__/project-e2e-helpers.js +49 -0
  26. package/cli/dist/commands/__tests__/ui-loading-screen.test.js +16 -0
  27. package/cli/dist/commands/agent.js +182 -0
  28. package/cli/dist/commands/completion-candidates.js +116 -0
  29. package/cli/dist/commands/completion.js +843 -0
  30. package/cli/dist/commands/config.js +362 -0
  31. package/cli/dist/commands/doctor.js +1504 -0
  32. package/cli/dist/commands/down.js +114 -0
  33. package/cli/dist/commands/eject.js +108 -0
  34. package/cli/dist/commands/help.js +152 -0
  35. package/cli/dist/commands/import.js +164 -0
  36. package/cli/dist/commands/logs.js +119 -0
  37. package/cli/dist/commands/maintainers.js +102 -0
  38. package/cli/dist/commands/mcp.js +28 -0
  39. package/cli/dist/commands/networks.js +306 -0
  40. package/cli/dist/commands/project.js +447 -0
  41. package/cli/dist/commands/projects.js +103 -0
  42. package/cli/dist/commands/registry.js +53 -0
  43. package/cli/dist/commands/resource.js +992 -0
  44. package/cli/dist/commands/resources.js +127 -0
  45. package/cli/dist/commands/restart.js +89 -0
  46. package/cli/dist/commands/runtime.js +75 -0
  47. package/cli/dist/commands/stack.js +171 -0
  48. package/cli/dist/commands/stacks.js +71 -0
  49. package/cli/dist/commands/status.js +211 -0
  50. package/cli/dist/commands/ui.js +725 -0
  51. package/cli/dist/commands/up.js +609 -0
  52. package/cli/dist/commands/upgrade.js +840 -0
  53. package/cli/dist/commands/version.js +10 -0
  54. package/cli/dist/components/Accessible.js +11 -0
  55. package/cli/dist/components/BaseTooltip.js +30 -0
  56. package/cli/dist/components/DetailPanel.js +33 -0
  57. package/cli/dist/components/FileTree.js +51 -0
  58. package/cli/dist/components/ResourceSelectInput.js +42 -0
  59. package/cli/dist/components/ResourceTable.js +35 -0
  60. package/cli/dist/components/ServiceIssues.js +13 -0
  61. package/cli/dist/components/TUIHeader.js +27 -0
  62. package/cli/dist/components/TabBar.js +27 -0
  63. package/cli/dist/components/Tooltip.js +13 -0
  64. package/cli/dist/components/index.js +11 -0
  65. package/cli/dist/components/ui-theme.js +120 -0
  66. package/cli/dist/config/platform-standards.js +182 -0
  67. package/cli/dist/frontend-frameworks/lit.js +64 -0
  68. package/cli/dist/frontend-frameworks/preact.js +62 -0
  69. package/cli/dist/frontend-frameworks/qwik.js +61 -0
  70. package/cli/dist/frontend-frameworks/react.js +69 -0
  71. package/cli/dist/frontend-frameworks/registry.js +83 -0
  72. package/cli/dist/frontend-frameworks/solid.js +62 -0
  73. package/cli/dist/frontend-frameworks/svelte.js +67 -0
  74. package/cli/dist/frontend-frameworks/tanstack-router.js +101 -0
  75. package/cli/dist/frontend-frameworks/types.js +1 -0
  76. package/cli/dist/frontend-frameworks/vanilla.js +59 -0
  77. package/cli/dist/frontend-frameworks/vue.js +72 -0
  78. package/cli/dist/generator/__tests__/golden-helpers.js +43 -0
  79. package/cli/dist/generator/extension-fetch.js +291 -0
  80. package/cli/dist/generator/resource-templates.js +133 -0
  81. package/cli/dist/generator/template-engine.js +801 -0
  82. package/cli/dist/index.js +10 -0
  83. package/cli/dist/mcp/server.js +101 -0
  84. package/cli/dist/mcp/tools.js +351 -0
  85. package/cli/dist/program.js +37 -0
  86. package/cli/dist/types/index.js +38 -0
  87. package/cli/dist/utils/agent-host.js +36 -0
  88. package/cli/dist/utils/atomic-write.js +36 -0
  89. package/cli/dist/utils/bun-floor.js +9 -0
  90. package/cli/dist/utils/bun-requirement.js +10 -0
  91. package/cli/dist/utils/cache.js +63 -0
  92. package/cli/dist/utils/cold-preflight.js +257 -0
  93. package/cli/dist/utils/command-helpers.js +57 -0
  94. package/cli/dist/utils/constants.js +160 -0
  95. package/cli/dist/utils/discovery-context.js +71 -0
  96. package/cli/dist/utils/discovery-paths.js +93 -0
  97. package/cli/dist/utils/docker.js +34 -0
  98. package/cli/dist/utils/doctor-crashloop.js +161 -0
  99. package/cli/dist/utils/doctor-fix.js +391 -0
  100. package/cli/dist/utils/doctor-host.js +66 -0
  101. package/cli/dist/utils/doctor-report.js +78 -0
  102. package/cli/dist/utils/doctor-runtime.js +880 -0
  103. package/cli/dist/utils/doctor-starlark.js +132 -0
  104. package/cli/dist/utils/doctor-wiring.js +698 -0
  105. package/cli/dist/utils/down-cleanup.js +125 -0
  106. package/cli/dist/utils/env-validator.js +266 -0
  107. package/cli/dist/utils/errors.js +175 -0
  108. package/cli/dist/utils/exec-async.js +31 -0
  109. package/cli/dist/utils/file-helpers.js +104 -0
  110. package/cli/dist/utils/formatting.js +165 -0
  111. package/cli/dist/utils/generated-paths.js +51 -0
  112. package/cli/dist/utils/help-command.js +11 -0
  113. package/cli/dist/utils/host-port-config.js +140 -0
  114. package/cli/dist/utils/host-port-plan.js +100 -0
  115. package/cli/dist/utils/json-output.js +29 -0
  116. package/cli/dist/utils/machine-output.js +33 -0
  117. package/cli/dist/utils/paths.js +43 -0
  118. package/cli/dist/utils/platform.js +58 -0
  119. package/cli/dist/utils/port-assignment.js +75 -0
  120. package/cli/dist/utils/port-conflict-fix.js +25 -0
  121. package/cli/dist/utils/port-fallback-notice.js +17 -0
  122. package/cli/dist/utils/port-listener-probe.js +20 -0
  123. package/cli/dist/utils/project-config-defaults.js +17 -0
  124. package/cli/dist/utils/project-features.js +91 -0
  125. package/cli/dist/utils/project-templates.js +32 -0
  126. package/cli/dist/utils/prompt.js +97 -0
  127. package/cli/dist/utils/resource-features.js +184 -0
  128. package/cli/dist/utils/resource-kind.js +9 -0
  129. package/cli/dist/utils/schema-version-gate.js +45 -0
  130. package/cli/dist/utils/search-status.js +12 -0
  131. package/cli/dist/utils/secret-redaction.js +73 -0
  132. package/cli/dist/utils/service-config-checks.js +189 -0
  133. package/cli/dist/utils/service-manifest.js +254 -0
  134. package/cli/dist/utils/service-runtime-state.js +66 -0
  135. package/cli/dist/utils/service-urls.js +112 -0
  136. package/cli/dist/utils/services.js +360 -0
  137. package/cli/dist/utils/shared-infra.js +45 -0
  138. package/cli/dist/utils/shared-platform-postgres.js +232 -0
  139. package/cli/dist/utils/single-flight.js +21 -0
  140. package/cli/dist/utils/smoke.js +401 -0
  141. package/cli/dist/utils/spinner.js +40 -0
  142. package/cli/dist/utils/stack-features.js +15 -0
  143. package/cli/dist/utils/stack-status.js +14 -0
  144. package/cli/dist/utils/startup-report.js +78 -0
  145. package/cli/dist/utils/status-message.js +21 -0
  146. package/cli/dist/utils/status-ports.js +33 -0
  147. package/cli/dist/utils/suggestions.js +63 -0
  148. package/cli/dist/utils/tar.js +46 -0
  149. package/cli/dist/utils/tdk-version.js +51 -0
  150. package/cli/dist/utils/terminal-layout.js +19 -0
  151. package/cli/dist/utils/tilt-events.js +194 -0
  152. package/cli/dist/utils/tilt-logs.js +61 -0
  153. package/cli/dist/utils/tilt-port-owner.js +63 -0
  154. package/cli/dist/utils/tilt-process.js +54 -0
  155. package/cli/dist/utils/tilt-resource-state.js +32 -0
  156. package/cli/dist/utils/tilt-startup.js +49 -0
  157. package/cli/dist/utils/tilt-unreachable-message.js +9 -0
  158. package/cli/dist/utils/tilt.js +104 -0
  159. package/cli/dist/utils/ui-keymap.js +129 -0
  160. package/cli/dist/utils/ui-service-state.js +34 -0
  161. package/cli/dist/utils/up-only.js +28 -0
  162. package/cli/dist/utils/up-readiness.js +118 -0
  163. package/cli/dist/utils/url-actions.js +103 -0
  164. package/cli/dist/utils/validation.js +62 -0
  165. package/cli/dist/utils/which.js +56 -0
  166. package/cli/dist/utils/windows-doctor.js +176 -0
  167. package/cli/dist-bundle/chunk-6EA4DGCY.js +2 -0
  168. package/cli/dist-bundle/chunk-BZ4H7MM4.js +3 -0
  169. package/cli/dist-bundle/chunk-CLCUIDBH.js +510 -0
  170. package/cli/dist-bundle/cli.js +2017 -0
  171. package/cli/dist-bundle/cold-preflight-SY26O2IG.js +2 -0
  172. package/cli/dist-bundle/devtools-ZUHXW33W.js +5 -0
  173. package/cli/dist-bundle/extension-fetch-J4FFH7TJ.js +2 -0
  174. package/cli/package.json +83 -0
  175. package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +25 -0
  176. package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +466 -0
  177. package/cli/shared-platform-engineering/docker-templates/install-deps.sh +154 -0
  178. package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +14 -0
  179. package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +58 -0
  180. package/cli/templates/.tiltignore.hbs +72 -0
  181. package/cli/templates/TILT_RESOURCE_DEFAULTS.star.hbs +220 -0
  182. package/cli/templates/TILT_TECH_STACK.star.hbs +149 -0
  183. package/cli/templates/Tiltfile.hbs +381 -0
  184. package/cli/templates/spec.master.hbs +192 -0
  185. package/discovery/AGENTS.md +362 -0
  186. package/discovery/TILT_DISCOVERY.star +429 -0
  187. package/discovery/Tiltfile +58 -0
  188. package/discovery/config.star +265 -0
  189. package/discovery/constants.star +167 -0
  190. package/discovery/deterministic.star +213 -0
  191. package/discovery/discovery_daemon.star +193 -0
  192. package/discovery/discovery_orchestrator.star +492 -0
  193. package/discovery/filtering.star +7 -0
  194. package/discovery/identity-management-backend/service.yaml +26 -0
  195. package/discovery/identity-management-frontend/service.yaml +29 -0
  196. package/discovery/identity-sdk/service.yaml +9 -0
  197. package/discovery/incremental_discovery.star +215 -0
  198. package/discovery/indexing.star +9 -0
  199. package/discovery/json_manifest_scanner.star +57 -0
  200. package/discovery/libraries.star +48 -0
  201. package/discovery/loading.star +25 -0
  202. package/discovery/manifest/constants.star +106 -0
  203. package/discovery/manifest/loading.star +418 -0
  204. package/discovery/manifest/normalize.star +289 -0
  205. package/discovery/manifest/validation.star +110 -0
  206. package/discovery/normalization.star +13 -0
  207. package/discovery/registry.star +562 -0
  208. package/discovery/resource_snapshot.py +384 -0
  209. package/discovery/resource_snapshot.star +145 -0
  210. package/discovery/schemas.star +3 -0
  211. package/discovery/services/platform/cdc/debezium/nats-connector/service.yaml +9 -0
  212. package/discovery/services/platform/cdc/nats-http-bridge/service.yaml +9 -0
  213. package/discovery/services/platform/orchestrator/orchestrator-glue-backend/service.yaml +26 -0
  214. package/discovery/services/platform/synthetic-monitor/service.yaml +9 -0
  215. package/discovery/test_resource_snapshot.py +253 -0
  216. package/discovery/validation.star +80 -0
  217. package/discovery/versioning.star +3 -0
  218. package/engine/AGENTS.md +706 -0
  219. package/engine/README.md +31 -0
  220. package/engine/REFACTORING_MANIFEST_SYSTEM.md +974 -0
  221. package/engine/Tiltfile +86 -0
  222. package/engine/assets/docker/golden-layers.Dockerfile +204 -0
  223. package/engine/assets/docker/wake-gateway/Dockerfile +28 -0
  224. package/engine/assets/docker/wake-gateway/decide.test.ts +177 -0
  225. package/engine/assets/docker/wake-gateway/decide.ts +135 -0
  226. package/engine/assets/docker/wake-gateway/server.ts +363 -0
  227. package/engine/assets/scripts/cleanup-completed-migrator-images.sh +39 -0
  228. package/engine/assets/scripts/cleanup-tilt-images.sh +122 -0
  229. package/engine/assets/scripts/mac-m1-resource-monitor.sh +123 -0
  230. package/engine/config.star +5 -0
  231. package/engine/entrypoint.star +101 -0
  232. package/engine/lifecycle/orchestrator.star +263 -0
  233. package/engine/paths.star +719 -0
  234. package/engine/schemas/project-schema.json +147 -0
  235. package/engine/schemas/service-schema.json +537 -0
  236. package/engine/scripts/README.md +89 -0
  237. package/engine/scripts/build-and-publish-lib.sh +207 -0
  238. package/engine/scripts/check-lib-publish.sh +87 -0
  239. package/engine/scripts/debug-container.sh +265 -0
  240. package/engine/scripts/generate-manifest.sh +200 -0
  241. package/engine/spec.master +702 -0
  242. package/engine/spec.master.example +341 -0
  243. package/engine/topologies/README.md +33 -0
  244. package/engine/topologies/platform/docker/AGENTS.md +59 -0
  245. package/engine/topologies/platform/docker/auth/auth.star +207 -0
  246. package/engine/topologies/platform/docker/build/frontend_build_plan.star +65 -0
  247. package/engine/topologies/platform/docker/build/golden_image.star +37 -0
  248. package/engine/topologies/platform/docker/build/golden_image_build.star +144 -0
  249. package/engine/topologies/platform/docker/build/golden_image_constants.star +100 -0
  250. package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +263 -0
  251. package/engine/topologies/platform/docker/compose/compose.star +637 -0
  252. package/engine/topologies/platform/docker/compose/compose_helpers.star +54 -0
  253. package/engine/topologies/platform/docker/compose/traefik_standalone.star +171 -0
  254. package/engine/topologies/platform/docker/config/bunignore.star +144 -0
  255. package/engine/topologies/platform/docker/config/dockerignore.star +152 -0
  256. package/engine/topologies/platform/docker/config/healthcheck.star +58 -0
  257. package/engine/topologies/platform/docker/constants.star +211 -0
  258. package/engine/topologies/platform/docker/dockerfile/dockerfile.star +56 -0
  259. package/engine/topologies/platform/docker/dockerfile/language_dockerfile.star +264 -0
  260. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +76 -0
  261. package/engine/topologies/platform/docker/index.star +40 -0
  262. package/engine/topologies/platform/docker/layers/bun_helpers.star +25 -0
  263. package/engine/topologies/platform/docker/layers/infisical/index.star +42 -0
  264. package/engine/topologies/platform/docker/layers/infisical/infisical_docker.star +264 -0
  265. package/engine/topologies/platform/docker/layers/l1_base_layers.star +89 -0
  266. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +147 -0
  267. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +229 -0
  268. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +282 -0
  269. package/engine/topologies/platform/docker/layers/prisma/constants.star +138 -0
  270. package/engine/topologies/platform/docker/layers/prisma/db_readiness.star +98 -0
  271. package/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +131 -0
  272. package/engine/topologies/platform/docker/layers/prisma/migrate_script.star +100 -0
  273. package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +128 -0
  274. package/engine/topologies/platform/docker/layers/prisma/prisma_runtime.star +205 -0
  275. package/engine/topologies/platform/docker/layers/prisma/secret_resolution.star +121 -0
  276. package/engine/topologies/platform/docker/networking/api_path_constants.star +206 -0
  277. package/engine/topologies/platform/docker/networking/sablier_container_cycle.star +31 -0
  278. package/engine/topologies/platform/docker/networking/traefik.star +266 -0
  279. package/engine/topologies/platform/docker/networking/traefik_constants.star +148 -0
  280. package/engine/topologies/platform/docker/networking/traefik_helpers.star +162 -0
  281. package/engine/topologies/platform/docker/networking/traefik_static_routes.star +165 -0
  282. package/engine/topologies/platform/docker/secrets/env_secrets.star +74 -0
  283. package/engine/topologies/platform/docker/secrets/infisical.star +109 -0
  284. package/engine/topologies/platform/net/AGENTS.md +64 -0
  285. package/engine/topologies/platform/net/proxy.star +6 -0
  286. package/engine/topologies/platform/registries/local.star +6 -0
  287. package/engine/topologies/platform/registries/verdaccio_loader.star +20 -0
  288. package/engine/topologies/platform/security/AGENTS.md +49 -0
  289. package/engine/topologies/platform/security/secrets.star +241 -0
  290. package/engine/topologies/platform/state/AGENTS.md +64 -0
  291. package/engine/topologies/platform/state/relational.star +6 -0
  292. package/engine/topologies/tilt/common/AGENTS.md +59 -0
  293. package/engine/topologies/tilt/common/utils.star +409 -0
  294. package/engine/topologies/tilt/common/utils_debug.star +71 -0
  295. package/engine/topologies/tilt/common/utils_deterministic.star +170 -0
  296. package/engine/topologies/tilt/common/utils_docker_networks.star +54 -0
  297. package/engine/topologies/tilt/common/utils_env.star +92 -0
  298. package/engine/topologies/tilt/common/utils_output.star +99 -0
  299. package/engine/topologies/tilt/config/AGENTS.md +68 -0
  300. package/engine/topologies/tilt/config/defaults.star +5 -0
  301. package/engine/topologies/tilt/config/feature_flags.star +12 -0
  302. package/engine/topologies/tilt/config/global.star +57 -0
  303. package/engine/topologies/tilt/config/profiles.star +398 -0
  304. package/engine/topologies/tilt/generators/agents_md.star +64 -0
  305. package/engine/topologies/tilt/generators/api_gateway.star +147 -0
  306. package/engine/topologies/tilt/generators/index.star +126 -0
  307. package/engine/topologies/tilt/generators/infisical/README.md +424 -0
  308. package/engine/topologies/tilt/generators/infisical/examples.star +314 -0
  309. package/engine/topologies/tilt/generators/infisical/index.star +59 -0
  310. package/engine/topologies/tilt/generators/infisical/machine_identity.star +422 -0
  311. package/engine/topologies/tilt/generators/infisical/organization.star +536 -0
  312. package/engine/topologies/tilt/generators/infisical/path_manager.star +478 -0
  313. package/engine/topologies/tilt/generators/infisical/secrets_generator.star +422 -0
  314. package/engine/topologies/tilt/generators/nginx_conf.star +145 -0
  315. package/engine/topologies/tilt/generators/npmrc.star +132 -0
  316. package/engine/topologies/tilt/generators/tsconfig.star +25 -0
  317. package/engine/topologies/tilt/generators/typescript/backend_library_tsconfig.star +73 -0
  318. package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +137 -0
  319. package/engine/topologies/tilt/generators/typescript/common.star +9 -0
  320. package/engine/topologies/tilt/generators/typescript/frontend_library_tsconfig.star +78 -0
  321. package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +140 -0
  322. package/engine/topologies/tilt/generators/typescript/include.star +25 -0
  323. package/engine/topologies/tilt/generators/typescript/playwright_config.star +42 -0
  324. package/engine/topologies/tilt/generators/typescript/prisma_config.star +51 -0
  325. package/engine/topologies/tilt/generators/validators.star +868 -0
  326. package/engine/topologies/tilt/generators/vite/backend.star +80 -0
  327. package/engine/topologies/tilt/generators/vite/frameworks/lit.star +92 -0
  328. package/engine/topologies/tilt/generators/vite/frameworks/preact.star +94 -0
  329. package/engine/topologies/tilt/generators/vite/frameworks/qwik.star +94 -0
  330. package/engine/topologies/tilt/generators/vite/frameworks/react.star +128 -0
  331. package/engine/topologies/tilt/generators/vite/frameworks/solid.star +94 -0
  332. package/engine/topologies/tilt/generators/vite/frameworks/svelte.star +94 -0
  333. package/engine/topologies/tilt/generators/vite/frameworks/vanilla.star +92 -0
  334. package/engine/topologies/tilt/generators/vite/frameworks/vue.star +94 -0
  335. package/engine/topologies/tilt/generators/vite/frontend.star +153 -0
  336. package/engine/topologies/tilt/generators/vite/generators.star +45 -0
  337. package/engine/topologies/tilt/generators/vite/helpers.star +256 -0
  338. package/engine/topologies/tilt/generators/vite/library.star +67 -0
  339. package/engine/topologies/tilt/generators/vite/sdk.star +75 -0
  340. package/engine/topologies/tilt/generators/vite/templates.star +460 -0
  341. package/engine/topologies/tilt/generators/vite_config.star +24 -0
  342. package/engine/topologies/tilt/manifest/AGENTS.md +61 -0
  343. package/engine/topologies/tilt/manifest/README.md +679 -0
  344. package/engine/topologies/tilt/manifest/__init__.star +220 -0
  345. package/engine/topologies/tilt/manifest/constants.star +262 -0
  346. package/engine/topologies/tilt/manifest/errors.star +268 -0
  347. package/engine/topologies/tilt/manifest/integration.star +348 -0
  348. package/engine/topologies/tilt/manifest/json_to_yaml.py +61 -0
  349. package/engine/topologies/tilt/manifest/loader.star +239 -0
  350. package/engine/topologies/tilt/manifest/mapper.star +229 -0
  351. package/engine/topologies/tilt/manifest/parser.star +490 -0
  352. package/engine/topologies/tilt/manifest/schema.star +502 -0
  353. package/engine/topologies/tilt/manifest/tests/test_integration.star +278 -0
  354. package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +475 -0
  355. package/engine/topologies/tilt/manifest/tests/test_manifest.star +319 -0
  356. package/engine/topologies/tilt/manifest/tests/test_validator.star +472 -0
  357. package/engine/topologies/tilt/manifest/validator.star +744 -0
  358. package/engine/topologies/tilt/manifest/yaml_to_json.py +20 -0
  359. package/engine/topologies/tilt/resources/AGENTS.md +71 -0
  360. package/engine/topologies/tilt/resources/build_concurrency.star +56 -0
  361. package/engine/topologies/tilt/resources/conditions.star +5 -0
  362. package/engine/topologies/tilt/resources/databases.star +246 -0
  363. package/engine/topologies/tilt/resources/declaration.star +5 -0
  364. package/engine/topologies/tilt/resources/deps.star +5 -0
  365. package/engine/topologies/tilt/resources/infra-loader.star +504 -0
  366. package/engine/topologies/tilt/resources/libs.star +32 -0
  367. package/engine/topologies/tilt/resources/libs_discovery.star +104 -0
  368. package/engine/topologies/tilt/resources/libs_paths.star +81 -0
  369. package/engine/topologies/tilt/resources/libs_resource_definition.star +153 -0
  370. package/engine/topologies/tilt/resources/libs_setup.star +166 -0
  371. package/engine/topologies/tilt/resources/libs_tsconfig.star +39 -0
  372. package/engine/topologies/tilt/resources/orchestrator/apply.star +79 -0
  373. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +864 -0
  374. package/engine/topologies/tilt/resources/orchestrator/apply_manifest_orchestration.star +90 -0
  375. package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +150 -0
  376. package/engine/topologies/tilt/resources/orchestrator/apply_resource_validation.star +38 -0
  377. package/engine/topologies/tilt/resources/orchestrator/apply_runtime_flags.star +46 -0
  378. package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +193 -0
  379. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/AGENTS.md +75 -0
  380. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/index.star +23 -0
  381. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +136 -0
  382. package/engine/topologies/tilt/resources/orchestrator/generators/env.star +219 -0
  383. package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +172 -0
  384. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +577 -0
  385. package/engine/topologies/tilt/resources/orchestrator/helpers.star +35 -0
  386. package/engine/topologies/tilt/resources/orchestrator.star +56 -0
  387. package/engine/topologies/tilt/resources/ordering.star +5 -0
  388. package/engine/topologies/tilt/resources/shared-platform-postgres.star +30 -0
  389. package/engine/topologies/tilt/resources/triggers.star +5 -0
  390. package/engine/topologies/tilt/resources/watchers.star +304 -0
  391. package/engine/where-things-live.star +719 -0
  392. package/examples/tdk-example/LICENSE +21 -0
  393. package/examples/tdk-example/README.md +23 -0
  394. package/examples/tdk-example/TILT_SERVICE_DEFAULTS.star +42 -0
  395. package/examples/tdk-example/TILT_TECH_STACK.star +67 -0
  396. package/examples/tdk-example/Tiltfile +25 -0
  397. package/examples/tdk-example/services/platform/messaging/docker-compose.yml +26 -0
  398. package/examples/tdk-example/services/shop/orders-api/package.json +20 -0
  399. package/examples/tdk-example/services/shop/orders-api/service.json +14 -0
  400. package/examples/tdk-example/services/shop/orders-api/src/index.ts +81 -0
  401. package/examples/tdk-example/services/shop/orders-app/index.html +12 -0
  402. package/examples/tdk-example/services/shop/orders-app/package.json +23 -0
  403. package/examples/tdk-example/services/shop/orders-app/public/.gitkeep +0 -0
  404. package/examples/tdk-example/services/shop/orders-app/service.json +9 -0
  405. package/examples/tdk-example/services/shop/orders-app/src/main.tsx +9 -0
  406. package/examples/tdk-example/services/shop/orders-app/src/orders.tsx +67 -0
  407. package/examples/tdk-example/services/shop/orders-app/src/style.css +15 -0
  408. package/examples/tdk-example/services/shop/orders-worker/package.json +18 -0
  409. package/examples/tdk-example/services/shop/orders-worker/service.json +13 -0
  410. package/examples/tdk-example/services/shop/orders-worker/src/index.ts +33 -0
  411. package/ext/Tiltfile +66 -0
  412. package/ext/ide-components/README.md +221 -0
  413. package/ext/ide-components/code_viewer/server.py +208 -0
  414. package/ext/ide-components/config_inspector/server.py +1052 -0
  415. package/ext/ide-components/file_browser/server.py +287 -0
  416. package/ext/ide-components/shared/config_service.py +471 -0
  417. package/ext/ide-components/shared/file_utils.py +300 -0
  418. package/ext/ide-components/shared/http_utils.py +234 -0
  419. package/ext/ide-components/shared/static/styles.css +1334 -0
  420. package/ext/ide-components/shared/syntax_highlighter.py +142 -0
  421. package/ext/ide-components/shared/template_engine.py +125 -0
  422. package/ext/ide-components/shared/templates/base.html +121 -0
  423. package/ext/ide-components/shared/test_config_service.py +328 -0
  424. package/ext/ide-components/shared/tilt_integration.py +160 -0
  425. package/ext/ui-enhancements/cron-status-server.sh +51 -0
  426. package/ext/ui-enhancements/cron-status-tracker.sh +148 -0
  427. package/ext/ui-enhancements/extension.yaml +36 -0
  428. package/ext/ui-enhancements/tiltfile.py +101 -0
  429. package/ext/ui-enhancements/ui/cron-jobs-tab.css +396 -0
  430. package/ext/ui-enhancements/ui/help-panel.css +279 -0
  431. package/ext/ui-enhancements/ui/icons.css +125 -0
  432. package/ext/ui-enhancements/ui/tooltips.css +114 -0
  433. package/package.json +94 -4
  434. package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +25 -0
  435. package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +466 -0
  436. package/shared-platform-engineering/docker-templates/install-deps.sh +154 -0
  437. package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +14 -0
  438. package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +58 -0
  439. package/specs/specs/TILT_SERVICE_DEFAULTS.star +207 -0
  440. package/specs/specs/TILT_TECH_STACK.star +161 -0
@@ -0,0 +1,471 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright (c) 2026 Flisk Landscape contributors
3
+ # SPDX-License-Identifier: MIT
4
+ """
5
+ ConfigService - File operations for Config Inspector
6
+ Handles safe file reading/writing with validation, backups, and conflict detection.
7
+ """
8
+
9
+ import os
10
+ import hashlib
11
+ import tempfile
12
+ from pathlib import Path
13
+ from datetime import datetime
14
+ from typing import Optional, Tuple, NamedTuple
15
+
16
+ from file_utils import PROJECT_ROOT, FileUtils
17
+
18
+
19
+ class SaveResult(NamedTuple):
20
+ """Result of a save operation"""
21
+ success: bool
22
+ message: str
23
+ backup_path: Optional[Path] = None
24
+ error_details: Optional[str] = None
25
+
26
+
27
+ class ConfigService:
28
+ """
29
+ Service layer for config file operations.
30
+ Separates file operations from HTTP handling.
31
+ """
32
+
33
+ # Allowed paths for write operations
34
+ # NOTE: These are paths within the Flisk CLI installation
35
+ ALLOWED_PATH_PREFIXES = [
36
+ 'engine/topologies/',
37
+ ]
38
+
39
+ # Master configs allowed at project root
40
+ ALLOWED_ROOT_CONFIGS = [
41
+ 'TILT_RESOURCE_DEFAULTS.star',
42
+ 'spec.master',
43
+ 'engine/spec.master',
44
+ ]
45
+
46
+ # Allowed file extensions
47
+ ALLOWED_EXTENSIONS = ['.star']
48
+
49
+ # Special filenames that are allowed without extension check
50
+ ALLOWED_FILENAMES = ['spec.master']
51
+
52
+ # File size limits
53
+ MAX_FILE_SIZE = 1024 * 1024 # 1MB
54
+ MAX_CONTENT_LENGTH = 1024 * 1024 # 1MB for request body
55
+
56
+ # Backup settings
57
+ BACKUP_DIR_NAME = '.backups'
58
+ MAX_BACKUPS = 3
59
+
60
+ @staticmethod
61
+ def validate_write_path(path: str) -> Optional[Path]:
62
+ """
63
+ Validate that a file path is safe for writing.
64
+
65
+ Security: Prevents path traversal attacks by using os.path.realpath()
66
+ and ensuring the resolved path is within allowed directories.
67
+
68
+ Allowed paths:
69
+ - Within engine/topologies/
70
+ - Project root master configs: TILT_RESOURCE_DEFAULTS.star, spec.master, engine/spec.master
71
+
72
+ Returns: Absolute Path if valid, None if invalid.
73
+ """
74
+ try:
75
+ # Handle relative paths
76
+ if not path.startswith('/'):
77
+ target = PROJECT_ROOT / path
78
+ else:
79
+ target = Path(path)
80
+
81
+ # Resolve to absolute path using realpath (follows symlinks, removes ..)
82
+ resolved = Path(os.path.realpath(target))
83
+ project_root_real = Path(os.path.realpath(PROJECT_ROOT))
84
+
85
+ # Check if path is within project root (prevent path traversal)
86
+ try:
87
+ resolved.relative_to(project_root_real)
88
+ except ValueError:
89
+ return None
90
+
91
+ # Get the relative path from project root
92
+ rel_path = resolved.relative_to(project_root_real)
93
+ rel_path_str = str(rel_path)
94
+
95
+ # Check if it's one of the allowed root configs
96
+ if rel_path_str in ConfigService.ALLOWED_ROOT_CONFIGS:
97
+ return resolved
98
+
99
+ # Check if it's within allowed prefixes
100
+ for prefix in ConfigService.ALLOWED_PATH_PREFIXES:
101
+ if rel_path_str.startswith(prefix):
102
+ return resolved
103
+
104
+ # Path is not in any allowed location
105
+ return None
106
+
107
+ except (ValueError, OSError, RuntimeError):
108
+ return None
109
+
110
+ @staticmethod
111
+ def validate_file_type(path: str) -> Tuple[bool, str]:
112
+ """
113
+ Validate that a file has an allowed extension or filename.
114
+
115
+ Allowed:
116
+ - Files with .star extension
117
+ - Files named exactly 'spec.master'
118
+
119
+ Returns: (is_valid, message)
120
+ """
121
+ path_obj = Path(path)
122
+ filename = path_obj.name
123
+ extension = path_obj.suffix.lower()
124
+
125
+ # Check if filename is in allowed list (spec.master)
126
+ if filename in ConfigService.ALLOWED_FILENAMES:
127
+ return True, "Valid"
128
+
129
+ # Check extension
130
+ if extension in ConfigService.ALLOWED_EXTENSIONS:
131
+ return True, "Valid"
132
+
133
+ return False, f"Invalid file type: only {', '.join(ConfigService.ALLOWED_EXTENSIONS)} and {', '.join(ConfigService.ALLOWED_FILENAMES)} are allowed"
134
+
135
+ @staticmethod
136
+ def check_file_size(path: Path) -> Tuple[bool, str]:
137
+ """
138
+ Check if a file size is within the allowed limit.
139
+
140
+ Returns: (is_valid, message)
141
+ """
142
+ try:
143
+ if not path.exists():
144
+ # For new files, this is ok
145
+ return True, "Valid"
146
+
147
+ size = path.stat().st_size
148
+ if size > ConfigService.MAX_FILE_SIZE:
149
+ return False, f"File too large: {size / (1024*1024):.1f}MB (max {ConfigService.MAX_FILE_SIZE / (1024*1024):.1f}MB)"
150
+
151
+ return True, "Valid"
152
+ except (OSError, IOError) as e:
153
+ return False, f"Cannot check file size: {e}"
154
+
155
+ @staticmethod
156
+ def compute_content_hash(content: str) -> str:
157
+ """
158
+ Compute SHA-256 hash of content for conflict detection.
159
+
160
+ Returns: Hex digest of the hash.
161
+ """
162
+ return hashlib.sha256(content.encode('utf-8')).hexdigest()
163
+
164
+ @staticmethod
165
+ def check_conflict(path: Path, original_hash: str) -> Tuple[bool, str]:
166
+ """
167
+ Check if file has been modified since it was loaded (conflict detection).
168
+
169
+ Uses SHA-256 content hash comparison instead of timestamps to avoid TOCTOU race conditions.
170
+
171
+ Args:
172
+ path: Path to the file
173
+ original_hash: SHA-256 hash of the content when it was loaded
174
+
175
+ Returns: (has_conflict, message)
176
+ - has_conflict: True if file has changed, False if it matches original_hash
177
+ - message: Human-readable description
178
+ """
179
+ try:
180
+ if not path.exists():
181
+ return True, "File has been deleted"
182
+
183
+ with open(path, 'r', encoding='utf-8', errors='replace') as f:
184
+ current_content = f.read()
185
+
186
+ current_hash = ConfigService.compute_content_hash(current_content)
187
+
188
+ if current_hash != original_hash:
189
+ return True, "File has been modified by another process"
190
+
191
+ return False, "No conflict detected"
192
+
193
+ except Exception as e:
194
+ return True, f"Cannot check for conflicts: {e}"
195
+
196
+ @staticmethod
197
+ def create_backup_with_rotation(file_path: Path, max_backups: int = MAX_BACKUPS) -> Tuple[Optional[Path], str]:
198
+ """
199
+ Create a backup of a file with rotation (keep only last N backups).
200
+
201
+ Backups are stored in a .backups/ subdirectory next to the file.
202
+ Old backups are automatically deleted when exceeding max_backups.
203
+
204
+ Args:
205
+ file_path: Path to the file to backup
206
+ max_backups: Maximum number of backups to keep (default: 3)
207
+
208
+ Returns: (backup_path, message)
209
+ - backup_path: Path to the created backup, or None if backup failed
210
+ - message: Description of what happened
211
+ """
212
+ try:
213
+ if not file_path.exists():
214
+ return None, "File does not exist, no backup needed"
215
+
216
+ # Create backup directory
217
+ backup_dir = file_path.parent / ConfigService.BACKUP_DIR_NAME
218
+ backup_dir.mkdir(exist_ok=True)
219
+
220
+ # Generate backup filename with timestamp
221
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
222
+ backup_name = f"{file_path.name}.{timestamp}.backup"
223
+ backup_path = backup_dir / backup_name
224
+
225
+ # Copy file to backup
226
+ import shutil
227
+ shutil.copy2(file_path, backup_path)
228
+
229
+ # Clean up old backups
230
+ existing_backups = sorted(
231
+ backup_dir.glob(f"{file_path.name}.*.backup"),
232
+ key=lambda p: p.stat().st_mtime,
233
+ reverse=True # Newest first
234
+ )
235
+
236
+ # Remove excess backups
237
+ removed_count = 0
238
+ for old_backup in existing_backups[max_backups:]:
239
+ old_backup.unlink()
240
+ removed_count += 1
241
+
242
+ msg = f"Backup created: {backup_path.name}"
243
+ if removed_count > 0:
244
+ msg += f" (removed {removed_count} old backup(s))"
245
+
246
+ return backup_path, msg
247
+
248
+ except Exception as e:
249
+ return None, f"Failed to create backup: {e}"
250
+
251
+ @staticmethod
252
+ def atomic_write(file_path: Path, content: str, original_hash: str = None,
253
+ create_backup: bool = True) -> SaveResult:
254
+ """
255
+ Atomically write content to a file with backup and conflict detection.
256
+
257
+ Uses write-to-temp-then-rename pattern for atomic writes.
258
+ If the write fails, the temp file is preserved for recovery.
259
+
260
+ Args:
261
+ file_path: Path to write to
262
+ content: Content to write
263
+ original_hash: If provided, check for conflicts before writing (SHA-256 hash)
264
+ create_backup: Whether to create a backup before writing (default: True)
265
+
266
+ Returns: SaveResult with success status, message, and details
267
+ """
268
+ temp_file = None
269
+
270
+ try:
271
+ # Validate file size
272
+ content_bytes = content.encode('utf-8')
273
+ if len(content_bytes) > ConfigService.MAX_FILE_SIZE:
274
+ return SaveResult(
275
+ success=False,
276
+ message=f"Content too large: {len(content_bytes) / (1024*1024):.1f}MB",
277
+ error_details=f"Maximum file size is {ConfigService.MAX_FILE_SIZE / (1024*1024):.1f}MB"
278
+ )
279
+
280
+ # Check for conflicts if hash provided
281
+ if original_hash and file_path.exists():
282
+ has_conflict, conflict_msg = ConfigService.check_conflict(file_path, original_hash)
283
+ if has_conflict:
284
+ return SaveResult(
285
+ success=False,
286
+ message=f"Conflict detected: {conflict_msg}",
287
+ error_details="The file has been modified since you loaded it. Please reload and try again."
288
+ )
289
+
290
+ # Create backup if requested and file exists
291
+ backup_path = None
292
+ backup_msg = ""
293
+ if create_backup and file_path.exists():
294
+ backup_path, backup_msg = ConfigService.create_backup_with_rotation(file_path)
295
+ if backup_path is None:
296
+ # Backup failed but we can still try to write
297
+ backup_msg = "Warning: Backup creation failed, but continuing with write"
298
+
299
+ # Atomic write: write to temp file, then rename
300
+ temp_fd, temp_path = tempfile.mkstemp(
301
+ dir=file_path.parent,
302
+ prefix=f".tmp_{file_path.name}_",
303
+ suffix='.tmp'
304
+ )
305
+ temp_file = Path(temp_path)
306
+
307
+ try:
308
+ # Write content to temp file
309
+ with os.fdopen(temp_fd, 'w', encoding='utf-8') as f:
310
+ f.write(content)
311
+
312
+ # Ensure data is written to disk
313
+ os.fsync(temp_fd)
314
+
315
+ # Atomic rename (POSIX guarantees this is atomic)
316
+ temp_file.rename(file_path)
317
+
318
+ # Sync parent directory to ensure rename is persisted
319
+ dir_fd = os.open(file_path.parent, os.O_RDONLY | os.O_DIRECTORY)
320
+ try:
321
+ os.fsync(dir_fd)
322
+ finally:
323
+ os.close(dir_fd)
324
+
325
+ success_msg = "File saved successfully"
326
+ if backup_msg:
327
+ success_msg += f". {backup_msg}"
328
+
329
+ return SaveResult(
330
+ success=True,
331
+ message=success_msg,
332
+ backup_path=backup_path
333
+ )
334
+
335
+ except Exception as e:
336
+ # If rename failed, temp file still exists for recovery
337
+ return SaveResult(
338
+ success=False,
339
+ message=f"Failed to write file: {e}",
340
+ error_details=f"Temp file preserved at: {temp_file}" if temp_file.exists() else None
341
+ )
342
+
343
+ except Exception as e:
344
+ return SaveResult(
345
+ success=False,
346
+ message=f"Unexpected error during save: {e}",
347
+ error_details=str(e)
348
+ )
349
+
350
+ finally:
351
+ # Clean up temp file if it still exists (shouldn't happen on success)
352
+ if temp_file and temp_file.exists():
353
+ try:
354
+ temp_file.unlink()
355
+ except (OSError, PermissionError):
356
+ pass # Best effort cleanup
357
+
358
+ @staticmethod
359
+ def validate_request_content_length(content_length: Optional[str]) -> Tuple[bool, str]:
360
+ """
361
+ Validate Content-Length header to prevent DoS attacks.
362
+
363
+ Returns: (is_valid, message)
364
+ """
365
+ if content_length is None:
366
+ return False, "Missing Content-Length header"
367
+
368
+ try:
369
+ length = int(content_length)
370
+ if length < 0:
371
+ return False, "Invalid Content-Length (negative)"
372
+ if length > ConfigService.MAX_CONTENT_LENGTH:
373
+ return False, f"Content too large: {length} bytes (max {ConfigService.MAX_CONTENT_LENGTH})"
374
+ return True, "Valid"
375
+ except ValueError:
376
+ return False, "Invalid Content-Length header"
377
+
378
+ @staticmethod
379
+ def validate_csrf_token(token: str, referer: Optional[str], origin: Optional[str]) -> Tuple[bool, str]:
380
+ """
381
+ Validate CSRF protection token.
382
+
383
+ Checks:
384
+ - Token is non-empty
385
+ - Referer or Origin header is from localhost
386
+
387
+ Returns: (is_valid, message)
388
+ """
389
+ if not token or len(token) < 8:
390
+ return False, "Invalid or missing CSRF token"
391
+
392
+ # Check that request comes from localhost
393
+ allowed_hosts = ['localhost', '127.0.0.1', '[::1]']
394
+
395
+ referer_valid = False
396
+ if referer:
397
+ referer_lower = referer.lower()
398
+ if any(host in referer_lower for host in allowed_hosts):
399
+ referer_valid = True
400
+
401
+ origin_valid = False
402
+ if origin:
403
+ origin_lower = origin.lower()
404
+ if any(host in origin_lower for host in allowed_hosts):
405
+ origin_valid = True
406
+
407
+ if not (referer_valid or origin_valid):
408
+ return False, "Invalid origin - request must come from localhost"
409
+
410
+ return True, "Valid"
411
+
412
+ @staticmethod
413
+ def generate_csrf_token() -> str:
414
+ """Generate a new CSRF token."""
415
+ import secrets
416
+ return secrets.token_urlsafe(32)
417
+
418
+
419
+ # Backwards compatibility: extend FileUtils with ConfigService methods
420
+ FileUtils.validate_write_path = staticmethod(ConfigService.validate_write_path)
421
+ FileUtils.validate_file_type = staticmethod(ConfigService.validate_file_type)
422
+ FileUtils.atomic_write = staticmethod(ConfigService.atomic_write)
423
+ FileUtils.create_backup_with_rotation = staticmethod(ConfigService.create_backup_with_rotation)
424
+ FileUtils.check_conflict = staticmethod(ConfigService.check_conflict)
425
+ FileUtils.compute_content_hash = staticmethod(ConfigService.compute_content_hash)
426
+
427
+
428
+ if __name__ == '__main__':
429
+ # Quick tests
430
+ print(f"Project root: {PROJECT_ROOT}")
431
+
432
+ # Test validate_write_path
433
+ test_paths = [
434
+ 'engine/topologies/tilt/discovery/registry.star',
435
+ 'TILT_RESOURCE_DEFAULTS.star',
436
+ 'spec.master',
437
+ 'engine/spec.master',
438
+ '../outside', # Should fail
439
+ '/etc/passwd', # Should fail
440
+ 'some/random/file.star', # Should fail - not in allowed paths
441
+ 'README.md', # Should fail - wrong extension
442
+ ]
443
+
444
+ print("\n=== Testing validate_write_path ===")
445
+ for path in test_paths:
446
+ result = ConfigService.validate_write_path(path)
447
+ status = "✅ VALID" if result else "❌ INVALID"
448
+ print(f"{status}: {path}")
449
+
450
+ # Test validate_file_type
451
+ print("\n=== Testing validate_file_type ===")
452
+ test_files = [
453
+ 'engine/topologies/tilt.star',
454
+ 'spec.master',
455
+ 'README.md',
456
+ 'file.txt',
457
+ ]
458
+ for path in test_files:
459
+ is_valid, msg = ConfigService.validate_file_type(path)
460
+ status = "✅" if is_valid else "❌"
461
+ print(f"{status}: {path} - {msg}")
462
+
463
+ # Test hash computation
464
+ print("\n=== Testing compute_content_hash ===")
465
+ test_content = "Hello, World!"
466
+ hash1 = ConfigService.compute_content_hash(test_content)
467
+ hash2 = ConfigService.compute_content_hash(test_content)
468
+ print(f"Content: '{test_content}'")
469
+ print(f"Hash 1: {hash1[:16]}...")
470
+ print(f"Hash 2: {hash2[:16]}...")
471
+ print(f"Hashes match: {hash1 == hash2}")