okengine 0.18.5 → 0.19.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 (510) hide show
  1. package/AGENTS.md +18 -10
  2. package/README.md +2 -2
  3. package/package.json +51 -50
  4. package/site/content/docs/ai/llms-txt.mdx +1 -1
  5. package/site/content/docs/ai/meta.json +1 -1
  6. package/site/content/docs/ai/skills.mdx +4 -1
  7. package/site/content/docs/ai/try-it.mdx +58 -0
  8. package/site/content/docs/client/auth.mdx +208 -0
  9. package/site/content/docs/client/calling.mdx +451 -0
  10. package/site/content/docs/client/index.mdx +209 -0
  11. package/site/content/docs/client/live.mdx +234 -0
  12. package/site/content/docs/client/meta.json +5 -0
  13. package/site/content/docs/client/react.mdx +249 -0
  14. package/site/content/docs/elements/ai/agents.mdx +232 -0
  15. package/site/content/docs/elements/ai/index.mdx +341 -0
  16. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  17. package/site/content/docs/elements/ai/meta.json +5 -0
  18. package/site/content/docs/elements/ai/models.mdx +283 -0
  19. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  20. package/site/content/docs/elements/channel/email.mdx +266 -0
  21. package/site/content/docs/elements/channel/index.mdx +344 -0
  22. package/site/content/docs/elements/channel/meta.json +5 -0
  23. package/site/content/docs/elements/channel/push.mdx +218 -0
  24. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  25. package/site/content/docs/elements/channel/sms.mdx +264 -0
  26. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  27. package/site/content/docs/elements/clock/index.mdx +300 -0
  28. package/site/content/docs/elements/clock/meta.json +5 -0
  29. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  30. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  31. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  32. package/site/content/docs/elements/flow/http.mdx +1080 -0
  33. package/site/content/docs/elements/flow/index.mdx +618 -0
  34. package/site/content/docs/elements/flow/meta.json +5 -0
  35. package/site/content/docs/elements/flow/routing.mdx +559 -0
  36. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  37. package/site/content/docs/elements/gate/auth.mdx +371 -0
  38. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  39. package/site/content/docs/elements/gate/index.mdx +418 -0
  40. package/site/content/docs/elements/gate/meta.json +5 -0
  41. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  42. package/site/content/docs/elements/gate/rls.mdx +303 -0
  43. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  44. package/site/content/docs/elements/index.mdx +20 -10
  45. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  46. package/site/content/docs/elements/signal/index.mdx +352 -0
  47. package/site/content/docs/elements/signal/live.mdx +590 -0
  48. package/site/content/docs/elements/signal/meta.json +5 -0
  49. package/site/content/docs/elements/signal/once.mdx +596 -0
  50. package/site/content/docs/elements/store/files.mdx +659 -0
  51. package/site/content/docs/elements/store/index.mdx +310 -0
  52. package/site/content/docs/elements/store/kv.mdx +629 -0
  53. package/site/content/docs/elements/store/meta.json +5 -0
  54. package/site/content/docs/elements/store/search.mdx +946 -0
  55. package/site/content/docs/elements/store/sql.mdx +937 -0
  56. package/site/content/docs/elements/vault/config.mdx +256 -0
  57. package/site/content/docs/elements/vault/index.mdx +356 -0
  58. package/site/content/docs/elements/vault/meta.json +5 -0
  59. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  60. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  61. package/site/content/docs/index.mdx +63 -36
  62. package/site/content/docs/meta.json +8 -7
  63. package/site/content/docs/plugins/cors.mdx +2 -1
  64. package/site/content/docs/plugins/csrf.mdx +4 -1
  65. package/site/content/docs/plugins/headers.mdx +2 -1
  66. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  67. package/site/content/docs/plugins/magic-link.mdx +6 -0
  68. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  69. package/site/content/docs/plugins/oauth.mdx +1 -1
  70. package/site/content/docs/plugins/otp.mdx +12 -1
  71. package/site/content/docs/plugins/passkey.mdx +39 -15
  72. package/site/content/docs/plugins/two-factor.mdx +91 -34
  73. package/site/content/docs/plugins/username.mdx +1 -1
  74. package/site/content/docs/recipes/caddy.mdx +3 -3
  75. package/site/content/docs/recipes/index.mdx +18 -28
  76. package/site/content/docs/recipes/mailpit.mdx +1 -1
  77. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  78. package/site/content/docs/recipes/meta.json +1 -4
  79. package/site/content/docs/recipes/nginx.mdx +2 -2
  80. package/site/content/docs/recipes/openrouter.mdx +283 -0
  81. package/site/content/docs/recipes/pgdog.mdx +3 -3
  82. package/site/content/docs/recipes/postgres.mdx +1 -2
  83. package/site/content/docs/recipes/rustfs.mdx +2 -2
  84. package/site/content/docs/recipes/traefik.mdx +3 -3
  85. package/site/content/docs/reference/cli.mdx +231 -0
  86. package/site/content/docs/reference/configuration.mdx +30 -10
  87. package/site/content/docs/reference/environment-variables.mdx +93 -20
  88. package/site/content/docs/reference/errors.mdx +132 -29
  89. package/site/content/docs/reference/fx.mdx +137 -73
  90. package/site/content/docs/reference/index.mdx +13 -21
  91. package/site/content/docs/reference/meta.json +4 -5
  92. package/site/content/docs/reference/okid.mdx +42 -17
  93. package/site/content/docs/reference/plugins.mdx +4 -3
  94. package/site/content/docs/reference/security.mdx +197 -0
  95. package/site/content/docs/understand/meta.json +5 -0
  96. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  97. package/site/content/docs/understand/the-model.mdx +32 -0
  98. package/site/content/docs/understand/the-problem.mdx +74 -0
  99. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  100. package/src/auth/api-keys.ts +2 -1
  101. package/src/auth/bindings.ts +64 -16
  102. package/src/auth/gate-auth.test.ts +6 -1
  103. package/src/auth/identity.ts +152 -5
  104. package/src/auth/index.ts +29 -0
  105. package/src/auth/invites.ts +2 -1
  106. package/src/auth/method-context.ts +41 -0
  107. package/src/auth/oauth-as/crypto.ts +2 -1
  108. package/src/auth/oauth-as/stores.ts +2 -1
  109. package/src/auth/operator.ts +2 -1
  110. package/src/auth/sessions-jwt.test.ts +77 -0
  111. package/src/auth/sessions.ts +20 -4
  112. package/src/auth/tenants.ts +3 -2
  113. package/src/auth/two-factor-challenge.test.ts +61 -0
  114. package/src/auth/two-factor-challenge.ts +284 -0
  115. package/src/auth/verification.ts +5 -0
  116. package/src/bench/REPORT.md +49 -0
  117. package/src/bench/g03-signal-once.bench.ts +1 -1
  118. package/src/bench/g10-observability-contention.bench.ts +1 -1
  119. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  120. package/src/bench/load-app.ts +2 -10
  121. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  122. package/src/cli/ai-setup/apply.ts +294 -52
  123. package/src/cli/ai-setup/catalog.ts +238 -1342
  124. package/src/cli/ai-setup/index.ts +14 -99
  125. package/src/cli/ai-setup/prompts.ts +60 -443
  126. package/src/cli/ask-seed.test.ts +96 -0
  127. package/src/cli/ask-seed.ts +82 -0
  128. package/src/cli/ask-vault-gaps.test.ts +84 -0
  129. package/src/cli/ask-vault-gaps.ts +146 -0
  130. package/src/cli/build.ts +2 -2
  131. package/src/cli/client-add.test.ts +26 -1
  132. package/src/cli/client-add.ts +106 -27
  133. package/src/cli/db-seed.ts +2 -0
  134. package/src/cli/db.ts +150 -11
  135. package/src/cli/dev.ts +119 -211
  136. package/src/cli/docker-clean.ts +2 -2
  137. package/src/cli/docker-cli.test.ts +1 -1
  138. package/src/cli/doctor-diff.ts +4 -2
  139. package/src/cli/doctor-pii.test.ts +1 -1
  140. package/src/cli/doctor.ts +14 -1
  141. package/src/cli/hero-meta.test.ts +4 -2
  142. package/src/cli/load-config.images.test.ts +8 -10
  143. package/src/cli/load-config.ts +1 -1
  144. package/src/cli/registry.ts +22 -10
  145. package/src/cli/replay.ts +3 -1
  146. package/src/cli/start.ts +1 -1
  147. package/src/cli/tui/keys.ts +2 -3
  148. package/src/client/auth/cookies.ts +74 -0
  149. package/src/client/auth/create-auth-client.ts +636 -0
  150. package/src/client/auth/denials.ts +99 -0
  151. package/src/client/auth/session.ts +283 -0
  152. package/src/client/auth.test.ts +251 -0
  153. package/src/client/auth.ts +39 -114
  154. package/src/client/create-with-session.ts +245 -0
  155. package/src/client/create.ts +51 -5
  156. package/src/client/index.ts +11 -1
  157. package/src/client/live.ts +11 -79
  158. package/src/client/notes-contract.test.ts +5 -10
  159. package/src/client/sse.ts +137 -0
  160. package/src/client/stream.ts +147 -0
  161. package/src/client/transport.test.ts +32 -0
  162. package/src/client/transport.ts +123 -26
  163. package/src/client/types.ts +118 -14
  164. package/src/client-react/index.ts +185 -24
  165. package/src/client-react/use-live-query.ts +13 -3
  166. package/src/compiler/aot.test.ts +7 -4
  167. package/src/compiler/effects-embed.test.ts +56 -0
  168. package/src/compiler/effects-fetch.test.ts +43 -0
  169. package/src/compiler/effects-infer.ts +58 -2
  170. package/src/compiler/extract.test.ts +257 -43
  171. package/src/compiler/extract.ts +566 -48
  172. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  173. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  174. package/src/compiler/fixtures/skyport.expected.json +4 -4
  175. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  176. package/src/compiler/response.ts +2 -2
  177. package/src/compiler/search-writer-isolation.test.ts +40 -0
  178. package/src/config/index.ts +11 -0
  179. package/src/console/server/app.ts +17 -23
  180. package/src/console/server/bind.ts +4 -0
  181. package/src/console/server/console.test.ts +2 -2
  182. package/src/console/server/flows-invoke.test.ts +50 -34
  183. package/src/console/server/flows.ts +436 -255
  184. package/src/console/server/runs-ingest.test.ts +6 -3
  185. package/src/console/server/serve.ts +1 -1
  186. package/src/console/server/signals.test.ts +1 -5
  187. package/src/console/server/signals.ts +8 -6
  188. package/src/console/server/state.ts +6 -1
  189. package/src/console/server/store.ts +1 -1
  190. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  191. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  192. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  193. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  194. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  195. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  196. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  197. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  198. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  199. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  200. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  201. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  202. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  203. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  204. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  205. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  206. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  207. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  208. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  209. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  210. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  211. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  212. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  213. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  214. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  215. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  216. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  217. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  218. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  219. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  220. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  221. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  222. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  223. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  224. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  225. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  226. package/src/console/ui-next/dist/index.html +6 -13
  227. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  228. package/src/console/ui-next/src/client.ts +7 -2
  229. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  230. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  231. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  232. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  233. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  235. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  236. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  237. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  238. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  239. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  240. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  241. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  242. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  243. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  244. package/src/docker/ai-model-status.test.ts +1 -12
  245. package/src/docker/ai-model-status.ts +10 -38
  246. package/src/docker/compose-up.test.ts +100 -0
  247. package/src/docker/compose-up.ts +292 -0
  248. package/src/docker/compose.ts +4 -14
  249. package/src/docker/derive.ts +9 -34
  250. package/src/docker/docker.test.ts +16 -261
  251. package/src/docker/images-config.test.ts +13 -25
  252. package/src/docker/index.ts +1 -24
  253. package/src/docker/recipes/index.ts +0 -23
  254. package/src/docker/recipes/pgdog.ts +6 -4
  255. package/src/docker/stack-id.test.ts +2 -2
  256. package/src/docker/stack-id.ts +0 -1
  257. package/src/docker/types.ts +2 -2
  258. package/src/drivers/ai-anthropic.ts +10 -0
  259. package/src/drivers/ai-openai-compatible.ts +34 -7
  260. package/src/drivers/ai-providers.test.ts +0 -107
  261. package/src/drivers/ai-stream.test.ts +0 -34
  262. package/src/drivers/ai-types.ts +17 -8
  263. package/src/drivers/channel-fcm.ts +6 -1
  264. package/src/drivers/channel-msegat.ts +6 -1
  265. package/src/drivers/channel-resend.ts +5 -1
  266. package/src/drivers/channel-smtp.ts +5 -1
  267. package/src/drivers/channel-sndr.ts +10 -1
  268. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  269. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  270. package/src/drivers/channel-taqnyat.ts +6 -1
  271. package/src/drivers/channel-types.ts +10 -0
  272. package/src/drivers/channel-unifonic.ts +6 -1
  273. package/src/drivers/channel-wa-cloud.ts +6 -1
  274. package/src/drivers/channel-webpush.ts +6 -1
  275. package/src/drivers/external.ts +33 -0
  276. package/src/drivers/index.ts +0 -10
  277. package/src/drivers/meilisearch.ts +6 -0
  278. package/src/drivers/oauth-types.ts +4 -0
  279. package/src/drivers/postgres.test.ts +74 -3
  280. package/src/drivers/postgres.ts +175 -6
  281. package/src/drivers/signal-types.ts +5 -5
  282. package/src/drivers/types.ts +7 -0
  283. package/src/drivers/vault-types.ts +5 -0
  284. package/src/elements/ai/declare.ts +17 -4
  285. package/src/elements/ai/eval.ts +1 -1
  286. package/src/elements/ai/mcp-http.ts +1 -1
  287. package/src/elements/ai/pii.ts +1 -1
  288. package/src/elements/ai/providers.test.ts +289 -0
  289. package/src/elements/ai/providers.ts +169 -0
  290. package/src/elements/ai/runtime.ts +68 -5
  291. package/src/elements/ai/schema.ts +2 -2
  292. package/src/elements/ai.test.ts +7 -7
  293. package/src/elements/ai.ts +17 -1
  294. package/src/elements/channel/runtime.ts +105 -12
  295. package/src/elements/clock/cron-fields.test.ts +144 -0
  296. package/src/elements/clock/cron-fields.ts +185 -0
  297. package/src/elements/clock/declare.ts +247 -3
  298. package/src/elements/clock.ts +21 -1
  299. package/src/elements/index.ts +15 -0
  300. package/src/elements/signal/chaos-child.ts +1 -2
  301. package/src/elements/signal/declare.ts +65 -37
  302. package/src/elements/signal/delivery-modes.test.ts +11 -31
  303. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  304. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  305. package/src/elements/signal/key-ordering.test.ts +6 -26
  306. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  307. package/src/elements/signal/optional-emit.test.ts +6 -9
  308. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  309. package/src/elements/signal/orphan-messages.test.ts +4 -15
  310. package/src/elements/signal/reconcile.test.ts +2 -2
  311. package/src/elements/signal/runtime.ts +1 -1
  312. package/src/elements/signal/schema-emit.test.ts +4 -8
  313. package/src/elements/signal.test.ts +24 -49
  314. package/src/elements/signal.ts +9 -2
  315. package/src/elements/store/domain-ddl.test.ts +1 -1
  316. package/src/elements/store/live-http.test.ts +9 -4
  317. package/src/elements/store/prepare-row.test.ts +123 -0
  318. package/src/elements/store/resource.ts +43 -12
  319. package/src/elements/store/schema-decl.ts +117 -0
  320. package/src/elements/store/search-backfill.ts +205 -0
  321. package/src/elements/store/search-bind.ts +66 -0
  322. package/src/elements/store/search-bm25.ts +78 -0
  323. package/src/elements/store/search-ddl.ts +156 -0
  324. package/src/elements/store/search-embed-flow.ts +141 -0
  325. package/src/elements/store/search-errors.ts +34 -0
  326. package/src/elements/store/search-fusion.ts +112 -0
  327. package/src/elements/store/search-lsh.ts +179 -0
  328. package/src/elements/store/search-runtime.ts +300 -0
  329. package/src/elements/store/search.test.ts +144 -0
  330. package/src/elements/store/sql-session.test.ts +1 -0
  331. package/src/elements/store/sql-session.ts +83 -3
  332. package/src/elements/store/table.ts +23 -2
  333. package/src/elements/store/upsert-app.test.ts +1 -3
  334. package/src/elements/store.ts +50 -0
  335. package/src/full.ts +4 -2
  336. package/src/http.ts +4 -1
  337. package/src/i18n/catalogs/ar.ts +14 -6
  338. package/src/i18n/catalogs/en.ts +14 -6
  339. package/src/index.ts +6 -1
  340. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  341. package/src/kernel/adopt-routes.ts +9 -1
  342. package/src/kernel/app-auth.ts +5 -0
  343. package/src/kernel/app.ts +102 -9
  344. package/src/kernel/auto-cache.test.ts +6 -12
  345. package/src/kernel/auto-registry.test.ts +3 -3
  346. package/src/kernel/boot-bind/ai.test.ts +22 -19
  347. package/src/kernel/boot-bind/ai.ts +5 -43
  348. package/src/kernel/boot-bind/clock.ts +23 -5
  349. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  350. package/src/kernel/boot.test.ts +11 -6
  351. package/src/kernel/boot.ts +66 -16
  352. package/src/kernel/boundary-contract.ts +93 -0
  353. package/src/kernel/budget.test.ts +1 -1
  354. package/src/kernel/call.ts +89 -0
  355. package/src/kernel/capability.ts +6 -0
  356. package/src/kernel/client-descriptor.ts +123 -0
  357. package/src/kernel/clock-timezone.test.ts +80 -0
  358. package/src/kernel/correlation.test.ts +1 -1
  359. package/src/kernel/dry-run.ts +11 -8
  360. package/src/kernel/effects-stamping.test.ts +42 -9
  361. package/src/kernel/effects.test.ts +4 -2
  362. package/src/kernel/effects.ts +51 -9
  363. package/src/kernel/errors-channel.ts +17 -0
  364. package/src/kernel/errors-live-resume.ts +3 -2
  365. package/src/kernel/errors-tenant.ts +7 -4
  366. package/src/kernel/errors.registry-helpers.ts +96 -0
  367. package/src/kernel/errors.registry.test.ts +139 -25
  368. package/src/kernel/errors.ts +102 -23
  369. package/src/kernel/external-effects.test.ts +196 -0
  370. package/src/kernel/flow.test.ts +13 -4
  371. package/src/kernel/flow.ts +69 -71
  372. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  373. package/src/kernel/fx-dead-letters.test.ts +3 -3
  374. package/src/kernel/fx-fetch.ts +55 -0
  375. package/src/kernel/fx-live-stream.ts +2 -2
  376. package/src/kernel/fx-live.test.ts +8 -8
  377. package/src/kernel/fx.test.ts +17 -11
  378. package/src/kernel/fx.ts +180 -57
  379. package/src/kernel/horizontal-child.ts +1 -1
  380. package/src/kernel/http-query.test.ts +3 -6
  381. package/src/kernel/index.ts +13 -3
  382. package/src/kernel/instance-id.ts +1 -1
  383. package/src/kernel/instances.test.ts +40 -0
  384. package/src/kernel/instances.ts +47 -9
  385. package/src/kernel/live-http.test.ts +6 -7
  386. package/src/kernel/live-http.ts +6 -2
  387. package/src/kernel/live-resume.test.ts +1 -1
  388. package/src/kernel/mcp-tool.test.ts +3 -3
  389. package/src/kernel/on.ts +98 -4
  390. package/src/kernel/pipeline.test.ts +12 -15
  391. package/src/kernel/plugin-elements.test.ts +1 -1
  392. package/src/kernel/stamp-http.test.ts +3 -3
  393. package/src/kernel/triggers.ts +210 -95
  394. package/src/kernel-entry.ts +0 -1
  395. package/src/manifest/diff.ts +11 -1
  396. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  397. package/src/manifest/types.ts +46 -2
  398. package/src/mcp/docs-index.ts +3 -3
  399. package/src/mcp/docs-mcp.test.ts +2 -2
  400. package/src/mcp/docs-tools.ts +1 -1
  401. package/src/okid.test.ts +56 -0
  402. package/src/okid.ts +55 -11
  403. package/src/plugins/anonymous.ts +8 -4
  404. package/src/plugins/auth/shared.ts +27 -3
  405. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  406. package/src/plugins/auth-methods.security.test.ts +561 -23
  407. package/src/plugins/config-source.ts +1 -1
  408. package/src/plugins/csrf.test.ts +65 -0
  409. package/src/plugins/index.ts +2 -0
  410. package/src/plugins/magic-link.ts +36 -16
  411. package/src/plugins/oauth/shared.ts +3 -1
  412. package/src/plugins/oauth.ts +22 -14
  413. package/src/plugins/otp.ts +37 -21
  414. package/src/plugins/passkey-webauthn.ts +9 -6
  415. package/src/plugins/passkey.ts +98 -26
  416. package/src/plugins/pre-account-hijack.test.ts +223 -0
  417. package/src/plugins/two-factor.ts +466 -37
  418. package/src/plugins/username.ts +19 -7
  419. package/src/release/build-lib.ts +1 -0
  420. package/src/release/limits.ts +2 -2
  421. package/src/release/measure.ts +1 -1
  422. package/src/runs/collect.test.ts +0 -2
  423. package/src/runtime/json-code-block.test.ts +250 -20
  424. package/src/runtime/json-code-block.ts +1261 -41
  425. package/src/term.test.ts +1 -1
  426. package/src/test/create-test-app.test.ts +12 -12
  427. package/src/test/live-signals.test.ts +6 -6
  428. package/src/test/provisions.integration.test.ts +10 -14
  429. package/src/test/reset-element-registries.ts +1 -1
  430. package/src/test/tenant-isolation.test.ts +12 -6
  431. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  432. package/site/content/docs/deployment/docker.mdx +0 -227
  433. package/site/content/docs/deployment/index.mdx +0 -83
  434. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  435. package/site/content/docs/deployment/meta.json +0 -5
  436. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  437. package/site/content/docs/elements/ai.mdx +0 -385
  438. package/site/content/docs/elements/channel.mdx +0 -346
  439. package/site/content/docs/elements/clock.mdx +0 -244
  440. package/site/content/docs/elements/flow.mdx +0 -420
  441. package/site/content/docs/elements/gate.mdx +0 -436
  442. package/site/content/docs/elements/signal.mdx +0 -380
  443. package/site/content/docs/elements/store.mdx +0 -1099
  444. package/site/content/docs/elements/vault.mdx +0 -405
  445. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  446. package/site/content/docs/get-started/index.mdx +0 -43
  447. package/site/content/docs/get-started/installation.mdx +0 -220
  448. package/site/content/docs/get-started/introduction.mdx +0 -144
  449. package/site/content/docs/get-started/meta.json +0 -13
  450. package/site/content/docs/get-started/project-structure.mdx +0 -925
  451. package/site/content/docs/get-started/testing.mdx +0 -328
  452. package/site/content/docs/get-started/why.mdx +0 -114
  453. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  454. package/site/content/docs/recipes/ollama.mdx +0 -142
  455. package/site/content/docs/recipes/sglang.mdx +0 -105
  456. package/site/content/docs/recipes/vllm.mdx +0 -106
  457. package/site/content/docs/reference/cli.md +0 -232
  458. package/site/content/docs/reference/client.mdx +0 -469
  459. package/site/content/docs/reference/security.md +0 -74
  460. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  461. package/src/cli/ai-setup/recommend.test.ts +0 -225
  462. package/src/cli/ai-setup/recommend.ts +0 -225
  463. package/src/cli/dev-controls.test.ts +0 -122
  464. package/src/cli/dev-controls.ts +0 -164
  465. package/src/cli/tui/DevLive.tsx +0 -126
  466. package/src/console/ui-next/dist/assets/access-page-3-EFj-2G.js +0 -4
  467. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  468. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  469. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  470. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  471. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  472. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  473. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  474. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  475. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  476. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  477. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  478. package/src/console/ui-next/dist/assets/flows-page-cVFnA4HH.js +0 -1
  479. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  480. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  481. package/src/console/ui-next/dist/assets/index-CMIgUbD0.js +0 -66
  482. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  483. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  484. package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.js +0 -4
  485. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  486. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  487. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  488. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  489. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  490. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  491. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  492. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  493. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  494. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  495. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  496. package/src/console/ui-next/dist/assets/units-page-l8FeKfnP.js +0 -1
  497. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  498. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  499. package/src/console/ui-next/dist/assets/vault-page-CL-d_mLE.js +0 -2
  500. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  501. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  502. package/src/docker/ollama-pull.ts +0 -232
  503. package/src/docker/recipes/llama-cpp.ts +0 -298
  504. package/src/docker/recipes/ollama.ts +0 -44
  505. package/src/docker/recipes/sglang.ts +0 -55
  506. package/src/docker/recipes/vllm.ts +0 -44
  507. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  508. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  509. package/src/drivers/ai-ollama.ts +0 -389
  510. package/src/drivers/ollama.ts +0 -14
