okengine 0.10.3 → 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 (318) hide show
  1. package/package.json +76 -10
  2. package/site/content/docs/deployment/docker-swarm.mdx +48 -111
  3. package/site/content/docs/deployment/docker.mdx +43 -36
  4. package/site/content/docs/deployment/index.mdx +9 -9
  5. package/site/content/docs/deployment/kubernetes.mdx +4 -4
  6. package/site/content/docs/elements/ai.mdx +7 -7
  7. package/site/content/docs/elements/channel.mdx +9 -7
  8. package/site/content/docs/elements/clock.mdx +8 -8
  9. package/site/content/docs/elements/flow.mdx +46 -47
  10. package/site/content/docs/elements/gate.mdx +6 -5
  11. package/site/content/docs/elements/signal.mdx +2 -2
  12. package/site/content/docs/elements/store.mdx +71 -78
  13. package/site/content/docs/elements/vault.mdx +89 -70
  14. package/site/content/docs/get-started/basic-usage.mdx +8 -8
  15. package/site/content/docs/get-started/installation.mdx +36 -36
  16. package/site/content/docs/get-started/introduction.mdx +11 -0
  17. package/site/content/docs/get-started/why.mdx +2 -2
  18. package/site/content/docs/plugins/anonymous.mdx +1 -1
  19. package/site/content/docs/plugins/magic-link.mdx +11 -11
  20. package/site/content/docs/plugins/otp.mdx +2 -2
  21. package/site/content/docs/plugins/passkey.mdx +1 -1
  22. package/site/content/docs/plugins/two-factor.mdx +1 -1
  23. package/site/content/docs/plugins/username.mdx +1 -1
  24. package/site/content/docs/recipes/caddy.mdx +1 -1
  25. package/site/content/docs/recipes/cockroachdb.mdx +2 -2
  26. package/site/content/docs/recipes/dragonfly.mdx +1 -1
  27. package/site/content/docs/recipes/index.mdx +1 -2
  28. package/site/content/docs/recipes/llama-cpp.mdx +21 -23
  29. package/site/content/docs/recipes/mailpit.mdx +11 -11
  30. package/site/content/docs/recipes/meilisearch.mdx +10 -10
  31. package/site/content/docs/recipes/meta.json +0 -1
  32. package/site/content/docs/recipes/ollama.mdx +1 -1
  33. package/site/content/docs/recipes/pgdog.mdx +12 -9
  34. package/site/content/docs/recipes/postgres.mdx +3 -3
  35. package/site/content/docs/recipes/redis.mdx +2 -2
  36. package/site/content/docs/recipes/rustfs.mdx +10 -7
  37. package/site/content/docs/recipes/sglang.mdx +1 -1
  38. package/site/content/docs/recipes/supabase-docker.mdx +1 -1
  39. package/site/content/docs/recipes/timescale.mdx +2 -2
  40. package/site/content/docs/recipes/valkey.mdx +1 -1
  41. package/site/content/docs/recipes/vllm.mdx +1 -1
  42. package/site/content/docs/recipes/yugabytedb.mdx +2 -2
  43. package/site/content/docs/reference/cli.md +61 -38
  44. package/site/content/docs/reference/configuration.mdx +145 -70
  45. package/site/content/docs/reference/environment-variables.mdx +36 -28
  46. package/site/content/docs/reference/errors.mdx +28 -29
  47. package/site/content/docs/reference/fx.mdx +11 -4
  48. package/site/content/docs/reference/i18n.mdx +17 -6
  49. package/site/content/docs/reference/index.mdx +5 -0
  50. package/site/content/docs/reference/meta.json +1 -0
  51. package/site/content/docs/reference/migrating-environments.mdx +158 -0
  52. package/site/content/docs/reference/plugins.mdx +2 -2
  53. package/src/auth/gate-auth.test.ts +12 -10
  54. package/src/cli/ai-setup/ai-setup.test.ts +27 -15
  55. package/src/cli/ai-setup/apply.ts +111 -15
  56. package/src/cli/ai-setup/catalog.ts +3 -3
  57. package/src/cli/ai-setup/index.ts +1 -1
  58. package/src/cli/ai-setup/prompts.ts +1 -1
  59. package/src/cli/ai-setup/recommend.test.ts +1 -1
  60. package/src/cli/ask-seed.test.ts +3 -3
  61. package/src/cli/build.ts +1 -1
  62. package/src/cli/db-auto-push.test.ts +6 -3
  63. package/src/cli/db-auto-push.ts +7 -5
  64. package/src/cli/db-seed.ts +7 -8
  65. package/src/cli/db.test.ts +70 -71
  66. package/src/cli/db.ts +86 -12
  67. package/src/cli/dev-controller.test.ts +35 -0
  68. package/src/cli/dev-controller.ts +298 -0
  69. package/src/cli/dev-controls.ts +11 -65
  70. package/src/cli/dev-db-push.test.ts +47 -15
  71. package/src/cli/dev-schema-sync.test.ts +11 -11
  72. package/src/cli/dev-schema-sync.ts +10 -10
  73. package/src/cli/dev-session-lock.test.ts +106 -0
  74. package/src/cli/dev-session-lock.ts +209 -0
  75. package/src/cli/dev.test.ts +166 -121
  76. package/src/cli/dev.ts +138 -192
  77. package/src/cli/doc-staleness.test.ts +3 -6
  78. package/src/cli/docker-clean.test.ts +33 -1
  79. package/src/cli/docker-clean.ts +28 -33
  80. package/src/cli/docker-cli.test.ts +12 -11
  81. package/src/cli/docker.ts +95 -20
  82. package/src/cli/doctor.test.ts +40 -0
  83. package/src/cli/doctor.ts +55 -15
  84. package/src/cli/drizzle-env.test.ts +11 -13
  85. package/src/cli/drizzle-env.ts +18 -11
  86. package/src/cli/ensure-drizzle-config.ts +11 -14
  87. package/src/cli/hero-meta.test.ts +15 -15
  88. package/src/cli/hero-meta.ts +10 -9
  89. package/src/cli/index.ts +36 -6
  90. package/src/cli/load-config.images.test.ts +16 -12
  91. package/src/cli/load-config.source.test.ts +50 -0
  92. package/src/cli/load-config.ts +34 -28
  93. package/src/cli/meilisearch-local.ts +1 -2
  94. package/src/cli/mode.ts +7 -58
  95. package/src/cli/registry.ts +175 -19
  96. package/src/cli/resolve-dev-sql-env.test.ts +8 -16
  97. package/src/cli/resolve-dev-sql-env.ts +13 -15
  98. package/src/cli/safe-defaults.test.ts +28 -10
  99. package/src/cli/schema.ts +65 -16
  100. package/src/cli/test.ts +58 -0
  101. package/src/cli/tui/App.tsx +252 -0
  102. package/src/cli/tui/DevLive.tsx +116 -0
  103. package/src/cli/tui/components/ConfirmDialog.tsx +48 -0
  104. package/src/cli/tui/components/HeaderBar.tsx +67 -0
  105. package/src/cli/tui/components/LogStream.tsx +47 -0
  106. package/src/cli/tui/components/PanelCard.tsx +39 -0
  107. package/src/cli/tui/components/SelectRow.tsx +37 -0
  108. package/src/cli/tui/components/SlashPalette.tsx +172 -0
  109. package/src/cli/tui/components/Spinner.tsx +32 -0
  110. package/src/cli/tui/components/StatusBar.tsx +51 -0
  111. package/src/cli/tui/components/StatusDot.tsx +37 -0
  112. package/src/cli/tui/components/TabBar.tsx +49 -0
  113. package/src/cli/tui/components/Wordmark.tsx +43 -0
  114. package/src/cli/tui/format.ts +38 -0
  115. package/src/cli/tui/keys.test.ts +18 -0
  116. package/src/cli/tui/keys.ts +26 -0
  117. package/src/cli/tui/launch.test.ts +18 -0
  118. package/src/cli/tui/launch.ts +57 -0
  119. package/src/cli/tui/panels/Dashboard.tsx +201 -0
  120. package/src/cli/tui/panels/Database.tsx +198 -0
  121. package/src/cli/tui/panels/Dev.tsx +257 -0
  122. package/src/cli/tui/panels/Docker.tsx +357 -0
  123. package/src/cli/tui/panels/Navigator.test.ts +22 -0
  124. package/src/cli/tui/panels/Navigator.tsx +213 -0
  125. package/src/cli/tui/slash-catalog.test.ts +31 -0
  126. package/src/cli/tui/slash-catalog.ts +184 -0
  127. package/src/cli/tui/slash-run.ts +54 -0
  128. package/src/cli/tui/state/ports.ts +95 -0
  129. package/src/cli/tui/theme.ts +24 -0
  130. package/src/cli/vault-cmd.test.ts +402 -0
  131. package/src/cli/vault-cmd.ts +727 -69
  132. package/src/cli/vault-secure-input.test.ts +56 -0
  133. package/src/cli/vault-secure-input.ts +124 -0
  134. package/src/compiler/effects-infer.ts +7 -0
  135. package/src/compiler/fixtures/skyport/oke.config.ts +3 -3
  136. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
  137. package/src/compiler/fixtures/skyport.expected.json +1 -1
  138. package/src/compiler-entry.ts +7 -0
  139. package/src/config/define-config.test.ts +111 -47
  140. package/src/config/driver-defaults.test.ts +16 -23
  141. package/src/config/driver-defaults.ts +33 -63
  142. package/src/config/index.ts +169 -144
  143. package/src/config/resolve-driver.test.ts +14 -19
  144. package/src/console/server/app.ts +4 -2
  145. package/src/console/server/console.test.ts +58 -1
  146. package/src/console/server/flows.ts +22 -2
  147. package/src/console/server/index.ts +3 -0
  148. package/src/console/server/operator-db.test.ts +55 -13
  149. package/src/console/server/operator-db.ts +305 -263
  150. package/src/console/server/serve.ts +16 -7
  151. package/src/console/server/state.ts +31 -9
  152. package/src/console/server/vault.test.ts +120 -5
  153. package/src/console/server/vault.ts +208 -12
  154. package/src/console/ui/dist/assets/{index-D-9KFce5.js → index--Y0Uaz-m.js} +2 -2
  155. package/src/console/ui/dist/assets/panel-vault-BkZdJPX8.js +1 -0
  156. package/src/console/ui/dist/assets/style-C3L349iZ.css +3 -0
  157. package/src/console/ui/dist/index.html +2 -2
  158. package/src/console/ui/shell/client.ts +16 -0
  159. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +54 -1
  160. package/src/console/ui/vault/VaultA11yView.tsx +33 -1
  161. package/src/console/ui/vault/backend.test.ts +75 -0
  162. package/src/console/ui/vault/backend.ts +117 -0
  163. package/src/console/ui/vault/fixture.ts +21 -2
  164. package/src/console/ui/vault/index.ts +11 -1
  165. package/src/console/ui/vault/types.ts +26 -0
  166. package/src/docker/compose.ts +164 -61
  167. package/src/docker/derive.ts +106 -12
  168. package/src/docker/docker.test.ts +156 -110
  169. package/src/docker/helpers.ts +98 -0
  170. package/src/docker/images-config.test.ts +3 -8
  171. package/src/docker/index.ts +30 -5
  172. package/src/docker/recipes/index.ts +0 -3
  173. package/src/docker/recipes/llama-cpp.ts +4 -4
  174. package/src/docker/recipes/pgdog.ts +9 -2
  175. package/src/docker/resources.test.ts +71 -0
  176. package/src/docker/resources.ts +176 -0
  177. package/src/docker/stack-id.test.ts +11 -4
  178. package/src/docker/stack-id.ts +42 -32
  179. package/src/docker/stack.integration.test.ts +4 -15
  180. package/src/docker/types.ts +35 -3
  181. package/src/drivers/ai-ollama.integration.test.ts +2 -2
  182. package/src/drivers/conformance.test.ts +11 -12
  183. package/src/drivers/drizzle-dialect.test.ts +0 -4
  184. package/src/drivers/drizzle-dialect.ts +7 -13
  185. package/src/drivers/index.ts +24 -7
  186. package/src/drivers/journal-postgres.test.ts +3 -1
  187. package/src/drivers/journal-postgres.ts +4 -2
  188. package/src/drivers/meilisearch.ts +1 -1
  189. package/src/drivers/pglite.ts +28 -5
  190. package/src/drivers/types.ts +9 -11
  191. package/src/drivers/vault-aws-secrets-manager.test.ts +159 -0
  192. package/src/drivers/vault-aws-secrets-manager.ts +333 -0
  193. package/src/drivers/vault-builtin.test.ts +110 -0
  194. package/src/drivers/vault-builtin.ts +184 -0
  195. package/src/drivers/vault-driver-removal.test.ts +13 -0
  196. package/src/drivers/vault-managed.test.ts +95 -0
  197. package/src/drivers/vault-managed.ts +101 -25
  198. package/src/drivers/vault-types.ts +25 -5
  199. package/src/elements/ai/runtime.ts +1 -1
  200. package/src/elements/clock/chaos.test.ts +4 -1
  201. package/src/elements/clock/durable.ts +57 -4
  202. package/src/elements/clock.test.ts +13 -14
  203. package/src/elements/gate/constants.ts +18 -0
  204. package/src/elements/gate.test.ts +1 -1
  205. package/src/elements/gate.ts +2 -2
  206. package/src/elements/store/domain-ddl.test.ts +5 -12
  207. package/src/elements/store/emit-drizzle.ts +44 -19
  208. package/src/elements/store/index-boot.test.ts +15 -116
  209. package/src/elements/store/load-plugin-tables.ts +1 -1
  210. package/src/elements/store/resource-list-docs.fixture.ts +12 -6
  211. package/src/elements/store/resource-list-docs.test.ts +64 -44
  212. package/src/elements/store/resource.test.ts +17 -10
  213. package/src/elements/store/runtime.ts +9 -12
  214. package/src/elements/store/schema-decl.test.ts +16 -32
  215. package/src/elements/store/seed.test.ts +1 -2
  216. package/src/elements/store/seed.ts +7 -8
  217. package/src/elements/store/sql-condition.test.ts +2 -2
  218. package/src/elements/store/sql-session.test.ts +8 -10
  219. package/src/elements/store/sql-session.ts +10 -2
  220. package/src/elements/store.test.ts +13 -13
  221. package/src/elements/vault/audit.ts +233 -0
  222. package/src/elements/vault/boot-chain.test.ts +78 -0
  223. package/src/elements/vault/boot-chain.ts +43 -39
  224. package/src/elements/vault/builtin-adapter.test.ts +479 -0
  225. package/src/elements/vault/builtin-adapter.ts +1184 -0
  226. package/src/elements/vault/crypto.test.ts +237 -0
  227. package/src/elements/vault/crypto.ts +431 -0
  228. package/src/elements/vault/declare.ts +164 -3
  229. package/src/elements/vault/errors.ts +56 -0
  230. package/src/elements/vault/kms-unseal.test.ts +76 -0
  231. package/src/elements/vault/kms-unseal.ts +181 -0
  232. package/src/elements/vault/path.ts +90 -0
  233. package/src/elements/vault/resilience.test.ts +102 -0
  234. package/src/elements/vault/resilience.ts +107 -0
  235. package/src/elements/vault/runtime.ts +36 -4
  236. package/src/elements/vault/security-checklist.test.ts +130 -0
  237. package/src/elements/vault/storage.ts +448 -0
  238. package/src/elements/vault/types.ts +288 -0
  239. package/src/elements/vault/unseal.ts +150 -0
  240. package/src/elements/vault.test.ts +72 -4
  241. package/src/elements/vault.ts +91 -2
  242. package/src/full.ts +248 -0
  243. package/src/http.ts +41 -0
  244. package/src/i18n/interpolate-simple.ts +37 -0
  245. package/src/i18n/messages.ts +51 -4
  246. package/src/i18n-entry.ts +7 -0
  247. package/src/index.ts +35 -145
  248. package/src/journal-entry.ts +20 -0
  249. package/src/kernel/adopt-barrel-fresh.test.ts +25 -10
  250. package/src/kernel/app.ts +40 -42
  251. package/src/kernel/auto-registry.test.ts +2 -2
  252. package/src/kernel/boot-bind/ai.test.ts +5 -5
  253. package/src/kernel/boot-bind/ai.ts +2 -1
  254. package/src/kernel/boot-bind/channel.test.ts +6 -6
  255. package/src/kernel/boot-bind/channel.ts +3 -10
  256. package/src/kernel/boot-bind/clock.ts +2 -2
  257. package/src/kernel/boot-bind/gate.ts +5 -9
  258. package/src/kernel/boot-bind/honor-config.test.ts +42 -42
  259. package/src/kernel/boot-bind/journal.ts +2 -2
  260. package/src/kernel/boot-bind/store.test.ts +43 -44
  261. package/src/kernel/boot-bind/store.ts +16 -41
  262. package/src/kernel/boot-bind/vault.ts +16 -1
  263. package/src/kernel/boot.test.ts +12 -4
  264. package/src/kernel/boot.ts +31 -19
  265. package/src/kernel/budget-entry.ts +1 -1
  266. package/src/kernel/compensate.test.ts +288 -1
  267. package/src/kernel/compensate.ts +126 -0
  268. package/src/kernel/concurrency.test.ts +10 -9
  269. package/src/kernel/effects-stamping.test.ts +27 -2
  270. package/src/kernel/element-registries.ts +5 -3
  271. package/src/kernel/flow.ts +4 -2
  272. package/src/kernel/fx.test.ts +119 -3
  273. package/src/kernel/fx.ts +215 -40
  274. package/src/kernel/index.ts +14 -0
  275. package/src/kernel/journal.ts +130 -7
  276. package/src/kernel/pipeline.test.ts +20 -6
  277. package/src/kernel/redacted.test.ts +35 -0
  278. package/src/kernel/redacted.ts +24 -2
  279. package/src/kernel/router/create-default.ts +16 -0
  280. package/src/kernel/router/create-edge.ts +18 -0
  281. package/src/kernel/router/create.ts +23 -0
  282. package/src/kernel/router/index.ts +23 -0
  283. package/src/kernel/router/linear.ts +88 -0
  284. package/src/kernel/router/regexp.ts +188 -0
  285. package/src/kernel/router/smart.ts +120 -0
  286. package/src/kernel/router/trie.ts +108 -0
  287. package/src/kernel/router/types.ts +56 -0
  288. package/src/kernel/router.ts +18 -532
  289. package/src/kernel-entry.ts +43 -0
  290. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  291. package/src/manifest/fixtures/skyport.manifest.json +1 -1
  292. package/src/plugins/config-source.test.ts +7 -7
  293. package/src/release/build-lib.ts +58 -0
  294. package/src/release/exports.test.ts +4 -2
  295. package/src/release/exports.ts +30 -4
  296. package/src/release/measure.ts +101 -22
  297. package/src/runs/duckdb.ts +7 -2
  298. package/src/shared/optional-peer.ts +41 -0
  299. package/src/term.test.ts +3 -44
  300. package/src/test/create-test-app.ts +12 -0
  301. package/src/test/provisions.integration.test.ts +1 -1
  302. package/src/test/reset-element-registries.ts +2 -0
  303. package/src/upgrade/codemods.test.ts +7 -20
  304. package/src/upgrade/codemods.ts +93 -60
  305. package/site/content/docs/recipes/openbao.mdx +0 -141
  306. package/src/cli/ask-dev-mode.ts +0 -40
  307. package/src/cli/dev-mode.test.ts +0 -45
  308. package/src/cli/dev-mode.ts +0 -78
  309. package/src/cli/openbao-bootstrap.test.ts +0 -150
  310. package/src/cli/openbao-bootstrap.ts +0 -321
  311. package/src/cli/openbao-restart.integration.test.ts +0 -145
  312. package/src/console/ui/dist/assets/panel-vault-Za1GyJfM.js +0 -1
  313. package/src/console/ui/dist/assets/style-C8MxEWPd.css +0 -3
  314. package/src/docker/recipes/openbao.ts +0 -47
  315. package/src/drivers/libsql.ts +0 -179
  316. package/src/drivers/sqlite.ts +0 -44
  317. package/src/drivers/vault-openbao.test.ts +0 -97
  318. package/src/drivers/vault-openbao.ts +0 -143
