@supatype/cli 0.1.1 → 0.1.3

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 (334) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +126 -99
  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 +12 -0
  22. package/dist/commands/admin.d.ts.map +1 -1
  23. package/dist/commands/admin.js +116 -26
  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 +26 -22
  31. package/dist/commands/dev.js.map +1 -1
  32. package/dist/commands/init.d.ts +3 -4
  33. package/dist/commands/init.d.ts.map +1 -1
  34. package/dist/commands/init.js +296 -121
  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/compose-local-server-image.d.ts +13 -0
  40. package/dist/compose-local-server-image.d.ts.map +1 -0
  41. package/dist/compose-local-server-image.js +57 -0
  42. package/dist/compose-local-server-image.js.map +1 -0
  43. package/dist/compose-rename.d.ts.map +1 -1
  44. package/dist/compose-rename.js +5 -8
  45. package/dist/compose-rename.js.map +1 -1
  46. package/dist/config.d.ts.map +1 -1
  47. package/dist/config.js +12 -1
  48. package/dist/config.js.map +1 -1
  49. package/dist/dev-compose.d.ts.map +1 -1
  50. package/dist/dev-compose.js +113 -56
  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 +16 -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.map +1 -1
  61. package/dist/dev-ports.js +19 -12
  62. package/dist/dev-ports.js.map +1 -1
  63. package/dist/dev-ready-panel.d.ts +21 -0
  64. package/dist/dev-ready-panel.d.ts.map +1 -0
  65. package/dist/dev-ready-panel.js +67 -0
  66. package/dist/dev-ready-panel.js.map +1 -0
  67. package/dist/dev-session-lock.d.ts.map +1 -1
  68. package/dist/dev-session-lock.js +5 -8
  69. package/dist/dev-session-lock.js.map +1 -1
  70. package/dist/dev-session.d.ts +18 -5
  71. package/dist/dev-session.d.ts.map +1 -1
  72. package/dist/dev-session.js +91 -18
  73. package/dist/dev-session.js.map +1 -1
  74. package/dist/dev-shutdown.d.ts +2 -0
  75. package/dist/dev-shutdown.d.ts.map +1 -1
  76. package/dist/dev-shutdown.js +29 -15
  77. package/dist/dev-shutdown.js.map +1 -1
  78. package/dist/dev-task-colors.d.ts +3 -1
  79. package/dist/dev-task-colors.d.ts.map +1 -1
  80. package/dist/dev-task-colors.js +23 -3
  81. package/dist/dev-task-colors.js.map +1 -1
  82. package/dist/docker-runtime.js +1 -1
  83. package/dist/docker-runtime.js.map +1 -1
  84. package/dist/engine-push-output.d.ts +1 -0
  85. package/dist/engine-push-output.d.ts.map +1 -1
  86. package/dist/engine-push-output.js +4 -1
  87. package/dist/engine-push-output.js.map +1 -1
  88. package/dist/ensure-binary.d.ts.map +1 -1
  89. package/dist/ensure-binary.js +14 -2
  90. package/dist/ensure-binary.js.map +1 -1
  91. package/dist/ensure-component-binaries.d.ts +17 -0
  92. package/dist/ensure-component-binaries.d.ts.map +1 -0
  93. package/dist/ensure-component-binaries.js +45 -0
  94. package/dist/ensure-component-binaries.js.map +1 -0
  95. package/dist/ensure-dev-api-config.d.ts +3 -0
  96. package/dist/ensure-dev-api-config.d.ts.map +1 -0
  97. package/dist/ensure-dev-api-config.js +17 -0
  98. package/dist/ensure-dev-api-config.js.map +1 -0
  99. package/dist/init-dependency-versions.d.ts +13 -0
  100. package/dist/init-dependency-versions.d.ts.map +1 -0
  101. package/dist/init-dependency-versions.js +53 -0
  102. package/dist/init-dependency-versions.js.map +1 -0
  103. package/dist/init-package-json.d.ts +21 -0
  104. package/dist/init-package-json.d.ts.map +1 -0
  105. package/dist/init-package-json.js +46 -0
  106. package/dist/init-package-json.js.map +1 -0
  107. package/dist/init-project-detect.d.ts +14 -0
  108. package/dist/init-project-detect.d.ts.map +1 -0
  109. package/dist/init-project-detect.js +89 -0
  110. package/dist/init-project-detect.js.map +1 -0
  111. package/dist/postgres-ctl.d.ts.map +1 -1
  112. package/dist/postgres-ctl.js +2 -1
  113. package/dist/postgres-ctl.js.map +1 -1
  114. package/dist/required-host-components.d.ts +11 -0
  115. package/dist/required-host-components.d.ts.map +1 -0
  116. package/dist/required-host-components.js +25 -0
  117. package/dist/required-host-components.js.map +1 -0
  118. package/dist/resolve-api-url.d.ts +6 -0
  119. package/dist/resolve-api-url.d.ts.map +1 -0
  120. package/dist/resolve-api-url.js +39 -0
  121. package/dist/resolve-api-url.js.map +1 -0
  122. package/dist/rest-cache-admin.d.ts +33 -0
  123. package/dist/rest-cache-admin.d.ts.map +1 -0
  124. package/dist/rest-cache-admin.js +96 -0
  125. package/dist/rest-cache-admin.js.map +1 -0
  126. package/dist/scripts/postinstall.d.ts +4 -6
  127. package/dist/scripts/postinstall.d.ts.map +1 -1
  128. package/dist/scripts/postinstall.js +8 -41
  129. package/dist/scripts/postinstall.js.map +1 -1
  130. package/dist/self-host-compose.d.ts.map +1 -1
  131. package/dist/self-host-compose.js +23 -13
  132. package/dist/self-host-compose.js.map +1 -1
  133. package/dist/studio-dev-server.d.ts +4 -2
  134. package/dist/studio-dev-server.d.ts.map +1 -1
  135. package/dist/studio-dev-server.js +3 -2
  136. package/dist/studio-dev-server.js.map +1 -1
  137. package/dist/ui/brand.d.ts +2 -5
  138. package/dist/ui/brand.d.ts.map +1 -1
  139. package/dist/ui/brand.js +2 -7
  140. package/dist/ui/brand.js.map +1 -1
  141. package/dist/ui/clack-api.d.ts +44 -0
  142. package/dist/ui/clack-api.d.ts.map +1 -0
  143. package/dist/ui/clack-api.js +76 -0
  144. package/dist/ui/clack-api.js.map +1 -0
  145. package/dist/ui/clack.d.ts +14 -0
  146. package/dist/ui/clack.d.ts.map +1 -0
  147. package/dist/ui/clack.js +147 -0
  148. package/dist/ui/clack.js.map +1 -0
  149. package/dist/ui/components/FlowSpinner.d.ts +5 -0
  150. package/dist/ui/components/FlowSpinner.d.ts.map +1 -0
  151. package/dist/ui/components/FlowSpinner.js +8 -0
  152. package/dist/ui/components/FlowSpinner.js.map +1 -0
  153. package/dist/ui/components/LogoWordmark.d.ts +9 -0
  154. package/dist/ui/components/LogoWordmark.d.ts.map +1 -0
  155. package/dist/ui/components/LogoWordmark.js +17 -0
  156. package/dist/ui/components/LogoWordmark.js.map +1 -0
  157. package/dist/ui/components/PromptPanel.d.ts +5 -0
  158. package/dist/ui/components/PromptPanel.d.ts.map +1 -0
  159. package/dist/ui/components/PromptPanel.js +7 -0
  160. package/dist/ui/components/PromptPanel.js.map +1 -0
  161. package/dist/ui/components/SelectField.d.ts +11 -0
  162. package/dist/ui/components/SelectField.d.ts.map +1 -0
  163. package/dist/ui/components/SelectField.js +19 -0
  164. package/dist/ui/components/SelectField.js.map +1 -0
  165. package/dist/ui/confirm.d.ts +2 -1
  166. package/dist/ui/confirm.d.ts.map +1 -1
  167. package/dist/ui/confirm.js +4 -4
  168. package/dist/ui/confirm.js.map +1 -1
  169. package/dist/ui/dev/DevDashboard.d.ts +8 -0
  170. package/dist/ui/dev/DevDashboard.d.ts.map +1 -0
  171. package/dist/ui/dev/DevDashboard.js +158 -0
  172. package/dist/ui/dev/DevDashboard.js.map +1 -0
  173. package/dist/ui/dev/DevReadyPanelView.d.ts +8 -0
  174. package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -0
  175. package/dist/ui/dev/DevReadyPanelView.js +48 -0
  176. package/dist/ui/dev/DevReadyPanelView.js.map +1 -0
  177. package/dist/ui/fatal.d.ts.map +1 -1
  178. package/dist/ui/fatal.js +5 -11
  179. package/dist/ui/fatal.js.map +1 -1
  180. package/dist/ui/flows/FlowApp.d.ts +8 -0
  181. package/dist/ui/flows/FlowApp.d.ts.map +1 -0
  182. package/dist/ui/flows/FlowApp.js +44 -0
  183. package/dist/ui/flows/FlowApp.js.map +1 -0
  184. package/dist/ui/flows/prompt-fields.d.ts +48 -0
  185. package/dist/ui/flows/prompt-fields.d.ts.map +1 -0
  186. package/dist/ui/flows/prompt-fields.js +92 -0
  187. package/dist/ui/flows/prompt-fields.js.map +1 -0
  188. package/dist/ui/flows/types.d.ts +41 -0
  189. package/dist/ui/flows/types.d.ts.map +1 -0
  190. package/dist/ui/flows/types.js +2 -0
  191. package/dist/ui/flows/types.js.map +1 -0
  192. package/dist/ui/index.d.ts +1 -1
  193. package/dist/ui/index.js +1 -1
  194. package/dist/ui/messages.d.ts +1 -0
  195. package/dist/ui/messages.d.ts.map +1 -1
  196. package/dist/ui/messages.js +48 -8
  197. package/dist/ui/messages.js.map +1 -1
  198. package/dist/ui/progress.d.ts +5 -1
  199. package/dist/ui/progress.d.ts.map +1 -1
  200. package/dist/ui/progress.js +12 -3
  201. package/dist/ui/progress.js.map +1 -1
  202. package/dist/ui/prompts.d.ts +7 -5
  203. package/dist/ui/prompts.d.ts.map +1 -1
  204. package/dist/ui/prompts.js +15 -8
  205. package/dist/ui/prompts.js.map +1 -1
  206. package/dist/ui/runtime/cancel.d.ts +4 -0
  207. package/dist/ui/runtime/cancel.d.ts.map +1 -0
  208. package/dist/ui/runtime/cancel.js +6 -0
  209. package/dist/ui/runtime/cancel.js.map +1 -0
  210. package/dist/ui/runtime/command-chrome.d.ts +17 -0
  211. package/dist/ui/runtime/command-chrome.d.ts.map +1 -0
  212. package/dist/ui/runtime/command-chrome.js +123 -0
  213. package/dist/ui/runtime/command-chrome.js.map +1 -0
  214. package/dist/ui/runtime/dev-prompt-queue.d.ts +30 -0
  215. package/dist/ui/runtime/dev-prompt-queue.d.ts.map +1 -0
  216. package/dist/ui/runtime/dev-prompt-queue.js +57 -0
  217. package/dist/ui/runtime/dev-prompt-queue.js.map +1 -0
  218. package/dist/ui/runtime/flow-session.d.ts +4 -0
  219. package/dist/ui/runtime/flow-session.d.ts.map +1 -0
  220. package/dist/ui/runtime/flow-session.js +8 -0
  221. package/dist/ui/runtime/flow-session.js.map +1 -0
  222. package/dist/ui/runtime/run-clack-flow.d.ts +5 -0
  223. package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -0
  224. package/dist/ui/runtime/run-clack-flow.js +46 -0
  225. package/dist/ui/runtime/run-clack-flow.js.map +1 -0
  226. package/dist/ui/runtime/stdin-after-ink.d.ts +3 -0
  227. package/dist/ui/runtime/stdin-after-ink.d.ts.map +1 -0
  228. package/dist/ui/runtime/stdin-after-ink.js +16 -0
  229. package/dist/ui/runtime/stdin-after-ink.js.map +1 -0
  230. package/dist/ui/theme.d.ts +24 -0
  231. package/dist/ui/theme.d.ts.map +1 -0
  232. package/dist/ui/theme.js +34 -0
  233. package/dist/ui/theme.js.map +1 -0
  234. package/dist/valkey-sidecar.d.ts +17 -0
  235. package/dist/valkey-sidecar.d.ts.map +1 -0
  236. package/dist/valkey-sidecar.js +94 -0
  237. package/dist/valkey-sidecar.js.map +1 -0
  238. package/package.json +9 -3
  239. package/src/binary-cache.ts +61 -0
  240. package/src/binary-download-lock.ts +124 -0
  241. package/src/cli-package-version.ts +15 -0
  242. package/src/cli.ts +6 -2
  243. package/src/commands/add.ts +26 -19
  244. package/src/commands/admin.ts +156 -29
  245. package/src/commands/cache.ts +105 -5
  246. package/src/commands/dev.ts +26 -21
  247. package/src/commands/init.ts +473 -247
  248. package/src/commands/link-helpers.ts +30 -29
  249. package/src/compose-local-server-image.ts +68 -0
  250. package/src/compose-rename.ts +5 -8
  251. package/src/config.ts +13 -1
  252. package/src/dev-compose.ts +139 -54
  253. package/src/dev-log-bus.ts +20 -0
  254. package/src/dev-log-filter.ts +42 -0
  255. package/src/dev-ports.ts +22 -11
  256. package/src/dev-ready-panel.ts +80 -0
  257. package/src/dev-session-lock.ts +5 -8
  258. package/src/dev-session.ts +98 -18
  259. package/src/dev-shutdown.ts +29 -17
  260. package/src/dev-task-colors.ts +22 -3
  261. package/src/docker-runtime.ts +1 -1
  262. package/src/engine-push-output.ts +5 -1
  263. package/src/ensure-binary.ts +21 -1
  264. package/src/ensure-component-binaries.ts +71 -0
  265. package/src/ensure-dev-api-config.ts +18 -0
  266. package/src/init-dependency-versions.ts +69 -0
  267. package/src/init-package-json.ts +63 -0
  268. package/src/init-project-detect.ts +99 -0
  269. package/src/postgres-ctl.ts +2 -1
  270. package/src/required-host-components.ts +37 -0
  271. package/src/resolve-api-url.ts +43 -0
  272. package/src/rest-cache-admin.ts +135 -0
  273. package/src/scripts/postinstall.ts +9 -46
  274. package/src/self-host-compose.ts +22 -12
  275. package/src/studio-dev-server.ts +5 -4
  276. package/src/ui/README.md +13 -9
  277. package/src/ui/brand.ts +2 -8
  278. package/src/ui/clack-api.ts +123 -0
  279. package/src/ui/clack.ts +153 -0
  280. package/src/ui/components/FlowSpinner.tsx +15 -0
  281. package/src/ui/components/LogoWordmark.tsx +36 -0
  282. package/src/ui/components/PromptPanel.tsx +17 -0
  283. package/src/ui/components/SelectField.tsx +58 -0
  284. package/src/ui/confirm.ts +5 -4
  285. package/src/ui/dev/DevDashboard.tsx +223 -0
  286. package/src/ui/dev/DevReadyPanelView.tsx +134 -0
  287. package/src/ui/fatal.ts +5 -10
  288. package/src/ui/flows/FlowApp.tsx +69 -0
  289. package/src/ui/flows/prompt-fields.tsx +198 -0
  290. package/src/ui/flows/types.ts +50 -0
  291. package/src/ui/index.ts +1 -1
  292. package/src/ui/messages.ts +50 -8
  293. package/src/ui/progress.ts +14 -3
  294. package/src/ui/prompts.ts +17 -9
  295. package/src/ui/runtime/cancel.ts +6 -0
  296. package/src/ui/runtime/command-chrome.ts +147 -0
  297. package/src/ui/runtime/dev-prompt-queue.ts +84 -0
  298. package/src/ui/runtime/flow-session.ts +11 -0
  299. package/src/ui/runtime/run-clack-flow.tsx +65 -0
  300. package/src/ui/runtime/stdin-after-ink.ts +18 -0
  301. package/src/ui/theme.ts +41 -0
  302. package/src/valkey-sidecar.ts +126 -0
  303. package/tests/admin-ensure.test.ts +80 -0
  304. package/tests/binary-cache-cloud-overrides.test.ts +2 -2
  305. package/tests/binary-download-lock.test.ts +41 -0
  306. package/tests/cli-help.test.ts +7 -4
  307. package/tests/cli-package-version.test.ts +15 -0
  308. package/tests/command-chrome-coverage.test.ts +149 -0
  309. package/tests/command-chrome-wrap.test.ts +51 -0
  310. package/tests/config.test.ts +27 -4
  311. package/tests/dev-ready-panel.test.ts +37 -0
  312. package/tests/dev-ui.test.ts +42 -2
  313. package/tests/docker-runtime.test.ts +9 -20
  314. package/tests/ensure-dev-api-config.test.ts +22 -0
  315. package/tests/init-dependency-versions.test.ts +62 -0
  316. package/tests/init-package-json.test.ts +18 -0
  317. package/tests/init-project-detect.test.ts +53 -0
  318. package/tests/init.test.ts +30 -3
  319. package/tests/required-host-components.test.ts +51 -0
  320. package/tests/resolve-api-url.test.ts +23 -0
  321. package/tests/rest-cache-admin.test.ts +28 -0
  322. package/tests/runtime-contract.test.ts +12 -3
  323. package/tests/stdin-after-ink.test.ts +8 -0
  324. package/tests/tsconfig.json +8 -2
  325. package/tests/ui-confirm.test.ts +13 -9
  326. package/tests/ui-messages.test.ts +4 -18
  327. package/tsconfig.json +1 -0
  328. package/tsconfig.tsbuildinfo +1 -1
  329. package/vitest.config.ts +3 -0
  330. package/dist/dev-tui.d.ts +0 -24
  331. package/dist/dev-tui.d.ts.map +0 -1
  332. package/dist/dev-tui.js +0 -188
  333. package/dist/dev-tui.js.map +0 -1
  334. package/src/dev-tui.ts +0 -232
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,80 @@
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
+ /** Shorter panel when the terminal is too small for every service row. */
38
+ export function devReadyPanelCompactRowCount(panel: DevReadyPanel): number {
39
+ let rows = 2 // border
40
+ rows += 1 // title
41
+ rows += 2 // gateway + studio summary
42
+ for (const hint of panel.hints ?? []) {
43
+ if (hint.trim()) rows += 1
44
+ }
45
+ if (panel.anonKey) {
46
+ rows += 1
47
+ rows += 1
48
+ if (panel.serviceRoleKey) rows += 1
49
+ }
50
+ rows += 1 // marginBottom
51
+ return rows
52
+ }
53
+
54
+ function formatStreamBlock(panel: DevReadyPanel): string {
55
+ const lines = [`[supatype] ${panel.title}`]
56
+ for (const link of panel.links) {
57
+ lines.push(` ${link.label.padEnd(16)} ${link.url}`)
58
+ }
59
+ for (const hint of panel.hints ?? []) {
60
+ lines.push(` ${hint}`)
61
+ }
62
+ if (panel.anonKey) {
63
+ lines.push(" API keys (local dev only):")
64
+ lines.push(` anon key ${panel.anonKey}`)
65
+ lines.push(` service_role ${panel.serviceRoleKey ?? ""}`)
66
+ }
67
+ lines.push(" Press Ctrl+C to stop.")
68
+ return lines.join("\n")
69
+ }
70
+
71
+ /** Pin links in the TUI or print the classic block in stream mode. */
72
+ export function publishDevReady(panel: DevReadyPanel): void {
73
+ const session = getActiveDevSession()
74
+ if (session?.isTui()) {
75
+ session.bus.setReadyPanel(panel)
76
+ appendStackOutput("Services running.", "log")
77
+ return
78
+ }
79
+ console.log(formatStreamBlock(panel))
80
+ }
@@ -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
  }
