okengine 0.18.5 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +18 -10
- package/README.md +2 -2
- package/package.json +51 -50
- 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 +20 -10
- 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 +63 -36
- package/site/content/docs/meta.json +8 -7
- 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/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 +257 -43
- 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 -144
- 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 -114
- 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-3-EFj-2G.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-cVFnA4HH.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-CMIgUbD0.js +0 -66
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +0 -2
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-CQ3p34ip.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-l8FeKfnP.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-CL-d_mLE.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
|
@@ -4,27 +4,28 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, expect, test } from "bun:test";
|
|
6
6
|
import { mockAiDriver } from "../../drivers/ai-mock.ts";
|
|
7
|
-
import {
|
|
8
|
-
import { aiDriverFor,
|
|
7
|
+
import { openaiCompatibleAiDriver } from "../../drivers/ai-openai-compatible.ts";
|
|
8
|
+
import { aiDriverFor, bindAi, openDefaultsFor, resolveAiDriverId } from "./ai.ts";
|
|
9
9
|
|
|
10
10
|
describe("ai boot binder", () => {
|
|
11
11
|
test("aiDriverFor maps protocol ids; unknown / reserved throw", () => {
|
|
12
12
|
expect(aiDriverFor("mock").id).toBe("mock");
|
|
13
|
-
expect(aiDriverFor("
|
|
13
|
+
expect(aiDriverFor("openai-compatible")).toBe(openaiCompatibleAiDriver);
|
|
14
|
+
expect(() => aiDriverFor("ollama")).toThrow(/unknown AI driver/);
|
|
14
15
|
expect(() => aiDriverFor("bedrock")).toThrow(/not implemented/);
|
|
15
16
|
expect(() => aiDriverFor("nope")).toThrow(/unknown AI driver/);
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
test("resolveAiDriverId defaults to mock; honours config + OKE_AI_DRIVER in docker", () => {
|
|
19
20
|
expect(resolveAiDriverId({}, "test")).toBe("mock");
|
|
20
|
-
expect(
|
|
21
|
-
"
|
|
22
|
-
);
|
|
21
|
+
expect(
|
|
22
|
+
resolveAiDriverId({ config: { drivers: { ai: { test: "openai-compatible" } } } }, "test"),
|
|
23
|
+
).toBe("openai-compatible");
|
|
23
24
|
|
|
24
25
|
const prev = process.env.OKE_AI_DRIVER;
|
|
25
|
-
process.env.OKE_AI_DRIVER = "
|
|
26
|
+
process.env.OKE_AI_DRIVER = "openai-compatible";
|
|
26
27
|
try {
|
|
27
|
-
expect(resolveAiDriverId({}, "test", true)).toBe("
|
|
28
|
+
expect(resolveAiDriverId({}, "test", true)).toBe("openai-compatible");
|
|
28
29
|
} finally {
|
|
29
30
|
if (prev === undefined) delete process.env.OKE_AI_DRIVER;
|
|
30
31
|
else process.env.OKE_AI_DRIVER = prev;
|
|
@@ -51,37 +52,39 @@ describe("ai boot binder", () => {
|
|
|
51
52
|
}
|
|
52
53
|
});
|
|
53
54
|
|
|
54
|
-
test("openDefaultsFor openai-compatible
|
|
55
|
+
test("openDefaultsFor openai-compatible allows docker without OKE_AI_URL (cloud registry)", () => {
|
|
55
56
|
const prevUrl = process.env.OKE_AI_URL;
|
|
56
57
|
const prevBase = process.env.OPENAI_BASE_URL;
|
|
57
58
|
delete process.env.OKE_AI_URL;
|
|
58
59
|
delete process.env.OPENAI_BASE_URL;
|
|
59
60
|
try {
|
|
60
|
-
expect(
|
|
61
|
+
expect(openDefaultsFor("openai-compatible", true)).toEqual({});
|
|
61
62
|
} finally {
|
|
62
63
|
if (prevUrl !== undefined) process.env.OKE_AI_URL = prevUrl;
|
|
63
64
|
if (prevBase !== undefined) process.env.OPENAI_BASE_URL = prevBase;
|
|
64
65
|
}
|
|
65
66
|
});
|
|
66
67
|
|
|
67
|
-
test("
|
|
68
|
+
test("openDefaultsFor openai-compatible keeps compose URL when present", () => {
|
|
68
69
|
const prevUrl = process.env.OKE_AI_URL;
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
delete process.env.
|
|
70
|
+
const prevBase = process.env.OPENAI_BASE_URL;
|
|
71
|
+
process.env.OKE_AI_URL = "http://127.0.0.1:8080/v1";
|
|
72
|
+
delete process.env.OPENAI_BASE_URL;
|
|
72
73
|
try {
|
|
73
|
-
expect((
|
|
74
|
-
|
|
74
|
+
expect(openDefaultsFor("openai-compatible", true)).toEqual({
|
|
75
|
+
baseUrl: "http://127.0.0.1:8080/v1",
|
|
76
|
+
});
|
|
75
77
|
} finally {
|
|
76
|
-
if (prevUrl
|
|
77
|
-
|
|
78
|
+
if (prevUrl === undefined) delete process.env.OKE_AI_URL;
|
|
79
|
+
else process.env.OKE_AI_URL = prevUrl;
|
|
80
|
+
if (prevBase !== undefined) process.env.OPENAI_BASE_URL = prevBase;
|
|
78
81
|
}
|
|
79
82
|
});
|
|
80
83
|
|
|
81
84
|
test("bindAi uses injected defaultDriver over config (tests stay on mock)", () => {
|
|
82
85
|
const runtime = bindAi(
|
|
83
86
|
{
|
|
84
|
-
config: { drivers: { ai: { test: "
|
|
87
|
+
config: { drivers: { ai: { test: "openai-compatible" } } },
|
|
85
88
|
ai: { defaultDriver: mockAiDriver },
|
|
86
89
|
},
|
|
87
90
|
undefined,
|
|
@@ -9,11 +9,6 @@
|
|
|
9
9
|
import { resolveDriverId, type ConfigEnv } from "../../config/index.ts";
|
|
10
10
|
import { anthropicAiDriver } from "../../drivers/ai-anthropic.ts";
|
|
11
11
|
import { mockAiDriver } from "../../drivers/ai-mock.ts";
|
|
12
|
-
import {
|
|
13
|
-
normalizeOllamaBaseUrl,
|
|
14
|
-
ollamaAiDriver,
|
|
15
|
-
OLLAMA_DEFAULT_BASE_URL,
|
|
16
|
-
} from "../../drivers/ai-ollama.ts";
|
|
17
12
|
import { openaiCompatibleAiDriver } from "../../drivers/ai-openai-compatible.ts";
|
|
18
13
|
import type { AiDriver, AiOpenOptions } from "../../drivers/ai-types.ts";
|
|
19
14
|
import { createAiRuntime, type AiRuntime } from "../../elements/ai.ts";
|
|
@@ -50,7 +45,6 @@ export function bindAi(
|
|
|
50
45
|
mock: lazyDriver("mock", docker),
|
|
51
46
|
anthropic: lazyDriver("anthropic", docker),
|
|
52
47
|
"openai-compatible": lazyDriver("openai-compatible", docker),
|
|
53
|
-
ollama: lazyDriver("ollama", docker),
|
|
54
48
|
},
|
|
55
49
|
gates: options.ai?.gates ?? gate,
|
|
56
50
|
now,
|
|
@@ -77,7 +71,7 @@ export function resolveAiDriverId(options: BootOptions, env: ConfigEnv, docker =
|
|
|
77
71
|
if (liveUrl && fromEnv && fromEnv !== "mock") return fromEnv;
|
|
78
72
|
const resolved = resolveDriverId(options.config?.drivers?.ai, env);
|
|
79
73
|
if (liveUrl && (resolved === undefined || resolved === "mock")) {
|
|
80
|
-
return fromEnv === "
|
|
74
|
+
return fromEnv === "openai-compatible" || !fromEnv ? "openai-compatible" : fromEnv;
|
|
81
75
|
}
|
|
82
76
|
if (resolved) return resolved;
|
|
83
77
|
return "mock";
|
|
@@ -97,8 +91,6 @@ export function aiDriverFor(id: string): AiDriver {
|
|
|
97
91
|
return anthropicAiDriver;
|
|
98
92
|
case "openai-compatible":
|
|
99
93
|
return openaiCompatibleAiDriver;
|
|
100
|
-
case "ollama":
|
|
101
|
-
return ollamaAiDriver;
|
|
102
94
|
case "bedrock":
|
|
103
95
|
case "vertex":
|
|
104
96
|
throw new Error(`oke boot: AI driver "${id}" is reserved but not implemented yet`);
|
|
@@ -113,13 +105,7 @@ export function aiDriverFor(id: string): AiDriver {
|
|
|
113
105
|
* @param id - Driver id
|
|
114
106
|
* @param docker - Docker mode
|
|
115
107
|
*/
|
|
116
|
-
export function openDefaultsFor(id: string,
|
|
117
|
-
if (id === "ollama") {
|
|
118
|
-
return {
|
|
119
|
-
baseUrl: aiUrlFor(docker),
|
|
120
|
-
...(process.env.OKE_AI_MODEL?.trim() ? { model: process.env.OKE_AI_MODEL.trim() } : {}),
|
|
121
|
-
};
|
|
122
|
-
}
|
|
108
|
+
export function openDefaultsFor(id: string, _docker = false): AiOpenOptions {
|
|
123
109
|
if (id === "anthropic") {
|
|
124
110
|
const apiKey = process.env.ANTHROPIC_API_KEY?.trim();
|
|
125
111
|
return {
|
|
@@ -129,12 +115,10 @@ export function openDefaultsFor(id: string, docker = false): AiOpenOptions {
|
|
|
129
115
|
}
|
|
130
116
|
if (id === "openai-compatible") {
|
|
131
117
|
const apiKey = process.env.OPENAI_API_KEY?.trim();
|
|
118
|
+
// Prefer explicit proxy / local compose URL. Cloud registry models
|
|
119
|
+
// (`ai.model({ provider: "openrouter" })`) resolve their own baseUrl —
|
|
120
|
+
// do not require OKE_AI_URL just because `oke dev` uses Docker for Postgres.
|
|
132
121
|
const baseUrl = process.env.OPENAI_BASE_URL?.trim() || process.env.OKE_AI_URL?.trim();
|
|
133
|
-
if (docker && !baseUrl) {
|
|
134
|
-
throw new Error(
|
|
135
|
-
"oke boot: openai-compatible driver needs OKE_AI_URL (did `oke dev` write .env.local?)",
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
122
|
return {
|
|
139
123
|
...(apiKey ? { apiKey } : {}),
|
|
140
124
|
...(baseUrl ? { baseUrl } : {}),
|
|
@@ -144,28 +128,6 @@ export function openDefaultsFor(id: string, docker = false): AiOpenOptions {
|
|
|
144
128
|
return {};
|
|
145
129
|
}
|
|
146
130
|
|
|
147
|
-
/**
|
|
148
|
-
* Ollama base URL — docker fails loud when compose did not write one.
|
|
149
|
-
*
|
|
150
|
-
* @param docker - Docker mode
|
|
151
|
-
*/
|
|
152
|
-
export function aiUrlFor(docker = false): string {
|
|
153
|
-
const raw = process.env.OKE_AI_URL?.trim() || process.env.OLLAMA_HOST?.trim();
|
|
154
|
-
if (raw) return normalizeOllamaBaseUrl(raw);
|
|
155
|
-
if (docker) {
|
|
156
|
-
throw new Error(
|
|
157
|
-
"oke boot: ollama driver needs OKE_AI_URL (did `oke dev -d` write .env.local?)",
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
return OLLAMA_DEFAULT_BASE_URL;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Merge fixed open defaults under per-call options.
|
|
165
|
-
*
|
|
166
|
-
* @param driver - Base driver
|
|
167
|
-
* @param defaults - Env-derived open options
|
|
168
|
-
*/
|
|
169
131
|
/**
|
|
170
132
|
* Open a protocol driver with env defaults only when a model actually uses it.
|
|
171
133
|
*
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { mkdirSync } from "node:fs";
|
|
6
6
|
import { dirname, resolve } from "node:path";
|
|
7
7
|
import {
|
|
8
|
+
applyClockTimezoneDefaults,
|
|
8
9
|
clock as declareClock,
|
|
9
10
|
createClockRuntime,
|
|
10
11
|
createFileCronStore,
|
|
@@ -38,6 +39,15 @@ export function resolveClockDriverId(options: BootOptions, env: ConfigEnv): stri
|
|
|
38
39
|
return resolveDriverId(options.config?.drivers?.clock, env, CLOCK_DEFAULTS)!;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Effective default timezone: `oke({ clock })` wins over `defineConfig({ clock })`.
|
|
44
|
+
*
|
|
45
|
+
* @param options - Boot options
|
|
46
|
+
*/
|
|
47
|
+
function resolveDefaultClockTimezone(options: BootOptions): string | undefined {
|
|
48
|
+
return options.clock?.timezone ?? options.config?.clock?.timezone;
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
/**
|
|
42
52
|
* Construct / adopt a Clock runtime, register decls, reconcile.
|
|
43
53
|
*
|
|
@@ -102,19 +112,27 @@ export async function bindClock(
|
|
|
102
112
|
);
|
|
103
113
|
}
|
|
104
114
|
|
|
115
|
+
const defaultTimezone = resolveDefaultClockTimezone(options);
|
|
105
116
|
const clockDecls = new Map<string, ClockDecl>();
|
|
106
|
-
for (const c of options.clocks ?? []) {
|
|
117
|
+
for (const c of applyClockTimezoneDefaults(options.clocks ?? [], defaultTimezone)) {
|
|
107
118
|
clockDecls.set(c.name, c);
|
|
108
119
|
}
|
|
109
120
|
const coveredEvery = new Set([...clockDecls.values()].flatMap((c) => (c.every ? [c.every] : [])));
|
|
110
121
|
for (const b of options.bindings ?? []) {
|
|
122
|
+
if (b.trigger.kind === "clock" && b.trigger.clock && !clockDecls.has(b.trigger.name)) {
|
|
123
|
+
const [resolved] = applyClockTimezoneDefaults([b.trigger.clock], defaultTimezone);
|
|
124
|
+
if (resolved) clockDecls.set(b.trigger.name, resolved);
|
|
125
|
+
}
|
|
111
126
|
if (b.trigger.kind !== "every") continue;
|
|
112
127
|
if (clockDecls.has(b.trigger.interval) || coveredEvery.has(b.trigger.interval)) continue;
|
|
113
|
-
|
|
114
|
-
b.trigger.interval,
|
|
115
|
-
|
|
128
|
+
const [synthetic] = applyClockTimezoneDefaults(
|
|
129
|
+
[declareClock(b.trigger.interval, { every: b.trigger.interval })],
|
|
130
|
+
defaultTimezone,
|
|
116
131
|
);
|
|
117
|
-
|
|
132
|
+
if (synthetic) {
|
|
133
|
+
clockDecls.set(b.trigger.interval, synthetic);
|
|
134
|
+
coveredEvery.add(b.trigger.interval);
|
|
135
|
+
}
|
|
118
136
|
}
|
|
119
137
|
for (const decl of clockDecls.values()) {
|
|
120
138
|
clock.register(decl);
|
|
@@ -303,7 +303,7 @@ describe("boot binders honour drivers.* config", () => {
|
|
|
303
303
|
test("signal: drivers.signal memory binds memory", async () => {
|
|
304
304
|
const result = await bootApplication({
|
|
305
305
|
env: "test",
|
|
306
|
-
signals: [signal("ping"
|
|
306
|
+
signals: [signal.once("ping")],
|
|
307
307
|
config: {
|
|
308
308
|
drivers: {
|
|
309
309
|
signal: { dev: "redis", test: "memory", prod: "redis" },
|
|
@@ -321,7 +321,7 @@ describe("boot binders honour drivers.* config", () => {
|
|
|
321
321
|
const fake = createSignalRedisFake();
|
|
322
322
|
const result = await bootApplication({
|
|
323
323
|
env: "test",
|
|
324
|
-
signals: [signal("ping"
|
|
324
|
+
signals: [signal.once("ping")],
|
|
325
325
|
clients: { signalRedis: fake },
|
|
326
326
|
config: {
|
|
327
327
|
drivers: {
|
|
@@ -340,7 +340,7 @@ describe("boot binders honour drivers.* config", () => {
|
|
|
340
340
|
await expect(
|
|
341
341
|
bootApplication({
|
|
342
342
|
env: "test",
|
|
343
|
-
signals: [signal("ping"
|
|
343
|
+
signals: [signal.once("ping")],
|
|
344
344
|
config: {
|
|
345
345
|
drivers: {
|
|
346
346
|
signal: { test: "postgres" },
|
|
@@ -421,7 +421,7 @@ describe("boot binders honour drivers.* config", () => {
|
|
|
421
421
|
await expect(
|
|
422
422
|
bootApplication({
|
|
423
423
|
env: "test",
|
|
424
|
-
signals: [signal("ping"
|
|
424
|
+
signals: [signal.once("ping")],
|
|
425
425
|
config: {
|
|
426
426
|
drivers: {
|
|
427
427
|
signal: { test: "nats" },
|
package/src/kernel/boot.test.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { store } from "../elements/store.ts";
|
|
|
16
16
|
import { vault, VaultBootError } from "../elements/vault.ts";
|
|
17
17
|
import { bootApplication, resolveElementNeeds } from "./boot.ts";
|
|
18
18
|
import { flow, resetFlowSeq } from "./flow.ts";
|
|
19
|
-
import {
|
|
19
|
+
import { http } from "./triggers.ts";
|
|
20
20
|
import { oke } from "./app.ts";
|
|
21
21
|
import { on, resetBindings } from "./on.ts";
|
|
22
22
|
|
|
@@ -79,7 +79,7 @@ describe("boot — lazy element needs", () => {
|
|
|
79
79
|
expect(needs.signal).toBe(false);
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
test("oke() Store-only graph stays under the prior
|
|
82
|
+
test("oke() Store-only graph stays under the prior 58 kB baseline", async () => {
|
|
83
83
|
const dir = await mkdtemp(join(tmpdir(), "oke-store-only-"));
|
|
84
84
|
const entry = join(dir, "entry.ts");
|
|
85
85
|
const appPath = join(import.meta.dir, "app.ts");
|
|
@@ -118,6 +118,8 @@ describe("boot — lazy element needs", () => {
|
|
|
118
118
|
}
|
|
119
119
|
// Rebased after host API-key persist + allowlist on `oke()` and the
|
|
120
120
|
// realtime bridge (CDC sink, LiveQuery runtime, openLiveStream).
|
|
121
|
+
// Browser JSON page (`json-code-block`) is lazy on `app.fetch` so this
|
|
122
|
+
// graph does not pin the traces-language HTML/CSS chunk.
|
|
121
123
|
expect(total).toBeLessThan(58_000);
|
|
122
124
|
} finally {
|
|
123
125
|
await rm(dir, { recursive: true, force: true });
|
|
@@ -152,8 +154,9 @@ describe("boot — cron fires without manual dispatch", () => {
|
|
|
152
154
|
resetFlowSeq();
|
|
153
155
|
|
|
154
156
|
let ran = 0;
|
|
157
|
+
const expireStale = clock("expire-stale", { every: "1h" });
|
|
155
158
|
on(
|
|
156
|
-
|
|
159
|
+
expireStale,
|
|
157
160
|
flow("cleanup.expire", {
|
|
158
161
|
do: () => {
|
|
159
162
|
ran += 1;
|
|
@@ -163,14 +166,14 @@ describe("boot — cron fires without manual dispatch", () => {
|
|
|
163
166
|
|
|
164
167
|
const app = oke({
|
|
165
168
|
name: "cron-boot",
|
|
166
|
-
clocks: [
|
|
169
|
+
clocks: [expireStale],
|
|
167
170
|
env: "test",
|
|
168
171
|
});
|
|
169
172
|
|
|
170
173
|
await app.boot({
|
|
171
174
|
env: "test",
|
|
172
175
|
startScheduler: false,
|
|
173
|
-
clocks: [
|
|
176
|
+
clocks: [expireStale],
|
|
174
177
|
});
|
|
175
178
|
|
|
176
179
|
const rt = app.bootResult!.clock!;
|
|
@@ -251,8 +254,9 @@ describe("boot — cron autostart via real timer loop", () => {
|
|
|
251
254
|
jest.useFakeTimers({ now: 1_000_000 });
|
|
252
255
|
|
|
253
256
|
let ran = 0;
|
|
257
|
+
const autostartClock = clock("cleanup.autostart", { every: "10m" });
|
|
254
258
|
on(
|
|
255
|
-
|
|
259
|
+
autostartClock,
|
|
256
260
|
flow("cleanup.autostart", {
|
|
257
261
|
do: () => {
|
|
258
262
|
ran += 1;
|
|
@@ -263,6 +267,7 @@ describe("boot — cron autostart via real timer loop", () => {
|
|
|
263
267
|
// Non-test env → startScheduler defaults ON. No createTestApp / advance.
|
|
264
268
|
const app = oke({
|
|
265
269
|
name: "cron-autostart",
|
|
270
|
+
clocks: [autostartClock],
|
|
266
271
|
env: "dev",
|
|
267
272
|
schedulerIntervalMs: 1000,
|
|
268
273
|
config: {
|
package/src/kernel/boot.ts
CHANGED
|
@@ -42,6 +42,7 @@ import type {
|
|
|
42
42
|
} from "../elements/vault.ts";
|
|
43
43
|
import type { JournalRuntime } from "./boot-bind/journal.ts";
|
|
44
44
|
import type { InstanceRuntime, InstanceStore } from "./instances.ts";
|
|
45
|
+
import { isBenignSqlDisconnect } from "./instances.ts";
|
|
45
46
|
import { resolveInstanceId } from "./instance-id.ts";
|
|
46
47
|
import {
|
|
47
48
|
resolveRunsConsoleBridge,
|
|
@@ -111,6 +112,14 @@ export interface BootOptions {
|
|
|
111
112
|
readonly signals?: readonly SignalDecl[];
|
|
112
113
|
/** Named clock declarations. */
|
|
113
114
|
readonly clocks?: readonly ClockDecl[];
|
|
115
|
+
/**
|
|
116
|
+
* Clock element defaults — `{ timezone }` for declarations that omit
|
|
117
|
+
* `timezone`. Merged with `defineConfig({ clock: { timezone } })` at bind
|
|
118
|
+
* (`oke({ clock })` wins over config).
|
|
119
|
+
*/
|
|
120
|
+
readonly clock?: {
|
|
121
|
+
readonly timezone?: string;
|
|
122
|
+
};
|
|
114
123
|
/** Store facet declarations to register. */
|
|
115
124
|
readonly stores?: readonly StoreDecl[];
|
|
116
125
|
/** Channel runtime options. */
|
|
@@ -281,7 +290,7 @@ export function resolveElementNeeds(options: BootOptions): ElementNeeds {
|
|
|
281
290
|
if ((e?.sends?.length ?? 0) > 0) channel = true;
|
|
282
291
|
if ((e?.asks?.length ?? 0) > 0) ai = true;
|
|
283
292
|
for (const t of f.triggers) {
|
|
284
|
-
if (t.kind === "every") clock = true;
|
|
293
|
+
if (t.kind === "clock" || t.kind === "every") clock = true;
|
|
285
294
|
if (t.kind === "signal") signal = true;
|
|
286
295
|
if (t.kind === "cdc") store = true;
|
|
287
296
|
if (t.kind === "http" && t.gates.length > 0) gate = true;
|
|
@@ -292,7 +301,7 @@ export function resolveElementNeeds(options: BootOptions): ElementNeeds {
|
|
|
292
301
|
for (const b of options.bindings ?? []) {
|
|
293
302
|
considerFlow(b.flow);
|
|
294
303
|
const t = b.trigger;
|
|
295
|
-
if (t.kind === "every") clock = true;
|
|
304
|
+
if (t.kind === "clock" || t.kind === "every") clock = true;
|
|
296
305
|
if (t.kind === "signal") signal = true;
|
|
297
306
|
if (t.kind === "cdc") store = true;
|
|
298
307
|
if (t.kind === "http" && t.gates.length > 0) gate = true;
|
|
@@ -529,10 +538,16 @@ export async function bootApplication(input: BootOptions = {}): Promise<BootResu
|
|
|
529
538
|
const clockRt = clock;
|
|
530
539
|
const durableResume = options.onDurableResume;
|
|
531
540
|
const fleet = instances;
|
|
541
|
+
/** Swallow pause / disconnect noise so compose-stop does not flood the TTY. */
|
|
542
|
+
const ignoreBenignSql = (err: unknown): void => {
|
|
543
|
+
if (isBenignSqlDisconnect(err)) return;
|
|
544
|
+
// Unexpected scheduler failures stay visible.
|
|
545
|
+
console.error(err);
|
|
546
|
+
};
|
|
532
547
|
schedulerTimer = setInterval(() => {
|
|
533
|
-
if (clockRt) void clockRt.tick();
|
|
534
|
-
if (journal && durableResume) void durableResume();
|
|
535
|
-
if (fleet) void fleet.maybeHeartbeat();
|
|
548
|
+
if (clockRt) void Promise.resolve(clockRt.tick()).catch(ignoreBenignSql);
|
|
549
|
+
if (journal && durableResume) void Promise.resolve(durableResume()).catch(ignoreBenignSql);
|
|
550
|
+
if (fleet) void fleet.maybeHeartbeat().catch(ignoreBenignSql);
|
|
536
551
|
}, period);
|
|
537
552
|
schedulerTimer.unref?.();
|
|
538
553
|
}
|
|
@@ -635,6 +650,13 @@ export function resetNoEffectsWarnForTests(): void {
|
|
|
635
650
|
noEffectsWarned = false;
|
|
636
651
|
}
|
|
637
652
|
|
|
653
|
+
/** Result of a best-effort Manifest extract (never throws). */
|
|
654
|
+
type AutoExtractResult = {
|
|
655
|
+
readonly manifest?: Manifest;
|
|
656
|
+
/** Present when extract threw — surfaced before OKE1020 in strict boots. */
|
|
657
|
+
readonly error?: string;
|
|
658
|
+
};
|
|
659
|
+
|
|
638
660
|
/**
|
|
639
661
|
* Best-effort AoT extraction from `rootDir` — mirrors the CLI's own lazy,
|
|
640
662
|
* defensive `extractManifest` use (`oke dev`'s `tryLoadProjectManifest`).
|
|
@@ -647,13 +669,14 @@ export function resetNoEffectsWarnForTests(): void {
|
|
|
647
669
|
*
|
|
648
670
|
* @param rootDir - Project root to extract from
|
|
649
671
|
*/
|
|
650
|
-
async function tryAutoExtractManifest(rootDir: string): Promise<
|
|
672
|
+
async function tryAutoExtractManifest(rootDir: string): Promise<AutoExtractResult> {
|
|
651
673
|
try {
|
|
652
674
|
const url = new URL("../compiler/extract.ts", import.meta.url);
|
|
653
675
|
const { extractManifest } = (await import(url.href)) as typeof import("../compiler/extract.ts");
|
|
654
|
-
return await extractManifest({ rootDir });
|
|
655
|
-
} catch {
|
|
656
|
-
|
|
676
|
+
return { manifest: await extractManifest({ rootDir }) };
|
|
677
|
+
} catch (err) {
|
|
678
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
679
|
+
return { error: message.length > 0 ? message : "unknown extract failure" };
|
|
657
680
|
}
|
|
658
681
|
}
|
|
659
682
|
|
|
@@ -663,9 +686,14 @@ async function tryAutoExtractManifest(rootDir: string): Promise<Manifest | undef
|
|
|
663
686
|
* an explicit {@link BootOptions.manifest}, or a lazy AoT extract from
|
|
664
687
|
* {@link BootOptions.rootDir} / `OKE_ROOT_DIR`.
|
|
665
688
|
*
|
|
666
|
-
* When
|
|
667
|
-
*
|
|
668
|
-
*
|
|
689
|
+
* When a Manifest is available but a Flow is absent from it (framework /
|
|
690
|
+
* plugin Flows outside the app tree), stamp an empty least-privilege token
|
|
691
|
+
* automatically — do not hand-declare `effects: {}` on those Flows.
|
|
692
|
+
*
|
|
693
|
+
* When neither Manifest nor declared effects are available: `test` stays
|
|
694
|
+
* open with a once-per-process `oke boot:` warning; `dev` with compose
|
|
695
|
+
* infra and `prod` fail loud (`OKE1020`) — never a silent open door in a
|
|
696
|
+
* deploy-shaped environment.
|
|
669
697
|
*
|
|
670
698
|
* @param flows - Adopted flows
|
|
671
699
|
* @param env - Resolved {@link ConfigEnv}
|
|
@@ -683,10 +711,15 @@ export async function mintCapabilities(
|
|
|
683
711
|
const map = new Map<string, CapabilityToken>();
|
|
684
712
|
|
|
685
713
|
let manifest = options.manifest;
|
|
714
|
+
let extractError: string | undefined;
|
|
686
715
|
const needsManifest = manifest === undefined && flows.some((f) => f.effects === undefined);
|
|
687
716
|
if (needsManifest) {
|
|
688
717
|
const rootDir = options.rootDir ?? process.env["OKE_ROOT_DIR"];
|
|
689
|
-
if (rootDir)
|
|
718
|
+
if (rootDir) {
|
|
719
|
+
const extracted = await tryAutoExtractManifest(rootDir);
|
|
720
|
+
manifest = extracted.manifest;
|
|
721
|
+
extractError = extracted.error;
|
|
722
|
+
}
|
|
690
723
|
}
|
|
691
724
|
|
|
692
725
|
for (const f of flows) {
|
|
@@ -701,17 +734,34 @@ export async function mintCapabilities(
|
|
|
701
734
|
continue;
|
|
702
735
|
}
|
|
703
736
|
|
|
737
|
+
// Manifest extract succeeded (or an explicit Manifest was passed) but this
|
|
738
|
+
// flow is absent from it — typically a framework / plugin Flow that lives
|
|
739
|
+
// outside the app tree. Stamp an empty least-privilege token; do not hand
|
|
740
|
+
// declare `effects: {}` on those Flows. Real fx usage still needs inference
|
|
741
|
+
// (app source) or an explicit non-empty `effects` block.
|
|
742
|
+
if (manifest !== undefined) {
|
|
743
|
+
map.set(f.name, createCapabilityToken(f.name, {}));
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
|
|
704
747
|
const strict = env === "prod" || (env === "dev" && options.docker === true);
|
|
705
748
|
if (strict) {
|
|
749
|
+
if (extractError !== undefined) {
|
|
750
|
+
emitBootWarn(`oke boot: Manifest extract failed — ${extractError}`);
|
|
751
|
+
}
|
|
706
752
|
throwOke("NO_EFFECTS_DECLARED", { flow: f.name });
|
|
707
753
|
}
|
|
708
754
|
if (!noEffectsWarned) {
|
|
709
755
|
noEffectsWarned = true;
|
|
756
|
+
const extractHint =
|
|
757
|
+
extractError !== undefined ? ` Manifest extract failed (${extractError}).` : "";
|
|
710
758
|
emitBootWarn(
|
|
711
759
|
`oke boot: flow "${f.name}" (and possibly others) has no declared effects and no ` +
|
|
712
760
|
"Manifest-derived effects — running with an OPEN capability token (every access " +
|
|
713
|
-
"allowed, ledgered but not gated).
|
|
714
|
-
|
|
761
|
+
"allowed, ledgered but not gated)." +
|
|
762
|
+
extractHint +
|
|
763
|
+
" Run `oke build`, or boot with `manifest` / `rootDir`, before deploying — " +
|
|
764
|
+
"dev+compose/prod refuse to boot this way.",
|
|
715
765
|
);
|
|
716
766
|
}
|
|
717
767
|
map.set(f.name, createCapabilityToken(f.name, undefined));
|
|
@@ -757,7 +807,7 @@ async function tryListFlowsUnits(rootDir: string): Promise<readonly string[] | u
|
|
|
757
807
|
* Opt-in only via {@link BootOptions.rootDir} / `OKE_ROOT_DIR` (same gate as
|
|
758
808
|
* {@link mintCapabilities}'s Manifest fallback) — never a filesystem read
|
|
759
809
|
* the kernel performs on its own. `test` warns once per process; `prod`
|
|
760
|
-
* (and `dev` with compose) fail loud (`
|
|
810
|
+
* (and `dev` with compose) fail loud (`OKE1030`) — same class as
|
|
761
811
|
* `NO_EFFECTS_DECLARED`, never a silently-incomplete route table in a
|
|
762
812
|
* deploy-shaped environment.
|
|
763
813
|
*
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared invoke-boundary contract — authored on HTTP / call / MCP exposures,
|
|
3
|
+
* projected onto Manifest `flows.*.{in,out,errors,breaking}`.
|
|
4
|
+
*
|
|
5
|
+
* Distinct from Signal/Channel **emit schema** (payload in motion).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { InferSchemaOutput, SchemaInput } from "../validation/standard-schema.ts";
|
|
9
|
+
import type { FlowErrorMap } from "./flow.ts";
|
|
10
|
+
|
|
11
|
+
/** Invoke contract bag shared by `http.*`, `call`, and `mcp.tool`. */
|
|
12
|
+
export interface BoundaryContract<
|
|
13
|
+
ISchema = SchemaInput,
|
|
14
|
+
OSchema = SchemaInput,
|
|
15
|
+
E extends FlowErrorMap = FlowErrorMap,
|
|
16
|
+
> {
|
|
17
|
+
/** Request / args schema (Standard Schema). */
|
|
18
|
+
readonly in?: ISchema;
|
|
19
|
+
/** Success reply schema (Manifest + typed client; not runtime-validated). */
|
|
20
|
+
readonly out?: OSchema;
|
|
21
|
+
/** Typed domain errors (`fx.fail`). */
|
|
22
|
+
readonly errors?: E;
|
|
23
|
+
/**
|
|
24
|
+
* Acknowledge intentional Manifest contract breaks for the bound flow
|
|
25
|
+
* (`oke doctor --diff` / CI gate).
|
|
26
|
+
*/
|
|
27
|
+
readonly breaking?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Infer validated input from a {@link BoundaryContract} bag. */
|
|
31
|
+
export type InferBoundaryIn<Bag> = Bag extends { readonly in: infer S }
|
|
32
|
+
? InferSchemaOutput<S>
|
|
33
|
+
: unknown;
|
|
34
|
+
|
|
35
|
+
/** Infer success output from a {@link BoundaryContract} bag. */
|
|
36
|
+
export type InferBoundaryOut<Bag> = Bag extends { readonly out: infer S }
|
|
37
|
+
? InferSchemaOutput<S>
|
|
38
|
+
: unknown;
|
|
39
|
+
|
|
40
|
+
/** Infer error map from a {@link BoundaryContract} bag. */
|
|
41
|
+
export type InferBoundaryErrors<Bag> = Bag extends {
|
|
42
|
+
readonly errors: infer E extends FlowErrorMap;
|
|
43
|
+
}
|
|
44
|
+
? E
|
|
45
|
+
: {};
|
|
46
|
+
|
|
47
|
+
/** Runtime fields stamped onto a Flow from an exposure bag. */
|
|
48
|
+
export interface StampedBoundaryContract {
|
|
49
|
+
readonly in: SchemaInput | undefined;
|
|
50
|
+
readonly out: SchemaInput | undefined;
|
|
51
|
+
readonly errors: FlowErrorMap | undefined;
|
|
52
|
+
readonly breaking: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Normalize an optional contract bag into stampable fields.
|
|
57
|
+
*
|
|
58
|
+
* @param bag - Exposure contract (may be undefined / partial)
|
|
59
|
+
*/
|
|
60
|
+
export function stampBoundaryContract(
|
|
61
|
+
bag: BoundaryContract | undefined | null,
|
|
62
|
+
): StampedBoundaryContract {
|
|
63
|
+
if (bag == null) {
|
|
64
|
+
return { in: undefined, out: undefined, errors: undefined, breaking: false };
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
in: bag.in as SchemaInput | undefined,
|
|
68
|
+
out: bag.out as SchemaInput | undefined,
|
|
69
|
+
errors: bag.errors,
|
|
70
|
+
breaking: bag.breaking ?? false,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Apply a stamped contract onto a flow carrier (runtime mutation).
|
|
76
|
+
*
|
|
77
|
+
* @param flow - Flow definition object
|
|
78
|
+
* @param stamped - Normalized contract fields
|
|
79
|
+
*/
|
|
80
|
+
export function applyBoundaryContract(
|
|
81
|
+
flow: {
|
|
82
|
+
in: SchemaInput | undefined;
|
|
83
|
+
out: SchemaInput | undefined;
|
|
84
|
+
errors: FlowErrorMap | undefined;
|
|
85
|
+
breaking: boolean;
|
|
86
|
+
},
|
|
87
|
+
stamped: StampedBoundaryContract,
|
|
88
|
+
): void {
|
|
89
|
+
flow.in = stamped.in;
|
|
90
|
+
flow.out = stamped.out;
|
|
91
|
+
flow.errors = stamped.errors;
|
|
92
|
+
if (stamped.breaking) flow.breaking = true;
|
|
93
|
+
}
|