@tdk-landscape/tdk-cli-core 1.3.49 → 1.3.50

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 (309) hide show
  1. package/.claude/skills/layer-autoresearch/SKILL.md +48 -0
  2. package/.claude/skills/tdk-troubleshoot/SKILL.md +222 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.yml +40 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.yml +18 -0
  6. package/.github/dependabot.yml +33 -0
  7. package/.github/pull_request_template.md +5 -0
  8. package/.github/workflows/ci.yml +187 -0
  9. package/.github/workflows/release-binaries.yml +19 -9
  10. package/.github/zizmor.yml +9 -0
  11. package/CODE_OF_CONDUCT.md +9 -0
  12. package/CONTRIBUTING.md +42 -0
  13. package/LICENSE +21 -0
  14. package/README.md +149 -0
  15. package/SECURITY.md +17 -0
  16. package/benchmarks/results/container-scale-2026-09-25T20-54-49-736Z.json +45 -0
  17. package/benchmarks/results/container-scale-2026-09-25T21-01-20-540Z.json +150 -0
  18. package/benchmarks/results/container-scale-2026-09-25T21-15-01-441Z.json +73 -0
  19. package/benchmarks/results/container-scale-2026-09-25T22-09-01-630Z.json +77 -0
  20. package/benchmarks/results/container-scale-2026-09-25T22-18-28-371Z.json +202 -0
  21. package/cli/README.md +25 -0
  22. package/cli/biome.json +1 -1
  23. package/cli/dist/cli.d.ts +3 -0
  24. package/cli/dist/cli.d.ts.map +1 -0
  25. package/cli/dist/cli.js +64 -0
  26. package/cli/dist/cli.js.map +1 -0
  27. package/cli/dist/commands/completion.d.ts +3 -0
  28. package/cli/dist/commands/completion.d.ts.map +1 -0
  29. package/cli/dist/commands/completion.js +294 -0
  30. package/cli/dist/commands/completion.js.map +1 -0
  31. package/cli/dist/commands/config.d.ts +3 -0
  32. package/cli/dist/commands/config.d.ts.map +1 -0
  33. package/cli/dist/commands/config.js +231 -0
  34. package/cli/dist/commands/config.js.map +1 -0
  35. package/cli/dist/commands/doctor.d.ts +19 -0
  36. package/cli/dist/commands/doctor.d.ts.map +1 -0
  37. package/cli/dist/commands/doctor.js +745 -0
  38. package/cli/dist/commands/doctor.js.map +1 -0
  39. package/cli/dist/commands/down.d.ts +3 -0
  40. package/cli/dist/commands/down.d.ts.map +1 -0
  41. package/cli/dist/commands/down.js +32 -0
  42. package/cli/dist/commands/down.js.map +1 -0
  43. package/cli/dist/commands/help.d.ts +2 -0
  44. package/cli/dist/commands/help.d.ts.map +1 -0
  45. package/cli/dist/commands/help.js +138 -0
  46. package/cli/dist/commands/help.js.map +1 -0
  47. package/cli/dist/commands/networks.d.ts +3 -0
  48. package/cli/dist/commands/networks.d.ts.map +1 -0
  49. package/cli/dist/commands/networks.js +260 -0
  50. package/cli/dist/commands/networks.js.map +1 -0
  51. package/cli/dist/commands/project.d.ts +3 -0
  52. package/cli/dist/commands/project.d.ts.map +1 -0
  53. package/cli/dist/commands/project.js +370 -0
  54. package/cli/dist/commands/project.js.map +1 -0
  55. package/cli/dist/commands/projects.d.ts +3 -0
  56. package/cli/dist/commands/projects.d.ts.map +1 -0
  57. package/cli/dist/commands/projects.js +71 -0
  58. package/cli/dist/commands/projects.js.map +1 -0
  59. package/cli/dist/commands/resource.d.ts +69 -0
  60. package/cli/dist/commands/resource.d.ts.map +1 -0
  61. package/cli/dist/commands/resource.js +579 -0
  62. package/cli/dist/commands/resource.js.map +1 -0
  63. package/cli/dist/commands/resources.d.ts +3 -0
  64. package/cli/dist/commands/resources.d.ts.map +1 -0
  65. package/cli/dist/commands/resources.js +73 -0
  66. package/cli/dist/commands/resources.js.map +1 -0
  67. package/cli/dist/commands/stack.d.ts +3 -0
  68. package/cli/dist/commands/stack.d.ts.map +1 -0
  69. package/cli/dist/commands/stack.js +97 -0
  70. package/cli/dist/commands/stack.js.map +1 -0
  71. package/cli/dist/commands/stacks.d.ts +3 -0
  72. package/cli/dist/commands/stacks.d.ts.map +1 -0
  73. package/cli/dist/commands/stacks.js +43 -0
  74. package/cli/dist/commands/stacks.js.map +1 -0
  75. package/cli/dist/commands/status.d.ts +3 -0
  76. package/cli/dist/commands/status.d.ts.map +1 -0
  77. package/cli/dist/commands/status.js +71 -0
  78. package/cli/dist/commands/status.js.map +1 -0
  79. package/cli/dist/commands/ui.d.ts +3 -0
  80. package/cli/dist/commands/ui.d.ts.map +1 -0
  81. package/cli/dist/commands/ui.js +445 -0
  82. package/cli/dist/commands/ui.js.map +1 -0
  83. package/cli/dist/commands/up.d.ts +3 -0
  84. package/cli/dist/commands/up.d.ts.map +1 -0
  85. package/cli/dist/commands/up.js +124 -0
  86. package/cli/dist/commands/up.js.map +1 -0
  87. package/cli/dist/commands/upgrade.d.ts +11 -0
  88. package/cli/dist/commands/upgrade.d.ts.map +1 -0
  89. package/cli/dist/commands/upgrade.js +496 -0
  90. package/cli/dist/commands/upgrade.js.map +1 -0
  91. package/cli/dist/commands/version.d.ts +3 -0
  92. package/cli/dist/commands/version.d.ts.map +1 -0
  93. package/cli/dist/commands/version.js +9 -0
  94. package/cli/dist/commands/version.js.map +1 -0
  95. package/cli/dist/components/Accessible.d.ts +8 -0
  96. package/cli/dist/components/Accessible.d.ts.map +1 -0
  97. package/cli/dist/components/Accessible.js +10 -0
  98. package/cli/dist/components/Accessible.js.map +1 -0
  99. package/cli/dist/components/BaseTooltip.d.ts +4 -0
  100. package/cli/dist/components/BaseTooltip.d.ts.map +1 -0
  101. package/cli/dist/components/BaseTooltip.js +28 -0
  102. package/cli/dist/components/BaseTooltip.js.map +1 -0
  103. package/cli/dist/components/DetailPanel.d.ts +4 -0
  104. package/cli/dist/components/DetailPanel.d.ts.map +1 -0
  105. package/cli/dist/components/DetailPanel.js +18 -0
  106. package/cli/dist/components/DetailPanel.js.map +1 -0
  107. package/cli/dist/components/FileTree.d.ts +4 -0
  108. package/cli/dist/components/FileTree.d.ts.map +1 -0
  109. package/cli/dist/components/FileTree.js +47 -0
  110. package/cli/dist/components/FileTree.js.map +1 -0
  111. package/cli/dist/components/ResourceSelectInput.d.ts +4 -0
  112. package/cli/dist/components/ResourceSelectInput.d.ts.map +1 -0
  113. package/cli/dist/components/ResourceSelectInput.js +7 -0
  114. package/cli/dist/components/ResourceSelectInput.js.map +1 -0
  115. package/cli/dist/components/ResourceTable.d.ts +4 -0
  116. package/cli/dist/components/ResourceTable.d.ts.map +1 -0
  117. package/cli/dist/components/ResourceTable.js +18 -0
  118. package/cli/dist/components/ResourceTable.js.map +1 -0
  119. package/cli/dist/components/TabBar.d.ts +4 -0
  120. package/cli/dist/components/TabBar.d.ts.map +1 -0
  121. package/cli/dist/components/TabBar.js +17 -0
  122. package/cli/dist/components/TabBar.js.map +1 -0
  123. package/cli/dist/components/Tooltip.d.ts +12 -0
  124. package/cli/dist/components/Tooltip.d.ts.map +1 -0
  125. package/cli/dist/components/Tooltip.js +12 -0
  126. package/cli/dist/components/Tooltip.js.map +1 -0
  127. package/cli/dist/components/index.d.ts +8 -0
  128. package/cli/dist/components/index.d.ts.map +1 -0
  129. package/cli/dist/components/index.js +8 -0
  130. package/cli/dist/components/index.js.map +1 -0
  131. package/cli/dist/config/platform-standards.d.ts +159 -0
  132. package/cli/dist/config/platform-standards.d.ts.map +1 -0
  133. package/cli/dist/config/platform-standards.js +181 -0
  134. package/cli/dist/config/platform-standards.js.map +1 -0
  135. package/cli/dist/generator/extension-fetch.d.ts +25 -0
  136. package/cli/dist/generator/extension-fetch.d.ts.map +1 -0
  137. package/cli/dist/generator/extension-fetch.js +259 -0
  138. package/cli/dist/generator/extension-fetch.js.map +1 -0
  139. package/cli/dist/generator/template-engine.d.ts +62 -0
  140. package/cli/dist/generator/template-engine.d.ts.map +1 -0
  141. package/cli/dist/generator/template-engine.js +623 -0
  142. package/cli/dist/generator/template-engine.js.map +1 -0
  143. package/cli/dist/index.d.ts +11 -0
  144. package/cli/dist/index.d.ts.map +1 -0
  145. package/cli/dist/index.js +11 -0
  146. package/cli/dist/index.js.map +1 -0
  147. package/cli/dist/types/index.d.ts +257 -0
  148. package/cli/dist/types/index.d.ts.map +1 -0
  149. package/cli/dist/types/index.js +17 -0
  150. package/cli/dist/types/index.js.map +1 -0
  151. package/cli/dist/utils/cache.d.ts +19 -0
  152. package/cli/dist/utils/cache.d.ts.map +1 -0
  153. package/cli/dist/utils/cache.js +64 -0
  154. package/cli/dist/utils/cache.js.map +1 -0
  155. package/cli/dist/utils/command-helpers.d.ts +17 -0
  156. package/cli/dist/utils/command-helpers.d.ts.map +1 -0
  157. package/cli/dist/utils/command-helpers.js +41 -0
  158. package/cli/dist/utils/command-helpers.js.map +1 -0
  159. package/cli/dist/utils/constants.d.ts +53 -0
  160. package/cli/dist/utils/constants.d.ts.map +1 -0
  161. package/cli/dist/utils/constants.js +115 -0
  162. package/cli/dist/utils/constants.js.map +1 -0
  163. package/cli/dist/utils/discovery-context.d.ts +4 -0
  164. package/cli/dist/utils/discovery-context.d.ts.map +1 -0
  165. package/cli/dist/utils/discovery-context.js +39 -0
  166. package/cli/dist/utils/discovery-context.js.map +1 -0
  167. package/cli/dist/utils/docker.d.ts +4 -0
  168. package/cli/dist/utils/docker.d.ts.map +1 -0
  169. package/cli/dist/utils/docker.js +19 -0
  170. package/cli/dist/utils/docker.js.map +1 -0
  171. package/cli/dist/utils/doctor-runtime.d.ts +81 -0
  172. package/cli/dist/utils/doctor-runtime.d.ts.map +1 -0
  173. package/cli/dist/utils/doctor-runtime.js +498 -0
  174. package/cli/dist/utils/doctor-runtime.js.map +1 -0
  175. package/cli/dist/utils/env-validator.d.ts +15 -0
  176. package/cli/dist/utils/env-validator.d.ts.map +1 -0
  177. package/cli/dist/utils/env-validator.js +109 -0
  178. package/cli/dist/utils/env-validator.js.map +1 -0
  179. package/cli/dist/utils/errors.d.ts +31 -0
  180. package/cli/dist/utils/errors.d.ts.map +1 -0
  181. package/cli/dist/utils/errors.js +140 -0
  182. package/cli/dist/utils/errors.js.map +1 -0
  183. package/cli/dist/utils/file-helpers.d.ts +14 -0
  184. package/cli/dist/utils/file-helpers.d.ts.map +1 -0
  185. package/cli/dist/utils/file-helpers.js +45 -0
  186. package/cli/dist/utils/file-helpers.js.map +1 -0
  187. package/cli/dist/utils/formatting.d.ts +30 -0
  188. package/cli/dist/utils/formatting.d.ts.map +1 -0
  189. package/cli/dist/utils/formatting.js +164 -0
  190. package/cli/dist/utils/formatting.js.map +1 -0
  191. package/cli/dist/utils/paths.d.ts +3 -0
  192. package/cli/dist/utils/paths.d.ts.map +1 -0
  193. package/cli/dist/utils/paths.js +41 -0
  194. package/cli/dist/utils/paths.js.map +1 -0
  195. package/cli/dist/utils/port-assignment.d.ts +6 -0
  196. package/cli/dist/utils/port-assignment.d.ts.map +1 -0
  197. package/cli/dist/utils/port-assignment.js +81 -0
  198. package/cli/dist/utils/port-assignment.js.map +1 -0
  199. package/cli/dist/utils/project-features.d.ts +23 -0
  200. package/cli/dist/utils/project-features.d.ts.map +1 -0
  201. package/cli/dist/utils/project-features.js +98 -0
  202. package/cli/dist/utils/project-features.js.map +1 -0
  203. package/cli/dist/utils/project-templates.d.ts +7 -0
  204. package/cli/dist/utils/project-templates.d.ts.map +1 -0
  205. package/cli/dist/utils/project-templates.js +24 -0
  206. package/cli/dist/utils/project-templates.js.map +1 -0
  207. package/cli/dist/utils/prompt.d.ts +28 -0
  208. package/cli/dist/utils/prompt.d.ts.map +1 -0
  209. package/cli/dist/utils/prompt.js +79 -0
  210. package/cli/dist/utils/prompt.js.map +1 -0
  211. package/cli/dist/utils/resource-features.d.ts +29 -0
  212. package/cli/dist/utils/resource-features.d.ts.map +1 -0
  213. package/cli/dist/utils/resource-features.js +175 -0
  214. package/cli/dist/utils/resource-features.js.map +1 -0
  215. package/cli/dist/utils/service-urls.d.ts +30 -0
  216. package/cli/dist/utils/service-urls.d.ts.map +1 -0
  217. package/cli/dist/utils/service-urls.js +93 -0
  218. package/cli/dist/utils/service-urls.js.map +1 -0
  219. package/cli/dist/utils/services.d.ts +18 -0
  220. package/cli/dist/utils/services.d.ts.map +1 -0
  221. package/cli/dist/utils/services.js +292 -0
  222. package/cli/dist/utils/services.js.map +1 -0
  223. package/cli/dist/utils/stack-features.d.ts +22 -0
  224. package/cli/dist/utils/stack-features.d.ts.map +1 -0
  225. package/cli/dist/utils/stack-features.js +13 -0
  226. package/cli/dist/utils/stack-features.js.map +1 -0
  227. package/cli/dist/utils/tar.d.ts +7 -0
  228. package/cli/dist/utils/tar.d.ts.map +1 -0
  229. package/cli/dist/utils/tar.js +21 -0
  230. package/cli/dist/utils/tar.js.map +1 -0
  231. package/cli/dist/utils/tilt.d.ts +20 -0
  232. package/cli/dist/utils/tilt.d.ts.map +1 -0
  233. package/cli/dist/utils/tilt.js +95 -0
  234. package/cli/dist/utils/tilt.js.map +1 -0
  235. package/cli/dist/utils/validation.d.ts +16 -0
  236. package/cli/dist/utils/validation.d.ts.map +1 -0
  237. package/cli/dist/utils/validation.js +58 -0
  238. package/cli/dist/utils/validation.js.map +1 -0
  239. package/cli/package.json +11 -12
  240. package/cli/src/commands/__tests__/always-enabled-infra.test.ts +2 -8
  241. package/cli/src/commands/__tests__/doctor.test.ts +181 -8
  242. package/cli/src/commands/__tests__/phases-model.test.ts +2 -4
  243. package/cli/src/commands/__tests__/project-resource-features-e2e.test.ts +4 -24
  244. package/cli/src/commands/__tests__/saas-starter-integration.test.ts +8 -10
  245. package/cli/src/commands/__tests__/stack-features-e2e.test.ts +10 -4
  246. package/cli/src/commands/__tests__/upgrade.test.ts +23 -13
  247. package/cli/src/commands/doctor.ts +136 -0
  248. package/cli/src/commands/help.ts +13 -3
  249. package/cli/src/commands/project.ts +11 -14
  250. package/cli/src/commands/projects.ts +2 -2
  251. package/cli/src/commands/up.ts +13 -4
  252. package/cli/src/commands/upgrade.ts +11 -4
  253. package/cli/src/config/platform-standards.test.ts +6 -6
  254. package/cli/src/generator/__tests__/extension-fetch.test.ts +36 -14
  255. package/cli/src/generator/extension-fetch.ts +12 -5
  256. package/cli/src/generator/template-engine.ts +19 -21
  257. package/cli/src/types/index.ts +2 -0
  258. package/cli/src/utils/__tests__/tilt.test.ts +29 -0
  259. package/cli/src/utils/constants.ts +4 -0
  260. package/cli/src/utils/doctor-runtime.ts +67 -27
  261. package/cli/src/utils/env-validator.ts +2 -2
  262. package/cli/src/utils/project-templates.ts +2 -1
  263. package/cli/src/utils/prompt.ts +2 -4
  264. package/cli/src/utils/resource-features.ts +10 -4
  265. package/cli/src/utils/services.ts +2 -1
  266. package/cli/src/utils/tilt.ts +20 -0
  267. package/engine/assets/docker/wake-gateway/Dockerfile +26 -0
  268. package/engine/assets/docker/wake-gateway/decide.test.ts +87 -0
  269. package/engine/assets/docker/wake-gateway/decide.ts +75 -0
  270. package/engine/assets/docker/wake-gateway/server.ts +220 -0
  271. package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +13 -23
  272. package/engine/topologies/platform/docker/compose/compose.star +7 -14
  273. package/engine/topologies/platform/docker/compose/compose_helpers.star +1 -1
  274. package/engine/topologies/platform/docker/compose/traefik_standalone.star +44 -6
  275. package/engine/topologies/platform/docker/config/healthcheck.star +56 -0
  276. package/engine/topologies/platform/docker/constants.star +10 -0
  277. package/engine/topologies/platform/docker/layers/l1_base_layers.star +1 -1
  278. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +55 -20
  279. package/engine/topologies/platform/docker/networking/traefik_static_routes.star +107 -0
  280. package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +2 -2
  281. package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +1 -1
  282. package/engine/topologies/tilt/manifest/schema.star +6 -0
  283. package/engine/topologies/tilt/manifest/validator.star +16 -0
  284. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +107 -8
  285. package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +12 -6
  286. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +2 -1
  287. package/knip.json +1 -1
  288. package/openspec/changes/optimize-runtime-docker-layers/.openspec.yaml +3 -0
  289. package/openspec/changes/optimize-runtime-docker-layers/design.md +81 -0
  290. package/openspec/changes/optimize-runtime-docker-layers/proposal.md +44 -0
  291. package/openspec/changes/optimize-runtime-docker-layers/specs/runtime-container-footprint/spec.md +79 -0
  292. package/openspec/changes/optimize-runtime-docker-layers/tasks.md +36 -0
  293. package/openspec/changes/prioritized-cold-start/.openspec.yaml +4 -0
  294. package/openspec/changes/prioritized-cold-start/design.md +113 -0
  295. package/openspec/changes/prioritized-cold-start/proposal.md +40 -0
  296. package/openspec/changes/prioritized-cold-start/specs/on-demand-resource-startup/spec.md +92 -0
  297. package/openspec/changes/prioritized-cold-start/tasks.md +46 -0
  298. package/openspec/config.yaml +1 -0
  299. package/package.json +35 -5
  300. package/scripts/autoresearch/layer-eval.sh +65 -0
  301. package/scripts/benchmark/README.md +25 -0
  302. package/scripts/benchmark/container-scale.ts +503 -0
  303. package/scripts/benchmark/pre-release-gate.sh +9 -0
  304. package/scripts/measure-idle-footprint.sh +58 -0
  305. package/tests/tilt-engine/test_runtime_container_footprint.py +162 -0
  306. package/tests/tilt-engine/test_sablier_defer_start.py +196 -0
  307. package/tests/tilt-engine/test_standalone_traefik.py +38 -0
  308. package/tests/tilt-engine/test_traefik_static_wake_route.py +119 -0
  309. package/bun.lock +0 -538
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Wake gateway: fronts every `sablier.deferStart` resource's Traefik static
3
+ * route (see openspec/changes/prioritized-cold-start). On the first request
4
+ * for a resource with no running container, it triggers the resource (and
5
+ * its cold dependencies) via `tilt trigger`, and/or Sablier's own start for
6
+ * an already-created container, then holds the request until the resource
7
+ * answers its healthcheck or the configured timeout elapses.
8
+ *
9
+ * Config for each request comes from headers a Traefik `headers` middleware
10
+ * injects (set by the file-provider entry generated in
11
+ * traefik_static_routes.star) rather than the path, so the original request
12
+ * path/query reach the real backend unchanged once it's ready:
13
+ * X-Wake-Resource Tilt/Compose resource name (for `tilt trigger`,
14
+ * and the proxy target -- Compose's embedded DNS
15
+ * resolves a service by this name too).
16
+ * X-Wake-Container The actual Docker container name (for `docker
17
+ * inspect` state/health -- NOT the same string as
18
+ * the resource name; Compose derives it as
19
+ * <project>-<service>-<replica>, confirmed empirically
20
+ * against a live TDK container, e.g.
21
+ * "analytics-autogenerated-data-governance-api-1"
22
+ * for the "data-governance-api" resource).
23
+ * X-Wake-Deps Comma-separated "resourceName=containerName" pairs
24
+ * for required dependencies (task 1.5: Tilt never
25
+ * cascades a trigger to these on its own).
26
+ * X-Wake-Timeout-Ms, X-Wake-Proxy-Port, X-Wake-Session-Duration
27
+ */
28
+ import { decideWakeAction, evaluateReadiness, type ContainerState } from "./decide";
29
+
30
+ const TILT_HOST = process.env.TILT_HOST || "host.docker.internal";
31
+ const TILT_PORT = process.env.TILT_PORT || "10350";
32
+ const SABLIER_URL = process.env.SABLIER_URL || "http://sablier:10000";
33
+ const POLL_INTERVAL_MS = 1000;
34
+ const DEFAULT_TIMEOUT_MS = 60_000;
35
+
36
+ async function run(cmd: string[]): Promise<{ code: number; stdout: string; stderr: string }> {
37
+ const proc = Bun.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
38
+ const [stdout, stderr, code] = await Promise.all([
39
+ new Response(proc.stdout).text(),
40
+ new Response(proc.stderr).text(),
41
+ proc.exited,
42
+ ]);
43
+ return { code, stdout, stderr };
44
+ }
45
+
46
+ async function containerState(name: string): Promise<ContainerState> {
47
+ const { code, stdout } = await run([
48
+ "docker", "inspect", "--format", "{{.State.Status}}", name,
49
+ ]);
50
+ if (code !== 0) return "absent";
51
+ const status = stdout.trim();
52
+ if (status === "running") return "running";
53
+ if (status === "created") return "created";
54
+ return "exited";
55
+ }
56
+
57
+ async function isHealthy(name: string): Promise<boolean> {
58
+ const { code, stdout } = await run([
59
+ "docker", "inspect",
60
+ "--format", "{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}",
61
+ name,
62
+ ]);
63
+ if (code !== 0) return false;
64
+ const status = stdout.trim();
65
+ return status === "healthy" || status === "running";
66
+ }
67
+
68
+ async function tiltTrigger(name: string): Promise<void> {
69
+ await run(["tilt", "trigger", name, "--host", TILT_HOST, "--port", TILT_PORT]);
70
+ }
71
+
72
+ /**
73
+ * Sablier's real blocking-strategy endpoint (GET /api/strategies/blocking,
74
+ * per https://sablierapp.dev/openapi.json): starts the named instance and
75
+ * holds until it's ready or `timeoutMs` elapses, returning that outcome via
76
+ * the X-Sablier-Session-Status header. Only valid for an instance Sablier
77
+ * already knows about (a container that exists with sablier.enable=true) --
78
+ * see decide.ts's useSablierStart.
79
+ */
80
+ async function sablierBlockingWait(resource: string, sessionDuration: string, timeoutMs: number): Promise<boolean> {
81
+ const params = new URLSearchParams({
82
+ names: resource,
83
+ session_duration: sessionDuration,
84
+ timeout: `${Math.max(1, Math.round(timeoutMs / 1000))}s`,
85
+ });
86
+ try {
87
+ const res = await fetch(`${SABLIER_URL}/api/strategies/blocking?${params}`);
88
+ return res.headers.get("X-Sablier-Session-Status") === "ready";
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+
94
+ interface NamedDep {
95
+ resourceName: string;
96
+ containerName: string;
97
+ }
98
+
99
+ function parseDeps(raw: string): NamedDep[] {
100
+ return raw
101
+ .split(",")
102
+ .filter(Boolean)
103
+ .map((pair) => {
104
+ const [resourceName, containerName] = pair.split("=");
105
+ return { resourceName, containerName: containerName || resourceName };
106
+ });
107
+ }
108
+
109
+ function parseRequest(req: Request) {
110
+ const resource = req.headers.get("X-Wake-Resource") || "";
111
+ const container = req.headers.get("X-Wake-Container") || resource;
112
+ const deps = parseDeps(req.headers.get("X-Wake-Deps") || "");
113
+ const timeoutMs = Number(req.headers.get("X-Wake-Timeout-Ms")) || DEFAULT_TIMEOUT_MS;
114
+ const proxyPort = req.headers.get("X-Wake-Proxy-Port") || "80";
115
+ const sessionDuration = req.headers.get("X-Wake-Session-Duration") || "10m";
116
+ return { resource, container, deps, timeoutMs, proxyPort, sessionDuration };
117
+ }
118
+
119
+ async function handleWake(req: Request): Promise<Response> {
120
+ const { resource, container, deps, timeoutMs, proxyPort, sessionDuration } = parseRequest(req);
121
+ if (!resource) {
122
+ return new Response("wake gateway: missing X-Wake-Resource header (set by the generated Traefik static route)", { status: 400 });
123
+ }
124
+
125
+ const resourceState = await containerState(container);
126
+ const dependencies = await Promise.all(
127
+ deps.map(async (dep) => ({ name: dep.resourceName, state: await containerState(dep.containerName) })),
128
+ );
129
+
130
+ const action = decideWakeAction({ resourceContainerName: resource, resourceState, dependencies });
131
+ const deadline = Date.now() + timeoutMs;
132
+
133
+ // tilt trigger runs for everything cold (task 1.5: Tilt never cascades this
134
+ // on its own); Sablier's blocking call, when applicable, both starts AND
135
+ // waits for the resource itself in one round trip.
136
+ const [, resourceReadyViaSablier] = await Promise.all([
137
+ Promise.all(action.tiltTriggerNames.map(tiltTrigger)),
138
+ action.useSablierStart
139
+ ? sablierBlockingWait(resource, sessionDuration, timeoutMs)
140
+ : Promise.resolve(false),
141
+ ]);
142
+
143
+ const requiredNames = [resource, ...deps.map((d) => d.resourceName)];
144
+ const containerByName: Record<string, string> = { [resource]: container };
145
+ for (const dep of deps) containerByName[dep.resourceName] = dep.containerName;
146
+
147
+ while (Date.now() < deadline) {
148
+ const healthy: Record<string, boolean> = {};
149
+ // Sablier's blocking call already confirmed the resource itself; re-check
150
+ // it too (cheap) so a false result from Sablier still gets a fair retry.
151
+ healthy[resource] = resourceReadyViaSablier || (await isHealthy(container));
152
+ for (const dep of deps) {
153
+ healthy[dep.resourceName] = await isHealthy(dep.containerName);
154
+ }
155
+ const outcome = evaluateReadiness(requiredNames, healthy);
156
+ if (outcome.ready) {
157
+ return proxyThrough(req, resource, proxyPort);
158
+ }
159
+ await Bun.sleep(POLL_INTERVAL_MS);
160
+ }
161
+
162
+ return new Response(
163
+ `wake gateway: timed out after ${timeoutMs}ms waiting for ${resource} and its dependencies to become healthy`,
164
+ { status: 504 },
165
+ );
166
+ }
167
+
168
+ // Headers that must not be forwarded verbatim to the upstream fetch: Host
169
+ // would otherwise carry the gateway's own address, and the X-Wake-* control
170
+ // headers are gateway-internal, not part of the real app request.
171
+ const STRIPPED_REQUEST_HEADERS = new Set([
172
+ "host",
173
+ "x-wake-resource",
174
+ "x-wake-container",
175
+ "x-wake-deps",
176
+ "x-wake-timeout-ms",
177
+ "x-wake-proxy-port",
178
+ "x-wake-session-duration",
179
+ ]);
180
+
181
+ async function proxyThrough(req: Request, resource: string, port: string): Promise<Response> {
182
+ // The original path/query reach the gateway untouched (config travels via
183
+ // X-Wake-* headers, not the path), so they're forwarded as-is.
184
+ const target = new URL(req.url);
185
+ target.protocol = "http:";
186
+ // Set hostname/port separately, not via `.host` -- confirmed empirically
187
+ // that `target.host = "name:port"` silently keeps the URL's original port
188
+ // when one is already present instead of replacing it.
189
+ target.hostname = resource;
190
+ target.port = port;
191
+
192
+ const headers = new Headers();
193
+ for (const [key, value] of req.headers) {
194
+ if (!STRIPPED_REQUEST_HEADERS.has(key.toLowerCase())) headers.set(key, value);
195
+ }
196
+
197
+ const upstream = await fetch(target.toString(), {
198
+ method: req.method,
199
+ headers,
200
+ body: req.method === "GET" || req.method === "HEAD" ? undefined : await req.arrayBuffer(),
201
+ });
202
+ return new Response(upstream.body, { status: upstream.status, headers: upstream.headers });
203
+ }
204
+
205
+ Bun.serve({
206
+ port: Number(process.env.PORT) || 9999,
207
+ // Bun's default idleTimeout (10s) is shorter than a real wake can take
208
+ // (tilt trigger + healthcheck polling) -- confirmed empirically: a request
209
+ // was killed mid-wait with "request timed out after 10 seconds" before this
210
+ // was raised. 255s is Bun's own max for this setting.
211
+ idleTimeout: 255,
212
+ fetch(req) {
213
+ return handleWake(req).catch((err) => {
214
+ console.error("wake gateway error:", err);
215
+ return new Response(`wake gateway error: ${String(err)}`, { status: 500 });
216
+ });
217
+ },
218
+ });
219
+
220
+ console.log(`wake gateway listening on :${Number(process.env.PORT) || 9999}`);
@@ -2,16 +2,8 @@
2
2
  # 🏗️ TILT SDK - GOLDEN IMAGE DOCKERFILE GENERATOR
3
3
  # =============================================================================
4
4
 
5
- # === INLINED CONSTANTS for pure extension loading ===
6
- get_docker_healthcheck_config = lambda: {"path": "/health", "interval_seconds": 10, "timeout_seconds": 5, "start_period_seconds": 30, "retries": 3, "frontend_start_period_seconds": 30}
7
- # === END INLINED CONSTANTS ===
8
-
9
-
10
5
  load('../../../tilt/common/utils.star', 'Utils')
11
-
12
-
13
- # Load centralized healthcheck configuration
14
- _docker_health = get_docker_healthcheck_config()
6
+ load('../config/healthcheck.star', 'DOCKER_HEALTHCHECK', 'dockerfile_healthcheck_flags')
15
7
 
16
8
 
17
9
  # Load project name for dynamic naming
@@ -76,7 +68,6 @@ RUN apk add --no-cache \
76
68
  ca-certificates \
77
69
  tzdata \
78
70
  curl \
79
- file \
80
71
  bash \
81
72
  openssl
82
73
 
@@ -180,9 +171,11 @@ LABEL layer="l4-backend-bun" \
180
171
  maintainer="{prefix}"
181
172
 
182
173
  ENV NODE_ENV=production
174
+ # Smaller heap growth and more frequent GC: idle services dominate a 100-service landscape.
175
+ ENV BUN_OPTIONS=--smol
183
176
 
184
- # Install hugo for services that require static site generation (specified via manifest features)
185
- RUN apk add --no-cache hugo
177
+ # Service-specific tooling (e.g. hugo) is installed per service by the L4 runtime
178
+ # layer when the manifest lists it in featuresEnabled - not here for every backend.
186
179
 
187
180
  # ⚠️ Infisical CLI is NOT installed in golden L4 images
188
181
  # Services use TypeScript SDK (fetch API) or read from env vars - no CLI needed
@@ -192,7 +185,7 @@ RUN apk add --no-cache hugo
192
185
  USER bun
193
186
 
194
187
  # Backend-specific healthcheck (checks Bun runtime)
195
- HEALTHCHECK --interval={interval}s --timeout={timeout}s --start-period={start_period}s --retries={retries} \
188
+ HEALTHCHECK {hc_flags} \
196
189
  CMD bun --version || exit 1
197
190
 
198
191
  WORKDIR /app
@@ -221,7 +214,7 @@ RUN addgroup -S app && adduser -S app -G app
221
214
  USER app
222
215
 
223
216
  # Backend-specific healthcheck (checks Node.js runtime)
224
- HEALTHCHECK --interval={interval}s --timeout={timeout}s --start-period={start_period}s --retries={retries} \
217
+ HEALTHCHECK {hc_flags} \
225
218
  CMD node --version || exit 1
226
219
 
227
220
  WORKDIR /app
@@ -246,7 +239,7 @@ ENV NODE_ENV=production
246
239
  RUN apk add --no-cache curl
247
240
 
248
241
  # Frontend-specific healthcheck (checks Nginx)
249
- HEALTHCHECK --interval={interval}s --timeout={timeout}s --start-period={frontend_start_period}s --retries={retries} \
242
+ HEALTHCHECK {hc_flags_frontend} \
250
243
  CMD curl -f http://localhost/ || exit 1
251
244
 
252
245
  WORKDIR /usr/share/nginx/html
@@ -276,16 +269,13 @@ RUN mkdir -p /cache/bun && chown -R bun:bun /cache/bun
276
269
 
277
270
  USER bun
278
271
 
279
- # Migrator-specific healthcheck (checks Prisma CLI)
280
- HEALTHCHECK --interval={interval}s --timeout={timeout}s --start-period={start_period}s --retries={retries} \
281
- CMD bunx prisma --version || exit 1
272
+ # Migrators are one-shot jobs (migrate.sh runs to completion), so no healthcheck.
273
+ # The per-service l4_migrator_runtime stage sets HEALTHCHECK NONE as well.
274
+ HEALTHCHECK NONE
282
275
 
283
276
  WORKDIR /app
284
277
  """.format(
285
- interval=_docker_health["interval_seconds"],
286
- timeout=_docker_health["timeout_seconds"],
287
- start_period=_docker_health["start_period_seconds"],
288
- retries=_docker_health["retries"],
289
- frontend_start_period=_docker_health["frontend_start_period_seconds"],
278
+ hc_flags=dockerfile_healthcheck_flags(),
279
+ hc_flags_frontend=dockerfile_healthcheck_flags(DOCKER_HEALTHCHECK["frontend_start_period_seconds"]),
290
280
  prefix=_GOLDEN_PREFIX,
291
281
  )
@@ -23,6 +23,7 @@ load("../secrets/infisical.star", "get_infisical_environment_vars")
23
23
  load("../auth/auth.star", "AuthConfig")
24
24
 
25
25
  load("../constants.star", "PlatformDockerConstants")
26
+ load("../config/healthcheck.star", "DOCKER_HEALTHCHECK", "compose_healthcheck_timing")
26
27
 
27
28
  # Load project name for dynamic host naming
28
29
  # Use TDK_PROJECT_ROOT env var set by Tilt, fallback to current directory
@@ -125,6 +126,7 @@ def generate_frontend_compose(resource_path, resource_name, res, manifest=None):
125
126
 
126
127
  return """ {res_name}:
127
128
  image: {image_name}
129
+ init: true
128
130
  {build_config}
129
131
  <<: *frontend-memory-limit
130
132
  env_file:
@@ -138,12 +140,7 @@ def generate_frontend_compose(resource_path, resource_name, res, manifest=None):
138
140
  - {network_backend}
139
141
  healthcheck:
140
142
  test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:{port}"]
141
- interval: 10s
142
- timeout: 5s
143
- retries: 3
144
- # STORY 4 FIX: 30s grace period to prevent 504 errors on startup
145
- start_period: 30s
146
- """.format(
143
+ {healthcheck_timing}""".format(
147
144
  res_name=res_name,
148
145
  image_name=image_name,
149
146
  build_config=build_config,
@@ -152,6 +149,7 @@ def generate_frontend_compose(resource_path, resource_name, res, manifest=None):
152
149
  port=port,
153
150
  network_traefik_public=PlatformDockerConstants.NETWORK_TRAEFIK_PUBLIC,
154
151
  network_backend=PlatformDockerConstants.NETWORK_BACKEND,
152
+ healthcheck_timing=compose_healthcheck_timing(DOCKER_HEALTHCHECK["frontend_start_period_seconds"]),
155
153
  )
156
154
 
157
155
 
@@ -261,13 +259,7 @@ def _generate_single_backend_entry(resource_path, resource_name, res, manifest,
261
259
  )
262
260
  healthcheck_section = """ healthcheck:
263
261
  test: ["CMD", "curl", "-f", "--max-time", "5", "http://localhost:{internal_port}{health_path}"]
264
- interval: 10s
265
- timeout: 5s
266
- retries: 6
267
- # STORY 4 FIX: 30s grace period to prevent 504 errors during startup
268
- # Services get 30s to initialize before health checks begin
269
- start_period: 30s
270
- """.format(internal_port=internal_port, health_path=health_path)
262
+ """.format(internal_port=internal_port, health_path=health_path) + compose_healthcheck_timing()
271
263
  infisical_env = get_infisical_environment_vars(as_array=True, resource_name=res_name)
272
264
 
273
265
  # Build auth environment variables section using centralized auth utilities
@@ -275,6 +267,7 @@ def _generate_single_backend_entry(resource_path, resource_name, res, manifest,
275
267
  return """
276
268
  {resource_entry_name}:
277
269
  image: {image_name}
270
+ init: true
278
271
  restart: unless-stopped
279
272
  {build_config}
280
273
  {ports_section}
@@ -619,7 +612,7 @@ services:
619
612
  {migrator_name}:
620
613
  image: {image_name}
621
614
  build:
622
- context: ../../../../.
615
+ context: ../../../.
623
616
  dockerfile: {dockerfile_ref}
624
617
  target: migrator
625
618
  environment:
@@ -40,7 +40,7 @@ def compose_build_config(resource_path, res_name, dockerfile=OUTPUT.AUTOGENERATE
40
40
  )
41
41
 
42
42
  base_config = """ build:
43
- context: ../../../../.
43
+ context: ../../../.
44
44
  dockerfile: {full_dockerfile_path}""".format(
45
45
  full_dockerfile_path=full_dockerfile_path
46
46
  )
@@ -6,6 +6,7 @@
6
6
  # skip Traefik entirely and api.{project}.localhost never served.
7
7
 
8
8
  load("../constants.star", "PlatformDockerConstants")
9
+ load("../config/healthcheck.star", "compose_healthcheck_timing")
9
10
 
10
11
 
11
12
  def generate_standalone_traefik_compose():
@@ -24,6 +25,7 @@ services:
24
25
  restart: unless-stopped
25
26
  depends_on:
26
27
  - sablier
28
+ - wake-gateway
27
29
  command:
28
30
  - "--api.insecure=true"
29
31
  - "--ping=true"
@@ -31,6 +33,13 @@ services:
31
33
  - "--providers.docker.exposedbydefault=false"
32
34
  - "--providers.docker.watch=true"
33
35
  - "--providers.docker.network={network}"
36
+ # Static routes for sablier.deferStart resources (openspec/changes/
37
+ # prioritized-cold-start): a never-created resource has no container
38
+ # and thus no Docker labels for the provider above to discover, so its
39
+ # route is defined here instead, pointed at wake-gateway. Harmless when
40
+ # no deferStart resource exists (empty directory, no routes emitted).
41
+ - "--providers.file.directory=/etc/traefik/dynamic"
42
+ - "--providers.file.watch=true"
34
43
  - "--entrypoints.web.address=:80"
35
44
  - "--entrypoints.websecure.address=:443"
36
45
  - "--log.level=INFO"
@@ -44,15 +53,12 @@ services:
44
53
  - "8080:8080"
45
54
  volumes:
46
55
  - /var/run/docker.sock:/var/run/docker.sock:ro
56
+ - ./{dynamic_dir_name}:/etc/traefik/dynamic:ro
47
57
  networks:
48
58
  - traefik-public
49
59
  healthcheck:
50
60
  test: ["CMD", "traefik", "healthcheck", "--ping"]
51
- interval: 10s
52
- timeout: 5s
53
- retries: 6
54
- start_period: 10s
55
-
61
+ {healthcheck_timing}
56
62
  sablier:
57
63
  image: sablierapp/sablier:1.18.0
58
64
  container_name: {name}_sablier
@@ -66,8 +72,40 @@ services:
66
72
  networks:
67
73
  - traefik-public
68
74
 
75
+ # Wake gateway (openspec/changes/prioritized-cold-start): fronts every
76
+ # deferStart resource's static route above. Triggers a cold resource (and
77
+ # its dependencies, which Tilt never cascades to on its own -- see design.md
78
+ # D3/task 1.5) via `tilt trigger`, or Sablier's own start when a container
79
+ # already exists, then holds the request until healthy. Needs the same
80
+ # Tilt session token Sablier/Traefik don't: confirmed empirically that
81
+ # `tilt trigger` from inside a container is otherwise rejected with
82
+ # "(403): invalid session token" even when the Tilt HTTP port is reachable.
83
+ wake-gateway:
84
+ build: ./{vendored_gateway_dir}
85
+ container_name: {gateway_container}
86
+ restart: unless-stopped
87
+ environment:
88
+ - TILT_HOST=host.docker.internal
89
+ - TILT_PORT=10350
90
+ - SABLIER_URL=http://sablier:10000
91
+ - PORT={gateway_port}
92
+ volumes:
93
+ - /var/run/docker.sock:/var/run/docker.sock
94
+ - {tilt_dev_dir}:/root/.tilt-dev:ro
95
+ networks:
96
+ - traefik-public
97
+
69
98
  networks:
70
99
  traefik-public:
71
100
  name: {network}
72
101
  external: true
73
- """.format(name=name, network=network)
102
+ """.format(
103
+ name=name,
104
+ network=network,
105
+ healthcheck_timing=compose_healthcheck_timing(10),
106
+ dynamic_dir_name=PlatformDockerConstants.TRAEFIK_DYNAMIC_DIR_REL.split("/")[-1],
107
+ vendored_gateway_dir="tdk-cli-ext/engine/assets/docker/wake-gateway",
108
+ gateway_container=PlatformDockerConstants.TRAEFIK_WAKE_GATEWAY_CONTAINER,
109
+ gateway_port=PlatformDockerConstants.TRAEFIK_WAKE_GATEWAY_PORT,
110
+ tilt_dev_dir=os.environ.get("HOME", "") + "/.tilt-dev",
111
+ )
@@ -0,0 +1,56 @@
1
+ # =============================================================================
2
+ # Docker healthcheck timing - single source for golden images, L4 runtimes and
3
+ # generated compose files.
4
+ # =============================================================================
5
+ # Steady-state probes are infrequent (each probe is a `docker exec`, which adds
6
+ # up across 100+ containers), while `start_interval` keeps first-healthy time
7
+ # fast during the start period. `start_interval` needs Docker Engine 25+ and
8
+ # Compose 2.20.2+ (checked by `tdk doctor`).
9
+ #
10
+ # Override per run with environment variables, e.g.:
11
+ # TDK_HEALTHCHECK_INTERVAL_SECONDS=15 tdk up
12
+ # =============================================================================
13
+
14
+
15
+ def _env_int(name, default):
16
+ raw = os.environ.get(name, '')
17
+ if raw and raw.isdigit() and int(raw) > 0:
18
+ return int(raw)
19
+ return default
20
+
21
+
22
+ DOCKER_HEALTHCHECK = {
23
+ "path": "/health",
24
+ "interval_seconds": _env_int("TDK_HEALTHCHECK_INTERVAL_SECONDS", 30),
25
+ "start_interval_seconds": _env_int("TDK_HEALTHCHECK_START_INTERVAL_SECONDS", 2),
26
+ "timeout_seconds": _env_int("TDK_HEALTHCHECK_TIMEOUT_SECONDS", 5),
27
+ "start_period_seconds": _env_int("TDK_HEALTHCHECK_START_PERIOD_SECONDS", 30),
28
+ "frontend_start_period_seconds": _env_int("TDK_HEALTHCHECK_START_PERIOD_SECONDS", 30),
29
+ "retries": _env_int("TDK_HEALTHCHECK_RETRIES", 3),
30
+ }
31
+
32
+
33
+ def dockerfile_healthcheck_flags(start_period_seconds = None):
34
+ """Flags for a Dockerfile `HEALTHCHECK` instruction."""
35
+ h = DOCKER_HEALTHCHECK
36
+ start_period = start_period_seconds if start_period_seconds != None else h["start_period_seconds"]
37
+ return "--interval={}s --start-interval={}s --timeout={}s --start-period={}s --retries={}".format(
38
+ h["interval_seconds"],
39
+ h["start_interval_seconds"],
40
+ h["timeout_seconds"],
41
+ start_period,
42
+ h["retries"],
43
+ )
44
+
45
+
46
+ def compose_healthcheck_timing(start_period_seconds = None, indent = " "):
47
+ """Timing keys for a compose `healthcheck:` block (everything except `test:`)."""
48
+ h = DOCKER_HEALTHCHECK
49
+ start_period = start_period_seconds if start_period_seconds != None else h["start_period_seconds"]
50
+ return (
51
+ indent + "interval: {}s\n".format(h["interval_seconds"])
52
+ + indent + "timeout: {}s\n".format(h["timeout_seconds"])
53
+ + indent + "retries: {}\n".format(h["retries"])
54
+ + indent + "start_period: {}s\n".format(start_period)
55
+ + indent + "start_interval: {}s\n".format(h["start_interval_seconds"])
56
+ )
@@ -62,6 +62,13 @@ NETWORK_DATABASE = PROJECT_NAME + "_database"
62
62
  NETWORK_INFISICAL = PROJECT_NAME + "_infisical-network"
63
63
  NETWORK_PROXY = PROJECT_NAME + "_proxy"
64
64
 
65
+ # Traefik file-provider dynamic config: one generated YAML per `sablier.deferStart`
66
+ # resource's static route (see openspec/changes/prioritized-cold-start), mounted
67
+ # read-only into the standalone Traefik container at /etc/traefik/dynamic.
68
+ TRAEFIK_DYNAMIC_DIR_REL = ".tdk/.tdk-out/traefik-dynamic"
69
+ TRAEFIK_WAKE_GATEWAY_CONTAINER = PROJECT_NAME_HYPHEN + "-wake-gateway"
70
+ TRAEFIK_WAKE_GATEWAY_PORT = 9999
71
+
65
72
  # NATS constants
66
73
  NATS_HOST = PROJECT_NAME + "_nats"
67
74
  NATS_PORT = "4222"
@@ -146,6 +153,9 @@ PlatformDockerConstants = struct(
146
153
  NETWORK_DATABASE = NETWORK_DATABASE,
147
154
  NETWORK_INFISICAL = NETWORK_INFISICAL,
148
155
  NETWORK_PROXY = NETWORK_PROXY,
156
+ TRAEFIK_DYNAMIC_DIR_REL = TRAEFIK_DYNAMIC_DIR_REL,
157
+ TRAEFIK_WAKE_GATEWAY_CONTAINER = TRAEFIK_WAKE_GATEWAY_CONTAINER,
158
+ TRAEFIK_WAKE_GATEWAY_PORT = TRAEFIK_WAKE_GATEWAY_PORT,
149
159
  NATS_HOST = NATS_HOST,
150
160
  NATS_PORT = NATS_PORT,
151
161
  NATS_URL = NATS_URL,
@@ -61,7 +61,7 @@ def L1_generate_os_base(base_image = None, maintainer = None, use_golden = True)
61
61
  "# ---- L1: os_base (" + base_image + ") ----\n"
62
62
  + "FROM " + base_image + " AS l1_os_base\n"
63
63
  + "LABEL maintainer=\"" + maintainer + "\"\n"
64
- + "RUN apk add --no-cache ca-certificates tzdata curl file bash openssl \\\n"
64
+ + "RUN apk add --no-cache ca-certificates tzdata curl bash openssl \\\n"
65
65
  + " && addgroup -S app && adduser -S app -G app\n"
66
66
  + "WORKDIR /app\n"
67
67
  )