@@ -2,48 +2,29 @@
2
2
  * Interactive Clack prompts for `oke ai setup` / create-oke AI wizard.
3
3
  */
4
4
 
5
- import { isCancel, note, password, select, text } from "@clack/prompts";
5
+ import { isCancel, password, select, text } from "@clack/prompts";
6
6
  import {
7
7
  CLOUD_PROVIDERS,
8
- MODEL_TIERS,
8
+ aiProviderSelectOptions,
9
9
  cloudChatModels,
10
- llamaCppModelsForTier,
11
- modelsForTier,
12
10
  recommendCloudChat,
13
- recommendForRole,
14
- recommendForTier,
15
- recommendLlamaCppForTier,
16
- type CatalogModel,
17
- type ModelTier,
18
11
  } from "./catalog.ts";
19
12
  import type { AiSetupApplyInput } from "./apply.ts";
20
- import { LLAMA_CPP_IMAGE } from "../../docker/recipes/llama-cpp.ts";
21
- import {
22
- detectMachineInfo,
23
- detectOllama,
24
- detectTotalRamGb,
25
- isInstalled,
26
- type OllamaDetectResult,
27
- } from "./detect-ollama.ts";
28
- import {
29
- formatLlamaCppBanner,
30
- formatModelRow,
31
- formatModelTableHeader,
32
- formatOllamaBanner,
33
- suggestTierForRam,
34
- } from "./recommend.ts";
35
13
 
