okengine 0.10.2 → 0.11.0

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 (343) hide show
  1. package/package.json +76 -10
  2. package/site/content/docs/ai/mcp.mdx +0 -1
  3. package/site/content/docs/deployment/docker-swarm.mdx +48 -111
  4. package/site/content/docs/deployment/docker.mdx +43 -36
  5. package/site/content/docs/deployment/index.mdx +9 -9
  6. package/site/content/docs/deployment/kubernetes.mdx +4 -4
  7. package/site/content/docs/elements/ai.mdx +7 -13
  8. package/site/content/docs/elements/channel.mdx +9 -13
  9. package/site/content/docs/elements/clock.mdx +8 -14
  10. package/site/content/docs/elements/flow.mdx +46 -54
  11. package/site/content/docs/elements/gate.mdx +6 -11
  12. package/site/content/docs/elements/signal.mdx +2 -8
  13. package/site/content/docs/elements/store.mdx +79 -93
  14. package/site/content/docs/elements/vault.mdx +89 -76
  15. package/site/content/docs/get-started/basic-usage.mdx +8 -14
  16. package/site/content/docs/get-started/installation.mdx +36 -36
  17. package/site/content/docs/get-started/introduction.mdx +11 -0
  18. package/site/content/docs/get-started/why.mdx +3 -4
  19. package/site/content/docs/index.mdx +0 -1
  20. package/site/content/docs/meta.json +0 -1
  21. package/site/content/docs/plugins/anonymous.mdx +1 -1
  22. package/site/content/docs/plugins/magic-link.mdx +11 -11
  23. package/site/content/docs/plugins/otp.mdx +2 -2
  24. package/site/content/docs/plugins/passkey.mdx +1 -1
  25. package/site/content/docs/plugins/two-factor.mdx +1 -1
  26. package/site/content/docs/plugins/username.mdx +1 -1
  27. package/site/content/docs/recipes/caddy.mdx +1 -1
  28. package/site/content/docs/recipes/cockroachdb.mdx +2 -2
  29. package/site/content/docs/recipes/dragonfly.mdx +1 -1
  30. package/site/content/docs/recipes/index.mdx +1 -2
  31. package/site/content/docs/recipes/llama-cpp.mdx +21 -23
  32. package/site/content/docs/recipes/mailpit.mdx +11 -11
  33. package/site/content/docs/recipes/meilisearch.mdx +10 -10
  34. package/site/content/docs/recipes/meta.json +0 -1
  35. package/site/content/docs/recipes/ollama.mdx +1 -1
  36. package/site/content/docs/recipes/pgdog.mdx +12 -9
  37. package/site/content/docs/recipes/postgres.mdx +3 -3
  38. package/site/content/docs/recipes/redis.mdx +2 -2
  39. package/site/content/docs/recipes/rustfs.mdx +10 -7
  40. package/site/content/docs/recipes/sglang.mdx +1 -1
  41. package/site/content/docs/recipes/supabase-docker.mdx +1 -1
  42. package/site/content/docs/recipes/timescale.mdx +2 -2
  43. package/site/content/docs/recipes/valkey.mdx +1 -1
  44. package/site/content/docs/recipes/vllm.mdx +1 -1
  45. package/site/content/docs/recipes/yugabytedb.mdx +2 -2
  46. package/site/content/docs/reference/cli.md +61 -38
  47. package/site/content/docs/reference/configuration.mdx +145 -70
  48. package/site/content/docs/reference/environment-variables.mdx +36 -28
  49. package/site/content/docs/reference/errors.mdx +28 -29
  50. package/site/content/docs/reference/fx.mdx +14 -7
  51. package/site/content/docs/reference/i18n.mdx +17 -6
  52. package/site/content/docs/reference/index.mdx +5 -0
  53. package/site/content/docs/reference/meta.json +1 -0
  54. package/site/content/docs/reference/migrating-environments.mdx +158 -0
  55. package/site/content/docs/reference/plugins.mdx +2 -2
  56. package/src/auth/gate-auth.test.ts +12 -10
  57. package/src/cli/ai-setup/ai-setup.test.ts +30 -16
  58. package/src/cli/ai-setup/apply.ts +111 -15
  59. package/src/cli/ai-setup/catalog.ts +3 -3
  60. package/src/cli/ai-setup/index.ts +1 -1
  61. package/src/cli/ai-setup/prompts.ts +1 -1
  62. package/src/cli/ai-setup/recommend.test.ts +1 -1
  63. package/src/cli/ask-seed.test.ts +3 -3
  64. package/src/cli/build.ts +8 -9
  65. package/src/cli/db-auto-push.test.ts +6 -3
  66. package/src/cli/db-auto-push.ts +7 -5
  67. package/src/cli/db-seed.ts +7 -8
  68. package/src/cli/db.test.ts +70 -71
  69. package/src/cli/db.ts +86 -12
  70. package/src/cli/dev-controller.test.ts +35 -0
  71. package/src/cli/dev-controller.ts +298 -0
  72. package/src/cli/dev-controls.ts +11 -65
  73. package/src/cli/dev-db-push.test.ts +47 -15
  74. package/src/cli/dev-schema-sync.test.ts +11 -11
  75. package/src/cli/dev-schema-sync.ts +10 -10
  76. package/src/cli/dev-session-lock.test.ts +106 -0
  77. package/src/cli/dev-session-lock.ts +209 -0
  78. package/src/cli/dev.test.ts +246 -114
  79. package/src/cli/dev.ts +153 -205
  80. package/src/cli/doc-staleness.test.ts +3 -6
  81. package/src/cli/docker-clean.test.ts +33 -1
  82. package/src/cli/docker-clean.ts +28 -33
  83. package/src/cli/docker-cli.test.ts +12 -11
  84. package/src/cli/docker.ts +95 -20
  85. package/src/cli/doctor.test.ts +40 -0
  86. package/src/cli/doctor.ts +142 -9
  87. package/src/cli/drizzle-env.test.ts +11 -13
  88. package/src/cli/drizzle-env.ts +18 -11
  89. package/src/cli/ensure-drizzle-config.ts +11 -14
  90. package/src/cli/hero-meta.test.ts +15 -15
  91. package/src/cli/hero-meta.ts +10 -9
  92. package/src/cli/index.ts +36 -6
  93. package/src/cli/load-config.images.test.ts +44 -10
  94. package/src/cli/load-config.source.test.ts +50 -0
  95. package/src/cli/load-config.ts +35 -29
  96. package/src/cli/meilisearch-local.ts +1 -2
  97. package/src/cli/mode.ts +7 -58
  98. package/src/cli/registry.ts +175 -19
  99. package/src/cli/resolve-dev-sql-env.test.ts +8 -16
  100. package/src/cli/resolve-dev-sql-env.ts +13 -15
  101. package/src/cli/safe-defaults.test.ts +29 -11
  102. package/src/cli/schema.ts +65 -16
  103. package/src/cli/test.ts +58 -0
  104. package/src/cli/tui/App.tsx +252 -0
  105. package/src/cli/tui/DevLive.tsx +116 -0
  106. package/src/cli/tui/components/ConfirmDialog.tsx +48 -0
  107. package/src/cli/tui/components/HeaderBar.tsx +67 -0
  108. package/src/cli/tui/components/LogStream.tsx +47 -0
  109. package/src/cli/tui/components/PanelCard.tsx +39 -0
  110. package/src/cli/tui/components/SelectRow.tsx +37 -0
  111. package/src/cli/tui/components/SlashPalette.tsx +172 -0
  112. package/src/cli/tui/components/Spinner.tsx +32 -0
  113. package/src/cli/tui/components/StatusBar.tsx +51 -0
  114. package/src/cli/tui/components/StatusDot.tsx +37 -0
  115. package/src/cli/tui/components/TabBar.tsx +49 -0
  116. package/src/cli/tui/components/Wordmark.tsx +43 -0
  117. package/src/cli/tui/format.ts +38 -0
  118. package/src/cli/tui/keys.test.ts +18 -0
  119. package/src/cli/tui/keys.ts +26 -0
  120. package/src/cli/tui/launch.test.ts +18 -0
  121. package/src/cli/tui/launch.ts +57 -0
  122. package/src/cli/tui/panels/Dashboard.tsx +201 -0
  123. package/src/cli/tui/panels/Database.tsx +198 -0
  124. package/src/cli/tui/panels/Dev.tsx +257 -0
  125. package/src/cli/tui/panels/Docker.tsx +357 -0
  126. package/src/cli/tui/panels/Navigator.test.ts +22 -0
  127. package/src/cli/tui/panels/Navigator.tsx +213 -0
  128. package/src/cli/tui/slash-catalog.test.ts +31 -0
  129. package/src/cli/tui/slash-catalog.ts +184 -0
  130. package/src/cli/tui/slash-run.ts +54 -0
  131. package/src/cli/tui/state/ports.ts +95 -0
  132. package/src/cli/tui/theme.ts +24 -0
  133. package/src/cli/vault-cmd.test.ts +402 -0
  134. package/src/cli/vault-cmd.ts +727 -69
  135. package/src/cli/vault-secure-input.test.ts +56 -0
  136. package/src/cli/vault-secure-input.ts +124 -0
  137. package/src/compiler/effects-infer.ts +7 -0
  138. package/src/compiler/extract.ts +12 -0
  139. package/src/compiler/fixtures/skyport/oke.config.ts +7 -5
  140. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
  141. package/src/compiler/fixtures/skyport.expected.json +1 -1
  142. package/src/compiler/generate-adopt.ts +27 -1
  143. package/src/compiler-entry.ts +7 -0
  144. package/src/config/define-config.test.ts +111 -47
  145. package/src/config/driver-defaults.test.ts +115 -0
  146. package/src/config/driver-defaults.ts +156 -0
  147. package/src/config/index.ts +246 -131
  148. package/src/config/resolve-driver.test.ts +14 -19
  149. package/src/console/server/app.ts +4 -2
  150. package/src/console/server/console.test.ts +58 -1
  151. package/src/console/server/flows.ts +22 -2
  152. package/src/console/server/index.ts +3 -0
  153. package/src/console/server/operator-db.test.ts +55 -13
  154. package/src/console/server/operator-db.ts +305 -263
  155. package/src/console/server/serve.ts +16 -7
  156. package/src/console/server/state.ts +31 -9
  157. package/src/console/server/vault.test.ts +120 -5
  158. package/src/console/server/vault.ts +208 -12
  159. package/src/console/ui/dist/assets/{index-D-9KFce5.js → index--Y0Uaz-m.js} +2 -2
  160. package/src/console/ui/dist/assets/panel-vault-BkZdJPX8.js +1 -0
  161. package/src/console/ui/dist/assets/style-C3L349iZ.css +3 -0
  162. package/src/console/ui/dist/index.html +2 -2
  163. package/src/console/ui/shell/client.ts +16 -0
  164. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +54 -1
  165. package/src/console/ui/vault/VaultA11yView.tsx +33 -1
  166. package/src/console/ui/vault/backend.test.ts +75 -0
  167. package/src/console/ui/vault/backend.ts +117 -0
  168. package/src/console/ui/vault/fixture.ts +21 -2
  169. package/src/console/ui/vault/index.ts +11 -1
  170. package/src/console/ui/vault/types.ts +26 -0
  171. package/src/docker/compose.ts +164 -61
  172. package/src/docker/derive.ts +106 -12
  173. package/src/docker/docker.test.ts +156 -110
  174. package/src/docker/helpers.ts +98 -0
  175. package/src/docker/images-config.test.ts +163 -0
  176. package/src/docker/index.ts +30 -5
  177. package/src/docker/recipes/index.ts +0 -3
  178. package/src/docker/recipes/llama-cpp.ts +4 -4
  179. package/src/docker/recipes/pgdog.ts +9 -2
  180. package/src/docker/resources.test.ts +71 -0
  181. package/src/docker/resources.ts +176 -0
  182. package/src/docker/stack-id.test.ts +11 -4
  183. package/src/docker/stack-id.ts +42 -32
  184. package/src/docker/stack.integration.test.ts +4 -15
  185. package/src/docker/types.ts +35 -3
  186. package/src/drivers/ai-ollama.integration.test.ts +2 -2
  187. package/src/drivers/conformance.test.ts +11 -12
  188. package/src/drivers/drizzle-dialect.test.ts +0 -4
  189. package/src/drivers/drizzle-dialect.ts +7 -13
  190. package/src/drivers/index.ts +24 -7
  191. package/src/drivers/journal-postgres.test.ts +3 -1
  192. package/src/drivers/journal-postgres.ts +4 -2
  193. package/src/drivers/meilisearch.ts +1 -1
  194. package/src/drivers/pglite.ts +28 -5
  195. package/src/drivers/types.ts +9 -11
  196. package/src/drivers/vault-aws-secrets-manager.test.ts +159 -0
  197. package/src/drivers/vault-aws-secrets-manager.ts +333 -0
  198. package/src/drivers/vault-builtin.test.ts +110 -0
  199. package/src/drivers/vault-builtin.ts +184 -0
  200. package/src/drivers/vault-driver-removal.test.ts +13 -0
  201. package/src/drivers/vault-managed.test.ts +95 -0
  202. package/src/drivers/vault-managed.ts +101 -25
  203. package/src/drivers/vault-types.ts +25 -5
  204. package/src/elements/ai/runtime.ts +1 -1
  205. package/src/elements/clock/chaos.test.ts +4 -1
  206. package/src/elements/clock/durable.ts +57 -4
  207. package/src/elements/clock.test.ts +13 -14
  208. package/src/elements/gate/constants.ts +18 -0
  209. package/src/elements/gate.test.ts +1 -1
  210. package/src/elements/gate.ts +2 -2
  211. package/src/elements/store/domain-ddl.test.ts +5 -12
  212. package/src/elements/store/emit-drizzle.ts +44 -19
  213. package/src/elements/store/index-boot.test.ts +15 -116
  214. package/src/elements/store/load-plugin-tables.ts +1 -1
  215. package/src/elements/store/resource-list-docs.fixture.ts +12 -6
  216. package/src/elements/store/resource-list-docs.test.ts +64 -44
  217. package/src/elements/store/resource.test.ts +17 -10
  218. package/src/elements/store/runtime.ts +9 -12
  219. package/src/elements/store/schema-decl.test.ts +16 -32
  220. package/src/elements/store/seed.test.ts +1 -2
  221. package/src/elements/store/seed.ts +7 -8
  222. package/src/elements/store/sql-condition.test.ts +2 -2
  223. package/src/elements/store/sql-session.test.ts +8 -10
  224. package/src/elements/store/sql-session.ts +10 -2
  225. package/src/elements/store.test.ts +13 -13
  226. package/src/elements/vault/audit.ts +233 -0
  227. package/src/elements/vault/boot-chain.test.ts +78 -0
  228. package/src/elements/vault/boot-chain.ts +43 -39
  229. package/src/elements/vault/builtin-adapter.test.ts +479 -0
  230. package/src/elements/vault/builtin-adapter.ts +1184 -0
  231. package/src/elements/vault/crypto.test.ts +237 -0
  232. package/src/elements/vault/crypto.ts +431 -0
  233. package/src/elements/vault/declare.ts +164 -3
  234. package/src/elements/vault/errors.ts +56 -0
  235. package/src/elements/vault/kms-unseal.test.ts +76 -0
  236. package/src/elements/vault/kms-unseal.ts +181 -0
  237. package/src/elements/vault/path.ts +90 -0
  238. package/src/elements/vault/resilience.test.ts +102 -0
  239. package/src/elements/vault/resilience.ts +107 -0
  240. package/src/elements/vault/runtime.ts +36 -4
  241. package/src/elements/vault/security-checklist.test.ts +130 -0
  242. package/src/elements/vault/storage.ts +448 -0
  243. package/src/elements/vault/types.ts +288 -0
  244. package/src/elements/vault/unseal.ts +150 -0
  245. package/src/elements/vault.test.ts +72 -4
  246. package/src/elements/vault.ts +91 -2
  247. package/src/full.ts +248 -0
  248. package/src/http.ts +41 -0
  249. package/src/i18n/interpolate-simple.ts +37 -0
  250. package/src/i18n/messages.ts +51 -4
  251. package/src/i18n-entry.ts +7 -0
  252. package/src/index.ts +35 -145
  253. package/src/journal-entry.ts +20 -0
  254. package/src/kernel/adopt-barrel-fresh.test.ts +25 -10
  255. package/src/kernel/app.ts +40 -42
  256. package/src/kernel/auto-registry.test.ts +2 -2
  257. package/src/kernel/boot-bind/ai.test.ts +5 -5
  258. package/src/kernel/boot-bind/ai.ts +2 -1
  259. package/src/kernel/boot-bind/channel.test.ts +6 -6
  260. package/src/kernel/boot-bind/channel.ts +5 -4
  261. package/src/kernel/boot-bind/clock.ts +5 -5
  262. package/src/kernel/boot-bind/gate.ts +6 -7
  263. package/src/kernel/boot-bind/honor-config.test.ts +42 -42
  264. package/src/kernel/boot-bind/journal.ts +5 -5
  265. package/src/kernel/boot-bind/signal.ts +3 -1
  266. package/src/kernel/boot-bind/store.test.ts +55 -37
  267. package/src/kernel/boot-bind/store.ts +24 -34
  268. package/src/kernel/boot-bind/vault.ts +19 -4
  269. package/src/kernel/boot.test.ts +12 -4
  270. package/src/kernel/boot.ts +31 -19
  271. package/src/kernel/budget-entry.ts +1 -1
  272. package/src/kernel/compensate.test.ts +288 -1
  273. package/src/kernel/compensate.ts +126 -0
  274. package/src/kernel/concurrency.test.ts +10 -9
  275. package/src/kernel/effects-stamping.test.ts +27 -2
  276. package/src/kernel/element-registries.ts +5 -3
  277. package/src/kernel/flow.ts +4 -2
  278. package/src/kernel/fx.test.ts +119 -3
  279. package/src/kernel/fx.ts +215 -40
  280. package/src/kernel/index.ts +14 -0
  281. package/src/kernel/journal.ts +130 -7
  282. package/src/kernel/pipeline.test.ts +20 -6
  283. package/src/kernel/redacted.test.ts +35 -0
  284. package/src/kernel/redacted.ts +24 -2
  285. package/src/kernel/router/create-default.ts +16 -0
  286. package/src/kernel/router/create-edge.ts +18 -0
  287. package/src/kernel/router/create.ts +23 -0
  288. package/src/kernel/router/index.ts +23 -0
  289. package/src/kernel/router/linear.ts +88 -0
  290. package/src/kernel/router/regexp.ts +188 -0
  291. package/src/kernel/router/smart.ts +120 -0
  292. package/src/kernel/router/trie.ts +108 -0
  293. package/src/kernel/router/types.ts +56 -0
  294. package/src/kernel/router.ts +18 -532
  295. package/src/kernel-entry.ts +43 -0
  296. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  297. package/src/manifest/fixtures/skyport.manifest.json +1 -1
  298. package/src/plugins/config-source.test.ts +7 -7
  299. package/src/release/build-lib.ts +58 -0
  300. package/src/release/exports.test.ts +4 -2
  301. package/src/release/exports.ts +30 -4
  302. package/src/release/measure.ts +101 -22
  303. package/src/runs/duckdb.ts +7 -2
  304. package/src/shared/optional-peer.ts +41 -0
  305. package/src/term.test.ts +3 -44
  306. package/src/test/create-test-app.ts +12 -0
  307. package/src/test/provisions.integration.test.ts +1 -1
  308. package/src/test/reset-element-registries.ts +2 -0
  309. package/src/upgrade/codemods.test.ts +7 -20
  310. package/src/upgrade/codemods.ts +93 -60
  311. package/site/content/docs/console/access.mdx +0 -29
  312. package/site/content/docs/console/ai.mdx +0 -35
  313. package/site/content/docs/console/architecture.mdx +0 -35
  314. package/site/content/docs/console/channels.mdx +0 -37
  315. package/site/content/docs/console/clock.mdx +0 -51
  316. package/site/content/docs/console/flows.mdx +0 -31
  317. package/site/content/docs/console/gates.mdx +0 -73
  318. package/site/content/docs/console/index.mdx +0 -54
  319. package/site/content/docs/console/manifest-diff.mdx +0 -34
  320. package/site/content/docs/console/meta.json +0 -24
  321. package/site/content/docs/console/overview.mdx +0 -40
  322. package/site/content/docs/console/plugins.mdx +0 -41
  323. package/site/content/docs/console/privacy.mdx +0 -32
  324. package/site/content/docs/console/runs.mdx +0 -50
  325. package/site/content/docs/console/signals.mdx +0 -40
  326. package/site/content/docs/console/store.mdx +0 -32
  327. package/site/content/docs/console/tenancy.mdx +0 -32
  328. package/site/content/docs/console/traces.mdx +0 -38
  329. package/site/content/docs/console/vault.mdx +0 -41
  330. package/site/content/docs/recipes/openbao.mdx +0 -141
  331. package/src/cli/ask-dev-mode.ts +0 -40
  332. package/src/cli/dev-mode.test.ts +0 -45
  333. package/src/cli/dev-mode.ts +0 -78
  334. package/src/cli/openbao-bootstrap.test.ts +0 -150
  335. package/src/cli/openbao-bootstrap.ts +0 -321
  336. package/src/cli/openbao-restart.integration.test.ts +0 -145
  337. package/src/console/ui/dist/assets/panel-vault-Za1GyJfM.js +0 -1
  338. package/src/console/ui/dist/assets/style-C8MxEWPd.css +0 -3
  339. package/src/docker/recipes/openbao.ts +0 -47
  340. package/src/drivers/libsql.ts +0 -179
  341. package/src/drivers/sqlite.ts +0 -44
  342. package/src/drivers/vault-openbao.test.ts +0 -97
  343. package/src/drivers/vault-openbao.ts +0 -143
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Headless `oke dev` controller — UI-agnostic lifecycle + events.
3
+ *
4
+ * Ink TUI and plain CLI both consume this; never kill a foreign pid.
5
+ */
6
+
7
+ import { resolve } from "node:path";
8
+ import { APP_PORT, CONSOLE_PORT, DOCS_MCP_PORT, MCP_PORT } from "../runtime/types.ts";
9
+ import type { DevStatus } from "../term.ts";
10
+ import { type DevOptions, type DevSession, runDev } from "./dev.ts";
11
+ import {
12
+ clearDevSessionLock,
13
+ type DevOwnership,
14
+ type DevSessionLock,
15
+ type DevSessionPorts,
16
+ probeDevPortsBusy,
17
+ resolveDevOwnership,
18
+ writeDevSessionLock,
19
+ } from "./dev-session-lock.ts";
20
+
21
+ /** Controller event kinds. */
22
+ export type DevControllerEvent =
23
+ | { readonly type: "boot"; readonly phase: string; readonly detail?: string }
24
+ | {
25
+ readonly type: "ready";
26
+ readonly ports: DevSessionPorts;
27
+ readonly ownership: DevOwnership;
28
+ }
29
+ | {
30
+ readonly type: "status";
31
+ readonly service: string;
32
+ readonly status: DevStatus;
33
+ readonly detail?: string;
34
+ }
35
+ | { readonly type: "log"; readonly text: string }
36
+ | { readonly type: "error"; readonly message: string; readonly fatal?: boolean }
37
+ | {
38
+ readonly type: "exit";
39
+ readonly code: number;
40
+ readonly signal?: string;
41
+ }
42
+ | { readonly type: "ownership"; readonly ownership: DevOwnership };
43
+
44
+ /** Listener for {@link DevController} events. */
45
+ export type DevControllerListener = (event: DevControllerEvent) => void;
46
+
47
+ /** Options for {@link DevController.start}. */
48
+ export type DevControllerStartOptions = Omit<DevOptions, "keepAlive" | "write" | "onReady"> & {
49
+ readonly cwd?: string;
50
+ /** Forward formatted chrome to stdout as well as events (default false). */
51
+ readonly echoLogs?: boolean;
52
+ };
53
+
54
+ /**
55
+ * Headless orchestration for a project’s dev stack.
56
+ */
57
+ export class DevController {
58
+ private readonly listeners = new Set<DevControllerListener>();
59
+ private session: DevSession | null = null;
60
+ private ownership: DevOwnership = "stopped";
61
+ private lock: DevSessionLock | null = null;
62
+ private starting = false;
63
+ private readonly preferredPorts: DevSessionPorts;
64
+
65
+ /**
66
+ * @param preferredPorts - Default ports when probing without a lock
67
+ */
68
+ constructor(
69
+ preferredPorts: DevSessionPorts = {
70
+ app: APP_PORT,
71
+ console: CONSOLE_PORT,
72
+ mcp: MCP_PORT,
73
+ docsMcp: DOCS_MCP_PORT,
74
+ },
75
+ ) {
76
+ this.preferredPorts = preferredPorts;
77
+ }
78
+
79
+ /** Current ownership classification. */
80
+ getOwnership(): DevOwnership {
81
+ return this.ownership;
82
+ }
83
+
84
+ /** Live session when managed and started. */
85
+ getSession(): DevSession | null {
86
+ return this.session;
87
+ }
88
+
89
+ /** Last lock we wrote (managed only). */
90
+ getLock(): DevSessionLock | null {
91
+ return this.lock;
92
+ }
93
+
94
+ /**
95
+ * Subscribe to controller events.
96
+ *
97
+ * @param listener - Callback
98
+ */
99
+ on(listener: DevControllerListener): () => void {
100
+ this.listeners.add(listener);
101
+ return () => {
102
+ this.listeners.delete(listener);
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Emit an event to all listeners.
108
+ *
109
+ * @param event - Payload
110
+ */
111
+ emit(event: DevControllerEvent): void {
112
+ for (const listener of this.listeners) {
113
+ try {
114
+ listener(event);
115
+ } catch {
116
+ // listeners must not break the controller
117
+ }
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Discover an existing stack without starting one.
123
+ *
124
+ * @param cwd - Project root
125
+ */
126
+ async attach(cwd: string = process.cwd()): Promise<{
127
+ readonly ownership: DevOwnership;
128
+ readonly lock: DevSessionLock | null;
129
+ readonly busy: Awaited<ReturnType<typeof probeDevPortsBusy>>;
130
+ }> {
131
+ const root = resolve(cwd);
132
+ const resolved = await resolveDevOwnership(root, this.preferredPorts);
133
+ this.ownership = resolved.ownership;
134
+ this.lock = resolved.lock;
135
+ this.emit({ type: "ownership", ownership: this.ownership });
136
+ if (this.ownership === "managed" || this.ownership === "external") {
137
+ const ports = resolved.lock?.ports ?? this.preferredPorts;
138
+ this.emit({
139
+ type: "ready",
140
+ ports,
141
+ ownership: this.ownership,
142
+ });
143
+ this.emit({
144
+ type: "log",
145
+ text:
146
+ this.ownership === "managed"
147
+ ? "Attached to managed oke dev session\n"
148
+ : "Connected to external oke dev (read-only)\n",
149
+ });
150
+ }
151
+ return resolved;
152
+ }
153
+
154
+ /**
155
+ * Start a managed session. Refuses if ports are already taken by another stack.
156
+ *
157
+ * @param options - Dev options (keepAlive forced false)
158
+ */
159
+ async start(options: DevControllerStartOptions = {}): Promise<number> {
160
+ if (this.starting) {
161
+ this.emit({ type: "error", message: "dev start already in progress" });
162
+ return 1;
163
+ }
164
+ if (this.session) {
165
+ this.emit({ type: "error", message: "dev session already running (managed)" });
166
+ return 1;
167
+ }
168
+
169
+ const cwd = resolve(options.cwd ?? process.cwd());
170
+ const attached = await this.attach(cwd);
171
+ if (attached.ownership === "external") {
172
+ this.emit({
173
+ type: "error",
174
+ message: "oke dev already running externally — attach only (will not start a second stack)",
175
+ fatal: false,
176
+ });
177
+ return 1;
178
+ }
179
+ if (attached.ownership === "managed" && attached.lock && attached.lock.pid !== process.pid) {
180
+ // Another managed process still alive — do not steal.
181
+ this.emit({
182
+ type: "error",
183
+ message: `oke dev already managed by pid ${attached.lock.pid}`,
184
+ fatal: false,
185
+ });
186
+ return 1;
187
+ }
188
+
189
+ this.starting = true;
190
+ this.emit({ type: "boot", phase: "starting" });
191
+
192
+ const echo = options.echoLogs === true;
193
+ const write = (text: string): void => {
194
+ this.emit({ type: "log", text });
195
+ if (echo) process.stdout.write(text);
196
+ };
197
+
198
+ try {
199
+ const result = await runDev({
200
+ ...options,
201
+ cwd,
202
+ keepAlive: false,
203
+ write,
204
+ onReady: async (session) => {
205
+ const ports: DevSessionPorts = {
206
+ app: session.appPort,
207
+ console: session.consolePort,
208
+ mcp: session.mcpPort,
209
+ docsMcp: session.docsMcpPort,
210
+ };
211
+ const lock: DevSessionLock = {
212
+ pid: process.pid,
213
+ ports,
214
+ startedAt: new Date().toISOString(),
215
+ cwd,
216
+ };
217
+ await writeDevSessionLock(cwd, lock);
218
+ this.lock = lock;
219
+ this.session = session;
220
+ this.ownership = "managed";
221
+ this.emit({ type: "ownership", ownership: "managed" });
222
+ this.emit({ type: "ready", ports, ownership: "managed" });
223
+ this.emit({ type: "boot", phase: "ready" });
224
+ },
225
+ });
226
+
227
+ if (result.code !== 0 || !result.session) {
228
+ this.ownership = "stopped";
229
+ this.session = null;
230
+ this.emit({ type: "error", message: "oke dev failed to start", fatal: true });
231
+ this.emit({ type: "exit", code: result.code });
232
+ return result.code;
233
+ }
234
+
235
+ // onReady may have already set session; ensure we hold it.
236
+ this.session = result.session;
237
+ return 0;
238
+ } catch (err) {
239
+ const message = err instanceof Error ? err.message : String(err);
240
+ this.emit({ type: "error", message, fatal: true });
241
+ this.emit({ type: "exit", code: 1 });
242
+ return 1;
243
+ } finally {
244
+ this.starting = false;
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Stop only a session this controller owns. Never kills an external pid.
250
+ */
251
+ async stop(): Promise<void> {
252
+ if (this.ownership === "external") {
253
+ this.emit({
254
+ type: "error",
255
+ message: "refusing to stop external oke dev session",
256
+ fatal: false,
257
+ });
258
+ return;
259
+ }
260
+ if (this.ownership !== "managed" || !this.session) {
261
+ this.ownership = "stopped";
262
+ this.emit({ type: "ownership", ownership: "stopped" });
263
+ return;
264
+ }
265
+
266
+ const cwd = this.lock?.cwd ?? process.cwd();
267
+ try {
268
+ this.session.stop();
269
+ } catch (err) {
270
+ const message = err instanceof Error ? err.message : String(err);
271
+ this.emit({ type: "error", message });
272
+ }
273
+ this.session = null;
274
+ this.lock = null;
275
+ this.ownership = "stopped";
276
+ await clearDevSessionLock(cwd);
277
+ this.emit({ type: "ownership", ownership: "stopped" });
278
+ this.emit({ type: "exit", code: 0 });
279
+ }
280
+
281
+ /**
282
+ * Stop then start (managed only).
283
+ *
284
+ * @param options - Start options
285
+ */
286
+ async restart(options: DevControllerStartOptions = {}): Promise<number> {
287
+ if (this.ownership === "external") {
288
+ this.emit({
289
+ type: "error",
290
+ message: "cannot restart an external oke dev session",
291
+ fatal: false,
292
+ });
293
+ return 1;
294
+ }
295
+ await this.stop();
296
+ return this.start(options);
297
+ }
298
+ }
@@ -1,19 +1,17 @@
1
1
  /**
2
- * Keyboard controls for a live `oke dev` session (TTY raw mode).
2
+ * Dev control key mapping + pure dispatcher (tests).
3
3
  *
4
- * Keys: `?` help · `q` quit · `r` refresh · `u` up all · `x` stop all.
5
- *
6
- * Compose health / model phase update the status board above Logs — it
7
- * already lists every service with a live ● status, so refresh alone is
8
- * enough to see the stack; there is no separate services panel.
4
+ * Interactive raw-mode stdin was removed Ink `useInput` owns TTY input
5
+ * via {@link ./tui/keys.ts}.
9
6
  */
10
7
 
11
8
  import { formatStatusLine, termColorEnabled, termStyle } from "../term.ts";
9
+ import { mapDevControlInput } from "./tui/keys.ts";
12
10
 
13
11
  /** Compose action against the whole stack. */
14
12
  export type DevComposeControlAction = "up" | "stop";
15
13
 
16
- /** Options for {@link startDevControls}. */
14
+ /** Options for {@link createDevControlDispatcher}. */
17
15
  export type StartDevControlsOptions = {
18
16
  readonly write: (text: string) => void;
19
17
  /** Called on `q` / Ctrl+C. */
@@ -23,14 +21,13 @@ export type StartDevControlsOptions = {
23
21
  */
24
22
  readonly onRefresh?: () => void | Promise<void>;
25
23
  /**
26
- * Show help in a clean pane (refreshes chrome first so the panel is not
27
- * interleaved with request logs).
24
+ * Show help in a clean pane.
28
25
  *
29
26
  * @param body - Formatted panel text
30
27
  */
31
28
  readonly onShowPanel?: (body: string) => void | Promise<void>;
32
29
  /**
33
- * After a compose up/stop — sync health into the board (no log lines).
30
+ * After a compose up/stop — sync health into the board.
34
31
  */
35
32
  readonly onComposeSettled?: () => void | Promise<void>;
36
33
  /**
@@ -39,16 +36,9 @@ export type StartDevControlsOptions = {
39
36
  * @param action - up / stop
40
37
  */
41
38
  readonly composeAction: (action: DevComposeControlAction) => Promise<void>;
42
- readonly stdin?: NodeJS.ReadStream;
43
- readonly isTTY?: boolean;
44
39
  readonly color?: boolean;
45
40
  };
46
41
 
47
- /** Handle returned by {@link startDevControls}. */
48
- export type DevControlsHandle = {
49
- readonly stop: () => void;
50
- };
51
-
52
42
  /**
53
43
  * One-line keys hint under Logs.
54
44
  *
@@ -97,24 +87,18 @@ export function formatDevControlsHelp(color: boolean = termColorEnabled()): stri
97
87
  */
98
88
  export function parseDevControlKey(chunk: string): string | null {
99
89
  if (chunk.length === 0) return null;
100
- if (chunk === "\u0003") return "q"; // Ctrl+C
101
- const ch = chunk[0]!;
102
- if (ch === "?" || ch === "h" || ch === "H") return "?";
103
- if (ch === "q" || ch === "Q") return "q";
104
- if (ch === "r" || ch === "R") return "r";
105
- if (ch === "u" || ch === "U") return "u";
106
- if (ch === "x" || ch === "X") return "x";
107
- return null;
90
+ if (chunk === "\u0003") return mapDevControlInput("c", { ctrl: true });
91
+ return mapDevControlInput(chunk[0] ?? "", {});
108
92
  }
109
93
 
110
- /** Key dispatcher for tests and {@link startDevControls}. */
94
+ /** Key dispatcher for tests. */
111
95
  export type DevControlDispatcher = {
112
96
  readonly handleKey: (key: string) => void;
113
97
  readonly stop: () => void;
114
98
  };
115
99
 
116
100
  /**
117
- * Pure key dispatcher (no stdin) — used by tests and the TTY listener.
101
+ * Pure key dispatcher (no stdin) — used by tests.
118
102
  *
119
103
  * @param opts - Quit / compose actions
120
104
  */
@@ -165,41 +149,3 @@ export function createDevControlDispatcher(opts: StartDevControlsOptions): DevCo
165
149
  },
166
150
  };
167
151
  }
168
-
169
- /**
170
- * Start raw-mode keyboard controls. No-op when stdin is not a TTY.
171
- *
172
- * @param opts - Quit / compose actions
173
- */
174
- export function startDevControls(opts: StartDevControlsOptions): DevControlsHandle {
175
- const stdin = opts.stdin ?? process.stdin;
176
- const isTTY = opts.isTTY ?? stdin.isTTY === true;
177
- const dispatcher = createDevControlDispatcher(opts);
178
-
179
- if (!isTTY || typeof stdin.setRawMode !== "function") {
180
- return { stop: () => dispatcher.stop() };
181
- }
182
-
183
- const onData = (buf: string | Buffer): void => {
184
- const chunk = typeof buf === "string" ? buf : buf.toString("utf8");
185
- const key = parseDevControlKey(chunk);
186
- if (key) dispatcher.handleKey(key);
187
- };
188
-
189
- stdin.setRawMode(true);
190
- stdin.resume();
191
- stdin.setEncoding("utf8");
192
- stdin.on("data", onData);
193
-
194
- return {
195
- stop() {
196
- dispatcher.stop();
197
- stdin.off("data", onData);
198
- try {
199
- if (typeof stdin.setRawMode === "function") stdin.setRawMode(false);
200
- } catch {
201
- // already closed
202
- }
203
- },
204
- };
205
- }
@@ -6,10 +6,39 @@ import { afterEach, describe, expect, test } from "bun:test";
6
6
  import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
7
7
  import { tmpdir } from "node:os";
8
8
  import { join } from "node:path";
9
- import { runDev, type DevSession, type DevWatchFn } from "./dev.ts";
9
+ import { runDev, type DevOptions, type DevSession, type DevWatchFn } from "./dev.ts";
10
10
 
11
11
  const sessions: DevSession[] = [];
12
12
 
13
+ const STUB_IMAGES = {
14
+ "store.sql": "postgres:16-alpine",
15
+ "store.kv": "redis:7-alpine",
16
+ } as const;
17
+ const STUB_CREDENTIALS = {
18
+ "store.sql": {
19
+ user: "oke",
20
+ password: "test-password-not-in-yaml",
21
+ database: "oke",
22
+ },
23
+ } as const;
24
+
25
+ function stubCompose(): Pick<
26
+ DevOptions,
27
+ "images" | "credentials" | "composeUp" | "composeHealth" | "composeStop"
28
+ > {
29
+ return {
30
+ images: STUB_IMAGES,
31
+ credentials: STUB_CREDENTIALS,
32
+ composeUp: async () => {},
33
+ composeHealth: async () =>
34
+ new Map([
35
+ ["store-sql", "ready"],
36
+ ["store-kv", "ready"],
37
+ ]),
38
+ composeStop: () => {},
39
+ };
40
+ }
41
+
13
42
  afterEach(() => {
14
43
  for (const s of sessions.splice(0)) s.stop();
15
44
  });
@@ -46,25 +75,26 @@ async function scaffoldProject(): Promise<string> {
46
75
  join(dir, "oke.config.ts"),
47
76
  `import { defineConfig } from ${JSON.stringify(configMod)};
48
77
  export default defineConfig({
49
- drivers: { store: { sql: { local: "sqlite", test: "memory", prod: "postgres" } } },
78
+ drivers: { store: { sql: { dev: "postgres", test: "pglite", prod: "postgres" } } },
50
79
  });
51
80
  `,
52
81
  );
53
82
  await writeFile(join(dir, "src", "app.ts"), `export {};\n`);
54
83
  await writeFile(
55
84
  join(dir, "src", "schema.ts"),
56
- `import { sqliteTable, text } from "drizzle-orm/sqlite-core";
57
- export const entries = sqliteTable("entries", { id: text("id").primaryKey() });
85
+ `import { pgTable, text } from "drizzle-orm/pg-core";
86
+ export const entries = pgTable("entries", { id: text("id").primaryKey() });
58
87
  `,
59
88
  );
60
89
  await writeFile(
61
90
  join(dir, "drizzle.config.ts"),
62
91
  `import { defineConfig } from "drizzle-kit";
63
92
  export default defineConfig({
64
- dialect: "sqlite",
93
+ dialect: "postgresql",
94
+ driver: "pglite",
65
95
  schema: "./src/schema.ts",
66
96
  out: "./drizzle",
67
- dbCredentials: { url: "file::memory:" },
97
+ dbCredentials: { url: "memory://" },
68
98
  });
69
99
  `,
70
100
  );
@@ -83,7 +113,7 @@ describe("oke dev db auto-push", () => {
83
113
 
84
114
  const result = await runDev({
85
115
  cwd,
86
- local: true,
116
+ ...stubCompose(),
87
117
  keepAlive: false,
88
118
  dryRun: false,
89
119
  appPort: 0,
@@ -127,8 +157,8 @@ describe("oke dev db auto-push", () => {
127
157
  const before = pushes.length;
128
158
  await writeFile(
129
159
  join(cwd, "src", "schema.ts"),
130
- `import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
131
- export const entries = sqliteTable("entries", {
160
+ `import { integer, pgTable, text } from "drizzle-orm/pg-core";
161
+ export const entries = pgTable("entries", {
132
162
  id: text("id").primaryKey(),
133
163
  n: integer("n"),
134
164
  });
@@ -139,7 +169,7 @@ export const entries = sqliteTable("entries", {
139
169
  expect(pushes.length).toBeGreaterThan(before);
140
170
  });
141
171
 
142
- test("schema.generated.ts change does not re-trigger dbPush", async () => {
172
+ test("schema.drizzle.ts change does not re-trigger dbPush", async () => {
143
173
  const cwd = await scaffoldProject();
144
174
  const pushes: string[] = [];
145
175
  const watcher = controlledWatcher();
@@ -150,7 +180,7 @@ export const entries = sqliteTable("entries", {
150
180
 
151
181
  const result = await runDev({
152
182
  cwd,
153
- local: true,
183
+ ...stubCompose(),
154
184
  keepAlive: false,
155
185
  dryRun: false,
156
186
  appPort: 0,
@@ -191,7 +221,9 @@ export const entries = sqliteTable("entries", {
191
221
  const before = pushes.length;
192
222
  expect(before).toBeGreaterThanOrEqual(1);
193
223
 
194
- // Simulate emit writing schema.generated.ts after push — must not loop.
224
+ // Simulate emit writing schema.drizzle.ts after push — must not loop.
225
+ watcher.change("schema.drizzle.ts");
226
+ watcher.change("src/db/schema.drizzle.ts");
195
227
  watcher.change("schema.generated.ts");
196
228
  watcher.change("src/schema.generated.ts");
197
229
  await Bun.sleep(500);
@@ -205,7 +237,7 @@ export const entries = sqliteTable("entries", {
205
237
 
206
238
  const result = await runDev({
207
239
  cwd,
208
- local: true,
240
+ ...stubCompose(),
209
241
  noDbPush: true,
210
242
  keepAlive: false,
211
243
  appPort: 0,
@@ -241,8 +273,8 @@ export const entries = sqliteTable("entries", {
241
273
  expect(result.code).toBe(0);
242
274
  await writeFile(
243
275
  join(cwd, "src", "schema.ts"),
244
- `import { sqliteTable, text } from "drizzle-orm/sqlite-core";
245
- export const entries = sqliteTable("entries", { id: text("id").primaryKey(), x: text("x") });
276
+ `import { pgTable, text } from "drizzle-orm/pg-core";
277
+ export const entries = pgTable("entries", { id: text("id").primaryKey(), x: text("x") });
246
278
  `,
247
279
  );
248
280
  watcher.change("schema.ts");
@@ -22,41 +22,41 @@ async function tempDir(): Promise<string> {
22
22
  }
23
23
 
24
24
  describe("sqlDialectForEnv", () => {
25
- test("localsqlite, docker → postgresql", async () => {
25
+ test("testpostgresql (pglite), dev → postgresql", async () => {
26
26
  const config = {
27
27
  drivers: {
28
- store: { sql: { local: "sqlite", docker: "postgres", prod: "postgres", test: "memory" } },
28
+ store: { sql: { dev: "postgres", test: "pglite", prod: "postgres" } },
29
29
  },
30
30
  } as never;
31
- expect(sqlDialectForEnv(config, "local").dialect).toBe("sqlite");
32
- expect(sqlDialectForEnv(config, "docker").dialect).toBe("postgresql");
31
+ expect(sqlDialectForEnv(config, "test").dialect).toBe("postgresql");
32
+ expect(sqlDialectForEnv(config, "dev").dialect).toBe("postgresql");
33
33
  });
34
34
 
35
- test("missing config → sqlite default", () => {
36
- expect(sqlDialectForEnv(null, "local").dialect).toBe("sqlite");
35
+ test("missing config → postgresql default", () => {
36
+ expect(sqlDialectForEnv(null, "test").dialect).toBe("postgresql");
37
37
  });
38
38
  });
39
39
 
40
40
  describe("syncDevSchema", () => {
41
- test("local: ensures drizzle config, emits, pushes with env", async () => {
41
+ test("test: ensures drizzle config, emits, pushes with env", async () => {
42
42
  const dir = await tempDir();
43
43
  const calls: { cwd: string; env: ConfigEnv }[] = [];
44
- const result = await syncDevSchema(dir, "local", {
44
+ const result = await syncDevSchema(dir, "test", {
45
45
  write: () => {},
46
46
  pushFn: async (cwd, env) => {
47
47
  calls.push({ cwd, env });
48
48
  return 0;
49
49
  },
50
50
  });
51
- expect(result.dialect).toBe("sqlite");
51
+ expect(result.dialect).toBe("postgresql");
52
52
  expect(result.pushed).toBe(true);
53
- expect(calls).toEqual([{ cwd: dir, env: "local" }]);
53
+ expect(calls).toEqual([{ cwd: dir, env: "test" }]);
54
54
  expect(await Bun.file(join(dir, "drizzle.config.ts")).exists()).toBe(true);
55
55
  });
56
56
 
57
57
  test("push failure surfaces code without throwing", async () => {
58
58
  const dir = await tempDir();
59
- const result = await syncDevSchema(dir, "local", {
59
+ const result = await syncDevSchema(dir, "test", {
60
60
  write: () => {},
61
61
  pushFn: async () => 1,
62
62
  });
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Shared local↔docker schema sync pipeline for `oke mode` and `oke dev`
2
+ * Shared schema sync pipeline for `oke dev` / `oke db`
3
3
  * one-shot start. Ensures artefacts, emits for the active dialect, then
4
4
  * pushes via drizzle-kit. Data planes stay isolated — no row copying.
5
5
  */
@@ -24,7 +24,7 @@ import { loadOkeConfig, resolveImages } from "./load-config.ts";
24
24
  export interface DevSchemaSyncOptions {
25
25
  /** Write output (default: stdout). */
26
26
  readonly write?: (text: string) => void;
27
- /** Skip emitting `schema.generated.ts` (tests). */
27
+ /** Skip emitting `schema.drizzle.ts` (tests). */
28
28
  readonly skipEmit?: boolean;
29
29
  /** Injectable push (tests). Default: `runDb("push", ...)`. */
30
30
  readonly pushFn?: (cwd: string, env: ConfigEnv) => Promise<number>;
@@ -40,7 +40,7 @@ export interface DevSchemaSyncOptions {
40
40
  /** Outcome of {@link syncDevSchema}. */
41
41
  export interface DevSchemaSyncResult {
42
42
  readonly env: ConfigEnv;
43
- readonly dialect: "sqlite" | "postgresql";
43
+ readonly dialect: "postgresql";
44
44
  readonly pushed: boolean;
45
45
  readonly code: number;
46
46
  }
@@ -54,11 +54,11 @@ export interface DevSchemaSyncResult {
54
54
  export function sqlDialectForEnv(
55
55
  config: OkeConfig | null | undefined,
56
56
  env: ConfigEnv,
57
- ): { readonly driverId: string; readonly dialect: "sqlite" | "postgresql" } {
58
- const driverId = resolveDriverId(config?.drivers?.store?.sql, env) ?? "sqlite";
59
- if (driverId !== "sqlite" && driverId !== "postgres") {
57
+ ): { readonly driverId: string; readonly dialect: "postgresql" } {
58
+ const driverId = resolveDriverId(config?.drivers?.store?.sql, env) ?? "postgres";
59
+ if (driverId !== "postgres" && driverId !== "pglite") {
60
60
  throw new Error(
61
- `store.sql driver "${driverId}" is not supported by drizzle-kit (sqlite | postgres)`,
61
+ `store.sql driver "${driverId}" is not supported by drizzle-kit (postgres | pglite)`,
62
62
  );
63
63
  }
64
64
  return { driverId, dialect: drizzleDialectFromSqlDriver(driverId) };
@@ -103,7 +103,7 @@ async function ensureDockerStack(
103
103
  await writeDerivedFiles(derived, resolve(cwd, DEFAULT_DOCKER_DIR), { writeStackEnv: true });
104
104
  if (!quietComposeReady) {
105
105
  write(
106
- "oke: docker compose env ready under docker/.env.docker (start containers with `oke dev -d` or docker compose up)\n",
106
+ "oke: docker compose env ready under docker/.env.docker (start containers with `oke dev` or docker compose up)\n",
107
107
  );
108
108
  }
109
109
  }
@@ -114,7 +114,7 @@ async function ensureDockerStack(
114
114
  * exists (writes `docker/.env.docker`) before pushing to the live DB.
115
115
  *
116
116
  * @param cwd - Project root
117
- * @param env - Target env (`local` | `docker`)
117
+ * @param env - Target {@link ConfigEnv} (`dev` for compose, `test` for laptop)
118
118
  * @param options - Injectables
119
119
  */
120
120
  export async function syncDevSchema(
@@ -130,7 +130,7 @@ export async function syncDevSchema(
130
130
 
131
131
  const { dialect } = sqlDialectForEnv(config, env);
132
132
 
133
- if (env === "docker") {
133
+ if (env === "dev") {
134
134
  if (!config) {
135
135
  throw new Error("docker mode: oke.config.ts not found");
136
136
  }