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
@@ -20,7 +20,6 @@ import type { WideEvent } from "../runs/types.ts";
20
20
  import { isDataEnvelope, MCP_DATA_KIND } from "../mcp/data.ts";
21
21
  import { mintMcpSession } from "../mcp/session.ts";
22
22
  import { runDev, type DevOptions, type DevSession } from "./dev.ts";
23
- import { readDevMode, writeDevMode } from "./dev-mode.ts";
24
23
  import { mcpContextFromConsole } from "./mcp-from-console.ts";
25
24
 
26
25
  /** Repo public entry — absolute import so temp apps need no install. */
@@ -28,6 +27,45 @@ const OKE_INDEX = resolve(import.meta.dir, "../index.ts");
28
27
 
29
28
  const SECRET = "oke-dev-mcp-integration-secret";
30
29
 
30
+ /** Minimal compose injectables — unit tests never shell out to Docker. */
31
+ const STUB_IMAGES = {
32
+ "store.sql": "postgres:16-alpine",
33
+ "store.kv": "redis:7-alpine",
34
+ } as const;
35
+ const STUB_CREDENTIALS = {
36
+ "store.sql": {
37
+ user: "oke",
38
+ password: "test-password-not-in-yaml",
39
+ database: "oke",
40
+ },
41
+ } as const;
42
+
43
+ function stubCompose(
44
+ overrides: Partial<
45
+ Pick<
46
+ DevOptions,
47
+ "images" | "credentials" | "composeUp" | "composeHealth" | "composeStop" | "noDbPush"
48
+ >
49
+ > = {},
50
+ ): Pick<
51
+ DevOptions,
52
+ "images" | "credentials" | "composeUp" | "composeHealth" | "composeStop" | "noDbPush"
53
+ > {
54
+ return {
55
+ images: STUB_IMAGES,
56
+ credentials: STUB_CREDENTIALS,
57
+ composeUp: async () => {},
58
+ composeHealth: async () =>
59
+ new Map([
60
+ ["store-sql", "ready"],
61
+ ["store-kv", "ready"],
62
+ ]),
63
+ composeStop: () => {},
64
+ noDbPush: true,
65
+ ...overrides,
66
+ };
67
+ }
68
+
31
69
  const LIVE_MANIFEST: Manifest = {
32
70
  oke: "1.0",
33
71
  app: "dev-live",
@@ -77,6 +115,52 @@ describe("mcpContextFromConsole", () => {
77
115
  });
78
116
  });
79
117
 
