okengine 0.18.4 → 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 (512) 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/index.mdx +25 -6
  5. package/site/content/docs/ai/llms-txt.mdx +1 -1
  6. package/site/content/docs/ai/meta.json +1 -1
  7. package/site/content/docs/ai/skills.mdx +4 -1
  8. package/site/content/docs/ai/try-it.mdx +58 -0
  9. package/site/content/docs/client/auth.mdx +208 -0
  10. package/site/content/docs/client/calling.mdx +451 -0
  11. package/site/content/docs/client/index.mdx +209 -0
  12. package/site/content/docs/client/live.mdx +234 -0
  13. package/site/content/docs/client/meta.json +5 -0
  14. package/site/content/docs/client/react.mdx +249 -0
  15. package/site/content/docs/elements/ai/agents.mdx +232 -0
  16. package/site/content/docs/elements/ai/index.mdx +341 -0
  17. package/site/content/docs/elements/ai/mcp.mdx +275 -0
  18. package/site/content/docs/elements/ai/meta.json +5 -0
  19. package/site/content/docs/elements/ai/models.mdx +283 -0
  20. package/site/content/docs/elements/ai/prompts.mdx +256 -0
  21. package/site/content/docs/elements/channel/email.mdx +266 -0
  22. package/site/content/docs/elements/channel/index.mdx +344 -0
  23. package/site/content/docs/elements/channel/meta.json +5 -0
  24. package/site/content/docs/elements/channel/push.mdx +218 -0
  25. package/site/content/docs/elements/channel/receipts.mdx +206 -0
  26. package/site/content/docs/elements/channel/sms.mdx +264 -0
  27. package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
  28. package/site/content/docs/elements/clock/index.mdx +300 -0
  29. package/site/content/docs/elements/clock/meta.json +5 -0
  30. package/site/content/docs/elements/clock/schedules.mdx +508 -0
  31. package/site/content/docs/elements/clock/sleep.mdx +445 -0
  32. package/site/content/docs/elements/flow/consumers.mdx +707 -0
  33. package/site/content/docs/elements/flow/http.mdx +1080 -0
  34. package/site/content/docs/elements/flow/index.mdx +618 -0
  35. package/site/content/docs/elements/flow/meta.json +5 -0
  36. package/site/content/docs/elements/flow/routing.mdx +559 -0
  37. package/site/content/docs/elements/flow/workflows.mdx +640 -0
  38. package/site/content/docs/elements/gate/auth.mdx +371 -0
  39. package/site/content/docs/elements/gate/authorization.mdx +298 -0
  40. package/site/content/docs/elements/gate/index.mdx +418 -0
  41. package/site/content/docs/elements/gate/meta.json +5 -0
  42. package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
  43. package/site/content/docs/elements/gate/rls.mdx +303 -0
  44. package/site/content/docs/elements/gate/tenancy.mdx +372 -0
  45. package/site/content/docs/elements/index.mdx +52 -11
  46. package/site/content/docs/elements/signal/broadcast.mdx +517 -0
  47. package/site/content/docs/elements/signal/index.mdx +352 -0
  48. package/site/content/docs/elements/signal/live.mdx +590 -0
  49. package/site/content/docs/elements/signal/meta.json +5 -0
  50. package/site/content/docs/elements/signal/once.mdx +596 -0
  51. package/site/content/docs/elements/store/files.mdx +659 -0
  52. package/site/content/docs/elements/store/index.mdx +310 -0
  53. package/site/content/docs/elements/store/kv.mdx +629 -0
  54. package/site/content/docs/elements/store/meta.json +5 -0
  55. package/site/content/docs/elements/store/search.mdx +946 -0
  56. package/site/content/docs/elements/store/sql.mdx +937 -0
  57. package/site/content/docs/elements/vault/config.mdx +256 -0
  58. package/site/content/docs/elements/vault/index.mdx +356 -0
  59. package/site/content/docs/elements/vault/meta.json +5 -0
  60. package/site/content/docs/elements/vault/rotation.mdx +273 -0
  61. package/site/content/docs/elements/vault/secrets.mdx +285 -0
  62. package/site/content/docs/index.mdx +78 -30
  63. package/site/content/docs/meta.json +11 -8
  64. package/site/content/docs/plugins/cors.mdx +2 -1
  65. package/site/content/docs/plugins/csrf.mdx +4 -1
  66. package/site/content/docs/plugins/headers.mdx +2 -1
  67. package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
  68. package/site/content/docs/plugins/magic-link.mdx +6 -0
  69. package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
  70. package/site/content/docs/plugins/oauth.mdx +1 -1
  71. package/site/content/docs/plugins/otp.mdx +12 -1
  72. package/site/content/docs/plugins/passkey.mdx +39 -15
  73. package/site/content/docs/plugins/two-factor.mdx +91 -34
  74. package/site/content/docs/plugins/username.mdx +1 -1
  75. package/site/content/docs/recipes/caddy.mdx +3 -3
  76. package/site/content/docs/recipes/index.mdx +18 -28
  77. package/site/content/docs/recipes/mailpit.mdx +1 -1
  78. package/site/content/docs/recipes/meilisearch.mdx +3 -3
  79. package/site/content/docs/recipes/meta.json +1 -4
  80. package/site/content/docs/recipes/nginx.mdx +2 -2
  81. package/site/content/docs/recipes/openrouter.mdx +283 -0
  82. package/site/content/docs/recipes/pgdog.mdx +3 -3
  83. package/site/content/docs/recipes/postgres.mdx +1 -2
  84. package/site/content/docs/recipes/rustfs.mdx +2 -2
  85. package/site/content/docs/recipes/traefik.mdx +3 -3
  86. package/site/content/docs/reference/cli.mdx +231 -0
  87. package/site/content/docs/reference/configuration.mdx +30 -10
  88. package/site/content/docs/reference/environment-variables.mdx +93 -20
  89. package/site/content/docs/reference/errors.mdx +132 -29
  90. package/site/content/docs/reference/fx.mdx +137 -73
  91. package/site/content/docs/reference/index.mdx +13 -21
  92. package/site/content/docs/reference/meta.json +4 -5
  93. package/site/content/docs/reference/okid.mdx +42 -17
  94. package/site/content/docs/reference/plugins.mdx +4 -3
  95. package/site/content/docs/reference/security.mdx +197 -0
  96. package/site/content/docs/understand/meta.json +5 -0
  97. package/site/content/docs/understand/the-anatomy.mdx +132 -0
  98. package/site/content/docs/understand/the-model.mdx +32 -0
  99. package/site/content/docs/understand/the-problem.mdx +74 -0
  100. package/site/content/docs/understand/the-vocabulary.mdx +26 -0
  101. package/src/auth/api-keys.ts +2 -1
  102. package/src/auth/bindings.ts +64 -16
  103. package/src/auth/gate-auth.test.ts +6 -1
  104. package/src/auth/identity.ts +152 -5
  105. package/src/auth/index.ts +29 -0
  106. package/src/auth/invites.ts +2 -1
  107. package/src/auth/method-context.ts +41 -0
  108. package/src/auth/oauth-as/crypto.ts +2 -1
  109. package/src/auth/oauth-as/stores.ts +2 -1
  110. package/src/auth/operator.ts +2 -1
  111. package/src/auth/sessions-jwt.test.ts +77 -0
  112. package/src/auth/sessions.ts +20 -4
  113. package/src/auth/tenants.ts +3 -2
  114. package/src/auth/two-factor-challenge.test.ts +61 -0
  115. package/src/auth/two-factor-challenge.ts +284 -0
  116. package/src/auth/verification.ts +5 -0
  117. package/src/bench/REPORT.md +49 -0
  118. package/src/bench/g03-signal-once.bench.ts +1 -1
  119. package/src/bench/g10-observability-contention.bench.ts +1 -1
  120. package/src/bench/g17-hybrid-search.bench.ts +572 -0
  121. package/src/bench/load-app.ts +2 -10
  122. package/src/cli/ai-setup/ai-setup.test.ts +331 -42
  123. package/src/cli/ai-setup/apply.ts +294 -52
  124. package/src/cli/ai-setup/catalog.ts +238 -1342
  125. package/src/cli/ai-setup/index.ts +14 -99
  126. package/src/cli/ai-setup/prompts.ts +60 -443
  127. package/src/cli/ask-seed.test.ts +96 -0
  128. package/src/cli/ask-seed.ts +82 -0
  129. package/src/cli/ask-vault-gaps.test.ts +84 -0
  130. package/src/cli/ask-vault-gaps.ts +146 -0
  131. package/src/cli/build.ts +2 -2
  132. package/src/cli/client-add.test.ts +26 -1
  133. package/src/cli/client-add.ts +106 -27
  134. package/src/cli/competitor-mention-removal.test.ts +1 -1
  135. package/src/cli/db-seed.ts +2 -0
  136. package/src/cli/db.ts +150 -11
  137. package/src/cli/dev.ts +119 -211
  138. package/src/cli/docker-clean.ts +2 -2
  139. package/src/cli/docker-cli.test.ts +1 -1
  140. package/src/cli/doctor-diff.ts +4 -2
  141. package/src/cli/doctor-pii.test.ts +1 -1
  142. package/src/cli/doctor.ts +14 -1
  143. package/src/cli/hero-meta.test.ts +4 -2
  144. package/src/cli/load-config.images.test.ts +8 -10
  145. package/src/cli/load-config.ts +1 -1
  146. package/src/cli/registry.ts +22 -10
  147. package/src/cli/replay.ts +3 -1
  148. package/src/cli/start.ts +1 -1
  149. package/src/cli/tui/keys.ts +2 -3
  150. package/src/client/auth/cookies.ts +74 -0
  151. package/src/client/auth/create-auth-client.ts +636 -0
  152. package/src/client/auth/denials.ts +99 -0
  153. package/src/client/auth/session.ts +283 -0
  154. package/src/client/auth.test.ts +251 -0
  155. package/src/client/auth.ts +39 -114
  156. package/src/client/create-with-session.ts +245 -0
  157. package/src/client/create.ts +51 -5
  158. package/src/client/index.ts +11 -1
  159. package/src/client/live.ts +11 -79
  160. package/src/client/notes-contract.test.ts +5 -10
  161. package/src/client/sse.ts +137 -0
  162. package/src/client/stream.ts +147 -0
  163. package/src/client/transport.test.ts +32 -0
  164. package/src/client/transport.ts +123 -26
  165. package/src/client/types.ts +118 -14
  166. package/src/client-react/index.ts +185 -24
  167. package/src/client-react/use-live-query.ts +13 -3
  168. package/src/compiler/aot.test.ts +7 -4
  169. package/src/compiler/effects-embed.test.ts +56 -0
  170. package/src/compiler/effects-fetch.test.ts +43 -0
  171. package/src/compiler/effects-infer.ts +58 -2
  172. package/src/compiler/extract.test.ts +258 -44
  173. package/src/compiler/extract.ts +566 -48
  174. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
  175. package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
  176. package/src/compiler/fixtures/skyport.expected.json +4 -4
  177. package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
  178. package/src/compiler/response.ts +2 -2
  179. package/src/compiler/search-writer-isolation.test.ts +40 -0
  180. package/src/config/index.ts +11 -0
  181. package/src/console/server/app.ts +17 -23
  182. package/src/console/server/bind.ts +4 -0
  183. package/src/console/server/console.test.ts +2 -2
  184. package/src/console/server/flows-invoke.test.ts +50 -34
  185. package/src/console/server/flows.ts +436 -255
  186. package/src/console/server/runs-ingest.test.ts +6 -3
  187. package/src/console/server/serve.ts +1 -1
  188. package/src/console/server/signals.test.ts +1 -5
  189. package/src/console/server/signals.ts +8 -6
  190. package/src/console/server/state.ts +6 -1
  191. package/src/console/server/store.ts +1 -1
  192. package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
  193. package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
  194. package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
  195. package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
  196. package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
  197. package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
  198. package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
  199. package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
  200. package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
  201. package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
  202. package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
  203. package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
  204. package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
  205. package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
  206. package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
  207. package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
  208. package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
  209. package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
  210. package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
  211. package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
  212. package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
  213. package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
  214. package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
  215. package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
  216. package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
  217. package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
  218. package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
  219. package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
  220. package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
  221. package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
  222. package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
  223. package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
  224. package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
  225. package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
  226. package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
  227. package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
  228. package/src/console/ui-next/dist/index.html +6 -13
  229. package/src/console/ui-next/seed-invoke-host.ts +21 -18
  230. package/src/console/ui-next/src/client.ts +7 -2
  231. package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
  232. package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
  233. package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
  234. package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
  235. package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
  236. package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
  237. package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
  238. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
  239. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
  240. package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
  241. package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
  242. package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
  243. package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
  244. package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
  245. package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
  246. package/src/docker/ai-model-status.test.ts +1 -12
  247. package/src/docker/ai-model-status.ts +10 -38
  248. package/src/docker/compose-up.test.ts +100 -0
  249. package/src/docker/compose-up.ts +292 -0
  250. package/src/docker/compose.ts +4 -14
  251. package/src/docker/derive.ts +9 -34
  252. package/src/docker/docker.test.ts +16 -261
  253. package/src/docker/images-config.test.ts +13 -25
  254. package/src/docker/index.ts +1 -24
  255. package/src/docker/recipes/index.ts +0 -23
  256. package/src/docker/recipes/pgdog.ts +6 -4
  257. package/src/docker/stack-id.test.ts +2 -2
  258. package/src/docker/stack-id.ts +0 -1
  259. package/src/docker/types.ts +2 -2
  260. package/src/drivers/ai-anthropic.ts +10 -0
  261. package/src/drivers/ai-openai-compatible.ts +34 -7
  262. package/src/drivers/ai-providers.test.ts +0 -107
  263. package/src/drivers/ai-stream.test.ts +0 -34
  264. package/src/drivers/ai-types.ts +17 -8
  265. package/src/drivers/channel-fcm.ts +6 -1
  266. package/src/drivers/channel-msegat.ts +6 -1
  267. package/src/drivers/channel-resend.ts +5 -1
  268. package/src/drivers/channel-smtp.ts +5 -1
  269. package/src/drivers/channel-sndr.ts +10 -1
  270. package/src/drivers/channel-taqnyat-mail.ts +5 -1
  271. package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
  272. package/src/drivers/channel-taqnyat.ts +6 -1
  273. package/src/drivers/channel-types.ts +10 -0
  274. package/src/drivers/channel-unifonic.ts +6 -1
  275. package/src/drivers/channel-wa-cloud.ts +6 -1
  276. package/src/drivers/channel-webpush.ts +6 -1
  277. package/src/drivers/external.ts +33 -0
  278. package/src/drivers/index.ts +0 -10
  279. package/src/drivers/meilisearch.ts +6 -0
  280. package/src/drivers/oauth-types.ts +4 -0
  281. package/src/drivers/postgres.test.ts +74 -3
  282. package/src/drivers/postgres.ts +175 -6
  283. package/src/drivers/signal-types.ts +5 -5
  284. package/src/drivers/types.ts +7 -0
  285. package/src/drivers/vault-types.ts +5 -0
  286. package/src/elements/ai/declare.ts +17 -4
  287. package/src/elements/ai/eval.ts +1 -1
  288. package/src/elements/ai/mcp-http.ts +1 -1
  289. package/src/elements/ai/pii.ts +1 -1
  290. package/src/elements/ai/providers.test.ts +289 -0
  291. package/src/elements/ai/providers.ts +169 -0
  292. package/src/elements/ai/runtime.ts +68 -5
  293. package/src/elements/ai/schema.ts +2 -2
  294. package/src/elements/ai.test.ts +7 -7
  295. package/src/elements/ai.ts +17 -1
  296. package/src/elements/channel/runtime.ts +105 -12
  297. package/src/elements/clock/cron-fields.test.ts +144 -0
  298. package/src/elements/clock/cron-fields.ts +185 -0
  299. package/src/elements/clock/declare.ts +247 -3
  300. package/src/elements/clock.ts +21 -1
  301. package/src/elements/index.ts +15 -0
  302. package/src/elements/signal/chaos-child.ts +1 -2
  303. package/src/elements/signal/declare.ts +65 -37
  304. package/src/elements/signal/delivery-modes.test.ts +11 -31
  305. package/src/elements/signal/dry-run-replay.test.ts +1 -7
  306. package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
  307. package/src/elements/signal/key-ordering.test.ts +6 -26
  308. package/src/elements/signal/lease-reclaim.test.ts +2 -6
  309. package/src/elements/signal/optional-emit.test.ts +6 -9
  310. package/src/elements/signal/order-lifecycle.test.ts +4 -17
  311. package/src/elements/signal/orphan-messages.test.ts +4 -15
  312. package/src/elements/signal/reconcile.test.ts +2 -2
  313. package/src/elements/signal/runtime.ts +1 -1
  314. package/src/elements/signal/schema-emit.test.ts +4 -8
  315. package/src/elements/signal.test.ts +24 -49
  316. package/src/elements/signal.ts +9 -2
  317. package/src/elements/store/domain-ddl.test.ts +1 -1
  318. package/src/elements/store/live-http.test.ts +9 -4
  319. package/src/elements/store/prepare-row.test.ts +123 -0
  320. package/src/elements/store/resource.ts +43 -12
  321. package/src/elements/store/schema-decl.ts +117 -0
  322. package/src/elements/store/search-backfill.ts +205 -0
  323. package/src/elements/store/search-bind.ts +66 -0
  324. package/src/elements/store/search-bm25.ts +78 -0
  325. package/src/elements/store/search-ddl.ts +156 -0
  326. package/src/elements/store/search-embed-flow.ts +141 -0
  327. package/src/elements/store/search-errors.ts +34 -0
  328. package/src/elements/store/search-fusion.ts +112 -0
  329. package/src/elements/store/search-lsh.ts +179 -0
  330. package/src/elements/store/search-runtime.ts +300 -0
  331. package/src/elements/store/search.test.ts +144 -0
  332. package/src/elements/store/sql-session.test.ts +1 -0
  333. package/src/elements/store/sql-session.ts +83 -3
  334. package/src/elements/store/table.ts +23 -2
  335. package/src/elements/store/upsert-app.test.ts +1 -3
  336. package/src/elements/store.ts +50 -0
  337. package/src/full.ts +4 -2
  338. package/src/http.ts +4 -1
  339. package/src/i18n/catalogs/ar.ts +14 -6
  340. package/src/i18n/catalogs/en.ts +14 -6
  341. package/src/index.ts +6 -1
  342. package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
  343. package/src/kernel/adopt-routes.ts +9 -1
  344. package/src/kernel/app-auth.ts +5 -0
  345. package/src/kernel/app.ts +102 -9
  346. package/src/kernel/auto-cache.test.ts +6 -12
  347. package/src/kernel/auto-registry.test.ts +3 -3
  348. package/src/kernel/boot-bind/ai.test.ts +22 -19
  349. package/src/kernel/boot-bind/ai.ts +5 -43
  350. package/src/kernel/boot-bind/clock.ts +23 -5
  351. package/src/kernel/boot-bind/honor-config.test.ts +4 -4
  352. package/src/kernel/boot.test.ts +11 -6
  353. package/src/kernel/boot.ts +66 -16
  354. package/src/kernel/boundary-contract.ts +93 -0
  355. package/src/kernel/budget.test.ts +1 -1
  356. package/src/kernel/call.ts +89 -0
  357. package/src/kernel/capability.ts +6 -0
  358. package/src/kernel/client-descriptor.ts +123 -0
  359. package/src/kernel/clock-timezone.test.ts +80 -0
  360. package/src/kernel/correlation.test.ts +1 -1
  361. package/src/kernel/dry-run.ts +11 -8
  362. package/src/kernel/effects-stamping.test.ts +42 -9
  363. package/src/kernel/effects.test.ts +4 -2
  364. package/src/kernel/effects.ts +51 -9
  365. package/src/kernel/errors-channel.ts +17 -0
  366. package/src/kernel/errors-live-resume.ts +3 -2
  367. package/src/kernel/errors-tenant.ts +7 -4
  368. package/src/kernel/errors.registry-helpers.ts +96 -0
  369. package/src/kernel/errors.registry.test.ts +139 -25
  370. package/src/kernel/errors.ts +102 -23
  371. package/src/kernel/external-effects.test.ts +196 -0
  372. package/src/kernel/flow.test.ts +13 -4
  373. package/src/kernel/flow.ts +69 -71
  374. package/src/kernel/fx-ask-telemetry.test.ts +2 -2
  375. package/src/kernel/fx-dead-letters.test.ts +3 -3
  376. package/src/kernel/fx-fetch.ts +55 -0
  377. package/src/kernel/fx-live-stream.ts +2 -2
  378. package/src/kernel/fx-live.test.ts +8 -8
  379. package/src/kernel/fx.test.ts +17 -11
  380. package/src/kernel/fx.ts +180 -57
  381. package/src/kernel/horizontal-child.ts +1 -1
  382. package/src/kernel/http-query.test.ts +3 -6
  383. package/src/kernel/index.ts +13 -3
  384. package/src/kernel/instance-id.ts +1 -1
  385. package/src/kernel/instances.test.ts +40 -0
  386. package/src/kernel/instances.ts +47 -9
  387. package/src/kernel/live-http.test.ts +6 -7
  388. package/src/kernel/live-http.ts +6 -2
  389. package/src/kernel/live-resume.test.ts +1 -1
  390. package/src/kernel/mcp-tool.test.ts +3 -3
  391. package/src/kernel/on.ts +98 -4
  392. package/src/kernel/pipeline.test.ts +12 -15
  393. package/src/kernel/plugin-elements.test.ts +1 -1
  394. package/src/kernel/stamp-http.test.ts +3 -3
  395. package/src/kernel/triggers.ts +210 -95
  396. package/src/kernel-entry.ts +0 -1
  397. package/src/manifest/diff.ts +11 -1
  398. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  399. package/src/manifest/types.ts +46 -2
  400. package/src/mcp/docs-index.ts +3 -3
  401. package/src/mcp/docs-mcp.test.ts +2 -2
  402. package/src/mcp/docs-tools.ts +1 -1
  403. package/src/okid.test.ts +56 -0
  404. package/src/okid.ts +55 -11
  405. package/src/plugins/anonymous.ts +8 -4
  406. package/src/plugins/auth/shared.ts +27 -3
  407. package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
  408. package/src/plugins/auth-methods.security.test.ts +561 -23
  409. package/src/plugins/config-source.ts +1 -1
  410. package/src/plugins/csrf.test.ts +65 -0
  411. package/src/plugins/index.ts +2 -0
  412. package/src/plugins/magic-link.ts +36 -16
  413. package/src/plugins/oauth/shared.ts +3 -1
  414. package/src/plugins/oauth.ts +22 -14
  415. package/src/plugins/otp.ts +37 -21
  416. package/src/plugins/passkey-webauthn.ts +9 -6
  417. package/src/plugins/passkey.ts +98 -26
  418. package/src/plugins/pre-account-hijack.test.ts +223 -0
  419. package/src/plugins/two-factor.ts +466 -37
  420. package/src/plugins/username.ts +19 -7
  421. package/src/release/build-lib.ts +1 -0
  422. package/src/release/limits.ts +2 -2
  423. package/src/release/measure.ts +1 -1
  424. package/src/runs/collect.test.ts +0 -2
  425. package/src/runtime/json-code-block.test.ts +250 -20
  426. package/src/runtime/json-code-block.ts +1261 -41
  427. package/src/term.test.ts +1 -1
  428. package/src/test/create-test-app.test.ts +12 -12
  429. package/src/test/live-signals.test.ts +6 -6
  430. package/src/test/provisions.integration.test.ts +10 -14
  431. package/src/test/reset-element-registries.ts +1 -1
  432. package/src/test/tenant-isolation.test.ts +12 -6
  433. package/site/content/docs/deployment/docker-swarm.mdx +0 -164
  434. package/site/content/docs/deployment/docker.mdx +0 -227
  435. package/site/content/docs/deployment/index.mdx +0 -83
  436. package/site/content/docs/deployment/kubernetes.mdx +0 -176
  437. package/site/content/docs/deployment/meta.json +0 -5
  438. package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
  439. package/site/content/docs/elements/ai.mdx +0 -385
  440. package/site/content/docs/elements/channel.mdx +0 -346
  441. package/site/content/docs/elements/clock.mdx +0 -244
  442. package/site/content/docs/elements/flow.mdx +0 -420
  443. package/site/content/docs/elements/gate.mdx +0 -436
  444. package/site/content/docs/elements/signal.mdx +0 -380
  445. package/site/content/docs/elements/store.mdx +0 -1099
  446. package/site/content/docs/elements/vault.mdx +0 -405
  447. package/site/content/docs/get-started/basic-usage.mdx +0 -173
  448. package/site/content/docs/get-started/index.mdx +0 -43
  449. package/site/content/docs/get-started/installation.mdx +0 -220
  450. package/site/content/docs/get-started/introduction.mdx +0 -132
  451. package/site/content/docs/get-started/meta.json +0 -13
  452. package/site/content/docs/get-started/project-structure.mdx +0 -925
  453. package/site/content/docs/get-started/testing.mdx +0 -328
  454. package/site/content/docs/get-started/why.mdx +0 -162
  455. package/site/content/docs/recipes/llama-cpp.mdx +0 -151
  456. package/site/content/docs/recipes/ollama.mdx +0 -142
  457. package/site/content/docs/recipes/sglang.mdx +0 -105
  458. package/site/content/docs/recipes/vllm.mdx +0 -106
  459. package/site/content/docs/reference/cli.md +0 -232
  460. package/site/content/docs/reference/client.mdx +0 -469
  461. package/site/content/docs/reference/security.md +0 -74
  462. package/src/cli/ai-setup/detect-ollama.ts +0 -213
  463. package/src/cli/ai-setup/recommend.test.ts +0 -225
  464. package/src/cli/ai-setup/recommend.ts +0 -225
  465. package/src/cli/dev-controls.test.ts +0 -122
  466. package/src/cli/dev-controls.ts +0 -164
  467. package/src/cli/tui/DevLive.tsx +0 -126
  468. package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
  469. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
  470. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
  471. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
  472. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
  473. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
  474. package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
  475. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
  476. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
  477. package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
  478. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
  479. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
  480. package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
  481. package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
  482. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
  483. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
  484. package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
  485. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
  486. package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
  487. package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
  488. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
  489. package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
  490. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
  491. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
  492. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
  493. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
  494. package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
  495. package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
  496. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
  497. package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
  498. package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
  499. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
  500. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
  501. package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
  502. package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
  503. package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
  504. package/src/docker/ollama-pull.ts +0 -232
  505. package/src/docker/recipes/llama-cpp.ts +0 -298
  506. package/src/docker/recipes/ollama.ts +0 -44
  507. package/src/docker/recipes/sglang.ts +0 -55
  508. package/src/docker/recipes/vllm.ts +0 -44
  509. package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
  510. package/src/drivers/ai-ollama.integration.test.ts +0 -184
  511. package/src/drivers/ai-ollama.ts +0 -389
  512. package/src/drivers/ollama.ts +0 -14
