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
@@ -7,15 +7,19 @@
7
7
  */
8
8
 
9
9
  import { afterEach, describe, expect, test } from "bun:test";
10
- import { mkdtemp } from "node:fs/promises";
10
+ import { mkdir, mkdtemp, readdir } from "node:fs/promises";
11
11
  import { tmpdir } from "node:os";
12
12
  import { join, resolve } from "node:path";
13
+ import {
14
+ ADOPT_BARREL_FILE,
15
+ generateAdoptBarrel,
16
+ writeAdoptBarrel,
17
+ } from "../compiler/generate-adopt.ts";
13
18
  import type { Manifest } from "../manifest/types.ts";
14
19
  import type { WideEvent } from "../runs/types.ts";
15
20
  import { isDataEnvelope, MCP_DATA_KIND } from "../mcp/data.ts";
16
21
  import { mintMcpSession } from "../mcp/session.ts";
17
22
  import { runDev, type DevOptions, type DevSession } from "./dev.ts";
18
- import { readDevMode, writeDevMode } from "./dev-mode.ts";
19
23
  import { mcpContextFromConsole } from "./mcp-from-console.ts";
20
24
 
21
25
  /** Repo public entry — absolute import so temp apps need no install. */
@@ -23,6 +27,45 @@ const OKE_INDEX = resolve(import.meta.dir, "../index.ts");
23
27
 
24
28
  const SECRET = "oke-dev-mcp-integration-secret";
25
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
+
26
69
  const LIVE_MANIFEST: Manifest = {
27
70
  oke: "1.0",
28
71
  app: "dev-live",
@@ -72,6 +115,52 @@ describe("mcpContextFromConsole", () => {
72
115
  });
73
116
  });
