@supatype/cli 0.1.1 → 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 (316) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +114 -90
  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.map +1 -1
  22. package/dist/commands/admin.js +22 -6
  23. package/dist/commands/admin.js.map +1 -1
  24. package/dist/commands/cache.d.ts +1 -1
  25. package/dist/commands/cache.d.ts.map +1 -1
  26. package/dist/commands/cache.js +98 -5
  27. package/dist/commands/cache.js.map +1 -1
  28. package/dist/commands/dev.d.ts.map +1 -1
  29. package/dist/commands/dev.js +26 -22
  30. package/dist/commands/dev.js.map +1 -1
  31. package/dist/commands/init.d.ts +3 -4
  32. package/dist/commands/init.d.ts.map +1 -1
  33. package/dist/commands/init.js +238 -168
  34. package/dist/commands/init.js.map +1 -1
  35. package/dist/commands/link-helpers.d.ts.map +1 -1
  36. package/dist/commands/link-helpers.js +26 -25
  37. package/dist/commands/link-helpers.js.map +1 -1
  38. package/dist/compose-local-server-image.d.ts +13 -0
  39. package/dist/compose-local-server-image.d.ts.map +1 -0
  40. package/dist/compose-local-server-image.js +57 -0
  41. package/dist/compose-local-server-image.js.map +1 -0
  42. package/dist/compose-rename.d.ts.map +1 -1
  43. package/dist/compose-rename.js +5 -8
  44. package/dist/compose-rename.js.map +1 -1
  45. package/dist/dev-compose.d.ts.map +1 -1
  46. package/dist/dev-compose.js +111 -54
  47. package/dist/dev-compose.js.map +1 -1
  48. package/dist/dev-log-bus.d.ts +5 -0
  49. package/dist/dev-log-bus.d.ts.map +1 -1
  50. package/dist/dev-log-bus.js +14 -0
  51. package/dist/dev-log-bus.js.map +1 -1
  52. package/dist/dev-log-filter.d.ts +4 -0
  53. package/dist/dev-log-filter.d.ts.map +1 -1
  54. package/dist/dev-log-filter.js +52 -0
  55. package/dist/dev-log-filter.js.map +1 -1
  56. package/dist/dev-ports.d.ts.map +1 -1
  57. package/dist/dev-ports.js +19 -12
  58. package/dist/dev-ports.js.map +1 -1
  59. package/dist/dev-ready-panel.d.ts +19 -0
  60. package/dist/dev-ready-panel.d.ts.map +1 -0
  61. package/dist/dev-ready-panel.js +49 -0
  62. package/dist/dev-ready-panel.js.map +1 -0
  63. package/dist/dev-session-lock.d.ts.map +1 -1
  64. package/dist/dev-session-lock.js +5 -8
  65. package/dist/dev-session-lock.js.map +1 -1
  66. package/dist/dev-session.d.ts +18 -5
  67. package/dist/dev-session.d.ts.map +1 -1
  68. package/dist/dev-session.js +91 -18
  69. package/dist/dev-session.js.map +1 -1
  70. package/dist/dev-shutdown.d.ts +2 -0
  71. package/dist/dev-shutdown.d.ts.map +1 -1
  72. package/dist/dev-shutdown.js +29 -15
  73. package/dist/dev-shutdown.js.map +1 -1
  74. package/dist/dev-task-colors.d.ts +3 -1
  75. package/dist/dev-task-colors.d.ts.map +1 -1
  76. package/dist/dev-task-colors.js +23 -3
  77. package/dist/dev-task-colors.js.map +1 -1
  78. package/dist/docker-runtime.js +1 -1
  79. package/dist/docker-runtime.js.map +1 -1
  80. package/dist/engine-push-output.d.ts +1 -0
  81. package/dist/engine-push-output.d.ts.map +1 -1
  82. package/dist/engine-push-output.js +4 -1
  83. package/dist/engine-push-output.js.map +1 -1
  84. package/dist/ensure-binary.d.ts.map +1 -1
  85. package/dist/ensure-binary.js +14 -2
  86. package/dist/ensure-binary.js.map +1 -1
  87. package/dist/ensure-component-binaries.d.ts +17 -0
  88. package/dist/ensure-component-binaries.d.ts.map +1 -0
  89. package/dist/ensure-component-binaries.js +45 -0
  90. package/dist/ensure-component-binaries.js.map +1 -0
  91. package/dist/ensure-dev-api-config.d.ts +3 -0
  92. package/dist/ensure-dev-api-config.d.ts.map +1 -0
  93. package/dist/ensure-dev-api-config.js +17 -0
  94. package/dist/ensure-dev-api-config.js.map +1 -0
  95. package/dist/init-dependency-versions.d.ts +13 -0
  96. package/dist/init-dependency-versions.d.ts.map +1 -0
  97. package/dist/init-dependency-versions.js +53 -0
  98. package/dist/init-dependency-versions.js.map +1 -0
  99. package/dist/init-package-json.d.ts +21 -0
  100. package/dist/init-package-json.d.ts.map +1 -0
  101. package/dist/init-package-json.js +46 -0
  102. package/dist/init-package-json.js.map +1 -0
  103. package/dist/postgres-ctl.d.ts.map +1 -1
  104. package/dist/postgres-ctl.js +2 -1
  105. package/dist/postgres-ctl.js.map +1 -1
  106. package/dist/required-host-components.d.ts +11 -0
  107. package/dist/required-host-components.d.ts.map +1 -0
  108. package/dist/required-host-components.js +25 -0
  109. package/dist/required-host-components.js.map +1 -0
  110. package/dist/resolve-api-url.d.ts +6 -0
  111. package/dist/resolve-api-url.d.ts.map +1 -0
  112. package/dist/resolve-api-url.js +39 -0
  113. package/dist/resolve-api-url.js.map +1 -0
  114. package/dist/rest-cache-admin.d.ts +33 -0
  115. package/dist/rest-cache-admin.d.ts.map +1 -0
  116. package/dist/rest-cache-admin.js +96 -0
  117. package/dist/rest-cache-admin.js.map +1 -0
  118. package/dist/scripts/postinstall.d.ts +4 -6
  119. package/dist/scripts/postinstall.d.ts.map +1 -1
  120. package/dist/scripts/postinstall.js +8 -41
  121. package/dist/scripts/postinstall.js.map +1 -1
  122. package/dist/self-host-compose.d.ts.map +1 -1
  123. package/dist/self-host-compose.js +23 -13
  124. package/dist/self-host-compose.js.map +1 -1
  125. package/dist/studio-dev-server.d.ts +4 -2
  126. package/dist/studio-dev-server.d.ts.map +1 -1
  127. package/dist/studio-dev-server.js +3 -2
  128. package/dist/studio-dev-server.js.map +1 -1
  129. package/dist/ui/brand.d.ts +2 -5
  130. package/dist/ui/brand.d.ts.map +1 -1
  131. package/dist/ui/brand.js +2 -7
  132. package/dist/ui/brand.js.map +1 -1
  133. package/dist/ui/clack-api.d.ts +44 -0
  134. package/dist/ui/clack-api.d.ts.map +1 -0
  135. package/dist/ui/clack-api.js +76 -0
  136. package/dist/ui/clack-api.js.map +1 -0
  137. package/dist/ui/clack.d.ts +14 -0
  138. package/dist/ui/clack.d.ts.map +1 -0
  139. package/dist/ui/clack.js +147 -0
  140. package/dist/ui/clack.js.map +1 -0
  141. package/dist/ui/components/FlowSpinner.d.ts +5 -0
  142. package/dist/ui/components/FlowSpinner.d.ts.map +1 -0
  143. package/dist/ui/components/FlowSpinner.js +8 -0
  144. package/dist/ui/components/FlowSpinner.js.map +1 -0
  145. package/dist/ui/components/LogoWordmark.d.ts +9 -0
  146. package/dist/ui/components/LogoWordmark.d.ts.map +1 -0
  147. package/dist/ui/components/LogoWordmark.js +17 -0
  148. package/dist/ui/components/LogoWordmark.js.map +1 -0
  149. package/dist/ui/components/PromptPanel.d.ts +5 -0
  150. package/dist/ui/components/PromptPanel.d.ts.map +1 -0
  151. package/dist/ui/components/PromptPanel.js +7 -0
  152. package/dist/ui/components/PromptPanel.js.map +1 -0
  153. package/dist/ui/components/SelectField.d.ts +11 -0
  154. package/dist/ui/components/SelectField.d.ts.map +1 -0
  155. package/dist/ui/components/SelectField.js +19 -0
  156. package/dist/ui/components/SelectField.js.map +1 -0
  157. package/dist/ui/confirm.d.ts +2 -1
  158. package/dist/ui/confirm.d.ts.map +1 -1
  159. package/dist/ui/confirm.js +4 -4
  160. package/dist/ui/confirm.js.map +1 -1
  161. package/dist/ui/dev/DevDashboard.d.ts +8 -0
  162. package/dist/ui/dev/DevDashboard.d.ts.map +1 -0
  163. package/dist/ui/dev/DevDashboard.js +151 -0
  164. package/dist/ui/dev/DevDashboard.js.map +1 -0
  165. package/dist/ui/dev/DevReadyPanelView.d.ts +7 -0
  166. package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -0
  167. package/dist/ui/dev/DevReadyPanelView.js +29 -0
  168. package/dist/ui/dev/DevReadyPanelView.js.map +1 -0
  169. package/dist/ui/fatal.d.ts.map +1 -1
  170. package/dist/ui/fatal.js +5 -11
  171. package/dist/ui/fatal.js.map +1 -1
  172. package/dist/ui/flows/FlowApp.d.ts +8 -0
  173. package/dist/ui/flows/FlowApp.d.ts.map +1 -0
  174. package/dist/ui/flows/FlowApp.js +42 -0
  175. package/dist/ui/flows/FlowApp.js.map +1 -0
  176. package/dist/ui/flows/prompt-fields.d.ts +48 -0
  177. package/dist/ui/flows/prompt-fields.d.ts.map +1 -0
  178. package/dist/ui/flows/prompt-fields.js +88 -0
  179. package/dist/ui/flows/prompt-fields.js.map +1 -0
  180. package/dist/ui/flows/types.d.ts +41 -0
  181. package/dist/ui/flows/types.d.ts.map +1 -0
  182. package/dist/ui/flows/types.js +2 -0
  183. package/dist/ui/flows/types.js.map +1 -0
  184. package/dist/ui/index.d.ts +1 -1
  185. package/dist/ui/index.js +1 -1
  186. package/dist/ui/messages.d.ts +1 -0
  187. package/dist/ui/messages.d.ts.map +1 -1
  188. package/dist/ui/messages.js +48 -8
  189. package/dist/ui/messages.js.map +1 -1
  190. package/dist/ui/progress.d.ts +5 -1
  191. package/dist/ui/progress.d.ts.map +1 -1
  192. package/dist/ui/progress.js +12 -3
  193. package/dist/ui/progress.js.map +1 -1
  194. package/dist/ui/prompts.d.ts +7 -5
  195. package/dist/ui/prompts.d.ts.map +1 -1
  196. package/dist/ui/prompts.js +15 -8
  197. package/dist/ui/prompts.js.map +1 -1
  198. package/dist/ui/runtime/cancel.d.ts +4 -0
  199. package/dist/ui/runtime/cancel.d.ts.map +1 -0
  200. package/dist/ui/runtime/cancel.js +6 -0
  201. package/dist/ui/runtime/cancel.js.map +1 -0
  202. package/dist/ui/runtime/command-chrome.d.ts +17 -0
  203. package/dist/ui/runtime/command-chrome.d.ts.map +1 -0
  204. package/dist/ui/runtime/command-chrome.js +123 -0
  205. package/dist/ui/runtime/command-chrome.js.map +1 -0
  206. package/dist/ui/runtime/dev-prompt-queue.d.ts +30 -0
  207. package/dist/ui/runtime/dev-prompt-queue.d.ts.map +1 -0
  208. package/dist/ui/runtime/dev-prompt-queue.js +57 -0
  209. package/dist/ui/runtime/dev-prompt-queue.js.map +1 -0
  210. package/dist/ui/runtime/flow-session.d.ts +4 -0
  211. package/dist/ui/runtime/flow-session.d.ts.map +1 -0
  212. package/dist/ui/runtime/flow-session.js +8 -0
  213. package/dist/ui/runtime/flow-session.js.map +1 -0
  214. package/dist/ui/runtime/run-clack-flow.d.ts +5 -0
  215. package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -0
  216. package/dist/ui/runtime/run-clack-flow.js +34 -0
  217. package/dist/ui/runtime/run-clack-flow.js.map +1 -0
  218. package/dist/ui/theme.d.ts +24 -0
  219. package/dist/ui/theme.d.ts.map +1 -0
  220. package/dist/ui/theme.js +34 -0
  221. package/dist/ui/theme.js.map +1 -0
  222. package/dist/valkey-sidecar.d.ts +17 -0
  223. package/dist/valkey-sidecar.d.ts.map +1 -0
  224. package/dist/valkey-sidecar.js +94 -0
  225. package/dist/valkey-sidecar.js.map +1 -0
  226. package/package.json +9 -3
  227. package/src/binary-cache.ts +61 -0
  228. package/src/binary-download-lock.ts +124 -0
  229. package/src/cli-package-version.ts +15 -0
  230. package/src/cli.ts +6 -2
  231. package/src/commands/add.ts +26 -19
  232. package/src/commands/admin.ts +24 -6
  233. package/src/commands/cache.ts +105 -5
  234. package/src/commands/dev.ts +26 -21
  235. package/src/commands/init.ts +124 -45
  236. package/src/commands/link-helpers.ts +30 -29
  237. package/src/compose-local-server-image.ts +68 -0
  238. package/src/compose-rename.ts +5 -8
  239. package/src/dev-compose.ts +136 -51
  240. package/src/dev-log-bus.ts +18 -0
  241. package/src/dev-log-filter.ts +42 -0
  242. package/src/dev-ports.ts +22 -11
  243. package/src/dev-ready-panel.ts +63 -0
  244. package/src/dev-session-lock.ts +5 -8
  245. package/src/dev-session.ts +98 -18
  246. package/src/dev-shutdown.ts +29 -17
  247. package/src/dev-task-colors.ts +22 -3
  248. package/src/docker-runtime.ts +1 -1
  249. package/src/engine-push-output.ts +5 -1
  250. package/src/ensure-binary.ts +21 -1
  251. package/src/ensure-component-binaries.ts +71 -0
  252. package/src/ensure-dev-api-config.ts +18 -0
  253. package/src/init-dependency-versions.ts +69 -0
  254. package/src/init-package-json.ts +63 -0
  255. package/src/postgres-ctl.ts +2 -1
  256. package/src/required-host-components.ts +37 -0
  257. package/src/resolve-api-url.ts +43 -0
  258. package/src/rest-cache-admin.ts +135 -0
  259. package/src/scripts/postinstall.ts +9 -46
  260. package/src/self-host-compose.ts +22 -12
  261. package/src/studio-dev-server.ts +5 -4
  262. package/src/ui/README.md +13 -9
  263. package/src/ui/brand.ts +2 -8
  264. package/src/ui/clack-api.ts +123 -0
  265. package/src/ui/clack.ts +153 -0
  266. package/src/ui/components/FlowSpinner.tsx +15 -0
  267. package/src/ui/components/LogoWordmark.tsx +36 -0
  268. package/src/ui/components/PromptPanel.tsx +17 -0
  269. package/src/ui/components/SelectField.tsx +58 -0
  270. package/src/ui/confirm.ts +5 -4
  271. package/src/ui/dev/DevDashboard.tsx +213 -0
  272. package/src/ui/dev/DevReadyPanelView.tsx +67 -0
  273. package/src/ui/fatal.ts +5 -10
  274. package/src/ui/flows/FlowApp.tsx +67 -0
  275. package/src/ui/flows/prompt-fields.tsx +192 -0
  276. package/src/ui/flows/types.ts +50 -0
  277. package/src/ui/index.ts +1 -1
  278. package/src/ui/messages.ts +50 -8
  279. package/src/ui/progress.ts +14 -3
  280. package/src/ui/prompts.ts +17 -9
  281. package/src/ui/runtime/cancel.ts +6 -0
  282. package/src/ui/runtime/command-chrome.ts +147 -0
  283. package/src/ui/runtime/dev-prompt-queue.ts +84 -0
  284. package/src/ui/runtime/flow-session.ts +11 -0
  285. package/src/ui/runtime/run-clack-flow.tsx +52 -0
  286. package/src/ui/theme.ts +41 -0
  287. package/src/valkey-sidecar.ts +126 -0
  288. package/tests/binary-cache-cloud-overrides.test.ts +2 -2
  289. package/tests/binary-download-lock.test.ts +41 -0
  290. package/tests/cli-help.test.ts +7 -4
  291. package/tests/cli-package-version.test.ts +15 -0
  292. package/tests/command-chrome-coverage.test.ts +149 -0
  293. package/tests/command-chrome-wrap.test.ts +51 -0
  294. package/tests/config.test.ts +8 -4
  295. package/tests/dev-ready-panel.test.ts +20 -0
  296. package/tests/dev-ui.test.ts +42 -2
  297. package/tests/docker-runtime.test.ts +9 -20
  298. package/tests/ensure-dev-api-config.test.ts +22 -0
  299. package/tests/init-dependency-versions.test.ts +62 -0
  300. package/tests/init-package-json.test.ts +18 -0
  301. package/tests/init.test.ts +30 -3
  302. package/tests/required-host-components.test.ts +51 -0
  303. package/tests/resolve-api-url.test.ts +23 -0
  304. package/tests/rest-cache-admin.test.ts +28 -0
  305. package/tests/runtime-contract.test.ts +12 -3
  306. package/tests/tsconfig.json +8 -2
  307. package/tests/ui-confirm.test.ts +13 -9
  308. package/tests/ui-messages.test.ts +4 -18
  309. package/tsconfig.json +1 -0
  310. package/tsconfig.tsbuildinfo +1 -1
  311. package/vitest.config.ts +3 -0
  312. package/dist/dev-tui.d.ts +0 -24
  313. package/dist/dev-tui.d.ts.map +0 -1
  314. package/dist/dev-tui.js +0 -188
  315. package/dist/dev-tui.js.map +0 -1
  316. package/src/dev-tui.ts +0 -232
