@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
@@ -0,0 +1,192 @@
1
+ import React, { useState } from "react"
2
+ import { Box, Text, useInput } from "ink"
3
+ import TextInput from "ink-text-input"
4
+ import { FlowSpinner } from "../components/FlowSpinner.js"
5
+ import { LogoWordmark } from "../components/LogoWordmark.js"
6
+ import { SelectField } from "../components/SelectField.js"
7
+ import { theme } from "../theme.js"
8
+ import type { ActiveFlowPrompt, FlowLogLine } from "./types.js"
9
+
10
+ const TASK_COL_WIDTH = 22
11
+
12
+ function flowLogLineProps(level: FlowLogLine["level"]): { color?: string; dimColor?: boolean } {
13
+ if (level === "plain") return { dimColor: true }
14
+ if (level === "warn") return { color: theme.warn }
15
+ if (level === "error") return { color: theme.error }
16
+ if (level === "success") return { color: theme.success }
17
+ return { color: theme.info }
18
+ }
19
+
20
+ export function FlowLogoHeader(): React.ReactElement {
21
+ return <LogoWordmark />
22
+ }
23
+
24
+ export function FlowLogPane({ lines }: { lines: FlowLogLine[] }): React.ReactElement {
25
+ return (
26
+ <Box flexDirection="column" marginBottom={1}>
27
+ {lines.map((line, index) => (
28
+ <Text key={`${index}-${line.text.slice(0, 24)}`} {...flowLogLineProps(line.level)}>
29
+ {line.text}
30
+ </Text>
31
+ ))}
32
+ </Box>
33
+ )
34
+ }
35
+
36
+ export function FlowSpinnerLine({ message }: { message: string }): React.ReactElement {
37
+ return <FlowSpinner message={message} />
38
+ }
39
+
40
+ interface ConfirmPromptProps {
41
+ spec: Extract<ActiveFlowPrompt, { kind: "confirm" }>
42
+ onSubmit: (value: boolean) => void
43
+ }
44
+
45
+ export function ConfirmPrompt({ spec, onSubmit }: ConfirmPromptProps): React.ReactElement {
46
+ const [value, setValue] = useState(spec.initialValue)
47
+
48
+ useInput((input, key) => {
49
+ if (key.leftArrow || input === "h") setValue(false)
50
+ if (key.rightArrow || input === "l") setValue(true)
51
+ if (input === "y" || input === "Y") setValue(true)
52
+ if (input === "n" || input === "N") setValue(false)
53
+ if (key.return) onSubmit(value)
54
+ })
55
+
56
+ return (
57
+ <Box flexDirection="column">
58
+ <Text bold color={theme.brand}>
59
+ {spec.message}
60
+ </Text>
61
+ <Text>
62
+ {value ? (
63
+ <Text color={theme.success} bold>
64
+ Yes
65
+ </Text>
66
+ ) : (
67
+ <Text>Yes</Text>
68
+ )}
69
+ <Text> / </Text>
70
+ {!value ? (
71
+ <Text color={theme.success} bold>
72
+ No
73
+ </Text>
74
+ ) : (
75
+ <Text>No</Text>
76
+ )}
77
+ <Text dimColor> — y/n, ←/→, enter</Text>
78
+ </Text>
79
+ </Box>
80
+ )
81
+ }
82
+
83
+ interface TextPromptProps {
84
+ spec: Extract<ActiveFlowPrompt, { kind: "text" }>
85
+ onSubmit: (value: string) => void
86
+ onCancel: () => void
87
+ }
88
+
89
+ export function TextPrompt({ spec, onSubmit, onCancel }: TextPromptProps): React.ReactElement {
90
+ const [value, setValue] = useState(spec.defaultValue ?? "")
91
+ const [error, setError] = useState<string | undefined>()
92
+
93
+ useInput((_input, key) => {
94
+ if (key.escape) onCancel()
95
+ })
96
+
97
+ return (
98
+ <Box flexDirection="column">
99
+ <Text bold color={theme.brand}>
100
+ {spec.message}
101
+ </Text>
102
+ <Box>
103
+ <Text color={theme.brand}>› </Text>
104
+ <TextInput
105
+ value={value}
106
+ {...(spec.placeholder !== undefined ? { placeholder: spec.placeholder } : {})}
107
+ onChange={setValue}
108
+ onSubmit={(submitted) => {
109
+ const err = spec.validate?.(submitted)
110
+ if (err) {
111
+ setError(err)
112
+ return
113
+ }
114
+ onSubmit(submitted)
115
+ }}
116
+ />
117
+ </Box>
118
+ {error ? <Text color={theme.error}>{error}</Text> : null}
119
+ <Text dimColor>enter submit · esc cancel</Text>
120
+ </Box>
121
+ )
122
+ }
123
+
124
+ interface PasswordPromptProps {
125
+ spec: Extract<ActiveFlowPrompt, { kind: "password" }>
126
+ onSubmit: (value: string) => void
127
+ onCancel: () => void
128
+ }
129
+
130
+ export function PasswordPrompt({ spec, onSubmit, onCancel }: PasswordPromptProps): React.ReactElement {
131
+ const [value, setValue] = useState("")
132
+
133
+ useInput((_input, key) => {
134
+ if (key.escape) onCancel()
135
+ })
136
+
137
+ return (
138
+ <Box flexDirection="column">
139
+ <Text bold color={theme.brand}>
140
+ {spec.message}
141
+ </Text>
142
+ <Box>
143
+ <Text color={theme.brand}>› </Text>
144
+ <TextInput mask="*" value={value} onChange={setValue} onSubmit={onSubmit} />
145
+ </Box>
146
+ <Text dimColor>enter submit · esc cancel</Text>
147
+ </Box>
148
+ )
149
+ }
150
+
151
+ interface SelectPromptProps {
152
+ spec: Extract<ActiveFlowPrompt, { kind: "select" }>
153
+ onSubmit: (value: string) => void
154
+ onCancel: () => void
155
+ }
156
+
157
+ export function SelectPrompt({ spec, onSubmit, onCancel }: SelectPromptProps): React.ReactElement {
158
+ return (
159
+ <SelectField
160
+ message={spec.message}
161
+ options={spec.options}
162
+ {...(spec.initialValue !== undefined ? { initialValue: spec.initialValue } : {})}
163
+ onSubmit={onSubmit}
164
+ onCancel={onCancel}
165
+ />
166
+ )
167
+ }
168
+
169
+ export function ActiveFlowPromptView({
170
+ prompt,
171
+ onSubmit,
172
+ onCancel,
173
+ }: {
174
+ prompt: ActiveFlowPrompt
175
+ onSubmit: (value: unknown) => void
176
+ onCancel: () => void
177
+ }): React.ReactElement {
178
+ switch (prompt.kind) {
179
+ case "confirm":
180
+ return <ConfirmPrompt spec={prompt} onSubmit={(v) => onSubmit(v)} />
181
+ case "text":
182
+ return <TextPrompt spec={prompt} onSubmit={(v) => onSubmit(v)} onCancel={onCancel} />
183
+ case "password":
184
+ return <PasswordPrompt spec={prompt} onSubmit={(v) => onSubmit(v)} onCancel={onCancel} />
185
+ case "select":
186
+ return <SelectPrompt spec={prompt} onSubmit={(v) => onSubmit(v)} onCancel={onCancel} />
187
+ default:
188
+ return <Text>Unsupported prompt</Text>
189
+ }
190
+ }
191
+
192
+ export { TASK_COL_WIDTH }
@@ -0,0 +1,50 @@
1
+ import type { ReactNode } from "react"
2
+ import type { DevSelectOption } from "../runtime/dev-prompt-queue.js"
3
+
4
+ export type FlowLogLevel = "info" | "warn" | "error" | "success" | "plain"
5
+
6
+ export interface FlowLogLine {
7
+ level: FlowLogLevel
8
+ text: string
9
+ }
10
+
11
+ export type ActiveFlowPrompt =
12
+ | {
13
+ kind: "confirm"
14
+ message: string
15
+ initialValue: boolean
16
+ }
17
+ | {
18
+ kind: "text"
19
+ message: string
20
+ defaultValue?: string
21
+ placeholder?: string
22
+ validate?: (value: string) => string | undefined
23
+ }
24
+ | {
25
+ kind: "password"
26
+ message: string
27
+ }
28
+ | {
29
+ kind: "select"
30
+ message: string
31
+ options: DevSelectOption[]
32
+ initialValue?: string
33
+ }
34
+
35
+ export interface FlowPromptHandle<T = unknown> {
36
+ resolve: (value: T) => void
37
+ reject: (err: Error) => void
38
+ }
39
+
40
+ export interface FlowController {
41
+ appendLog(level: FlowLogLevel, text: string): void
42
+ appendPlain(text: string): void
43
+ setSpinner(message: string | null): void
44
+ waitForPrompt<T>(spec: ActiveFlowPrompt): Promise<T>
45
+ }
46
+
47
+ export type FlowShellProps = {
48
+ controller: FlowController
49
+ children?: ReactNode
50
+ }
package/src/ui/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from "./brand.js"
1
+ export * from "./theme.js"
2
2
  export * from "./interactive.js"