@@ -1,11 +1,15 @@
1
1
  /**
2
- * `supatype dev` output session — TUI (default) or interleaved stream mode.
2
+ * `supatype dev` output session — Ink dashboard (default) or interleaved stream mode.
3
3
  */
4
4
 
5
+ import { render, type Instance } from "ink"
6
+ import React from "react"
5
7
  import type { ProcessOptions } from "./process-manager.js"
6
- import { filterDevSubprocessLine, formatConsoleArgs } from "./dev-log-filter.js"
8
+ import { filterDevSubprocessLine, filterStackLogLine, formatConsoleArgs } from "./dev-log-filter.js"
7
9
  import { DevLogBus, type DevLogLevel } from "./dev-log-bus.js"
8
- import { DevTui } from "./dev-tui.js"
10
+ import { DevDashboard } from "./ui/dev/DevDashboard.js"
11
+ import { ensureDevShutdownHooks } from "./dev-shutdown.js"
12
+ import { clearDevPromptQueue } from "./ui/runtime/dev-prompt-queue.js"
9
13
 
10
14
  export type DevUiMode = "tui" | "stream"
11
15
 
@@ -18,26 +22,74 @@ export function resolveDevUiMode(streamFlag: boolean): DevUiMode {
18
22
  let activeSession: DevSession | null = null
19
23
 
20
24
  export function beginDevSession(mode: DevUiMode): DevSession {
25
+ ensureDevShutdownHooks()
21
26
  activeSession?.stop()
22
27
  activeSession = new DevSession(mode)
28
+ if (activeSession.isTui()) {
29
+ activeSession.enableConsoleCapture(false)
30
+ activeSession.startInk()
31
+ }
23
32
  return activeSession
24
33
  }
25
34
 
26
- /** Enter console capture + TUI. */
35
+ /** @deprecated Ink starts in beginDevSession — kept for callers that invoke late. */
27
36
  export function startDevSession(): void {
28
- activeSession?.start()
37
+ activeSession?.startInk()
38
+ }
39
+
40
+ export function isDevTuiActive(): boolean {
41
+ return activeSession?.isInkMounted() ?? false
29
42
  }
30
43
 
31
44
  export function endDevSession(): void {
45
+ clearDevPromptQueue()
32
46
  activeSession?.stop()
33
47
  activeSession = null
34
48
  }
35
49
 
50
+ /** @deprecated Dev prompts render inside the Ink dashboard — no suspend needed. */
51
+ export function suspendDevSessionForPrompt(): void {
52
+ prepareStdinForInteractivePrompt()
53
+ }
54
+
55
+ export function prepareStdinForInteractivePrompt(): void {
56
+ if (!process.stdin.isTTY) return
57
+ process.stdin.setRawMode(false)
58
+ process.stdin.resume()
59
+ }
60
+
61
+ /** @deprecated Dev prompts render inside the Ink dashboard — no resume needed. */
62
+ export function resumeDevSessionAfterPrompt(): void {
63
+ // no-op — prompts are Ink overlays
64
+ }
65
+
66
+ /** @deprecated Use Ink overlay prompts — runs fn without tearing down the dashboard. */
67
+ export async function withDevSessionSuspended<T>(fn: () => Promise<T>): Promise<T> {
68
+ return fn()
69
+ }
70
+
36
71
  export function getActiveDevSession(): DevSession | null {
37
72
  return activeSession
38
73
  }
39
74
 
40
- /** Log to a TUI task stream, or prefixed console output in stream mode. */
75
+ export function appendStackOutput(
76
+ text: string | null | undefined,
77
+ level: DevLogLevel = "log",
78
+ ): void {
79
+ if (text == null || text.trim() === "") return
80
+ const session = getActiveDevSession()
81
+ for (const line of text.split(/\r?\n/)) {
82
+ if (line.trim() === "") continue
83
+ if (session?.isTui() && !filterStackLogLine(line)) continue
84
+ if (session?.isConsoleCaptured()) {
85
+ session.bus.append("stack", line, level)
86
+ continue
87
+ }
88
+ const write = level === "warn" ? console.warn : level === "error" ? console.error : console.log
89
+ write(`[supatype] ${line}`)
90
+ }
91
+ }
92
+
41
93
  export function appendDevTaskLog(
42
94
  taskId: string,
43
95
  taskTitle: string,
@@ -58,8 +110,9 @@ export function appendDevTaskLog(
58
110
  export class DevSession {
59
111
  readonly bus = new DevLogBus()
60
112
  readonly mode: DevUiMode
61
- private tui: DevTui | null = null
113
+ private ink: Instance | null = null
62
114
  private restoreConsole: (() => void) | null = null
115
+ private consoleCaptured = false
63
116
 
64
117
  constructor(mode: DevUiMode) {
65
118
  this.mode = mode
@@ -69,18 +122,37 @@ export class DevSession {
69
122
  return this.mode === "tui"
70
123
  }
71
124
 
72
- start(): void {
73
- if (!this.isTui()) return
125
+ isInkMounted(): boolean {
126
+ return this.ink !== null
127
+ }
128
+
129
+ isConsoleCaptured(): boolean {
130
+ return this.consoleCaptured
131
+ }
132
+
133
+ needsPromptSuspend(): boolean {
134
+ return false
135
+ }
136
+
137
+ enableConsoleCapture(_tee: boolean): void {
138
+ if (!this.isTui() || this.consoleCaptured) return
74
139
  this.restoreConsole = patchConsole(this.bus)
75
- this.tui = new DevTui(this.bus)
76
- this.tui.start()
140
+ this.consoleCaptured = true
141
+ }
142
+
143
+ startInk(): void {
144
+ if (!this.isTui() || this.ink) return
145
+ this.ink = render(React.createElement(DevDashboard, { bus: this.bus }))
77
146
  }
78
147
 
79
148
  stop(): void {
80
- this.tui?.stop()
81
- this.tui = null
82
- this.restoreConsole?.()
83
- this.restoreConsole = null
149
+ this.ink?.unmount()
150
+ this.ink = null
151
+ if (this.restoreConsole) {
152
+ this.restoreConsole()
153
+ this.restoreConsole = null
154
+ this.consoleCaptured = false
155
+ }
84
156
  }
85
157
  }
86
158
 
@@ -105,6 +177,14 @@ export function enhanceProcessOptions(label: string, opts: ProcessOptions): Proc
105
177
  }
106
178
  }
107
179
 
180
+ function appendFilteredStackLine(bus: DevLogBus, line: string, level: DevLogLevel): void {
181
+ for (const part of line.split(/\r?\n/)) {
182
+ if (!part.trim()) continue
183
+ if (!filterStackLogLine(part)) continue
184
+ bus.append("stack", part, level)
185
+ }
186
+ }
187
+
108
188
  function patchConsole(bus: DevLogBus): () => void {
109
189
  const original = {
110
190
  log: console.log,
@@ -113,13 +193,13 @@ function patchConsole(bus: DevLogBus): () => void {
113
193
  }
114
194
 
115
195
  console.log = (...args: unknown[]) => {
116
- bus.append("stack", formatConsoleArgs(args), "log")
196
+ appendFilteredStackLine(bus, formatConsoleArgs(args), "log")
117
197
  }
118
198
  console.warn = (...args: unknown[]) => {
119
- bus.append("stack", formatConsoleArgs(args), "warn")
199
+ appendFilteredStackLine(bus, formatConsoleArgs(args), "warn")
120
200
  }
121
201
  console.error = (...args: unknown[]) => {
122
- bus.append("stack", formatConsoleArgs(args), "error")
202
+ appendFilteredStackLine(bus, formatConsoleArgs(args), "error")
123
203
  }
124
204
 
125
205
  return () => {
@@ -21,8 +21,28 @@ let shuttingDown = false
21
21
  let shutdownCompleted = false
22
22
  let forceQuitRequested = false
23
23
  let hooksRegistered = false
24
+ let ignoreSigintUntil = 0
25
+
26
+ /** SIGINT/SIGTERM hooks — call as soon as `supatype dev` starts (before compose is up). */
27
+ export function ensureDevShutdownHooks(): void {
28
+ if (hooksRegistered) return
29
+ hooksRegistered = true
30
+
31
+ process.on("SIGINT", onSignal)
32
+ process.on("SIGTERM", onSignal)
33
+ if (process.platform === "win32") {
34
+ process.on("SIGBREAK", onSignal)
35
+ }
36
+ process.on("exit", onProcessExit)
37
+
38
+ if (process.stdin.isTTY) {
39
+ process.stdin.on("end", onStdinClose)
40
+ process.stdin.on("close", onStdinClose)
41
+ }
42
+ }
24
43
 
25
44
  function onSignal(): void {
45
+ if (Date.now() < ignoreSigintUntil) return
26
46
  void runDevShutdown()
27
47
  }
28
48
 
@@ -66,25 +86,12 @@ export function registerDevShutdown(
66
86
  shutdownWork = work
67
87
  composeFallback = opts?.compose ?? null
68
88
  shutdownCwd = opts?.cwd ?? opts?.compose?.cwd ?? null
69
-
70
- if (hooksRegistered) return
71
- hooksRegistered = true
72
-
73
- process.on("SIGINT", onSignal)
74
- process.on("SIGTERM", onSignal)
75
- if (process.platform === "win32") {
76
- process.on("SIGBREAK", onSignal)
77
- }
78
- process.on("exit", onProcessExit)
79
-
80
- if (process.stdin.isTTY) {
81
- process.stdin.on("end", onStdinClose)
82
- process.stdin.on("close", onStdinClose)
83
- }
89
+ ensureDevShutdownHooks()
84
90
  }
85
91
 
86
92
  /** TUI Ctrl+C — do not re-emit SIGINT (avoids double-fire on Windows raw mode). */
87
93
  export function requestDevShutdown(): void {
94
+ ignoreSigintUntil = Date.now() + 400
88
95
  void runDevShutdown()
89
96
  }
90
97
 
@@ -101,6 +108,7 @@ export function resetDevShutdownForTests(): void {
101
108
  shutdownCompleted = false
102
109
  forceQuitRequested = false
103
110
  hooksRegistered = false
111
+ ignoreSigintUntil = 0
104
112
  }
105
113
 
106
114
  async function runDevShutdown(): Promise<void> {
@@ -125,8 +133,12 @@ async function runDevShutdown(): Promise<void> {
125
133
 
126
134
  try {
127
135
  endDevSession()
128
- process.stdout.write("\n")
129
- await shutdownWork?.()
136
+ process.stderr.write("\n[supatype] Shutting down…\n")
137
+ if (!shutdownWork) {
138
+ if (shutdownCwd) clearDevSessionLock(shutdownCwd)
139
+ process.exit(130)
140
+ }
141
+ await shutdownWork()
130
142
  shutdownCompleted = true
131
143
  if (shutdownCwd) clearDevSessionLock(shutdownCwd)
132
144
  process.exit(0)
@@ -41,7 +41,26 @@ export function colorizeLogLine(taskId: string, line: string): string {
41
41
  return `${base}${line}${RESET}`
42
42
  }
43
43
 
44
- export function colorizeTaskLabel(taskId: string, label: string, focused: boolean): string {
45
- const style = focused ? BOLD + taskColor(taskId) : taskColor(taskId)
46
- return `${style}${label}${RESET}`
44
+ /** Ink colour names for React components (parallel to ANSI taskColor). */
45
+ export function taskInkColor(taskId: string): string {
46
+ switch (taskId) {
47
+ case "stack":
48
+ return "magenta"
49
+ case "app":
50
+ return "green"
51
+ case "studio":
52
+ return "cyan"
53
+ case "server":
54
+ return "green"
55
+ case "postgrest":
56
+ return "blue"
57
+ default:
58
+ return "white"
59
+ }
60
+ }
61
+
62
+ export function logInkColor(taskId: string, line: string): string {
63
+ if (line.startsWith("✗ ")) return "red"
64
+ if (line.startsWith("⚠ ")) return "yellow"
65
+ return taskInkColor(taskId)
47
66
  }
@@ -7,7 +7,7 @@ import { platform } from "node:os"
7
7
  import { endDevSession, getActiveDevSession } from "./dev-session.js"
8
8
  import { isInteractive } from "./ui/interactive.js"
9
9
  import { error, plain } from "./ui/messages.js"
10
- import { clack as p, printLogo } from "./ui/prompts.js"
10
+ import { p, printLogo } from "./ui/prompts.js"
11
11
 
12
12
  export type DockerDaemonProbe =
13
13
  | { ok: true }
@@ -59,6 +59,10 @@ export function formatEnginePushMessage(result: EnginePushResult): string {
59
59
  const COMPOSE_PROGRESS_LINE =
60
60
  /^Container\s+.+\s+(Running|Waiting|Healthy|Created|Starting|Started|Exited|Stopped)\s*$/i
61
61
 
62
+ export function isComposeProgressLine(line: string): boolean {
63
+ return COMPOSE_PROGRESS_LINE.test(line.trim())
64
+ }
65
+
62
66
  /** Drop docker compose progress noise; keep engine JSON and real errors. */
63
67
  export function filterComposeNoise(output: string): string {
64
68
  return output
@@ -67,7 +71,7 @@ export function filterComposeNoise(output: string): string {
67
71
  .filter((line) => {
68
72
  const t = line.trim()
69
73
  if (!t) return false
70
- if (COMPOSE_PROGRESS_LINE.test(t)) return false
74
+ if (isComposeProgressLine(t)) return false
71
75
  return true
72
76
  })
73
77
  .join("\n")
@@ -7,8 +7,10 @@ import {
7
7
  download,
8
8
  currentPlatform,
9
9
  resolveVersionFor,
10
+ isCachedBinaryReady,
10
11
  type Component,
11
12
  } from "./binary-cache.js"
13
+ import { isDownloadInProgress, waitForComponentDownload } from "./binary-download-lock.js"
12
14
  import type { SupatypeProjectConfig } from "./project-config.js"
13
15
 
14
16
  export async function ensureBinary(
@@ -24,5 +26,23 @@ export async function ensureBinary(
24
26
  }
25
27
  }
26
28
 
27
- return download(component, await resolveVersionFor(component, config), currentPlatform())
29
+ const version = await resolveVersionFor(component, config)
30
+ const platform = currentPlatform()
31
+
32
+ if (isDownloadInProgress(component, version)) {
33
+ console.log(`[supatype] Waiting for in-progress ${component} download...`)
34
+ const outcome = await waitForComponentDownload(
35
+ component,
36
+ version,
37
+ () => isCachedBinaryReady(component, version, platform),
38
+ (c) => {
39
+ console.log(`[supatype] Still waiting for ${c}...`)
40
+ },
41
+ )
42
+ if (outcome === "ready") {
43
+ return resolveBinary(component, config)
44
+ }
45
+ }
46
+
47
+ return download(component, version, platform)
28
48
  }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Ensure CDN component binaries are cached when the host CLI needs them.
3
+ */
4
+
5
+ import { ensureBinary } from "./ensure-binary.js"
6
+ import type { Component } from "./components.js"
7
+ import type { SupatypeProjectConfig } from "./project-config.js"
8
+ import {
9
+ describeRequiredHostComponents,
10
+ requiredHostComponents,
11
+ } from "./required-host-components.js"
12
+ import { resolveRuntimeProvider } from "./project-config.js"
13
+ import { error, info, warn } from "./ui/messages.js"
14
+
15
+ export interface ComponentBinaryFailure {
16
+ component: Component
17
+ message: string
18
+ }
19
+
20
+ export interface EnsureComponentBinariesResult {
21
+ ok: boolean
22
+ failures: ComponentBinaryFailure[]
23
+ skipped: boolean
24
+ }
25
+
26
+ export async function ensureComponentBinaries(
27
+ config: SupatypeProjectConfig,
28
+ cwd: string = process.cwd(),
29
+ ): Promise<EnsureComponentBinariesResult> {
30
+ const components = requiredHostComponents(config, cwd)
31
+ const provider = resolveRuntimeProvider(config)
32
+
33
+ if (components.length === 0) {
34
+ return { ok: true, failures: [], skipped: true }
35
+ }
36
+
37
+ const list = describeRequiredHostComponents(components)
38
+ if (provider === "docker") {
39
+ info(
40
+ `Preparing host CLI tools (${list}) — Compose still runs Postgres and the API stack.`,
41
+ )
42
+ } else {
43
+ info(`Preparing local runtime components (${list})…`)
44
+ }
45
+
46
+ const failures: ComponentBinaryFailure[] = []
47
+
48
+ for (const component of components) {
49
+ try {
50
+ await ensureBinary(component, config)
51
+ info(`${component} ready.`)
52
+ } catch (err) {
53
+ const message = err instanceof Error ? err.message : String(err)
54
+ failures.push({ component, message })
55
+ error(`Could not prepare ${component}: ${message}`)
56
+ }
57
+ }
58
+
59
+ return { ok: failures.length === 0, failures, skipped: false }
60
+ }
61
+
62
+ export function reportComponentBinaryFailures(failures: ComponentBinaryFailure[]): void {
63
+ if (failures.length === 0) return
64
+
65
+ warn("Some component binaries are still missing.")
66
+ for (const { component, message } of failures) {
67
+ warn(` ${component}: ${message}`)
68
+ }
69
+ warn("Run manually from your project directory: supatype update")
70
+ warn("If downloads keep failing, check your network or try again in a few minutes.")
71
+ }
@@ -0,0 +1,18 @@
1
+ import { copyFileSync, existsSync, mkdirSync } from "node:fs"
2
+ import { dirname, join } from "node:path"
3
+
4
+ const TEMPLATE_REL = join("config", "api-config.json")
5
+ const TARGET_REL = join(".supatype", "api-config.json")
6
+
7
+ /** Copy committed `config/api-config.json` when `.supatype/api-config.json` is missing. */
8
+ export function ensureDevApiConfig(cwd: string): boolean {
9
+ const target = join(cwd, TARGET_REL)
10
+ if (existsSync(target)) return false
11
+
12
+ const template = join(cwd, TEMPLATE_REL)
13
+ if (!existsSync(template)) return false
14
+
15
+ mkdirSync(dirname(target), { recursive: true })
16
+ copyFileSync(template, target)
17
+ return true
18
+ }