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.
- package/AGENTS.md +18 -10
- package/README.md +2 -2
- package/package.json +51 -50
- package/site/content/docs/ai/index.mdx +25 -6
- package/site/content/docs/ai/llms-txt.mdx +1 -1
- package/site/content/docs/ai/meta.json +1 -1
- package/site/content/docs/ai/skills.mdx +4 -1
- package/site/content/docs/ai/try-it.mdx +58 -0
- package/site/content/docs/client/auth.mdx +208 -0
- package/site/content/docs/client/calling.mdx +451 -0
- package/site/content/docs/client/index.mdx +209 -0
- package/site/content/docs/client/live.mdx +234 -0
- package/site/content/docs/client/meta.json +5 -0
- package/site/content/docs/client/react.mdx +249 -0
- package/site/content/docs/elements/ai/agents.mdx +232 -0
- package/site/content/docs/elements/ai/index.mdx +341 -0
- package/site/content/docs/elements/ai/mcp.mdx +275 -0
- package/site/content/docs/elements/ai/meta.json +5 -0
- package/site/content/docs/elements/ai/models.mdx +283 -0
- package/site/content/docs/elements/ai/prompts.mdx +256 -0
- package/site/content/docs/elements/channel/email.mdx +266 -0
- package/site/content/docs/elements/channel/index.mdx +344 -0
- package/site/content/docs/elements/channel/meta.json +5 -0
- package/site/content/docs/elements/channel/push.mdx +218 -0
- package/site/content/docs/elements/channel/receipts.mdx +206 -0
- package/site/content/docs/elements/channel/sms.mdx +264 -0
- package/site/content/docs/elements/channel/whatsapp.mdx +220 -0
- package/site/content/docs/elements/clock/index.mdx +300 -0
- package/site/content/docs/elements/clock/meta.json +5 -0
- package/site/content/docs/elements/clock/schedules.mdx +508 -0
- package/site/content/docs/elements/clock/sleep.mdx +445 -0
- package/site/content/docs/elements/flow/consumers.mdx +707 -0
- package/site/content/docs/elements/flow/http.mdx +1080 -0
- package/site/content/docs/elements/flow/index.mdx +618 -0
- package/site/content/docs/elements/flow/meta.json +5 -0
- package/site/content/docs/elements/flow/routing.mdx +559 -0
- package/site/content/docs/elements/flow/workflows.mdx +640 -0
- package/site/content/docs/elements/gate/auth.mdx +371 -0
- package/site/content/docs/elements/gate/authorization.mdx +298 -0
- package/site/content/docs/elements/gate/index.mdx +418 -0
- package/site/content/docs/elements/gate/meta.json +5 -0
- package/site/content/docs/elements/gate/rate-limits.mdx +365 -0
- package/site/content/docs/elements/gate/rls.mdx +303 -0
- package/site/content/docs/elements/gate/tenancy.mdx +372 -0
- package/site/content/docs/elements/index.mdx +52 -11
- package/site/content/docs/elements/signal/broadcast.mdx +517 -0
- package/site/content/docs/elements/signal/index.mdx +352 -0
- package/site/content/docs/elements/signal/live.mdx +590 -0
- package/site/content/docs/elements/signal/meta.json +5 -0
- package/site/content/docs/elements/signal/once.mdx +596 -0
- package/site/content/docs/elements/store/files.mdx +659 -0
- package/site/content/docs/elements/store/index.mdx +310 -0
- package/site/content/docs/elements/store/kv.mdx +629 -0
- package/site/content/docs/elements/store/meta.json +5 -0
- package/site/content/docs/elements/store/search.mdx +946 -0
- package/site/content/docs/elements/store/sql.mdx +937 -0
- package/site/content/docs/elements/vault/config.mdx +256 -0
- package/site/content/docs/elements/vault/index.mdx +356 -0
- package/site/content/docs/elements/vault/meta.json +5 -0
- package/site/content/docs/elements/vault/rotation.mdx +273 -0
- package/site/content/docs/elements/vault/secrets.mdx +285 -0
- package/site/content/docs/index.mdx +78 -30
- package/site/content/docs/meta.json +11 -8
- package/site/content/docs/plugins/cors.mdx +2 -1
- package/site/content/docs/plugins/csrf.mdx +4 -1
- package/site/content/docs/plugins/headers.mdx +2 -1
- package/site/content/docs/plugins/ip-allowlist.mdx +2 -1
- package/site/content/docs/plugins/magic-link.mdx +6 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +2 -1
- package/site/content/docs/plugins/oauth.mdx +1 -1
- package/site/content/docs/plugins/otp.mdx +12 -1
- package/site/content/docs/plugins/passkey.mdx +39 -15
- package/site/content/docs/plugins/two-factor.mdx +91 -34
- package/site/content/docs/plugins/username.mdx +1 -1
- package/site/content/docs/recipes/caddy.mdx +3 -3
- package/site/content/docs/recipes/index.mdx +18 -28
- package/site/content/docs/recipes/mailpit.mdx +1 -1
- package/site/content/docs/recipes/meilisearch.mdx +3 -3
- package/site/content/docs/recipes/meta.json +1 -4
- package/site/content/docs/recipes/nginx.mdx +2 -2
- package/site/content/docs/recipes/openrouter.mdx +283 -0
- package/site/content/docs/recipes/pgdog.mdx +3 -3
- package/site/content/docs/recipes/postgres.mdx +1 -2
- package/site/content/docs/recipes/rustfs.mdx +2 -2
- package/site/content/docs/recipes/traefik.mdx +3 -3
- package/site/content/docs/reference/cli.mdx +231 -0
- package/site/content/docs/reference/configuration.mdx +30 -10
- package/site/content/docs/reference/environment-variables.mdx +93 -20
- package/site/content/docs/reference/errors.mdx +132 -29
- package/site/content/docs/reference/fx.mdx +137 -73
- package/site/content/docs/reference/index.mdx +13 -21
- package/site/content/docs/reference/meta.json +4 -5
- package/site/content/docs/reference/okid.mdx +42 -17
- package/site/content/docs/reference/plugins.mdx +4 -3
- package/site/content/docs/reference/security.mdx +197 -0
- package/site/content/docs/understand/meta.json +5 -0
- package/site/content/docs/understand/the-anatomy.mdx +132 -0
- package/site/content/docs/understand/the-model.mdx +32 -0
- package/site/content/docs/understand/the-problem.mdx +74 -0
- package/site/content/docs/understand/the-vocabulary.mdx +26 -0
- package/src/auth/api-keys.ts +2 -1
- package/src/auth/bindings.ts +64 -16
- package/src/auth/gate-auth.test.ts +6 -1
- package/src/auth/identity.ts +152 -5
- package/src/auth/index.ts +29 -0
- package/src/auth/invites.ts +2 -1
- package/src/auth/method-context.ts +41 -0
- package/src/auth/oauth-as/crypto.ts +2 -1
- package/src/auth/oauth-as/stores.ts +2 -1
- package/src/auth/operator.ts +2 -1
- package/src/auth/sessions-jwt.test.ts +77 -0
- package/src/auth/sessions.ts +20 -4
- package/src/auth/tenants.ts +3 -2
- package/src/auth/two-factor-challenge.test.ts +61 -0
- package/src/auth/two-factor-challenge.ts +284 -0
- package/src/auth/verification.ts +5 -0
- package/src/bench/REPORT.md +49 -0
- package/src/bench/g03-signal-once.bench.ts +1 -1
- package/src/bench/g10-observability-contention.bench.ts +1 -1
- package/src/bench/g17-hybrid-search.bench.ts +572 -0
- package/src/bench/load-app.ts +2 -10
- package/src/cli/ai-setup/ai-setup.test.ts +331 -42
- package/src/cli/ai-setup/apply.ts +294 -52
- package/src/cli/ai-setup/catalog.ts +238 -1342
- package/src/cli/ai-setup/index.ts +14 -99
- package/src/cli/ai-setup/prompts.ts +60 -443
- package/src/cli/ask-seed.test.ts +96 -0
- package/src/cli/ask-seed.ts +82 -0
- package/src/cli/ask-vault-gaps.test.ts +84 -0
- package/src/cli/ask-vault-gaps.ts +146 -0
- package/src/cli/build.ts +2 -2
- package/src/cli/client-add.test.ts +26 -1
- package/src/cli/client-add.ts +106 -27
- package/src/cli/competitor-mention-removal.test.ts +1 -1
- package/src/cli/db-seed.ts +2 -0
- package/src/cli/db.ts +150 -11
- package/src/cli/dev.ts +119 -211
- package/src/cli/docker-clean.ts +2 -2
- package/src/cli/docker-cli.test.ts +1 -1
- package/src/cli/doctor-diff.ts +4 -2
- package/src/cli/doctor-pii.test.ts +1 -1
- package/src/cli/doctor.ts +14 -1
- package/src/cli/hero-meta.test.ts +4 -2
- package/src/cli/load-config.images.test.ts +8 -10
- package/src/cli/load-config.ts +1 -1
- package/src/cli/registry.ts +22 -10
- package/src/cli/replay.ts +3 -1
- package/src/cli/start.ts +1 -1
- package/src/cli/tui/keys.ts +2 -3
- package/src/client/auth/cookies.ts +74 -0
- package/src/client/auth/create-auth-client.ts +636 -0
- package/src/client/auth/denials.ts +99 -0
- package/src/client/auth/session.ts +283 -0
- package/src/client/auth.test.ts +251 -0
- package/src/client/auth.ts +39 -114
- package/src/client/create-with-session.ts +245 -0
- package/src/client/create.ts +51 -5
- package/src/client/index.ts +11 -1
- package/src/client/live.ts +11 -79
- package/src/client/notes-contract.test.ts +5 -10
- package/src/client/sse.ts +137 -0
- package/src/client/stream.ts +147 -0
- package/src/client/transport.test.ts +32 -0
- package/src/client/transport.ts +123 -26
- package/src/client/types.ts +118 -14
- package/src/client-react/index.ts +185 -24
- package/src/client-react/use-live-query.ts +13 -3
- package/src/compiler/aot.test.ts +7 -4
- package/src/compiler/effects-embed.test.ts +56 -0
- package/src/compiler/effects-fetch.test.ts +43 -0
- package/src/compiler/effects-infer.ts +58 -2
- package/src/compiler/extract.test.ts +258 -44
- package/src/compiler/extract.ts +566 -48
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -4
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +2 -8
- package/src/compiler/fixtures/skyport.expected.json +4 -4
- package/src/compiler/fixtures/triggers/five-triggers.ts +14 -10
- package/src/compiler/response.ts +2 -2
- package/src/compiler/search-writer-isolation.test.ts +40 -0
- package/src/config/index.ts +11 -0
- package/src/console/server/app.ts +17 -23
- package/src/console/server/bind.ts +4 -0
- package/src/console/server/console.test.ts +2 -2
- package/src/console/server/flows-invoke.test.ts +50 -34
- package/src/console/server/flows.ts +436 -255
- package/src/console/server/runs-ingest.test.ts +6 -3
- package/src/console/server/serve.ts +1 -1
- package/src/console/server/signals.test.ts +1 -5
- package/src/console/server/signals.ts +8 -6
- package/src/console/server/state.ts +6 -1
- package/src/console/server/store.ts +1 -1
- package/src/console/ui-next/dist/assets/FileExportIcon-Ck-5od4R.js +1 -0
- package/src/console/ui-next/dist/assets/MoreHorizontalCircle01Icon-gMNGsE37.js +1 -0
- package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +1 -0
- package/src/console/ui-next/dist/assets/UnavailableIcon-D9cvHVPr.js +1 -0
- package/src/console/ui-next/dist/assets/UserIcon-DaE2PB5_.js +1 -0
- package/src/console/ui-next/dist/assets/access-page-DFeymU07.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-U1rdfblp.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-BeFJeqBG.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-CVAONPii.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-D2A6NJ-3.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-CAYD18cr.js +1 -0
- package/src/console/ui-next/dist/assets/dagre.esm-B1_XeuLP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DVWNjWjg.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-4h2LOVXM.js +1 -0
- package/src/console/ui-next/dist/assets/duration-tone-Cgk_h5ja.js +9 -0
- package/src/console/ui-next/dist/assets/element-icons-BI8cJgdh.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-CJs5A-wm.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-Dss7941e.js +1 -0
- package/src/console/ui-next/dist/assets/highlighted-json-MYZQtRnw.js +154 -0
- package/src/console/ui-next/dist/assets/http-method-Jrh39p7A.js +1 -0
- package/src/console/ui-next/dist/assets/index-D0zS5rKO.css +2 -0
- package/src/console/ui-next/dist/assets/index-DXP2dBIF.js +63 -0
- package/src/console/ui-next/dist/assets/observability-page-HvolXxTI.js +4 -0
- package/src/console/ui-next/dist/assets/react-dom-Ddte4I-Q.js +9 -0
- package/src/console/ui-next/dist/assets/replica-lag-CSh2dzrb.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-BatF8KrK.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-3ILd8oGn.js +1 -0
- package/src/console/ui-next/dist/assets/{sql-BskegiFM.js → sql-BsFa4tDR.js} +1 -1
- package/src/console/ui-next/dist/assets/store-page-eiKiHnNe.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-CZkMeKS-.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CW8y5A2h.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-B_RWJrEO.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-uk4WVboC.js → use-vault-list-CT4-gajj.js} +1 -1
- package/src/console/ui-next/dist/assets/vault-page-CWrg-A68.js +2 -0
- package/src/console/ui-next/dist/assets/xyflow-CSyC6ryz.css +1 -0
- package/src/console/ui-next/dist/assets/xyflow-yApv7D4e.js +7 -0
- package/src/console/ui-next/dist/index.html +6 -13
- package/src/console/ui-next/seed-invoke-host.ts +21 -18
- package/src/console/ui-next/src/client.ts +7 -2
- package/src/console/ui-next/src/features/flows/graph/element-map.ts +2 -0
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +12 -2
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +8 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-bars.ts +4 -0
- package/src/console/ui-next/src/features/flows/traces/waterfall-tooltip.ts +9 -3
- package/src/console/ui-next/src/features/store/detail/reveal-cell.tsx +7 -11
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +2 -2
- package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +2 -0
- package/src/console/ui-next/src/features/store/lib/grid-model.test.ts +25 -1
- package/src/console/ui-next/src/features/store/lib/grid-model.ts +36 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +3 -3
- package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +2 -0
- package/src/console/ui-next/src/features/units/lib/call-read-safe.ts +3 -1
- package/src/console/ui-next/src/features/units/lib/fields-from-schema.ts +6 -0
- package/src/docker/ai-model-status.test.ts +1 -12
- package/src/docker/ai-model-status.ts +10 -38
- package/src/docker/compose-up.test.ts +100 -0
- package/src/docker/compose-up.ts +292 -0
- package/src/docker/compose.ts +4 -14
- package/src/docker/derive.ts +9 -34
- package/src/docker/docker.test.ts +16 -261
- package/src/docker/images-config.test.ts +13 -25
- package/src/docker/index.ts +1 -24
- package/src/docker/recipes/index.ts +0 -23
- package/src/docker/recipes/pgdog.ts +6 -4
- package/src/docker/stack-id.test.ts +2 -2
- package/src/docker/stack-id.ts +0 -1
- package/src/docker/types.ts +2 -2
- package/src/drivers/ai-anthropic.ts +10 -0
- package/src/drivers/ai-openai-compatible.ts +34 -7
- package/src/drivers/ai-providers.test.ts +0 -107
- package/src/drivers/ai-stream.test.ts +0 -34
- package/src/drivers/ai-types.ts +17 -8
- package/src/drivers/channel-fcm.ts +6 -1
- package/src/drivers/channel-msegat.ts +6 -1
- package/src/drivers/channel-resend.ts +5 -1
- package/src/drivers/channel-smtp.ts +5 -1
- package/src/drivers/channel-sndr.ts +10 -1
- package/src/drivers/channel-taqnyat-mail.ts +5 -1
- package/src/drivers/channel-taqnyat-whatsapp.ts +6 -1
- package/src/drivers/channel-taqnyat.ts +6 -1
- package/src/drivers/channel-types.ts +10 -0
- package/src/drivers/channel-unifonic.ts +6 -1
- package/src/drivers/channel-wa-cloud.ts +6 -1
- package/src/drivers/channel-webpush.ts +6 -1
- package/src/drivers/external.ts +33 -0
- package/src/drivers/index.ts +0 -10
- package/src/drivers/meilisearch.ts +6 -0
- package/src/drivers/oauth-types.ts +4 -0
- package/src/drivers/postgres.test.ts +74 -3
- package/src/drivers/postgres.ts +175 -6
- package/src/drivers/signal-types.ts +5 -5
- package/src/drivers/types.ts +7 -0
- package/src/drivers/vault-types.ts +5 -0
- package/src/elements/ai/declare.ts +17 -4
- package/src/elements/ai/eval.ts +1 -1
- package/src/elements/ai/mcp-http.ts +1 -1
- package/src/elements/ai/pii.ts +1 -1
- package/src/elements/ai/providers.test.ts +289 -0
- package/src/elements/ai/providers.ts +169 -0
- package/src/elements/ai/runtime.ts +68 -5
- package/src/elements/ai/schema.ts +2 -2
- package/src/elements/ai.test.ts +7 -7
- package/src/elements/ai.ts +17 -1
- package/src/elements/channel/runtime.ts +105 -12
- package/src/elements/clock/cron-fields.test.ts +144 -0
- package/src/elements/clock/cron-fields.ts +185 -0
- package/src/elements/clock/declare.ts +247 -3
- package/src/elements/clock.ts +21 -1
- package/src/elements/index.ts +15 -0
- package/src/elements/signal/chaos-child.ts +1 -2
- package/src/elements/signal/declare.ts +65 -37
- package/src/elements/signal/delivery-modes.test.ts +11 -31
- package/src/elements/signal/dry-run-replay.test.ts +1 -7
- package/src/elements/signal/dry-run-write-isolation.test.ts +1 -7
- package/src/elements/signal/key-ordering.test.ts +6 -26
- package/src/elements/signal/lease-reclaim.test.ts +2 -6
- package/src/elements/signal/optional-emit.test.ts +6 -9
- package/src/elements/signal/order-lifecycle.test.ts +4 -17
- package/src/elements/signal/orphan-messages.test.ts +4 -15
- package/src/elements/signal/reconcile.test.ts +2 -2
- package/src/elements/signal/runtime.ts +1 -1
- package/src/elements/signal/schema-emit.test.ts +4 -8
- package/src/elements/signal.test.ts +24 -49
- package/src/elements/signal.ts +9 -2
- package/src/elements/store/domain-ddl.test.ts +1 -1
- package/src/elements/store/live-http.test.ts +9 -4
- package/src/elements/store/prepare-row.test.ts +123 -0
- package/src/elements/store/resource.ts +43 -12
- package/src/elements/store/schema-decl.ts +117 -0
- package/src/elements/store/search-backfill.ts +205 -0
- package/src/elements/store/search-bind.ts +66 -0
- package/src/elements/store/search-bm25.ts +78 -0
- package/src/elements/store/search-ddl.ts +156 -0
- package/src/elements/store/search-embed-flow.ts +141 -0
- package/src/elements/store/search-errors.ts +34 -0
- package/src/elements/store/search-fusion.ts +112 -0
- package/src/elements/store/search-lsh.ts +179 -0
- package/src/elements/store/search-runtime.ts +300 -0
- package/src/elements/store/search.test.ts +144 -0
- package/src/elements/store/sql-session.test.ts +1 -0
- package/src/elements/store/sql-session.ts +83 -3
- package/src/elements/store/table.ts +23 -2
- package/src/elements/store/upsert-app.test.ts +1 -3
- package/src/elements/store.ts +50 -0
- package/src/full.ts +4 -2
- package/src/http.ts +4 -1
- package/src/i18n/catalogs/ar.ts +14 -6
- package/src/i18n/catalogs/en.ts +14 -6
- package/src/index.ts +6 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +7 -7
- package/src/kernel/adopt-routes.ts +9 -1
- package/src/kernel/app-auth.ts +5 -0
- package/src/kernel/app.ts +102 -9
- package/src/kernel/auto-cache.test.ts +6 -12
- package/src/kernel/auto-registry.test.ts +3 -3
- package/src/kernel/boot-bind/ai.test.ts +22 -19
- package/src/kernel/boot-bind/ai.ts +5 -43
- package/src/kernel/boot-bind/clock.ts +23 -5
- package/src/kernel/boot-bind/honor-config.test.ts +4 -4
- package/src/kernel/boot.test.ts +11 -6
- package/src/kernel/boot.ts +66 -16
- package/src/kernel/boundary-contract.ts +93 -0
- package/src/kernel/budget.test.ts +1 -1
- package/src/kernel/call.ts +89 -0
- package/src/kernel/capability.ts +6 -0
- package/src/kernel/client-descriptor.ts +123 -0
- package/src/kernel/clock-timezone.test.ts +80 -0
- package/src/kernel/correlation.test.ts +1 -1
- package/src/kernel/dry-run.ts +11 -8
- package/src/kernel/effects-stamping.test.ts +42 -9
- package/src/kernel/effects.test.ts +4 -2
- package/src/kernel/effects.ts +51 -9
- package/src/kernel/errors-channel.ts +17 -0
- package/src/kernel/errors-live-resume.ts +3 -2
- package/src/kernel/errors-tenant.ts +7 -4
- package/src/kernel/errors.registry-helpers.ts +96 -0
- package/src/kernel/errors.registry.test.ts +139 -25
- package/src/kernel/errors.ts +102 -23
- package/src/kernel/external-effects.test.ts +196 -0
- package/src/kernel/flow.test.ts +13 -4
- package/src/kernel/flow.ts +69 -71
- package/src/kernel/fx-ask-telemetry.test.ts +2 -2
- package/src/kernel/fx-dead-letters.test.ts +3 -3
- package/src/kernel/fx-fetch.ts +55 -0
- package/src/kernel/fx-live-stream.ts +2 -2
- package/src/kernel/fx-live.test.ts +8 -8
- package/src/kernel/fx.test.ts +17 -11
- package/src/kernel/fx.ts +180 -57
- package/src/kernel/horizontal-child.ts +1 -1
- package/src/kernel/http-query.test.ts +3 -6
- package/src/kernel/index.ts +13 -3
- package/src/kernel/instance-id.ts +1 -1
- package/src/kernel/instances.test.ts +40 -0
- package/src/kernel/instances.ts +47 -9
- package/src/kernel/live-http.test.ts +6 -7
- package/src/kernel/live-http.ts +6 -2
- package/src/kernel/live-resume.test.ts +1 -1
- package/src/kernel/mcp-tool.test.ts +3 -3
- package/src/kernel/on.ts +98 -4
- package/src/kernel/pipeline.test.ts +12 -15
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/stamp-http.test.ts +3 -3
- package/src/kernel/triggers.ts +210 -95
- package/src/kernel-entry.ts +0 -1
- package/src/manifest/diff.ts +11 -1
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/types.ts +46 -2
- package/src/mcp/docs-index.ts +3 -3
- package/src/mcp/docs-mcp.test.ts +2 -2
- package/src/mcp/docs-tools.ts +1 -1
- package/src/okid.test.ts +56 -0
- package/src/okid.ts +55 -11
- package/src/plugins/anonymous.ts +8 -4
- package/src/plugins/auth/shared.ts +27 -3
- package/src/plugins/auth-delivery.mailpit.integration.test.ts +1 -1
- package/src/plugins/auth-methods.security.test.ts +561 -23
- package/src/plugins/config-source.ts +1 -1
- package/src/plugins/csrf.test.ts +65 -0
- package/src/plugins/index.ts +2 -0
- package/src/plugins/magic-link.ts +36 -16
- package/src/plugins/oauth/shared.ts +3 -1
- package/src/plugins/oauth.ts +22 -14
- package/src/plugins/otp.ts +37 -21
- package/src/plugins/passkey-webauthn.ts +9 -6
- package/src/plugins/passkey.ts +98 -26
- package/src/plugins/pre-account-hijack.test.ts +223 -0
- package/src/plugins/two-factor.ts +466 -37
- package/src/plugins/username.ts +19 -7
- package/src/release/build-lib.ts +1 -0
- package/src/release/limits.ts +2 -2
- package/src/release/measure.ts +1 -1
- package/src/runs/collect.test.ts +0 -2
- package/src/runtime/json-code-block.test.ts +250 -20
- package/src/runtime/json-code-block.ts +1261 -41
- package/src/term.test.ts +1 -1
- package/src/test/create-test-app.test.ts +12 -12
- package/src/test/live-signals.test.ts +6 -6
- package/src/test/provisions.integration.test.ts +10 -14
- package/src/test/reset-element-registries.ts +1 -1
- package/src/test/tenant-isolation.test.ts +12 -6
- package/site/content/docs/deployment/docker-swarm.mdx +0 -164
- package/site/content/docs/deployment/docker.mdx +0 -227
- package/site/content/docs/deployment/index.mdx +0 -83
- package/site/content/docs/deployment/kubernetes.mdx +0 -176
- package/site/content/docs/deployment/meta.json +0 -5
- package/site/content/docs/deployment/reverse-proxy.mdx +0 -234
- package/site/content/docs/elements/ai.mdx +0 -385
- package/site/content/docs/elements/channel.mdx +0 -346
- package/site/content/docs/elements/clock.mdx +0 -244
- package/site/content/docs/elements/flow.mdx +0 -420
- package/site/content/docs/elements/gate.mdx +0 -436
- package/site/content/docs/elements/signal.mdx +0 -380
- package/site/content/docs/elements/store.mdx +0 -1099
- package/site/content/docs/elements/vault.mdx +0 -405
- package/site/content/docs/get-started/basic-usage.mdx +0 -173
- package/site/content/docs/get-started/index.mdx +0 -43
- package/site/content/docs/get-started/installation.mdx +0 -220
- package/site/content/docs/get-started/introduction.mdx +0 -132
- package/site/content/docs/get-started/meta.json +0 -13
- package/site/content/docs/get-started/project-structure.mdx +0 -925
- package/site/content/docs/get-started/testing.mdx +0 -328
- package/site/content/docs/get-started/why.mdx +0 -162
- package/site/content/docs/recipes/llama-cpp.mdx +0 -151
- package/site/content/docs/recipes/ollama.mdx +0 -142
- package/site/content/docs/recipes/sglang.mdx +0 -105
- package/site/content/docs/recipes/vllm.mdx +0 -106
- package/site/content/docs/reference/cli.md +0 -232
- package/site/content/docs/reference/client.mdx +0 -469
- package/site/content/docs/reference/security.md +0 -74
- package/src/cli/ai-setup/detect-ollama.ts +0 -213
- package/src/cli/ai-setup/recommend.test.ts +0 -225
- package/src/cli/ai-setup/recommend.ts +0 -225
- package/src/cli/dev-controls.test.ts +0 -122
- package/src/cli/dev-controls.ts +0 -164
- package/src/cli/tui/DevLive.tsx +0 -126
- package/src/console/ui-next/dist/assets/access-page-De7Lc2JC.js +0 -4
- package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +0 -1
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +0 -1
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +0 -1
- package/src/console/ui-next/dist/assets/dagre.esm-ZwcdTuZZ.js +0 -1
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +0 -1
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +0 -1
- package/src/console/ui-next/dist/assets/duration-tone-sC3lGABz.js +0 -9
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-RGy7VEA_.js +0 -1
- package/src/console/ui-next/dist/assets/highlighted-json-Awq7gYdu.js +0 -154
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +0 -1
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
- package/src/console/ui-next/dist/assets/index-_rgpdVzo.js +0 -66
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-Ds6pcnh-.js +0 -4
- package/src/console/ui-next/dist/assets/preload-helper-oH4irX4C.js +0 -1
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +0 -1
- package/src/console/ui-next/dist/assets/react-dom-Bph1y7z7.js +0 -9
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +0 -18
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +0 -1
- package/src/console/ui-next/dist/assets/skeleton-D-czQJT6.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-02xOiqIK.js +0 -41
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +0 -2
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BtyhmWb4.js +0 -55
- package/src/console/ui-next/dist/assets/units-page-4rHOePuE.js +0 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +0 -1
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-DISPgxLM.js +0 -2
- package/src/console/ui-next/dist/assets/xyflow-D7n4g6go.js +0 -7
- package/src/console/ui-next/dist/assets/xyflow-DZ0Ws1xk.css +0 -1
- package/src/docker/ollama-pull.ts +0 -232
- package/src/docker/recipes/llama-cpp.ts +0 -298
- package/src/docker/recipes/ollama.ts +0 -44
- package/src/docker/recipes/sglang.ts +0 -55
- package/src/docker/recipes/vllm.ts +0 -44
- package/src/drivers/ai-ollama-tools.integration.test.ts +0 -109
- package/src/drivers/ai-ollama.integration.test.ts +0 -184
- package/src/drivers/ai-ollama.ts +0 -389
- 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,
|
|
5
|
+
import { isCancel, password, select, text } from "@clack/prompts";
|
|
6
6
|
import {
|
|
7
7
|
CLOUD_PROVIDERS,
|
|
8
|
-
|
|
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
|
-
| "
|
|
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
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
|
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:
|
|
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
|
+
});
|