okengine 0.2.6 → 0.3.2

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 (681) hide show
  1. package/AGENTS.md +33 -30
  2. package/README.md +74 -340
  3. package/{spec/manifest.v1.schema.json → manifest.v1.schema.json} +40 -9
  4. package/package.json +47 -44
  5. package/site/content/docs/ai/llms-txt.mdx +54 -0
  6. package/site/content/docs/ai/mcp.mdx +123 -0
  7. package/site/content/docs/ai/meta.json +5 -0
  8. package/site/content/docs/ai/skills.mdx +53 -0
  9. package/site/content/docs/console/access.mdx +29 -0
  10. package/site/content/docs/console/ai.mdx +35 -0
  11. package/site/content/docs/console/architecture.mdx +35 -0
  12. package/site/content/docs/console/channels.mdx +37 -0
  13. package/site/content/docs/console/clock.mdx +31 -0
  14. package/site/content/docs/console/flows.mdx +31 -0
  15. package/site/content/docs/console/gates.mdx +35 -0
  16. package/site/content/docs/console/manifest-diff.mdx +34 -0
  17. package/site/content/docs/console/meta.json +23 -0
  18. package/site/content/docs/console/overview.mdx +40 -0
  19. package/site/content/docs/console/plugins.mdx +41 -0
  20. package/site/content/docs/console/privacy.mdx +32 -0
  21. package/site/content/docs/console/runs.mdx +40 -0
  22. package/site/content/docs/console/signals.mdx +31 -0
  23. package/site/content/docs/console/store.mdx +32 -0
  24. package/site/content/docs/console/tenancy.mdx +32 -0
  25. package/site/content/docs/console/traces.mdx +34 -0
  26. package/site/content/docs/console/vault.mdx +37 -0
  27. package/site/content/docs/elements/ai.mdx +180 -0
  28. package/site/content/docs/elements/channel.mdx +167 -0
  29. package/site/content/docs/elements/clock.mdx +182 -0
  30. package/site/content/docs/elements/flow.mdx +288 -0
  31. package/site/content/docs/elements/gate.mdx +171 -0
  32. package/site/content/docs/elements/meta.json +5 -0
  33. package/site/content/docs/elements/signal.mdx +171 -0
  34. package/site/content/docs/elements/store.mdx +320 -0
  35. package/site/content/docs/elements/vault.mdx +263 -0
  36. package/site/content/docs/get-started/basic-usage.mdx +124 -0
  37. package/site/content/docs/get-started/comparison.mdx +65 -0
  38. package/site/content/docs/get-started/installation.mdx +113 -0
  39. package/site/content/docs/get-started/introduction.mdx +123 -0
  40. package/site/content/docs/get-started/meta.json +5 -0
  41. package/site/content/docs/index.mdx +63 -0
  42. package/site/content/docs/meta.json +5 -0
  43. package/site/content/docs/plugins/compression.mdx +60 -0
  44. package/site/content/docs/plugins/cors.mdx +92 -0
  45. package/site/content/docs/plugins/csrf.mdx +96 -0
  46. package/site/content/docs/plugins/ip-allowlist.mdx +92 -0
  47. package/site/content/docs/plugins/maintenance-mode.mdx +101 -0
  48. package/site/content/docs/plugins/meta.json +15 -0
  49. package/site/content/docs/plugins/security-headers.mdx +136 -0
  50. package/site/content/docs/reference/cli.md +101 -0
  51. package/site/content/docs/reference/configuration.mdx +159 -0
  52. package/site/content/docs/reference/environment-variables.mdx +87 -0
  53. package/site/content/docs/reference/errors.mdx +80 -0
  54. package/site/content/docs/reference/fx.mdx +117 -0
  55. package/site/content/docs/reference/meta.json +5 -0
  56. package/site/content/docs/reference/plugins.mdx +249 -0
  57. package/site/content/docs/reference/security.md +63 -0
  58. package/src/auth/api-keys.ts +2 -5
  59. package/src/auth/attenuation.ts +2 -6
  60. package/src/auth/auth.test.ts +56 -31
  61. package/src/auth/invoke-as.ts +1 -3
  62. package/src/auth/operator.ts +8 -26
  63. package/src/auth/planes.ts +2 -7
  64. package/src/auth/plugin.ts +4 -7
  65. package/src/auth/roles.ts +1 -5
  66. package/src/auth/sessions.ts +10 -39
  67. package/src/cli/args.ts +1 -5
  68. package/src/cli/ask-dev-mode.ts +40 -0
  69. package/src/cli/build.ts +1 -2
  70. package/src/cli/client-add.test.ts +8 -15
  71. package/src/cli/client-add.ts +5 -14
  72. package/src/cli/completion.test.ts +1 -5
  73. package/src/cli/completion.ts +4 -17
  74. package/src/cli/db-auto-push.test.ts +64 -0
  75. package/src/cli/db-auto-push.ts +72 -0
  76. package/src/cli/db.test.ts +189 -0
  77. package/src/cli/db.ts +456 -0
  78. package/src/cli/dev-app-runner.ts +2 -8
  79. package/src/cli/dev-db-push.test.ts +193 -0
  80. package/src/cli/dev-mode.test.ts +45 -0
  81. package/src/cli/dev-mode.ts +78 -0
  82. package/src/cli/dev-schema-sync.test.ts +66 -0
  83. package/src/cli/dev-schema-sync.ts +139 -0
  84. package/src/cli/dev.test.ts +264 -15
  85. package/src/cli/dev.ts +355 -139
  86. package/src/cli/doc-staleness.test.ts +69 -0
  87. package/src/cli/docker-cli.test.ts +27 -8
  88. package/src/cli/docker.ts +11 -3
  89. package/src/cli/doctor-diff.test.ts +1 -5
  90. package/src/cli/doctor-diff.ts +17 -46
  91. package/src/cli/doctor-pii.ts +2 -7
  92. package/src/cli/doctor.test.ts +26 -4
  93. package/src/cli/doctor.ts +35 -11
  94. package/src/cli/drizzle-env.test.ts +67 -0
  95. package/src/cli/drizzle-env.ts +78 -0
  96. package/src/cli/ensure-drizzle-config.ts +50 -0
  97. package/src/cli/eval.ts +4 -14
  98. package/src/cli/gates-list.ts +1 -4
  99. package/src/cli/hero-meta.test.ts +17 -19
  100. package/src/cli/hero-meta.ts +20 -30
  101. package/src/cli/images.ts +3 -8
  102. package/src/cli/index.ts +11 -2
  103. package/src/cli/load-config.images.test.ts +14 -14
  104. package/src/cli/load-config.ts +47 -47
  105. package/src/cli/mcp-from-console.ts +1 -4
  106. package/src/cli/mode.ts +65 -0
  107. package/src/cli/openbao-bootstrap.test.ts +147 -0
  108. package/src/cli/openbao-bootstrap.ts +280 -0
  109. package/src/cli/openbao-restart.integration.test.ts +136 -0
  110. package/src/cli/ports.test.ts +7 -14
  111. package/src/cli/ports.ts +7 -5
  112. package/src/cli/privacy-erase.ts +1 -3
  113. package/src/cli/registry.ts +75 -25
  114. package/src/cli/resolve-dev-sql-env.test.ts +48 -0
  115. package/src/cli/resolve-dev-sql-env.ts +42 -0
  116. package/src/cli/schema.ts +3 -10
  117. package/src/cli/stack.ts +8 -10
  118. package/src/cli/start.ts +4 -15
  119. package/src/cli/upgrade.ts +1 -4
  120. package/src/cli/vault-cmd.ts +63 -0
  121. package/src/client/budget-entry.ts +1 -6
  122. package/src/client/create.test.ts +6 -12
  123. package/src/client/create.ts +2 -8
  124. package/src/client/errors.ts +1 -4
  125. package/src/client/index.ts +1 -6
  126. package/src/client/notes-contract.test.ts +4 -13
  127. package/src/client/transport.ts +9 -30
  128. package/src/client/types.ts +15 -30
  129. package/src/compiler/aot.test.ts +2 -9
  130. package/src/compiler/aot.ts +1 -4
  131. package/src/compiler/differential.test.ts +3 -11
  132. package/src/compiler/dynamic.ts +1 -4
  133. package/src/compiler/effects-infer.ts +8 -34
  134. package/src/compiler/emit.ts +1 -3
  135. package/src/compiler/extract.test.ts +136 -30
  136. package/src/compiler/extract.ts +485 -157
  137. package/src/compiler/fixtures/skyport/oke.config.ts +3 -3
  138. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -3
  139. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -3
  140. package/src/compiler/fixtures/skyport/src/flows/support/index.ts +4 -1
  141. package/src/compiler/fixtures/skyport.expected.json +24 -83
  142. package/src/compiler/fixtures/triggers/five-triggers.ts +1 -2
  143. package/src/compiler/http-parse.ts +2 -9
  144. package/src/compiler/index.ts +1 -5
  145. package/src/compiler/response.ts +16 -7
  146. package/src/compiler/sucrose.ts +11 -31
  147. package/src/config/define-config.test.ts +55 -34
  148. package/src/config/index.ts +177 -46
  149. package/src/config/resolve-driver.test.ts +28 -17
  150. package/src/console/budget.test.ts +1 -4
  151. package/src/console/index.ts +1 -4
  152. package/src/console/server/access.test.ts +15 -43
  153. package/src/console/server/access.ts +19 -49
  154. package/src/console/server/ai.test.ts +3 -11
  155. package/src/console/server/ai.ts +23 -46
  156. package/src/console/server/app.ts +16 -45
  157. package/src/console/server/auth-rate.test.ts +4 -12
  158. package/src/console/server/bind.ts +1 -5
  159. package/src/console/server/channels.test.ts +2 -6
  160. package/src/console/server/channels.ts +10 -35
  161. package/src/console/server/claim.ts +2 -8
  162. package/src/console/server/clock.test.ts +2 -6
  163. package/src/console/server/clock.ts +8 -26
  164. package/src/console/server/console.test.ts +1 -3
  165. package/src/console/server/diff.test.ts +5 -17
  166. package/src/console/server/diff.ts +10 -31
  167. package/src/console/server/dry-run.audit.test.ts +2 -8
  168. package/src/console/server/flows.ts +64 -233
  169. package/src/console/server/gates.test.ts +14 -50
  170. package/src/console/server/gates.ts +13 -48
  171. package/src/console/server/index.ts +2 -9
  172. package/src/console/server/live.ts +3 -12
  173. package/src/console/server/operator-db.test.ts +4 -15
  174. package/src/console/server/operator-db.ts +13 -32
  175. package/src/console/server/plugin.ts +50 -53
  176. package/src/console/server/plugins.test.ts +2 -6
  177. package/src/console/server/plugins.ts +17 -56
  178. package/src/console/server/runs-pii.ts +2 -7
  179. package/src/console/server/security.gate.test.ts +18 -40
  180. package/src/console/server/serve.ts +10 -25
  181. package/src/console/server/signals.test.ts +1 -3
  182. package/src/console/server/signals.ts +17 -40
  183. package/src/console/server/state.ts +21 -59
  184. package/src/console/server/store.test.ts +20 -22
  185. package/src/console/server/store.ts +31 -76
  186. package/src/console/server/vault.test.ts +12 -29
  187. package/src/console/server/vault.ts +44 -66
  188. package/src/console/ui/access/AccessA11yView.tsx +6 -21
  189. package/src/console/ui/access/a11y.test.tsx +3 -12
  190. package/src/console/ui/access/acknowledgement.test.ts +1 -4
  191. package/src/console/ui/access/acknowledgement.ts +1 -3
  192. package/src/console/ui/access/blast-radius.ts +1 -3
  193. package/src/console/ui/access/confirmation.test.ts +1 -5
  194. package/src/console/ui/access/confirmation.ts +1 -4
  195. package/src/console/ui/access/fixture.ts +1 -5
  196. package/src/console/ui/access/grantable.test.ts +1 -3
  197. package/src/console/ui/access/grantable.ts +2 -7
  198. package/src/console/ui/access/index.ts +2 -9
  199. package/src/console/ui/access/provenance.test.ts +1 -3
  200. package/src/console/ui/access/provenance.ts +2 -6
  201. package/src/console/ui/access/search.test.ts +1 -5
  202. package/src/console/ui/access/search.ts +2 -6
  203. package/src/console/ui/ai/AiA11yView.tsx +6 -17
  204. package/src/console/ui/ai/a11y.test.tsx +2 -5
  205. package/src/console/ui/ai/fixture.ts +2 -7
  206. package/src/console/ui/ai/format.ts +1 -3
  207. package/src/console/ui/ai/group.test.ts +1 -3
  208. package/src/console/ui/ai/group.ts +4 -12
  209. package/src/console/ui/ai/index.ts +2 -9
  210. package/src/console/ui/ai/promotion.test.ts +2 -7
  211. package/src/console/ui/ai/promotion.ts +4 -13
  212. package/src/console/ui/ai/search.test.ts +2 -11
  213. package/src/console/ui/ai/search.ts +3 -13
  214. package/src/console/ui/ai/types.ts +1 -8
  215. package/src/console/ui/architecture/ArchitectureA11yView.tsx +8 -29
  216. package/src/console/ui/architecture/a11y.test.tsx +1 -2
  217. package/src/console/ui/architecture/declared.ts +1 -5
  218. package/src/console/ui/architecture/index.ts +7 -35
  219. package/src/console/ui/architecture/layout.ts +1 -3
  220. package/src/console/ui/architecture/pathologies.ts +10 -19
  221. package/src/console/ui/architecture/search.ts +6 -15
  222. package/src/console/ui/architecture/traffic.ts +4 -14
  223. package/src/console/ui/architecture/types.ts +2 -10
  224. package/src/console/ui/architecture/view.test.ts +7 -19
  225. package/src/console/ui/architecture/view.ts +9 -36
  226. package/src/console/ui/channels/ChannelsA11yView.tsx +4 -13
  227. package/src/console/ui/channels/a11y.test.tsx +1 -2
  228. package/src/console/ui/channels/confirmation.test.ts +1 -4
  229. package/src/console/ui/channels/confirmation.ts +1 -4
  230. package/src/console/ui/channels/findings.ts +1 -5
  231. package/src/console/ui/channels/index.ts +2 -8
  232. package/src/console/ui/channels/locale.test.ts +1 -3
  233. package/src/console/ui/channels/locale.ts +2 -8
  234. package/src/console/ui/channels/search.ts +4 -13
  235. package/src/console/ui/channels/taxonomy.ts +1 -3
  236. package/src/console/ui/clock/ClockA11yView.tsx +9 -26
  237. package/src/console/ui/clock/a11y.test.tsx +2 -5
  238. package/src/console/ui/clock/confirmation.test.ts +1 -3
  239. package/src/console/ui/clock/confirmation.ts +1 -4
  240. package/src/console/ui/clock/findings.ts +1 -4
  241. package/src/console/ui/clock/health.ts +2 -7
  242. package/src/console/ui/clock/index.ts +2 -10
  243. package/src/console/ui/clock/search.test.ts +1 -3
  244. package/src/console/ui/clock/search.ts +2 -6
  245. package/src/console/ui/clock/timeline.test.ts +1 -3
  246. package/src/console/ui/clock/waiting-on.test.ts +1 -5
  247. package/src/console/ui/clock/waiting-on.ts +2 -7
  248. package/src/console/ui/diff/DiffA11yView.tsx +8 -17
  249. package/src/console/ui/diff/a11y.test.tsx +2 -5
  250. package/src/console/ui/diff/group.test.ts +3 -13
  251. package/src/console/ui/diff/group.ts +2 -8
  252. package/src/console/ui/diff/index.ts +2 -8
  253. package/src/console/ui/diff/search.test.ts +1 -5
  254. package/src/console/ui/diff/search.ts +4 -16
  255. package/src/console/ui/diff/summary.ts +1 -2
  256. package/src/console/ui/dist/assets/{index-Dy4jht9P.js → index-BWo8R7NR.js} +2 -2
  257. package/src/console/ui/dist/assets/{panel-access-Dd37LU2c.js → panel-access-C0J2D-a2.js} +1 -1
  258. package/src/console/ui/dist/assets/{panel-ai-CC7LR6-J.js → panel-ai-D_m6WQI8.js} +1 -1
  259. package/src/console/ui/dist/assets/{panel-architecture-B5b3iKCz.js → panel-architecture-CKnXFyUx.js} +1 -1
  260. package/src/console/ui/dist/assets/{panel-channels-CeNjTKXp.js → panel-channels-BOmQ-onL.js} +1 -1
  261. package/src/console/ui/dist/assets/{panel-clock-DgFTLoHV.js → panel-clock-giAq0Ccv.js} +1 -1
  262. package/src/console/ui/dist/assets/{panel-diff-DxehccqB.js → panel-diff-cdonmH8c.js} +1 -1
  263. package/src/console/ui/dist/assets/{panel-flows-BtrVn-Eg.js → panel-flows-DlCU5zjA.js} +2 -2
  264. package/src/console/ui/dist/assets/{panel-gates-Z9MKRGdH.js → panel-gates-XclZxWD5.js} +1 -1
  265. package/src/console/ui/dist/assets/{panel-overview-Dt_AeXgd.js → panel-overview-BznEOTnb.js} +1 -1
  266. package/src/console/ui/dist/assets/{panel-plugins-DWd0TowH.js → panel-plugins-CcGM1g64.js} +1 -1
  267. package/src/console/ui/dist/assets/{panel-runs-DGstFHeq.js → panel-runs-CGWNHLR4.js} +1 -1
  268. package/src/console/ui/dist/assets/{panel-signals-DzEa2Fnt.js → panel-signals-CNywkdak.js} +1 -1
  269. package/src/console/ui/dist/assets/{panel-store-BJkbNgxx.js → panel-store-KmTbFHMH.js} +1 -1
  270. package/src/console/ui/dist/assets/{panel-traces-BaRVO3gM.js → panel-traces-DBLx2ilD.js} +1 -1
  271. package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +1 -0
  272. package/src/console/ui/dist/index.html +1 -1
  273. package/src/console/ui/flows/FlowsA11yView.tsx +7 -22
  274. package/src/console/ui/flows/a11y.test.tsx +2 -8
  275. package/src/console/ui/flows/buffer.test.ts +3 -11
  276. package/src/console/ui/flows/buffer.ts +4 -11
  277. package/src/console/ui/flows/confirmation.ts +1 -3
  278. package/src/console/ui/flows/contract.test.ts +3 -7
  279. package/src/console/ui/flows/contract.ts +12 -36
  280. package/src/console/ui/flows/graph.test.ts +3 -11
  281. package/src/console/ui/flows/graph.ts +5 -15
  282. package/src/console/ui/flows/index.ts +1 -6
  283. package/src/console/ui/flows/save-as-test.ts +1 -3
  284. package/src/console/ui/flows/search.ts +8 -25
  285. package/src/console/ui/flows/tiers.ts +2 -9
  286. package/src/console/ui/gates/GatesA11yView.tsx +4 -13
  287. package/src/console/ui/gates/a11y.test.tsx +2 -5
  288. package/src/console/ui/gates/audit.ts +1 -3
  289. package/src/console/ui/gates/denial.test.ts +1 -3
  290. package/src/console/ui/gates/denial.ts +4 -12
  291. package/src/console/ui/gates/findings.ts +1 -3
  292. package/src/console/ui/gates/group.test.ts +3 -3
  293. package/src/console/ui/gates/group.ts +3 -11
  294. package/src/console/ui/gates/index.ts +2 -10
  295. package/src/console/ui/gates/search.ts +3 -10
  296. package/src/console/ui/overview/OverviewA11yView.tsx +9 -27
  297. package/src/console/ui/overview/a11y.test.tsx +1 -2
  298. package/src/console/ui/overview/busiest.ts +1 -3
  299. package/src/console/ui/overview/compose.ts +1 -13
  300. package/src/console/ui/overview/cost.ts +34 -38
  301. package/src/console/ui/overview/fixture.ts +21 -26
  302. package/src/console/ui/overview/golden.ts +4 -14
  303. package/src/console/ui/overview/index.ts +2 -9
  304. package/src/console/ui/overview/rank.test.ts +3 -10
  305. package/src/console/ui/overview/rank.ts +2 -7
  306. package/src/console/ui/overview/slo.test.ts +5 -9
  307. package/src/console/ui/overview/slo.ts +6 -23
  308. package/src/console/ui/overview/verdict.ts +3 -10
  309. package/src/console/ui/plugins/PluginsA11yView.tsx +8 -18
  310. package/src/console/ui/plugins/a11y.test.tsx +1 -2
  311. package/src/console/ui/plugins/fixture.ts +2 -4
  312. package/src/console/ui/plugins/group.test.ts +1 -3
  313. package/src/console/ui/plugins/group.ts +1 -6
  314. package/src/console/ui/plugins/index.ts +3 -13
  315. package/src/console/ui/plugins/search.ts +7 -19
  316. package/src/console/ui/runs/RunsA11yView.tsx +5 -20
  317. package/src/console/ui/runs/a11y.test.tsx +2 -5
  318. package/src/console/ui/runs/explain.ts +2 -6
  319. package/src/console/ui/runs/group.ts +3 -12
  320. package/src/console/ui/runs/histogram.test.ts +2 -11
  321. package/src/console/ui/runs/histogram.ts +1 -4
  322. package/src/console/ui/runs/index.ts +4 -18
  323. package/src/console/ui/runs/project.ts +2 -6
  324. package/src/console/ui/runs/query.test.ts +1 -3
  325. package/src/console/ui/runs/query.ts +12 -35
  326. package/src/console/ui/runs/search.ts +5 -19
  327. package/src/console/ui/runs/trace-link.test.ts +2 -6
  328. package/src/console/ui/runs/trace-link.ts +4 -16
  329. package/src/console/ui/runs/types.ts +1 -3
  330. package/src/console/ui/shell/App.tsx +2 -10
  331. package/src/console/ui/shell/client.ts +22 -115
  332. package/src/console/ui/shell/components/ui.tsx +1 -4
  333. package/src/console/ui/shell/layout/Shell.tsx +2 -4
  334. package/src/console/ui/shell/main.tsx +31 -96
  335. package/src/console/ui/shell/panels/access/AccessPanel.tsx +34 -98
  336. package/src/console/ui/shell/panels/ai/AiPanel.tsx +35 -106
  337. package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +18 -66
  338. package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +20 -57
  339. package/src/console/ui/shell/panels/clock/ClockPanel.tsx +22 -54
  340. package/src/console/ui/shell/panels/diff/DiffPanel.tsx +20 -61
  341. package/src/console/ui/shell/panels/flows/ContractEditor.tsx +6 -23
  342. package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +13 -28
  343. package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +9 -29
  344. package/src/console/ui/shell/panels/gates/GatesPanel.tsx +24 -80
  345. package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +20 -58
  346. package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +16 -41
  347. package/src/console/ui/shell/panels/runs/RunsPanel.tsx +31 -99
  348. package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +38 -101
  349. package/src/console/ui/shell/panels/store/StorePanel.tsx +27 -66
  350. package/src/console/ui/shell/panels/traces/TracesPanel.tsx +28 -89
  351. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +22 -53
  352. package/src/console/ui/shell/setup/Wizard.tsx +3 -5
  353. package/src/console/ui/shell/styles.css +1 -2
  354. package/src/console/ui/signals/SignalsA11yView.tsx +9 -33
  355. package/src/console/ui/signals/a11y.test.tsx +2 -5
  356. package/src/console/ui/signals/confirmation.test.ts +1 -5
  357. package/src/console/ui/signals/confirmation.ts +2 -6
  358. package/src/console/ui/signals/dry-run.ts +1 -3
  359. package/src/console/ui/signals/durable.ts +1 -3
  360. package/src/console/ui/signals/findings.ts +1 -3
  361. package/src/console/ui/signals/group.test.ts +2 -8
  362. package/src/console/ui/signals/group.ts +2 -10
  363. package/src/console/ui/signals/index.ts +1 -4
  364. package/src/console/ui/signals/monitor.ts +2 -7
  365. package/src/console/ui/signals/schema-form.test.ts +3 -14
  366. package/src/console/ui/signals/schema-form.ts +1 -4
  367. package/src/console/ui/signals/search.ts +4 -13
  368. package/src/console/ui/store/StoreA11yView.tsx +3 -9
  369. package/src/console/ui/store/a11y.test.tsx +2 -5
  370. package/src/console/ui/store/confirmation.test.ts +1 -3
  371. package/src/console/ui/store/confirmation.ts +1 -4
  372. package/src/console/ui/store/dry-run.ts +2 -5
  373. package/src/console/ui/store/fixture.ts +1 -2
  374. package/src/console/ui/store/group.test.ts +1 -6
  375. package/src/console/ui/store/group.ts +1 -4
  376. package/src/console/ui/store/index.ts +1 -5
  377. package/src/console/ui/store/search.ts +2 -6
  378. package/src/console/ui/store/will-not-fire.ts +1 -2
  379. package/src/console/ui/traces/TracesA11yView.tsx +10 -29
  380. package/src/console/ui/traces/a11y.test.tsx +1 -2
  381. package/src/console/ui/traces/chain.test.ts +5 -19
  382. package/src/console/ui/traces/chain.ts +1 -3
  383. package/src/console/ui/traces/critical-path.test.ts +1 -3
  384. package/src/console/ui/traces/critical-path.ts +2 -6
  385. package/src/console/ui/traces/filter.test.ts +2 -6
  386. package/src/console/ui/traces/filter.ts +5 -13
  387. package/src/console/ui/traces/fold.test.ts +4 -12
  388. package/src/console/ui/traces/fold.ts +1 -3
  389. package/src/console/ui/traces/mini.ts +1 -3
  390. package/src/console/ui/traces/replay.test.ts +1 -3
  391. package/src/console/ui/traces/replay.ts +1 -3
  392. package/src/console/ui/traces/sampling.ts +1 -4
  393. package/src/console/ui/traces/search.ts +4 -17
  394. package/src/console/ui/traces/tier.ts +1 -3
  395. package/src/console/ui/vault/VaultA11yView.tsx +5 -17
  396. package/src/console/ui/vault/a11y.test.tsx +1 -2
  397. package/src/console/ui/vault/blast-radius.ts +1 -3
  398. package/src/console/ui/vault/confirmation.test.ts +1 -5
  399. package/src/console/ui/vault/confirmation.ts +1 -4
  400. package/src/console/ui/vault/dormant.test.ts +1 -4
  401. package/src/console/ui/vault/export-safe.test.ts +1 -5
  402. package/src/console/ui/vault/export-safe.ts +1 -4
  403. package/src/console/ui/vault/fixture.ts +3 -3
  404. package/src/console/ui/vault/group.ts +1 -2
  405. package/src/console/ui/vault/index.ts +2 -10
  406. package/src/console/ui/vault/search.ts +2 -6
  407. package/src/console/ui/vault/types.ts +1 -1
  408. package/src/console/xss.gate.test.ts +1 -3
  409. package/src/docker/compose.ts +215 -27
  410. package/src/docker/derive.ts +6 -14
  411. package/src/docker/docker.test.ts +118 -15
  412. package/src/docker/dockerfile.integration.test.ts +12 -21
  413. package/src/docker/helpers.ts +5 -4
  414. package/src/docker/index.ts +9 -6
  415. package/src/docker/pin.ts +1 -9
  416. package/src/docker/recipes/index.ts +6 -6
  417. package/src/docker/recipes/mailpit.ts +22 -0
  418. package/src/docker/recipes/openbao.ts +47 -0
  419. package/src/docker/recipes/postgres.ts +17 -6
  420. package/src/docker/recipes/redis.ts +11 -2
  421. package/src/docker/recipes/rustfs.ts +32 -0
  422. package/src/docker/stack-id.test.ts +73 -5
  423. package/src/docker/stack-id.ts +124 -33
  424. package/src/docker/stack.integration.test.ts +6 -21
  425. package/src/docker/stack.ts +36 -4
  426. package/src/docker/types.ts +16 -4
  427. package/src/docs-origin.ts +4 -4
  428. package/src/drivers/ai-anthropic.ts +5 -14
  429. package/src/drivers/ai-mock.ts +2 -5
  430. package/src/drivers/ai-openai-compatible.ts +4 -10
  431. package/src/drivers/ai-providers.test.ts +6 -13
  432. package/src/drivers/bun-native-completeness.test.ts +6 -15
  433. package/src/drivers/channel-console.ts +6 -7
  434. package/src/drivers/channel-fcm.ts +20 -30
  435. package/src/drivers/channel-resend.ts +1 -3
  436. package/src/drivers/channel-smtp.ts +2 -6
  437. package/src/drivers/channel-types.ts +2 -12
  438. package/src/drivers/channel-unifonic.ts +2 -6
  439. package/src/drivers/channel-wa-cloud.ts +2 -6
  440. package/src/drivers/channel-webpush.ts +21 -60
  441. package/src/drivers/conformance.test.ts +2 -10
  442. package/src/drivers/conformance.ts +8 -21
  443. package/src/drivers/drizzle-dialect.test.ts +20 -0
  444. package/src/drivers/drizzle-dialect.ts +37 -0
  445. package/src/drivers/fs.ts +2 -8
  446. package/src/drivers/index.ts +12 -57
  447. package/src/drivers/memory.ts +287 -75
  448. package/src/drivers/pgvector.ts +7 -11
  449. package/src/drivers/postgres.ts +7 -13
  450. package/src/drivers/redis.ts +13 -37
  451. package/src/drivers/s3.ts +15 -18
  452. package/src/drivers/signal-engine.ts +12 -49
  453. package/src/drivers/signal-memory.ts +2 -8
  454. package/src/drivers/signal-nats.ts +4 -9
  455. package/src/drivers/signal-postgres.ts +62 -129
  456. package/src/drivers/signal-redis.ts +7 -21
  457. package/src/drivers/signal-types.ts +1 -4
  458. package/src/drivers/sqlite.ts +3 -12
  459. package/src/drivers/types.ts +4 -23
  460. package/src/drivers/vault-driver-removal.test.ts +55 -0
  461. package/src/drivers/vault-env.ts +2 -6
  462. package/src/drivers/vault-infisical.ts +1 -5
  463. package/src/drivers/vault-managed.ts +1 -5
  464. package/src/drivers/vault-memory.ts +1 -5
  465. package/src/drivers/vault-openbao.test.ts +97 -0
  466. package/src/drivers/vault-openbao.ts +103 -40
  467. package/src/drivers/vault-types.ts +3 -16
  468. package/src/elements/ai/declare.ts +4 -14
  469. package/src/elements/ai/eval.ts +2 -6
  470. package/src/elements/ai/pii.ts +1 -3
  471. package/src/elements/ai/runtime.ts +12 -50
  472. package/src/elements/ai/schema.ts +4 -15
  473. package/src/elements/ai.test.ts +13 -24
  474. package/src/elements/ai.ts +1 -5
  475. package/src/elements/channel/consent.ts +3 -12
  476. package/src/elements/channel/costs.ts +4 -15
  477. package/src/elements/channel/declare.ts +1 -4
  478. package/src/elements/channel/email-auth.ts +1 -4
  479. package/src/elements/channel/locale.ts +2 -10
  480. package/src/elements/channel/mask.ts +1 -3
  481. package/src/elements/channel/mime.ts +2 -12
  482. package/src/elements/channel/outcomes.test.ts +1 -5
  483. package/src/elements/channel/outcomes.ts +5 -15
  484. package/src/elements/channel/receipts.ts +2 -7
  485. package/src/elements/channel/runtime.ts +28 -74
  486. package/src/elements/channel/suppression.ts +8 -14
  487. package/src/elements/channel.test.ts +8 -31
  488. package/src/elements/channel.ts +3 -14
  489. package/src/elements/clock/actions.ts +6 -18
  490. package/src/elements/clock/declare.ts +1 -3
  491. package/src/elements/clock/dst.ts +1 -5
  492. package/src/elements/clock/durable.ts +4 -16
  493. package/src/elements/clock/health.ts +2 -7
  494. package/src/elements/clock/leader.ts +2 -9
  495. package/src/elements/clock/reconcile.ts +4 -15
  496. package/src/elements/clock/runtime.ts +2 -5
  497. package/src/elements/clock/schedule.ts +4 -20
  498. package/src/elements/clock.test.ts +2 -7
  499. package/src/elements/clock.ts +5 -23
  500. package/src/elements/gate/permissions.ts +1 -4
  501. package/src/elements/gate/runtime.ts +7 -28
  502. package/src/elements/gate.ts +1 -5
  503. package/src/elements/index.ts +9 -7
  504. package/src/elements/signal/declare.ts +2 -7
  505. package/src/elements/signal/reconcile.test.ts +1 -4
  506. package/src/elements/signal/runtime.ts +1 -3
  507. package/src/elements/signal.test.ts +3 -15
  508. package/src/elements/signal.ts +2 -8
  509. package/src/elements/store/cache.test.ts +46 -0
  510. package/src/elements/store/cache.ts +3 -11
  511. package/src/elements/store/classify.ts +5 -22
  512. package/src/elements/store/declare.ts +11 -17
  513. package/src/elements/store/domain-ddl.test.ts +86 -0
  514. package/src/elements/store/emit-drizzle.ts +363 -0
  515. package/src/elements/store/files-policy.test.ts +1 -5
  516. package/src/elements/store/files-policy.ts +4 -12
  517. package/src/elements/store/load-plugin-tables.ts +103 -0
  518. package/src/elements/store/missing-relation.test.ts +83 -0
  519. package/src/elements/store/missing-relation.ts +38 -0
  520. package/src/elements/store/replica.ts +1 -4
  521. package/src/elements/store/resource-list-docs.fixture.ts +56 -0
  522. package/src/elements/store/resource-list-docs.test.ts +79 -0
  523. package/src/elements/store/resource.test.ts +253 -0
  524. package/src/elements/store/resource.ts +786 -0
  525. package/src/elements/store/runtime.ts +25 -28
  526. package/src/elements/store/schema-decl.test.ts +227 -0
  527. package/src/elements/store/schema-decl.ts +516 -0
  528. package/src/elements/store/sql-condition.test.ts +132 -0
  529. package/src/elements/store/sql-condition.ts +293 -65
  530. package/src/elements/store/sql-session.test.ts +135 -0
  531. package/src/elements/store/sql-session.ts +241 -84
  532. package/src/elements/store/table.ts +69 -23
  533. package/src/elements/store.test.ts +17 -39
  534. package/src/elements/store.ts +53 -31
  535. package/src/elements/vault/chain.ts +34 -0
  536. package/src/elements/vault/declare.ts +22 -32
  537. package/src/elements/vault/fingerprint.ts +2 -7
  538. package/src/elements/vault/redact.ts +2 -6
  539. package/src/elements/vault/runtime.ts +19 -33
  540. package/src/elements/vault.test.ts +12 -55
  541. package/src/elements/vault.ts +9 -13
  542. package/src/index.ts +5 -2
  543. package/src/kernel/adopt-routes.ts +15 -29
  544. package/src/kernel/app.ts +92 -118
  545. package/src/kernel/auth-resolve.ts +2 -9
  546. package/src/kernel/boot-bind/ai.ts +1 -4
  547. package/src/kernel/boot-bind/channel.test.ts +60 -0
  548. package/src/kernel/boot-bind/channel.ts +61 -5
  549. package/src/kernel/boot-bind/clock.ts +2 -6
  550. package/src/kernel/boot-bind/gate.ts +2 -8
  551. package/src/kernel/boot-bind/runs.ts +1 -3
  552. package/src/kernel/boot-bind/signal.ts +3 -10
  553. package/src/kernel/boot-bind/store.test.ts +27 -18
  554. package/src/kernel/boot-bind/store.ts +93 -52
  555. package/src/kernel/boot-bind/vault.ts +2 -8
  556. package/src/kernel/boot.test.ts +4 -10
  557. package/src/kernel/boot.ts +30 -77
  558. package/src/kernel/budget.test.ts +1 -3
  559. package/src/kernel/capability.ts +3 -10
  560. package/src/kernel/dry-run.test.ts +1 -4
  561. package/src/kernel/dry-run.ts +3 -11
  562. package/src/kernel/edge.test.ts +68 -0
  563. package/src/kernel/effects.test.ts +1 -5
  564. package/src/kernel/effects.ts +4 -11
  565. package/src/kernel/errors.registry.test.ts +2 -8
  566. package/src/kernel/errors.ts +23 -32
  567. package/src/kernel/flow.test.ts +12 -25
  568. package/src/kernel/flow.ts +13 -25
  569. package/src/kernel/fx.test.ts +31 -40
  570. package/src/kernel/fx.ts +135 -90
  571. package/src/kernel/hook-timing.test.ts +3 -6
  572. package/src/kernel/hook-timing.ts +5 -14
  573. package/src/kernel/hooks.test.ts +43 -10
  574. package/src/kernel/hooks.ts +25 -12
  575. package/src/kernel/index.ts +11 -17
  576. package/src/kernel/journal.ts +7 -31
  577. package/src/kernel/on.ts +45 -9
  578. package/src/kernel/pipeline.test.ts +10 -19
  579. package/src/kernel/pipeline.ts +5 -17
  580. package/src/kernel/plug.ts +4 -11
  581. package/src/kernel/plugin/conflicts.test.ts +13 -25
  582. package/src/kernel/plugin/decorate.test.ts +8 -13
  583. package/src/kernel/plugin/scoping.test.ts +4 -11
  584. package/src/kernel/plugin.ts +81 -27
  585. package/src/kernel/registry-isolation.test.ts +74 -0
  586. package/src/kernel/registry.ts +102 -30
  587. package/src/kernel/router.test.ts +15 -13
  588. package/src/kernel/router.ts +9 -29
  589. package/src/kernel/routing-budget.test.ts +1 -3
  590. package/src/kernel/run-telemetry.ts +1 -3
  591. package/src/kernel/triggers.ts +63 -21
  592. package/src/manifest/diff.test.ts +26 -91
  593. package/src/manifest/diff.ts +95 -225
  594. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  595. package/src/manifest/fixtures/skyport.manifest.json +1 -1
  596. package/src/manifest/index.ts +3 -10
  597. package/src/manifest/types.ts +30 -6
  598. package/src/manifest/undeclared.test.ts +4 -12
  599. package/src/manifest/undeclared.ts +1 -2
  600. package/src/manifest/validate.test.ts +2 -6
  601. package/src/manifest/validate.ts +5 -15
  602. package/src/mcp/authorization.ts +8 -27
  603. package/src/mcp/confirmation.ts +3 -10
  604. package/src/mcp/data.ts +2 -6
  605. package/src/mcp/docs-index.ts +4 -7
  606. package/src/mcp/docs-mcp.test.ts +3 -12
  607. package/src/mcp/docs-server.ts +7 -27
  608. package/src/mcp/docs-tools.ts +2 -6
  609. package/src/mcp/index.ts +1 -6
  610. package/src/mcp/injection.gate.test.ts +4 -18
  611. package/src/mcp/mcp.test.ts +18 -49
  612. package/src/mcp/protocol.ts +3 -10
  613. package/src/mcp/server.ts +11 -52
  614. package/src/mcp/session.ts +4 -14
  615. package/src/mcp/tools.ts +14 -50
  616. package/src/plugins/catalogue.test.ts +3 -7
  617. package/src/plugins/catalogue.ts +1 -3
  618. package/src/plugins/compression.test.ts +127 -0
  619. package/src/plugins/compression.ts +94 -0
  620. package/src/plugins/config-source.test.ts +204 -0
  621. package/src/plugins/config-source.ts +209 -0
  622. package/src/plugins/cors.test.ts +138 -0
  623. package/src/plugins/cors.ts +129 -0
  624. package/src/plugins/csrf.test.ts +102 -0
  625. package/src/plugins/csrf.ts +86 -0
  626. package/src/plugins/headers.ts +54 -0
  627. package/src/plugins/index.ts +26 -0
  628. package/src/plugins/ip-allowlist.test.ts +105 -0
  629. package/src/plugins/ip-allowlist.ts +76 -0
  630. package/src/plugins/maintenance-mode.test.ts +91 -0
  631. package/src/plugins/maintenance-mode.ts +85 -0
  632. package/src/plugins/node-import-scan.ts +2 -5
  633. package/src/plugins/security-headers.test.ts +243 -0
  634. package/src/plugins/security-headers.ts +255 -0
  635. package/src/plugins/supply-chain.ts +5 -17
  636. package/src/release/exports.ts +3 -8
  637. package/src/release/measure.exports.test.ts +1 -3
  638. package/src/release/measure.ts +16 -45
  639. package/src/runs/bench.test.ts +1 -3
  640. package/src/runs/collect.ts +8 -25
  641. package/src/runs/drivers/clickhouse.ts +2 -9
  642. package/src/runs/drivers/files.ts +9 -31
  643. package/src/runs/drivers/memory.ts +5 -22
  644. package/src/runs/drivers/postgres.ts +8 -20
  645. package/src/runs/duckdb.ts +2 -8
  646. package/src/runs/index.ts +3 -14
  647. package/src/runs/outlier.ts +3 -10
  648. package/src/runs/parquet.ts +8 -26
  649. package/src/runs/privacy.ts +2 -8
  650. package/src/runs/runs.test.ts +3 -9
  651. package/src/runs/runtime.ts +7 -32
  652. package/src/runs/shred.ts +10 -29
  653. package/src/runs/types.ts +1 -4
  654. package/src/runtime/bun.ts +4 -15
  655. package/src/runtime/cold-start.bench.ts +1 -3
  656. package/src/runtime/dev-request-log.test.ts +1 -4
  657. package/src/runtime/dev-request-log.ts +1 -4
  658. package/src/runtime/index.ts +1 -6
  659. package/src/runtime/primitives.ts +5 -4
  660. package/src/runtime/security.ts +3 -13
  661. package/src/runtime/serve.test.ts +4 -7
  662. package/src/runtime/types.ts +1 -4
  663. package/src/runtime/web-standard.ts +3 -15
  664. package/src/term.test.ts +4 -6
  665. package/src/term.ts +49 -98
  666. package/src/test/create-test-app.test.ts +4 -14
  667. package/src/test/create-test-app.ts +27 -33
  668. package/src/test/provisions.integration.test.ts +3 -11
  669. package/src/upgrade/codemods.test.ts +25 -6
  670. package/src/upgrade/codemods.ts +116 -3
  671. package/src/validation/standard-schema.test.ts +2 -5
  672. package/src/validation/standard-schema.ts +13 -33
  673. package/docs/spec/console.md +0 -739
  674. package/docs/spec/example.md +0 -1196
  675. package/docs/spec/four-applications.md +0 -1195
  676. package/docs/spec/unified-theory.md +0 -489
  677. package/src/cli/doc-drift.test.ts +0 -146
  678. package/src/cli/doc-drift.ts +0 -430
  679. package/src/cli/doctor-diff-examples.ts +0 -92
  680. package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +0 -1
  681. package/src/drivers/vault-sops.ts +0 -271