36
- /** Provider menu value. */
14
+ /** Provider menu value (cloud + host-side OpenAI-compatible). */
37
15
  export type AiSetupProvider =
38
- | "llama-cpp"
39
- | "ollama"
40
- | "vllm"
41
- | "sglang"
16
+ | "openrouter"
42
17
  | "openai"
43
18
  | "anthropic"
19
+ | "groq"
20
+ | "together"
21
+ | "deepseek"
22
+ | "mistral"
23
+ | "xai"
24
+ | "deepinfra"
25
+ | "vercel"
44
26
  | "gemini"
45
27
  | "lmstudio"
46
- | "openrouter"
47
28
  | "custom";
48
29
 
49
30
  /** Sentinel for Back in AI setup selects. */
@@ -53,14 +34,12 @@ type Back = typeof BACK;
53
34
  /**
54
35
  * Run the interactive AI setup prompts.
55
36
  *
56
- * @param options - Preselected provider / seams
37
+ * @param options - Preselected provider
57
38
  * @returns Apply input or null on cancel
58
39
  */
59
40
  export async function askAiSetup(
60
41
  options: {
61
42
  readonly provider?: string;
62
- readonly detect?: () => Promise<OllamaDetectResult>;
63
- readonly ramGb?: number | null;
64
43
  } = {},
65
44
  ): Promise<AiSetupApplyInput | null> {
66
45
  const rawProvider = options.provider?.trim() ?? "";
@@ -70,346 +49,16 @@ export async function askAiSetup(
70
49
  if (!provider) {
71
50
  const value = await select({
72
51
  message: "AI Provider",
73
- options: [
74
- {
75
- value: "llama-cpp",
76
- label: "llama.cpp (Local)",
77
- hint: "default · Docker Hub ai/ · recommend for your RAM",
78
- },
79
- {
80
- value: "ollama",
81
- label: "Ollama (Local)",
82
- hint: "detect models · recommend for your RAM",
83
- },
84
- {
85
- value: "vllm",
86
- label: "vLLM (self-hosted GPU)",
87
- hint: "multi-user / production concurrency",
88
- },
89
- {
90
- value: "sglang",
91
- label: "SGLang (self-hosted GPU)",
92
- hint: "structured / agent workloads",
93
- },
94
- { value: "openai", label: "OpenAI" },
95
- { value: "anthropic", label: "Anthropic" },
96
- { value: "gemini", label: "Gemini", hint: "OpenAI-compatible proxy URL" },
97
- { value: "lmstudio", label: "LM Studio" },
98
- { value: "openrouter", label: "OpenRouter" },
99
- { value: "custom", label: "Custom OpenAI Compatible" },
100
- ],
52
+ options: [...aiProviderSelectOptions()],
53
+ initialValue: "openrouter",
101
54
  });
102
55
  if (isCancel(value)) return null;
103
56
  provider = String(value) as AiSetupProvider;
104
57
  }
105
58
 
106
- if (provider === "llama-cpp") {
107
- return askLlamaCppPath({
108
- ramGb: options.ramGb === undefined ? detectTotalRamGb() : options.ramGb,
109
- });
110
- }
111
- if (provider === "ollama") {
112
- return askOllamaPath({
113
- detect: options.detect ?? detectOllama,
114
- ramGb: options.ramGb === undefined ? detectTotalRamGb() : options.ramGb,
115
- });
116
- }
117
- if (provider === "vllm" || provider === "sglang") {
118
- return askSelfHostedGpuPath(provider);
119
- }
120
-
121
59
  return askCloudPath(provider);
122
60
  }
123
61
 
124
- /**
125
- * llama.cpp path — banner → Select model (tier) / Manual (Docker Hub `ai/`).
126
- *
127
- * @param options - RAM override
128
- */
129
- async function askLlamaCppPath(options: {
130
- readonly ramGb: number | null;
131
- }): Promise<AiSetupApplyInput | null> {
132
- const machine = detectMachineInfo();
133
- const ramGb = options.ramGb ?? machine.ramGb;
134
- note(formatLlamaCppBanner({ ...machine, ramGb }), "llama.cpp");
135
-
136
- mode: for (;;) {
137
- const mode = await selectWithBack(
138
- "How do you want to pick models?",
139
- [
140
- {
141
- value: "select",
142
- label: "Select model",
143
- hint: "Ultra Fast · Fast · Balanced · Smart",
144
- },
145
- {
146
- value: "manual",
147
- label: "Manual model",
148
- hint: "type any Docker Hub ai/ model id",
149
- },
150
- ],
151
- "select",
152
- false,
153
- );
154
- if (mode === null) return null;
155
-
156
- if (mode === "manual") {
157
- const id = await askOtherModelId("granite3.3:2b");
158
- if (id === null) return null;
159
- return finishLlamaCpp(id);
160
- }
161
-
162
- tier: for (;;) {
163
- const tierPick = await selectWithBack(
164
- "Select model",
165
- MODEL_TIERS.map((t) => ({
166
- value: t.value,
167
- label: t.label,
168
- hint: t.hint,
169
- })),
170
- suggestTierForRam(ramGb),
171
- true,
172
- );
173
- if (tierPick === null) return null;
174
- if (tierPick === BACK) continue mode;
175
-
176
- const tier = tierPick as ModelTier;
177
- const recommended = recommendLlamaCppForTier(tier, ramGb);
178
-
179
- how: for (;;) {
180
- const how = await selectWithBack(
181
- MODEL_TIERS.find((t) => t.value === tier)?.label ?? tier,
182
- [
183
- {
184
- value: "recommended",
185
- label: "Use recommended",
186
- hint: formatModelRow(recommended),
187
- },
188
- {
189
- value: "manual",
190
- label: "Select manually",
191
- hint: "up to 20 models in this tier",
192
- },
193
- ],
194
- "recommended",
195
- true,
196
- );
197
- if (how === null) return null;
198
- if (how === BACK) continue tier;
199
-
200
- if (how === "recommended") {
201
- return finishLlamaCpp(recommended.id, recommended);
202
- }
203
-
204
- const list = llamaCppModelsForTier(tier);
205
- note(formatModelTableHeader(), "Select model");
206
- const picked = await selectWithBack(
207
- "Pick a model",
208
- list.map((m) => ({
209
- value: m.id,
210
- label: formatModelRow(m),
211
- hint: m.hint,
212
- })),
213
- recommended.id,
214
- true,
215
- );
216
- if (picked === null) return null;
217
- if (picked === BACK) continue how;
218
- const model = list.find((m) => m.id === picked);
219
- return finishLlamaCpp(picked, model);
220
- }
221
- }
222
- }
223
- }
224
-
225
- /**
226
- * Build apply input for llama.cpp — OpenAI-compatible + curated image pin.
227
- *
228
- * @param chatId - Docker Hub `ai/` model id (org prefix optional)
229
- * @param catalog - Optional catalog row (modalities)
230
- */
231
- function finishLlamaCpp(chatId: string, catalog?: CatalogModel): AiSetupApplyInput {
232
- const id = chatId.replace(/^ai\//, "");
233
- return {
234
- driver: "openai-compatible",
235
- baseUrl: process.env.OKE_AI_URL ?? "http://127.0.0.1:8080/v1",
236
- chatModel: id,
237
- visionModel: catalog?.modalities.includes("vision") ? id : null,
238
- embedModel: null,
239
- image: LLAMA_CPP_IMAGE,
240
- };
241
- }
242
-
243
- /**
244
- * vLLM / SGLang — Hugging Face model path for self-hosted GPU inference.
245
- *
246
- * @param provider - vllm | sglang
247
- */
248
- async function askSelfHostedGpuPath(
249
- provider: "vllm" | "sglang",
250
- ): Promise<AiSetupApplyInput | null> {
251
- const port = provider === "vllm" ? 8000 : 30000;
252
- const image =
253
- provider === "vllm" ? "vllm/vllm-openai:v0.27.1" : "lmsysorg/sglang:v0.5.17-runtime";
254
- const model = await text({
255
- message: "Hugging Face model id",
256
- placeholder: "Qwen/Qwen3-0.6B",
257
- initialValue: "Qwen/Qwen3-0.6B",
258
- validate: (v) => {
259
- if (!v?.trim()) return "Model id required";
260
- return undefined;
261
- },
262
- });
263
- if (isCancel(model)) return null;
264
- return {
265
- driver: "openai-compatible",
266
- baseUrl: process.env.OKE_AI_URL ?? `http://127.0.0.1:${port}/v1`,
267
- chatModel: String(model).trim(),
268
- visionModel: null,
269
- embedModel: null,
270
- image,
271
- };
272
- }
273
-
274
- /**
275
- * Ollama path — banner → Select model (tier) / Manual model.
276
- *
277
- * @param options - Detect / RAM
278
- */
279
- async function askOllamaPath(options: {
280
- readonly detect: () => Promise<OllamaDetectResult>;
281
- readonly ramGb: number | null;
282
- }): Promise<AiSetupApplyInput | null> {
283
- const detected = await options.detect();
284
- const machine = detectMachineInfo();
285
- const ramGb = options.ramGb ?? machine.ramGb;
286
- const detectedIds =
287
- detected.installed.length > 0
288
- ? detected.installed.slice(0, 12)
289
- : detected.curatedInstalled.map((m) => m.id);
290
-
291
- note(formatOllamaBanner({ ...machine, ramGb }, detectedIds), "Ollama");
292
-
293
- mode: for (;;) {
294
- const mode = await selectWithBack(
295
- "How do you want to pick models?",
296
- [
297
- {
298
- value: "select",
299
- label: "Select model",
300
- hint: "Ultra Fast · Fast · Balanced · Smart",
301
- },
302
- {
303
- value: "manual",
304
- label: "Manual model",
305
- hint: "type any Ollama model id",
306
- },
307
- ],
308
- "select",
309
- false,
310
- );
311
- if (mode === null) return null;
312
-
313
- if (mode === "manual") {
314
- const id = await askOtherModelId("gemma4:e4b");
315
- if (id === null) return null;
316
- return finishOllama(id, detected);
317
- }
318
-
319
- tier: for (;;) {
320
- const tierPick = await selectWithBack(
321
- "Select model",
322
- MODEL_TIERS.map((t) => ({
323
- value: t.value,
324
- label: t.label,
325
- hint: t.hint,
326
- })),
327
- suggestTierForRam(ramGb),
328
- true,
329
- );
330
- if (tierPick === null) return null;
331
- if (tierPick === BACK) continue mode;
332
-
333
- const tier = tierPick as ModelTier;
334
- const recommended = recommendForTier(tier, ramGb);
335
-
336
- how: for (;;) {
337
- const how = await selectWithBack(
338
- MODEL_TIERS.find((t) => t.value === tier)?.label ?? tier,
339
- [
340
- {
341
- value: "recommended",
342
- label: "Use recommended",
343
- hint: formatModelRow(recommended),
344
- },
345
- {
346
- value: "manual",
347
- label: "Select manually",
348
- hint: "up to 10 models in this tier",
349
- },
350
- ],
351
- "recommended",
352
- true,
353
- );
354
- if (how === null) return null;
355
- if (how === BACK) continue tier;
356
-
357
- if (how === "recommended") {
358
- return finishOllama(recommended.id, detected, recommended);
359
- }
360
-
361
- const list = modelsForTier(tier);
362
- note(formatModelTableHeader(), "Select model");
363
- const picked = await selectWithBack(
364
- "Pick a model",
365
- list.map((m) => ({
366
- value: m.id,
367
- label: formatModelRow(m),
368
- hint: isInstalled(m.id, detected.installed) ? "installed" : m.hint,
369
- })),
370
- recommended.id,
371
- true,
372
- );
373
- if (picked === null) return null;
374
- if (picked === BACK) continue how;
375
- const model = list.find((m) => m.id === picked);
376
- return finishOllama(picked, detected, model);
377
- }
378
- }
379
- }
380
- }
381
-
382
- /**
383
- * Build apply input for Ollama — embed default, optional silent pull.
384
- *
385
- * @param chatId - Chat model id
386
- * @param detected - Detect result
387
- * @param catalog - Optional catalog row (modalities)
388
- */
389
- async function finishOllama(
390
- chatId: string,
391
- detected: OllamaDetectResult,
392
- catalog?: CatalogModel,
393
- ): Promise<AiSetupApplyInput> {
394
- const embed = recommendForRole("embed");
395
- const visionModel = catalog?.modalities.includes("vision") ? chatId : null;
396
-
397
- const needed = [chatId, embed.id].filter(
398
- (id) => detected.available && !isInstalled(id, detected.installed),
399
- );
400
- if (needed.length > 0 && detected.available) {
401
- await pullModels(needed, detected.baseUrl);
402
- }
403
-
404
- return {
405
- driver: "ollama",
406
- baseUrl: detected.baseUrl,
407
- chatModel: chatId,
408
- visionModel,
409
- embedModel: embed.id,
410
- };
411
- }
412
-
413
62
  /**
414
63
  * Cloud / openai-compatible path — API token → Select model / Manual.
415
64
  *
@@ -417,26 +66,30 @@ async function finishOllama(
417
66
  */
418
67
  async function askCloudPath(provider: AiSetupProvider): Promise<AiSetupApplyInput | null> {
419
68
  const meta = CLOUD_PROVIDERS.find((p) => p.value === provider);
420
- const driver = meta?.driver ?? "openai-compatible";
421
-
422
- let baseUrl: string | undefined = meta?.baseUrl;
423
- if (provider === "custom" || provider === "gemini" || !baseUrl) {
424
- if (provider !== "anthropic") {
425
- const urlValue = await text({
426
- message:
427
- provider === "gemini"
428
- ? "OpenAI-compatible base URL for Gemini"
429
- : "OpenAI-compatible base URL",
430
- placeholder: baseUrl ?? "https://api.example.com/v1",
431
- initialValue: baseUrl ?? "",
432
- });
433
- if (isCancel(urlValue)) return null;
434
- const trimmed = String(urlValue).trim();
435
- baseUrl = trimmed.length > 0 ? trimmed : undefined;
436
- }
69
+ if (!meta) {
70
+ throw new Error(`oke ai setup: unknown cloud provider "${provider}"`);
71
+ }
72
+ const driver = meta.driver;
73
+ const declProvider = meta.provider ?? meta.value;
74
+
75
+ let baseUrl: string | undefined = meta.baseUrl;
76
+ const needsUrlPrompt =
77
+ meta.promptBaseUrl === true || (baseUrl === undefined && driver !== "anthropic");
78
+ if (needsUrlPrompt) {
79
+ const urlValue = await text({
80
+ message: "OpenAI-compatible base URL",
81
+ placeholder: baseUrl ?? "https://api.example.com/v1",
82
+ initialValue: baseUrl ?? "",
83
+ validate: (v) => {
84
+ if (!v?.trim()) return "Base URL is required for this provider";
85
+ return undefined;
86
+ },
87
+ });
88
+ if (isCancel(urlValue)) return null;
89
+ baseUrl = String(urlValue).trim();
437
90
  }
438
91
 
439
- const apiKeyEnv = meta?.apiKeyEnv;
92
+ const apiKeyEnv = meta.apiKeyEnv;
440
93
  let apiKey: string | undefined;
441
94
  if (apiKeyEnv) {
442
95
  const token = await password({
@@ -450,14 +103,34 @@ async function askCloudPath(provider: AiSetupProvider): Promise<AiSetupApplyInpu
450
103
  apiKey = String(token).trim();
451
104
  }
452
105
 
106
+ const finish = (chatModel: string): AiSetupApplyInput => {
107
+ // Registry openai-compat: omit baseUrl so ai.model auto-resolves.
108
+ const omitBase =
109
+ meta.baseUrl !== undefined && !meta.promptBaseUrl && driver === "openai-compatible";
110
+ return {
111
+ driver,
112
+ provider: declProvider,
113
+ ...(omitBase || baseUrl === undefined ? {} : { baseUrl }),
114
+ chatModel,
115
+ visionModel: null,
116
+ embedModel: null,
117
+ ...(apiKeyEnv ? { apiKeyEnv, ...(apiKey ? { apiKey } : {}) } : {}),
118
+ };
119
+ };
120
+
453
121
  for (;;) {
122
+ const models = cloudChatModels(provider);
123
+ const selectHint =
124
+ provider === "openrouter"
125
+ ? "routers + popular models"
126
+ : `up to ${Math.min(10, Math.max(models.length, 1))} curated models`;
454
127
  const mode = await selectWithBack(
455
128
  "How do you want to pick models?",
456
129
  [
457
130
  {
458
131
  value: "select",
459
132
  label: "Select model",
460
- hint: "up to 10 latest curated models",
133
+ hint: selectHint,
461
134
  },
462
135
  {
463
136
  value: "manual",
@@ -473,28 +146,13 @@ async function askCloudPath(provider: AiSetupProvider): Promise<AiSetupApplyInpu
473
146
  if (mode === "manual") {
474
147
  const id = await askOtherModelId(recommendCloudChat(provider));
475
148
  if (id === null) return null;
476
- return {
477
- driver,
478
- ...(baseUrl !== undefined ? { baseUrl } : {}),
479
- chatModel: id,
480
- visionModel: null,
481
- embedModel: null,
482
- ...(apiKeyEnv ? { apiKeyEnv, ...(apiKey ? { apiKey } : {}) } : {}),
483
- };
149
+ return finish(id);
484
150
  }
485
151
 
486
- const models = cloudChatModels(provider);
487
152
  if (models.length === 0) {
488
153
  const id = await askOtherModelId(recommendCloudChat(provider));
489
154
  if (id === null) return null;
490
- return {
491
- driver,
492
- ...(baseUrl !== undefined ? { baseUrl } : {}),
493
- chatModel: id,
494
- visionModel: null,
495
- embedModel: null,
496
- ...(apiKeyEnv ? { apiKeyEnv, ...(apiKey ? { apiKey } : {}) } : {}),
497
- };
155
+ return finish(id);
498
156
  }
499
157
 
500
158
  const initial = recommendCloudChat(provider);
@@ -511,14 +169,7 @@ async function askCloudPath(provider: AiSetupProvider): Promise<AiSetupApplyInpu
511
169
  if (chat === null) return null;
512
170
  if (chat === BACK) continue;
513
171
 
514
- return {
515
- driver,
516
- ...(baseUrl !== undefined ? { baseUrl } : {}),
517
- chatModel: chat,
518
- visionModel: null,
519
- embedModel: null,
520
- ...(apiKeyEnv ? { apiKeyEnv, ...(apiKey ? { apiKey } : {}) } : {}),
521
- };
172
+ return finish(chat);
522
173
  }
523
174
  }
524
175
 
@@ -557,37 +208,3 @@ async function askOtherModelId(placeholder: string): Promise<string | null> {
557
208
  if (isCancel(value)) return null;
558
209
  return String(value).trim();
559
210
  }
560
-
561
- /**
562
- * Pull each model via the Ollama HTTP API (`POST /api/pull`).
563
- *
564
- * @param models - Model ids
565
- * @param baseUrl - Ollama server base URL
566
- */
567
- async function pullModels(models: readonly string[], baseUrl: string): Promise<void> {
568
- const base = baseUrl.replace(/\/+$/, "");
569
- for (const id of models) {
570
- console.log(`ollama: pulling ${id} via ${base}/api/pull…`);
571
- try {
572
- const tags = await fetch(`${base}/api/tags`);
573
- if (!tags.ok) {
574
- throw new Error(`GET ${base}/api/tags → ${tags.status}`);
575
- }
576
- const res = await fetch(`${base}/api/pull`, {
577
- method: "POST",
578
- headers: { "content-type": "application/json" },
579
- body: JSON.stringify({ model: id, stream: false }),
580
- });
581
- if (!res.ok) {
582
- const body = await res.text().catch(() => "");
583
- throw new Error(
584
- `POST ${base}/api/pull → ${res.status}${body ? ` ${body.slice(0, 120)}` : ""}`,
585
- );
586
- }
587
- await res.arrayBuffer();
588
- } catch (err) {
589
- const msg = err instanceof Error ? err.message : String(err);
590
- console.error(`oke ai setup: pull ${id} failed (continuing) — ${msg}`);
591
- }
592
- }
593
- }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Tests for the one-shot seed prompt after `oke db push`.
3
+ */
4
+
5
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { describe, expect, test } from "bun:test";
9
+ import { maybeAskSeed } from "./ask-seed.ts";
10
+ import { PROJECT_STATE_REL } from "./project-state.ts";
11
+
12
+ describe("maybeAskSeed", () => {
13
+ test("skips when non-TTY", async () => {
14
+ const dir = mkdtempSync(join(tmpdir(), "ask-seed-"));
15
+ try {
16
+ mkdirSync(join(dir, "src", "db", "seed"), { recursive: true });
17
+ writeFileSync(join(dir, "src", "db", "seed", "index.ts"), "export default {};\n");
18
+ let seeded = false;
19
+ await maybeAskSeed({
20
+ cwd: dir,
21
+ env: "test",
22
+ stdinIsTTY: false,
23
+ confirmFn: async () => true,
24
+ seedFn: async () => {
25
+ seeded = true;
26
+ return 0;
27
+ },
28
+ });
29
+ expect(seeded).toBe(false);
30
+ } finally {
31
+ rmSync(dir, { recursive: true, force: true });
32
+ }
33
+ });
34
+
35
+ test("re-asks when the seed identity changes", async () => {
36
+ const dir = mkdtempSync(join(tmpdir(), "ask-seed-"));
37
+ try {
38
+ mkdirSync(join(dir, "src", "db", "seed"), { recursive: true });
39
+ writeFileSync(join(dir, "src", "db", "seed", "index.ts"), "export default {};\n");
40
+ mkdirSync(join(dir, ".oke"), { recursive: true });
41
+ writeFileSync(
42
+ join(dir, PROJECT_STATE_REL),
43
+ `${JSON.stringify({ seededAt: "2026-01-01T00:00:00.000Z", seed: "notes" }, null, 2)}\n`,
44
+ );
45
+ let calls = 0;
46
+ await maybeAskSeed({
47
+ cwd: dir,
48
+ env: "test",
49
+ stdinIsTTY: true,
50
+ confirmFn: async () => true,
51
+ seedFn: async () => {
52
+ calls += 1;
53
+ return 0;
54
+ },
55
+ });
56
+ expect(calls).toBe(1);
57
+ } finally {
58
+ rmSync(dir, { recursive: true, force: true });
59
+ }
60
+ });
61
+
62
+ test("asks once then writes .oke/state.json seededAt", async () => {
63
+ const dir = mkdtempSync(join(tmpdir(), "ask-seed-"));
64
+ try {
65
+ mkdirSync(join(dir, "src", "db", "seed"), { recursive: true });
66
+ writeFileSync(join(dir, "src", "db", "seed", "index.ts"), "export default {};\n");
67
+ let calls = 0;
68
+ await maybeAskSeed({
69
+ cwd: dir,
70
+ env: "test",
71
+ stdinIsTTY: true,
72
+ confirmFn: async () => true,
73
+ seedFn: async () => {
74
+ calls += 1;
75
+ return 0;
76
+ },
77
+ });
78
+ expect(calls).toBe(1);
79
+ expect(existsSync(join(dir, PROJECT_STATE_REL))).toBe(true);
80
+
81
+ await maybeAskSeed({
82
+ cwd: dir,
83
+ env: "test",
84
+ stdinIsTTY: true,
85
+ confirmFn: async () => true,
86
+ seedFn: async () => {
87
+ calls += 1;
88
+ return 0;
89
+ },
90
+ });
91
+ expect(calls).toBe(1);
92
+ } finally {
93
+ rmSync(dir, { recursive: true, force: true });
94
+ }
95
+ });
96
+ });