118
+ /**
119
+ * Console for MCP tests — boot with `docker: false` so host `OKE_DOCKER=1`
120
+ * from compose stubs does not force redis/smtp docker URLs.
121
+ */
122
+ async function serveTestConsole(
123
+ port: number,
124
+ cwd: string,
125
+ manifest: Manifest = LIVE_MANIFEST,
126
+ ): Promise<{
127
+ readonly console: import("../console/server/app.ts").ConsoleAppHandle;
128
+ readonly port: number;
129
+ readonly stop: () => void;
130
+ }> {
131
+ const { createConsoleApp } = await import("../console/server/app.ts");
132
+ const handle = createConsoleApp({
133
+ cwd,
134
+ secret: SECRET,
135
+ silentClaim: true,
136
+ manifest,
137
+ });
138
+ await handle.app.boot({ env: "test", docker: false });
139
+ handle.state.listRuns = async () => {
140
+ const runs = handle.app.bootResult?.runs;
141
+ if (!runs) return [];
142
+ return runs.all();
143
+ };
144
+ const server = Bun.serve({
145
+ port,
146
+ hostname: "127.0.0.1",
147
+ fetch: (req) => handle.app.fetch(req),
148
+ });
149
+ const boundPort = server.port;
150
+ if (boundPort === undefined) {
151
+ server.stop(true);
152
+ throw new Error("test console: Bun.serve did not bind a port");
153
+ }
154
+ return {
155
+ console: handle,
156
+ port: boundPort,
157
+ stop() {
158
+ server.stop(true);
159
+ void handle.app.stop();
160
+ },
161
+ };
162
+ }
163
+
80
164
  describe("oke dev MCP live wiring", () => {
81
165
  let session: DevSession | undefined;
82
166
 
@@ -99,9 +183,11 @@ describe("oke dev MCP live wiring", () => {
99
183
  consolePort: 0,
100
184
  mcpPort: 0,
101
185
  appPort: 0,
186
+ ...stubCompose(),
102
187
  startApp: async () => ({ stop() {} }),
103
188
  regenClient: async () => {},
104
189
  write: () => {},
190
+ serveConsole: async (port) => serveTestConsole(port, dir),
105
191
  });
106
192
 
107
193
  expect(result.code).toBe(0);
@@ -228,26 +314,18 @@ describe("oke dev MCP live wiring", () => {
228
314
  mcpPort: 0,
229
315
  appPort: 0,
230
316
  manifest: LIVE_MANIFEST,
317
+ ...stubCompose(),
231
318
  startApp: async () => ({ stop() {} }),
232
319
  regenClient: async () => {},
233
320
  write: () => {},
234
321
  serveConsole: async (port) => {
235
- const { serveConsole } = await import("../console/server/serve.ts");
236
- const server = await serveConsole({
237
- port,
238
- hostname: "127.0.0.1",
239
- cwd: dir,
240
- secret: SECRET,
241
- silentClaim: true,
242
- env: "dev",
243
- manifest: LIVE_MANIFEST,
244
- });
322
+ const server = await serveTestConsole(port, dir);
245
323
  return {
246
324
  console: server.console,
247
325
  port: server.port,
248
326
  stop() {
249
327
  consoleStopped = true;
250
- server.stop(true);
328
+ server.stop();
251
329
  },
252
330
  };
253
331
  },
@@ -298,6 +376,7 @@ describe("oke dev docs MCP", () => {
298
376
  consolePort: 0,
299
377
  mcpPort: 0,
300
378
  docsMcpPort: 0,
379
+ ...stubCompose(),
301
380
  startApp: async () => ({ stop() {} }),
302
381
  regenClient: async () => {},
303
382
  write: () => {},
@@ -371,6 +450,7 @@ describe("oke dev docs MCP", () => {
371
450
  consolePort: 0,
372
451
  mcpPort: 0,
373
452
  docsMcpPort: 0,
453
+ ...stubCompose(),
374
454
  startApp: async () => ({ stop() {} }),
375
455
  regenClient: async () => {},
376
456
  write: (t) => {
@@ -426,7 +506,7 @@ export const app = oke({ name: "dev-hot", env: "test" }).adopt({ ping });
426
506
  );
427
507
  }
428
508
 
429
- /** Stub Console + MCP so boot/hot tests isolate the app child. */
509
+ /** Stub Console + MCP + compose so boot/hot tests isolate the app child. */
430
510
  function stubSurfaces(): Pick<
431
511
  DevOptions,
432
512
  | "serveConsole"
@@ -436,6 +516,12 @@ function stubSurfaces(): Pick<
436
516
  | "write"
437
517
  | "silentClaim"
438
518
  | "keepAlive"
519
+ | "images"
520
+ | "credentials"
521
+ | "composeUp"
522
+ | "composeHealth"
523
+ | "composeStop"
524
+ | "noDbPush"
439
525
  > {
440
526
  return {
441
527
  silentClaim: true,
@@ -445,6 +531,7 @@ function stubSurfaces(): Pick<
445
531
  serveConsole: async () => ({ stop() {} }),
446
532
  serveMcp: async () => ({ stop() {} }),
447
533
  serveDocsMcp: async () => ({ stop() {} }),
534
+ ...stubCompose(),
448
535
  };
449
536
  }
450
537
 
@@ -561,6 +648,8 @@ describe("oke dev syncAdoptBarrel atomic write", () => {
561
648
  cwd: dir,
562
649
  dryRun: true,
563
650
  stdinIsTTY: false,
651
+ images: STUB_IMAGES,
652
+ credentials: STUB_CREDENTIALS,
564
653
  write: () => {},
565
654
  });
566
655
  expect(code).toBe(0);
@@ -631,109 +720,49 @@ describe("oke dev syncAdoptBarrel atomic write", () => {
631
720
  });
632
721
  });
633
722
 
634
- describe("oke dev mode resolution", () => {
635
- test("non-TTY + unset mode local, zero ask, zero docker, no save", async () => {
636
- const dir = await mkdtemp(join(tmpdir(), "oke-dev-mode-nontty-"));
723
+ describe("oke dev Docker-first", () => {
724
+ test("dryRun always plans compose (no mode ask)", async () => {
725
+ const dir = await mkdtemp(join(tmpdir(), "oke-dev-always-compose-"));
637
726
  await Bun.write(join(dir, "src/app.ts"), "export {}\n");
638
- let askCalls = 0;
639
727
  let composeCalls = 0;
640
728
  const { code, plan } = await runDev({
641
729
  cwd: dir,
642
730
  dryRun: true,
643
731
  stdinIsTTY: false,
644
- ask: async () => {
645
- askCalls++;
646
- return "docker";
647
- },
732
+ images: STUB_IMAGES,
733
+ credentials: STUB_CREDENTIALS,
648
734
  composeUp: async () => {
649
735
  composeCalls++;
650
736
  },
651
737
  write: () => {},
652
738
  });
653
739
  expect(code).toBe(0);
654
- expect(askCalls).toBe(0);
655
740
  expect(composeCalls).toBe(0);
656
- expect(plan?.stackRoles).toBeNull();
657
- expect(await readDevMode(dir)).toBeNull();
741
+ expect(plan?.stackRoles).toEqual(["store.sql", "store.kv"]);
658
742
  });
659
743
 
660
- test("TTY + unset mode ask once, save choice", async () => {
661
- const dir = await mkdtemp(join(tmpdir(), "oke-dev-mode-tty-"));
744
+ test("role filter -d store.sql plans only that role", async () => {
745
+ const dir = await mkdtemp(join(tmpdir(), "oke-dev-roles-"));
662
746
  await Bun.write(join(dir, "src/app.ts"), "export {}\n");
663
- let askCalls = 0;
664
- const { code } = await runDev({
665
- cwd: dir,
666
- dryRun: true,
667
- stdinIsTTY: true,
668
- ask: async () => {
669
- askCalls++;
670
- return "local";
671
- },
672
- write: () => {},
673
- });
674
- expect(code).toBe(0);
675
- expect(askCalls).toBe(1);
676
- expect(await readDevMode(dir)).toBe("local");
677
- });
678
-
679
- test("TTY + unset mode → docker choice saves and plans compose on dryRun", async () => {
680
- const dir = await mkdtemp(join(tmpdir(), "oke-dev-mode-tty-d-"));
681
- await Bun.write(join(dir, "src/app.ts"), "export {}\n");
682
- let askCalls = 0;
683
- let composeCalls = 0;
684
747
  const { code, plan } = await runDev({
685
748
  cwd: dir,
686
749
  dryRun: true,
687
- stdinIsTTY: true,
688
- images: { "store.sql": "postgres:16-alpine" },
689
- credentials: {
690
- "store.sql": {
691
- user: "oke",
692
- password: "test-password-not-in-yaml",
693
- database: "oke",
694
- },
695
- },
696
- ask: async () => {
697
- askCalls++;
698
- return "docker";
699
- },
700
- composeUp: async () => {
701
- composeCalls++;
750
+ stdinIsTTY: false,
751
+ docker: ["store.sql"],
752
+ images: {
753
+ "store.sql": "postgres:16-alpine",
754
+ "store.kv": "redis:7-alpine",
702
755
  },
756
+ credentials: STUB_CREDENTIALS,
703
757
  write: () => {},
704
758
  });
705
759
  expect(code).toBe(0);
706
- expect(askCalls).toBe(1);
707
- expect(composeCalls).toBe(0);
708
- expect(await readDevMode(dir)).toBe("docker");
709
760
  expect(plan?.stackRoles).toEqual(["store.sql"]);
710
761
  });
711
762
 
712
- test("session --local never writes .oke/mode", async () => {
713
- const dir = await mkdtemp(join(tmpdir(), "oke-dev-mode-local-"));
714
- await Bun.write(join(dir, "src/app.ts"), "export {}\n");
715
- let askCalls = 0;
716
- const { code, plan } = await runDev({
717
- cwd: dir,
718
- dryRun: true,
719
- local: true,
720
- stdinIsTTY: true,
721
- ask: async () => {
722
- askCalls++;
723
- return "docker";
724
- },
725
- write: () => {},
726
- });
727
- expect(code).toBe(0);
728
- expect(askCalls).toBe(0);
729
- expect(plan?.stackRoles).toBeNull();
730
- expect(await readDevMode(dir)).toBeNull();
731
- });
732
-
733
- test("session stop calls composeStop after a successful docker up", async () => {
763
+ test("session stop calls composeStop after a successful compose up", async () => {
734
764
  const dir = await mkdtemp(join(tmpdir(), "oke-dev-compose-stop-"));
735
765
  await Bun.write(join(dir, "src/app.ts"), "export {}\n");
736
- await writeDevMode(dir, "docker");
737
766
  let upCalls = 0;
738
767
  let stopCalls = 0;
739
768
  let stoppedFiles: readonly string[] | undefined;
@@ -746,26 +775,20 @@ describe("oke dev mode resolution", () => {
746
775
  const result = await runDev({
747
776
  cwd: dir,
748
777
  stdinIsTTY: false,
749
- images: { "store.sql": "postgres:16-alpine" },
750
- credentials: {
751
- "store.sql": {
752
- user: "oke",
753
- password: "test-password-not-in-yaml",
754
- database: "oke",
778
+ ...stubCompose({
779
+ composeUp: async (files) => {
780
+ upCalls++;
781
+ expect(files.length).toBeGreaterThan(0);
755
782
  },
756
- },
757
- composeUp: async (files) => {
758
- upCalls++;
759
- expect(files.length).toBeGreaterThan(0);
760
- },
761
- composeHealth: async ({ onUpdate }) => {
762
- onUpdate?.("store-sql", "ready");
763
- return new Map([["store-sql", "ready"]]);
764
- },
765
- composeStop: (files) => {
766
- stopCalls++;
767
- stoppedFiles = files;
768
- },
783
+ composeHealth: async ({ onUpdate }) => {
784
+ onUpdate?.("store-sql", "ready");
785
+ return new Map([["store-sql", "ready"]]);
786
+ },
787
+ composeStop: (files) => {
788
+ stopCalls++;
789
+ stoppedFiles = files;
790
+ },
791
+ }),
769
792
  startApp: async () => ({ stop() {} }),
770
793
  serveConsole: async () => ({ stop() {} }),
771
794
  regenClient: async () => {},
@@ -790,10 +813,9 @@ describe("oke dev mode resolution", () => {
790
813
  }
791
814
  });
792
815
 
793
- test("saved docker + compose failure exits loudly (no silent downgrade)", async () => {
794
- const dir = await mkdtemp(join(tmpdir(), "oke-dev-mode-fail-"));
816
+ test("compose failure exits loudly (no silent downgrade)", async () => {
817
+ const dir = await mkdtemp(join(tmpdir(), "oke-dev-compose-fail-"));
795
818
  await Bun.write(join(dir, "src/app.ts"), "export {}\n");
796
- await writeDevMode(dir, "docker");
797
819
  const lines: string[] = [];
798
820
  const origErr = console.error;
799
821
  console.error = (...args: unknown[]) => {
@@ -803,17 +825,11 @@ describe("oke dev mode resolution", () => {
803
825
  const { code } = await runDev({
804
826
  cwd: dir,
805
827
  stdinIsTTY: false,
806
- images: { "store.sql": "postgres:16-alpine" },
807
- credentials: {
808
- "store.sql": {
809
- user: "oke",
810
- password: "test-password-not-in-yaml",
811
- database: "oke",
828
+ ...stubCompose({
829
+ composeUp: async () => {
830
+ throw new Error("compose boom");
812
831
  },
813
- },
814
- composeUp: async () => {
815
- throw new Error("compose boom");
816
- },
832
+ }),
817
833
  startApp: async () => ({ stop() {} }),
818
834
  serveConsole: async () => ({ stop() {} }),
819
835
  regenClient: async () => {},
@@ -821,7 +837,36 @@ describe("oke dev mode resolution", () => {
821
837
  keepAlive: false,
822
838
  });
823
839
  expect(code).toBe(1);
824
- expect(lines.some((l) => l.includes("oke mode local"))).toBe(true);
840
+ expect(lines.some((l) => l.includes("Docker Compose failed"))).toBe(true);
841
+ } finally {
842
+ console.error = origErr;
843
+ }
844
+ });
845
+
846
+ test("missing Docker daemon fails fast with a clear error", async () => {
847
+ const dir = await mkdtemp(join(tmpdir(), "oke-dev-no-daemon-"));
848
+ await Bun.write(join(dir, "src/app.ts"), "export {}\n");
849
+ const lines: string[] = [];
850
+ const origErr = console.error;
851
+ console.error = (...args: unknown[]) => {
852
+ lines.push(args.map(String).join(" "));
853
+ };
854
+ try {
855
+ const { code } = await runDev({
856
+ cwd: dir,
857
+ stdinIsTTY: false,
858
+ images: STUB_IMAGES,
859
+ credentials: STUB_CREDENTIALS,
860
+ assertDocker: async () => {
861
+ throw new Error(
862
+ "oke dev: Docker daemon unavailable — start Docker Desktop (or the Docker daemon) and retry",
863
+ );
864
+ },
865
+ write: () => {},
866
+ keepAlive: false,
867
+ });
868
+ expect(code).toBe(1);
869
+ expect(lines.some((l) => l.includes("Docker daemon unavailable"))).toBe(true);
825
870
  } finally {
826
871
  console.error = origErr;
827
872
  }