3
3
  export * from "./messages.js"
4
4
  export * from "./fatal.js"
@@ -1,27 +1,47 @@
1
- import * as p from "@clack/prompts"
1
+ import { getActiveFlowApi } from "./runtime/flow-session.js"
2
2
  import { isInteractive } from "./interactive.js"
3
+ import { plainError, plainInfo, plainSuccess, plainWarn } from "./theme.js"
3
4
 
4
5
  export const SUPATYPE_PREFIX = "[supatype]"
5
6
 
7
+ function writePlain(message = ""): void {
8
+ console.log(message)
9
+ }
10
+
6
11
  export function info(message: string): void {
12
+ const api = getActiveFlowApi()
13
+ if (api) {
14
+ api.log.info(message)
15
+ return
16
+ }
7
17
  if (isInteractive()) {
8
- p.log.info(message)
18
+ writePlain(plainInfo(message))
9
19
  return
10
20
  }
11
- console.log(`${SUPATYPE_PREFIX} ${message}`)
21
+ writePlain(`${SUPATYPE_PREFIX} ${message}`)
12
22
  }
13
23
 
14
24
  export function warn(message: string): void {
25
+ const api = getActiveFlowApi()
26
+ if (api) {
27
+ api.log.warn(message)
28
+ return
29
+ }
15
30
  if (isInteractive()) {
16
- p.log.warn(message)
31
+ writePlain(plainWarn(message))
17
32
  return
18
33
  }
19
34
  console.warn(`${SUPATYPE_PREFIX} ${message}`)
20
35
  }