@@ -12,7 +12,6 @@ import {
12
12
  resolveSelection,
13
13
  selectionKey,
14
14
  type DockerRunner,
15
- type OkeContainer,
16
15
  type OkeStack,
17
16
  } from "../docker/cleanup.ts";
18
17
  import { EXIT_OK, EXIT_RUNTIME, EXIT_USAGE } from "./exit.ts";
@@ -49,8 +48,8 @@ export function dockerCleanHelp(): string {
49
48
 
50
49
  Remove leftover OKE docker compose stacks (containers, networks, volumes).
51
50
 
52
- Inside a project: focuses the current stack (oke-dev-<id>); optionally
53
- include other stacks on this machine.
51
+ Selection is by compose project name (oke-dev-<id>) — never per-service.
52
+ Inside a project: focuses the current stack; optionally include others.
54
53
  Outside a project: list every oke-dev-* stack and pick what to remove.
55
54
 
56
55
  --yes, -y Non-interactive; required when stdin is not a TTY
@@ -235,10 +234,10 @@ async function runInteractive(options: {
235
234
  if (current.length === 0) {
236
235
  write(`No stack for this project (${currentProject}).\n`);
237
236
  } else {
238
- const keys = await pickContainerKeys({
237
+ const keys = await pickProjectKeys({
239
238
  stacks: current,
240
239
  scope: "current",
241
- message: `Containers for this project (${currentProject})`,
240
+ message: `Stack for this project (${currentProject})`,
242
241
  initialAll: true,
243
242
  pickKeys,
244
243
  });
@@ -256,7 +255,7 @@ async function runInteractive(options: {
256
255
  return EXIT_OK;
257
256
  }
258
257
  if (include) {
259
- const keys = await pickContainerKeys({
258
+ const keys = await pickProjectKeys({
260
259
  stacks: others,
261
260
  scope: "other",
262
261
  message: "Other OKE stacks on this machine",
@@ -271,7 +270,7 @@ async function runInteractive(options: {
271
270
  }
272
271
  }
273
272
  } else {
274
- const keys = await pickContainerKeys({
273
+ const keys = await pickProjectKeys({
275
274
  stacks,
276
275
  scope: "all",
277
276
  message: "OKE docker stacks on this machine",
@@ -330,7 +329,10 @@ async function askIncludeOthersPrompt(): Promise<boolean | null> {
330
329
  return value;
331
330
  }
332
331
 
333
- async function pickContainerKeys(options: {
332
+ /**
333
+ * Multiselect compose projects (one row per `oke-dev-*` name).
334
+ */
335
+ async function pickProjectKeys(options: {
334
336
  readonly stacks: readonly OkeStack[];
335
337
  readonly scope: "current" | "other" | "all";
336
338
  readonly message: string;
@@ -344,7 +346,7 @@ async function pickContainerKeys(options: {
344
346
  if (entries.length === 0) return [];
345
347
 
346
348
  const value = await multiselect({
347
- message: `${message} — selecting any container removes its whole stack`,
349
+ message: `${message} — removes the whole stack (containers, networks, volumes)`,
348
350
  options: entries.map((e) => ({
349
351
  value: e.key,
350
352
  label: e.label,
@@ -357,34 +359,27 @@ async function pickContainerKeys(options: {
357
359
  return value;
358
360
  }
359
361
 
360
- function selectionEntries(stacks: readonly OkeStack[]): readonly {
362
+ /**
363
+ * One selectable row per compose project.
364
+ *
365
+ * @param stacks - Discovered stacks
366
+ */
367
+ export function selectionEntries(stacks: readonly OkeStack[]): readonly {
361
368
  readonly key: string;
362
369
  readonly label: string;
363
370
  readonly hint?: string;
364
371
  }[] {
365
- const out: { key: string; label: string; hint?: string }[] = [];
366
- for (const stack of stacks) {
367
- if (stack.containers.length === 0) {
368
- out.push({
369
- key: selectionKey(stack.project),
370
- label: stack.project,
371
- hint: "no containers · volumes/networks may remain",
372
- });
373
- continue;
374
- }
375
- for (const c of stack.containers) {
376
- out.push({
377
- key: selectionKey(stack.project, c.name),
378
- label: `${stack.project} · ${displayContainer(c)}`,
379
- hint: c.state || undefined,
380
- });
381
- }
382
- }
383
- return out;
384
- }
385
-
386
- function displayContainer(c: OkeContainer): string {
387
- return c.service && c.service !== c.name ? `${c.service} (${c.name})` : c.name;
372
+ return stacks.map((stack) => {
373
+ const n = stack.containers.length;
374
+ return {
375
+ key: selectionKey(stack.project),
376
+ label: stack.project,
377
+ hint:
378
+ n === 0
379
+ ? "no containers · volumes/networks may remain"
380
+ : `${n} container${n === 1 ? "" : "s"}`,
381
+ };
382
+ });
388
383
  }
389
384
 
390
385
  async function tearDownProjects(
@@ -39,11 +39,12 @@ describe("oke docker CLI", () => {
39
39
  expect(code).toBe(0);
40
40
  expect(result).toBeDefined();
41
41
  expect(await Bun.file(join(dir, "docker/Dockerfile")).exists()).toBe(true);
42
- expect(await Bun.file(join(dir, "docker/compose.store.sql.yml")).exists()).toBe(true);
43
- const yml = await Bun.file(join(dir, "docker/compose.store.sql.yml")).text();
42
+ expect(await Bun.file(join(dir, "docker/docker-compose.yml")).exists()).toBe(true);
43
+ const yml = await Bun.file(join(dir, "docker/docker-compose.yml")).text();
44
44
  expect(yml).not.toContain("test-password-not-in-yaml");
45
45
  expect(yml).toContain(".env.docker");
46
- expect(logs.join("")).toContain("docker/compose.override.yml");
46
+ expect(yml).toContain("/_/ready");
47
+ expect(logs.join("")).toContain("docker-compose.override.yml");
47
48
  });
48
49
  });
49
50
 
@@ -136,7 +137,7 @@ describe("oke images list", () => {
136
137
  });
137
138
 
138
139
  describe("oke schema generate", () => {
139
- test("emits schema/oke.ts and --check catches drift", async () => {
140
+ test("emits .oke/schema/oke.ts and --check catches drift", async () => {
140
141
  const dir = await mkdtemp(join(tmpdir(), "oke-cli-schema-"));
141
142
  const code = await runSchemaGenerate({
142
143
  cwd: dir,
@@ -144,7 +145,7 @@ describe("oke schema generate", () => {
144
145
  write: () => {},
145
146
  });
146
147
  expect(code).toBe(0);
147
- const src = await Bun.file(join(dir, "schema/oke.ts")).text();
148
+ const src = await Bun.file(join(dir, ".oke/schema/oke.ts")).text();
148
149
  expect(src).toContain("bookings");
149
150
  expect(src).toContain("oke_roles");
150
151
 
@@ -156,7 +157,7 @@ describe("oke schema generate", () => {
156
157
  });
157
158
  expect(checkOk).toBe(0);
158
159
 
159
- await Bun.write(join(dir, "schema/oke.ts"), emitSchemaSource(["other"]));
160
+ await Bun.write(join(dir, ".oke/schema/oke.ts"), emitSchemaSource(["other"]));
160
161
  const checkFail = await runSchemaGenerate({
161
162
  cwd: dir,
162
163
  extraTables: ["bookings"],
@@ -167,7 +168,7 @@ describe("oke schema generate", () => {
167
168
  });
168
169
  });
169
170
 
170
- describe("oke dev --docker", () => {
171
+ describe("oke dev compose plan", () => {
171
172
  test("plans docker with postgres and writes nothing on dryRun", async () => {
172
173
  const dir = await mkdtemp(join(tmpdir(), "oke-cli-dev-dry-"));
173
174
  await Bun.write(join(dir, "src/app.ts"), "export {}\n");
@@ -182,11 +183,11 @@ describe("oke dev --docker", () => {
182
183
  });
183
184
  expect(code).toBe(0);
184
185
  expect(plan?.stackRoles).toEqual(["store.sql"]);
185
- expect(plan?.composeFiles?.some((f) => f.includes("store.sql"))).toBe(true);
186
- expect(plan?.composeFiles).toContain("compose.yml");
186
+ expect(plan?.composeFiles).toContain("docker-compose.yml");
187
+ expect(plan?.composeFiles?.some((f) => f.includes("store.sql"))).toBe(false);
187
188
  expect(plan?.stackEnv?.DATABASE_URL).toContain("postgres://");
188
- expect(await Bun.file(join(dir, "compose.yml")).exists()).toBe(false);
189
- expect(await Bun.file(join(dir, "docker/compose.yml")).exists()).toBe(false);
189
+ expect(await Bun.file(join(dir, "docker-compose.yml")).exists()).toBe(false);
190
+ expect(await Bun.file(join(dir, "docker/docker-compose.yml")).exists()).toBe(false);
190
191
  });
191
192
  });
192
193
 
package/src/cli/docker.ts CHANGED
@@ -1,12 +1,17 @@
1
1
  /**
2
- * `oke docker` — Dockerfile + per-role compose files · `clean` leftover stacks.
2
+ * `oke docker` — Dockerfile + compose artefacts · `clean` leftover stacks.
3
+ *
4
+ * Default layout is a single production-grade `docker-compose.yml`.
5
+ * Opt into `--split` (per-role files) or `--stack` (`docker-stack.yml`).
3
6
  */
4
7
 
5
8
  import { resolve } from "node:path";
6
9
  import {
7
10
  DEFAULT_DOCKER_DIR,
11
+ DEFAULT_SERVER_BUDGET,
8
12
  deriveInfrastructure,
9
13
  writeDerivedFiles,
14
+ type ComposeLayout,
10
15
  type DeriveOptions,
11
16
  type DeriveResult,
12
17
  } from "../docker/index.ts";
@@ -18,7 +23,14 @@ import { loadOkeConfig, loadManifest, resolveImages } from "./load-config.ts";
18
23
  export interface DockerCliOptions {
19
24
  readonly cwd?: string;
20
25
  readonly outDir?: string;
26
+ /** Production overlays + resource budget (default true for `oke docker`). */
21
27
  readonly prod?: boolean;
28
+ /** Compose layout (default `single`). */
29
+ readonly layout?: ComposeLayout;
30
+ /** Host CPUs for resource apportioning (default 4). */
31
+ readonly serverCpus?: number;
32
+ /** Host RAM GiB for resource apportioning (default 8). */
33
+ readonly serverMemoryGb?: number;
22
34
  readonly configPath?: string;
23
35
  readonly manifestPath?: string;
24
36
  readonly write?: (text: string) => void;
@@ -75,11 +87,19 @@ export async function runDockerDerive(
75
87
  return { code: 1 };
76
88
  }
77
89
 
90
+ const layout = options.layout ?? "single";
91
+ const prod = options.prod ?? true;
92
+ const serverCpus = options.serverCpus ?? DEFAULT_SERVER_BUDGET.cpus;
93
+ const serverMemoryGb = options.serverMemoryGb ?? DEFAULT_SERVER_BUDGET.memoryGb;
94
+
78
95
  try {
79
96
  const result = deriveInfrastructure({
80
97
  images,
81
98
  app,
82
- prod: options.prod ?? false,
99
+ prod,
100
+ layout,
101
+ serverCpus,
102
+ serverMemoryGb,
83
103
  outDir,
84
104
  composeDir,
85
105
  includeApp: true,
@@ -90,25 +110,22 @@ export async function runDockerDerive(
90
110
  await writeDerivedFiles(result, outDir, { writeStackEnv: false });
91
111
  }
92
112
 
93
- const vaultSpec = result.specs.find((s) => s.role === "vault");
94
- if (vaultSpec) {
95
- const rel = composeDir === "." ? ".oke/openbao/" : `${composeDir}/../.oke/openbao/`;
96
- write(
97
- `oke docker: OpenBao first boot — bring the stack up (e.g. \`oke dev -d\`), and oke ` +
98
- `will initialize + unseal it once. Root/unseal material stays on the host at ${rel} ` +
99
- `(0600, gitignored) — back it up; losing it loses every secret permanently.\n`,
100
- );
101
- }
102
-
103
113
  write(`oke docker: wrote ${result.files.length} file(s) → ${composeDir}/\n`);
104
114
  for (const f of result.files) write(` ${composeDir}/${f.path}\n`);
105
115
  write(
106
- `compose merge order (cwd ${composeDir}/):\n${result.composeFiles.map((f) => ` -f ${f}`).join("\n")}\n`,
116
+ `layout: ${layout} · server budget: ${serverCpus} CPU / ${serverMemoryGb} GiB` +
117
+ (prod ? " · production-grade\n" : "\n"),
107
118
  );
108
119
  write(
109
- `(${composeDir}/compose.override.yml is layer 4 — user-owned, never written by oke)\n` +
110
- `(${composeDir}/compose.all.yml is layers 1–3 merged — single-file alternative for Swarm / one \`-c\`)\n`,
120
+ `compose merge order (cwd ${composeDir}/):\n${result.composeFiles.map((f) => ` -f ${f}`).join("\n")}\n`,
111
121
  );
122
+ if (layout === "single") {
123
+ write(`(${composeDir}/docker-compose.override.yml is user-owned — never written by oke)\n`);
124
+ } else if (layout === "split") {
125
+ write(`(${composeDir}/compose.override.yml is user-owned — never written by oke)\n`);
126
+ } else {
127
+ write(`(Swarm: docker stack deploy -c ${composeDir}/docker-stack.yml <name>)\n`);
128
+ }
112
129
  return { code: 0, result };
113
130
  } catch (err) {
114
131
  console.error(err instanceof Error ? err.message : String(err));
@@ -127,21 +144,61 @@ export async function dockerCli(args: readonly string[]): Promise<number> {
127
144
  return dockerCleanCli(rest);
128
145
  }
129
146
 
130
- let prod = false;
147
+ let prod: boolean | undefined;
148
+ let layout: ComposeLayout | undefined;
149
+ let serverCpus: number | undefined;
150
+ let serverMemoryGb: number | undefined;
131
151
  let outDir: string | undefined;
132
152
  let configPath: string | undefined;
133
153
  let manifestPath: string | undefined;
134
154
  for (let i = 0; i < args.length; i++) {
135
155
  const a = args[i]!;
136
156
  if (a === "--prod" || a === "-p") prod = true;
137
- else if (a === "--out" || a === "-o") outDir = args[++i];
157
+ else if (a === "--no-prod") prod = false;
158
+ else if (a === "--split") layout = "split";
159
+ else if (a === "--stack") layout = "stack";
160
+ else if (a === "--single") layout = "single";
161
+ else if (a === "--cpus" || a === "--cpu") {
162
+ const raw = args[++i];
163
+ const n = Number(raw);
164
+ if (!raw || !(n > 0)) {
165
+ console.error("oke docker: --cpus requires a positive number");
166
+ return 1;
167
+ }
168
+ serverCpus = n;
169
+ } else if (a === "--memory" || a === "--mem" || a === "--memory-gb") {
170
+ const raw = args[++i];
171
+ const n = Number(raw);
172
+ if (!raw || !(n > 0)) {
173
+ console.error("oke docker: --memory requires a positive GiB number");
174
+ return 1;
175
+ }
176
+ serverMemoryGb = n;
177
+ } else if (a === "--out" || a === "-o") outDir = args[++i];
138
178
  else if (a === "--config" || a === "-c") configPath = args[++i];
139
179
  else if (a === "--manifest" || a === "-m") manifestPath = args[++i];
140
180
  else if (a === "--help" || a === "-h") {
141
- console.log(`oke docker [--prod|-p] [--out|-o docker] [--config|-c oke.config.ts]
181
+ console.log(`oke docker [options]
142
182
 
143
- Derive Dockerfile + compose.<role>.yml under docker/ (default).
144
- --prod is opt-in (never the default). Credentials are never written into YAML.
183
+ Derive production-grade Docker artefacts under docker/ (default).
184
+
185
+ Layouts (mutually exclusive; default --single):
186
+ --single docker-compose.yml + Dockerfile (+ .env.docker via oke dev)
187
+ --split compose.yml + compose.<role>.yml (+ compose.prod.yml)
188
+ --stack docker-stack.yml for docker stack deploy
189
+
190
+ Server budget (sizes deploy.resources; default 4 CPU / 8 GiB):
191
+ --cpus <n> host CPUs
192
+ --memory <gib> host RAM in GiB
193
+
194
+ Other:
195
+ --prod / -p production overlays (default on)
196
+ --no-prod skip readiness / deploy / resource budget
197
+ --out / -o output directory (default docker)
198
+ --config / -c oke.config.ts path
199
+ --manifest / -m Manifest path
200
+
201
+ Credentials are never written into YAML. PgDog configs land in docker/pgdog/.
145
202
 
146
203
  ${dockerCleanHelp()}`);
147
204
  return EXIT_OK;
@@ -149,10 +206,28 @@ ${dockerCleanHelp()}`);
149
206
  console.error(`oke docker: unknown subcommand ${a}`);
150
207
  console.error("Run `oke docker --help` for usage.");
151
208
  return 1;
209
+ } else {
210
+ console.error(`oke docker: unknown flag ${a}`);
211
+ console.error("Run `oke docker --help` for usage.");
212
+ return 1;
213
+ }
214
+ }
215
+ {
216
+ const layoutFlags = [
217
+ args.includes("--split"),
218
+ args.includes("--stack"),
219
+ args.includes("--single"),
220
+ ].filter(Boolean).length;
221
+ if (layoutFlags > 1) {
222
+ console.error("oke docker: use only one of --single, --split, --stack");
223
+ return 1;
152
224
  }
153
225
  }
154
226
  const { code } = await runDockerDerive({
155
227
  prod,
228
+ layout,
229
+ serverCpus,
230
+ serverMemoryGb,
156
231
  outDir,
157
232
  configPath,
158
233
  manifestPath,
@@ -84,6 +84,46 @@ describe("oke doctor", () => {
84
84
  expect(findings).toHaveLength(0);
85
85
  });
86
86
 
87
+ test("fails closed in prod when builtin vault master key is env-sourced", async () => {
88
+ const { code, findings } = await runDoctor({
89
+ secrets: [],
90
+ ports: [],
91
+ isPortInUse: async () => false,
92
+ skipDbDrift: true,
93
+ configEnv: "prod",
94
+ driversConfig: {
95
+ vault: { dev: "vault", test: "memory", prod: "vault" },
96
+ },
97
+ vaultConfig: {
98
+ encryption: { masterKey: { kind: "env", name: "OKE_VAULT_MASTER_KEY" } },
99
+ },
100
+ write: () => {},
101
+ });
102
+ expect(code).toBe(2);
103
+ const finding = findings.find((f) => f.code === "vault_master_key");
104
+ expect(finding?.severity).toBe("error");
105
+ expect(finding?.message).toContain("environment variable");
106
+ });
107
+
108
+ test("NODE_ENV=production selects prod env for the vault master-key gate", async () => {
109
+ const { code, findings } = await runDoctor({
110
+ secrets: [],
111
+ ports: [],
112
+ isPortInUse: async () => false,
113
+ skipDbDrift: true,
114
+ env: (k) => (k === "NODE_ENV" ? "production" : undefined),
115
+ driversConfig: {
116
+ vault: { dev: "vault", test: "memory", prod: "vault" },
117
+ },
118
+ vaultConfig: {
119
+ encryption: { masterKey: { kind: "env", name: "OKE_VAULT_MASTER_KEY" } },
120
+ },
121
+ write: () => {},
122
+ });
123
+ expect(code).toBe(2);
124
+ expect(findings.some((f) => f.code === "vault_master_key")).toBe(true);
125
+ });
126
+
87
127
  test("fails with flow name when PII reaches unguarded ask", async () => {
88
128
  const manifest: Manifest = {
89
129
  oke: "1.0",
package/src/cli/doctor.ts CHANGED
@@ -3,7 +3,13 @@
3
3
  */
4
4
 
5
5
  import { resolve } from "node:path";
6
- import type { ConfigEnv, DriverRef, DriversConfig, EnvDriverMap } from "../config/index.ts";
6
+ import type {
7
+ ConfigEnv,
8
+ DriverRef,
9
+ DriversConfig,
10
+ EnvDriverMap,
11
+ VaultElementConfig,
12
+ } from "../config/index.ts";
7
13
  import { resolveEffectiveDrivers, type EffectiveDriversConfig } from "../config/driver-defaults.ts";
8
14
  import type { Manifest } from "../manifest/types.ts";
9
15
  import { APP_PORT, CONSOLE_PORT, MCP_PORT } from "../runtime/types.ts";
@@ -61,7 +67,7 @@ function formatDriversSummary(drivers: EffectiveDriversConfig, configEnv: Config
61
67
  const lines = [`drivers (env=${configEnv}):`];
62
68
  for (const [name, map] of rows) {
63
69
  const active = driverRefId(map[configEnv]);
64
- const full = `local=${driverRefId(map.local)} docker=${driverRefId(map.docker)} test=${driverRefId(map.test)} prod=${driverRefId(map.prod)}`;
70
+ const full = `dev=${driverRefId(map.dev)} test=${driverRefId(map.test)} prod=${driverRefId(map.prod)}`;
65
71
  lines.push(` ${name.padEnd(nameWidth)} ${active.padEnd(activeWidth)} { ${full} }`);
66
72
  }
67
73
  return lines.join("\n");
@@ -76,7 +82,8 @@ export interface DoctorFinding {
76
82
  | "db_drift"
77
83
  | "tenancy"
78
84
  | "driver"
79
- | "pii_ask";
85
+ | "pii_ask"
86
+ | "vault_master_key";
80
87
  readonly severity: "error" | "warn";
81
88
  readonly message: string;
82
89
  }
@@ -116,9 +123,13 @@ export interface DoctorOptions {
116
123
  */
117
124
  readonly driversConfig?: DriversConfig;
118
125
  /**
119
- * Active {@link ConfigEnv} for the drivers summary. Default: `docker` when
120
- * `OKE_DOCKER=1` (via {@link env}), else `local` the same resolution
121
- * `oke boot` uses without an explicit `env`.
126
+ * Inject the `vault` block directly (tests). When omitted, it is read
127
+ * from the same best-effort `oke.config.ts` load as {@link driversConfig}.
128
+ */
129
+ readonly vaultConfig?: VaultElementConfig;
130
+ /**
131
+ * Active {@link ConfigEnv} for the drivers summary. Default: `dev` when
132
+ * `OKE_DOCKER=1` (via {@link env}), else `test`.
122
133
  */
123
134
  readonly configEnv?: ConfigEnv;
124
135
  readonly write?: (text: string) => void;
@@ -161,15 +172,20 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
161
172
  // Fully-resolved `drivers.*` — real defaults + every override merged, so
162
173
  // the picture is complete even when `oke.config.ts` only pins one key.
163
174
  let driversConfig = options.driversConfig;
164
- if (driversConfig === undefined) {
175
+ let vaultConfig = options.vaultConfig;
176
+ if (driversConfig === undefined || vaultConfig === undefined) {
165
177
  try {
166
- driversConfig = (await loadOkeConfig(cwd)).config.drivers;
178
+ const loaded = (await loadOkeConfig(cwd)).config;
179
+ driversConfig ??= loaded.drivers;
180
+ vaultConfig ??= loaded.vault;
167
181
  } catch {
168
182
  // No oke.config.ts (or it failed to load) — show real defaults only.
169
183
  }
170
184
  }
185
+ // Match `bootApplication`: production NODE_ENV → prod; compose → dev; else test.
171
186
  const configEnv: ConfigEnv =
172
- options.configEnv ?? (env("OKE_DOCKER") === "1" ? "docker" : "local");
187
+ options.configEnv ??
188
+ (env("NODE_ENV") === "production" ? "prod" : env("OKE_DOCKER") === "1" ? "dev" : "test");
173
189
  const effectiveDrivers = resolveEffectiveDrivers(driversConfig);
174
190
 
175
191
  for (const name of secretNames) {
@@ -184,6 +200,25 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
184
200
  }
185
201
  }
186
202
 
203
+ // An env-var master key is only as protected as the process environment:
204
+ // it lands in shell history, orchestrator manifests, and crash dumps.
205
+ const usesBuiltinVault =
206
+ vaultConfig !== undefined || driverRefId(effectiveDrivers.vault[configEnv]) === "vault";
207
+ if (
208
+ configEnv === "prod" &&
209
+ usesBuiltinVault &&
210
+ (vaultConfig?.encryption?.masterKey?.kind ?? "env") === "env"
211
+ ) {
212
+ findings.push({
213
+ code: "vault_master_key",
214
+ // Fail closed in prod: an env-sourced master key collapses envelope
215
+ // protection the moment DB + env are stolen together.
216
+ severity: "error",
217
+ message:
218
+ 'vault master key comes from an environment variable in prod — set vault.encryption.masterKey = { kind: "kms", keyId } (or use managed aws-secrets-manager) so the key is never readable from the process environment',
219
+ });
220
+ }
221
+
187
222
  const ports = options.ports ?? [APP_PORT, CONSOLE_PORT, MCP_PORT];
188
223
  const probe = options.isPortInUse ?? isPortInUse;
189
224
  for (const port of ports) {
@@ -213,13 +248,13 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
213
248
  code: "schema_drift",
214
249
  severity: "error",
215
250
  message:
216
- "core stub schema drift — run `oke schema generate` (schema/oke.ts; not domain migrations)",
251
+ "core stub schema drift — run `oke schema generate` (.oke/schema/oke.ts; not domain migrations)",
217
252
  });
218
253
  } else if (expected !== null && current === null) {
219
254
  findings.push({
220
255
  code: "schema_drift",
221
256
  severity: "error",
222
- message: "schema/oke.ts missing — run `oke schema generate` (core/plugin stubs)",
257
+ message: ".oke/schema/oke.ts missing — run `oke schema generate` (core/plugin stubs)",
223
258
  });
224
259
  }
225
260
  }
@@ -237,7 +272,7 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
237
272
  severity: "error",
238
273
  message:
239
274
  drift.detail ??
240
- "domain schema differs from the database — run `oke db generate` then `oke db migrate` (or `oke db push` in local)",
275
+ "domain schema differs from the database — run `oke db generate` then `oke db migrate` (or `oke db push` in dev)",
241
276
  });
242
277
  }
243
278
  }
@@ -299,18 +334,23 @@ export async function doctorCli(args: readonly string[]): Promise<number> {
299
334
  if (a === "--manifest" || a === "-m") manifestPath = args[++i];
300
335
  else if (a === "--env" || a === "-e") {
301
336
  const value = args[++i];
302
- if (value === "local" || value === "docker" || value === "test" || value === "prod") {
337
+ if (value === "dev" || value === "test" || value === "prod") {
303
338
  configEnv = value;
339
+ } else if (value === "local") {
340
+ configEnv = "test";
341
+ } else if (value === "docker") {
342
+ configEnv = "dev";
304
343
  }
305
344
  } else if (a === "--help" || a === "-h") {
306
- console.log(`oke doctor [--manifest|-m path] [--env|-e local|docker|test|prod] [--json|-j]
345
+ console.log(`oke doctor [--manifest|-m path] [--env|-e dev|test|prod] [--json|-j]
307
346
  oke doctor --diff|-d [--before|-b <path> --after|-a <path>] [--base|-B <branch>]
308
347
 
309
348
  Verify secrets, ports, schema drift, and PII→model egress before serving.
310
349
  Also prints drivers.* fully resolved (every default + every override merged)
311
350
  for the active env, from oke.config.ts.
312
351
 
313
- --env Env to resolve drivers for (default: docker when OKE_DOCKER=1, else local).
352
+ --env Env to resolve drivers for (default: prod when NODE_ENV=production,
353
+ dev when OKE_DOCKER=1 / \`oke dev\`, else test).
314
354
  --diff CI gate: block undeclared contract breaks (Manifest Diff).
315
355
  Default baseline is git merge-base (main/master) vs the working
316
356
  tree; pass --before/--after for an explicit comparison.
@@ -24,22 +24,20 @@ async function tempDir(): Promise<string> {
24
24
  const dualConfig = {
25
25
  drivers: {
26
26
  store: {
27
- sql: { local: "sqlite", docker: "postgres", prod: "postgres", test: "memory" },
27
+ sql: { dev: "postgres", test: "pglite", prod: "postgres" },
28
28
  },
29
29
  },
30
30
  } as unknown as OkeConfig;
31
31
 
32
32
  describe("resolveDrizzleKitEnv", () => {
33
- test("local env → sqlite dialect + sqlite url", async () => {
33
+ test("test env → postgresql dialect (pglite)", async () => {
34
34
  const dir = await tempDir();
35
- const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "local");
36
- expect(ctx.dialect).toBe("sqlite");
37
- expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("sqlite");
38
- expect(ctx.overlay.OKE_SQLITE_URL).toContain(".oke/app.sqlite");
39
- expect(ctx.overlay.DATABASE_URL).toBeUndefined();
35
+ const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "test");
36
+ expect(ctx.dialect).toBe("postgresql");
37
+ expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("postgresql");
40
38
  });
41
39
 
42
- test("docker env → postgresql dialect + DATABASE_URL from compose env", async () => {
40
+ test("dev env → postgresql dialect + DATABASE_URL from compose env", async () => {
43
41
  const dir = await tempDir();
44
42
  await mkdir(join(dir, "docker"), { recursive: true });
45
43
  await writeFile(
@@ -49,7 +47,7 @@ describe("resolveDrizzleKitEnv", () => {
49
47
  const prevDb = process.env["DATABASE_URL"];
50
48
  delete process.env["DATABASE_URL"];
51
49
  try {
52
- const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "docker");
50
+ const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "dev");
53
51
  expect(ctx.dialect).toBe("postgresql");
54
52
  expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("postgresql");
55
53
  expect(ctx.overlay.DATABASE_URL).toBe("postgres://u:p@127.0.0.1:5432/oke");
@@ -60,16 +58,16 @@ describe("resolveDrizzleKitEnv", () => {
60
58
  }
61
59
  });
62
60
 
63
- test("docker dialect is postgresql even when DATABASE_URL unset (no URL guessing)", async () => {
61
+ test("dev dialect is postgresql even when DATABASE_URL unset (no URL guessing)", async () => {
64
62
  const dir = await tempDir();
65
- const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "docker");
63
+ const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "dev");
66
64
  expect(ctx.dialect).toBe("postgresql");
67
65
  });
68
66
 
69
67
  test("unsupported driver id errors clearly", async () => {
70
68
  const dir = await tempDir();
71
- const bad = { drivers: { store: { sql: { local: "memory" } } } } as unknown as OkeConfig;
72
- await expect(resolveDrizzleKitEnv(dir, bad, "local")).rejects.toThrow(/not supported/);
69
+ const bad = { drivers: { store: { sql: { test: "memory" } } } } as unknown as OkeConfig;
70
+ await expect(resolveDrizzleKitEnv(dir, bad, "test")).rejects.toThrow(/not supported/);
73
71
  });
74
72
  });
75
73