@supatype/cli 0.1.0 → 0.1.2

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 (331) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +121 -86
  3. package/.turbo/turbo-typecheck.log +2 -2
  4. package/dist/binary-cache.d.ts +2 -0
  5. package/dist/binary-cache.d.ts.map +1 -1
  6. package/dist/binary-cache.js +36 -0
  7. package/dist/binary-cache.js.map +1 -1
  8. package/dist/binary-download-lock.d.ts +12 -0
  9. package/dist/binary-download-lock.d.ts.map +1 -0
  10. package/dist/binary-download-lock.js +106 -0
  11. package/dist/binary-download-lock.js.map +1 -0
  12. package/dist/cli-package-version.d.ts +3 -0
  13. package/dist/cli-package-version.d.ts.map +1 -0
  14. package/dist/cli-package-version.js +15 -0
  15. package/dist/cli-package-version.js.map +1 -0
  16. package/dist/cli.d.ts.map +1 -1
  17. package/dist/cli.js +5 -2
  18. package/dist/cli.js.map +1 -1
  19. package/dist/commands/add.js +25 -19
  20. package/dist/commands/add.js.map +1 -1
  21. package/dist/commands/admin.d.ts +28 -1
  22. package/dist/commands/admin.d.ts.map +1 -1
  23. package/dist/commands/admin.js +290 -112
  24. package/dist/commands/admin.js.map +1 -1
  25. package/dist/commands/cache.d.ts +1 -1
  26. package/dist/commands/cache.d.ts.map +1 -1
  27. package/dist/commands/cache.js +98 -5
  28. package/dist/commands/cache.js.map +1 -1
  29. package/dist/commands/dev.d.ts.map +1 -1
  30. package/dist/commands/dev.js +28 -22
  31. package/dist/commands/dev.js.map +1 -1
  32. package/dist/commands/init.d.ts +8 -4
  33. package/dist/commands/init.d.ts.map +1 -1
  34. package/dist/commands/init.js +278 -139
  35. package/dist/commands/init.js.map +1 -1
  36. package/dist/commands/link-helpers.d.ts.map +1 -1
  37. package/dist/commands/link-helpers.js +26 -25
  38. package/dist/commands/link-helpers.js.map +1 -1
  39. package/dist/commands/push.js +3 -3
  40. package/dist/commands/push.js.map +1 -1
  41. package/dist/compose-local-server-image.d.ts +13 -0
  42. package/dist/compose-local-server-image.d.ts.map +1 -0
  43. package/dist/compose-local-server-image.js +57 -0
  44. package/dist/compose-local-server-image.js.map +1 -0
  45. package/dist/compose-rename.d.ts +10 -0
  46. package/dist/compose-rename.d.ts.map +1 -0
  47. package/dist/compose-rename.js +64 -0
  48. package/dist/compose-rename.js.map +1 -0
  49. package/dist/dev-compose.d.ts.map +1 -1
  50. package/dist/dev-compose.js +138 -97
  51. package/dist/dev-compose.js.map +1 -1
  52. package/dist/dev-log-bus.d.ts +5 -0
  53. package/dist/dev-log-bus.d.ts.map +1 -1
  54. package/dist/dev-log-bus.js +14 -0
  55. package/dist/dev-log-bus.js.map +1 -1
  56. package/dist/dev-log-filter.d.ts +4 -0
  57. package/dist/dev-log-filter.d.ts.map +1 -1
  58. package/dist/dev-log-filter.js +52 -0
  59. package/dist/dev-log-filter.js.map +1 -1
  60. package/dist/dev-ports.d.ts +27 -0
  61. package/dist/dev-ports.d.ts.map +1 -0
  62. package/dist/dev-ports.js +178 -0
  63. package/dist/dev-ports.js.map +1 -0
  64. package/dist/dev-ready-panel.d.ts +19 -0
  65. package/dist/dev-ready-panel.d.ts.map +1 -0
  66. package/dist/dev-ready-panel.js +49 -0
  67. package/dist/dev-ready-panel.js.map +1 -0
  68. package/dist/dev-session-lock.d.ts +25 -0
  69. package/dist/dev-session-lock.d.ts.map +1 -0
  70. package/dist/dev-session-lock.js +78 -0
  71. package/dist/dev-session-lock.js.map +1 -0
  72. package/dist/dev-session.d.ts +18 -5
  73. package/dist/dev-session.d.ts.map +1 -1
  74. package/dist/dev-session.js +91 -18
  75. package/dist/dev-session.js.map +1 -1
  76. package/dist/dev-shutdown.d.ts +20 -2
  77. package/dist/dev-shutdown.d.ts.map +1 -1
  78. package/dist/dev-shutdown.js +88 -10
  79. package/dist/dev-shutdown.js.map +1 -1
  80. package/dist/dev-task-colors.d.ts +3 -1
  81. package/dist/dev-task-colors.d.ts.map +1 -1
  82. package/dist/dev-task-colors.js +23 -3
  83. package/dist/dev-task-colors.js.map +1 -1
  84. package/dist/docker-runtime.js +1 -1
  85. package/dist/docker-runtime.js.map +1 -1
  86. package/dist/engine-push-output.d.ts +1 -0
  87. package/dist/engine-push-output.d.ts.map +1 -1
  88. package/dist/engine-push-output.js +4 -1
  89. package/dist/engine-push-output.js.map +1 -1
  90. package/dist/ensure-binary.d.ts.map +1 -1
  91. package/dist/ensure-binary.js +14 -2
  92. package/dist/ensure-binary.js.map +1 -1
  93. package/dist/ensure-component-binaries.d.ts +17 -0
  94. package/dist/ensure-component-binaries.d.ts.map +1 -0
  95. package/dist/ensure-component-binaries.js +45 -0
  96. package/dist/ensure-component-binaries.js.map +1 -0
  97. package/dist/ensure-dev-api-config.d.ts +3 -0
  98. package/dist/ensure-dev-api-config.d.ts.map +1 -0
  99. package/dist/ensure-dev-api-config.js +17 -0
  100. package/dist/ensure-dev-api-config.js.map +1 -0
  101. package/dist/env-file.d.ts +5 -0
  102. package/dist/env-file.d.ts.map +1 -0
  103. package/dist/env-file.js +33 -0
  104. package/dist/env-file.js.map +1 -0
  105. package/dist/init-dependency-versions.d.ts +13 -0
  106. package/dist/init-dependency-versions.d.ts.map +1 -0
  107. package/dist/init-dependency-versions.js +53 -0
  108. package/dist/init-dependency-versions.js.map +1 -0
  109. package/dist/init-package-json.d.ts +21 -0
  110. package/dist/init-package-json.d.ts.map +1 -0
  111. package/dist/init-package-json.js +46 -0
  112. package/dist/init-package-json.js.map +1 -0
  113. package/dist/postgres-ctl.d.ts.map +1 -1
  114. package/dist/postgres-ctl.js +2 -1
  115. package/dist/postgres-ctl.js.map +1 -1
  116. package/dist/required-host-components.d.ts +11 -0
  117. package/dist/required-host-components.d.ts.map +1 -0
  118. package/dist/required-host-components.js +25 -0
  119. package/dist/required-host-components.js.map +1 -0
  120. package/dist/resolve-api-url.d.ts +6 -0
  121. package/dist/resolve-api-url.d.ts.map +1 -0
  122. package/dist/resolve-api-url.js +39 -0
  123. package/dist/resolve-api-url.js.map +1 -0
  124. package/dist/rest-cache-admin.d.ts +33 -0
  125. package/dist/rest-cache-admin.d.ts.map +1 -0
  126. package/dist/rest-cache-admin.js +96 -0
  127. package/dist/rest-cache-admin.js.map +1 -0
  128. package/dist/scripts/postinstall.d.ts +4 -6
  129. package/dist/scripts/postinstall.d.ts.map +1 -1
  130. package/dist/scripts/postinstall.js +8 -41
  131. package/dist/scripts/postinstall.js.map +1 -1
  132. package/dist/self-host-compose.d.ts.map +1 -1
  133. package/dist/self-host-compose.js +24 -13
  134. package/dist/self-host-compose.js.map +1 -1
  135. package/dist/studio-dev-server.d.ts +4 -2
  136. package/dist/studio-dev-server.d.ts.map +1 -1
  137. package/dist/studio-dev-server.js +3 -2
  138. package/dist/studio-dev-server.js.map +1 -1
  139. package/dist/ui/brand.d.ts +2 -5
  140. package/dist/ui/brand.d.ts.map +1 -1
  141. package/dist/ui/brand.js +2 -7
  142. package/dist/ui/brand.js.map +1 -1
  143. package/dist/ui/clack-api.d.ts +44 -0
  144. package/dist/ui/clack-api.d.ts.map +1 -0
  145. package/dist/ui/clack-api.js +76 -0
  146. package/dist/ui/clack-api.js.map +1 -0
  147. package/dist/ui/clack.d.ts +14 -0
  148. package/dist/ui/clack.d.ts.map +1 -0
  149. package/dist/ui/clack.js +147 -0
  150. package/dist/ui/clack.js.map +1 -0
  151. package/dist/ui/components/FlowSpinner.d.ts +5 -0
  152. package/dist/ui/components/FlowSpinner.d.ts.map +1 -0
  153. package/dist/ui/components/FlowSpinner.js +8 -0
  154. package/dist/ui/components/FlowSpinner.js.map +1 -0
  155. package/dist/ui/components/LogoWordmark.d.ts +9 -0
  156. package/dist/ui/components/LogoWordmark.d.ts.map +1 -0
  157. package/dist/ui/components/LogoWordmark.js +17 -0
  158. package/dist/ui/components/LogoWordmark.js.map +1 -0
  159. package/dist/ui/components/PromptPanel.d.ts +5 -0
  160. package/dist/ui/components/PromptPanel.d.ts.map +1 -0
  161. package/dist/ui/components/PromptPanel.js +7 -0
  162. package/dist/ui/components/PromptPanel.js.map +1 -0
  163. package/dist/ui/components/SelectField.d.ts +11 -0
  164. package/dist/ui/components/SelectField.d.ts.map +1 -0
  165. package/dist/ui/components/SelectField.js +19 -0
  166. package/dist/ui/components/SelectField.js.map +1 -0
  167. package/dist/ui/confirm.d.ts +2 -1
  168. package/dist/ui/confirm.d.ts.map +1 -1
  169. package/dist/ui/confirm.js +4 -4
  170. package/dist/ui/confirm.js.map +1 -1
  171. package/dist/ui/dev/DevDashboard.d.ts +8 -0
  172. package/dist/ui/dev/DevDashboard.d.ts.map +1 -0
  173. package/dist/ui/dev/DevDashboard.js +151 -0
  174. package/dist/ui/dev/DevDashboard.js.map +1 -0
  175. package/dist/ui/dev/DevReadyPanelView.d.ts +7 -0
  176. package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -0
  177. package/dist/ui/dev/DevReadyPanelView.js +29 -0
  178. package/dist/ui/dev/DevReadyPanelView.js.map +1 -0
  179. package/dist/ui/fatal.d.ts.map +1 -1
  180. package/dist/ui/fatal.js +5 -11
  181. package/dist/ui/fatal.js.map +1 -1
  182. package/dist/ui/flows/FlowApp.d.ts +8 -0
  183. package/dist/ui/flows/FlowApp.d.ts.map +1 -0
  184. package/dist/ui/flows/FlowApp.js +42 -0
  185. package/dist/ui/flows/FlowApp.js.map +1 -0
  186. package/dist/ui/flows/prompt-fields.d.ts +48 -0
  187. package/dist/ui/flows/prompt-fields.d.ts.map +1 -0
  188. package/dist/ui/flows/prompt-fields.js +88 -0
  189. package/dist/ui/flows/prompt-fields.js.map +1 -0
  190. package/dist/ui/flows/types.d.ts +41 -0
  191. package/dist/ui/flows/types.d.ts.map +1 -0
  192. package/dist/ui/flows/types.js +2 -0
  193. package/dist/ui/flows/types.js.map +1 -0
  194. package/dist/ui/index.d.ts +1 -1
  195. package/dist/ui/index.js +1 -1
  196. package/dist/ui/messages.d.ts +1 -0
  197. package/dist/ui/messages.d.ts.map +1 -1
  198. package/dist/ui/messages.js +48 -8
  199. package/dist/ui/messages.js.map +1 -1
  200. package/dist/ui/progress.d.ts +5 -1
  201. package/dist/ui/progress.d.ts.map +1 -1
  202. package/dist/ui/progress.js +12 -3
  203. package/dist/ui/progress.js.map +1 -1
  204. package/dist/ui/prompts.d.ts +7 -5
  205. package/dist/ui/prompts.d.ts.map +1 -1
  206. package/dist/ui/prompts.js +15 -8
  207. package/dist/ui/prompts.js.map +1 -1
  208. package/dist/ui/runtime/cancel.d.ts +4 -0
  209. package/dist/ui/runtime/cancel.d.ts.map +1 -0
  210. package/dist/ui/runtime/cancel.js +6 -0
  211. package/dist/ui/runtime/cancel.js.map +1 -0
  212. package/dist/ui/runtime/command-chrome.d.ts +17 -0
  213. package/dist/ui/runtime/command-chrome.d.ts.map +1 -0
  214. package/dist/ui/runtime/command-chrome.js +123 -0
  215. package/dist/ui/runtime/command-chrome.js.map +1 -0
  216. package/dist/ui/runtime/dev-prompt-queue.d.ts +30 -0
  217. package/dist/ui/runtime/dev-prompt-queue.d.ts.map +1 -0
  218. package/dist/ui/runtime/dev-prompt-queue.js +57 -0
  219. package/dist/ui/runtime/dev-prompt-queue.js.map +1 -0
  220. package/dist/ui/runtime/flow-session.d.ts +4 -0
  221. package/dist/ui/runtime/flow-session.d.ts.map +1 -0
  222. package/dist/ui/runtime/flow-session.js +8 -0
  223. package/dist/ui/runtime/flow-session.js.map +1 -0
  224. package/dist/ui/runtime/run-clack-flow.d.ts +5 -0
  225. package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -0
  226. package/dist/ui/runtime/run-clack-flow.js +34 -0
  227. package/dist/ui/runtime/run-clack-flow.js.map +1 -0
  228. package/dist/ui/theme.d.ts +24 -0
  229. package/dist/ui/theme.d.ts.map +1 -0
  230. package/dist/ui/theme.js +34 -0
  231. package/dist/ui/theme.js.map +1 -0
  232. package/dist/valkey-sidecar.d.ts +17 -0
  233. package/dist/valkey-sidecar.d.ts.map +1 -0
  234. package/dist/valkey-sidecar.js +94 -0
  235. package/dist/valkey-sidecar.js.map +1 -0
  236. package/package.json +11 -3
  237. package/src/binary-cache.ts +61 -0
  238. package/src/binary-download-lock.ts +124 -0
  239. package/src/cli-package-version.ts +15 -0
  240. package/src/cli.ts +6 -2
  241. package/src/commands/add.ts +26 -19
  242. package/src/commands/admin.ts +380 -137
  243. package/src/commands/cache.ts +105 -5
  244. package/src/commands/dev.ts +29 -21
  245. package/src/commands/init.ts +217 -46
  246. package/src/commands/link-helpers.ts +30 -29
  247. package/src/commands/push.ts +3 -3
  248. package/src/compose-local-server-image.ts +68 -0
  249. package/src/compose-rename.ts +73 -0
  250. package/src/dev-compose.ts +175 -96
  251. package/src/dev-log-bus.ts +18 -0
  252. package/src/dev-log-filter.ts +42 -0
  253. package/src/dev-ports.ts +223 -0
  254. package/src/dev-ready-panel.ts +63 -0
  255. package/src/dev-session-lock.ts +98 -0
  256. package/src/dev-session.ts +98 -18
  257. package/src/dev-shutdown.ts +114 -9
  258. package/src/dev-task-colors.ts +22 -3
  259. package/src/docker-runtime.ts +1 -1
  260. package/src/engine-push-output.ts +5 -1
  261. package/src/ensure-binary.ts +21 -1
  262. package/src/ensure-component-binaries.ts +71 -0
  263. package/src/ensure-dev-api-config.ts +18 -0
  264. package/src/env-file.ts +37 -0
  265. package/src/init-dependency-versions.ts +69 -0
  266. package/src/init-package-json.ts +63 -0
  267. package/src/postgres-ctl.ts +2 -1
  268. package/src/required-host-components.ts +37 -0
  269. package/src/resolve-api-url.ts +43 -0
  270. package/src/rest-cache-admin.ts +135 -0
  271. package/src/scripts/postinstall.ts +9 -46
  272. package/src/self-host-compose.ts +23 -12
  273. package/src/studio-dev-server.ts +5 -4
  274. package/src/ui/README.md +13 -9
  275. package/src/ui/brand.ts +2 -8
  276. package/src/ui/clack-api.ts +123 -0
  277. package/src/ui/clack.ts +153 -0
  278. package/src/ui/components/FlowSpinner.tsx +15 -0
  279. package/src/ui/components/LogoWordmark.tsx +36 -0
  280. package/src/ui/components/PromptPanel.tsx +17 -0
  281. package/src/ui/components/SelectField.tsx +58 -0
  282. package/src/ui/confirm.ts +5 -4
  283. package/src/ui/dev/DevDashboard.tsx +213 -0
  284. package/src/ui/dev/DevReadyPanelView.tsx +67 -0
  285. package/src/ui/fatal.ts +5 -10
  286. package/src/ui/flows/FlowApp.tsx +67 -0
  287. package/src/ui/flows/prompt-fields.tsx +192 -0
  288. package/src/ui/flows/types.ts +50 -0
  289. package/src/ui/index.ts +1 -1
  290. package/src/ui/messages.ts +50 -8
  291. package/src/ui/progress.ts +14 -3
  292. package/src/ui/prompts.ts +17 -9
  293. package/src/ui/runtime/cancel.ts +6 -0
  294. package/src/ui/runtime/command-chrome.ts +147 -0
  295. package/src/ui/runtime/dev-prompt-queue.ts +84 -0
  296. package/src/ui/runtime/flow-session.ts +11 -0
  297. package/src/ui/runtime/run-clack-flow.tsx +52 -0
  298. package/src/ui/theme.ts +41 -0
  299. package/src/valkey-sidecar.ts +126 -0
  300. package/tests/admin-ensure.test.ts +59 -0
  301. package/tests/binary-cache-cloud-overrides.test.ts +2 -2
  302. package/tests/binary-download-lock.test.ts +41 -0
  303. package/tests/cli-help.test.ts +7 -4
  304. package/tests/cli-package-version.test.ts +15 -0
  305. package/tests/command-chrome-coverage.test.ts +149 -0
  306. package/tests/command-chrome-wrap.test.ts +51 -0
  307. package/tests/config.test.ts +8 -4
  308. package/tests/dev-ports.test.ts +41 -0
  309. package/tests/dev-ready-panel.test.ts +20 -0
  310. package/tests/dev-session-lock.test.ts +54 -0
  311. package/tests/dev-ui.test.ts +66 -3
  312. package/tests/docker-runtime.test.ts +9 -20
  313. package/tests/ensure-dev-api-config.test.ts +22 -0
  314. package/tests/init-dependency-versions.test.ts +62 -0
  315. package/tests/init-package-json.test.ts +18 -0
  316. package/tests/init.test.ts +30 -3
  317. package/tests/required-host-components.test.ts +51 -0
  318. package/tests/resolve-api-url.test.ts +23 -0
  319. package/tests/rest-cache-admin.test.ts +28 -0
  320. package/tests/runtime-contract.test.ts +12 -3
  321. package/tests/tsconfig.json +8 -2
  322. package/tests/ui-confirm.test.ts +13 -9
  323. package/tests/ui-messages.test.ts +4 -18
  324. package/tsconfig.json +1 -0
  325. package/tsconfig.tsbuildinfo +1 -1
  326. package/vitest.config.ts +3 -0
  327. package/dist/dev-tui.d.ts +0 -24
  328. package/dist/dev-tui.d.ts.map +0 -1
  329. package/dist/dev-tui.js +0 -188
  330. package/dist/dev-tui.js.map +0 -1
  331. package/src/dev-tui.ts +0 -232
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Detect and clean up Docker Compose state when `project.name` changes.
3
+ */
4
+
5
+ import { existsSync, readFileSync } from "node:fs"
6
+ import { join } from "node:path"
7
+ import { loadLocalEnvironment } from "./link.js"
8
+ import { composeStackHasContainers } from "./dev-session-lock.js"
9
+ import { composeProjectName, runDockerCompose, type SelfHostComposePaths } from "./self-host-compose.js"
10
+ import { confirm as uiConfirm } from "./ui/confirm.js"
11
+ import { isInteractive } from "./ui/interactive.js"
12
+ import { success, warn } from "./ui/messages.js"
13
+
14
+ /**
15
+ * When `project.name` changes, the compose project slug changes too (`supatype-{name}`).
16
+ * Offer to stop containers from the previous slug so volumes/ports do not linger.
17
+ */
18
+ export async function handleComposeProjectRename(
19
+ cwd: string,
20
+ currentProjectName: string,
21
+ paths: SelfHostComposePaths,
22
+ ): Promise<void> {
23
+ const local = loadLocalEnvironment(cwd)
24
+ const previousRef = local?.projectRef?.trim()
25
+ if (!previousRef || previousRef === currentProjectName) return
26
+
27
+ const previousCompose = composeProjectName(previousRef)
28
+ const currentCompose = composeProjectName(currentProjectName)
29
+ if (previousCompose === currentCompose) return
30
+ if (!composeStackHasContainers(previousCompose)) return
31
+
32
+ const message =
33
+ `Project renamed from "${previousRef}" to "${currentProjectName}".\n` +
34
+ `Docker stack "${previousCompose}" may still be running.`
35
+
36
+ if (!isInteractive()) {
37
+ warn(message)
38
+ warn(`Stop it manually: docker compose -p ${previousCompose} down`)
39
+ return
40
+ }
41
+
42
+ const stopOld = await uiConfirm(`${message}\n\nStop the old Docker stack now?`, { default: true })
43
+
44
+ if (!stopOld) {
45
+ warn(`Leaving "${previousCompose}" running. Stop it with: docker compose -p ${previousCompose} down`)
46
+ return
47
+ }
48
+
49
+ const status = runDockerCompose(paths.composePath, ["down"], cwd, previousCompose, { quiet: true })
50
+ if (status === 0) {
51
+ success(`Stopped old stack "${previousCompose}".`)
52
+ warnIfHardcodedComposeScripts(cwd, previousCompose)
53
+ } else {
54
+ warn(`Could not stop "${previousCompose}" (exit ${status}). Try: docker compose -p ${previousCompose} down`)
55
+ }
56
+ }
57
+
58
+ function warnIfHardcodedComposeScripts(cwd: string, oldComposeProject: string): void {
59
+ const pkgPath = join(cwd, "package.json")
60
+ if (!existsSync(pkgPath)) return
61
+ try {
62
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as { scripts?: Record<string, string> }
63
+ const scripts = Object.values(pkg.scripts ?? {}).join("\n")
64
+ if (scripts.includes(`-p ${oldComposeProject}`)) {
65
+ warn(
66
+ `package.json scripts still reference compose project "${oldComposeProject}". ` +
67
+ "Update them if you renamed project.name in supatype.config.ts.",
68
+ )
69
+ }
70
+ } catch {
71
+ // ignore invalid package.json
72
+ }
73
+ }
@@ -2,11 +2,11 @@
2
2
  * `supatype dev` when `provider: docker` — full self-host Compose stack (Kong gateway).
