@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
@@ -1,24 +1,30 @@
1
1
  /**
2
- * supatype cache — list and clean cached component binaries.
2
+ * supatype cache — list and clean cached component binaries + REST Valkey cache.
3
3
  */
4
4
 
5
5
  import type { Command } from "commander"
6
6
  import { existsSync, readdirSync, rmSync, statSync } from "node:fs"
7
7
  import { join } from "node:path"
8
8
  import { cacheRoot, type Component } from "../binary-cache.js"
9
- import { info, plain } from "../ui/messages.js"
9
+ import {
10
+ deleteRestCacheEntry,
11
+ flushRestCache,
12
+ getRestCacheEntry,
13
+ listRestCacheEntries,
14
+ } from "../rest-cache-admin.js"
15
+ import { error, info, plain } from "../ui/messages.js"
10
16
 
11
17
  const COMPONENTS: Component[] = ["engine", "server", "postgres", "deno"]
12
18
 
13
19
  export function registerCache(program: Command): void {
14
20
  const cache = program
15
21
  .command("cache")
16
- .description("Manage cached component binaries")
22
+ .description("Manage cached component binaries and REST API response cache")
17
23
 
18
24
  cache
19
25
  .command("list")
20
26
  .description("List cached component binaries and their sizes")
21
- .action(() => {
27
+ .action(async () => {
22
28
  const root = cacheRoot()
23
29
  if (!existsSync(root)) {
24
30
  info("Cache is empty.")
@@ -62,7 +68,7 @@ export function registerCache(program: Command): void {
62
68
  " supatype cache clean engine # remove all engine versions\n" +
63
69
  " supatype cache clean engine 0.4.2",
64
70
  )
65
- .action((component?: string, version?: string) => {
71
+ .action(async (component?: string, version?: string) => {
66
72
  const root = cacheRoot()
67
73
  if (!existsSync(root)) {
68
74
  info("Cache is already empty.")
@@ -91,6 +97,100 @@ export function registerCache(program: Command): void {
91
97
 
92
98
  info("Done.")
93
99
  })
100
+
101
+ const rest = cache
102
+ .command("rest")
103
+ .description("Manage REST API response cache in Valkey")
104
+
105
+ rest
106
+ .command("list")
107
+ .description("List cached REST GET entries")
108
+ .option("--table <name>", "Filter by table name")
109
+ .option("--json", "Output JSON")
110
+ .action(async (opts: { table?: string; json?: boolean }) => {
111
+ const cwd = process.cwd()
112
+ try {
113
+ const result = await listRestCacheEntries(cwd, { table: opts.table, limit: 100 })
114
+ if (opts.json) {
115
+ plain(JSON.stringify(result, null, 2))
116
+ return
117
+ }
118
+ if (result.entries.length === 0) {
119
+ info("No REST cache entries.")
120
+ return
121
+ }
122
+ for (const e of result.entries) {
123
+ plain(
124
+ ` ${e.table ?? "?"} ${e.scope ?? "?"} ttl=${e.ttl_seconds}s ${e.size_bytes}B ${e.key}`,
125
+ )
126
+ }
127
+ if (result.cursor !== "0") {
128
+ plain(`\n(more available — cursor ${result.cursor})`)
129
+ }
130
+ } catch (e) {
131
+ error(e instanceof Error ? e.message : String(e))
132
+ process.exitCode = 1
133
+ }
134
+ })
135
+
136
+ rest
137
+ .command("get <key>")
138
+ .description("Show one cache entry (full Valkey key)")
139
+ .option("--json", "Output JSON")
140
+ .action(async (key: string, opts: { json?: boolean }) => {
141
+ const cwd = process.cwd()
142
+ try {
143
+ const detail = await getRestCacheEntry(cwd, key)
144
+ if (opts.json) {
145
+ plain(JSON.stringify(detail, null, 2))
146
+ return
147
+ }
148
+ plain(`key: ${detail.key}`)
149
+ plain(`table: ${detail.table ?? ""}`)
150
+ plain(`scope: ${detail.scope ?? ""}`)
151
+ plain(`ttl: ${detail.ttl_seconds}s`)
152
+ plain(`status: ${detail.status_code}`)
153
+ if (detail.body_preview) plain(`body:\n${detail.body_preview}`)
154
+ } catch (e) {
155
+ error(e instanceof Error ? e.message : String(e))
156
+ process.exitCode = 1
157
+ }
158
+ })
159
+
160
+ rest
161
+ .command("delete <key>")
162
+ .description("Delete one cache entry")
163
+ .action(async (key: string) => {
164
+ const cwd = process.cwd()
165
+ try {
166
+ await deleteRestCacheEntry(cwd, key)
167
+ info(`Deleted ${key}`)
168
+ } catch (e) {
169
+ error(e instanceof Error ? e.message : String(e))
170
+ process.exitCode = 1
171
+ }
172
+ })
173
+
174
+ rest
175
+ .command("flush")
176
+ .description("Flush REST cache entries")
177
+ .option("--table <name>", "Only flush one table")
178
+ .option("--yes", "Confirm flush")
179
+ .action(async (opts: { table?: string; yes?: boolean }) => {
180
+ if (!opts.yes) {
181
+ error("Pass --yes to confirm flush")
182
+ process.exitCode = 1
183
+ return
184
+ }
185
+ const cwd = process.cwd()
186
+ try {
187
+ await flushRestCache(cwd, opts.table)
188
+ info(opts.table ? `Flushed cache for table ${opts.table}` : "Flushed all REST cache entries")
189
+ } catch (e) {
190
+ error(e instanceof Error ? e.message : String(e))
191
+ process.exitCode = 1
192
+ }
193
+ })
94
194
  }
95
195
 
96
196
  function dirSize(dir: string): number {
@@ -36,13 +36,15 @@ import {
36
36
  import { ensureBinary } from "../ensure-binary.js"
37
37
  import { startProxyDevApp } from "../app/proxy-dev-app.js"
38
38
  import { ProcessManager } from "../process-manager.js"
39
- import { startStudioViteDevServer } from "../studio-dev-server.js"
39
+ import { STUDIO_DEV_PORT, startStudioViteDevServer } from "../studio-dev-server.js"
40
40
  import { restoreSystemRelationTargets } from "../restore-system-relation-targets.js"
41
41
  import { localStorageEnv } from "../local-storage.js"
42
42
  import { beginDevSession, endDevSession, resolveDevUiMode, startDevSession } from "../dev-session.js"
43
+ import { publishDevReady } from "../dev-ready-panel.js"
43
44
  import { probeDockerDaemon, reportDockerUnavailable } from "../docker-runtime.js"
44
45
  import { fatalError } from "../ui/fatal.js"
45
46
  import { registerDevShutdown } from "../dev-shutdown.js"
47
+ import { ensureValkeySidecar, stopValkeySidecar } from "../valkey-sidecar.js"
46
48
  import {
47
49
  initdb,
48
50
  start as pgStart,
@@ -133,7 +135,6 @@ export function registerDev(program: Command): void {
133
135
  }
134
136
 
135
137
  beginDevSession(resolveDevUiMode(opts.stream === true))
136
- startDevSession()
137
138
  if (hasMeaningfulOverrides(config)) {
138
139
  console.warn("[supatype] Local binary overrides active:")
139
140
  for (const line of describeActiveOverrides(config)) {
@@ -326,9 +327,13 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
326
327
  ? "ses"
327
328
  : "smtp"
328
329
 
330
+ const valkeySidecar = ensureValkeySidecar(projectName)
331
+
329
332
  const serverEnv: Record<string, string> = {
330
333
  // supatype-server outer layer
331
334
  SUPATYPE_MODE: config.server.mode ?? "dev",
335
+ ...(valkeySidecar.addr ? { SUPATYPE_VALKEY_ADDR: valkeySidecar.addr } : {}),
336
+ SUPATYPE_MANAGED_PROJECT_REF: projectName,
332
337
  SUPATYPE_MANIFEST_PATH: manifestPath,
333
338
  SUPATYPE_ADMIN_CONFIG_PATH: adminConfigPath,
334
339
  SUPATYPE_POSTGREST_URL: `http://127.0.0.1:${postgrestPort}`,
@@ -471,7 +476,7 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
471
476
  }
472
477
 
473
478
  // ── 9d. Studio (optional) ─────────────────────────────────────────────
474
- const studioPort = 3002
479
+ const studioPort = STUDIO_DEV_PORT
475
480
  let studioProc: ProcessManager | null = null
476
481
 
477
482
  const studioOverride = config.overrides?.studio
@@ -489,25 +494,24 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
489
494
 
490
495
  const appProc = startProxyDevApp(cwd, config, pidDir)
491
496
 
492
- // ── Print status ──────────────────────────────────────────────────────
493
- console.log(`
494
- [supatype] Services running:
495
- Postgres ${dbURL}
496
- supatype-server http://localhost:${serverPort}
497
- REST API http://localhost:${serverPort}/rest/v1/
498
- Auth http://localhost:${serverPort}/auth/v1/
499
- Storage http://localhost:${serverPort}/storage/v1/
500
- Realtime ws://localhost:${serverPort}/realtime/v1/${studioProc ? `\n Studio http://localhost:${studioPort}` : ""}
501
-
502
- API keys (local dev only):
503
- anon key ${anonKey}
504
- service_role ${serviceRoleKey}
505
-
506
- JWT secret: ${LOCAL_JWT_SECRET}
507
-
508
- Press Ctrl+C to stop.
509
- `)
497
+ const links = [
498
+ { label: "API", url: `http://localhost:${serverPort}` },
499
+ { label: "REST", url: `http://localhost:${serverPort}/rest/v1/` },
500
+ { label: "Auth", url: `http://localhost:${serverPort}/auth/v1/` },
501
+ { label: "Storage", url: `http://localhost:${serverPort}/storage/v1/` },
502
+ { label: "Realtime", url: `ws://localhost:${serverPort}/realtime/v1/` },
503
+ ]
504
+ if (studioProc) {
505
+ links.push({ label: "Studio", url: `http://localhost:${studioPort}` })
506
+ }
510
507
 
508
+ publishDevReady({
509
+ title: "Services running",
510
+ links,
511
+ anonKey,
512
+ serviceRoleKey,
513
+ hints: [`Postgres ${dbURL}`, `JWT secret: ${LOCAL_JWT_SECRET}`],
514
+ })
511
515
 
512
516
  // ── Shutdown handler ──────────────────────────────────────────────────
513
517
  registerDevShutdown(async () => {
@@ -518,6 +522,7 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
518
522
  studioProc?.stop(),
519
523
  appProc?.stop(),
520
524
  ])
525
+ stopValkeySidecar(valkeySidecar.containerName)
521
526
  await stopPostgres()
522
527
  })
523
528
 
@@ -1,36 +1,25 @@
1
1
  import type { Command } from "commander"
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
3
- import { resolve, join, dirname, basename } from "node:path"
4
- import { fileURLToPath } from "node:url"
3
+ import { resolve, join, basename } from "node:path"
5
4
  import { spawnSync } from "node:child_process"
6
- import * as p from "@clack/prompts"
5
+ import { p, runClackFlow } from "../ui/clack.js"
7
6
  import { ensureNotCancelled, printLogo } from "../ui/prompts.js"
8
7
  import { generateAndWriteKeys } from "./keys.js"
9
8
  import { file, error, info, plain, warn } from "../ui/messages.js"
10
9
  import { nextSteps } from "../ui/next-steps.js"
11
10
  import { probeDockerDaemon, reportDockerUnavailable } from "../docker-runtime.js"
12
-
13
- export { scaffold }
14
-
15
- // ─── Markers used by `supatype app add / remove` (app.ts) ────────────────────
16
- export const APP_COMPOSE_MARKER = " # ─── App service (run: supatype app add) ───"
17
- export const KONG_APP_MARKER = " # ─── App fallback route (run: supatype app add) ───"
18
-
19
- const CLI_PACKAGE_JSON = join(
20
- dirname(fileURLToPath(import.meta.url)),
21
- "..",
22
- "..",
23
- "package.json",
24
- )
25
-
26
- function cliPackageVersion(): string {
27
- try {
28
- const pkg = JSON.parse(readFileSync(CLI_PACKAGE_JSON, "utf8")) as { version?: string }
29
- return pkg.version ?? "0.1.0"
30
- } catch {
31
- return "0.1.0"
32
- }
33
- }
11
+ import { loadConfig } from "../config.js"
12
+ import {
13
+ ensureComponentBinaries,
14
+ reportComponentBinaryFailures,
15
+ } from "../ensure-component-binaries.js"
16
+ import { mergeSupatypePackageJson } from "../init-package-json.js"
17
+ import { cliPackageVersion } from "../cli-package-version.js"
18
+ import {
19
+ initDependencyVersionsFallback,
20
+ resolveInitDependencyVersions,
21
+ type InitDependencyVersions,
22
+ } from "../init-dependency-versions.js"
34
23
 
35
24
  // ─── Options model ─────────────────────────────────────────────────────────--
36
25
 
@@ -200,9 +189,23 @@ export function registerInit(program: Command): void {
200
189
 
201
190
  if (name) mkdirSync(dir, { recursive: true })
202
191
 
203
- scaffold(dir, result)
192
+ const deps = await resolveInitDependencyVersions()
193
+ const runningCli = cliPackageVersion()
194
+ if (deps.cli !== runningCli || deps.types !== runningCli) {
195
+ info(
196
+ `Using published npm versions: @supatype/cli ^${deps.cli}, @supatype/types ^${deps.types}`,
197
+ )
198
+ }
199
+
200
+ scaffold(dir, result, deps)
201
+
202
+ const installOk = doInstall ? runInstall(dir, result.packageManager) : true
203
+
204
+ const binariesReady = installOk ? await ensureInitBinaries(dir) : false
205
+ if (doInstall && !installOk) {
206
+ warn("Skipping component binary check until dependencies install successfully.")
207
+ }
204
208
 
205
- if (doInstall) runInstall(dir, result.packageManager)
206
209
  const keysGenerated = doKeys ? writeKeys(dir) : false
207
210
 
208
211
  warnDockerUnavailableForProvider(result.provider)
@@ -210,8 +213,9 @@ export function registerInit(program: Command): void {
210
213
  printNextSteps({
211
214
  name,
212
215
  result,
213
- installed: doInstall,
216
+ installed: doInstall && installOk,
214
217
  keysGenerated,
218
+ binariesReady,
215
219
  })
216
220
  })
217
221
  }
@@ -222,6 +226,7 @@ async function runWizard(
222
226
  defaultName: string,
223
227
  defaultTarget: ProductionTarget,
224
228
  ): Promise<WizardResult> {
229
+ return runClackFlow(async () => {
225
230
  p.intro("Create a new Supatype project")
226
231
 
227
232
  const projectName = ensureNotCancelled(
@@ -407,6 +412,7 @@ async function runWizard(
407
412
  generateKeys,
408
413
  ...(adminEmail && adminPassword ? { adminEmail, adminPassword } : {}),
409
414
  }
415
+ })
410
416
  }
411
417
 
412
418
  async function promptApp(): Promise<ScaffoldAppOptions> {
@@ -485,7 +491,7 @@ function detectInvokingPackageManager(): PackageManager {
485
491
  return "npm"
486
492
  }
487
493
 
488
- function runInstall(dir: string, pm: PackageManager): void {
494
+ function runInstall(dir: string, pm: PackageManager): boolean {
489
495
  info(`Installing dependencies with ${pm}...`)
490
496
  const res = spawnSync(pm, ["install"], {
491
497
  cwd: dir,
@@ -494,6 +500,29 @@ function runInstall(dir: string, pm: PackageManager): void {
494
500
  })
495
501
  if (res.status !== 0 || res.error) {
496
502
  warn(`Dependency install did not complete (run "${pm} install" manually).`)
503
+ return false
504
+ }
505
+ return true
506
+ }
507
+
508
+ async function ensureInitBinaries(dir: string): Promise<boolean> {
509
+ const previousCwd = process.cwd()
510
+ try {
511
+ process.chdir(dir)
512
+ const config = loadConfig(dir)
513
+ const result = await ensureComponentBinaries(config, dir)
514
+ if (!result.ok) {
515
+ reportComponentBinaryFailures(result.failures)
516
+ return false
517
+ }
518
+ return true
519
+ } catch (err) {
520
+ const message = err instanceof Error ? err.message : String(err)
521
+ warn(`Could not verify component binaries: ${message}`)
522
+ warn("Run manually from your project directory: supatype update")
523
+ return false
524
+ } finally {
525
+ process.chdir(previousCwd)
497
526
  }
498
527
  }
499
528
 
@@ -508,7 +537,11 @@ function writeKeys(dir: string): boolean {
508
537
 
509
538
  // ─── Scaffold ──────────────────────────────────────────────────────────────--
510
539
 
511
- function scaffold(dir: string, optsOrName: ScaffoldOptions | string): void {
540
+ export function scaffold(
541
+ dir: string,
542
+ optsOrName: ScaffoldOptions | string,
543
+ deps: InitDependencyVersions = initDependencyVersionsFallback(),
544
+ ): void {
512
545
  const opts =
513
546
  typeof optsOrName === "string" ? defaultScaffoldOptions(optsOrName) : optsOrName
514
547
  const write = (rel: string, content: string) => {
@@ -520,9 +553,17 @@ function scaffold(dir: string, optsOrName: ScaffoldOptions | string): void {
520
553
 
521
554
  const pkgPath = join(dir, "package.json")
522
555
  if (!existsSync(pkgPath)) {
523
- write("package.json", packageJsonTemplate(opts, cliPackageVersion()))
556
+ write("package.json", packageJsonTemplate(opts, deps))
524
557
  } else {
525
- file("skipped", "package.json (already exists)")
558
+ try {
559
+ const existing = JSON.parse(readFileSync(pkgPath, "utf8")) as Record<string, unknown>
560
+ const merged = mergeSupatypePackageJson(existing, opts, deps)
561
+ writeFileSync(pkgPath, `${JSON.stringify(merged, null, 2)}\n`, "utf8")
562
+ file("updated", "package.json (added Supatype dependencies)")
563
+ } catch {
564
+ warn("Could not merge into package.json — add @supatype/cli and @supatype/types manually.")
565
+ file("skipped", "package.json (invalid JSON)")
566
+ }
526
567
  }
527
568
 
528
569
  write("supatype.config.ts", tsConfigTemplate(opts))
@@ -565,8 +606,13 @@ function scaffoldAppAssets(
565
606
 
566
607
  if (opts.app.mode === "static") {
567
608
  const staticRel = staticDirRelative(opts.app.staticDir)
609
+ if (opts.app.viteDevUrl && opts.productionTarget !== "later") {
610
+ write("dist/index.html", holding)
611
+ scaffoldVite(opts, write, holding)
612
+ return
613
+ }
568
614
  write(`${staticRel}/index.html`, holding)
569
- if (hasVite) scaffoldVite(opts, write, holding)
615
+ if (opts.app.viteDevUrl) scaffoldVite(opts, write, holding)
570
616
  return
571
617
  }
572
618
 
@@ -609,7 +655,7 @@ function scaffoldHelloFunction(
609
655
 
610
656
  // ─── Templates ───────────────────────────────────────────────────────────────
611
657
 
612
- function packageJsonTemplate(opts: ScaffoldOptions, cliVersion: string): string {
658
+ function packageJsonTemplate(opts: ScaffoldOptions, deps: InitDependencyVersions): string {
613
659
  const scripts: string[] = [
614
660
  ` "dev": "supatype dev"`,
615
661
  ` "push": "supatype push"`,
@@ -633,8 +679,8 @@ function packageJsonTemplate(opts: ScaffoldOptions, cliVersion: string): string
633
679
  ${scripts.join(",\n")}
634
680
  },
635
681
  "dependencies": {
636
- "@supatype/cli": "^${cliVersion}",
637
- "@supatype/types": "^${cliVersion}"
682
+ "@supatype/cli": "^${deps.cli}",
683
+ "@supatype/types": "^${deps.types}"
638
684
  },
639
685
  "devDependencies": {
640
686
  ${devDeps.join(",\n")}
@@ -701,20 +747,49 @@ function localConfigTemplate(app: ScaffoldAppOptions): string {
701
747
  `const localConfig: Partial<SupatypeConfig> = {`,
702
748
  ` server: { mode: "dev" },`,
703
749
  ]
704
- if (app.mode === "proxy") {
705
- lines.push(` app: {`)
706
- lines.push(` mode: "proxy",`)
707
- lines.push(` upstream: "${app.upstream ?? "http://localhost:3000"}",`)
708
- lines.push(` start: "${app.start ?? "dev"}",`)
709
- if (app.viteDevUrl) lines.push(` vite_dev_url: "${app.viteDevUrl}",`)
710
- lines.push(` },`)
750
+ const localApp = localDevAppConfigLines(app)
751
+ if (localApp.length > 0) {
752
+ lines.push(...localApp)
711
753
  }
712
754
  lines.push(`}`, ``, `export default localConfig`, ``)
713
755
  return lines.join("\n")
714
756
  }
715
757
 
758
+ /** App block for supatype.local.config.ts (proxy + Vite during local dev). */
759
+ function localDevAppConfigLines(app: ScaffoldAppOptions): string[] {
760
+ if (app.mode === "proxy") {
761
+ return [
762
+ ` app: {`,
763
+ ` mode: "proxy",`,
764
+ ` upstream: "${app.upstream ?? "http://localhost:3000"}",`,
765
+ ` start: "${app.start ?? "dev"}",`,
766
+ ...(app.viteDevUrl ? [` vite_dev_url: "${app.viteDevUrl}",`] : []),
767
+ ` },`,
768
+ ]
769
+ }
770
+ if (app.viteDevUrl) {
771
+ return [
772
+ ` app: {`,
773
+ ` mode: "proxy",`,
774
+ ` upstream: "${app.viteDevUrl}",`,
775
+ ` start: "vite",`,
776
+ ` vite_dev_url: "${app.viteDevUrl}",`,
777
+ ` },`,
778
+ ]
779
+ }
780
+ return []
781
+ }
782
+
716
783
  function appConfigLines(app: ScaffoldAppOptions, productionTarget: ProductionTarget): string[] {
717
784
  if (app.mode === "static") {
785
+ if (productionTarget !== "later" && app.viteDevUrl) {
786
+ return [
787
+ ` app: {`,
788
+ ` mode: "static",`,
789
+ ` static_dir: "./dist", // production build output`,
790
+ ` },`,
791
+ ]
792
+ }
718
793
  const out = [
719
794
  ` app: {`,
720
795
  ` mode: "static",`,
@@ -774,7 +849,7 @@ function holdingPageTemplate(projectName: string): string {
774
849
  <meta charset="UTF-8" />
775
850
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
776
851
  <title>${name} — Supatype</title>
777
- <meta name="description" content="A Supatype project. Define your types — we generate your backend." />
852
+ <meta name="description" content="A Supatype project. Define your types — we generate your platform." />
778
853
  <link rel="preconnect" href="https://fonts.googleapis.com" />
779
854
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
780
855
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
@@ -909,6 +984,8 @@ export default defineConfig({
909
984
  host: "127.0.0.1",
910
985
  port: ${port},
911
986
  strictPort: true,
987
+ // Docker Compose app proxy reaches the host as host.docker.internal.
988
+ allowedHosts: ["127.0.0.1", "localhost", "host.docker.internal"],
912
989
  },
913
990
  })
914
991
  `
@@ -1185,11 +1262,13 @@ function printNextSteps(args: {
1185
1262
  result: WizardResult
1186
1263
  installed: boolean
1187
1264
  keysGenerated: boolean
1265
+ binariesReady: boolean
1188
1266
  }): void {
1189
- const { name, result, installed, keysGenerated } = args
1267
+ const { name, result, installed, keysGenerated, binariesReady } = args
1190
1268
  const steps: string[] = []
1191
1269
  if (name) steps.push(`cd ${name}`)
1192
1270
  if (!installed) steps.push(`${result.packageManager} install`)
1271
+ if (!binariesReady) steps.push("supatype update # download component binaries")
1193
1272
  if (!keysGenerated) steps.push("supatype keys")
1194
1273
  const kongHint =
1195
1274
  result.provider === "docker" && result.kongPort !== undefined
@@ -12,7 +12,7 @@ import { targetFetch } from "../target-client.js"
12
12
  import { error, info, plain } from "../ui/messages.js"
13
13
  import { nextSteps } from "../ui/next-steps.js"
14
14
  import { isInteractive } from "../ui/interactive.js"
15
- import { ensureNotCancelled, printLogo, clack as p } from "../ui/prompts.js"
15
+ import { ensureNotCancelled, p, runClackFlow } from "../ui/prompts.js"
16
16
 
17
17
  function resolveLinkToken(opts: {
18
18
  token?: string
@@ -145,35 +145,36 @@ export async function runLinkAction(opts: {
145
145
  let url = opts.url
146
146
 
147
147
  if (isInteractive() && !project && !url) {
148
- printLogo()
149
- p.intro("Link this project")
150
- const targetKind = ensureNotCancelled(
151
- await p.select<"cloud" | "self-host">({
152
- message: "Link target",
153
- options: [
154
- { value: "cloud", label: "Supatype Cloud", hint: "managed project on supatype.com" },
155
- { value: "self-host", label: "Self-host", hint: "your Kong gateway URL" },
156
- ],
157
- }),
158
- )
159
- if (targetKind === "cloud") {
160
- project = ensureNotCancelled(
161
- await p.text({
162
- message: "Cloud project slug",
163
- placeholder: projectRef,
164
- defaultValue: projectRef,
148
+ await runClackFlow(async () => {
149
+ p.intro("Link this project")
150
+ const targetKind = ensureNotCancelled(
151
+ await p.select<"cloud" | "self-host">({
152
+ message: "Link target",
153
+ options: [
154
+ { value: "cloud", label: "Supatype Cloud", hint: "managed project on supatype.com" },
155
+ { value: "self-host", label: "Self-host", hint: "your Kong gateway URL" },
156
+ ],
165
157
  }),
166
- ).trim()
167
- } else {
168
- url = ensureNotCancelled(
169
- await p.text({
170
- message: "Kong gateway URL",
171
- placeholder: "https://api.example.com",
172
- defaultValue: "http://localhost:18473",
173
- }),
174
- ).trim()
175
- }
176
- p.outro("Linking...")
158
+ )
159
+ if (targetKind === "cloud") {
160
+ project = ensureNotCancelled(
161
+ await p.text({
162
+ message: "Cloud project slug",
163
+ placeholder: projectRef,
164
+ defaultValue: projectRef,
165
+ }),
166
+ ).trim()
167
+ } else {
168
+ url = ensureNotCancelled(
169
+ await p.text({
170
+ message: "Kong gateway URL",
171
+ placeholder: "https://api.example.com",
172
+ defaultValue: "http://localhost:18473",
173
+ }),
174
+ ).trim()
175
+ }
176
+ p.outro("Linking...")
177
+ })
177
178
  }
178
179
 
179
180
  const token = resolveLinkToken(opts)
@@ -0,0 +1,68 @@
1
+ import { existsSync } from "node:fs"
2
+ import { dirname, isAbsolute, join, resolve } from "node:path"
3
+ import { spawnSync } from "node:child_process"
4
+ import { VERSION_PIN_LOCAL } from "./binary-cache.js"
5
+ import { appendStackOutput, getActiveDevSession } from "./dev-session.js"
6
+ import { requireDockerDaemon } from "./docker-runtime.js"
7
+ import type { SupatypeProjectConfig } from "./project-config.js"
8
+
9
+ /** Docker tag for compose dev when `versions.server` is `local`. */
10
+ export const LOCAL_SERVER_DOCKER_IMAGE = "supatype/server:local-dev"
11
+
12
+ /** Find the server repo root (directory containing `Dockerfile`) from an overrides.server binary path. */
13
+ export function resolveServerSourceRoot(serverOverride: string, cwd: string): string | null {
14
+ const binaryPath = isAbsolute(serverOverride) ? serverOverride : resolve(cwd, serverOverride)
15
+ let dir = dirname(binaryPath)
16
+ for (let depth = 0; depth < 5; depth++) {
17
+ if (existsSync(join(dir, "Dockerfile"))) return dir
18
+ const parent = dirname(dir)
19
+ if (parent === dir) break
20
+ dir = parent
21
+ }
22
+ return null
23
+ }
24
+
25
+ /**
26
+ * When `versions.server` is `local`, build a Linux server image from the source tree
27
+ * next to `overrides.server` so compose dev matches the contributor's binary.
28
+ */
29
+ export async function ensureLocalServerDockerImage(
30
+ cwd: string,
31
+ config: SupatypeProjectConfig,
32
+ brand?: { intro?: string },
33
+ ): Promise<string | undefined> {
34
+ if (config.versions?.server !== VERSION_PIN_LOCAL) return undefined
35
+
36
+ const override = config.overrides?.server?.trim()
37
+ if (!override) return undefined
38
+
39
+ const root = resolveServerSourceRoot(override, cwd)
40
+ if (!root) {
41
+ console.warn(
42
+ `[supatype] ⚠ versions.server is "local" but no Dockerfile found near ${override}. ` +
43
+ "Compose will use the default supatype/server:latest image.",
44
+ )
45
+ return undefined
46
+ }
47
+
48
+ requireDockerDaemon(brand?.intro ? { brand: { intro: brand.intro } } : undefined)
49
+ const dockerfile = join(root, "Dockerfile")
50
+ console.log(`[supatype] Building local server Docker image from ${root}…`)
51
+ const result = spawnSync(
52
+ "docker",
53
+ ["build", "--progress=plain", "-t", LOCAL_SERVER_DOCKER_IMAGE, "-f", dockerfile, root],
54
+ { encoding: "utf8", stdio: "pipe" },
55
+ )
56
+ const tui = getActiveDevSession()?.isTui() ?? false
57
+ if (!tui) {
58
+ appendStackOutput(result.stdout, "log")
59
+ appendStackOutput(result.stderr, result.status === 0 ? "log" : "error")
60
+ } else if (result.status !== 0) {
61
+ appendStackOutput(result.stderr || result.stdout || "docker build failed", "error")
62
+ }
63
+ if (result.status !== 0) {
64
+ throw new Error(`docker build failed for local server (exit ${result.status ?? "unknown"})`)
65
+ }
66
+ console.log(`[supatype] Local server image ready: ${LOCAL_SERVER_DOCKER_IMAGE}`)
67
+ return LOCAL_SERVER_DOCKER_IMAGE
68
+ }