@@ -1,232 +0,0 @@
1
- /**
2
- * Pull an Ollama model via the container's HTTP API on its exposed host port.
3
- *
4
- * Never shells out to a host `ollama` CLI — that talks to whichever server wins
5
- * on the default local port (often a separately installed host daemon), so the
6
- * model can land outside the recipe container.
7
- *
8
- * Skips `/api/pull` when the model is already listed on `/api/tags`.
9
- */
10
-
11
- /** Fetch contract (injectable for tests) — call signature only, not Bun's `preconnect`. */
12
- export type OllamaFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
13
-
14
- /** Options for {@link ensureOllamaModel}. */
15
- export interface EnsureOllamaModelOptions {
16
- /** Base URL of the container API, e.g. `http://127.0.0.1:11434`. */
17
- readonly url: string;
18
- /** Model tag to pull (e.g. `qwen3.5:9b`). */
19
- readonly model: string;
20
- readonly fetch?: OllamaFetch;
21
- /** How long to wait for `/api/tags` before giving up (ms). Default 90s. */
22
- readonly readyTimeoutMs?: number;
23
- /** Overall pull deadline (ms). Default 20 minutes. */
24
- readonly pullTimeoutMs?: number;
25
- /** Progress / status lines (e.g. `oke: …`). */
26
- readonly onStatus?: (line: string) => void;
27
- }
28
-
29
- /** Fail-loud pull / readiness error. */
30
- export class OllamaPullError extends Error {
31
- constructor(message: string) {
32
- super(message);
33
- this.name = "OllamaPullError";
34
- }
35
- }
36
-
37
- /**
38
- * Normalize a base URL (trim trailing slash).
39
- *
40
- * @param url - Raw base URL
41
- */
42
- export function normalizeOllamaPullUrl(url: string): string {
43
- return url.replace(/\/+$/, "");
44
- }
45
-
46
- /**
47
- * Whether an installed tag covers the requested model id.
48
- *
49
- * @param want - Requested id (e.g. `gemma4:e4b`)
50
- * @param installed - Names from `/api/tags`
51
- */
52
- export function ollamaTagsInclude(want: string, installed: readonly string[]): boolean {
53
- const id = want.trim();
54
- if (!id) return false;
55
- const base = id.split(":")[0] ?? id;
56
- return installed.some((name) => {
57
- if (name === id) return true;
58
- if (name.startsWith(`${id}-`)) return true;
59
- if (name.startsWith(`${base}:`) && id.startsWith(`${base}:`)) {
60
- const instTag = name.slice(base.length + 1);
61
- const wantTag = id.slice(base.length + 1);
62
- return (
63
- instTag === wantTag ||
64
- instTag.startsWith(`${wantTag}-`) ||
65
- wantTag.startsWith(instTag) ||
66
- (instTag === "latest" && wantTag === "latest")
67
- );
68
- }
69
- // bare name matches `name:latest`
70
- if (name === `${id}:latest` || id === `${name}:latest`) return true;
71
- return false;
72
- });
73
- }
74
-
75
- /**
76
- * Parse model names from an `/api/tags` JSON body.
77
- *
78
- * @param json - Parsed response
79
- */
80
- export function parseOllamaTagsNames(json: unknown): string[] {
81
- if (!json || typeof json !== "object") return [];
82
- const models = (json as { models?: unknown }).models;
83
- if (!Array.isArray(models)) return [];
84
- return models
85
- .map((m) =>
86
- m && typeof m === "object" && typeof (m as { name?: unknown }).name === "string"
87
- ? (m as { name: string }).name
88
- : null,
89
- )
90
- .filter((n): n is string => typeof n === "string" && n.length > 0);
91
- }
92
-
93
- /**
94
- * Wait until `GET /api/tags` succeeds; skip pull when the model is already
95
- * present; otherwise `POST /api/pull` with streaming progress.
96
- *
97
- * @param opts - Target URL + model (+ optional injectable fetch)
98
- */
99
- export async function ensureOllamaModel(opts: EnsureOllamaModelOptions): Promise<void> {
100
- const fetchFn = opts.fetch ?? globalThis.fetch;
101
- const base = normalizeOllamaPullUrl(opts.url);
102
- const model = opts.model.trim();
103
- const status = opts.onStatus ?? (() => {});
104
- if (!model) throw new OllamaPullError("ollama pull: model id is empty");
105
-
106
- const readyDeadline = Date.now() + (opts.readyTimeoutMs ?? 90_000);
107
- let tagsBody: unknown = null;
108
- while (Date.now() < readyDeadline) {
109
- try {
110
- const res = await fetchFn(`${base}/api/tags`, { method: "GET" });
111
- if (res.ok) {
112
- tagsBody = await res.json().catch(() => null);
113
- break;
114
- }
115
- } catch {
116
- // still starting
117
- }
118
- await Bun.sleep(500);
119
- }
120
- if (tagsBody === null) {
121
- throw new OllamaPullError(`ollama pull: unreachable at ${base}/api/tags`);
122
- }
123
-
124
- const installed = parseOllamaTagsNames(tagsBody);
125
- if (ollamaTagsInclude(model, installed)) {
126
- status(`oke: Ollama already has ${model} at ${base} — skip pull`);
127
- return;
128
- }
129
-
130
- status(`oke: pulling ${model} into Ollama at ${base}…`);
131
-
132
- const pullDeadline = Date.now() + (opts.pullTimeoutMs ?? 20 * 60_000);
133
- const controller = new AbortController();
134
- const timer = setTimeout(() => controller.abort(), Math.max(1, pullDeadline - Date.now()));
135
- try {
136
- const res = await fetchFn(`${base}/api/pull`, {
137
- method: "POST",
138
- headers: { "content-type": "application/json" },
139
- body: JSON.stringify({ model, stream: true }),
140
- signal: controller.signal,
141
- });
142
- if (!res.ok) {
143
- const text = await res.text().catch(() => "");
144
- throw new OllamaPullError(
145
- `ollama pull: POST ${base}/api/pull → ${res.status}${text ? ` ${text.slice(0, 200)}` : ""}`,
146
- );
147
- }
148
- await consumeOllamaPullStream(res, status);
149
- } catch (err) {
150
- if (err instanceof OllamaPullError) throw err;
151
- const msg = err instanceof Error ? err.message : String(err);
152
- throw new OllamaPullError(`ollama pull: POST ${base}/api/pull failed — ${msg}`);
153
- } finally {
154
- clearTimeout(timer);
155
- }
156
- }
157
-
158
- /**
159
- * Read NDJSON pull progress; surface concise status updates.
160
- *
161
- * @param res - Streaming pull response
162
- * @param status - Status writer
163
- */
164
- async function consumeOllamaPullStream(
165
- res: Response,
166
- status: (line: string) => void,
167
- ): Promise<void> {
168
- // Non-body responses (mocked tests) — treat as complete.
169
- if (!res.body) {
170
- await res.arrayBuffer().catch(() => undefined);
171
- return;
172
- }
173
-
174
- const reader = res.body.getReader();
175
- const decoder = new TextDecoder();
176
- let buf = "";
177
- const state: PullStreamState = { lastStatus: "", lastPct: -1 };
178
-
179
- for (;;) {
180
- const { done, value } = await reader.read();
181
- if (done) break;
182
- buf += decoder.decode(value, { stream: true });
183
- const lines = buf.split("\n");
184
- buf = lines.pop() ?? "";
185
- for (const line of lines) {
186
- handlePullLine(line, status, state);
187
- }
188
- }
189
- buf += decoder.decode();
190
- if (buf.trim()) {
191
- handlePullLine(buf, status, state);
192
- }
193
- }
194
-
195
- type PullStreamState = { lastStatus: string; lastPct: number };
196
-
197
- function handlePullLine(
198
- line: string,
199
- status: (line: string) => void,
200
- state: PullStreamState,
201
- ): void {
202
- const trimmed = line.trim();
203
- if (!trimmed) return;
204
- let row: {
205
- status?: string;
206
- error?: string;
207
- completed?: number;
208
- total?: number;
209
- };
210
- try {
211
- row = JSON.parse(trimmed) as typeof row;
212
- } catch {
213
- return;
214
- }
215
- if (row.error) {
216
- throw new OllamaPullError(`ollama pull: ${row.error}`);
217
- }
218
- const st = row.status?.trim() ?? "";
219
- if (typeof row.completed === "number" && typeof row.total === "number" && row.total > 0) {
220
- const pct = Math.min(100, Math.floor((100 * row.completed) / row.total));
221
- if (pct !== state.lastPct && (pct === 100 || pct - state.lastPct >= 5)) {
222
- state.lastPct = pct;
223
- status(`oke: Ollama pull ${pct}%${st ? ` (${st})` : ""}`);
224
- }
225
- return;
226
- }
227
- if (st && st !== state.lastStatus) {
228
- state.lastStatus = st;
229
- if (/^pulling\s+[a-f0-9]{12}/i.test(st)) return;
230
- status(`oke: Ollama ${st}`);
231
- }
232
- }
@@ -1,298 +0,0 @@
1
- /**
2
- * llama.cpp image recipe — default local AI (`ghcr.io/ggml-org/llama.cpp`).
3
- *
4
- * OpenAI-compatible `llama-server` on 8080. Pin ≥ {@link LLAMA_CPP_MIN_SAFE_BUILD}
5
- * (CVE-2026-27940 / CVE-2026-33298 floor). Host publish is loopback-only;
6
- * never publish RPC. Models via Docker Hub `ai/` — do not load arbitrary
7
- * untrusted GGUF files.
8
- *
9
- * b10290+ issues this recipe works around:
10
- * 1. Bare `--docker-repo` / router `--models-preset` children re-enter empty
11
- * router mode (`is_router_server` only checks `-m` / `-hf`) → forever
12
- * `loading`.
13
- * 2. `llama download -dr` only accepts `application/vnd.docker.ai.gguf.v3`;
14
- * newer Hub models (e.g. gemma4) ship `vnd.cncf.model.weight.v1.raw`.
15
- *
16
- * Entrypoint: Hub-pull (native download, then CNCF-aware fallback) → serve
17
- * single-model with `-m` + `--alias`.
18
- */
19
-
20
- import type { ImageRecipe } from "../types.ts";
21
-
22
- /** Minimum safe llama.cpp build (GGUF parser CVEs through CVE-2026-27940). */
23
- export const LLAMA_CPP_MIN_SAFE_BUILD = 8146;
24
-
25
- /** Pinned default image — verified ≥ {@link LLAMA_CPP_MIN_SAFE_BUILD}; never `latest`. */
26
- export const LLAMA_CPP_IMAGE = "ghcr.io/ggml-org/llama.cpp:server-b10450";
27
-
28
- /**
29
- * Basename of the generated entrypoint.
30
- *
31
- * Lives under `.oke/` (gitignored generated artefacts), never beside app
32
- * TypeScript — compose mounts it via {@link LLAMA_CPP_ENTRYPOINT_HOST_PATH}.
33
- */
34
- export const LLAMA_CPP_ENTRYPOINT_FILE = "llama-entrypoint.py";
35
-
36
- /**
37
- * Host path relative to the compose directory (`docker/`) for the generated
38
- * entrypoint. Resolves to `<project>/.oke/llama-entrypoint.py`.
39
- */
40
- export const LLAMA_CPP_ENTRYPOINT_HOST_PATH = `../.oke/${LLAMA_CPP_ENTRYPOINT_FILE}`;
41
-
42
- /** In-container path for {@link LLAMA_CPP_ENTRYPOINT_FILE}. */
43
- export const LLAMA_CPP_ENTRYPOINT_MOUNT = "/oke/llama-entrypoint.py";
44
-
45
- /**
46
- * Python entrypoint (runs inside the llama.cpp image, which ships `python3`):
47
- * pull curated Docker Hub `ai/` GGUF into the cache volume, then `exec`
48
- * `llama-server -m <gguf> --alias <id>` (single-model, not router).
49
- *
50
- * Source of truth is this TypeScript string — the `.py` file is a generated
51
- * runtime artefact under `.oke/`, not app code.
52
- */
53
- export function buildLlamaCppEntrypoint(): string {
54
- return `#!/usr/bin/env python3
55
- # Generated by oke — do not edit. Source: okengine docker llama.cpp recipe.
56
- """OKE llama.cpp entrypoint — Hub-pull curated ai/ model, then serve single-model."""
57
-
58
- from __future__ import annotations
59
-
60
- import json
61
- import os
62
- import subprocess
63
- import sys
64
- import urllib.error
65
- import urllib.parse
66
- import urllib.request
67
-
68
-
69
- def cache_dir() -> str:
70
- return os.environ.get("LLAMA_CACHE", "/root/.cache/llama.cpp")
71
-
72
-
73
- def parse_model(model: str) -> tuple[str, str]:
74
- raw = model.strip() or "granite3.3:2b"
75
- if "/" not in raw.split(":", 1)[0]:
76
- raw = f"ai/{raw}"
77
- if ":" in raw:
78
- repo, tag = raw.rsplit(":", 1)
79
- else:
80
- repo, tag = raw, "latest"
81
- return repo, tag
82
-
83
-
84
- def expected_gguf(repo: str, tag: str) -> str:
85
- return f"{repo.replace('/', '_')}_{tag}.gguf"
86
-
87
-
88
- def existing_gguf(path: str) -> str | None:
89
- if os.path.isfile(path) and os.path.getsize(path) > 0:
90
- return path
91
- return None
92
-
93
-
94
- def llama_download(model: str) -> int:
95
- return subprocess.call(
96
- ["/app/llama", "download", "-dr", model, "--no-mmproj"],
97
- stdout=sys.stdout,
98
- stderr=sys.stderr,
99
- )
100
-
101
-
102
- def registry_token(repo: str) -> str:
103
- scope = urllib.parse.quote(f"repository:{repo}:pull", safe="")
104
- url = f"https://auth.docker.io/token?service=registry.docker.io&scope={scope}"
105
- with urllib.request.urlopen(url, timeout=60) as res:
106
- return json.load(res)["token"]
107
-
108
-
109
- def fetch_manifest(repo: str, tag: str, token: str) -> dict:
110
- req = urllib.request.Request(
111
- f"https://registry-1.docker.io/v2/{repo}/manifests/{tag}",
112
- headers={
113
- "Authorization": f"Bearer {token}",
114
- "Accept": (
115
- "application/vnd.oci.image.manifest.v1+json,"
116
- "application/vnd.docker.distribution.manifest.v2+json,"
117
- "application/vnd.oci.image.index.v1+json,"
118
- "application/vnd.docker.distribution.manifest.list.v2+json"
119
- ),
120
- },
121
- )
122
- with urllib.request.urlopen(req, timeout=60) as res:
123
- return json.load(res)
124
-
125
-
126
- def pick_gguf_layer(manifest: dict) -> tuple[str, str]:
127
- """Return (digest, suggested_filename) for the primary GGUF weight layer."""
128
- layers = manifest.get("layers") or []
129
- candidates: list[tuple[str, str, int]] = []
130
- for layer in layers:
131
- media = str(layer.get("mediaType") or "")
132
- digest = str(layer.get("digest") or "")
133
- ann = layer.get("annotations") or {}
134
- filepath = str(ann.get("org.cncf.model.filepath") or "")
135
- name = filepath.split("/")[-1] if filepath else ""
136
- is_gguf = (
137
- media == "application/vnd.docker.ai.gguf.v3"
138
- or "gguf" in media.lower()
139
- or name.lower().endswith(".gguf")
140
- )
141
- if not is_gguf or not digest:
142
- continue
143
- if name.lower().startswith("mmproj"):
144
- continue
145
- size = int(layer.get("size") or 0)
146
- candidates.append((digest, name or "model.gguf", size))
147
- if not candidates:
148
- raise RuntimeError("No GGUF weight layer in Docker / CNCF model manifest")
149
- # Prefer the largest non-mmproj GGUF (main weights vs projectors).
150
- candidates.sort(key=lambda c: c[2], reverse=True)
151
- digest, name, _ = candidates[0]
152
- return digest, name
153
-
154
-
155
- def download_blob(repo: str, digest: str, token: str, dest: str) -> None:
156
- url = f"https://registry-1.docker.io/v2/{repo}/blobs/{digest}"
157
- req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"})
158
- os.makedirs(os.path.dirname(dest), exist_ok=True)
159
- partial = f"{dest}.partial"
160
- print(f"oke ai: downloading {digest} → {dest}", flush=True)
161
- with urllib.request.urlopen(req, timeout=600) as res, open(partial, "wb") as out:
162
- total = int(res.headers.get("Content-Length") or 0)
163
- done = 0
164
- last_pct = -1
165
- while True:
166
- chunk = res.read(1024 * 1024)
167
- if not chunk:
168
- break
169
- out.write(chunk)
170
- done += len(chunk)
171
- if total:
172
- pct = (100 * done) // total
173
- if pct >= last_pct + 5 or done == total:
174
- last_pct = pct
175
- print(
176
- f"oke ai: download {pct}% ({done // (1024 * 1024)} / {total // (1024 * 1024)} MiB)",
177
- flush=True,
178
- )
179
- os.replace(partial, dest)
180
-
181
-
182
- def hub_pull(repo: str, tag: str, dest: str) -> None:
183
- token = registry_token(repo)
184
- manifest = fetch_manifest(repo, tag, token)
185
- # Index → pick first linux manifest if present.
186
- if "manifests" in manifest:
187
- digests = [m.get("digest") for m in manifest["manifests"] if m.get("digest")]
188
- if not digests:
189
- raise RuntimeError("Empty OCI index for model")
190
- req = urllib.request.Request(
191
- f"https://registry-1.docker.io/v2/{repo}/manifests/{digests[0]}",
192
- headers={
193
- "Authorization": f"Bearer {token}",
194
- "Accept": "application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.v2+json",
195
- },
196
- )
197
- with urllib.request.urlopen(req, timeout=60) as res:
198
- manifest = json.load(res)
199
- digest, _name = pick_gguf_layer(manifest)
200
- download_blob(repo, digest, token, dest)
201
-
202
-
203
- def ensure_model(model: str) -> str:
204
- repo, tag = parse_model(model)
205
- # llama download -dr expects id without forced ai/ prefix when using default org.
206
- dr = model.strip() or "granite3.3:2b"
207
- if dr.startswith("ai/"):
208
- dr = dr[3:]
209
- dest = os.path.join(cache_dir(), expected_gguf(repo, tag))
210
- hit = existing_gguf(dest)
211
- if hit:
212
- print(f"oke ai: using cached {hit}", flush=True)
213
- return hit
214
-
215
- print(f"oke ai: ensuring Docker Hub model '{dr}' is cached…", flush=True)
216
- rc = llama_download(dr)
217
- hit = existing_gguf(dest)
218
- if hit:
219
- return hit
220
- # Native downloader may write the same basename; also accept exact path only.
221
- print(
222
- f"oke ai: native download exit {rc}; trying CNCF / Hub registry pull…",
223
- flush=True,
224
- )
225
- try:
226
- hub_pull(repo, tag, dest)
227
- except (urllib.error.URLError, urllib.error.HTTPError, TimeoutError, RuntimeError, KeyError, ValueError) as err:
228
- print(f"oke ai: failed to download model '{dr}': {err}", file=sys.stderr, flush=True)
229
- sys.exit(1)
230
- hit = existing_gguf(dest)
231
- if not hit:
232
- print(f"oke ai: download produced no file at {dest}", file=sys.stderr, flush=True)
233
- sys.exit(1)
234
- return hit
235
-
236
-
237
- def main() -> None:
238
- model = (os.environ.get("OKE_AI_MODEL") or "granite3.3:2b").strip() or "granite3.3:2b"
239
- gguf = ensure_model(model)
240
- # Without an explicit --ctx-size, llama-server defaults to the model's
241
- # full native training context (32K-256K+ for many current models). The
242
- # KV cache for that is allocated up front and can be many times the
243
- # model's own file size, OOM-killing the container even with generous
244
- # Docker memory — independent of how small the model itself is.
245
- # OKE_AI_CTX_SIZE (.env.local) overrides this default.
246
- ctx_size = (os.environ.get("OKE_AI_CTX_SIZE") or "4096").strip() or "4096"
247
- print(f"oke ai: serving {model} from {gguf} (ctx={ctx_size})", flush=True)
248
- os.execv(
249
- "/app/llama-server",
250
- [
251
- "/app/llama-server",
252
- "--host",
253
- "0.0.0.0",
254
- "--port",
255
- "8080",
256
- "--model",
257
- gguf,
258
- "--alias",
259
- model,
260
- "--ctx-size",
261
- ctx_size,
262
- ],
263
- )
264
-
265
-
266
- if __name__ == "__main__":
267
- main()
268
- `;
269
- }
270
-
271
- /** llama-server — OpenAI-compatible HTTP on 8080; loopback publish only. */
272
- export const llamaCpp: ImageRecipe = {
273
- id: "llama-cpp",
274
- port: 8080,
275
- match: (i) => /llama\.cpp|llamacpp/i.test(i),
276
- apply: (s) => ({
277
- // OKE_AI_MODEL comes from compose `env_file` (`.env.local`). Do not
278
- // re-declare it as `${OKE_AI_MODEL:-granite3.3:2b}` here — Compose interpolates
279
- // that from the *host* shell and can override the stack file with the
280
- // default. Never set LLAMA_ARG_MODELS_PRESET / LLAMA_ARG_DOCKER_REPO
281
- // (router children inherit `LLAMA_ARG_*` and can OOM).
282
- entrypoint: ["/usr/bin/python3", LLAMA_CPP_ENTRYPOINT_MOUNT],
283
- volumes: [
284
- `${s.serviceName}-models:/root/.cache`,
285
- `${LLAMA_CPP_ENTRYPOINT_HOST_PATH}:${LLAMA_CPP_ENTRYPOINT_MOUNT}:ro`,
286
- ],
287
- publishBind: "127.0.0.1",
288
- healthcheck: {
289
- test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8080/health >/dev/null || exit 1"],
290
- interval: "5s",
291
- timeout: "5s",
292
- retries: 24,
293
- // First boot may download a multi‑GB Hub model (CNCF pull) into cache.
294
- start_period: "900s",
295
- },
296
- }),
297
- url: (_s, c) => `http://${c.host}:${c.port}/v1`,
298
- };
@@ -1,44 +0,0 @@
1
- /**
2
- * Ollama image recipe — local model server (`ollama/ollama`).
3
- *
4
- * Fully supported alternative to the default llama.cpp recipe. Pin ≥
5
- * {@link OLLAMA_MIN_SAFE_VERSION} (CVE-2026-7482 floor). Host publish is
6
- * loopback-only — never expose `:11434` on `0.0.0.0`. Load models only from
7
- * Ollama's library; do not feed arbitrary untrusted GGUF into `/api/create`.
8
- *
9
- * Model pull is **not** done here: after compose is up,
10
- * {@link ensureOllamaModel} POSTs `/api/pull` to this container's loopback URL.
11
- */
12
-
13
- import type { ImageRecipe } from "../types.ts";
14
-
15
- /** Minimum safe Ollama release (CVE-2026-7482 — own Go GGUF loader). */
16
- export const OLLAMA_MIN_SAFE_VERSION = "0.17.1";
17
-
18
- /** Pinned default Ollama image — verified ≥ {@link OLLAMA_MIN_SAFE_VERSION}; never `latest`. */
19
- export const OLLAMA_IMAGE = "ollama/ollama:0.32.13";
20
-
21
- /** Ollama local model server. API on 11434; loopback publish only. */
22
- export const ollama: ImageRecipe = {
23
- id: "ollama",
24
- port: 11434,
25
- match: (i) => /(?:^|\/)ollama(?:[:@/]|$)/i.test(i),
26
- apply: (s) => ({
27
- environment: {
28
- // Listen on all interfaces *inside* the container (Docker networking).
29
- // Host publish uses publishBind — never 0.0.0.0 on the host.
30
- OLLAMA_HOST: "0.0.0.0:11434",
31
- OKE_AI_MODEL: "${OKE_AI_MODEL:-qwen3.5:9b}",
32
- },
33
- volumes: [`${s.serviceName}-data:/root/.ollama`],
34
- publishBind: "127.0.0.1",
35
- healthcheck: {
36
- test: ["CMD-SHELL", "/bin/ollama list >/dev/null 2>&1 || exit 1"],
37
- interval: "5s",
38
- timeout: "5s",
39
- retries: 24,
40
- start_period: "10s",
41
- },
42
- }),
43
- url: (_s, c) => `http://${c.host}:${c.port}`,
44
- };
@@ -1,55 +0,0 @@
1
- /**
2
- * SGLang image recipe — production-tier structured/agent inference.
3
- *
4
- * Prefer a managed provider unless you run inference yourself. Use SGLang when
5
- * the workload is structured generation / agent-heavy on real GPU hardware;
6
- * use vLLM for general multi-user serving. Pin an explicit tag — never
7
- * `latest`. Host publish is loopback-only.
8
- */
9
-
10
- import type { ImageRecipe } from "../types.ts";
11
-
12
- /** Pinned SGLang runtime image — never `latest`. */
13
- export const SGLANG_IMAGE = "lmsysorg/sglang:v0.5.17-runtime";
14
-
15
- /** SGLang — OpenAI-compatible HTTP on 30000; GPU reservation; loopback publish. */
16
- export const sglang: ImageRecipe = {
17
- id: "sglang",
18
- port: 30000,
19
- match: (i) => /sglang/i.test(i),
20
- apply: (s) => ({
21
- command: [
22
- "python3",
23
- "-m",
24
- "sglang.launch_server",
25
- "--model-path",
26
- "${OKE_AI_MODEL:-Qwen/Qwen3-0.6B}",
27
- "--host",
28
- "0.0.0.0",
29
- "--port",
30
- "30000",
31
- ],
32
- environment: {
33
- OKE_AI_MODEL: "${OKE_AI_MODEL:-Qwen/Qwen3-0.6B}",
34
- HF_HOME: "/root/.cache/huggingface",
35
- },
36
- volumes: [`${s.serviceName}-models:/root/.cache/huggingface`],
37
- ipc: "host",
38
- publishBind: "127.0.0.1",
39
- deploy: {
40
- resources: {
41
- reservations: {
42
- devices: [{ driver: "nvidia", count: "all", capabilities: ["gpu"] }],
43
- },
44
- },
45
- },
46
- healthcheck: {
47
- test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:30000/health >/dev/null || exit 1"],
48
- interval: "10s",
49
- timeout: "5s",
50
- retries: 30,
51
- start_period: "120s",
52
- },
53
- }),
54
- url: (_s, c) => `http://${c.host}:${c.port}/v1`,
55
- };
@@ -1,44 +0,0 @@
1
- /**
2
- * vLLM image recipe — production-tier local inference (`vllm/vllm-openai`).
3
- *
4
- * OpenAI-compatible server for multi-user GPU concurrency. Prefer a managed
5
- * provider unless you run inference yourself. Pin an explicit tag — never
6
- * `latest`. Host publish is loopback-only (same hard rule as llama.cpp).
7
- */
8
-
9
- import type { ImageRecipe } from "../types.ts";
10
-
11
- /** Pinned vLLM OpenAI server image — never `latest`. */
12
- export const VLLM_IMAGE = "vllm/vllm-openai:v0.27.1";
13
-
14
- /** vLLM — OpenAI-compatible HTTP on 8000; GPU reservation; loopback publish. */
15
- export const vllm: ImageRecipe = {
16
- id: "vllm",
17
- port: 8000,
18
- match: (i) => /(?:^|\/)vllm(?:[:@/]|$)/i.test(i),
19
- apply: (s) => ({
20
- command: ["--model", "${OKE_AI_MODEL:-Qwen/Qwen3-0.6B}", "--host", "0.0.0.0", "--port", "8000"],
21
- environment: {
22
- OKE_AI_MODEL: "${OKE_AI_MODEL:-Qwen/Qwen3-0.6B}",
23
- HF_HOME: "/root/.cache/huggingface",
24
- },
25
- volumes: [`${s.serviceName}-models:/root/.cache/huggingface`],
26
- ipc: "host",
27
- publishBind: "127.0.0.1",
28
- deploy: {
29
- resources: {
30
- reservations: {
31
- devices: [{ driver: "nvidia", count: "all", capabilities: ["gpu"] }],
32
- },
33
- },
34
- },
35
- healthcheck: {
36
- test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8000/health >/dev/null || exit 1"],
37
- interval: "10s",
38
- timeout: "5s",
39
- retries: 30,
40
- start_period: "120s",
41
- },
42
- }),
43
- url: (_s, c) => `http://${c.host}:${c.port}/v1`,
44
- };