package/src/cli/dev.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * `oke dev` — watch · hot reload · Console :6533 · MCP :6535 · client types on save.
3
- * `oke dev --stack` boots infra compose and binds prod store drivers (Postgres/Redis).
3
+ * Mode: `local` (in-memory) or `docker` (compose infra + host Bun).
4
4
  */
5
5
 
6
6
  import { watch } from "node:fs";
@@ -13,8 +13,9 @@ import type { ConsoleState } from "../console/server/state.ts";
13
13
  import {
14
14
  DEFAULT_DOCKER_DIR,
15
15
  deriveInfrastructure,
16
- formatStackEnv,
16
+ loadExistingStackControls,
17
17
  loadExistingStackCredentials,
18
+ resolveExtraPorts,
18
19
  stackAppSlug,
19
20
  stackInstanceId,
20
21
  writeDerivedFiles,
@@ -22,11 +23,7 @@ import {
22
23
  } from "../docker/index.ts";
23
24
  import type { Manifest } from "../manifest/types.ts";
24
25
  import type { McpContext } from "../mcp/tools.ts";
25
- import {
26
- APP_PORT,
27
- CONSOLE_PORT,
28
- MCP_PORT,
29
- } from "../runtime/types.ts";
26
+ import { APP_PORT, CONSOLE_PORT, DOCS_MCP_PORT, MCP_PORT } from "../runtime/types.ts";
30
27
  import {
31
28
  formatDevBanner,
32
29
  formatDevLogSeparator,
@@ -36,15 +33,16 @@ import {
36
33
  } from "../term.ts";
37
34
  import { clientAdd } from "./client-add.ts";
38
35
  import { resolveDriverId } from "../config/index.ts";
36
+ import { askDevMode, type AskDevModeFn } from "./ask-dev-mode.ts";
39
37
  import {
40
- buildDevHeroSnapshot,
41
- encodeHeroSnapshot,
42
- } from "./hero-meta.ts";
43
- import {
44
- loadManifest,
45
- loadOkeConfig,
46
- resolveImages,
47
- } from "./load-config.ts";
38
+ createDebouncedRunner,
39
+ isDomainSchemaWatchPath,
40
+ resolveDevAutoPush,
41
+ } from "./db-auto-push.ts";
42
+ import { resolveDrizzleConfigPath, runPush } from "./db.ts";
43
+ import { readDevMode, shouldAskDevMode, writeDevMode, type DevMode } from "./dev-mode.ts";
44
+ import { buildDevHeroSnapshot, encodeHeroSnapshot } from "./hero-meta.ts";
45
+ import { loadManifest, loadOkeConfig, resolveImages } from "./load-config.ts";
48
46
  import { mcpContextFromConsole } from "./mcp-from-console.ts";
49
47
  import { resolveDevPorts } from "./ports.ts";
50
48
 
@@ -94,6 +92,12 @@ export interface DevServeMcpOptions {
94
92
  readonly hostname?: string;
95
93
  }
96
94
 
95
+ /** Options for {@link DevOptions.serveDocsMcp}. */
96
+ export interface DevServeDocsMcpOptions {
97
+ readonly port: number;
98
+ readonly hostname?: string;
99
+ }
100
+
97
101
  /** Live `oke dev` session — returned when {@link DevOptions.keepAlive} is false. */
98
102
  export interface DevSession {
99
103
  readonly plan: DevPlan;
@@ -103,6 +107,10 @@ export interface DevSession {
103
107
  readonly consolePort: number;
104
108
  readonly mcpPort: number;
105
109
  readonly mcpUrl: URL | null;
110
+ /** Docs MCP listen port (read-only, unauthenticated). */
111
+ readonly docsMcpPort: number;
112
+ /** Docs MCP base URL — null when the surface was skipped. */
113
+ readonly docsMcpUrl: URL | null;
106
114
  /** Shared with Console — mint `oke-mcp` tokens against this store/secret. */
107
115
  readonly secret: string | null;
108
116
  readonly sessions: SessionStore | null;
@@ -110,11 +118,43 @@ export interface DevSession {
110
118
  readonly consoleState: ConsoleState | null;
111
119
  }
112
120
 
121
+ /** Minimal filesystem watcher contract, injectable for deterministic tests. */
122
+ export type DevWatchFn = (
123
+ path: string,
124
+ options: { readonly recursive: true },
125
+ listener: (event: "rename" | "change", filename: string | null) => void,
126
+ ) => { close(): void };
127
+
113
128
  /** Options for {@link runDev}. */
114
129
  export interface DevOptions {
115
130
  readonly cwd?: string;
116
131
  readonly entry?: string;
117
- readonly stack?: boolean | readonly string[];
132
+ /**
133
+ * Session-only mode override. `true` / role list → docker; `false` → local.
134
+ * When unset, resolve from `.oke/mode` / TTY ask / non-TTY default `local`.
135
+ */
136
+ readonly docker?: boolean | readonly string[];
137
+ /** Force local for this session only (never writes `.oke/mode`). */
138
+ readonly local?: boolean;
139
+ /**
140
+ * Opt out of auto `oke db push` on schema change for this session.
141
+ * Also set via `--no-db-push`.
142
+ */
143
+ readonly noDbPush?: boolean;
144
+ /**
145
+ * Injectable domain-schema push (tests). Default: {@link runPush}.
146
+ *
147
+ * @param cwd - Project root
148
+ */
149
+ readonly dbPush?: (cwd: string) => Promise<number>;
150
+ /**
151
+ * Called when a schema watch path triggers auto-push (tests).
152
+ *
153
+ * @param filename - Relative path from the watcher
154
+ */
155
+ readonly onDbAutoPush?: (filename: string) => void;
156
+ /** Watch project source changes (injectable for tests). */
157
+ readonly watchFs?: DevWatchFn;
118
158
  readonly images?: Readonly<Record<string, string>>;
119
159
  readonly credentials?: DeriveOptions["credentials"];
120
160
  readonly write?: (text: string) => void;
@@ -137,16 +177,14 @@ export interface DevOptions {
137
177
  readonly appPort?: number;
138
178
  readonly consolePort?: number;
139
179
  readonly mcpPort?: number;
180
+ readonly docsMcpPort?: number;
140
181
  /**
141
- * Boot compose stack (injectable).
182
+ * Boot compose (injectable).
142
183
  *
143
184
  * @param composeFiles - `-f` list excluding override if missing
144
- * @param cwd - Project root
185
+ * @param cwd - Compose directory (`docker/`)
145
186
  */
146
- readonly composeUp?: (
147
- composeFiles: readonly string[],
148
- cwd: string,
149
- ) => Promise<void>;
187
+ readonly composeUp?: (composeFiles: readonly string[], cwd: string) => Promise<void>;
150
188
  /**
151
189
  * Regenerate client types (injectable).
152
190
  *
@@ -160,16 +198,27 @@ export interface DevOptions {
160
198
  * Default: {@link serveMcp} on :6535.
161
199
  */
162
200
  readonly serveMcp?: (options: DevServeMcpOptions) => Promise<DevMcpHandle>;
201
+ /**
202
+ * Serve the read-only docs MCP (injectable).
203
+ * Default: {@link serveDocsMcp} on :6536. Boot failure skips the surface —
204
+ * docs search must never take `oke dev` down.
205
+ */
206
+ readonly serveDocsMcp?: (options: DevServeDocsMcpOptions) => Promise<DevMcpHandle>;
163
207
  /**
164
208
  * Boot + serve the app entry (injectable).
165
209
  * Default: spawn `bun --hot` on the shipped {@link ./dev-app-runner.ts}
166
210
  * (import → `app.boot()` → `createBunRuntime().serve`) so Bun soft-reloads
167
211
  * app code while preserving the listen socket.
168
212
  */
169
- readonly startApp?: (
170
- entry: string,
171
- env: Record<string, string>,
172
- ) => Promise<DevAppHandle>;
213
+ readonly startApp?: (entry: string, env: Record<string, string>) => Promise<DevAppHandle>;
214
+ /**
215
+ * Injectable mode prompt (tests). Default: {@link askDevMode}.
216
+ */
217
+ readonly ask?: AskDevModeFn;
218
+ /**
219
+ * Override stdin TTY detection (tests). Default: `process.stdin.isTTY`.
220
+ */
221
+ readonly stdinIsTTY?: boolean;
173
222
  }
174
223
 
175
224
  /** Result of a dry-run / prepared / live-but-detached dev session. */
@@ -178,6 +227,7 @@ export interface DevPlan {
178
227
  readonly appPort: number;
179
228
  readonly consolePort: number;
180
229
  readonly mcpPort: number;
230
+ readonly docsMcpPort: number;
181
231
  readonly stackRoles: readonly string[] | null;
182
232
  readonly composeFiles: readonly string[] | null;
183
233
  readonly stackEnv: Readonly<Record<string, string>> | null;
@@ -202,24 +252,29 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
202
252
  const preferredApp = options.appPort ?? Number(Bun.env.PORT ?? APP_PORT);
203
253
  const preferredConsole = options.consolePort ?? CONSOLE_PORT;
204
254
  const preferredMcp = options.mcpPort ?? MCP_PORT;
255
+ const preferredDocsMcp = options.docsMcpPort ?? DOCS_MCP_PORT;
205
256
  // Explicit overrides (incl. `0` ephemeral) skip probing; otherwise +1 until free.
206
257
  const ports =
207
258
  options.appPort !== undefined ||
208
259
  options.consolePort !== undefined ||
209
- options.mcpPort !== undefined
260
+ options.mcpPort !== undefined ||
261
+ options.docsMcpPort !== undefined
210
262
  ? {
211
263
  app: preferredApp,
212
264
  console: preferredConsole,
213
265
  mcp: preferredMcp,
266
+ docsMcp: preferredDocsMcp,
214
267
  }
215
268
  : await resolveDevPorts({
216
269
  app: preferredApp,
217
270
  console: preferredConsole,
218
271
  mcp: preferredMcp,
272
+ docsMcp: preferredDocsMcp,
219
273
  });
220
274
  const appPort = ports.app;
221
275
  const consolePort = ports.console;
222
276
  const mcpPort = ports.mcp;
277
+ const docsMcpPort = ports.docsMcp;
223
278
  const keepAlive = options.keepAlive ?? true;
224
279
 
225
280
  let entry = options.entry;
@@ -236,20 +291,55 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
236
291
  return { code: 1 };
237
292
  }
238
293
 
294
+ const explicitLocal = options.local === true;
295
+ const explicitDocker = options.docker === true || Array.isArray(options.docker);
296
+ if (explicitLocal && explicitDocker) {
297
+ console.error("oke dev: use either --local or --docker, not both");
298
+ return { code: 1 };
299
+ }
300
+
301
+ const savedMode = await readDevMode(cwd);
302
+ const explicit = explicitLocal || explicitDocker;
303
+ let mode: DevMode;
304
+ if (explicitLocal) {
305
+ mode = "local";
306
+ } else if (explicitDocker) {
307
+ mode = "docker";
308
+ } else if (savedMode !== null) {
309
+ mode = savedMode;
310
+ } else if (
311
+ shouldAskDevMode({
312
+ saved: savedMode,
313
+ explicit,
314
+ stdinIsTTY: options.stdinIsTTY ?? process.stdin.isTTY,
315
+ })
316
+ ) {
317
+ const ask = options.ask ?? askDevMode;
318
+ const chosen = await ask();
319
+ if (chosen === null) {
320
+ console.error("oke dev: cancelled");
321
+ return { code: 1 };
322
+ }
323
+ mode = chosen;
324
+ await writeDevMode(cwd, mode);
325
+ } else {
326
+ // Non-TTY + unset: deterministic local — zero ask, zero docker, no save.
327
+ mode = "local";
328
+ }
329
+
239
330
  let stackRoles: string[] | null = null;
240
331
  let composeFiles: string[] | null = null;
241
332
  let stackEnv: Record<string, string> | null = null;
242
333
  let stackSqlDriver = "postgres";
243
334
  let stackKvDriver = "redis";
244
- let loadedConfig: Awaited<ReturnType<typeof loadOkeConfig>>["config"] | null =
245
- null;
335
+ let loadedConfig: Awaited<ReturnType<typeof loadOkeConfig>>["config"] | null = null;
246
336
  try {
247
337
  loadedConfig = (await loadOkeConfig(cwd)).config;
248
338
  } catch {
249
339
  loadedConfig = null;
250
340
  }
251
341
 
252
- if (options.stack) {
342
+ if (mode === "docker") {
253
343
  let images = options.images;
254
344
  if (!images) {
255
345
  if (!loadedConfig) {
@@ -271,28 +361,24 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
271
361
  loadedConfig = null;
272
362
  }
273
363
  }
274
- if (Array.isArray(options.stack)) {
275
- const allow = new Set(options.stack);
276
- images = Object.fromEntries(
277
- Object.entries(images).filter(([role]) => allow.has(role)),
278
- );
364
+ if (Array.isArray(options.docker)) {
365
+ const allow = new Set(options.docker);
366
+ images = Object.fromEntries(Object.entries(images).filter(([role]) => allow.has(role)));
279
367
  stackRoles = [...allow];
280
368
  } else {
281
369
  stackRoles = Object.keys(images);
282
370
  }
283
371
 
284
- stackSqlDriver =
285
- resolveDriverId(loadedConfig?.drivers?.store?.sql, "prod") ?? "postgres";
286
- stackKvDriver =
287
- resolveDriverId(loadedConfig?.drivers?.store?.kv, "prod") ?? "redis";
372
+ stackSqlDriver = resolveDriverId(loadedConfig?.drivers?.store?.sql, "prod") ?? "postgres";
373
+ stackKvDriver = resolveDriverId(loadedConfig?.drivers?.store?.kv, "prod") ?? "redis";
288
374
 
289
375
  const composeDir = DEFAULT_DOCKER_DIR;
290
376
  const dockerOut = resolve(cwd, composeDir);
291
377
  const instanceId = stackInstanceId(cwd);
292
378
  const appSlug = stackAppSlug(cwd);
293
379
  const reusedCreds =
294
- options.credentials ??
295
- (await loadExistingStackCredentials(cwd, stackRoles));
380
+ options.credentials ?? (await loadExistingStackCredentials(cwd, stackRoles));
381
+ const controls = await loadExistingStackControls(cwd);
296
382
  const derived = deriveInfrastructure({
297
383
  images,
298
384
  app: appSlug,
@@ -301,46 +387,77 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
301
387
  composeDir,
302
388
  instanceId,
303
389
  ...(reusedCreds ? { credentials: reusedCreds } : {}),
390
+ ...(controls ? { controls } : {}),
304
391
  host: "127.0.0.1",
305
392
  });
306
393
  // Layer 4 is user-owned — include only if the file already exists.
307
- const existingOverride = await Bun.file(
308
- resolve(dockerOut, "compose.override.yml"),
309
- ).exists();
394
+ const existingOverride = await Bun.file(resolve(dockerOut, "compose.override.yml")).exists();
310
395
  composeFiles = derived.composeFiles.filter(
311
396
  (f) => f !== "compose.override.yml" || existingOverride,
312
397
  );
313
398
  stackEnv = { ...derived.stackEnv };
314
399
 
315
400
  if (!options.dryRun) {
316
- await writeDerivedFiles(derived, dockerOut, {
317
- writeStackEnv: true,
318
- stackEnvDir: cwd,
319
- });
320
- // Ensure .env.stack is at project root for the host Bun app / vault.
321
- await Bun.write(resolve(cwd, ".env.stack"), formatStackEnv(stackEnv));
322
- const up =
323
- options.composeUp ??
324
- (async (files, dir) => {
325
- const args = ["compose", ...files.flatMap((f) => ["-f", f]), "up", "-d"];
326
- const proc = Bun.spawn(["docker", ...args], {
327
- cwd: dir,
328
- stdout: "inherit",
329
- stderr: "inherit",
330
- env: { ...process.env, ...stackEnv! },
331
- });
332
- const code = await proc.exited;
333
- if (code !== 0) {
334
- throw new Error(`oke dev --stack: docker compose exited ${code}`);
335
- }
401
+ try {
402
+ await writeDerivedFiles(derived, dockerOut, {
403
+ writeStackEnv: true,
336
404
  });
337
- // Compose files live under docker/; cwd for compose is that directory.
338
- await up(composeFiles, dockerOut);
405
+ const up =
406
+ options.composeUp ??
407
+ (async (files, dir) => {
408
+ const args = ["compose", ...files.flatMap((f) => ["-f", f]), "up", "-d"];
409
+ const proc = Bun.spawn(["docker", ...args], {
410
+ cwd: dir,
411
+ stdout: "inherit",
412
+ stderr: "inherit",
413
+ env: { ...process.env, ...stackEnv! },
414
+ });
415
+ const code = await proc.exited;
416
+ if (code !== 0) {
417
+ throw new Error(`oke dev --docker: docker compose exited ${code}`);
418
+ }
419
+ });
420
+ // Compose files live under docker/; cwd for compose is that directory.
421
+ await up(composeFiles, dockerOut);
422
+
423
+ // Real OpenBao vault: init once / unseal every start, then expose the
424
+ // least-privilege app token to the app + Console (never root).
425
+ const vaultSpec = derived.specs.find((s) => s.role === "vault");
426
+ if (vaultSpec) {
427
+ const { ensureOpenBao, openbaoStackEnv } = await import("./openbao-bootstrap.ts");
428
+ const vaultNames = await tryLoadVaultSecretNames(cwd);
429
+ const boot = await ensureOpenBao({
430
+ cwd,
431
+ url: `http://127.0.0.1:${vaultSpec.hostPort}`,
432
+ names: vaultNames,
433
+ });
434
+ Object.assign(stackEnv!, openbaoStackEnv(boot));
435
+ const envPath = resolve(dockerOut, ".env.docker");
436
+ const current = await Bun.file(envPath).text();
437
+ const tokenLine = `OKE_VAULT_TOKEN=${boot.appToken}`;
438
+ const next = /OKE_VAULT_TOKEN=.*/.test(current)
439
+ ? current.replace(/OKE_VAULT_TOKEN=.*/, tokenLine)
440
+ : `${current.trimEnd()}\nOKE_VAULT_TOKEN=${boot.appToken}\nOKE_VAULT_MOUNT=secret\n`;
441
+ await Bun.write(envPath, next);
442
+ }
443
+ } catch (err) {
444
+ const msg = err instanceof Error ? err.message : String(err);
445
+ console.error(msg);
446
+ console.error("oke dev: docker mode failed — fix compose, or run `oke mode local`");
447
+ return { code: 1 };
448
+ }
339
449
  const roleLabel = (role: string): string => {
340
450
  if (role === "store.sql") return "postgres";
341
451
  if (role === "store.kv") return "redis";
452
+ if (role === "store.files") return "files";
453
+ if (role === "channel.email") return "mail";
342
454
  return role;
343
455
  };
456
+ const extraLabel = (role: string, containerPort: number): string => {
457
+ if (role === "channel.email" && containerPort === 8025) return "mail-ui";
458
+ if (role === "store.files" && containerPort === 9001) return "files-ui";
459
+ return `${roleLabel(role)}+${containerPort}`;
460
+ };
344
461
  const appDrivers = [
345
462
  ...(stackRoles.includes("store.sql") ? [stackSqlDriver] : []),
346
463
  ...(stackRoles.includes("store.kv") ? [stackKvDriver] : []),
@@ -348,10 +465,13 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
348
465
  write(
349
466
  formatStackSummary({
350
467
  project: `oke-${appSlug}`,
351
- services: derived.specs.map((spec) => ({
352
- label: roleLabel(spec.role),
353
- hostPort: spec.hostPort,
354
- })),
468
+ services: derived.specs.flatMap((spec) => [
469
+ { label: roleLabel(spec.role), hostPort: spec.hostPort },
470
+ ...resolveExtraPorts(spec, { images, instanceId }).map((p) => ({
471
+ label: extraLabel(spec.role, p.containerPort),
472
+ hostPort: p.hostPort,
473
+ })),
474
+ ]),
355
475
  appDrivers,
356
476
  }),
357
477
  );
@@ -363,6 +483,7 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
363
483
  appPort,
364
484
  consolePort,
365
485
  mcpPort,
486
+ docsMcpPort,
366
487
  stackRoles,
367
488
  composeFiles,
368
489
  stackEnv,
@@ -370,9 +491,9 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
370
491
 
371
492
  let okeVersion = "0.0.0";
372
493
  try {
373
- const pkg = (await Bun.file(
374
- resolve(import.meta.dir, "../../package.json"),
375
- ).json()) as { version?: string };
494
+ const pkg = (await Bun.file(resolve(import.meta.dir, "../../package.json")).json()) as {
495
+ version?: string;
496
+ };
376
497
  if (typeof pkg.version === "string") okeVersion = pkg.version;
377
498
  } catch {
378
499
  // shipped binary may not sit next to package.json
@@ -380,9 +501,9 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
380
501
 
381
502
  const heroSnapshot = buildDevHeroSnapshot({
382
503
  config: loadedConfig,
383
- stack: Boolean(options.stack),
384
- sqlDriver: options.stack ? stackSqlDriver : undefined,
385
- kvDriver: options.stack ? stackKvDriver : undefined,
504
+ docker: mode === "docker",
505
+ sqlDriver: mode === "docker" ? stackSqlDriver : undefined,
506
+ kvDriver: mode === "docker" ? stackKvDriver : undefined,
386
507
  version: okeVersion,
387
508
  nodeEnv: "development",
388
509
  });
@@ -399,6 +520,34 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
399
520
 
400
521
  if (options.dryRun) return { code: 0, plan };
401
522
 
523
+ // One-shot schema sync for the session profile (both local and docker):
524
+ // emits schema.generated.ts for the active dialect, then pushes via
525
+ // drizzle-kit. Data planes stay isolated — session flags never rewrite
526
+ // `.oke/mode`. Watch auto-push below remains local-only.
527
+ if (!options.noDbPush) {
528
+ const { syncDevSchema } = await import("./dev-schema-sync.ts");
529
+ try {
530
+ const result = await syncDevSchema(cwd, mode, { write });
531
+ write(
532
+ formatStatusLine(
533
+ `oke db push (${mode} · ${result.dialect}) ${result.code === 0 ? "ok" : "failed"}`,
534
+ ),
535
+ );
536
+ } catch (err) {
537
+ write(
538
+ formatStatusLine(
539
+ `oke db push (${mode}) skipped — ${err instanceof Error ? err.message : String(err)}`,
540
+ ),
541
+ );
542
+ }
543
+ }
544
+
545
+ const autoPushEnabled = resolveDevAutoPush({
546
+ noDbPush: options.noDbPush,
547
+ docker: Boolean(stackEnv),
548
+ configAutoPush: loadedConfig?.db?.autoPush,
549
+ });
550
+
402
551
  const env: Record<string, string> = {
403
552
  ...(process.env as Record<string, string>),
404
553
  NODE_ENV: "development",
@@ -408,10 +557,12 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
408
557
  OKE_DEV_HERO_CONSOLE: `http://127.0.0.1:${consolePort}`,
409
558
  OKE_DEV_HERO_MCP: `http://127.0.0.1:${mcpPort}`,
410
559
  OKE_DEV_HERO_META: encodeHeroSnapshot(heroSnapshot),
560
+ // Propagate opt-out so the app disables ensureFromMeta when auto-push owns DDL.
561
+ ...(autoPushEnabled ? { OKE_DB_AUTO_PUSH: "1" } : { OKE_DB_AUTO_PUSH: "0" }),
411
562
  ...(stackEnv ?? {}),
412
563
  ...(stackEnv
413
564
  ? {
414
- OKE_STACK: "1",
565
+ OKE_DOCKER: "1",
415
566
  OKE_SQL_DRIVER: stackSqlDriver,
416
567
  OKE_KV_DRIVER: stackKvDriver,
417
568
  }
@@ -419,18 +570,12 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
419
570
  };
420
571
  process.env.OKE_DEV_REQUEST_LOG = "1";
421
572
 
422
- const startApp =
423
- options.startApp ??
424
- ((entryPath, appEnv) => startAppHot(cwd, entryPath, appEnv));
573
+ const startApp = options.startApp ?? ((entryPath, appEnv) => startAppHot(cwd, entryPath, appEnv));
425
574
 
426
575
  const seedManifest =
427
- options.manifest !== undefined
428
- ? options.manifest
429
- : await tryLoadProjectManifest(cwd);
576
+ options.manifest !== undefined ? options.manifest : await tryLoadProjectManifest(cwd);
430
577
 
431
- async function refreshManifestInto(
432
- state: ConsoleState | null,
433
- ): Promise<void> {
578
+ async function refreshManifestInto(state: ConsoleState | null): Promise<void> {
434
579
  if (!state) return;
435
580
  try {
436
581
  const { extractManifest } = await import("../compiler/extract.ts");
@@ -445,9 +590,7 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
445
590
  const serveConsole =
446
591
  options.serveConsole ??
447
592
  (async (port) => {
448
- const { serveConsole: serveConsoleKernel } = await import(
449
- "../console/server/serve.ts"
450
- );
593
+ const { serveConsole: serveConsoleKernel } = await import("../console/server/serve.ts");
451
594
  const server = await serveConsoleKernel({
452
595
  port,
453
596
  hostname: "127.0.0.1",
@@ -455,16 +598,9 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
455
598
  env: "dev",
456
599
  silentClaim: options.silentClaim ?? false,
457
600
  ...(options.secret !== undefined ? { secret: options.secret } : {}),
458
- ...(seedManifest !== undefined && seedManifest !== null
459
- ? { manifest: seedManifest }
460
- : {}),
601
+ ...(seedManifest !== undefined && seedManifest !== null ? { manifest: seedManifest } : {}),
461
602
  });
462
- write(
463
- formatServiceLine(
464
- "Console",
465
- `http://127.0.0.1:${server.port}`,
466
- ),
467
- );
603
+ write(formatServiceLine("Console", `http://127.0.0.1:${server.port}`));
468
604
  return {
469
605
  console: server.console,
470
606
  port: server.port,
@@ -496,6 +632,24 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
496
632
  };
497
633
  });
498
634
 
635
+ const serveDocsMcpSurface =
636
+ options.serveDocsMcp ??
637
+ (async (docsOptions) => {
638
+ const { serveDocsMcp } = await import("../mcp/docs-server.ts");
639
+ const server = await serveDocsMcp({
640
+ port: docsOptions.port,
641
+ hostname: docsOptions.hostname ?? "127.0.0.1",
642
+ });
643
+ write(formatServiceLine("Docs MCP", `http://127.0.0.1:${server.port}`));
644
+ return {
645
+ port: server.port,
646
+ url: server.url,
647
+ stop() {
648
+ server.stop(true);
649
+ },
650
+ };
651
+ });
652
+
499
653
  const regen =
500
654
  options.regenClient ??
501
655
  (async (appUrl) => {
@@ -535,34 +689,76 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
535
689
  });
536
690
  }
537
691
 
692
+ // Docs MCP is independent of Console state — read-only, no sessions/secret.
693
+ // Boot failure (missing docs content, busy port) skips it, never kills dev.
694
+ let docsMcpServer: DevMcpHandle | null = null;
695
+ try {
696
+ docsMcpServer = await serveDocsMcpSurface({ port: docsMcpPort, hostname: "127.0.0.1" });
697
+ } catch (err) {
698
+ write(
699
+ formatStatusLine(`Docs MCP skipped — ${err instanceof Error ? err.message : String(err)}`),
700
+ );
701
+ }
702
+
538
703
  const boundMcpPort = mcpServer?.port ?? mcpPort;
539
704
  const mcpUrl = mcpServer?.url ?? null;
705
+ const boundDocsMcpPort = docsMcpServer?.port ?? docsMcpPort;
706
+ const docsMcpUrl = docsMcpServer?.url ?? null;
540
707
 
541
- const appUrl =
542
- app.url?.origin ?? `http://127.0.0.1:${boundAppPort}`;
708
+ const appUrl = app.url?.origin ?? `http://127.0.0.1:${boundAppPort}`;
543
709
  await regen(appUrl);
710
+
711
+ const dbPush =
712
+ options.dbPush ??
713
+ (async (projectCwd: string) => {
714
+ const configPath = await resolveDrizzleConfigPath(projectCwd);
715
+ return runPush(configPath, (t) => write(t), { cwd: projectCwd, env: mode });
716
+ });
717
+
718
+ let lastSchemaFilename = "";
719
+ const autoPushRunner = createDebouncedRunner(async () => {
720
+ if (!autoPushEnabled) return;
721
+ options.onDbAutoPush?.(lastSchemaFilename);
722
+ write(formatStatusLine("oke db push (schema change)"));
723
+ try {
724
+ await dbPush(cwd);
725
+ } catch {
726
+ // Mid-edit / kit unavailable — next save retries.
727
+ }
728
+ });
729
+
730
+ // Initial sync so local DB matches schema.ts without a prior manual push.
731
+ if (autoPushEnabled) {
732
+ lastSchemaFilename = "src/schema.ts";
733
+ autoPushRunner.trigger();
734
+ }
735
+
544
736
  write(formatDevLogSeparator());
545
737
 
546
- const watcher = watch(
547
- resolve(cwd, "src"),
548
- { recursive: true },
549
- () => {
550
- void regen(appUrl);
551
- // Only live-extract when the host did not pin a Manifest (tests).
552
- if (options.manifest === undefined) {
553
- void refreshManifestInto(consoleState);
554
- }
555
- },
556
- );
738
+ const watchFs: DevWatchFn =
739
+ options.watchFs ?? ((path, watchOptions, listener) => watch(path, watchOptions, listener));
740
+ const watcher = watchFs(resolve(cwd, "src"), { recursive: true }, (_event, filename) => {
741
+ void regen(appUrl);
742
+ // Only live-extract when the host did not pin a Manifest (tests).
743
+ if (options.manifest === undefined) {
744
+ void refreshManifestInto(consoleState);
745
+ }
746
+ if (autoPushEnabled && isDomainSchemaWatchPath(filename?.toString())) {
747
+ lastSchemaFilename = filename?.toString() ?? "schema.ts";
748
+ autoPushRunner.trigger();
749
+ }
750
+ });
557
751
 
558
752
  let stopped = false;
559
753
  const stop = () => {
560
754
  if (stopped) return;
561
755
  stopped = true;
756
+ autoPushRunner.cancel();
562
757
  watcher.close();
563
758
  app.stop();
564
759
  consoleServer.stop();
565
760
  mcpServer?.stop();
761
+ docsMcpServer?.stop();
566
762
  process.off("SIGINT", onSignal);
567
763
  process.off("SIGTERM", onSignal);
568
764
  };
@@ -580,12 +776,15 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
580
776
  appPort: boundAppPort,
581
777
  consolePort: boundConsolePort,
582
778
  mcpPort: boundMcpPort,
779
+ docsMcpPort: boundDocsMcpPort,
583
780
  },
584
781
  stop,
585
782
  appPort: boundAppPort,
586
783
  consolePort: boundConsolePort,
587
784
  mcpPort: boundMcpPort,
588
785
  mcpUrl,
786
+ docsMcpPort: boundDocsMcpPort,
787
+ docsMcpUrl,
589
788
  secret,
590
789
  sessions,
591
790
  consoleState,
@@ -602,6 +801,21 @@ export async function runDev(options: DevOptions = {}): Promise<DevResult> {
602
801
  return { code: 0, plan: session.plan, session };
603
802
  }
604
803
 
804
+ /**
805
+ * Declared vault secret names from the project Manifest (policy scope).
806
+ *
807
+ * @param cwd - Project root
808
+ */
809
+ async function tryLoadVaultSecretNames(cwd: string): Promise<readonly string[]> {
810
+ try {
811
+ const { extractManifest } = await import("../compiler/extract.ts");
812
+ const manifest = await extractManifest({ rootDir: cwd });
813
+ return Object.keys(manifest.vault ?? {});
814
+ } catch {
815
+ return [];
816
+ }
817
+ }
818
+
605
819
  /**
606
820
  * True when a Manifest has no declared flows (scaffold / empty extract).
607
821
  *
@@ -619,9 +833,7 @@ function isSparseManifest(manifest: Manifest): boolean {
619
833
  *
620
834
  * @param cwd - Project root
621
835
  */
622
- async function tryLoadProjectManifest(
623
- cwd: string,
624
- ): Promise<Manifest | null | undefined> {
836
+ async function tryLoadProjectManifest(cwd: string): Promise<Manifest | null | undefined> {
625
837
  let extracted: Manifest | undefined;
626
838
  try {
627
839
  const { extractManifest } = await import("../compiler/extract.ts");
@@ -658,10 +870,7 @@ async function startAppHot(
658
870
  ): Promise<DevAppHandle> {
659
871
  const absoluteEntry = resolve(cwd, entryPath);
660
872
  const runner = resolve(import.meta.dir, "dev-app-runner.ts");
661
- const readyPath = join(
662
- tmpdir(),
663
- `oke-dev-ready-${crypto.randomUUID()}.txt`,
664
- );
873
+ const readyPath = join(tmpdir(), `oke-dev-ready-${crypto.randomUUID()}.txt`);
665
874
  const hostname = "127.0.0.1";
666
875
  const env: Record<string, string> = {
667
876
  ...appEnv,
@@ -696,11 +905,7 @@ async function startAppHot(
696
905
  };
697
906
 
698
907
  try {
699
- const boundPort = await waitForAppReady(
700
- readyPath,
701
- proc,
702
- APP_READY_TIMEOUT_MS,
703
- );
908
+ const boundPort = await waitForAppReady(readyPath, proc, APP_READY_TIMEOUT_MS);
704
909
  // Child prints the App ready line (again on each soft reload).
705
910
  const url = new URL(`http://${hostname}:${boundPort}/`);
706
911
  return { stop, port: boundPort, url };
@@ -726,9 +931,7 @@ async function waitForAppReady(
726
931
  while (Date.now() - started < timeoutMs) {
727
932
  const exitCode = proc.exitCode;
728
933
  if (exitCode !== null) {
729
- throw new Error(
730
- `oke dev: app process exited before ready (code ${exitCode})`,
731
- );
934
+ throw new Error(`oke dev: app process exited before ready (code ${exitCode})`);
732
935
  }
733
936
  if (await Bun.file(readyPath).exists()) {
734
937
  const text = (await Bun.file(readyPath).text()).trim();
@@ -740,44 +943,57 @@ async function waitForAppReady(
740
943
  }
741
944
  await Bun.sleep(25);
742
945
  }
743
- throw new Error(
744
- `oke dev: app did not become ready within ${timeoutMs}ms`,
745
- );
946
+ throw new Error(`oke dev: app did not become ready within ${timeoutMs}ms`);
746
947
  }
747
948
 
748
949
  /**
749
- * CLI entry for `oke dev [--stack|-s [roles]]`.
950
+ * CLI entry for `oke dev [--local|-l] [--docker|-d [roles]]`.
750
951
  *
751
952
  * @param args - Args after `dev`
752
953
  */
753
954
  export async function devCli(args: readonly string[]): Promise<number> {
754
- let stack: boolean | string[] | undefined;
955
+ let docker: boolean | string[] | undefined;
956
+ let local = false;
957
+ let noDbPush = false;
755
958
  let entry: string | undefined;
756
959
  for (let i = 0; i < args.length; i++) {
757
960
  const a = args[i]!;
758
- if (a === "--stack" || a === "-s") {
961
+ if (a === "--local" || a === "-l") {
962
+ local = true;
963
+ } else if (a === "--docker" || a === "-d") {
759
964
  const next = args[i + 1];
760
965
  if (next && !next.startsWith("-") && /^[\w.,]+$/.test(next)) {
761
- stack = next.split(",").map((s) => s.trim()).filter(Boolean);
966
+ docker = next
967
+ .split(",")
968
+ .map((s) => s.trim())
969
+ .filter(Boolean);
762
970
  i++;
763
971
  } else {
764
- stack = true;
972
+ docker = true;
765
973
  }
974
+ } else if (a === "--no-db-push") {
975
+ noDbPush = true;
766
976
  } else if (a === "--entry" || a === "-e") entry = args[++i];
767
977
  else if (a === "--help" || a === "-h") {
768
- console.log(`oke dev [--stack|-s [roles]] [--entry|-e src/app.ts]
978
+ console.log(`oke dev [--local|-l] [--docker|-d [roles]] [--no-db-push] [--entry|-e src/app.ts]
769
979
 
770
980
  Watch · hot reload · Console :6533 · app :6530 · MCP :6535
771
981
  Regenerates client types on every save.
772
- --stack boots infra under docker/ and runs the app on host Bun with prod
773
- store drivers (Postgres/Redis) so local mimics the server.
774
- Partial roles: -s store.sql,store.kv.
982
+ Local mode auto-runs \`oke db push\` when schema.ts changes (opt out: --no-db-push).
983
+
984
+ Bare \`oke dev\` uses .oke/mode (one-time prompt on a TTY; non-TTY → local).
985
+ --local / --docker override for this session only (never write .oke/mode).
986
+ --docker boots infra under docker/ and runs the app on host Bun with prod
987
+ store drivers (Postgres/Redis). Partial roles: -d store.sql,store.kv.
988
+ Change the saved default with \`oke mode local|docker\`.
775
989
  `);
776
990
  return 0;
777
991
  }
778
992
  }
779
993
  const { code } = await runDev({
780
- stack: stack === undefined ? undefined : stack,
994
+ ...(local ? { local: true } : {}),
995
+ ...(docker === undefined ? {} : { docker }),
996
+ ...(noDbPush ? { noDbPush: true } : {}),
781
997
  entry,
782
998
  });
783
999
  return code;