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
package/src/cli/doctor.ts CHANGED
@@ -3,17 +3,76 @@
3
3
  */
4
4
 
5
5
  import { resolve } from "node:path";
6
+ import type {
7
+ ConfigEnv,
8
+ DriverRef,
9
+ DriversConfig,
10
+ EnvDriverMap,
11
+ VaultElementConfig,
12
+ } from "../config/index.ts";
13
+ import { resolveEffectiveDrivers, type EffectiveDriversConfig } from "../config/driver-defaults.ts";
6
14
  import type { Manifest } from "../manifest/types.ts";
7
15
  import { APP_PORT, CONSOLE_PORT, MCP_PORT } from "../runtime/types.ts";
8
16
  import { hasFlag, wantsJson } from "./args.ts";
9
17
  import { checkManifestPiiAsks } from "./doctor-pii.ts";
10
18
  import { EXIT_OK, EXIT_RUNTIME } from "./exit.ts";
11
- import { loadManifest } from "./load-config.ts";
19
+ import { loadManifest, loadOkeConfig } from "./load-config.ts";
12
20
  import { isPortInUse } from "./ports.ts";
13
21
  import { schemaFingerprint, readSchemaFingerprint } from "./schema.ts";
14
22
 
15
23
  export { isPortInUse } from "./ports.ts";
16
24
 
25
+ /**
26
+ * `label → merged EnvDriverMap` pairs, in the fixed order shown by
27
+ * {@link formatDriversSummary} / the `drivers` JSON key.
28
+ *
29
+ * @param drivers - Fully-resolved drivers config
30
+ */
31
+ function flattenEffectiveDrivers(
32
+ drivers: EffectiveDriversConfig,
33
+ ): ReadonlyArray<readonly [string, EnvDriverMap]> {
34
+ return [
35
+ ["store.sql", drivers.store.sql],
36
+ ["store.kv", drivers.store.kv],
37
+ ["store.files", drivers.store.files],
38
+ ["signal", drivers.signal],
39
+ ["clock", drivers.clock],
40
+ ["journal", drivers.journal],
41
+ ["vault", drivers.vault],
42
+ ["channel.email", drivers.channel.email],
43
+ ["channel.sms", drivers.channel.sms],
44
+ ];
45
+ }
46
+
47
+ /**
48
+ * @param ref - Driver ref (string id, rich `{ driver }` object, or unset)
49
+ */
50
+ function driverRefId(ref: DriverRef | undefined): string {
51
+ if (ref === undefined) return "—";
52
+ return typeof ref === "string" ? ref : ref.driver;
53
+ }
54
+
55
+ /**
56
+ * Render the fully-resolved drivers config as aligned text lines — every
57
+ * default plus every override, per env, for a developer whose own
58
+ * `oke.config.ts` only pins a handful of keys.
59
+ *
60
+ * @param drivers - Fully-resolved drivers config
61
+ * @param configEnv - Active env (marked in the active-driver column)
62
+ */
63
+ function formatDriversSummary(drivers: EffectiveDriversConfig, configEnv: ConfigEnv): string {
64
+ const rows = flattenEffectiveDrivers(drivers);
65
+ const nameWidth = Math.max(...rows.map(([name]) => name.length));
66
+ const activeWidth = Math.max(...rows.map(([, map]) => driverRefId(map[configEnv]).length));
67
+ const lines = [`drivers (env=${configEnv}):`];
68
+ for (const [name, map] of rows) {
69
+ const active = driverRefId(map[configEnv]);
70
+ const full = `dev=${driverRefId(map.dev)} test=${driverRefId(map.test)} prod=${driverRefId(map.prod)}`;
71
+ lines.push(` ${name.padEnd(nameWidth)} ${active.padEnd(activeWidth)} { ${full} }`);
72
+ }
73
+ return lines.join("\n");
74
+ }
75
+
17
76
  /** One doctor finding. */