@@ -4,12 +4,12 @@
4
4
 
5
5
  import { existsSync, readFileSync } from "node:fs"
6
6
  import { join } from "node:path"
7
- import * as p from "@clack/prompts"
8
7
  import { loadLocalEnvironment } from "./link.js"
9
8
  import { composeStackHasContainers } from "./dev-session-lock.js"
10
9
  import { composeProjectName, runDockerCompose, type SelfHostComposePaths } from "./self-host-compose.js"
10
+ import { confirm as uiConfirm } from "./ui/confirm.js"
11
11
  import { isInteractive } from "./ui/interactive.js"
12
- import { warn } from "./ui/messages.js"
12
+ import { success, warn } from "./ui/messages.js"
13
13
 
14
14
  /**
15
15
  * When `project.name` changes, the compose project slug changes too (`supatype-{name}`).
@@ -39,19 +39,16 @@ export async function handleComposeProjectRename(
39
39
  return
40
40
  }
41
41
 
42
- const stopOld = await p.confirm({
43
- message: `${message}\n\nStop the old Docker stack now?`,
44
- initialValue: true,
45
- })
42
+ const stopOld = await uiConfirm(`${message}\n\nStop the old Docker stack now?`, { default: true })
46
43
 
47
- if (p.isCancel(stopOld) || !stopOld) {
44
+ if (!stopOld) {
48
45
  warn(`Leaving "${previousCompose}" running. Stop it with: docker compose -p ${previousCompose} down`)
49
46
  return
50
47
  }
51
48
 
52
49
  const status = runDockerCompose(paths.composePath, ["down"], cwd, previousCompose, { quiet: true })
53
50
  if (status === 0) {
54
- p.log.success(`Stopped old stack "${previousCompose}".`)
51
+ success(`Stopped old stack "${previousCompose}".`)
55
52
  warnIfHardcodedComposeScripts(cwd, previousCompose)
56
53
  } else {
57
54
  warn(`Could not stop "${previousCompose}" (exit ${status}). Try: docker compose -p ${previousCompose} down`)
@@ -6,7 +6,7 @@ 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,
@@ -20,7 +20,8 @@ import { signJwt } from "./jwt.js"
20
20
  import { ensureDevDbPort, ensureKongPort } from "./dev-ports.js"
21
21
  import { handleComposeProjectRename } from "./compose-rename.js"
22
22
  import { recoverStaleDevSession, writeDevSessionLock } from "./dev-session-lock.js"
23
- import { readEnvValue, upsertEnvFile } from "./env-file.js"
23
+ import { endDevSession, startDevSession } from "./dev-session.js"
24
+ import { ensureDevApiConfig } from "./ensure-dev-api-config.js"
24
25
  import {
25
26
  COMPOSE_PINNED_IMAGE_ENV_KEYS,
26
27
  composeDockerImageEnv,
@@ -33,10 +34,11 @@ import {
33
34
  } from "./self-host-compose.js"
34
35
  import type { DockerBrandOptions } from "./docker-runtime.js"
35
36
  import { hasEngineOverride } from "./binary-cache.js"
36
- 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"
37
39
  import { ensureEngine, engineRequest, type DiffResult } from "./engine-client.js"
38
40
  import { writeSchemaSourcePushArtifacts, type SchemaSourcePushArtifacts } from "./schema-sources.js"
39
- import { endDevSession } from "./dev-session.js"
41
+ import { readEnvValue, upsertEnvFile } from "./env-file.js"
40
42
  import { writeLocalEnvironment } from "./link.js"
41
43
  import { registerDevShutdown } from "./dev-shutdown.js"
42
44
  import {
@@ -50,6 +52,7 @@ import { restoreSystemRelationTargets } from "./restore-system-relation-targets.
50
52
  import { provisionBucketsFromAst } from "./storage-provision.js"
51
53
  import type { ExtractedSchemaAstV2 } from "./schema-ast-v2.js"
52
54
  import { ensureFirstAdminUserForProject } from "./commands/admin.js"
55
+ import { publishDevReady } from "./dev-ready-panel.js"
53
56
 
54
57
  const LOCAL_JWT_SECRET = "super-secret-jwt-token-with-at-least-32-characters-long"
55
58
 
@@ -171,6 +174,7 @@ function upsertDevComposeEnv(
171
174
  serviceRoleKey: string,
172
175
  kongPort: number,
173
176
  devDbPort?: number,
177
+ localServerImage?: string,
174
178
  ): void {
175
179
  const apiUrl = `http://localhost:${kongPort}`
176
180
  const imagePins = composeDockerImageEnv(config)
@@ -189,6 +193,7 @@ function upsertDevComposeEnv(
189
193
  SITE_URL: apiUrl,
190
194
  GOTRUE_MAILER_AUTOCONFIRM: "true",
191
195
  ...imagePins,
196
+ ...(localServerImage !== undefined && { SUPATYPE_SERVER_IMAGE: localServerImage }),
192
197
  }
193
198
  if (devDbPort !== undefined) {
194
199
  updates.SUPATYPE_DEV_DB_PORT = String(devDbPort)
@@ -207,8 +212,9 @@ function ensureDevComposeEnv(
207
212
  serviceRoleKey: string,
208
213
  kongPort: number,
209
214
  devDbPort?: number,
215
+ localServerImage?: string,
210
216
  ): void {
211
- upsertDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort)
217
+ upsertDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort, localServerImage)
212
218
  }
213
219
 
214
220
  async function waitComposeHealthy(paths: SelfHostComposePaths, cwd: string, maxMs: number, composeProject: string): Promise<void> {
@@ -244,6 +250,34 @@ async function waitKongReady(kongPort: number, maxSec: number): Promise<void> {
244
250
  throw new Error(`Kong gateway at ${base} did not become ready within ${maxSec}s`)
245
251
  }
246
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
+
247
281
  async function provisionDockerStorageBuckets(
248
282
  ast: ExtractedSchemaAstV2,
249
283
  kongPort: number,
@@ -641,6 +675,7 @@ export async function pushSchemaDocker(cwd: string, config: SupatypeProjectConfi
641
675
  exitComposeFailed(upGateway, "Could not start the Compose gateway stack.", pushBrand)
642
676
  }
643
677
  await waitKongReady(kongPort, 120)
678
+ await waitStorageApiReady(kongPort, serviceRoleKey, 90)
644
679
  await provisionDockerStorageBuckets(ast, kongPort, serviceRoleKey)
645
680
 
646
681
  await ensureFirstAdminUserForProject(cwd, config, {
@@ -666,14 +701,56 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
666
701
  const anonKey = signJwt({ ...jwtBase, role: "anon" }, LOCAL_JWT_SECRET)
667
702
  const serviceRoleKey = signJwt({ ...jwtBase, role: "service_role" }, LOCAL_JWT_SECRET)
668
703
 
669
- 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)
670
708
 
671
709
  console.log(`[supatype] provider: docker — starting self-host Compose stack (project ${project}, gateway :${kongPort})...`)
672
710
  const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
711
+ if (ensureDevApiConfig(cwd)) {
712
+ console.log("[supatype] API config written to .supatype/api-config.json")
713
+ }
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
+
673
750
  await recoverStaleDevSession(cwd)
674
751
  await handleComposeProjectRename(cwd, config.project.name, paths)
675
- const devBrand = { intro: "Local development" }
676
752
 
753
+ console.log("[supatype] Bringing up Docker Compose services...")
677
754
  const upStatus = runDockerCompose(paths.composePath, ["up", "-d"], cwd, project, {
678
755
  quiet: true,
679
756
  brand: devBrand,
@@ -695,8 +772,25 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
695
772
  compose: { project, composePath: paths.composePath },
696
773
  })
697
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
+
698
790
  console.log("[supatype] Waiting for API gateway...")
699
791
  await waitKongReady(kongPort, 120)
792
+ console.log("[supatype] Waiting for storage API...")
793
+ await waitStorageApiReady(kongPort, serviceRoleKey, 90)
700
794
 
701
795
  writeLocalEnvironment(cwd, {
702
796
  target: "local",
@@ -721,7 +815,9 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
721
815
  const pidDir = join(homedir(), ".supatype", "projects", config.project.name, "pid")
722
816
  mkdirSync(pidDir, { recursive: true })
723
817
 
724
- let studioProc: Awaited<ReturnType<typeof startStudioViteDevServer>> = null
818
+ startDevSession()
819
+
820
+ let studioProc: StudioProc = null
725
821
  const studioOverride = config.overrides?.studio
726
822
  if (studioOverride) {
727
823
  studioProc = startStudioViteDevServer({
@@ -730,66 +826,55 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
730
826
  pidDir,
731
827
  serviceRoleKey,
732
828
  proxyTarget: `http://localhost:${kongPort}`,
733
- viteSupatypeUrl: `http://localhost:${kongPort}`,
829
+ viteSupatypeUrl: `http://localhost:${STUDIO_DEV_PORT}`,
734
830
  basePath: "/studio/",
735
831
  })
736
832
  studioProc?.start()
833
+ shutdownState.studioProc = studioProc
737
834
  if (studioProc) {
738
- 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
+ )
739
838
  }
740
839
  }
741
840
 
742
- console.log(`
743
- [supatype] Services running (Docker Compose · project ${project}):
744
- API (Kong) http://localhost:${kongPort}
745
- REST API http://localhost:${kongPort}/rest/v1/
746
- Auth http://localhost:${kongPort}/auth/v1/
747
- Storage http://localhost:${kongPort}/storage/v1/
748
- Realtime ws://localhost:${kongPort}/realtime/v1/
749
- 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/` })
750
852
 
751
- API keys (local dev only):
752
- anon key ${anonKey}
753
- service_role ${serviceRoleKey}
853
+ const hints: string[] = []
854
+ if (existsSync(join(cwd, "seed.ts"))) {
855
+ hints.push("Demo data: pnpm seed")
856
+ }
754
857
 
755
- Press Ctrl+C to stop.
756
- `)
858
+ publishDevReady({
859
+ title: `Services running (Docker · ${project})`,
860
+ links,
861
+ anonKey,
862
+ serviceRoleKey,
863
+ ...(hints.length > 0 ? { hints } : {}),
864
+ })
757
865
 
758
866
  const appProc = startProxyDevApp(cwd, config, pidDir)
759
-
760
- let schemaWatcher: import("node:fs").FSWatcher | null = null
761
- let debounceTimer: ReturnType<typeof setTimeout> | null = null
762
-
763
- registerDevShutdown(async () => {
764
- schemaWatcher?.close()
765
- schemaWatcher = null
766
- if (debounceTimer) {
767
- clearTimeout(debounceTimer)
768
- debounceTimer = null
769
- }
770
- console.log("[supatype] Shutting down compose...")
771
- await studioProc?.stop()
772
- await appProc?.stop()
773
- const downStatus = runDockerCompose(paths.composePath, ["down"], cwd, project, { quiet: true })
774
- if (downStatus === 0) {
775
- console.log("[supatype] Compose stack stopped.")
776
- } else {
777
- console.warn(`[supatype] Compose down exited with status ${downStatus}.`)
778
- }
779
- }, {
780
- cwd,
781
- compose: { cwd, composePath: paths.composePath, composeProject: project },
782
- })
867
+ shutdownState.appProc = appProc
783
868
 
784
869
  if (opts.watch) {
785
870
  const schemaDir = join(projectRootFromConfig(config, cwd), config.schema?.path ?? "schema/index.ts", "..")
786
871
  console.log(`[supatype] Watching ${schemaDir} for changes...`)
787
872
  const { watch } = await import("node:fs")
788
- schemaWatcher = watch(schemaDir, { recursive: true }, (_eventType, filename) => {
873
+ shutdownState.schemaWatcher = watch(schemaDir, { recursive: true }, (_eventType, filename) => {
789
874
  if (!filename?.endsWith(".ts")) return
790
- if (debounceTimer) clearTimeout(debounceTimer)
791
- debounceTimer = setTimeout(() => {
792
- debounceTimer = null
875
+ if (shutdownState.debounceTimer) clearTimeout(shutdownState.debounceTimer)
876
+ shutdownState.debounceTimer = setTimeout(() => {
877
+ shutdownState.debounceTimer = null
793
878
  console.log(`\n[supatype] Change detected in ${filename}, pushing schema...`)
794
879
  runComposeSchemaPushQueued(cwd, config, paths, schemaPath, project)
795
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
package/src/dev-ports.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * availability so multiple projects and port collisions are surfaced clearly.
5
5
  */