3
3
  */
4
4
 
5
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
5
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs"
6
6
  import { homedir } from "node:os"
7
7
  import { dirname, join, resolve } from "node:path"
8
8
  import { spawnSync } from "node:child_process"
9
- import { startProxyDevApp } from "./app/proxy-dev-app.js"
9
+ import { startProxyDevApp, resolveProxyDevScript } from "./app/proxy-dev-app.js"
10
10
  import { loadSchemaAst } from "./config.js"
11
11
  import {
12
12
  COMPOSE_DEV_KONG_PORT,
@@ -17,7 +17,11 @@ import {
17
17
  type SupatypeProjectConfig,
18
18
  } from "./project-config.js"
19
19
  import { signJwt } from "./jwt.js"
20
- import { isPortInUse } from "./postgres-ctl.js"
20
+ import { ensureDevDbPort, ensureKongPort } from "./dev-ports.js"
21
+ import { handleComposeProjectRename } from "./compose-rename.js"
22
+ import { recoverStaleDevSession, writeDevSessionLock } from "./dev-session-lock.js"
23
+ import { endDevSession, startDevSession } from "./dev-session.js"
24
+ import { ensureDevApiConfig } from "./ensure-dev-api-config.js"
21
25
  import {
22
26
  COMPOSE_PINNED_IMAGE_ENV_KEYS,
23
27
  composeDockerImageEnv,
@@ -30,10 +34,11 @@ import {
30
34
  } from "./self-host-compose.js"
31
35
  import type { DockerBrandOptions } from "./docker-runtime.js"
32
36
  import { hasEngineOverride } from "./binary-cache.js"
33
- import { startStudioViteDevServer } from "./studio-dev-server.js"
37
+ import { STUDIO_DEV_PORT, startStudioViteDevServer } from "./studio-dev-server.js"
38
+ import { ensureLocalServerDockerImage } from "./compose-local-server-image.js"
34
39
  import { ensureEngine, engineRequest, type DiffResult } from "./engine-client.js"
35
40
  import { writeSchemaSourcePushArtifacts, type SchemaSourcePushArtifacts } from "./schema-sources.js"
36
- import { endDevSession } from "./dev-session.js"
41
+ import { readEnvValue, upsertEnvFile } from "./env-file.js"
37
42
  import { writeLocalEnvironment } from "./link.js"
38
43
  import { registerDevShutdown } from "./dev-shutdown.js"
39
44
  import {
@@ -46,6 +51,8 @@ import { withAdminRoles } from "./studio-admin-roles.js"
46
51
  import { restoreSystemRelationTargets } from "./restore-system-relation-targets.js"
47
52
  import { provisionBucketsFromAst } from "./storage-provision.js"
48
53
  import type { ExtractedSchemaAstV2 } from "./schema-ast-v2.js"
54
+ import { ensureFirstAdminUserForProject } from "./commands/admin.js"
55
+ import { publishDevReady } from "./dev-ready-panel.js"
49
56
 
50
57
  const LOCAL_JWT_SECRET = "super-secret-jwt-token-with-at-least-32-characters-long"
51
58
 
@@ -70,30 +77,12 @@ export function composeDbUrl(): string {
70
77
 
71
78
  /**
72
79
  * Resolve the host Kong port for this project. Persisted in `.env` as
73
- * SUPATYPE_KONG_PORT so re-runs are stable; on first run it picks the default
74
- * (18473) or the next free port, so multiple projects can run concurrently.
80
+ * SUPATYPE_KONG_PORT; prompts when the configured port is already taken.
75
81
  */
76
82
  async function resolveDevDbPort(cwd: string): Promise<number> {
77
- const envPath = join(cwd, ".env")
78
- if (existsSync(envPath)) {
79
- const m = readFileSync(envPath, "utf8").match(/^SUPATYPE_DEV_DB_PORT=(\d+)/m)
80
- if (m && m[1]) return Number(m[1])
81
- }
82
- let port = COMPOSE_DEV_DB_PORT
83
- while (await isPortInUse(port)) port++
84
- return port
83
+ return ensureDevDbPort(cwd)
85
84
  }
86
85
 
87
- function readEnvValue(cwd: string, key: string, fallback: string): string {
88
- const envPath = join(cwd, ".env")
89
- if (existsSync(envPath)) {
90
- const m = readFileSync(envPath, "utf8").match(new RegExp(`^${key}=(.+)$`, "m"))
91
- if (m?.[1]) return m[1].trim()
92
- }
93
- return fallback
94
- }
95
-
96
- /** Postgres DSN for compose db when published to the host (local engine push). */
97
86
  function hostComposeDbUrl(cwd: string): string {
98
87
  const port = readEnvValue(cwd, "SUPATYPE_DEV_DB_PORT", String(COMPOSE_DEV_DB_PORT))
99
88
  const user = readEnvValue(cwd, "POSTGRES_USER", "supatype_admin")
@@ -175,42 +164,17 @@ export async function resolveHostEngineDatabaseUrl(
175
164
  }
176
165
 
177
166
  async function resolveKongPort(cwd: string): Promise<number> {
178
- const envPath = join(cwd, ".env")
179
- if (existsSync(envPath)) {
180
- const m = readFileSync(envPath, "utf8").match(/^SUPATYPE_KONG_PORT=(\d+)/m)
181
- if (m && m[1]) return Number(m[1])
182
- }
183
- let port = COMPOSE_DEV_KONG_PORT
184
- while (await isPortInUse(port)) port++
185
- return port
167
+ return ensureKongPort(cwd, { context: "dev" })
186
168
  }
187
169
 
188
- function upsertEnvFile(
189
- cwd: string,
190
- updates: Record<string, string>,
191
- removeKeys: readonly string[] = [],
192
- ): void {
193
- const envPath = join(cwd, ".env")
194
- const existing = existsSync(envPath) ? readFileSync(envPath, "utf8") : ""
195
- const keys = new Set([...Object.keys(updates), ...removeKeys])
196
- const kept = existing
197
- .split("\n")
198
- .filter((line) => {
199
- const key = line.split("=")[0]?.trim()
200
- return key && line.includes("=") && !keys.has(key)
201
- })
202
- const merged = [...kept, ...Object.entries(updates).map(([key, value]) => `${key}=${value}`)]
203
- writeFileSync(envPath, `${merged.join("\n").trimEnd()}\n`, "utf8")
204
- }
205
-
206
- /** Keep compose + Studio on the same freshly signed dev JWTs; sync optional image pins from config. */
207
- function ensureDevComposeEnv(
170
+ function upsertDevComposeEnv(
208
171
  cwd: string,
209
172
  config: SupatypeProjectConfig,
210
173
  anonKey: string,
211
174
  serviceRoleKey: string,
212
175
  kongPort: number,
213
176
  devDbPort?: number,
177
+ localServerImage?: string,
214
178
  ): void {
215
179
  const apiUrl = `http://localhost:${kongPort}`
216
180
  const imagePins = composeDockerImageEnv(config)
@@ -222,12 +186,14 @@ function ensureDevComposeEnv(
222
186
  ANON_KEY: anonKey,
223
187
  SERVICE_ROLE_KEY: serviceRoleKey,
224
188
  PUBLIC_SUPATYPE_ANON_KEY: anonKey,
189
+ VITE_SUPATYPE_ANON_KEY: anonKey,
225
190
  PUBLIC_SUPATYPE_URL: apiUrl,
226
191
  SUPATYPE_KONG_PORT: String(kongPort),
227
192
  API_EXTERNAL_URL: apiUrl,
228
193
  SITE_URL: apiUrl,
229
194
  GOTRUE_MAILER_AUTOCONFIRM: "true",
230
195
  ...imagePins,
196
+ ...(localServerImage !== undefined && { SUPATYPE_SERVER_IMAGE: localServerImage }),
231
197
  }
232
198
  if (devDbPort !== undefined) {
233
199
  updates.SUPATYPE_DEV_DB_PORT = String(devDbPort)
@@ -238,6 +204,19 @@ function ensureDevComposeEnv(
238
204
  upsertEnvFile(cwd, updates, removeImageKeys)
239
205
  }
240
206
 
207
+ /** Keep compose + Studio on the same freshly signed dev JWTs; sync optional image pins from config. */
208
+ function ensureDevComposeEnv(
209
+ cwd: string,
210
+ config: SupatypeProjectConfig,
211
+ anonKey: string,
212
+ serviceRoleKey: string,
213
+ kongPort: number,
214
+ devDbPort?: number,
215
+ localServerImage?: string,
216
+ ): void {
217
+ upsertDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort, localServerImage)
218
+ }
219
+
241
220
  async function waitComposeHealthy(paths: SelfHostComposePaths, cwd: string, maxMs: number, composeProject: string): Promise<void> {
242
221
  const composeDir = dirname(paths.composePath)
243
222
  const envFile = join(cwd, ".env")
@@ -271,6 +250,34 @@ async function waitKongReady(kongPort: number, maxSec: number): Promise<void> {
271
250
  throw new Error(`Kong gateway at ${base} did not become ready within ${maxSec}s`)
272
251
  }
273
252
 
253
+ /** Kong may be up while server → storage is still starting (503 or upstream errors). */
254
+ async function waitStorageApiReady(
255
+ kongPort: number,
256
+ serviceRoleKey: string,
257
+ maxSec: number,
258
+ ): Promise<void> {
259
+ const url = `http://localhost:${kongPort}/storage/v1/bucket`
260
+ const headers = { Authorization: `Bearer ${serviceRoleKey}` }
261
+ for (let i = 0; i < maxSec; i++) {
262
+ try {
263
+ const res = await fetch(url, { headers })
264
+ if (res.ok) return
265
+ const body = await res.text()
266
+ const kongUpstreamDown = body.includes("invalid response was received from the upstream server")
267
+ if (!kongUpstreamDown && res.status !== 503) {
268
+ // Non-transient storage response (e.g. 401) — stop waiting.
269
+ return
270
+ }
271
+ } catch {
272
+ /* retry */
273
+ }
274
+ await new Promise((r) => setTimeout(r, 1000))
275
+ }
276
+ console.warn(
277
+ `[supatype] Storage API at ${url} did not become ready within ${maxSec}s — bucket provisioning may fail.`,
278
+ )
279
+ }
280
+
274
281
  async function provisionDockerStorageBuckets(
275
282
  ast: ExtractedSchemaAstV2,
276
283
  kongPort: number,
@@ -668,8 +675,13 @@ export async function pushSchemaDocker(cwd: string, config: SupatypeProjectConfi
668
675
  exitComposeFailed(upGateway, "Could not start the Compose gateway stack.", pushBrand)
669
676
  }
670
677
  await waitKongReady(kongPort, 120)
678
+ await waitStorageApiReady(kongPort, serviceRoleKey, 90)
671
679
  await provisionDockerStorageBuckets(ast, kongPort, serviceRoleKey)
672
680
 
681
+ await ensureFirstAdminUserForProject(cwd, config, {
682
+ compose: { project, composePath: paths.composePath },
683
+ })
684
+
673
685
  console.log("[supatype] Schema pushed.")
674
686
  }
675
687
 
@@ -689,12 +701,56 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
689
701
  const anonKey = signJwt({ ...jwtBase, role: "anon" }, LOCAL_JWT_SECRET)
690
702
  const serviceRoleKey = signJwt({ ...jwtBase, role: "service_role" }, LOCAL_JWT_SECRET)
691
703
 
692
- ensureDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort)
704
+ const devBrand = { intro: "Local development" }
705
+ const localServerImage = await ensureLocalServerDockerImage(cwd, config, devBrand)
706
+
707
+ ensureDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort, localServerImage)
693
708
 
694
709
  console.log(`[supatype] provider: docker — starting self-host Compose stack (project ${project}, gateway :${kongPort})...`)
695
710
  const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
696
- const devBrand = { intro: "Local development" }
711
+ if (ensureDevApiConfig(cwd)) {
712
+ console.log("[supatype] API config written to .supatype/api-config.json")
713
+ }
697
714
 
715
+ type StudioProc = Awaited<ReturnType<typeof startStudioViteDevServer>>
716
+ type AppProc = ReturnType<typeof startProxyDevApp>
717
+ const shutdownState: {
718
+ studioProc: StudioProc
719
+ appProc: AppProc
720
+ schemaWatcher: import("node:fs").FSWatcher | null
721
+ debounceTimer: ReturnType<typeof setTimeout> | null
722
+ } = {
723
+ studioProc: null,
724
+ appProc: null,
725
+ schemaWatcher: null,
726
+ debounceTimer: null,
727
+ }
728
+
729
+ registerDevShutdown(async () => {
730
+ shutdownState.schemaWatcher?.close()
731
+ shutdownState.schemaWatcher = null
732
+ if (shutdownState.debounceTimer) {
733
+ clearTimeout(shutdownState.debounceTimer)
734
+ shutdownState.debounceTimer = null
735
+ }
736
+ console.log("[supatype] Shutting down compose...")
737
+ await shutdownState.studioProc?.stop()
738
+ await shutdownState.appProc?.stop()
739
+ const downStatus = runDockerCompose(paths.composePath, ["down"], cwd, project, { quiet: true })
740
+ if (downStatus === 0) {
741
+ console.log("[supatype] Compose stack stopped.")
742
+ } else {
743
+ console.warn(`[supatype] Compose down exited with status ${downStatus}.`)
744
+ }
745
+ }, {
746
+ cwd,
747
+ compose: { cwd, composePath: paths.composePath, composeProject: project },
748
+ })
749
+
750
+ await recoverStaleDevSession(cwd)
751
+ await handleComposeProjectRename(cwd, config.project.name, paths)
752
+
753
+ console.log("[supatype] Bringing up Docker Compose services...")
698
754
  const upStatus = runDockerCompose(paths.composePath, ["up", "-d"], cwd, project, {
699
755
  quiet: true,
700
756
  brand: devBrand,
@@ -712,8 +768,29 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
712
768
  console.error("[supatype] Initial schema push failed:", (e as Error).message),
713
769
  )
714
770
 
771
+ await ensureFirstAdminUserForProject(cwd, config, {
772
+ compose: { project, composePath: paths.composePath },
773
+ })
774
+
775
+ if (localServerImage !== undefined) {
776
+ console.log("[supatype] Recreating server with local image...")
777
+ const recreateStatus = runDockerCompose(
778
+ paths.composePath,
779
+ ["up", "-d", "--force-recreate", "--no-deps", "server"],
780
+ cwd,
781
+ project,
782
+ { quiet: true, brand: devBrand },
783
+ )
784
+ if (recreateStatus !== 0) {
785
+ endDevSession()
786
+ exitComposeFailed(recreateStatus, "Could not recreate the server container with the local image.", devBrand)
787
+ }
788
+ }
789
+
715
790
  console.log("[supatype] Waiting for API gateway...")
716
791
  await waitKongReady(kongPort, 120)
792
+ console.log("[supatype] Waiting for storage API...")
793
+ await waitStorageApiReady(kongPort, serviceRoleKey, 90)
717
794
 
718
795
  writeLocalEnvironment(cwd, {
719
796
  target: "local",
@@ -724,13 +801,23 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
724
801
  provider: "docker",
725
802
  })
726
803
 
804
+ writeDevSessionLock(cwd, {
805
+ composeProject: project,
806
+ projectRef: config.project.name,
807
+ composePath: paths.composePath,
808
+ kongPort,
809
+ startedAt: new Date().toISOString(),
810
+ })
811
+
727
812
  const ast = loadSchemaAst(schemaPath, cwd)
728
813
  await provisionDockerStorageBuckets(ast, kongPort, serviceRoleKey)
729
814
 
730
815
  const pidDir = join(homedir(), ".supatype", "projects", config.project.name, "pid")
731
816
  mkdirSync(pidDir, { recursive: true })
732
817
 
733
- let studioProc: Awaited<ReturnType<typeof startStudioViteDevServer>> = null
818
+ startDevSession()
819
+
820
+ let studioProc: StudioProc = null
734
821
  const studioOverride = config.overrides?.studio
735
822
  if (studioOverride) {
736
823
  studioProc = startStudioViteDevServer({
@@ -739,63 +826,55 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
739
826
  pidDir,
740
827
  serviceRoleKey,
741
828
  proxyTarget: `http://localhost:${kongPort}`,
742
- viteSupatypeUrl: `http://localhost:${kongPort}`,
829
+ viteSupatypeUrl: `http://localhost:${STUDIO_DEV_PORT}`,
743
830
  basePath: "/studio/",
744
831
  })
745
832
  studioProc?.start()
833
+ shutdownState.studioProc = studioProc
746
834
  if (studioProc) {
747
- console.log("[supatype] Studio Vite dev server (overrides.studio) — live reload at /studio/")
835
+ console.log(
836
+ `[supatype] Studio (overrides.studio) — live reload proxied at http://localhost:${kongPort}/studio/`,
837
+ )
748
838
  }
749
839
  }
750
840
 
751
- console.log(`
752
- [supatype] Services running (Docker Compose · project ${project}):
753
- API (Kong) http://localhost:${kongPort}
754
- REST API http://localhost:${kongPort}/rest/v1/
755
- Auth http://localhost:${kongPort}/auth/v1/
756
- Storage http://localhost:${kongPort}/storage/v1/
757
- Realtime ws://localhost:${kongPort}/realtime/v1/
758
- Studio http://localhost:${kongPort}/studio/
841
+ const links = [
842
+ { label: "API", url: `http://localhost:${kongPort}` },
843
+ { label: "REST", url: `http://localhost:${kongPort}/rest/v1/` },
844
+ { label: "Auth", url: `http://localhost:${kongPort}/auth/v1/` },
845
+ { label: "Storage", url: `http://localhost:${kongPort}/storage/v1/` },
846
+ { label: "Realtime", url: `ws://localhost:${kongPort}/realtime/v1/` },
847
+ ]
848
+ if (resolveProxyDevScript(config) !== null) {
849
+ links.push({ label: "App", url: `http://localhost:${kongPort}/` })
850
+ }
851
+ links.push({ label: "Studio", url: `http://localhost:${kongPort}/studio/` })
759
852
 
760
- API keys (local dev only):
761
- anon key ${anonKey}
762
- service_role ${serviceRoleKey}
853
+ const hints: string[] = []
854
+ if (existsSync(join(cwd, "seed.ts"))) {
855
+ hints.push("Demo data: pnpm seed")
856
+ }
763
857
 
764
- Press Ctrl+C to stop.
765
- `)
858
+ publishDevReady({
859
+ title: `Services running (Docker · ${project})`,
860
+ links,
861
+ anonKey,
862
+ serviceRoleKey,
863
+ ...(hints.length > 0 ? { hints } : {}),
864
+ })
766
865
 
767
866
  const appProc = startProxyDevApp(cwd, config, pidDir)
768
-
769
- let schemaWatcher: import("node:fs").FSWatcher | null = null
770
- let debounceTimer: ReturnType<typeof setTimeout> | null = null
771
-
772
- registerDevShutdown(async () => {
773
- schemaWatcher?.close()
774
- schemaWatcher = null
775
- if (debounceTimer) {
776
- clearTimeout(debounceTimer)
777
- debounceTimer = null
778
- }
779
- console.log("[supatype] Shutting down compose...")
780
- await studioProc?.stop()
781
- await appProc?.stop()
782
- const downStatus = runDockerCompose(paths.composePath, ["down"], cwd, project, { quiet: true })
783
- if (downStatus === 0) {
784
- console.log("[supatype] Compose stack stopped.")
785
- } else {
786
- console.warn(`[supatype] Compose down exited with status ${downStatus}.`)
787
- }
788
- })
867
+ shutdownState.appProc = appProc
789
868
 
790
869
  if (opts.watch) {
791
870
  const schemaDir = join(projectRootFromConfig(config, cwd), config.schema?.path ?? "schema/index.ts", "..")
792
871
  console.log(`[supatype] Watching ${schemaDir} for changes...`)
793
872
  const { watch } = await import("node:fs")
794
- schemaWatcher = watch(schemaDir, { recursive: true }, (_eventType, filename) => {
873
+ shutdownState.schemaWatcher = watch(schemaDir, { recursive: true }, (_eventType, filename) => {
795
874
  if (!filename?.endsWith(".ts")) return
796
- if (debounceTimer) clearTimeout(debounceTimer)
797
- debounceTimer = setTimeout(() => {
798
- debounceTimer = null
875
+ if (shutdownState.debounceTimer) clearTimeout(shutdownState.debounceTimer)
876
+ shutdownState.debounceTimer = setTimeout(() => {
877
+ shutdownState.debounceTimer = null
799
878
  console.log(`\n[supatype] Change detected in ${filename}, pushing schema...`)
800
879
  runComposeSchemaPushQueued(cwd, config, paths, schemaPath, project)
801
880
  .then(async () => {
@@ -2,6 +2,9 @@
2
2
  * Per-task log buffers for `supatype dev` TUI mode.
3
3
  */
4
4
 
5
+ import type { DevReadyPanel } from "./dev-ready-panel.js"
6
+ import { devReadyPanelRowCount } from "./dev-ready-panel.js"
7
+
5
8
  export type DevLogLevel = "log" | "warn" | "error"
6
9
 
7
10
  export interface DevTask {
@@ -18,6 +21,7 @@ export class DevLogBus {
18
21
  private readonly order: string[] = []
19
22
  private readonly listeners = new Set<() => void>()
20
23
  private focusedTaskId = "stack"
24
+ private readyPanel: DevReadyPanel | null = null
21
25
 
22
26
  constructor(private readonly maxLines = DEFAULT_MAX_LINES) {
23
27
  this.registerTask("stack", "supatype")
@@ -66,6 +70,20 @@ export class DevLogBus {
66
70
  if (prev) this.setFocusedTaskId(prev)
67
71
  }
68
72
 
73
+ setReadyPanel(panel: DevReadyPanel): void {
74
+ this.readyPanel = panel
75
+ this.notify()
76
+ }
77
+
78
+ getReadyPanel(): DevReadyPanel | null {
79
+ return this.readyPanel
80
+ }
81
+
82
+ readyPanelRowCount(): number {
83
+ if (!this.readyPanel) return 0
84
+ return devReadyPanelRowCount(this.readyPanel)
85
+ }
86
+
69
87
  append(taskId: string, line: string, level: DevLogLevel = "log"): void {
70
88
  this.ensureTask(taskId, taskId)
71
89
  const task = this.tasks.get(taskId)
@@ -2,6 +2,8 @@
2
2
  * Line filters for subprocess output during `supatype dev`.
3
3
  */
4
4
 
5
+ import { isComposeProgressLine } from "./engine-push-output.js"
6
+
5
7
  /** Strip ANSI SGR sequences for TUI rendering. */
6
8
  export function stripAnsi(text: string): string {
7
9
  return text.replace(/\x1b\[[0-9;]*m/g, "")
@@ -17,6 +19,46 @@ export function filterDevSubprocessLine(taskId: string, line: string): boolean {
17
19
  return true
18
20
  }
19
21
 
22
+ const DOCKER_BUILD_NOISE =
23
+ /^#\d+\s|^#\d+ \[|^\s*=> \[|^\s*=>\s|^\s*--->\s|^\s*\d+\.\d+s\s|^\s*CACHED\s|^\s*DONE\s[\d.]+s?$|^Step \d+\/\d+|^Successfully (built|tagged)|^WARNING:|^DEPRECATED:|^View build details:|^What's next:|^The push refers to|^Using docker|^Sending build context|^Pulling fs layer|^Waiting$|^Verifying Checksum|^Download complete|^Pull complete|^Extracting \[|^Already exists$|^Building dependency tree|^Reading state information|^Get:\d+ |^Fetched \d+ |^Reading package lists|^Preparing to unpack|^Unpacking |^Setting up /i
24
+
25
+ /** Drop docker build / compose progress from the orchestrator (stack) pane. */
26
+ export function filterStackLogLine(line: string): boolean {
27
+ const t = stripAnsi(line).trim()
28
+ if (!t) return false
29
+
30
+ if (t.startsWith("✗ ") || t.startsWith("⚠ ")) return true
31
+ if (/https?:\/\//.test(t) || /^ws:\/\//.test(t)) return true
32
+ if (/\[supatype\]/.test(line)) return true
33
+ if (/^Services running/i.test(t)) return true
34
+ if (/^(anon key|service_role|API \(Kong\)|REST API|Auth|Storage|Realtime|Studio|App|Demo data|Postgres|supatype-server|Press Ctrl)/.test(t)) {
35
+ return true
36
+ }
37
+ if (/^Applied \d+ operation|^Schema up to date|^Schema pushed|^Watching .+ for changes|^Change detected|^Shutting down|^Compose stack stopped|^Local server image ready|^Building local server|^Waiting for |^Recreating server|^API config written|^provider: docker|^Bringing up Docker|^Schema push failed|^Initial schema push failed|^Storage bucket|^Storage API at|^Local binary overrides active|^Resolving component binaries|^Postgres |^Demo data:|^JWT secret:/.test(t)) {
38
+ return true
39
+ }
40
+ if (/^\S+\s+→\s/.test(t)) return true
41
+ if (isComposeProgressLine(t)) return false
42
+ if (DOCKER_BUILD_NOISE.test(t)) return false
43
+ if (/^Container\s+/i.test(t)) return false
44
+ if (/^Image\s+/i.test(t)) return false
45
+ if (/^network\s+\S+\s+(Creating|Created)/i.test(t)) return false
46
+ if (/^volume\s+\S+\s+(Creating|Created)/i.test(t)) return false
47
+
48
+ return false
49
+ }
50
+
51
+ /** Drop docker noise only — keep diff tables, push status, and command output. */
52
+ export function filterCommandLogLine(line: string): boolean {
53
+ const t = stripAnsi(line).trim()
54
+ if (!t) return false
55
+ if (isComposeProgressLine(t)) return false
56
+ if (DOCKER_BUILD_NOISE.test(t)) return false
57
+ if (/^Container\s+/i.test(t)) return false
58
+ if (/^Image\s+/i.test(t)) return false
59
+ return true
60
+ }
61
+
20
62
  /** Format console.* arguments the way Node does for a single log line. */
21
63
  export function formatConsoleArgs(args: unknown[]): string {
22
64
  return args