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
@@ -3,8 +3,8 @@
3
3
  * flow under that unit — the stale/missing `.adopt()` barrel case.
4
4
  *
5
5
  * Mirrors `effects-stamping.test.ts`'s OKE1008 shape exactly: opt-in only
6
- * via `rootDir`, `local`/`test` warn once (dev-loop stays unbroken),
7
- * `docker`/`prod` hard-fail (`OKE1009`) — never a silently-incomplete route
6
+ * via `rootDir`, `test` warns once (dev-loop stays unbroken),
7
+ * `dev`+compose / `prod` hard-fail (`OKE1009`) — never a silently-incomplete route
8
8
  * table in a deploy-shaped environment.
9
9
  */
10
10
 
@@ -41,23 +41,23 @@ describe("assertAdoptBarrelFresh — direct", () => {
41
41
  try {
42
42
  await makeUnitDir(root, "notes");
43
43
  const notesFlow = flow("notes.create", { do: () => ({ ok: true as const }) });
44
- await expect(assertAdoptBarrelFresh([notesFlow], "docker", root)).resolves.toBeUndefined();
44
+ await expect(assertAdoptBarrelFresh([notesFlow], "dev", root, true)).resolves.toBeUndefined();
45
45
  } finally {
46
46
  await rm(root, { recursive: true, force: true });
47
47
  }
48
48
  });
49
49
 