6
6
 
7
- import * as p from "@clack/prompts"
7
+ import { CLACK_CANCEL, isCancel, p } from "./ui/clack.js"
8
8
  import { COMPOSE_DEV_KONG_PORT } from "./project-config.js"
9
9
  import { isPortInUse } from "./postgres-ctl.js"
10
10
  import { readEnvInt, upsertEnvFile } from "./env-file.js"
@@ -119,7 +119,7 @@ export async function promptKongPortChoice(): Promise<number> {
119
119
  },
120
120
  })
121
121
 
122
- if (p.isCancel(value)) {
122
+ if (isCancel(value)) {
123
123
  p.cancel("Cancelled.")
124
124
  process.exit(0)
125
125
  }
@@ -154,7 +154,7 @@ async function promptPortConflictWithoutPersist(
154
154
  ],
155
155
  })
156
156
 
157
- if (p.isCancel(choice) || choice === "cancel") {
157
+ if (isCancel(choice) || choice === "cancel") {
158
158
  p.cancel("Cancelled.")
159
159
  process.exit(0)
160
160
  }
@@ -171,7 +171,7 @@ async function promptPortConflictWithoutPersist(
171
171
  },
172
172
  })
173
173
 
174
- if (p.isCancel(custom)) {
174
+ if (isCancel(custom)) {
175
175
  p.cancel("Cancelled.")
176
176
  process.exit(0)
177
177
  }
@@ -194,19 +194,30 @@ async function promptPortConflictWithoutPersist(
194
194
 
195
195
  const COMPOSE_DEV_DB_PORT = 54329
196
196
 
197
+ function devDbConnectionUrl(port: number): string {
198
+ return `postgresql://supatype_admin:postgres@localhost:${port}/supatype?sslmode=disable`
199
+ }
200
+
197
201
  export async function ensureDevDbPort(cwd: string): Promise<number> {
198
202
  const persisted = readEnvInt(cwd, "SUPATYPE_DEV_DB_PORT")
203
+
204
+ const persist = (port: number): number => {
205
+ upsertEnvFile(cwd, {
206
+ SUPATYPE_DEV_DB_PORT: String(port),
207
+ DATABASE_URL: devDbConnectionUrl(port),
208
+ })
209
+ return port
210
+ }
211
+
199
212
  if (persisted !== null) {
200
213
  if (!(await isPortInUse(persisted))) return persisted
201
214
  const next = await findNextFreePort(persisted + 1)
202
- upsertEnvFile(cwd, {
203
- SUPATYPE_DEV_DB_PORT: String(next),
204
- DATABASE_URL: `postgresql://supatype_admin:postgres@localhost:${next}/supatype?sslmode=disable`,
205
- })
206
- return next
215
+ return persist(next)
207
216
  }
208
217
 
209
218
  let port = COMPOSE_DEV_DB_PORT
210
- while (await isPortInUse(port)) port++
211
- return port
219
+ if (await isPortInUse(port)) {
220
+ port = await findNextFreePort(port + 1)
221
+ }
222
+ return persist(port)
212
223
  }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Pinned service URLs + dev keys for the Ink dashboard.
3
+ */
4
+
5
+ import { appendStackOutput, getActiveDevSession } from "./dev-session.js"
6
+
7
+ export interface DevServiceLink {
8
+ label: string
9
+ url: string
10
+ }
11
+
12
+ export interface DevReadyPanel {
13
+ title: string
14
+ links: DevServiceLink[]
15
+ anonKey?: string
16
+ serviceRoleKey?: string
17
+ hints?: string[]
18
+ }
19
+
20
+ /** Ink row budget for DevReadyPanelView (border, title, links, hints, keys, margin). */
21
+ export function devReadyPanelRowCount(panel: DevReadyPanel): number {
22
+ let rows = 2 // round border top + bottom
23
+ rows += 1 // title
24
+ rows += panel.links.length
25
+ for (const hint of panel.hints ?? []) {
26
+ if (hint.trim()) rows += 1
27
+ }
28
+ if (panel.anonKey) {
29
+ rows += 1 // "API keys (local dev)"
30
+ rows += 1 // anon
31
+ if (panel.serviceRoleKey) rows += 1 // service_role
32
+ }
33
+ rows += 1 // marginBottom on panel box
34
+ return rows
35
+ }
36
+
37
+ function formatStreamBlock(panel: DevReadyPanel): string {
38
+ const lines = [`[supatype] ${panel.title}`]
39
+ for (const link of panel.links) {
40
+ lines.push(` ${link.label.padEnd(16)} ${link.url}`)
41
+ }
42
+ for (const hint of panel.hints ?? []) {
43
+ lines.push(` ${hint}`)
44
+ }
45
+ if (panel.anonKey) {
46
+ lines.push(" API keys (local dev only):")
47
+ lines.push(` anon key ${panel.anonKey}`)
48
+ lines.push(` service_role ${panel.serviceRoleKey ?? ""}`)
49
+ }
50
+ lines.push(" Press Ctrl+C to stop.")
51
+ return lines.join("\n")
52
+ }
53
+
54
+ /** Pin links in the TUI or print the classic block in stream mode. */
55
+ export function publishDevReady(panel: DevReadyPanel): void {
56
+ const session = getActiveDevSession()
57
+ if (session?.isTui()) {
58
+ session.bus.setReadyPanel(panel)
59
+ appendStackOutput("Services running.", "log")
60
+ return
61
+ }
62
+ console.log(formatStreamBlock(panel))
63
+ }
@@ -6,10 +6,10 @@
6
6
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs"
7
7
  import { join, resolve } from "node:path"
8
8
  import { spawnSync } from "node:child_process"
9
- import * as p from "@clack/prompts"
10
9
  import { runDockerCompose } from "./self-host-compose.js"
10
+ import { confirm as uiConfirm } from "./ui/confirm.js"
11
+ import { success, warn } from "./ui/messages.js"
11
12
  import { isInteractive } from "./ui/interactive.js"
12
- import { warn } from "./ui/messages.js"
13
13
 
14
14
  const LOCK_VERSION = 1 as const
15
15
 
@@ -81,12 +81,9 @@ export async function recoverStaleDevSession(cwd: string): Promise<void> {
81
81
  return
82
82
  }
83
83
 
84
- const stop = await p.confirm({
85
- message: `${message}\n\nStop the orphaned stack before starting?`,
86
- initialValue: true,
87
- })
84
+ const stop = await uiConfirm(`${message}\n\nStop the orphaned stack before starting?`, { default: true })
88
85
 
89
- if (p.isCancel(stop) || !stop) {
86
+ if (!stop) {
90
87
  warn(`Leaving "${lock.composeProject}" running.`)
91
88
  return
92
89
  }
@@ -94,7 +91,7 @@ export async function recoverStaleDevSession(cwd: string): Promise<void> {
94
91
  const status = runDockerCompose(lock.composePath, ["down"], cwd, lock.composeProject, { quiet: true })
95
92
  if (status === 0) {
96
93
  clearDevSessionLock(cwd)
97
- p.log.success(`Stopped orphaned stack "${lock.composeProject}".`)
94
+ success(`Stopped orphaned stack "${lock.composeProject}".`)
98
95
  } else {
99
96
  warn(`Could not stop "${lock.composeProject}" (exit ${status}).`)
100
97
  }