74
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
+
75
164
  describe("oke dev MCP live wiring", () => {
76
165
  let session: DevSession | undefined;
77
166
 
@@ -94,9 +183,11 @@ describe("oke dev MCP live wiring", () => {
94
183
  consolePort: 0,
95
184
  mcpPort: 0,
96
185
  appPort: 0,
186
+ ...stubCompose(),
97
187
  startApp: async () => ({ stop() {} }),
98
188
  regenClient: async () => {},
99
189
  write: () => {},
190
+ serveConsole: async (port) => serveTestConsole(port, dir),
100
191
  });
101
192
 
102
193
  expect(result.code).toBe(0);
@@ -223,26 +314,18 @@ describe("oke dev MCP live wiring", () => {
223
314
  mcpPort: 0,
224
315
  appPort: 0,
225
316
  manifest: LIVE_MANIFEST,
317
+ ...stubCompose(),
226
318
  startApp: async () => ({ stop() {} }),
227
319
  regenClient: async () => {},
228
320
  write: () => {},
229
321
  serveConsole: async (port) => {
230
- const { serveConsole } = await import("../console/server/serve.ts");
231
- const server = await serveConsole({
232
- port,
233
- hostname: "127.0.0.1",
234
- cwd: dir,
235
- secret: SECRET,
236
- silentClaim: true,
237
- env: "dev",
238
- manifest: LIVE_MANIFEST,
239
- });
322
+ const server = await serveTestConsole(port, dir);
240
323
  return {
241
324
  console: server.console,
242
325
  port: server.port,
243
326
  stop() {
244
327
  consoleStopped = true;
245
- server.stop(true);
328
+ server.stop();
246
329
  },
247
330
  };
248
331
  },
@@ -293,6 +376,7 @@ describe("oke dev docs MCP", () => {
293
376
  consolePort: 0,
294
377
  mcpPort: 0,
295
378
  docsMcpPort: 0,
379
+ ...stubCompose(),
296
380
  startApp: async () => ({ stop() {} }),
297
381
  regenClient: async () => {},
298
382
  write: () => {},
@@ -366,6 +450,7 @@ describe("oke dev docs MCP", () => {
366
450
  consolePort: 0,
367
451
  mcpPort: 0,
368
452
  docsMcpPort: 0,
453
+ ...stubCompose(),
369
454
  startApp: async () => ({ stop() {} }),
370
455
  regenClient: async () => {},
371
456
  write: (t) => {
@@ -421,7 +506,7 @@ export const app = oke({ name: "dev-hot", env: "test" }).adopt({ ping });
421
506
  );
422
507
  }
423
508
 
424
- /** Stub Console + MCP so boot/hot tests isolate the app child. */
509
+ /** Stub Console + MCP + compose so boot/hot tests isolate the app child. */
425
510
  function stubSurfaces(): Pick<
426
511
  DevOptions,
427
512
  | "serveConsole"
@@ -431,6 +516,12 @@ function stubSurfaces(): Pick<
431
516
  | "write"
432
517
  | "silentClaim"
433
518
  | "keepAlive"
519
+ | "images"
520
+ | "credentials"
521
+ | "composeUp"
522
+ | "composeHealth"
523
+ | "composeStop"
524
+ | "noDbPush"
434
525
  > {
435
526
  return {
436
527
  silentClaim: true,
@@ -440,6 +531,7 @@ function stubSurfaces(): Pick<
440
531
  serveConsole: async () => ({ stop() {} }),
441
532
  serveMcp: async () => ({ stop() {} }),
442
533
  serveDocsMcp: async () => ({ stop() {} }),
534
+ ...stubCompose(),
443
535
  };
444
536
  }
445
537
 
@@ -544,109 +636,133 @@ describe("oke dev hot reload", () => {
544
636
  }, 60_000);
545
637
  });
546
638
 
547
- describe("oke dev mode resolution", () => {
548
- test("non-TTY + unset mode local, zero ask, zero docker, no save", async () => {
549
- const dir = await mkdtemp(join(tmpdir(), "oke-dev-mode-nontty-"));
550
- await Bun.write(join(dir, "src/app.ts"), "export {}\n");
551
- let askCalls = 0;
552
- let composeCalls = 0;
553
- const { code, plan } = await runDev({
639
+ describe("oke dev syncAdoptBarrel atomic write", () => {
640
+ test("default path regenerates generated.ts and leaves no .tmp behind", async () => {
641
+ const dir = await mkdtemp(join(tmpdir(), "oke-dev-adopt-atomic-"));
642
+ await mkdir(join(dir, "src/flows/notes"), { recursive: true });
643
+ await Bun.write(join(dir, "src/flows/notes/index.ts"), "export {};\n");
644
+ await Bun.write(join(dir, "src/app.ts"), "export {};\n");
645
+ await Bun.write(join(dir, "src/flows", ADOPT_BARREL_FILE), "// stale stub\n");
646
+
647
+ const { code } = await runDev({
554
648
  cwd: dir,
555
649
  dryRun: true,
556
650
  stdinIsTTY: false,
557
- ask: async () => {
558
- askCalls++;
559
- return "docker";
560
- },
561
- composeUp: async () => {
562
- composeCalls++;
563
- },
651
+ images: STUB_IMAGES,
652
+ credentials: STUB_CREDENTIALS,
564
653
  write: () => {},
565
654
  });
566
655
  expect(code).toBe(0);
567
- expect(askCalls).toBe(0);
568
- expect(composeCalls).toBe(0);
569
- expect(plan?.stackRoles).toBeNull();
570
- expect(await readDevMode(dir)).toBeNull();
656
+
657
+ const barrelPath = join(dir, "src/flows", ADOPT_BARREL_FILE);
658
+ const text = await Bun.file(barrelPath).text();
659
+ expect(text).toContain('export * as notes from "./notes/index.ts";');
660
+ expect(text).not.toContain("stale stub");
661
+ expect(await Bun.file(`${barrelPath}.tmp`).exists()).toBe(false);
662
+ const leftovers = (await readdir(join(dir, "src/flows"))).filter((f) => f.includes(".tmp"));
663
+ expect(leftovers).toEqual([]);
571
664
  });
572
665
 
573
- test("TTY + unset mode ask once, save choice", async () => {
574
- const dir = await mkdtemp(join(tmpdir(), "oke-dev-mode-tty-"));
575
- await Bun.write(join(dir, "src/app.ts"), "export {}\n");
576
- let askCalls = 0;
577
- const { code } = await runDev({
578
- cwd: dir,
579
- dryRun: true,
580
- stdinIsTTY: true,
581
- ask: async () => {
582
- askCalls++;
583
- return "local";
584
- },
585
- write: () => {},
586
- });
587
- expect(code).toBe(0);
588
- expect(askCalls).toBe(1);
589
- expect(await readDevMode(dir)).toBe("local");
666
+ test("writeAdoptBarrel uses temp then rename concurrent readers never see a torn file", async () => {
667
+ const dir = await mkdtemp(join(tmpdir(), "oke-dev-adopt-race-"));
668
+ const flowsDir = join(dir, "src/flows");
669
+ await mkdir(flowsDir, { recursive: true });
670
+ const target = join(flowsDir, ADOPT_BARREL_FILE);
671
+
672
+ // Two complete, same-length payloads so a mid-write truncate would yield
673
+ // mixed or short content that fails both equality checks.
674
+ const markerA = "A";
675
+ const markerB = "B";
676
+ const body = (marker: string) => `// ${marker}\n` + `${marker.repeat(64)}\n`.repeat(2_000);
677
+ const payloadA = body(markerA);
678
+ const payloadB = body(markerB);
679
+ expect(payloadA.length).toBe(payloadB.length);
680
+ expect(payloadA).not.toBe(payloadB);
681
+
682
+ await writeAdoptBarrel(dir, payloadA);
683
+ expect(await Bun.file(target).text()).toBe(payloadA);
684
+
685
+ const reads: string[] = [];
686
+ let stop = false;
687
+ const reader = (async () => {
688
+ while (!stop) {
689
+ try {
690
+ reads.push(await Bun.file(target).text());
691
+ } catch {
692
+ // Target briefly missing only if rename races a delete — ignore.
693
+ }
694
+ // Yield so writers interleave; Bun.sleep(0) is enough on this loop.
695
+ await Bun.sleep(0);
696
+ }
697
+ })();
698
+
699
+ for (let i = 0; i < 80; i++) {
700
+ await writeAdoptBarrel(dir, i % 2 === 0 ? payloadB : payloadA);
701
+ }
702
+ stop = true;
703
+ await reader;
704
+
705
+ expect(reads.length).toBeGreaterThan(10);
706
+ for (const sample of reads) {
707
+ // Every observation is a complete prior or complete next payload —
708
+ // never a truncated prefix, never a mix of A/B markers.
709
+ const ok = sample === payloadA || sample === payloadB;
710
+ expect(ok).toBe(true);
711
+ expect(sample.length).toBe(payloadA.length);
712
+ expect(sample.includes(markerA) && sample.includes(markerB)).toBe(false);
713
+ }
714
+ expect(await Bun.file(`${target}.tmp`).exists()).toBe(false);
715
+
716
+ // Same helper the default `oke dev` path uses end-to-end.
717
+ const { source } = await generateAdoptBarrel({ rootDir: dir });
718
+ await writeAdoptBarrel(dir, source);
719
+ expect(await Bun.file(target).text()).toBe(source);
590
720
  });
721
+ });
591
722
 
592
- test("TTY + unset mode → docker choice saves and plans compose on dryRun", async () => {
593
- const dir = await mkdtemp(join(tmpdir(), "oke-dev-mode-tty-d-"));
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-"));
594
726
  await Bun.write(join(dir, "src/app.ts"), "export {}\n");
595
- let askCalls = 0;
596
727
  let composeCalls = 0;
597
728
  const { code, plan } = await runDev({
598
729
  cwd: dir,
599
730
  dryRun: true,
600
- stdinIsTTY: true,
601
- images: { "store.sql": "postgres:16-alpine" },
602
- credentials: {
603
- "store.sql": {
604
- user: "oke",
605
- password: "test-password-not-in-yaml",
606
- database: "oke",
607
- },
608
- },
609
- ask: async () => {
610
- askCalls++;
611
- return "docker";
612
- },
731
+ stdinIsTTY: false,
732
+ images: STUB_IMAGES,
733
+ credentials: STUB_CREDENTIALS,
613
734
  composeUp: async () => {
614
735
  composeCalls++;
615
736
  },
616
737
  write: () => {},
617
738
  });
618
739
  expect(code).toBe(0);
619
- expect(askCalls).toBe(1);
620
740
  expect(composeCalls).toBe(0);
621
- expect(await readDevMode(dir)).toBe("docker");
622
- expect(plan?.stackRoles).toEqual(["store.sql"]);
741
+ expect(plan?.stackRoles).toEqual(["store.sql", "store.kv"]);
623
742
  });
624
743
 
625
- test("session --local never writes .oke/mode", async () => {
626
- const dir = await mkdtemp(join(tmpdir(), "oke-dev-mode-local-"));
744
+ test("role filter -d store.sql plans only that role", async () => {
745
+ const dir = await mkdtemp(join(tmpdir(), "oke-dev-roles-"));
627
746
  await Bun.write(join(dir, "src/app.ts"), "export {}\n");
628
- let askCalls = 0;
629
747
  const { code, plan } = await runDev({
630
748
  cwd: dir,
631
749
  dryRun: true,
632
- local: true,
633
- stdinIsTTY: true,
634
- ask: async () => {
635
- askCalls++;
636
- return "docker";
750
+ stdinIsTTY: false,
751
+ docker: ["store.sql"],
752
+ images: {
753
+ "store.sql": "postgres:16-alpine",
754
+ "store.kv": "redis:7-alpine",
637
755
  },
756
+ credentials: STUB_CREDENTIALS,
638
757
  write: () => {},
639
758
  });
640
759
  expect(code).toBe(0);
641
- expect(askCalls).toBe(0);
642
- expect(plan?.stackRoles).toBeNull();
643
- expect(await readDevMode(dir)).toBeNull();
760
+ expect(plan?.stackRoles).toEqual(["store.sql"]);
644
761
  });
645
762
 
646
- test("session stop calls composeStop after a successful docker up", async () => {
763
+ test("session stop calls composeStop after a successful compose up", async () => {
647
764
  const dir = await mkdtemp(join(tmpdir(), "oke-dev-compose-stop-"));
648
765
  await Bun.write(join(dir, "src/app.ts"), "export {}\n");
649
- await writeDevMode(dir, "docker");
650
766
  let upCalls = 0;
651
767
  let stopCalls = 0;
652
768
  let stoppedFiles: readonly string[] | undefined;
@@ -659,26 +775,20 @@ describe("oke dev mode resolution", () => {
659
775
  const result = await runDev({
660
776
  cwd: dir,
661
777
  stdinIsTTY: false,
662
- images: { "store.sql": "postgres:16-alpine" },
663
- credentials: {
664
- "store.sql": {
665
- user: "oke",
666
- password: "test-password-not-in-yaml",
667
- database: "oke",
778
+ ...stubCompose({
779
+ composeUp: async (files) => {
780
+ upCalls++;
781
+ expect(files.length).toBeGreaterThan(0);
668
782
  },
669
- },
670
- composeUp: async (files) => {
671
- upCalls++;
672
- expect(files.length).toBeGreaterThan(0);
673
- },
674
- composeHealth: async ({ onUpdate }) => {
675
- onUpdate?.("store-sql", "ready");
676
- return new Map([["store-sql", "ready"]]);
677
- },
678
- composeStop: (files) => {
679
- stopCalls++;
680
- stoppedFiles = files;
681
- },
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
+ }),
682
792
  startApp: async () => ({ stop() {} }),
683
793
  serveConsole: async () => ({ stop() {} }),
684
794
  regenClient: async () => {},
@@ -703,10 +813,9 @@ describe("oke dev mode resolution", () => {
703
813
  }
704
814
  });
705
815
 
706
- test("saved docker + compose failure exits loudly (no silent downgrade)", async () => {
707
- 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-"));
708
818
  await Bun.write(join(dir, "src/app.ts"), "export {}\n");
709
- await writeDevMode(dir, "docker");
710
819
  const lines: string[] = [];
711
820
  const origErr = console.error;
712
821
  console.error = (...args: unknown[]) => {
@@ -716,17 +825,11 @@ describe("oke dev mode resolution", () => {
716
825
  const { code } = await runDev({
717
826
  cwd: dir,
718
827
  stdinIsTTY: false,
719
- images: { "store.sql": "postgres:16-alpine" },
720
- credentials: {
721
- "store.sql": {
722
- user: "oke",
723
- password: "test-password-not-in-yaml",
724
- database: "oke",
828
+ ...stubCompose({
829
+ composeUp: async () => {
830
+ throw new Error("compose boom");
725
831
  },
726
- },
727
- composeUp: async () => {
728
- throw new Error("compose boom");
729
- },
832
+ }),
730
833
  startApp: async () => ({ stop() {} }),
731
834
  serveConsole: async () => ({ stop() {} }),
732
835
  regenClient: async () => {},
@@ -734,7 +837,36 @@ describe("oke dev mode resolution", () => {
734
837
  keepAlive: false,
735
838
  });
736
839
  expect(code).toBe(1);
737
- 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);
738
870
  } finally {
739
871
  console.error = origErr;
740
872
  }