okengine 0.5.1 → 0.6.1
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/README.md +148 -13
- package/package.json +4 -3
- package/site/content/docs/elements/ai.mdx +82 -1
- package/site/content/docs/elements/channel.mdx +77 -8
- package/site/content/docs/elements/flow.mdx +20 -17
- package/site/content/docs/plugins/email-otp.mdx +25 -19
- package/site/content/docs/plugins/magic-link.mdx +27 -21
- package/site/content/docs/reference/configuration.mdx +12 -4
- package/site/content/docs/reference/environment-variables.mdx +42 -13
- package/site/content/docs/reference/errors.mdx +14 -0
- package/site/content/docs/reference/fx.mdx +68 -16
- package/site/content/docs/reference/i18n.mdx +313 -0
- package/site/content/docs/reference/index.mdx +6 -1
- package/site/content/docs/reference/meta.json +1 -0
- package/site/content/docs/reference/plugins.mdx +1 -0
- package/src/auth/auth.test.ts +3 -0
- package/src/auth/bindings.ts +1 -1
- package/src/auth/method-context.ts +12 -2
- package/src/cli/openbao-restart.integration.test.ts +106 -97
- package/src/compiler/aot.test.ts +16 -13
- package/src/compiler/effects-infer.ts +46 -0
- package/src/console/server/ai.test.ts +34 -5
- package/src/docker/compose.ts +9 -0
- package/src/docker/docker.test.ts +39 -0
- package/src/docker/dockerfile.integration.test.ts +126 -119
- package/src/docker/index.ts +11 -1
- package/src/docker/recipes/index.ts +3 -1
- package/src/docker/recipes/ollama.ts +43 -0
- package/src/docker/stack-id.ts +2 -0
- package/src/docker/stack.integration.test.ts +118 -102
- package/src/drivers/ai-mock.ts +60 -0
- package/src/drivers/ai-ollama-tools.integration.test.ts +109 -0
- package/src/drivers/ai-ollama.integration.test.ts +181 -0
- package/src/drivers/ai-ollama.ts +327 -0
- package/src/drivers/ai-openai-compatible.ts +211 -21
- package/src/drivers/ai-providers.test.ts +179 -2
- package/src/drivers/ai-stream.test.ts +195 -0
- package/src/drivers/ai-types.ts +42 -1
- package/src/drivers/channel-fcm.ts +49 -53
- package/src/drivers/channel-msegat.ts +61 -0
- package/src/drivers/channel-sently-map.ts +57 -0
- package/src/drivers/channel-sently.test.ts +99 -0
- package/src/drivers/channel-smtp.ts +8 -2
- package/src/drivers/channel-sndr.ts +28 -0
- package/src/drivers/channel-taqnyat.ts +57 -0
- package/src/drivers/channel-types.ts +79 -2
- package/src/drivers/channel-unifonic.ts +26 -43
- package/src/drivers/channel-wa-cloud.ts +33 -47
- package/src/drivers/channel-webpush.ts +39 -239
- package/src/drivers/index.ts +25 -1
- package/src/drivers/ollama.ts +14 -0
- package/src/elements/ai/rate.test.ts +53 -0
- package/src/elements/ai/rate.ts +66 -0
- package/src/elements/ai/redacted-prompt.test.ts +90 -0
- package/src/elements/ai/runtime.ts +330 -100
- package/src/elements/ai/tools.test.ts +99 -0
- package/src/elements/ai.test.ts +26 -2
- package/src/elements/ai.ts +10 -1
- package/src/elements/channel/costs.test.ts +2 -2
- package/src/elements/channel/costs.ts +14 -2
- package/src/elements/channel/mime.ts +11 -0
- package/src/elements/channel/runtime.ts +94 -0
- package/src/elements/channel/sndr-webhooks.test.ts +26 -0
- package/src/elements/channel.ts +10 -1
- package/src/elements/index.ts +9 -0
- package/src/i18n/catalogs/ar.ts +67 -0
- package/src/i18n/catalogs/en.ts +68 -0
- package/src/i18n/failure-message.test.ts +56 -0
- package/src/i18n/failure-message.ts +93 -0
- package/src/i18n/format.ts +67 -0
- package/src/i18n/index.ts +57 -0
- package/src/i18n/locale-context.ts +48 -0
- package/src/i18n/messages.test.ts +173 -0
- package/src/i18n/messages.ts +169 -0
- package/src/i18n/types.ts +90 -0
- package/src/index.ts +26 -0
- package/src/kernel/app.ts +92 -2
- package/src/kernel/boot-bind/ai.test.ts +60 -0
- package/src/kernel/boot-bind/ai.ts +125 -2
- package/src/kernel/boot-bind/channel.test.ts +68 -3
- package/src/kernel/boot-bind/channel.ts +93 -2
- package/src/kernel/boot.test.ts +4 -3
- package/src/kernel/boot.ts +1 -1
- package/src/kernel/errors.ts +56 -5
- package/src/kernel/fx.test.ts +27 -0
- package/src/kernel/fx.ts +74 -18
- package/src/kernel/pipeline.test.ts +4 -0
- package/src/kernel/pipeline.ts +1 -1
- package/src/kernel/plugin.ts +16 -0
- package/src/kernel/registry.ts +15 -0
- package/src/plugins/auth/shared.ts +5 -1
- package/src/plugins/auth-delivery.mailpit.integration.test.ts +336 -0
- package/src/plugins/auth-methods.security.test.ts +12 -10
- package/src/plugins/email-otp.ts +54 -1
- package/src/plugins/index.ts +16 -2
- package/src/plugins/magic-link.ts +63 -3
- package/src/plugins/username-policy.test.ts +302 -0
- package/src/plugins/username.ts +290 -9
- package/src/release/exports.test.ts +26 -0
- package/src/release/exports.ts +64 -5
- package/src/release/index.ts +5 -0
- package/src/release/measure.exports.test.ts +13 -1
- package/src/release/measure.ts +84 -14
- package/src/release/official-plugins.ts +46 -0
- package/src/release/readme.test.ts +30 -2
|
@@ -5,12 +5,13 @@ icon: "Link2"
|
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
`magicLink()` issues a hashed, single-use token (default 10 minutes).
|
|
9
|
-
hybrid session and creates the user
|
|
8
|
+
`magicLink()` issues a hashed, single-use token (default 10 minutes). Request sends the link
|
|
9
|
+
via Channel (`auth-magic-link`); verify exchanges it for a hybrid session and creates the user
|
|
10
|
+
on first success.
|
|
10
11
|
|
|
11
12
|
<Callout title="The one rule">
|
|
12
13
|
Enable `gate.auth`, then `.plug(magicLink())`. Tokens are hashed at rest — never log the raw link.
|
|
13
|
-
|
|
14
|
+
Delivery goes through `fx.send`; use `exposeDevToken` only for local DX without SMTP.
|
|
14
15
|
</Callout>
|
|
15
16
|
|
|
16
17
|
## Quick start
|
|
@@ -28,7 +29,7 @@ export const app = oke({
|
|
|
28
29
|
name: "shop",
|
|
29
30
|
env: "local",
|
|
30
31
|
gate: { auth: {} },
|
|
31
|
-
}).plug(magicLink({
|
|
32
|
+
}).plug(magicLink({ baseUrl: "http://127.0.0.1:6530" }));
|
|
32
33
|
```
|
|
33
34
|
|
|
34
35
|
</Step>
|
|
@@ -38,10 +39,12 @@ export const app = oke({
|
|
|
38
39
|
|
|
39
40
|
```typescript
|
|
40
41
|
const { data } = await api.auth.requestMagicLink({ email: "ali@example.com" });
|
|
41
|
-
// data.ok === true;
|
|
42
|
+
// data.ok === true; Channel delivers auth-magic-link
|
|
43
|
+
// data.devToken only when exposeDevToken
|
|
42
44
|
```
|
|
43
45
|
|
|
44
|
-
`POST /auth/magic-link/request`.
|
|
46
|
+
`POST /auth/magic-link/request`. Locally the `console` driver captures mail; with
|
|
47
|
+
`oke dev --docker`, Mailpit receives the real SMTP message.
|
|
45
48
|
|
|
46
49
|
</Step>
|
|
47
50
|
|
|
@@ -49,7 +52,7 @@ const { data } = await api.auth.requestMagicLink({ email: "ali@example.com" });
|
|
|
49
52
|
### Verify
|
|
50
53
|
|
|
51
54
|
```typescript
|
|
52
|
-
const { data } = await api.auth.verifyMagicLink({ token
|
|
55
|
+
const { data } = await api.auth.verifyMagicLink({ token });
|
|
53
56
|
// session tokens + userId
|
|
54
57
|
```
|
|
55
58
|
|
|
@@ -62,14 +65,16 @@ const { data } = await api.auth.verifyMagicLink({ token: data!.devToken! });
|
|
|
62
65
|
|
|
63
66
|
## Options
|
|
64
67
|
|
|
65
|
-
| Option | Type | Default
|
|
66
|
-
| ---------------- | ------------------- |
|
|
67
|
-
| `secret` | `string` | active\*
|
|
68
|
-
| `sessions` | `SessionStore` | active\*
|
|
69
|
-
| `ttlMs` | `number` | 10m
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
68
|
+
| Option | Type | Default | Meaning |
|
|
69
|
+
| ---------------- | ------------------- | -------------------------- | ----------------------------------------- |
|
|
70
|
+
| `secret` | `string` | active\* | HMAC secret (\*from `gate.auth`) |
|
|
71
|
+
| `sessions` | `SessionStore` | active\* | Session store |
|
|
72
|
+
| `ttlMs` | `number` | 10m | Challenge lifetime |
|
|
73
|
+
| `baseUrl` | `string` | `OKE_APP_URL` or `:6530` | Origin used to build the magic link |
|
|
74
|
+
| `from` | `string` | `OKE <no-reply@oke.local>` | Template From address |
|
|
75
|
+
| `exposeDevToken` | `boolean` | `false` | Include raw token in the request response |
|
|
76
|
+
| `identities` | `IdentityStore` | new | Email → user map |
|
|
77
|
+
| `verifications` | `VerificationStore` | new | Challenge store |
|
|
73
78
|
|
|
74
79
|
## Surfaces
|
|
75
80
|
|
|
@@ -78,8 +83,8 @@ const { data } = await api.auth.verifyMagicLink({ token: data!.devToken! });
|
|
|
78
83
|
| `auth.requestMagicLink` | `POST /auth/magic-link/request` | `gate.public` + otp rate |
|
|
79
84
|
| `auth.verifyMagicLink` | `POST /auth/magic-link/verify` | `gate.public` + otp rate |
|
|
80
85
|
|
|
81
|
-
**Consequence:**
|
|
82
|
-
|
|
86
|
+
**Consequence:** the plugin contributes the `auth-magic-link` Channel template and EN/AR
|
|
87
|
+
catalog bodies (`{{link}}`, `{{token}}`). Override copy by merging your own catalog at boot.
|
|
83
88
|
|
|
84
89
|
## Troubleshooting
|
|
85
90
|
|
|
@@ -91,8 +96,9 @@ Token expired (default 10m), already used, or mistyped. Request a new link.
|
|
|
91
96
|
</Accordion>
|
|
92
97
|
<Accordion title="No email arrived">
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
In `local` / `test` the `console` driver captures mail into the inbox — nothing hits a mailbox.
|
|
100
|
+
Run `oke dev --docker` and open Mailpit (`MAILPIT_UI_URL`) to see the rendered message. For
|
|
101
|
+
unit tests without SMTP, set `exposeDevToken: true`.
|
|
96
102
|
|
|
97
103
|
</Accordion>
|
|
98
104
|
</Accordions>
|
|
@@ -101,12 +107,12 @@ link via a Channel Flow you own.
|
|
|
101
107
|
|
|
102
108
|
- [Email OTP](/docs/plugins/email-otp) — numeric code instead of a link
|
|
103
109
|
- [Gate](/docs/elements/gate) — `gate.auth`
|
|
104
|
-
- [Channel](/docs/elements/channel) —
|
|
110
|
+
- [Channel](/docs/elements/channel) — `fx.send`, Mailpit, consents
|
|
105
111
|
|
|
106
112
|
## Next
|
|
107
113
|
|
|
108
114
|
<Cards>
|
|
109
115
|
<Card title="Email OTP" description="Six-digit email codes." href="/docs/plugins/email-otp" />
|
|
110
116
|
<Card title="Gate" description="Builtin auth and policies." href="/docs/elements/gate" />
|
|
111
|
-
<Card title="
|
|
117
|
+
<Card title="Channel" description="Email delivery and Mailpit." href="/docs/elements/channel" />
|
|
112
118
|
</Cards>
|
|
@@ -30,6 +30,7 @@ drivers: {
|
|
|
30
30
|
vault: { local: "dotenv", docker: "openbao", test: "memory", prod: "openbao" },
|
|
31
31
|
channel: {
|
|
32
32
|
email: { local: "console", docker: "smtp", test: "console", prod: "smtp" },
|
|
33
|
+
sms: { local: "console", docker: "taqnyat", test: "console", prod: "taqnyat" },
|
|
33
34
|
},
|
|
34
35
|
},
|
|
35
36
|
```
|
|
@@ -43,10 +44,10 @@ drivers: {
|
|
|
43
44
|
| `signal` | env driver map | `memory` · `postgres` · `redis` · `nats` |
|
|
44
45
|
| `clock` | env driver map | `memory` · `postgres` · `frozen` |
|
|
45
46
|
| `vault` | env driver map | `dotenv` · `openbao` · `memory` |
|
|
46
|
-
| `channel.email` | env driver map | `console` · `smtp` · `resend`
|
|
47
|
-
| `channel.sms` | env driver map | `console` · `unifonic`
|
|
48
|
-
| `channel.whatsapp` | env driver map |
|
|
49
|
-
| `channel.push` | env driver map |
|
|
47
|
+
| `channel.email` | env driver map | `console` · `smtp` · `resend` · `sndr` |
|
|
48
|
+
| `channel.sms` | env driver map | `console` · `taqnyat` · `msegat` · `unifonic` |
|
|
49
|
+
| `channel.whatsapp` | env driver map | `console` · `wa-cloud` |
|
|
50
|
+
| `channel.push` | env driver map | `console` · `webpush` · `fcm` |
|
|
50
51
|
| `ai` | env driver map | `mock` · `anthropic` · `openai-compatible` · `bedrock` · `vertex` · `ollama` |
|
|
51
52
|
| `runs` | env driver map | runs-store driver (distinct from top-level `runs`) |
|
|
52
53
|
| `prod` | `string[]` | flat protocol list for the Manifest — nested maps are preferred |
|
|
@@ -76,9 +77,12 @@ images: {
|
|
|
76
77
|
"store.files": "rustfs/rustfs:1.0.0-beta.11",
|
|
77
78
|
"channel.email": "axllent/mailpit:v1.22.3",
|
|
78
79
|
vault: "openbao/openbao:2.6.1",
|
|
80
|
+
ai: "ollama/ollama:latest",
|
|
79
81
|
},
|
|
80
82
|
```
|
|
81
83
|
|
|
84
|
+
For `ai`, the Ollama recipe pulls `OKE_AI_MODEL` (default `qwen3.5:9b` — a balanced local-dev starting point; override freely; on Apple Silicon prefer `qwen3.5:9b-mlx`) and writes `OKE_AI_URL`.
|
|
85
|
+
|
|
82
86
|
## i18n
|
|
83
87
|
|
|
84
88
|
| Option | Type | Default | Meaning |
|
|
@@ -91,6 +95,10 @@ images: {
|
|
|
91
95
|
i18n: { locales: ["en", "ar"], default: "en", dir: { ar: "rtl" } },
|
|
92
96
|
```
|
|
93
97
|
|
|
98
|
+
`fx.t` catalogs are ICU strings from `defineLocale` / `defineMessages` — see
|
|
99
|
+
[i18n](/docs/reference/i18n). Built-ins also cover failure `error.message` and
|
|
100
|
+
OKE cause/fix; Channel templates keep separate `locales` + `{{field}}` bodies.
|
|
101
|
+
|
|
94
102
|
## tenancy
|
|
95
103
|
|
|
96
104
|
Multi-tenant isolation policy.
|
|
@@ -61,22 +61,51 @@ Written by the first-boot bootstrap; you normally only set these when pointing a
|
|
|
61
61
|
| `OKE_VAULT_TOKEN` | App token (least-privilege) | — |
|
|
62
62
|
| `OKE_VAULT_MOUNT` | KV v2 mount path | `"secret"` |
|
|
63
63
|
|
|
64
|
-
## Channel (email)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
64
|
+
## Channel (email) — boot binder
|
|
65
|
+
|
|
66
|
+
Read when `drivers.channel.email` resolves to that driver id.
|
|
67
|
+
|
|
68
|
+
| Variable | Used for |
|
|
69
|
+
| ----------------------- | ------------------------------------------------ |
|
|
70
|
+
| `SMTP_URL` | `smtp` — full SMTP URL (`smtp://…`) |
|
|
71
|
+
| `SMTP_USER` | Overrides the user embedded in `SMTP_URL` |
|
|
72
|
+
| `SMTP_PASSWORD` | Overrides the password in `SMTP_URL` |
|
|
73
|
+
| `OKE_CHANNEL_EMAIL_URL` | Alternative to `SMTP_URL` |
|
|
74
|
+
| `RESEND_API_KEY` | `resend` API key |
|
|
75
|
+
| `SNDR_API_KEY` | `sndr` API key |
|
|
76
|
+
| `SNDR_BASE_URL` | Optional SNDR API origin (default `api.sndr.sh`) |
|
|
77
|
+
|
|
78
|
+
## Channel (SMS) — boot binder
|
|
79
|
+
|
|
80
|
+
Read when `drivers.channel.sms` resolves to that driver id (`console` opens nothing).
|
|
81
|
+
|
|
82
|
+
| Variable | Used for |
|
|
83
|
+
| ---------------------- | -------------------------------- |
|
|
84
|
+
| `TAQNYAT_BEARER_TOKEN` | `taqnyat` bearer token |
|
|
85
|
+
| `TAQNYAT_TOKEN` | Alias for `TAQNYAT_BEARER_TOKEN` |
|
|
86
|
+
| `TAQNYAT_SENDER` | Taqnyat pre-approved sender id |
|
|
87
|
+
| `MSEGAT_USERNAME` | `msegat` account username |
|
|
88
|
+
| `MSEGAT_API_KEY` | `msegat` API key |
|
|
89
|
+
| `MSEGAT_SENDER` | Msegat pre-approved sender id |
|
|
90
|
+
| `UNIFONIC_APPSID` | `unifonic` AppSid |
|
|
91
|
+
| `UNIFONIC_APP_SID` | Alias for `UNIFONIC_APPSID` |
|
|
92
|
+
| `UNIFONIC_SENDER` | Unifonic SenderID (optional) |
|
|
93
|
+
|
|
94
|
+
WhatsApp (`wa-cloud`) and push (`webpush` / `fcm`) are not opened from env at
|
|
95
|
+
boot — pass them on `BootOptions.channel.drivers` with their open options.
|
|
72
96
|
|
|
73
97
|
## AI providers
|
|
74
98
|
|
|
75
|
-
| Variable | Used for
|
|
76
|
-
| ------------------- |
|
|
77
|
-
| `ANTHROPIC_API_KEY` | `anthropic` driver credential
|
|
78
|
-
| `ANTHROPIC_MODEL` | Model override for the anthropic driver |
|
|
79
|
-
| `OPENAI_API_KEY` | `openai-compatible` driver credential
|
|
99
|
+
| Variable | Used for | Default when unset |
|
|
100
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
|
|
101
|
+
| `ANTHROPIC_API_KEY` | `anthropic` driver credential | — |
|
|
102
|
+
| `ANTHROPIC_MODEL` | Model override for the anthropic driver | — |
|
|
103
|
+
| `OPENAI_API_KEY` | `openai-compatible` driver credential | — |
|
|
104
|
+
| `OPENAI_BASE_URL` | `openai-compatible` base URL | OpenAI cloud |
|
|
105
|
+
| `OKE_AI_DRIVER` | Force the AI driver id (honoured in docker mode) | config map |
|
|
106
|
+
| `OKE_AI_URL` | Ollama (or openai-compatible) base URL | `http://127.0.0.1:11434` (ollama) |
|
|
107
|
+
| `OKE_AI_MODEL` | Default model for `ollama` (any pulled model; `qwen3.5:9b` is a balanced local-dev starting point; on Apple Silicon try `qwen3.5:9b-mlx`) | `qwen3.5:9b` |
|
|
108
|
+
| `OLLAMA_HOST` | Alternate Ollama host (`host:port` or URL) when `OKE_AI_URL` is unset | — |
|
|
80
109
|
|
|
81
110
|
## Framework behavior
|
|
82
111
|
|
|
@@ -14,6 +14,19 @@ OKE1101 domain table not found — migrations have not been applied.
|
|
|
14
14
|
|
|
15
15
|
Codes are permanent once published — you can match on them safely across upgrades.
|
|
16
16
|
|
|
17
|
+
## Localized messages
|
|
18
|
+
|
|
19
|
+
Typed failures and OKE codes ship English and Arabic ICU catalogs. Locale comes
|
|
20
|
+
from `Accept-Language` (matched to `i18n.locales`); fallback is `i18n.default`.
|
|
21
|
+
|
|
22
|
+
| Surface | Keys | How it appears |
|
|
23
|
+
| ------------------ | ------------------------------------------ | ---------------------------------------- |
|
|
24
|
+
| `fx.fail` / `fail` | `errors.{code}` · `errors.{code}.{reason}` | Optional `error.message` on the envelope |
|
|
25
|
+
| Thrown `OkeError` | `oke.{code}.cause` · `oke.{code}.fix` | Cause + fix lines in the thrown message |
|
|
26
|
+
|
|
27
|
+
Override via `defineLocale`. Pass `fail(code, data, { message })` for a custom
|
|
28
|
+
string. Custom app codes stay message-less until registered.
|
|
29
|
+
|
|
17
30
|
## OKE numeric codes
|
|
18
31
|
|
|
19
32
|
| Code | Name | Cause | Fix |
|
|
@@ -75,6 +88,7 @@ Thrown by specific subsystems — each names its own cause:
|
|
|
75
88
|
|
|
76
89
|
## Learn more
|
|
77
90
|
|
|
91
|
+
- [i18n](/docs/reference/i18n) — catalogs, `fx.t`, locale matching
|
|
78
92
|
- [Flow](/docs/elements/flow) — `fx.fail` and the response envelope
|
|
79
93
|
- [Gate](/docs/elements/gate) — where the three denials come from
|
|
80
94
|
- [CLI Reference](/docs/reference/cli) — `oke db migrate` and friends
|
|
@@ -102,20 +102,23 @@ const rows = await fx.using(
|
|
|
102
102
|
|
|
103
103
|
## Channel
|
|
104
104
|
|
|
105
|
-
| Signature
|
|
106
|
-
|
|
|
107
|
-
| `fx.send(template, { to?, data?, via
|
|
105
|
+
| Signature | Records | Notes |
|
|
106
|
+
| -------------------------------------------- | ------- | ------------------------------------------------------------------------------------------ |
|
|
107
|
+
| `fx.send(template, { to?, data?, via?, … })` | `send` | `via` orders fallback; `locale` / `profileLocale` / `acceptLanguage` feed the locale chain |
|
|
108
|
+
|
|
109
|
+
Omit locale opts and the send uses `fx.locale`. Dry runs record _would have fired_ and never
|
|
110
|
+
contact a provider. Channel bodies use `{{field}}` catalogs — not ICU (see [Channel](/docs/elements/channel)).
|
|
108
111
|
|
|
109
112
|
## AI
|
|
110
113
|
|
|
111
|
-
| Signature
|
|
112
|
-
|
|
|
113
|
-
| `fx.ask(prompt, input?, { via? })`
|
|
114
|
-
| `fx.run(agent, input?)`
|
|
115
|
-
| `fx.stream(model, { prompt?, data? })`
|
|
116
|
-
| `fx.search(embed, query, { topK? })`
|
|
114
|
+
| Signature | Records | Returns |
|
|
115
|
+
| ----------------------------------------------------- | ------------------------- | ----------------------------------------------------------------------------- |
|
|
116
|
+
| `fx.ask(prompt, input?, { via?, tools?, maxSteps? })` | `ask` (+ `call` per tool) | Object validated against the prompt's `out` |
|
|
117
|
+
| `fx.run(agent, input?)` | `ask` | Agent result |
|
|
118
|
+
| `fx.stream(model, { prompt?, data? })` | `ask` | `AsyncIterable<string>` — real driver stream; cancels via ambient `fx.signal` |
|
|
119
|
+
| `fx.search(embed, query, { topK? })` | `read` | Matches from the index/embed |
|
|
117
120
|
|
|
118
|
-
AI calls are nondeterministic: journaling is forced on and auto-cache disabled around them.
|
|
121
|
+
AI calls are nondeterministic: journaling is forced on and auto-cache disabled around them. `tools` are Flow refs — each model tool call goes through `fx.call` (same capability and Runs path).
|
|
119
122
|
|
|
120
123
|
## Vault
|
|
121
124
|
|
|
@@ -153,11 +156,59 @@ Returning a plain value instead answers 200 with `{ data: value, error: null }`
|
|
|
153
156
|
|
|
154
157
|
## Logging, i18n, ids
|
|
155
158
|
|
|
156
|
-
| Signature | Notes
|
|
157
|
-
| ------------------------------------------ |
|
|
158
|
-
| `fx.log.debug/info/warn/error(msg, data?)` | Redacting — secrets print as `***`
|
|
159
|
-
| `fx.t(key,
|
|
160
|
-
| `fx.
|
|
159
|
+
| Signature | Notes |
|
|
160
|
+
| ------------------------------------------ | ----------------------------------------------------------- |
|
|
161
|
+
| `fx.log.debug/info/warn/error(msg, data?)` | Redacting — secrets print as `***` |
|
|
162
|
+
| `fx.t(key, values?)` | ICU MessageFormat — active locale → `i18n.default` → key |
|
|
163
|
+
| `fx.locale` | Active locale (`Accept-Language` matched to `i18n.locales`) |
|
|
164
|
+
| `fx.id()` | UUID — the only legal id generator |
|
|
165
|
+
|
|
166
|
+
`fx.t` uses [ICU MessageFormat](https://unicode-org.github.io/icu/userguide/format_parse/messages/)
|
|
167
|
+
(FormatJS): `{name}`, plurals, `select` / `selectordinal`, rich-text tags.
|
|
168
|
+
Full syntax and escaping: [i18n · ICU](/docs/reference/i18n#icu-messageformat).
|
|
169
|
+
|
|
170
|
+
Register catalogs with `defineLocale` before boot (nested trees → dot keys). Augment
|
|
171
|
+
`Register` so keys autocomplete and typos fail at compile time:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { defineLocale, defineMessages, type MessagesFor } from "okengine";
|
|
175
|
+
|
|
176
|
+
export const en = defineMessages({
|
|
177
|
+
greeting: "Hello, {name}",
|
|
178
|
+
items: "{count, plural, one {# item} other {# items}}",
|
|
179
|
+
place: "You finished {place, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}!",
|
|
180
|
+
status: "{status, select, online {Online} offline {Offline} other {Unknown}}",
|
|
181
|
+
cta: "Read <docs>the docs</docs>",
|
|
182
|
+
});
|
|
183
|
+
defineLocale("en", en);
|
|
184
|
+
|
|
185
|
+
defineLocale("ar", {
|
|
186
|
+
greeting: "مرحباً، {name}",
|
|
187
|
+
items: "{count, plural, zero {لا عناصر} one {عنصر واحد} other {# عناصر}}",
|
|
188
|
+
place: "أنهيت في المرتبة {place, selectordinal, other {#}}!",
|
|
189
|
+
status: "{status, select, online {متصل} offline {غير متصل} other {غير معروف}}",
|
|
190
|
+
cta: "اقرأ <docs>التوثيق</docs>",
|
|
191
|
+
} satisfies MessagesFor<typeof en>);
|
|
192
|
+
|
|
193
|
+
declare module "okengine" {
|
|
194
|
+
interface Register {
|
|
195
|
+
messages: typeof en;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
fx.t("greeting", { name: input.name });
|
|
202
|
+
fx.t("items", { count: 3 });
|
|
203
|
+
fx.t("cta", { docs: (chunks) => `<a href="/docs">${chunks.join("")}</a>` });
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Supported locales and the fallback come from `oke.config.ts` `i18n`.
|
|
207
|
+
`create-oke` scaffolds English and Arabic locale modules already wired this way.
|
|
208
|
+
|
|
209
|
+
Built-in `errors.*` / `oke.*` catalogs localize `fx.fail` messages and thrown
|
|
210
|
+
`OkeError` cause/fix the same way — full i18n guide:
|
|
211
|
+
[i18n](/docs/reference/i18n), [Errors](/docs/reference/errors).
|
|
161
212
|
|
|
162
213
|
## Principals
|
|
163
214
|
|
|
@@ -181,5 +232,6 @@ must log who started the call chain.
|
|
|
181
232
|
## Learn more
|
|
182
233
|
|
|
183
234
|
- [Flow](/docs/elements/flow) — why `fx` is the only door
|
|
235
|
+
- [Channel](/docs/elements/channel) — `fx.send`, consent, locale chain, `{{field}}` catalogs
|
|
184
236
|
- [Errors](/docs/reference/errors) — what `fx.fail` produces
|
|
185
|
-
- [Configuration](/docs/reference/configuration) —
|
|
237
|
+
- [Configuration](/docs/reference/configuration) — drivers and the `i18n` block
|