21
36
 
22
37
  export function error(message: string): void {
38
+ const api = getActiveFlowApi()
39
+ if (api) {
40
+ api.log.error(message)
41
+ return
42
+ }
23
43
  if (isInteractive()) {
24
- p.log.error(message)
44
+ writePlain(plainError(message))
25
45
  return
26
46
  }
27
47
  console.error(`${SUPATYPE_PREFIX} ${message}`)
@@ -29,15 +49,37 @@ export function error(message: string): void {
29
49
 
30
50
  /** Unprefixed line (tables, diff output, blank lines). */
31
51
  export function plain(message = ""): void {
32
- console.log(message)
52
+ const api = getActiveFlowApi()
53
+ if (api) {
54
+ if (message === "") {
55
+ api.note("")
56
+ return
57
+ }
58
+ api.note(message)
59
+ return
60
+ }
61
+ writePlain(message)
33
62
  }
34
63
 
35
64
  export function step(title: string): void {
36
- console.log(`\n${title}`)
65
+ plain(`\n${title}`)
37
66
  }
38
67
 
39
68
  export type FileAction = "created" | "updated" | "skipped" | "removed" | "wrote"
40
69
 
41
70
  export function file(action: FileAction, rel: string): void {
42
- console.log(` ${action} ${rel}`)
71
+ plain(` ${action} ${rel}`)
72
+ }
73
+
74
+ export function success(message: string): void {
75
+ const api = getActiveFlowApi()
76
+ if (api) {
77
+ api.log.success(message)
78
+ return
79
+ }
80
+ if (isInteractive()) {
81
+ writePlain(plainSuccess(message))
82
+ return
83
+ }
84
+ plain(`${SUPATYPE_PREFIX} ${message}`)
43
85
  }
@@ -1,9 +1,15 @@
1
- import * as p from "@clack/prompts"
1
+ /**
2
+ * Progress UI for one-shot commands — Ink spinner inside command chrome.
3
+ */
4
+
5
+ import { getActiveFlowApi } from "./runtime/flow-session.js"
2
6
  import { isInteractive } from "./interactive.js"
3
7
  import { info } from "./messages.js"
8
+ import { runCommandChrome } from "./runtime/command-chrome.js"
4
9
 
5
10
  /**
6
- * Run an async task with a Clack spinner (TTY) or a plain status line (CI/pipes).
11
+ * Show progress with the branded Ink spinner when command chrome is active
12
+ * (or mount chrome for a standalone spinner).
7
13
  */
8
14
  export async function withSpinner<T>(
9
15
  message: string,
@@ -15,7 +21,12 @@ export async function withSpinner<T>(
15
21
  return task()
16
22
  }
17
23
 
18
- const spinner = p.spinner()
24
+ const api = getActiveFlowApi()
25
+ if (!api) {
26
+ return runCommandChrome(() => withSpinner(message, task, doneMessage))
27
+ }
28
+
29
+ const spinner = api.spinner()
19
30
  spinner.start(message)
20
31
  try {
21
32
  const result = await task()
package/src/ui/prompts.ts CHANGED
@@ -1,7 +1,7 @@
1
- import * as p from "@clack/prompts"
2
- import { SUPATYPE_ASCII_LOGO_WORDMARK, colorLogoLines } from "../dev-logo.js"
3
- import { plain } from "./messages.js"
1
+ import { colorLogoLines, SUPATYPE_ASCII_LOGO_WORDMARK } from "../dev-logo.js"
4
2
  import { isInteractive } from "./interactive.js"
3
+ import { plain } from "./messages.js"
4
+ import { CLACK_CANCEL, isCancel, p } from "./clack.js"
5
5
 
6
6
  /** Print the coloured Supatype ASCII wordmark at the top of an interactive command. */
7
7
  export function printLogo(): void {
@@ -11,17 +11,16 @@ export function printLogo(): void {
11
11
  }
12
12
 
13
13
  /**
14
- * Unwrap a clack prompt result, exiting cleanly when the user cancels (Ctrl-C).
14
+ * Unwrap a prompt result, exiting cleanly when the user cancels (Ctrl-C).
15
15
  */
16
- export function ensureNotCancelled<T>(value: T | symbol, cancelMessage = "Cancelled."): T {
17
- if (p.isCancel(value)) {
16
+ export function ensureNotCancelled<T>(value: T | typeof CLACK_CANCEL, cancelMessage = "Cancelled."): T {
17
+ if (isCancel(value)) {
18
18
  p.cancel(cancelMessage)
19
- process.exit(0)
20
19
  }
21
20
  return value as T
22
21
  }
23
22
 
24
- /** Single-line text input via Clack (TTY only). */
23
+ /** Single-line text input (TTY only). */
25
24
  export async function promptText(
26
25
  message: string,
27
26
  opts?: { defaultValue?: string; placeholder?: string },
@@ -37,4 +36,13 @@ export async function promptText(
37
36
  return ensureNotCancelled(value).trim()
38
37
  }
39
38
 
40
- export { p as clack }
39
+ /** Masked password input (TTY only). */
40
+ export async function promptPassword(message: string): Promise<string> {
41
+ if (!isInteractive()) {
42
+ throw new Error(`Cannot prompt for "${message}" in non-interactive mode.`)
43
+ }
44
+ const value = await p.password({ message })
45
+ return ensureNotCancelled(value).trim()
46
+ }
47
+
48
+ export { p, clack, isCancel, CLACK_CANCEL, runClackFlow } from "./clack.js"
@@ -0,0 +1,6 @@
1
+ /** Clack-compatible cancel sentinel for Ink prompt flows. */
2
+ export const CLACK_CANCEL = Symbol("supatype:cancel")
3
+
4
+ export function isCancel(value: unknown): value is typeof CLACK_CANCEL {
5
+ return value === CLACK_CANCEL
6
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Branded Ink chrome for one-shot CLI commands (push, diff, adopt, …).
3
+ * Reuses the flow shell: figlet logo, coloured log pane, ink-spinner, prompts.
4
+ */
5
+
6
+ import type { Command } from "commander"
7
+ import type { ClackApi } from "../clack-api.js"
8
+ import { getActiveFlowApi } from "./flow-session.js"
9
+ import { runClackFlow } from "../clack.js"
10
+ import { isInteractive } from "../interactive.js"
11
+ import {
12
+ filterCommandLogLine,
13
+ formatConsoleArgs,
14
+ stripAnsi,
15
+ } from "../../dev-log-filter.js"
16
+
17
+ function patchConsoleForCommand(api: ClackApi): () => void {
18
+ const original = {
19
+ log: console.log,
20
+ warn: console.warn,
21
+ error: console.error,
22
+ }
23
+
24
+ const emitLine = (raw: string, stream: "log" | "warn" | "error"): void => {
25
+ if (!filterCommandLogLine(raw)) return
26
+ const t = stripAnsi(raw).trim()
27
+ if (!t) return
28
+ const stripped = t.replace(/^\[supatype\]\s*/, "")
29
+
30
+ if (stream === "error" || stripped.startsWith("✗")) {
31
+ api.log.error(stripped.replace(/^✗\s*/, ""))
32
+ return
33
+ }
34
+ if (stream === "warn" || stripped.startsWith("⚠")) {
35
+ api.log.warn(stripped.replace(/^⚠\s*/, ""))
36
+ return
37
+ }
38
+ if (stripped.startsWith("✓")) {
39
+ api.log.success(stripped.replace(/^✓\s*/, ""))
40
+ return
41
+ }
42
+ if (/\[supatype\]/.test(raw)) {
43
+ api.log.info(stripped)
44
+ return
45
+ }
46
+ api.note(t)
47
+ }
48
+
49
+ const wrap =
50
+ (stream: "log" | "warn" | "error") =>
51
+ (...args: unknown[]): void => {
52
+ for (const part of formatConsoleArgs(args).split(/\r?\n/)) {
53
+ emitLine(part, stream)
54
+ }
55
+ }
56
+
57
+ console.log = wrap("log")
58
+ console.warn = wrap("warn")
59
+ console.error = wrap("error")
60
+
61
+ return () => {
62
+ console.log = original.log
63
+ console.warn = original.warn
64
+ console.error = original.error
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Run a command body inside the branded Ink shell (logo + logs + spinner).
70
+ * Falls back to plain stdout when not interactive.
71
+ */
72
+ export async function runCommandChrome<T>(fn: () => Promise<T>): Promise<T> {
73
+ if (!isInteractive() || isCommandChromeActive()) {
74
+ return fn()
75
+ }
76
+
77
+ return runClackFlow(async (api) => {
78
+ const restore = patchConsoleForCommand(api)
79
+ try {
80
+ return await fn()
81
+ } finally {
82
+ restore()
83
+ }
84
+ })
85
+ }
86
+
87
+ /** True while `runCommandChrome` / `runClackFlow` is active. */
88
+ export function isCommandChromeActive(): boolean {
89
+ return getActiveFlowApi() !== null
90
+ }
91
+
92
+ /** Commands that mount their own Ink UI — do not wrap with command chrome. */
93
+ const WIZARD_COMMAND_PATHS = new Set(["dev", "init", "link"])
94
+
95
+ /** Prefixes for subcommands that call `runClackFlow` internally. */
96
+ const WIZARD_COMMAND_PREFIXES = ["add "]
97
+
98
+ /** Long-running or stdio-inherit commands where chrome would fight subprocess output. */
99
+ const STREAMING_COMMAND_PATHS = new Set([
100
+ "logs",
101
+ "pg psql",
102
+ "functions serve",
103
+ "self-host compose logs",
104
+ ])
105
+
106
+ type CommandWithAction = Command & {
107
+ parent?: Command | null
108
+ _actionHandler?: ((args: unknown) => unknown) | null
109
+ }
110
+
111
+ /** Whether a registered command path should skip automatic command chrome. */
112
+ export function shouldExcludeCommandChrome(commandPath: string): boolean {
113
+ if (WIZARD_COMMAND_PATHS.has(commandPath)) return true
114
+ if (WIZARD_COMMAND_PREFIXES.some((prefix) => commandPath.startsWith(prefix))) return true
115
+ if (STREAMING_COMMAND_PATHS.has(commandPath)) return true
116
+ return false
117
+ }
118
+
119
+ function commandPath(cmd: CommandWithAction, root: Command): string {
120
+ const segments: string[] = []
121
+ let current: CommandWithAction | null = cmd
122
+ while (current && current !== root) {
123
+ segments.unshift(current.name())
124
+ current = current.parent ?? null
125
+ }
126
+ return segments.join(" ")
127
+ }
128
+
129
+ function wrapCommandAction(cmd: CommandWithAction, root: Command): void {
130
+ const path = commandPath(cmd, root)
131
+ const handler = cmd._actionHandler
132
+ if (!handler || shouldExcludeCommandChrome(path)) return
133
+
134
+ cmd._actionHandler = (args: unknown) =>
135
+ runCommandChrome(() => Promise.resolve(handler(args)))
136
+ }
137
+
138
+ /** Wrap every leaf command action with branded Ink chrome (see exclusions above). */
139
+ export function wrapProgramActionsWithChrome(program: Command): void {
140
+ const walk = (cmd: CommandWithAction): void => {
141
+ wrapCommandAction(cmd, program)
142
+ for (const sub of cmd.commands) {
143
+ walk(sub as CommandWithAction)
144
+ }
145
+ }
146
+ walk(program as CommandWithAction)
147
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Imperative prompt queue for the dev dashboard overlay.
3
+ */
4
+
5
+ import { CLACK_CANCEL } from "./cancel.js"
6
+
7
+ export type DevPromptKind = "confirm" | "text" | "password" | "select"
8
+
9
+ export interface DevSelectOption<T extends string = string> {
10
+ value: T
11
+ label: string
12
+ hint?: string
13
+ }
14
+
15
+ export interface DevPromptRequest<T = unknown> {
16
+ id: number
17
+ kind: DevPromptKind
18
+ message: string
19
+ initialValue?: string | boolean
20
+ defaultValue?: string
21
+ placeholder?: string
22
+ mask?: boolean
23
+ options?: DevSelectOption[]
24
+ validate?: (value: string) => string | undefined
25
+ resolve: (value: T | typeof CLACK_CANCEL) => void
26
+ }
27
+
28
+ let nextId = 0
29
+ let active: DevPromptRequest | null = null
30
+ const waiters: DevPromptRequest[] = []
31
+ const listeners = new Set<() => void>()
32
+
33
+ function notify(): void {
34
+ for (const listener of listeners) listener()
35
+ }
36
+
37
+ export function subscribeDevPrompts(listener: () => void): () => void {
38
+ listeners.add(listener)
39
+ return () => listeners.delete(listener)
40
+ }
41
+
42
+ export function getActiveDevPrompt(): DevPromptRequest | null {
43
+ return active
44
+ }
45
+
46
+ export function enqueueDevPrompt<T>(spec: Omit<DevPromptRequest<T>, "id" | "resolve">): Promise<T | typeof CLACK_CANCEL> {
47
+ return new Promise((resolve) => {
48
+ const request: DevPromptRequest<T> = {
49
+ ...spec,
50
+ id: nextId++,
51
+ resolve: resolve as DevPromptRequest["resolve"],
52
+ }
53
+ waiters.push(request as DevPromptRequest)
54
+ if (!active) {
55
+ active = waiters.shift() ?? null
56
+ }
57
+ notify()
58
+ })
59
+ }
60
+
61
+ export function resolveDevPrompt(value: unknown): void {
62
+ if (!active) return
63
+ const current = active
64
+ active = waiters.shift() ?? null
65
+ current.resolve(value)
66
+ notify()
67
+ }
68
+
69
+ export function clearDevPromptQueue(): void {
70
+ while (active) {
71
+ active.resolve(CLACK_CANCEL)
72
+ active = waiters.shift() ?? null
73
+ }
74
+ for (const pending of waiters.splice(0)) {
75
+ pending.resolve(CLACK_CANCEL)
76
+ }
77
+ notify()
78
+ }
79
+
80
+ /** @internal Tests */
81
+ export function resetDevPromptQueueForTests(): void {
82
+ clearDevPromptQueue()
83
+ nextId = 0
84
+ }
@@ -0,0 +1,11 @@
1
+ import type { ClackApi } from "../clack-api.js"
2
+
3
+ let activeFlowApi: ClackApi | null = null
4
+
5
+ export function setActiveFlowApi(api: ClackApi | null): void {
6
+ activeFlowApi = api
7
+ }
8
+
9
+ export function getActiveFlowApi(): ClackApi | null {
10
+ return activeFlowApi
11
+ }