50
- test("stale: unit on disk, zero adopted flows for it — docker hard-fails OKE1009", async () => {
50
+ test("stale: unit on disk, zero adopted flows for it — dev+compose hard-fails OKE1009", async () => {
51
51
  const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
52
52
  try {
53
53
  await makeUnitDir(root, "notes");
54
- await expect(assertAdoptBarrelFresh([], "docker", root)).rejects.toThrow(/OKE1009/);
54
+ await expect(assertAdoptBarrelFresh([], "dev", root, true)).rejects.toThrow(/OKE1009/);
55
55
  } finally {
56
56
  await rm(root, { recursive: true, force: true });
57
57
  }
58
58
  });
59
59
 
60
- test("stale: prod — same posture as docker", async () => {
60
+ test("stale: prod — same posture as dev+compose", async () => {
61
61
  const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
62
62
  try {
63
63
  await makeUnitDir(root, "notes");
@@ -67,11 +67,11 @@ describe("assertAdoptBarrelFresh — direct", () => {
67
67
  }
68
68
  });
69
69
 
70
- test("stale: local — warns, never throws (dev-loop stays unbroken)", async () => {
70
+ test("stale: test — warns, never throws (dev-loop stays unbroken)", async () => {
71
71
  const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
72
72
  try {
73
73
  await makeUnitDir(root, "notes");
74
- await expect(assertAdoptBarrelFresh([], "local", root)).resolves.toBeUndefined();
74
+ await expect(assertAdoptBarrelFresh([], "test", root)).resolves.toBeUndefined();
75
75
  } finally {
76
76
  await rm(root, { recursive: true, force: true });
77
77
  }
@@ -79,7 +79,7 @@ describe("assertAdoptBarrelFresh — direct", () => {
79
79
  });
80
80
 
81
81
  describe("boot-level: stale barrel through a real oke() boot", () => {
82
- test("docker: real app.boot() hard-fails OKE1009 when a unit folder was never adopted", async () => {
82
+ test("dev+compose: real app.boot() hard-fails OKE1009 when a unit folder was never adopted", async () => {
83
83
  const root = await mkdtemp(join(tmpdir(), "oke-adopt-fresh-"));
84
84
  try {
85
85
  // "notes" exists on disk but this app only ever adopted "main" —
@@ -94,7 +94,22 @@ describe("boot-level: stale barrel through a real oke() boot", () => {
94
94
  const app = oke({ name: "stale-barrel", gate: { unguardedHttp: "allow" } });
95
95
 
96
96
  await expect(
97
- app.boot({ env: "docker", rootDir: root, unguardedHttp: "allow", startScheduler: false }),
97
+ app.boot({
98
+ env: "dev",
99
+ docker: true,
100
+ rootDir: root,
101
+ unguardedHttp: "allow",
102
+ startScheduler: false,
103
+ config: {
104
+ drivers: {
105
+ store: {
106
+ sql: { dev: "memory" },
107
+ kv: { dev: "memory" },
108
+ },
109
+ channel: { email: { dev: "console" } },
110
+ },
111
+ },
112
+ }),
98
113
  ).rejects.toThrow(/OKE1009/);
99
114
  } finally {
100
115
  await rm(root, { recursive: true, force: true });
package/src/kernel/app.ts CHANGED
@@ -41,6 +41,7 @@ import { runWithLocale } from "../i18n/locale-context.ts";
41
41
  import { getMessageCatalogs, matchConfiguredLocale } from "../i18n/messages.ts";
42
42
  import { runDurable } from "../elements/clock/durable.ts";
43
43
  import { isFlow, type AnyFlowDef } from "./flow.ts";
44
+ import { failureCodeOf, runCompensationPhase } from "./compensate.ts";
44
45
  import { fxRetry } from "./concurrency.ts";
45
46
  import {
46
47
  createFxContext,
@@ -85,6 +86,7 @@ import type { JournalRuntime } from "./boot-bind/journal.ts";
85
86
  import { listBindings, resetBindings, type Binding } from "./on.ts";
86
87
  import {
87
88
  channelTemplateRegistry,
89
+ requiredEnvRegistry,
88
90
  secretRegistry,
89
91
  signalRegistry,
90
92
  storeRegistry,
@@ -159,11 +161,11 @@ export interface OkeOptions {
159
161
  * subject key.
160
162
  */
161
163
  readonly archiveInputFields?: readonly string[];
162
- /** Active environment for {@link OkeApp.boot} (defaults to `local`). */
164
+ /** Active environment for {@link OkeApp.boot} (`dev` / `test` / `prod`). */
163
165
  readonly env?: BootOptions["env"];
164
166
  /**
165
- * Docker mode (`oke dev -d` / `OKE_DOCKER=1`) — force the `docker`
166
- * driver profile at boot.
167
+ * Compose mode (`oke dev` / `OKE_DOCKER=1`) — select the `dev` driver
168
+ * profile at boot when `env` is omitted.
167
169
  */
168
170
  readonly docker?: BootOptions["docker"];
169
171
  /** Optional `oke.config.ts` document consumed at boot. */
@@ -485,22 +487,35 @@ export function oke(options: OkeOptions): OkeApp {
485
487
  // explicitly-passed decl that is also in the registry is not doubled.
486
488
  const registrySnapshot =
487
489
  registry === "ignore"
488
- ? { stores: [], secrets: [], signals: [], channelTemplates: [] }
490
+ ? { stores: [], secrets: [], requiredEnv: [], signals: [], channelTemplates: [] }
489
491
  : {
490
492
  stores: storeRegistry.slice(),
491
493
  secrets: secretRegistry.slice(),
494
+ requiredEnv: requiredEnvRegistry.slice(),
492
495
  signals: signalRegistry.slice(),
493
496
  channelTemplates: channelTemplateRegistry.slice(),
494
497
  };
495
498
  if (registry === "consume") {
496
499
  storeRegistry.length = 0;
497
500
  secretRegistry.length = 0;
501
+ requiredEnvRegistry.length = 0;
498
502
  signalRegistry.length = 0;
499
503
  channelTemplateRegistry.length = 0;
500
504
  }
501
505
  const effectiveStores = mergeUnique(options.stores, registrySnapshot.stores);
502
506
  const effectiveSecrets = mergeUnique(options.secrets, registrySnapshot.secrets);
503
507
  const effectiveSignals = mergeUnique(options.signals, registrySnapshot.signals);
508
+ /**
509
+ * Fold registered `vault.env.required` names into the vault boot options so
510
+ * boot reports missing env vars in the same gap list as missing secrets.
511
+ *
512
+ * @param base - Vault options from `oke({ vault })` / a boot override
513
+ */
514
+ const withRequiredEnv = (base: BootOptions["vault"]): BootOptions["vault"] => {
515
+ const names = [...new Set([...(base?.requiredEnv ?? []), ...registrySnapshot.requiredEnv])];
516
+ if (names.length === 0) return base;
517
+ return { ...(base ?? {}), requiredEnv: names };
518
+ };
504
519
  // Stay `undefined` (never a defined-but-empty object) when neither an
505
520
  // explicit `options.channel` nor a registered template exists — a defined
506
521
  // object here would flip `resolveElementNeeds`'s `channel` need to `true`
@@ -655,7 +670,7 @@ export function oke(options: OkeOptions): OkeApp {
655
670
  let bootResult: BootResult | undefined;
656
671
  let bootPromise: Promise<BootResult> | undefined;
657
672
  let readyState: ReadyState = "booting";
658
- let bootEnv: BootOptions["env"] = options.env ?? "local";
673
+ let bootEnv: BootOptions["env"] = options.env;
659
674
  let authBinding: AppAuthBinding | undefined =
660
675
  gateConfig.auth !== undefined
661
676
  ? createAppAuthBinding({
@@ -787,7 +802,7 @@ export function oke(options: OkeOptions): OkeApp {
787
802
  }
788
803
  }
789
804
 
790
- /** Boot-time orphan scan — `running`/`sleeping` runs with no live lease. */
805
+ /** Boot-time orphan scan — `running`/`sleeping`/`compensating` with no live lease. */
791
806
  async function resumeOrphanedDurableRuns(): Promise<void> {
792
807
  const { store } = activeJournal();
793
808
  if (!hasJournalLease(store)) return;
@@ -807,7 +822,15 @@ export function oke(options: OkeOptions): OkeApp {
807
822
  const { bootApplication, resolveElementNeeds } = await import("./boot.ts");
808
823
  const { assertHttpGatePosture } = await import("../elements/gate/boot.ts");
809
824
  const { assertPluginNeeds, buildAvailableNeedTokens } = await import("./plugin-needs.ts");
810
- bootEnv = overrides?.env ?? options.env ?? "local";
825
+ const dockerFlag =
826
+ overrides?.docker === true ||
827
+ (overrides?.docker !== false &&
828
+ (options.docker === true || (options.docker !== false && process.env.OKE_DOCKER === "1")));
829
+ // Mirror bootApplication: prod / compose-dev / else test (PGLite + memory).
830
+ bootEnv =
831
+ overrides?.env ??
832
+ options.env ??
833
+ (process.env.NODE_ENV === "production" ? "prod" : dockerFlag ? "dev" : "test");
811
834
 
812
835
  // Prod without a real secret fails even if construction minted a dev secret.
813
836
  if (gateConfig.auth?.secretMinted && bootEnv === "prod") {
@@ -830,7 +853,7 @@ export function oke(options: OkeOptions): OkeApp {
830
853
  config: overrides?.config ?? options.config,
831
854
  elements: overrides?.elements ?? options.elements,
832
855
  secrets: overrides?.secrets ?? effectiveSecrets,
833
- vault: overrides?.vault ?? options.vault,
856
+ vault: withRequiredEnv(overrides?.vault ?? options.vault),
834
857
  gates: [...baseGates, ...authGates],
835
858
  signals: overrides?.signals ?? effectiveSignals,
836
859
  clocks: overrides?.clocks ?? options.clocks,
@@ -900,7 +923,7 @@ export function oke(options: OkeOptions): OkeApp {
900
923
  rootDir: overrides?.rootDir ?? options.rootDir,
901
924
  elements: overrides?.elements ?? options.elements,
902
925
  secrets: [...baseSecrets, ...pluginSecrets],
903
- vault: overrides?.vault ?? options.vault,
926
+ vault: withRequiredEnv(overrides?.vault ?? options.vault),
904
927
  gates: [...baseGates, ...authGates, ...pluginGates],
905
928
  unguardedHttp,
906
929
  signals: [...baseSignals, ...pluginSignals],
@@ -1013,7 +1036,7 @@ export function oke(options: OkeOptions): OkeApp {
1013
1036
  registerFlow(flowDef);
1014
1037
 
1015
1038
  const i18nConfig = options.config?.i18n;
1016
- const configuredLocales = i18nConfig?.locales ?? ["en", "ar"];
1039
+ const configuredLocales = i18nConfig?.locales ?? ["en"];
1017
1040
  const defaultLocale = i18nConfig?.default ?? "en";
1018
1041
  const acceptLanguage = extras?.request?.headers.get("accept-language") ?? undefined;
1019
1042
  const resolvedLocale = matchConfiguredLocale(
@@ -1281,30 +1304,13 @@ export function oke(options: OkeOptions): OkeApp {
1281
1304
  });
1282
1305
  } else if (!sleeping && isTerminalFailure(result)) {
1283
1306
  const terminalErr = result.failure ?? result.ctx.error;
1284
- if (flowDef.compensate) {
1285
- try {
1286
- const completedSteps = journalSession.run.entries
1287
- .filter((e) => e.kind === "step")
1288
- .map((e) => e.name);
1289
- await flowDef.compensate(
1290
- {
1291
- input: ctx.input as never,
1292
- error: terminalErr,
1293
- completedSteps,
1294
- },
1295
- fx,
1296
- );
1297
- } catch (compErr) {
1298
- await journalSession.commit("failed", {
1299
- error: `compensate:${failureCodeOf(compErr)}`,
1300
- });
1301
- }
1302
- }
1303
- if (journalSession.run.status === "running") {
1304
- await journalSession.commit("failed", {
1305
- error: failureCodeOf(terminalErr),
1306
- });
1307
- }
1307
+ await runCompensationPhase({
1308
+ flow: flowDef,
1309
+ input: ctx.input,
1310
+ session: journalSession,
1311
+ fx,
1312
+ error: terminalErr,
1313
+ });
1308
1314
  } else if (!sleeping) {
1309
1315
  await journalSession.commit("completed", { output: result.output });
1310
1316
  }
@@ -1699,14 +1705,6 @@ function isTerminalFailure(result: PipelineResult): boolean {
1699
1705
  return result.ctx.error !== undefined && !isJournalSuspend(result.ctx.error);
1700
1706
  }
1701
1707
 
1702
- /** Machine-readable code for journal / Runs failure fields. */
1703
- function failureCodeOf(err: unknown): string {
1704
- if (isFlowFailure(err)) return err.error.code;
1705
- if (err instanceof Error && err.name && err.name !== "Error") return err.name;
1706
- if (err instanceof Error && err.message) return err.message.slice(0, 120);
1707
- return "Error";
1708
- }
1709
-
1710
1708
  /** Coerce a thrown value into a FlowFailure for Runs wide events. */
1711
1709
  function failureFromUnknown(err: unknown): FlowFailure {
1712
1710
  if (isFlowFailure(err)) return err;
@@ -40,7 +40,7 @@ describe("oke() auto-registry — stores/secrets/signals/channel.templates", ()
40
40
  sends: ["before-note-created"],
41
41
  },
42
42
  do: async (_input, fx) => {
43
- const secret = fx.vault(secretBefore);
43
+ const secret = await fx.vault.get(secretBefore);
44
44
  await fx.emit(signalBefore, {});
45
45
  await fx.send(templateBefore, { to: "you@localhost", data: {} });
46
46
  return { secret };
@@ -78,7 +78,7 @@ describe("oke() auto-registry — stores/secrets/signals/channel.templates", ()
78
78
  sends: ["after-note-created"],
79
79
  },
80
80
  do: async (_input, fx) => {
81
- const secret = fx.vault(secretAfter);
81
+ const secret = await fx.vault.get(secretAfter);
82
82
  await fx.emit(signalAfter, {});
83
83
  await fx.send(templateAfter, { to: "you@localhost", data: {} });
84
84
  return { secret };
@@ -16,15 +16,15 @@ describe("ai boot binder", () => {
16
16
  });
17
17
 
18
18
  test("resolveAiDriverId defaults to mock; honours config + OKE_AI_DRIVER in docker", () => {
19
- expect(resolveAiDriverId({}, "local")).toBe("mock");
20
- expect(resolveAiDriverId({ config: { drivers: { ai: { local: "ollama" } } } }, "local")).toBe(
19
+ expect(resolveAiDriverId({}, "test")).toBe("mock");
20
+ expect(resolveAiDriverId({ config: { drivers: { ai: { test: "ollama" } } } }, "test")).toBe(
21
21
  "ollama",
22
22
  );
23
23
 
24
24
  const prev = process.env.OKE_AI_DRIVER;
25
25
  process.env.OKE_AI_DRIVER = "ollama";
26
26
  try {
27
- expect(resolveAiDriverId({}, "local", true)).toBe("ollama");
27
+ expect(resolveAiDriverId({}, "test", true)).toBe("ollama");
28
28
  } finally {
29
29
  if (prev === undefined) delete process.env.OKE_AI_DRIVER;
30
30
  else process.env.OKE_AI_DRIVER = prev;
@@ -48,12 +48,12 @@ describe("ai boot binder", () => {
48
48
  test("bindAi uses injected defaultDriver over config (tests stay on mock)", () => {
49
49
  const runtime = bindAi(
50
50
  {
51
- config: { drivers: { ai: { local: "ollama" } } },
51
+ config: { drivers: { ai: { test: "ollama" } } },
52
52
  ai: { defaultDriver: mockAiDriver },
53
53
  },
54
54
  undefined,
55
55
  () => 0,
56
- "local",
56
+ "test",
57
57
  );
58
58
  expect(runtime.autoCacheDisabled).toBe(true);
59
59
  });
@@ -33,7 +33,7 @@ export function bindAi(
33
33
  options: BootOptions,
34
34
  gate: GateRuntime | undefined,
35
35
  now: () => number,
36
- env: ConfigEnv = "local",
36
+ env: ConfigEnv = "test",
37
37
  docker = false,
38
38
  ): AiRuntime {
39
39
  const id = resolveAiDriverId(options, env, docker);
@@ -115,6 +115,7 @@ export function openDefaultsFor(id: string, docker = false): AiOpenOptions {
115
115
  return {
116
116
  ...(apiKey ? { apiKey } : {}),
117
117
  ...(baseUrl ? { baseUrl } : {}),
118
+ ...(process.env.OKE_AI_MODEL?.trim() ? { model: process.env.OKE_AI_MODEL.trim() } : {}),
118
119
  };
119
120
  }
120
121
  return {};
@@ -58,23 +58,23 @@ describe("bindChannel driver resolution", () => {
58
58
  config: {
59
59
  drivers: {
60
60
  channel: {
61
- email: { local: "console", docker: "smtp", test: "console", prod: "sndr" },
62
- sms: { local: "console", docker: "taqnyat", test: "msegat", prod: "taqnyat" },
61
+ email: { dev: "smtp", test: "console", prod: "sndr" },
62
+ sms: { dev: "taqnyat", test: "msegat", prod: "taqnyat" },
63
63
  },
64
64
  },
65
65
  },
66
66
  };
67
67
 
68
68
  test("uses console locally and smtp in docker", () => {
69
- expect(resolveEmailDriverId(options, "local", false)).toBe("console");
70
- expect(resolveEmailDriverId(options, "docker", true)).toBe("smtp");
69
+ expect(resolveEmailDriverId(options, "test", false)).toBe("console");
70
+ expect(resolveEmailDriverId(options, "dev", true)).toBe("smtp");
71
71
  expect(resolveEmailDriverId(options, "prod", false)).toBe("sndr");
72
72
  });
73
73
 
74
74
  test("resolves sms driver ids", () => {
75
- expect(resolveSmsDriverId(options, "local")).toBe("console");
76
- expect(resolveSmsDriverId(options, "docker")).toBe("taqnyat");
77
75
  expect(resolveSmsDriverId(options, "test")).toBe("msegat");
76
+ expect(resolveSmsDriverId(options, "dev")).toBe("taqnyat");
77
+ expect(resolveSmsDriverId(options, "prod")).toBe("taqnyat");
78
78
  });
79
79
 
80
80
  test("parses SMTP_URL and optional auth overrides", () => {
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import { resolveDriverId, type ConfigEnv } from "../../config/index.ts";
6
+ import { CHANNEL_EMAIL_DEFAULTS, CHANNEL_SMS_DEFAULTS } from "../../config/driver-defaults.ts";
6
7
  import { openConsoleChannel } from "../../drivers/channel-console.ts";
7
8
  import { openMsegatChannel } from "../../drivers/channel-msegat.ts";
8
9
  import { openResendChannel } from "../../drivers/channel-resend.ts";
@@ -81,9 +82,9 @@ export function resolveEmailDriverId(
81
82
  env: ConfigEnv,
82
83
  docker: boolean,
83
84
  ): string {
84
- return (
85
- resolveDriverId(options.config?.drivers?.channel?.email, env) ?? (docker ? "smtp" : "console")
86
- );
85
+ void docker;
86
+ // Defaults cover every ConfigEnv key, so this is never undefined.
87
+ return resolveDriverId(options.config?.drivers?.channel?.email, env, CHANNEL_EMAIL_DEFAULTS)!;
87
88
  }
88
89
 
89
90
  /**
@@ -93,7 +94,7 @@ export function resolveEmailDriverId(
93
94
  * @param env - Active environment
94
95
  */
95
96
  export function resolveSmsDriverId(options: BootOptions, env: ConfigEnv): string | undefined {
96
- return resolveDriverId(options.config?.drivers?.channel?.sms, env);
97
+ return resolveDriverId(options.config?.drivers?.channel?.sms, env, CHANNEL_SMS_DEFAULTS);
97
98
  }
98
99
 
99
100
  /**
@@ -15,6 +15,7 @@ import {
15
15
  } from "../../elements/clock.ts";
16
16
  import { createPostgresCronStore } from "../../drivers/clock-postgres.ts";
17
17
  import { resolveDriverId, type ConfigEnv } from "../../config/index.ts";
18
+ import { CLOCK_DEFAULTS } from "../../config/driver-defaults.ts";
18
19
  import type { BootOptions } from "../boot.ts";
19
20
 
20
21
  /** Result of binding a clock runtime. */
@@ -33,9 +34,8 @@ export const DEFAULT_FILE_CRON_PATH = ".oke/crons.json";
33
34
  * @param env - Active environment
34
35
  */
35
36
  export function resolveClockDriverId(options: BootOptions, env: ConfigEnv): string {
36
- const resolved = resolveDriverId(options.config?.drivers?.clock, env);
37
- if (resolved) return resolved;
38
- return env === "test" ? "frozen" : "memory";
37
+ // Defaults cover every ConfigEnv key, so this is never undefined.
38
+ return resolveDriverId(options.config?.drivers?.clock, env, CLOCK_DEFAULTS)!;
39
39
  }
40
40
 
41
41
  /**
@@ -79,8 +79,8 @@ export async function bindClock(
79
79
  const url = process.env.DATABASE_URL ?? process.env.OKE_STORE_SQL_URL ?? undefined;
80
80
  if (!url) {
81
81
  throw new Error(
82
- env === "docker"
83
- ? 'oke boot: clock driver "postgres" needs DATABASE_URL (did `oke dev -d` write docker/.env.docker?)'
82
+ env === "dev"
83
+ ? 'oke boot: clock driver "postgres" needs DATABASE_URL (did `oke dev` write docker/.env.docker?)'
84
84
  : 'oke boot: clock driver "postgres" needs DATABASE_URL',
85
85
  );
86
86
  }
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import { resolveDriverId, type ConfigEnv } from "../../config/index.ts";
6
+ import { STORE_KV_DEFAULTS } from "../../config/driver-defaults.ts";
6
7
  import { memoryDrivers } from "../../drivers/memory.ts";
7
8
  import { redisDriver } from "../../drivers/redis.ts";
8
9
  import type { KvClientLike } from "../../drivers/types.ts";
@@ -27,10 +28,8 @@ export function resolveGateKvDriverId(
27
28
  const fromEnv = process.env.OKE_KV_DRIVER?.trim();
28
29
  if (docker && fromEnv === "redis") return "redis";
29
30
  if (docker && fromEnv === "memory") return "memory";
30
- const resolved = resolveDriverId(options.config?.drivers?.store?.kv, env);
31
- if (resolved === "redis") return "redis";
32
- if (resolved === "memory") return "memory";
33
- return docker ? "redis" : "memory";
31
+ const resolved = resolveDriverId(options.config?.drivers?.store?.kv, env, STORE_KV_DEFAULTS);
32
+ return resolved === "redis" ? "redis" : "memory";
34
33
  }
35
34
 
36
35
  function kvUrlFor(docker: boolean): string {
@@ -38,8 +37,8 @@ function kvUrlFor(docker: boolean): string {
38
37
  if (!url) {
39
38
  throw new Error(
40
39
  docker
41
- ? "oke boot: gate redis kv needs REDIS_URL (did `oke dev -d` write docker/.env.docker?)"
42
- : "oke boot: gate redis kv needs REDIS_URL — declare drivers.store.kv memory for local, or set REDIS_URL",
40
+ ? "oke boot: gate redis kv needs REDIS_URL (did `oke dev` write docker/.env.docker?)"
41
+ : "oke boot: gate redis kv needs REDIS_URL — declare drivers.store.kv memory for test, or set REDIS_URL",
43
42
  );
44
43
  }
45
44
  return url;
@@ -56,7 +55,7 @@ function kvUrlFor(docker: boolean): string {
56
55
  export async function bindGate(
57
56
  options: BootOptions,
58
57
  now: () => number,
59
- env: ConfigEnv = "local",
58
+ env: ConfigEnv = "test",
60
59
  docker = false,
61
60
  ): Promise<GateRuntime> {
62
61
  const kvId = resolveGateKvDriverId(options, env, docker);