18
77
  export interface DoctorFinding {
19
78
  readonly code:
@@ -23,7 +82,8 @@ export interface DoctorFinding {
23
82
  | "db_drift"
24
83
  | "tenancy"
25
84
  | "driver"
26
- | "pii_ask";
85
+ | "pii_ask"
86
+ | "vault_master_key";
27
87
  readonly severity: "error" | "warn";
28
88
  readonly message: string;
29
89
  }
@@ -55,6 +115,23 @@ export interface DoctorOptions {
55
115
  }>;
56
116
  /** Skip live DB drift probe (default false). */
57
117
  readonly skipDbDrift?: boolean;
118
+ /**
119
+ * Inject the `drivers` block directly (tests) — skips loading
120
+ * `oke.config.ts` from disk. When omitted, `oke doctor` best-effort loads
121
+ * `oke.config.ts` from {@link cwd}; a project without one just gets the
122
+ * real, untouched defaults in the drivers summary.
123
+ */
124
+ readonly driversConfig?: DriversConfig;
125
+ /**
126
+ * Inject the `vault` block directly (tests). When omitted, it is read
127
+ * from the same best-effort `oke.config.ts` load as {@link driversConfig}.
128
+ */
129
+ readonly vaultConfig?: VaultElementConfig;
130
+ /**
131
+ * Active {@link ConfigEnv} for the drivers summary. Default: `dev` when
132
+ * `OKE_DOCKER=1` (via {@link env}), else `test`.
133
+ */
134
+ readonly configEnv?: ConfigEnv;
58
135
  readonly write?: (text: string) => void;
59
136
  /** Write hints / progress (defaults to stderr). */
60
137
  readonly writeErr?: (text: string) => void;
@@ -92,6 +169,25 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
92
169
  const env = options.env ?? ((k) => Bun.env[k] ?? process.env[k]);
93
170
  const secretNames = options.secrets ?? (manifest?.vault ? Object.keys(manifest.vault) : []);
94
171
 
172
+ // Fully-resolved `drivers.*` — real defaults + every override merged, so
173
+ // the picture is complete even when `oke.config.ts` only pins one key.
174
+ let driversConfig = options.driversConfig;
175
+ let vaultConfig = options.vaultConfig;
176
+ if (driversConfig === undefined || vaultConfig === undefined) {
177
+ try {
178
+ const loaded = (await loadOkeConfig(cwd)).config;
179
+ driversConfig ??= loaded.drivers;
180
+ vaultConfig ??= loaded.vault;
181
+ } catch {
182
+ // No oke.config.ts (or it failed to load) — show real defaults only.
183
+ }
184
+ }
185
+ // Match `bootApplication`: production NODE_ENV → prod; compose → dev; else test.
186
+ const configEnv: ConfigEnv =
187
+ options.configEnv ??
188
+ (env("NODE_ENV") === "production" ? "prod" : env("OKE_DOCKER") === "1" ? "dev" : "test");
189
+ const effectiveDrivers = resolveEffectiveDrivers(driversConfig);
190
+
95
191
  for (const name of secretNames) {
96
192
  const value = env(name);
97
193
  if (value === undefined || value.length === 0) {
@@ -104,6 +200,25 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
104
200
  }
105
201
  }
106
202
 
203
+ // An env-var master key is only as protected as the process environment:
204
+ // it lands in shell history, orchestrator manifests, and crash dumps.
205
+ const usesBuiltinVault =
206
+ vaultConfig !== undefined || driverRefId(effectiveDrivers.vault[configEnv]) === "vault";
207
+ if (
208
+ configEnv === "prod" &&
209
+ usesBuiltinVault &&
210
+ (vaultConfig?.encryption?.masterKey?.kind ?? "env") === "env"
211
+ ) {
212
+ findings.push({
213
+ code: "vault_master_key",
214
+ // Fail closed in prod: an env-sourced master key collapses envelope
215
+ // protection the moment DB + env are stolen together.
216
+ severity: "error",
217
+ message:
218
+ 'vault master key comes from an environment variable in prod — set vault.encryption.masterKey = { kind: "kms", keyId } (or use managed aws-secrets-manager) so the key is never readable from the process environment',
219
+ });
220
+ }
221
+
107
222
  const ports = options.ports ?? [APP_PORT, CONSOLE_PORT, MCP_PORT];
108
223
  const probe = options.isPortInUse ?? isPortInUse;
109
224
  for (const port of ports) {
@@ -133,13 +248,13 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
133
248
  code: "schema_drift",
134
249
  severity: "error",
135
250
  message:
136
- "core stub schema drift — run `oke schema generate` (schema/oke.ts; not domain migrations)",
251
+ "core stub schema drift — run `oke schema generate` (.oke/schema/oke.ts; not domain migrations)",
137
252
  });
