@flisk-sh/flisk 0.0.0-stage → 1.3.189

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-QWYXYAX6.js +510 -0
  170. package/cli/dist-bundle/cli.js +2017 -0
  171. package/cli/dist-bundle/cold-preflight-MM46GFQA.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,93 @@
1
+ // Copyright (c) 2026 Flisk Landscape contributors
2
+ // SPDX-License-Identifier: MIT
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { join, relative, resolve, sep } from "node:path";
5
+ import { PROJECT_JSON } from "./constants.js";
6
+ // Mirrors discovery/json_manifest_scanner.star: every `discovery.paths` entry is a shell
7
+ // glob of directories, and `find -maxdepth 3` looks for service.json under each match. So a
8
+ // resource is only seen by Tilt if some directory matching a pattern is the resource
9
+ // directory itself or at most this many levels above it.
10
+ const MAX_LEVELS_BELOW_MATCH = 2;
11
+ const DEFAULT_DISCOVERY_PATHS = ["services/*/*"];
12
+ function segments(path) {
13
+ return path.split(sep).filter(Boolean);
14
+ }
15
+ // Wildcard match without building a RegExp: `*` matches any run of characters, `?` one character,
16
+ // everything else is literal. Greedy with a single backtrack point, so it stays linear-ish on
17
+ // hostile patterns such as "a*a*a*b".
18
+ function segmentMatches(pattern, name) {
19
+ let p = 0;
20
+ let n = 0;
21
+ let starP = -1;
22
+ let starN = 0;
23
+ while (n < name.length) {
24
+ if (p < pattern.length &&
25
+ pattern[p] !== "*" &&
26
+ (pattern[p] === "?" || pattern[p] === name[n])) {
27
+ p++;
28
+ n++;
29
+ }
30
+ else if (p < pattern.length && pattern[p] === "*") {
31
+ starP = p++;
32
+ starN = n;
33
+ }
34
+ else if (starP !== -1) {
35
+ p = starP + 1;
36
+ n = ++starN;
37
+ }
38
+ else {
39
+ return false;
40
+ }
41
+ }
42
+ while (p < pattern.length && pattern[p] === "*")
43
+ p++;
44
+ return p === pattern.length;
45
+ }
46
+ function globMatchesDir(patternSegments, dirSegments) {
47
+ return (patternSegments.length === dirSegments.length &&
48
+ patternSegments.every((segment, index) => segmentMatches(segment, dirSegments[index])));
49
+ }
50
+ /** `discovery.paths` from .tdk/project.json, or the generic default when unset/unreadable. */
51
+ export function readDiscoveryPaths(projectRoot) {
52
+ const configPath = join(projectRoot, ".tdk", PROJECT_JSON);
53
+ if (!existsSync(configPath))
54
+ return DEFAULT_DISCOVERY_PATHS;
55
+ try {
56
+ const paths = JSON.parse(readFileSync(configPath, "utf-8"))?.discovery?.paths;
57
+ if (Array.isArray(paths) && paths.every((p) => typeof p === "string"))
58
+ return paths;
59
+ }
60
+ catch { }
61
+ return DEFAULT_DISCOVERY_PATHS;
62
+ }
63
+ /** Whether Tilt's discovery would find a service.json in `resourceDir` given `patterns`. */
64
+ export function isPathDiscovered(projectRoot, resourceDir, patterns) {
65
+ const resourceSegments = segments(relative(projectRoot, resolve(projectRoot, resourceDir)));
66
+ return patterns.some((pattern) => {
67
+ const patternSegments = segments(relative(projectRoot, resolve(projectRoot, pattern)));
68
+ for (let levels = 0; levels <= MAX_LEVELS_BELOW_MATCH; levels++) {
69
+ const depth = resourceSegments.length - levels;
70
+ if (depth > 0 && globMatchesDir(patternSegments, resourceSegments.slice(0, depth))) {
71
+ return true;
72
+ }
73
+ }
74
+ return false;
75
+ });
76
+ }
77
+ /**
78
+ * Picks the folder for a new resource without an explicit --path. The conventional folder
79
+ * per type (apps/, workers/, packages/) is kept when discovery covers it; otherwise the
80
+ * resource goes under services/<stack>/, which the default discovery paths cover, because a
81
+ * resource outside every discovery path is scaffolded but never started by `flisk up`.
82
+ */
83
+ export function chooseResourcePath(projectRoot, conventionalPath, stack, name) {
84
+ const patterns = readDiscoveryPaths(projectRoot);
85
+ if (isPathDiscovered(projectRoot, conventionalPath, patterns)) {
86
+ return { path: conventionalPath };
87
+ }
88
+ const fallback = `services/${stack}/${name}`;
89
+ if (isPathDiscovered(projectRoot, fallback, patterns)) {
90
+ return { path: fallback, adjustedFrom: conventionalPath };
91
+ }
92
+ return { path: conventionalPath };
93
+ }
@@ -0,0 +1,34 @@
1
+ // Copyright (c) 2026 Flisk Landscape contributors
2
+ // SPDX-License-Identifier: MIT
3
+ import { execFileSync, execSync } from "node:child_process";
4
+ import { findOnPath } from "./which.js";
5
+ // A wedged daemon makes `docker ps` block forever instead of failing.
6
+ const RUNTIME_CHECK_TIMEOUT_MS = 10_000;
7
+ /** Whether Docker, Colima, or Podman is reachable, hung, or absent. */
8
+ export function getContainerRuntimeStatus() {
9
+ let sawTimeout = false;
10
+ if (process.platform === "win32") {
11
+ try {
12
+ execFileSync(findOnPath("docker") ?? "docker", ["ps"], {
13
+ stdio: "ignore",
14
+ timeout: RUNTIME_CHECK_TIMEOUT_MS,
15
+ windowsHide: true,
16
+ });
17
+ return "running";
18
+ }
19
+ catch (err) {
20
+ return err.code === "ETIMEDOUT" ? "unresponsive" : "missing";
21
+ }
22
+ }
23
+ for (const command of ["docker ps", "colima status", "podman ps"]) {
24
+ try {
25
+ execSync(command, { stdio: "ignore", timeout: RUNTIME_CHECK_TIMEOUT_MS });
26
+ return "running";
27
+ }
28
+ catch (err) {
29
+ if (err.code === "ETIMEDOUT")
30
+ sawTimeout = true;
31
+ }
32
+ }
33
+ return sawTimeout ? "unresponsive" : "missing";
34
+ }
@@ -0,0 +1,161 @@
1
+ // Copyright (c) 2026 Flisk Landscape contributors
2
+ // SPDX-License-Identifier: MIT
3
+ import { execFile } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+ import { toComposeProjectPrefix } from "./doctor-runtime.js";
6
+ import { redactSecrets } from "./secret-redaction.js";
7
+ const execFilePromise = promisify(execFile);
8
+ const DOCKER_TIMEOUT_MS = 10_000;
9
+ /** Restarts that make a container a crash loop rather than a one-off. */
10
+ export const CRASH_LOOP_RESTARTS = 5;
11
+ /** A container that restarted this recently is still looping; an old count on a healthy container is history. */
12
+ const RECENT_START_MS = 10 * 60_000;
13
+ const LOG_LINES = 3;
14
+ const LOG_LINE_MAX = 200;
15
+ const runDocker = async (args) => {
16
+ const { stdout, stderr } = await execFilePromise("docker", args, {
17
+ encoding: "utf-8",
18
+ timeout: DOCKER_TIMEOUT_MS,
19
+ windowsHide: true,
20
+ });
21
+ return `${stdout}${stderr}`;
22
+ };
23
+ /** Whether this container's compose project belongs to the Flisk project, however the engine spelled the separator. */
24
+ export function belongsToProject(composeProject, projectName) {
25
+ const prefix = toComposeProjectPrefix(projectName).toLowerCase();
26
+ const label = composeProject.replace(/-/g, "_").toLowerCase();
27
+ return label === prefix || label.startsWith(`${prefix}_`);
28
+ }
29
+ export function isCrashLooping(state, now = Date.now()) {
30
+ if (state.status === "restarting")
31
+ return true;
32
+ if (state.restartCount < CRASH_LOOP_RESTARTS)
33
+ return false;
34
+ if (state.status === "running") {
35
+ const started = Date.parse(state.startedAt);
36
+ return Number.isNaN(started) || now - started < RECENT_START_MS;
37
+ }
38
+ // Stopped: only a recent failure is a loop that gave up (restart policy exhausted). A container stopped by hand
39
+ // keeps its old restart count, and that is history.
40
+ const finished = Date.parse(state.finishedAt ?? "");
41
+ return state.exitCode !== 0 && !Number.isNaN(finished) && now - finished < RECENT_START_MS;
42
+ }
43
+ /** A hint for a cause that the last log lines name. Null when they do not say. */
44
+ export function crashLoopHint(logs, state) {
45
+ const database = /database "([^"]+)" does not exist/i.exec(logs);
46
+ if (database) {
47
+ return `database "${database[1]}" does not exist: re-run the project's database provisioning (flisk up) or create it`;
48
+ }
49
+ if (/Cannot find module|MODULE_NOT_FOUND|module not found/i.test(logs)) {
50
+ return "a module is missing from the image: rebuild it (flisk up --force) after checking the resource's dependencies";
51
+ }
52
+ if (/EADDRINUSE|address already in use/i.test(logs)) {
53
+ return "the port inside the container is already in use: check for two processes started by the same command";
54
+ }
55
+ if (/ECONNREFUSED|connection refused|could not connect/i.test(logs)) {
56
+ return "a dependency is not reachable: check that the service it connects to is running and healthy";
57
+ }
58
+ if (/\b(?:environment variable|env var)\b.*\b(?:missing|not set|required|undefined)\b/i.test(logs) ||
59
+ /\b[A-Z][A-Z0-9_]{2,}\b (?:is )?(?:not set|required|missing)\b/.test(logs)) {
60
+ return "a required environment variable is missing: check the project's .env";
61
+ }
62
+ if (state.exitCode === 137)
63
+ return "killed (exit 137), usually out of memory: raise the container's memory limit";
64
+ return null;
65
+ }
66
+ function parseStates(output) {
67
+ const states = [];
68
+ for (const line of output.split(/\r?\n/)) {
69
+ const [name, restarts, status, startedAt, exitCode, finishedAt] = line.split("\t");
70
+ if (!name || restarts === undefined)
71
+ continue;
72
+ states.push({
73
+ name: name.replace(/^\//, ""),
74
+ restartCount: Number(restarts) || 0,
75
+ status: status ?? "",
76
+ startedAt: startedAt ?? "",
77
+ exitCode: Number(exitCode) || 0,
78
+ finishedAt: finishedAt ?? "",
79
+ });
80
+ }
81
+ return states;
82
+ }
83
+ /** Containers of this project that keep restarting, with their last log lines and a cause hint when one is clear. */
84
+ export async function checkCrashLoopingContainers(projectName, secrets = [], run = runDocker, now = Date.now()) {
85
+ const name = "Crash-looping containers";
86
+ let candidates;
87
+ try {
88
+ const listing = await run([
89
+ "ps",
90
+ "-a",
91
+ "--format",
92
+ '{{.ID}}\t{{.Label "com.docker.compose.project"}}',
93
+ ]);
94
+ candidates = listing
95
+ .split(/\r?\n/)
96
+ .map((line) => line.split("\t"))
97
+ .filter(([id, project]) => id && project && belongsToProject(project, projectName))
98
+ .map(([id]) => id);
99
+ }
100
+ catch {
101
+ return {
102
+ name,
103
+ didPass: true,
104
+ isSkipped: true,
105
+ message: "Docker not available - skipped crash-loop check",
106
+ };
107
+ }
108
+ if (candidates.length === 0) {
109
+ return { name, didPass: true, isSkipped: true, message: "No project containers to check" };
110
+ }
111
+ let states;
112
+ try {
113
+ states = parseStates(await run([
114
+ "inspect",
115
+ "--format",
116
+ "{{.Name}}\t{{.RestartCount}}\t{{.State.Status}}\t{{.State.StartedAt}}\t{{.State.ExitCode}}\t{{.State.FinishedAt}}",
117
+ ...candidates,
118
+ ]));
119
+ }
120
+ catch {
121
+ // A container removed between ps and inspect fails the whole batch; that is not a finding.
122
+ return {
123
+ name,
124
+ didPass: true,
125
+ isSkipped: true,
126
+ message: "Containers changed while checking - skipped crash-loop check",
127
+ };
128
+ }
129
+ const looping = states.filter((state) => isCrashLooping(state, now));
130
+ if (looping.length === 0) {
131
+ return { name, didPass: true, message: "No project container is crash-looping" };
132
+ }
133
+ const details = [];
134
+ const hints = new Set();
135
+ for (const state of looping) {
136
+ let logs = "";
137
+ try {
138
+ logs = await run(["logs", "--tail", String(LOG_LINES), state.name]);
139
+ }
140
+ catch {
141
+ // The container may have just been removed; report it without logs.
142
+ }
143
+ const lines = redactSecrets(logs, secrets)
144
+ .split(/\r?\n/)
145
+ .map((line) => line.trim().slice(0, LOG_LINE_MAX))
146
+ .filter(Boolean);
147
+ const hint = crashLoopHint(logs, state);
148
+ if (hint)
149
+ hints.add(hint);
150
+ details.push([
151
+ `${state.name}: ${state.status}, restarted ${state.restartCount} times`,
152
+ ...lines.map((line) => ` | ${line}`),
153
+ ].join("\n "));
154
+ }
155
+ return {
156
+ name,
157
+ didPass: false,
158
+ message: `${looping.length} container${looping.length === 1 ? " is" : "s are"} crash-looping:\n ${details.join("\n ")}`,
159
+ fix: [...hints, "Read the full log: docker logs <container>"].join("; "),
160
+ };
161
+ }
@@ -0,0 +1,391 @@
1
+ // Copyright (c) 2026 Flisk Landscape contributors
2
+ // SPDX-License-Identifier: MIT
3
+ import { execFile } from "node:child_process";
4
+ import { realpathSync, statfsSync } from "node:fs";
5
+ import { homedir } from "node:os";
6
+ import { join } from "node:path";
7
+ import { promisify } from "node:util";
8
+ import { DISK_WARN_GIB } from "./doctor-host.js";
9
+ import { findProjectTiltUpPids, projectNetworkNames, tdkNetworkOwner, tdkNetworkPrefixes, } from "./down-cleanup.js";
10
+ import { executableName, legacyExecutableName } from "./platform.js";
11
+ import { tiltGetUiResources } from "./up-readiness.js";
12
+ import { findAllOnPath } from "./which.js";
13
+ const execFilePromise = promisify(execFile);
14
+ const DOCKER_TIMEOUT_MS = 15_000;
15
+ /** The networks `flisk doctor` creates to test capacity; an interrupted run leaves one behind. */
16
+ const PROBE_NETWORK = /^flisk_doctor_probe_(\d+)$/;
17
+ const runDocker = async (args) => {
18
+ const { stdout } = await execFilePromise("docker", args, {
19
+ encoding: "utf-8",
20
+ timeout: DOCKER_TIMEOUT_MS,
21
+ windowsHide: true,
22
+ });
23
+ return stdout;
24
+ };
25
+ function firstLine(error) {
26
+ const stderr = String(error.stderr ?? error).trim();
27
+ return stderr.split(/\r?\n/)[0] ?? "";
28
+ }
29
+ const STALE_TILT_MIN_AGE_S = 120;
30
+ /** `ps -o etime=` ("[[dd-]hh:]mm:ss") in seconds. */
31
+ export function parseElapsed(etime) {
32
+ const match = /^\s*(?:(?:(\d+)-)?(\d+):)?(\d+):(\d+)\s*$/.exec(etime);
33
+ if (!match)
34
+ return null;
35
+ const [, days = "0", hours = "0", minutes = "0", seconds = "0"] = match;
36
+ return ((Number(days) * 24 + Number(hours)) * 60 + Number(minutes)) * 60 + Number(seconds);
37
+ }
38
+ /**
39
+ * Whether any of `ports` answers as a Tilt UI. A busy Tilt can miss one request, and a wrong "stale" ends in a kill,
40
+ * so a port gets a second try before it counts as silent.
41
+ */
42
+ export async function anyTiltAnswers(ports, probe = tiltGetUiResources, attempts = 2, wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
43
+ for (let attempt = 0; attempt < attempts; attempt++) {
44
+ if (attempt > 0)
45
+ await wait(1000);
46
+ for (const port of ports) {
47
+ if ((await probe(port)) !== null)
48
+ return true;
49
+ }
50
+ }
51
+ return false;
52
+ }
53
+ async function tiltProcessStatus(pid) {
54
+ if (process.platform === "win32")
55
+ return "unknown";
56
+ try {
57
+ const { stdout: etime } = await execFilePromise("ps", ["-p", String(pid), "-o", "etime="], {
58
+ encoding: "utf-8",
59
+ timeout: 5000,
60
+ });
61
+ const age = parseElapsed(etime);
62
+ // A Tilt that is still starting has not bound its port yet.
63
+ if (age === null || age < STALE_TILT_MIN_AGE_S)
64
+ return "unknown";
65
+ const { stdout } = await execFilePromise("lsof", ["-nP", "-a", "-p", String(pid), "-iTCP", "-sTCP:LISTEN", "-Fn"], { encoding: "utf-8", timeout: 5000 });
66
+ const ports = [...stdout.matchAll(/^n.*:(\d+)$/gm)].map((m) => Number(m[1]));
67
+ return (await anyTiltAnswers(ports)) ? "healthy" : "stale";
68
+ }
69
+ catch {
70
+ // No lsof, or the process is gone: do not guess.
71
+ return "unknown";
72
+ }
73
+ }
74
+ export function createDockerFixDeps(run = runDocker) {
75
+ return {
76
+ async listNetworks() {
77
+ const names = (await run(["network", "ls", "--format", "{{.Name}}"]))
78
+ .split(/\r?\n/)
79
+ .map((line) => line.trim())
80
+ .filter(Boolean);
81
+ // One inspect per network: another process (a doctor probe, `flisk down`) can remove one mid-listing, and a
82
+ // batched inspect would then fail for all of them.
83
+ const inspected = await Promise.all(names.map(async (name) => {
84
+ try {
85
+ const count = await run([
86
+ "network",
87
+ "inspect",
88
+ "--format",
89
+ "{{len .Containers}}",
90
+ name,
91
+ ]);
92
+ return { name, containers: Number(count.trim()) || 0 };
93
+ }
94
+ catch (error) {
95
+ if (/no such network|not found/i.test(String(error)))
96
+ return null;
97
+ throw error;
98
+ }
99
+ }));
100
+ return inspected.filter((network) => network !== null);
101
+ },
102
+ async removeNetwork(name) {
103
+ try {
104
+ await run(["network", "rm", name]);
105
+ }
106
+ catch (error) {
107
+ throw new Error(firstLine(error));
108
+ }
109
+ },
110
+ freeDiskGib() {
111
+ try {
112
+ const stats = statfsSync(homedir());
113
+ return (Number(stats.bavail) * Number(stats.bsize)) / 1024 ** 3;
114
+ }
115
+ catch {
116
+ return null;
117
+ }
118
+ },
119
+ async reclaimable() {
120
+ let buildCache = null;
121
+ try {
122
+ const df = await run(["system", "df", "--format", "{{.Type}}\t{{.Reclaimable}}"]);
123
+ for (const line of df.split(/\r?\n/)) {
124
+ const [type, size] = line.split("\t");
125
+ // "1.531GB" or "1.5GB (39%)"; the percentage is of the whole, not useful here.
126
+ if (type === "Build Cache" && size)
127
+ buildCache = size.replace(/\s*\(.*\)/, "").trim();
128
+ }
129
+ }
130
+ catch {
131
+ // Leave unknown.
132
+ }
133
+ let danglingImages = 0;
134
+ try {
135
+ const ids = await run(["images", "-q", "-f", "dangling=true"]);
136
+ danglingImages = ids.split(/\r?\n/).filter(Boolean).length;
137
+ }
138
+ catch {
139
+ // Leave zero.
140
+ }
141
+ return { buildCache, danglingImages };
142
+ },
143
+ async prune(kind) {
144
+ try {
145
+ // Never `volume prune`, never `-a`: only the build cache and untagged images.
146
+ await run(kind === "builder" ? ["builder", "prune", "-f"] : ["image", "prune", "-f"]);
147
+ }
148
+ catch (error) {
149
+ throw new Error(firstLine(error));
150
+ }
151
+ },
152
+ async tiltStatus(pid) {
153
+ return tiltProcessStatus(pid);
154
+ },
155
+ stopProcess(pid) {
156
+ process.kill(pid, "SIGTERM");
157
+ },
158
+ isProcessAlive(pid) {
159
+ try {
160
+ process.kill(pid, 0);
161
+ return true;
162
+ }
163
+ catch (error) {
164
+ // EPERM means the process exists but belongs to someone else.
165
+ return error.code === "EPERM";
166
+ }
167
+ },
168
+ };
169
+ }
170
+ export const dockerFixDeps = createDockerFixDeps();
171
+ /**
172
+ * What `--fix` would do: stop this project's stale Tilt; remove unused networks that are a leftover doctor probe or
173
+ * this project's own (the exact names the engine creates, skipped while its Tilt runs). Other Flisk projects' unused
174
+ * networks are planned only with `context.machine`, each naming its owner.
175
+ */
176
+ export async function planDoctorFixes(projectName, deps = dockerFixDeps, context = {}) {
177
+ const actions = [];
178
+ // A Tilt that has run a while without serving its UI is stale and may be stopped; any other Tilt of this project is
179
+ // running or starting, and its networks are not to be touched.
180
+ const liveTilt = [];
181
+ for (const pid of context.tiltPids ?? []) {
182
+ if ((await deps.tiltStatus(pid)) === "stale") {
183
+ actions.push({
184
+ id: `stop-tilt:${pid}`,
185
+ check: "Tilt processes",
186
+ scope: "project",
187
+ command: `kill -TERM ${pid}`,
188
+ status: "planned",
189
+ reason: "this project's Tilt is running but its UI does not answer",
190
+ });
191
+ }
192
+ else {
193
+ liveTilt.push(pid);
194
+ }
195
+ }
196
+ const projectBusy = liveTilt.length > 0
197
+ ? `Tilt is running for this project (pid ${liveTilt.join(", ")}); run flisk down first`
198
+ : undefined;
199
+ const networks = await deps.listNetworks();
200
+ const freeGib = deps.freeDiskGib();
201
+ if (freeGib !== null && freeGib < DISK_WARN_GIB) {
202
+ const { buildCache, danglingImages } = await deps.reclaimable();
203
+ const low = `${freeGib.toFixed(1)} GB free`;
204
+ if (buildCache && !/^0(\.0+)?\s*B$/i.test(buildCache)) {
205
+ actions.push({
206
+ id: "prune:builder",
207
+ check: "Disk Space",
208
+ scope: "host",
209
+ command: "docker builder prune -f",
210
+ status: "planned",
211
+ reason: `${low}; ${buildCache} of build cache reclaimable`,
212
+ });
213
+ }
214
+ if (danglingImages > 0) {
215
+ actions.push({
216
+ id: "prune:images",
217
+ check: "Disk Space",
218
+ scope: "host",
219
+ command: "docker image prune -f",
220
+ status: "planned",
221
+ reason: `${low}; ${danglingImages} untagged image${danglingImages === 1 ? "" : "s"}`,
222
+ });
223
+ }
224
+ }
225
+ const own = new Set(projectName ? projectNetworkNames(projectName) : []);
226
+ const prefixes = tdkNetworkPrefixes(networks.map((network) => network.name));
227
+ for (const network of networks) {
228
+ const probe = PROBE_NETWORK.exec(network.name);
229
+ let scope;
230
+ let check;
231
+ let reason = "unused network";
232
+ if (probe) {
233
+ scope = "probe";
234
+ check = "Orphaned probe networks";
235
+ }
236
+ else if (own.has(network.name)) {
237
+ scope = "project";
238
+ check = "Docker networks";
239
+ }
240
+ else if (context.machine && tdkNetworkOwner(network.name, prefixes)) {
241
+ scope = "machine";
242
+ check = "Docker networks";
243
+ reason = `unused network of project "${tdkNetworkOwner(network.name, prefixes)}"`;
244
+ }
245
+ else {
246
+ continue;
247
+ }
248
+ const action = {
249
+ id: `remove-network:${network.name}`,
250
+ check,
251
+ scope,
252
+ command: `docker network rm ${network.name}`,
253
+ status: "planned",
254
+ reason,
255
+ };
256
+ if (network.containers > 0) {
257
+ action.status = "skipped";
258
+ action.reason = `${network.containers} container(s) still attached`;
259
+ }
260
+ else if (scope === "project" && projectBusy) {
261
+ // A starting stack creates its networks before any container joins them, so "unused" is not safe to remove.
262
+ action.status = "skipped";
263
+ action.reason = projectBusy;
264
+ }
265
+ else if (probe && deps.isProcessAlive(Number(probe[1]))) {
266
+ // The doctor run that created it may still be using it.
267
+ action.status = "skipped";
268
+ action.reason = `probe of a running process (pid ${probe[1]})`;
269
+ }
270
+ actions.push(action);
271
+ }
272
+ return actions;
273
+ }
274
+ /** Run the planned actions in order. A declined or failed action never stops the rest, so a re-run converges. */
275
+ export async function applyDoctorFixes(actions, options, deps = dockerFixDeps) {
276
+ const results = [];
277
+ for (const action of actions) {
278
+ if (action.status !== "planned" || options.dryRun) {
279
+ results.push(action);
280
+ continue;
281
+ }
282
+ if (!options.confirm) {
283
+ results.push({
284
+ ...action,
285
+ status: "skipped",
286
+ reason: "confirmation required; run in a terminal or pass --yes",
287
+ });
288
+ continue;
289
+ }
290
+ let confirmed = false;
291
+ try {
292
+ confirmed = await options.confirm(action);
293
+ }
294
+ catch {
295
+ // No terminal to ask on.
296
+ }
297
+ if (!confirmed) {
298
+ results.push({ ...action, status: "skipped", reason: "not confirmed" });
299
+ continue;
300
+ }
301
+ try {
302
+ if (action.id.startsWith("prune:")) {
303
+ await deps.prune(action.id === "prune:builder" ? "builder" : "images");
304
+ results.push({ ...action, status: "applied", reason: "pruned" });
305
+ continue;
306
+ }
307
+ if (action.id.startsWith("stop-tilt:")) {
308
+ deps.stopProcess(Number(action.id.slice("stop-tilt:".length)));
309
+ results.push({ ...action, status: "applied", reason: "stopped" });
310
+ continue;
311
+ }
312
+ await deps.removeNetwork(action.id.slice("remove-network:".length));
313
+ results.push({ ...action, status: "applied", reason: "removed" });
314
+ }
315
+ catch (error) {
316
+ results.push({ ...action, status: "failed", reason: firstLine(error) });
317
+ }
318
+ }
319
+ return results;
320
+ }
321
+ /** Whether any action leaves a problem behind that a re-run could still repair. */
322
+ export function hasFailedActions(actions) {
323
+ return actions.some((action) => action.status === "failed");
324
+ }
325
+ async function readTdkVersion(path) {
326
+ try {
327
+ const { stdout } = await execFilePromise(path, ["--version"], {
328
+ encoding: "utf-8",
329
+ timeout: 5000,
330
+ windowsHide: true,
331
+ });
332
+ return stdout.trim().split(/\r?\n/)[0] || null;
333
+ }
334
+ catch {
335
+ return null;
336
+ }
337
+ }
338
+ /** Every distinct Flisk install on PATH (symlinks to one binary count once), in PATH order. */
339
+ export async function findTdkInstalls(paths = [...findAllOnPath(executableName()), ...findAllOnPath(legacyExecutableName())], readVersion = readTdkVersion) {
340
+ const seen = new Set();
341
+ const installs = [];
342
+ for (const path of paths) {
343
+ let real = path;
344
+ try {
345
+ real = realpathSync(path);
346
+ }
347
+ catch {
348
+ // Keep the PATH entry as is.
349
+ }
350
+ if (seen.has(real))
351
+ continue;
352
+ seen.add(real);
353
+ installs.push({ path, version: await readVersion(path) });
354
+ }
355
+ return installs;
356
+ }
357
+ /** Two `flisk` binaries can disagree (a dashboard or MCP client may launch the older one), so say which is which. */
358
+ export async function checkDuplicateTdkInstalls(installs) {
359
+ const found = installs ?? (await findTdkInstalls());
360
+ if (found.length <= 1) {
361
+ return { name: "Flisk installs", didPass: true, message: "One flisk install on PATH" };
362
+ }
363
+ const list = found.map((i) => `${i.path} (${i.version ?? "version unknown"})`).join(", ");
364
+ const versions = new Set(found.map((i) => i.version));
365
+ return {
366
+ name: "Flisk installs",
367
+ didPass: false,
368
+ isWarning: true,
369
+ message: `${found.length} flisk installs on PATH${versions.size > 1 ? " with different versions" : ""}: ${list}`,
370
+ fix: "Keep one: remove the others, or run flisk upgrade. The first on PATH is the one your shell runs; tools that launch /usr/local/bin/flisk directly may use another",
371
+ };
372
+ }
373
+ /**
374
+ * PIDs of `tilt up` processes for this project. The process list shows the Tiltfile path as it was typed at launch,
375
+ * which may or may not be the symlink-resolved path, so both spellings are looked up.
376
+ */
377
+ export function projectTiltPids(projectRoot, find = findProjectTiltUpPids) {
378
+ const roots = new Set([projectRoot]);
379
+ try {
380
+ roots.add(realpathSync(projectRoot));
381
+ }
382
+ catch {
383
+ // Keep the path as given.
384
+ }
385
+ const pids = new Set();
386
+ for (const root of roots) {
387
+ for (const pid of find(join(root, ".tdk", ".tdk-out", "Tiltfile")))
388
+ pids.add(pid);
389
+ }
390
+ return [...pids];
391
+ }