138
253
  } else if (expected !== null && current === null) {
139
254
  findings.push({
140
255
  code: "schema_drift",
141
256
  severity: "error",
142
- message: "schema/oke.ts missing — run `oke schema generate` (core/plugin stubs)",
257
+ message: ".oke/schema/oke.ts missing — run `oke schema generate` (core/plugin stubs)",
143
258
  });
144
259
  }
145
260
  }
@@ -157,7 +272,7 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
157
272
  severity: "error",
158
273
  message:
159
274
  drift.detail ??
160
- "domain schema differs from the database — run `oke db generate` then `oke db migrate` (or `oke db push` in local)",
275
+ "domain schema differs from the database — run `oke db generate` then `oke db migrate` (or `oke db push` in dev)",
161
276
  });
162
277
  }
163
278
  }
@@ -177,7 +292,9 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
177
292
  const code = ok ? EXIT_OK : EXIT_RUNTIME;
178
293
 
179
294
  if (json) {
180
- write(`${JSON.stringify({ ok, findings }, null, 2)}\n`);
295
+ write(
296
+ `${JSON.stringify({ ok, findings, drivers: { env: configEnv, resolved: effectiveDrivers } }, null, 2)}\n`,
297
+ );
181
298
  if (!ok) {
182
299
  writeErr("Hint: fix error-severity findings, then re-run oke doctor.\n");
183
300
  }
@@ -186,6 +303,7 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
186
303
 
187
304
  if (findings.length === 0) {
188
305
  write("oke doctor: ok\n");
306
+ write(`${formatDriversSummary(effectiveDrivers, configEnv)}\n`);
189
307
  return { code: EXIT_OK, findings };
190
308
  }
191
309
 
@@ -193,6 +311,7 @@ export async function runDoctor(options: DoctorOptions = {}): Promise<{
193
311
  for (const f of findings) {
194
312
  write(` [${f.severity}] ${f.code}: ${f.message}\n`);
195
313
  }
314
+ write(`${formatDriversSummary(effectiveDrivers, configEnv)}\n`);
196
315
  return { code, findings };
197
316
  }
198
317
 
@@ -208,16 +327,30 @@ export async function doctorCli(args: readonly string[]): Promise<number> {
208
327
  }
209
328
 
210
329
  let manifestPath: string | undefined;
330
+ let configEnv: ConfigEnv | undefined;
211
331
  const json = wantsJson(args);
212
332
  for (let i = 0; i < args.length; i++) {
213
333
  const a = args[i]!;
214
334
  if (a === "--manifest" || a === "-m") manifestPath = args[++i];
215
- else if (a === "--help" || a === "-h") {
216
- console.log(`oke doctor [--manifest|-m path] [--json|-j]
335
+ else if (a === "--env" || a === "-e") {
336
+ const value = args[++i];
337
+ if (value === "dev" || value === "test" || value === "prod") {
338
+ configEnv = value;
339
+ } else if (value === "local") {
340
+ configEnv = "test";
341
+ } else if (value === "docker") {
342
+ configEnv = "dev";
343
+ }
344
+ } else if (a === "--help" || a === "-h") {
345
+ console.log(`oke doctor [--manifest|-m path] [--env|-e dev|test|prod] [--json|-j]
217
346
  oke doctor --diff|-d [--before|-b <path> --after|-a <path>] [--base|-B <branch>]
218
347
 
219
348
  Verify secrets, ports, schema drift, and PII→model egress before serving.
349
+ Also prints drivers.* fully resolved (every default + every override merged)
350
+ for the active env, from oke.config.ts.
220
351
 
352
+ --env Env to resolve drivers for (default: prod when NODE_ENV=production,
353
+ dev when OKE_DOCKER=1 / \`oke dev\`, else test).
221
354
  --diff CI gate: block undeclared contract breaks (Manifest Diff).
222
355
  Default baseline is git merge-base (main/master) vs the working
223
356
  tree; pass --before/--after for an explicit comparison.
@@ -227,6 +360,6 @@ Verify secrets, ports, schema drift, and PII→model egress before serving.
227
360
  return EXIT_OK;
228
361
  }
229
362
  }
230
- const { code } = await runDoctor({ manifestPath, json });
363
+ const { code } = await runDoctor({ manifestPath, configEnv, json });
231
364
  return code;
232
365
  }
@@ -24,22 +24,20 @@ async function tempDir(): Promise<string> {
24
24
  const dualConfig = {
25
25
  drivers: {
26
26
  store: {
27
- sql: { local: "sqlite", docker: "postgres", prod: "postgres", test: "memory" },
27
+ sql: { dev: "postgres", test: "pglite", prod: "postgres" },
28
28
  },
29
29
  },
30
30
  } as unknown as OkeConfig;
31
31
 
32
32
  describe("resolveDrizzleKitEnv", () => {
33
- test("local env → sqlite dialect + sqlite url", async () => {
33
+ test("test env → postgresql dialect (pglite)", async () => {
34
34
  const dir = await tempDir();
35
- const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "local");
36
- expect(ctx.dialect).toBe("sqlite");
37
- expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("sqlite");
38
- expect(ctx.overlay.OKE_SQLITE_URL).toContain(".oke/app.sqlite");
39
- expect(ctx.overlay.DATABASE_URL).toBeUndefined();
35
+ const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "test");
36
+ expect(ctx.dialect).toBe("postgresql");
37
+ expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("postgresql");
40
38
  });
41
39
 
42
- test("docker env → postgresql dialect + DATABASE_URL from compose env", async () => {
40
+ test("dev env → postgresql dialect + DATABASE_URL from compose env", async () => {
43
41
  const dir = await tempDir();
44
42
  await mkdir(join(dir, "docker"), { recursive: true });
45
43
  await writeFile(
@@ -49,7 +47,7 @@ describe("resolveDrizzleKitEnv", () => {
49
47
  const prevDb = process.env["DATABASE_URL"];
50
48
  delete process.env["DATABASE_URL"];
51
49
  try {
52
- const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "docker");
50
+ const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "dev");
53
51
  expect(ctx.dialect).toBe("postgresql");
54
52
  expect(ctx.overlay.OKE_DRIZZLE_DIALECT).toBe("postgresql");
55
53
  expect(ctx.overlay.DATABASE_URL).toBe("postgres://u:p@127.0.0.1:5432/oke");
@@ -60,16 +58,16 @@ describe("resolveDrizzleKitEnv", () => {
60
58
  }
61
59
  });
62
60
 
63
- test("docker dialect is postgresql even when DATABASE_URL unset (no URL guessing)", async () => {
61
+ test("dev dialect is postgresql even when DATABASE_URL unset (no URL guessing)", async () => {
64
62
  const dir = await tempDir();
65
- const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "docker");
63
+ const ctx = await resolveDrizzleKitEnv(dir, dualConfig, "dev");
66
64
  expect(ctx.dialect).toBe("postgresql");
67
65
  });
68
66
 
69
67
  test("unsupported driver id errors clearly", async () => {
70
68
  const dir = await tempDir();
71
- const bad = { drivers: { store: { sql: { local: "memory" } } } } as unknown as OkeConfig;
72
- await expect(resolveDrizzleKitEnv(dir, bad, "local")).rejects.toThrow(/not supported/);
69
+ const bad = { drivers: { store: { sql: { test: "memory" } } } } as unknown as OkeConfig;
70
+ await expect(resolveDrizzleKitEnv(dir, bad, "test")).rejects.toThrow(/not supported/);
73
71
  });
74
72
  });
75
73
 
@@ -64,36 +64,43 @@ export async function applyComposeEnvToProcess(cwd: string): Promise<readonly st
64
64
  * Dialect comes from the configured driver via the exhaustive
65
65
  * `SQL_DRIVER_TO_DRIZZLE_DIALECT` map — a missing/unsupported driver id is a
66
66
  * compile-time gap there, and a clear runtime error here (never a silent
67
- * sqlite fallback).
67
+ * fallback).
68
68
  *
69
69
  * @param cwd - Project root
70
- * @param config - Loaded `oke.config.ts` (null → sqlite/local defaults)
71
- * @param env - Active env (`local` | `docker` for dev tooling)
70
+ * @param config - Loaded `oke.config.ts` (null → postgres/dev defaults)
71
+ * @param env - Active {@link ConfigEnv}
72
72
  */
73
73
  export async function resolveDrizzleKitEnv(
74
74
  cwd: string,
75
75
  config: OkeConfig | null | undefined,
76
76
  env: ConfigEnv,
77
77
  ): Promise<DrizzleKitEnvContext> {
78
- const driverId = resolveDriverId(config?.drivers?.store?.sql, env) ?? "sqlite";
79
- if (driverId !== "sqlite" && driverId !== "postgres") {
78
+ const driverId = resolveDriverId(config?.drivers?.store?.sql, env) ?? "postgres";
79
+ if (driverId !== "postgres" && driverId !== "pglite") {
80
80
  throw new Error(
81
- `oke db: store.sql driver "${driverId}" is not supported by drizzle-kit (sqlite | postgres)`,
81
+ `oke db: store.sql driver "${driverId}" is not supported by drizzle-kit (postgres | pglite)`,
82
82
  );
83
83
  }
84
84
  const dialect = drizzleDialectFromSqlDriver(driverId);
85
85
 
86
- const compose = env === "docker" ? await readComposeEnv(cwd) : new Map<string, string>();
86
+ const compose = env === "dev" ? await readComposeEnv(cwd) : new Map<string, string>();
87
87
 
88
88
  const overlay: Record<string, string> = { OKE_DRIZZLE_DIALECT: dialect };
89
- if (dialect === "postgresql") {
89
+ if (driverId === "pglite") {
90
90
  const url =
91
+ process.env.OKE_PGLITE_URL ??
92
+ compose.get("OKE_PGLITE_URL") ??
93
+ (env === "test" ? "memory://" : ".oke/pgdata");
94
+ overlay.OKE_PGLITE_URL = url;
95
+ // drizzle-kit still wants a postgres-shaped URL for dialect postgresql when
96
+ // not using PGlite kit plugins — prefer DATABASE_URL from compose for push.
97
+ const pgUrl =
91
98
  process.env.DATABASE_URL ?? compose.get("DATABASE_URL") ?? compose.get("OKE_STORE_SQL_URL");
92
- if (url !== undefined) overlay.DATABASE_URL = url;
99
+ if (pgUrl !== undefined) overlay.DATABASE_URL = pgUrl;
93
100
  } else {
94
101
  const url =
95
- process.env.OKE_SQLITE_URL ?? compose.get("OKE_SQLITE_URL") ?? "file:.oke/app.sqlite";
96
- overlay.OKE_SQLITE_URL = url;
102
+ process.env.DATABASE_URL ?? compose.get("DATABASE_URL") ?? compose.get("OKE_STORE_SQL_URL");
103
+ if (url !== undefined) overlay.DATABASE_URL = url;
97
104
  }
98
105
  return { env, dialect, overlay };
99
106
  }
@@ -5,32 +5,29 @@
5
5
 
6
6
  import { resolve } from "node:path";
7
7
 
8
- /** Mode-aware template (matches create-oke templates' drizzle.config.ts). */
8
+ /** PostgreSQL-only template (matches create-oke templates' drizzle.config.ts). */
9
9
  const DRIZZLE_CONFIG_TEMPLATE = `import { defineConfig } from "drizzle-kit";
10
10
 
11
11
  /**
12
12
  * Domain schema sync for \`oke db push|generate|migrate\`.
13
13
  *
14
- * Dialect comes from OKE's \`store.sql\` driver map (resolved by the CLI and
15
- * injected as \`OKE_DRIZZLE_DIALECT\`) never inferred from \`DATABASE_URL\`
16
- * presence. \`src/db/schema.generated.ts\` is emitted from \`src/db/schema.decl.ts\`
17
- * for the active dialect as a pre-step of \`oke db\` / \`oke dev\`.
14
+ * SQL is PostgreSQL-only (postgres / pglite). Domain tables come from
15
+ * \`src/db/schema.decl.ts\` \`src/db/schema.drizzle.ts\` (emitted as a
16
+ * pre-step of \`oke db\` / \`oke dev\`).
18
17
  *
19
- * Versioned prod migrations land in \`./src/db/migrations\` never mix with \`.oke/\`.
18
+ * System / auth / plugin stubs are separate: \`oke schema generate\` →
19
+ * \`.oke/schema/oke.ts\` (gitignored with \`.oke/\`). Re-run after adding plugins.
20
+ *
21
+ * Versioned prod migrations land in \`./src/db/migrations\`.
20
22
  */
21
- const dialect = (process.env.OKE_DRIZZLE_DIALECT ?? "sqlite") as "sqlite" | "postgresql";
22
-
23
23
  export default defineConfig({
24
- dialect,
25
- schema: "./src/db/schema.generated.ts",
24
+ dialect: "postgresql",
25
+ schema: "./src/db/schema.drizzle.ts",
26
26
  out: "./src/db/migrations",
27
27
  // Core runtime tables (oke_crons, …) are created by drivers — not domain schema.
28
28
  tablesFilter: ["!oke_*"],
29
29
  dbCredentials: {
30
- url:
31
- dialect === "postgresql"
32
- ? (process.env.DATABASE_URL ?? process.env.OKE_STORE_SQL_URL!)
33
- : (process.env.OKE_SQLITE_URL ?? "file:.oke/app.sqlite"),
30
+ url: process.env.DATABASE_URL ?? process.env.OKE_STORE_SQL_URL!,
34
31
  },
35
32
  });
36
33
  `;
@@ -15,23 +15,23 @@ import {
15
15
  const SAMPLE: OkeConfig = {
16
16
  drivers: {
17
17
  store: {
18
- sql: { local: "sqlite", docker: "postgres", prod: "postgres" },
19
- kv: { local: "memory", docker: "redis", prod: "redis" },
18
+ sql: { dev: "postgres", test: "pglite", prod: "postgres" },
19
+ kv: { dev: "redis", test: "memory", prod: "redis" },
20
20
  },
21
- signal: { local: "memory", docker: "redis", prod: "redis" },
22
- clock: { local: "memory", docker: "file", prod: "file" },
23
- vault: { local: "env", docker: "openbao", prod: "openbao" },
21
+ signal: { dev: "redis", test: "memory", prod: "redis" },
22
+ clock: { dev: "file", test: "memory", prod: "file" },
23
+ vault: { dev: "vault", test: "env", prod: "vault" },
24
24
  channel: {
25
- email: { local: "console", docker: "smtp", prod: "smtp" },
25
+ email: { dev: "smtp", test: "console", prod: "smtp" },
26
26
  },
27
- ai: { local: "mock", docker: "mock" },
27
+ ai: { dev: "mock", test: "mock" },
28
28
  },
29
29
  };
30
30
 
31
31
  describe("hero-meta", () => {
32
- test("resolveDevProfile maps docker and NODE_ENV", () => {
33
- expect(resolveDevProfile({ docker: false })).toBe("local");
34
- expect(resolveDevProfile({ docker: true })).toBe("docker");
32
+ test("resolveDevProfile maps NODE_ENV (docker flag ignored)", () => {
33
+ expect(resolveDevProfile({ docker: false })).toBe("dev");
34
+ expect(resolveDevProfile({ docker: true })).toBe("dev");
35
35
  expect(resolveDevProfile({ docker: false, nodeEnv: "test" })).toBe("test");
36
36
  expect(resolveDevProfile({ docker: false, nodeEnv: "production" })).toBe("production");
37
37
  });
@@ -48,12 +48,12 @@ describe("hero-meta", () => {
48
48
  "channel",
49
49
  "ai",
50
50
  ]);
51
- expect(rows.find((r) => r.element === "store")?.detail).toContain("sqlite");
51
+ expect(rows.find((r) => r.element === "store")?.detail).toContain("pglite");
52
52
  expect(rows.find((r) => r.element === "flow")?.detail).toBe("");
53
53
  expect(rows.find((r) => r.element === "flow")?.status).toBe("ready");
54
54
  });
55
55
 
56
- test("docker profile applies to every element", () => {
56
+ test("dev (compose) profile applies to every element", () => {
57
57
  const rows = resolveHeroElements(SAMPLE, {
58
58
  docker: true,
59
59
  sqlDriver: "postgres",
@@ -62,7 +62,7 @@ describe("hero-meta", () => {
62
62
  const store = rows.find((r) => r.element === "store")?.detail ?? "";
63
63
  expect(store).toContain("postgres");
64
64
  expect(store).toContain("redis");
65
- expect(store).not.toContain("sqlite");
65
+ expect(store).not.toContain("pglite");
66
66
  expect(rows.find((r) => r.element === "signal")?.detail).toBe("redis");
67
67
  expect(rows.find((r) => r.element === "clock")?.detail).toBe("file");
68
68
  expect(rows.find((r) => r.element === "gate")?.detail).toBe("redis");
@@ -78,8 +78,8 @@ describe("hero-meta", () => {
78
78
  version: "0.2.4",
79
79
  nodeEnv: "development",
80
80
  });
81
- expect(snap.profile).toBe("docker");
82
- expect(snap.runtimeEnv).toBe("local");
81
+ expect(snap.profile).toBe("dev");
82
+ expect(snap.runtimeEnv).toBe("dev");
83
83
  expect(decodeHeroSnapshot(encodeHeroSnapshot(snap))).toEqual(snap);
84
84
  });
85
85
 
@@ -11,11 +11,11 @@ import {
11
11
  } from "../config/index.ts";
12
12
  import type { DevStatus } from "../term.ts";
13
13
 
14
- /** How the process is running (local laptop → production). */
15
- export type DevRuntimeProfile = "local" | "docker" | "test" | "production";
14
+ /** How the process is running (dev Compose → production). */
15
+ export type DevRuntimeProfile = "dev" | "test" | "production";
16
16
 
17
- /** Data plane: still on a laptop vs real production. */
18
- export type DevRuntimeEnv = "local" | "production";
17
+ /** Data plane: still on a laptop vs production deploy. */
18
+ export type DevRuntimeEnv = "dev" | "production";
19
19
 
20
20
  /** One of the eight elements with its active driver summary. */
21
21
  export type HeroElementRow = {
@@ -69,16 +69,17 @@ export function resolveDevProfile(options: {
69
69
  }): DevRuntimeProfile {
70
70
  if (options.nodeEnv === "test") return "test";
71
71
  if (options.nodeEnv === "production") return "production";
72
- return options.docker ? "docker" : "local";
72
+ void options.docker;
73
+ return "dev";
73
74
  }
74
75
 
75
76
  /**
76
- * Data-plane environment (local machine vs production deploy).
77
+ * Data-plane environment (dev machine vs production deploy).
77
78
  *
78
79
  * @param profile - Runtime profile
79
80
  */
80
81
  export function resolveDevRuntimeEnv(profile: DevRuntimeProfile): DevRuntimeEnv {
81
- return profile === "production" ? "production" : "local";
82
+ return profile === "production" ? "production" : "dev";
82
83
  }
83
84
 
84
85
  /**
@@ -155,13 +156,13 @@ export function resolveHeroElements(
155
156
  readonly aiModel?: string;
156
157
  /** AI readiness when known (model loading / ready). */
157
158
  readonly aiStatus?: DevStatus;
158
- /** Which driver map column to read (default `local`). */
159
+ /** Which driver map column to read (default `test` unless `docker`). */
159
160
  readonly configEnv?: ConfigEnv;
160
161
  },
161
162
  ): readonly HeroElementRow[] {
162
163
  const drivers = config?.drivers;
163
164
  // `-d` reads the `docker` profile for every element (not a store-only override).
164
- const configEnv: ConfigEnv = options.configEnv ?? (options.docker ? "docker" : "local");
165
+ const configEnv: ConfigEnv = options.configEnv ?? (options.docker ? "dev" : "test");
165
166
 
166
167
  // Gate rates share `drivers.store.kv` (no separate drivers.gate).
167
168
  const gateKv =
package/src/cli/index.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
3
  * `oke` CLI entry.
4
+ *
5
+ * Bare `oke` (TTY) → interactive Ink TUI. `oke <command>` runs directly.
4
6
  */
5
7
 
6
8
  import { aiCli } from "./ai.ts";
@@ -23,11 +25,32 @@ import { replayCli } from "./replay.ts";
23
25
  import { schemaCli } from "./schema.ts";
24
26
  import { stackCli } from "./stack.ts";
25
27
  import { startCli } from "./start.ts";
28
+ import { testCli } from "./test.ts";
26
29
  import { upgradeCli } from "./upgrade.ts";
27
30
  import { vaultCli } from "./vault-cmd.ts";
28
31
 
29
- const argv = process.argv.slice(2);
32
+ const rawArgv = process.argv.slice(2);
33
+ const wantTui = rawArgv.includes("--tui");
34
+ const argv = rawArgv.filter((a) => a !== "--tui");
30
35
  const [cmd, sub, ...rest] = argv;
36
+ /** Nested TUI / slash child — never re-enter the interactive shell. */
37
+ const noTui = process.env["OKE_NO_TUI"] === "1";
38
+
39
+ /**
40
+ * Launch interactive TUI when allowed; otherwise print help (non-TTY).
41
+ */
42
+ async function maybeLaunchTui(): Promise<never> {
43
+ const { canRenderTui, launchTui } = await import("./tui/launch.ts");
44
+ if (noTui || !canRenderTui(process.stdout)) {
45
+ console.log(formatOkeHelp());
46
+ process.exit(EXIT_USAGE);
47
+ }
48
+ process.exit(await launchTui(process.cwd()));
49
+ }
50
+
51
+ if (wantTui && !noTui) {
52
+ await maybeLaunchTui();
53
+ }
31
54
 
32
55
  if (cmd === "dev") {
33
56
  process.exit(await devCli(sub ? [sub, ...rest] : rest));
@@ -37,6 +60,10 @@ if (cmd === "mode") {
37
60
  process.exit(await modeCli(sub ? [sub, ...rest] : rest));
38
61
  }
39
62
 
63
+ if (cmd === "test") {
64
+ process.exit(await testCli(sub ? [sub, ...rest] : rest));
65
+ }
66
+
40
67
  if (cmd === "start") {
41
68
  process.exit(await startCli(sub ? [sub, ...rest] : rest));
42
69
  }
@@ -109,11 +136,14 @@ if (cmd === "completion") {
109
136
  process.exit(completionCli(sub ? [sub, ...rest] : rest));
110
137
  }
111
138
 
112
- if (cmd === undefined || cmd === "--help" || cmd === "-h" || cmd === "help") {
113
- // Bare `oke` — commands only. Exit-code table only on explicit --help.
114
- const help = cmd === undefined ? formatOkeHelp() : `${formatOkeHelp()}${EXIT_CODE_HELP}`;
115
- console.log(help);
116
- process.exit(cmd ? EXIT_OK : EXIT_USAGE);
139
+ if (cmd === "--help" || cmd === "-h" || cmd === "help") {
140
+ console.log(`${formatOkeHelp()}${EXIT_CODE_HELP}`);
141
+ process.exit(EXIT_OK);
142
+ }
143
+
144
+ if (cmd === undefined) {
145
+ // Bare `oke` — TUI on TTY; help + EXIT_USAGE when piped / CI / OKE_NO_TUI.
146
+ await maybeLaunchTui();
117
147
  }
118
148
 
119
149
  console.error(`Unknown command: ${cmd}${sub ? ` ${sub}` : ""}`);