okengine 0.6.0 → 0.7.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 +2 -2
- package/README.md +98 -71
- package/package.json +4 -4
- package/site/content/docs/console/clock.mdx +25 -5
- package/site/content/docs/console/signals.mdx +13 -4
- package/site/content/docs/elements/ai.mdx +29 -2
- package/site/content/docs/elements/channel.mdx +90 -20
- package/site/content/docs/elements/clock.mdx +56 -24
- package/site/content/docs/elements/flow.mdx +2 -0
- package/site/content/docs/elements/gate.mdx +8 -1
- package/site/content/docs/elements/signal.mdx +173 -29
- package/site/content/docs/elements/store.mdx +464 -73
- package/site/content/docs/elements/vault.mdx +15 -5
- package/site/content/docs/get-started/basic-usage.mdx +13 -6
- package/site/content/docs/get-started/installation.mdx +49 -13
- package/site/content/docs/get-started/introduction.mdx +1 -1
- package/site/content/docs/reference/cli.md +30 -2
- package/site/content/docs/reference/configuration.mdx +18 -17
- package/site/content/docs/reference/environment-variables.mdx +44 -18
- package/site/content/docs/reference/errors.mdx +12 -11
- package/site/content/docs/reference/fx.mdx +15 -13
- package/site/content/docs/reference/security.md +3 -1
- package/src/cli/ai-setup/ai-setup.test.ts +144 -0
- package/src/cli/ai-setup/apply.ts +214 -0
- package/src/cli/ai-setup/catalog.ts +263 -0
- package/src/cli/ai-setup/detect-ollama.ts +166 -0
- package/src/cli/ai-setup/index.ts +228 -0
- package/src/cli/ai-setup/prompts.ts +649 -0
- package/src/cli/ai-setup/recommend.test.ts +100 -0
- package/src/cli/ai-setup/recommend.ts +203 -0
- package/src/cli/ai.ts +38 -0
- package/src/cli/db-auto-push.test.ts +6 -1
- package/src/cli/db-auto-push.ts +8 -3
- package/src/cli/dev-db-push.test.ts +59 -0
- package/src/cli/dev.test.ts +40 -0
- package/src/cli/dev.ts +55 -1
- package/src/cli/docker-clean.test.ts +179 -0
- package/src/cli/docker-clean.ts +406 -0
- package/src/cli/docker.ts +16 -4
- package/src/cli/ensure-drizzle-config.ts +1 -1
- package/src/cli/hero-meta.test.ts +6 -5
- package/src/cli/hero-meta.ts +7 -1
- package/src/cli/index.ts +5 -0
- package/src/cli/load-config.ts +4 -4
- package/src/cli/openbao-bootstrap.test.ts +1 -0
- package/src/cli/openbao-bootstrap.ts +9 -2
- package/src/cli/openbao-restart.integration.test.ts +106 -97
- package/src/cli/registry.ts +79 -1
- package/src/client/live-gap.test.ts +35 -0
- package/src/client/transport.test.ts +21 -0
- package/src/client/transport.ts +27 -5
- package/src/compiler/fixtures/skyport/oke.config.ts +1 -1
- package/src/config/define-config.test.ts +6 -6
- package/src/config/index.ts +1 -1
- package/src/console/server/console.test.ts +45 -0
- package/src/console/server/flows.ts +39 -9
- package/src/console/server/i18n.ts +41 -0
- package/src/console/server/vault.ts +12 -38
- package/src/console/ui/dist/assets/index-ClX0ZCe5.js +10 -0
- package/src/console/ui/dist/assets/{panel-access-BGv45snf.js → panel-access-ri1X9Otj.js} +1 -1
- package/src/console/ui/dist/assets/{panel-ai-B2S7LEii.js → panel-ai-DLHjRoJk.js} +1 -1
- package/src/console/ui/dist/assets/{panel-architecture-D7UJh91v.js → panel-architecture-DI3bQF3J.js} +1 -1
- package/src/console/ui/dist/assets/{panel-channels-9T3ybqRu.js → panel-channels-BDYWX29p.js} +1 -1
- package/src/console/ui/dist/assets/{panel-clock-Cb1UXGRQ.js → panel-clock-i-JpTjVB.js} +1 -1
- package/src/console/ui/dist/assets/{panel-diff-DmYbKWmN.js → panel-diff-CFuWxiXy.js} +1 -1
- package/src/console/ui/dist/assets/{panel-flows-PiHwT55z.js → panel-flows-B8TRhcoe.js} +1 -1
- package/src/console/ui/dist/assets/{panel-gates-BQGYXvjT.js → panel-gates-CfkBgco7.js} +1 -1
- package/src/console/ui/dist/assets/{panel-overview-BBnRO18l.js → panel-overview-DONDzBd2.js} +1 -1
- package/src/console/ui/dist/assets/{panel-plugins-D0PsmVw2.js → panel-plugins-DDJwTehL.js} +1 -1
- package/src/console/ui/dist/assets/{panel-runs-CWuRDe0r.js → panel-runs-D5zf-D9c.js} +1 -1
- package/src/console/ui/dist/assets/{panel-signals-Bbg4ewpP.js → panel-signals-Dsxdu_AR.js} +1 -1
- package/src/console/ui/dist/assets/{panel-store-CPCbsDRa.js → panel-store-hy7O8HOs.js} +1 -1
- package/src/console/ui/dist/assets/{panel-traces-DVAzuA_S.js → panel-traces-CBiAe5go.js} +1 -1
- package/src/console/ui/dist/assets/{panel-vault-D1_MvOmo.js → panel-vault-Za1GyJfM.js} +1 -1
- package/src/console/ui/dist/index.html +1 -1
- package/src/console/ui/shell/components/ui.tsx +5 -1
- package/src/console/ui/shell/setup/Wizard.tsx +26 -5
- package/src/docker/cleanup.test.ts +193 -0
- package/src/docker/cleanup.ts +355 -0
- package/src/docker/docker.test.ts +4 -0
- package/src/docker/dockerfile.integration.test.ts +126 -119
- package/src/docker/index.ts +19 -0
- package/src/docker/recipes/ollama.ts +10 -5
- package/src/docker/stack.integration.test.ts +118 -102
- package/src/drivers/ai-ollama-tools.integration.test.ts +8 -6
- package/src/drivers/ai-ollama.integration.test.ts +3 -19
- package/src/drivers/ai-ollama.ts +45 -2
- package/src/drivers/ai-providers.test.ts +31 -0
- 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-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 +7 -0
- package/src/drivers/signal-engine.ts +98 -25
- package/src/drivers/signal-nats.ts +4 -4
- package/src/drivers/signal-postgres.ts +158 -64
- package/src/drivers/signal-redis.ts +4 -4
- package/src/drivers/signal-types.ts +61 -2
- package/src/drivers/vault-driver-removal.test.ts +5 -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/clock/chaos-child.ts +150 -0
- package/src/elements/clock/chaos.test.ts +204 -0
- package/src/elements/clock/reconcile.ts +110 -0
- package/src/elements/clock/runtime.ts +11 -0
- package/src/elements/clock.test.ts +164 -2
- package/src/elements/clock.ts +1 -0
- package/src/elements/gate/runtime.ts +6 -0
- package/src/elements/index.ts +9 -0
- package/src/elements/signal/chaos-child.ts +41 -4
- package/src/elements/signal/declare.ts +4 -1
- package/src/elements/signal/delivery-modes.test.ts +179 -0
- package/src/elements/signal/key-ordering.test.ts +306 -0
- package/src/elements/signal/lease-reclaim.test.ts +123 -0
- package/src/elements/signal/optional-emit.test.ts +76 -0
- package/src/elements/signal/order-lifecycle.test.ts +130 -0
- package/src/elements/signal/orphan-messages.test.ts +152 -0
- package/src/elements/signal/runtime.ts +11 -4
- package/src/elements/signal/schema-emit.test.ts +107 -0
- package/src/elements/signal.test.ts +89 -5
- package/src/elements/store/files-fx.ts +104 -0
- package/src/elements/store/files-image.test.ts +251 -0
- package/src/elements/store/files-image.ts +494 -0
- package/src/elements/store/runtime.ts +60 -0
- package/src/elements/store.ts +24 -0
- package/src/elements/vault/boot-chain.ts +150 -0
- package/src/elements/vault/runtime.ts +8 -0
- package/src/kernel/app.ts +3 -1
- package/src/kernel/boot-bind/channel.test.ts +68 -3
- package/src/kernel/boot-bind/channel.ts +93 -2
- package/src/kernel/boot-bind/clock.ts +52 -7
- package/src/kernel/boot-bind/gate.ts +71 -3
- package/src/kernel/boot-bind/honor-config.test.ts +247 -0
- package/src/kernel/boot-bind/signal.ts +64 -7
- package/src/kernel/boot-bind/vault.ts +27 -8
- package/src/kernel/boot.ts +10 -2
- package/src/kernel/errors.ts +8 -2
- package/src/kernel/fx.test.ts +13 -0
- package/src/kernel/fx.ts +27 -4
- package/src/plugins/auth-delivery.mailpit.integration.test.ts +10 -4
- 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 +76 -13
- package/src/release/official-plugins.ts +46 -0
- package/src/release/readme.test.ts +30 -2
- package/src/runtime/dev-request-log.test.ts +20 -1
- package/src/runtime/dev-request-log.ts +38 -0
- package/src/term.test.ts +15 -0
- package/src/term.ts +8 -3
- package/src/console/ui/dist/assets/index-CjxwRGVv.js +0 -10
package/AGENTS.md
CHANGED
|
@@ -78,9 +78,9 @@ A regression fails the build. Claims we cannot measure, we do not make.
|
|
|
78
78
|
Published packages:
|
|
79
79
|
|
|
80
80
|
- `okengine` — framework. Subpath exports: `.`, `./client`, `./test`, `./config`, `./auth`, `./plugins`, `./drivers/*`. `"sideEffects": false`. CLI binary: `oke`.
|
|
81
|
-
- `create-oke` — scaffold CLI (`bunx create-oke@latest <name>`). Lives in `packages/create-oke` and ships
|
|
81
|
+
- `create-oke` — scaffold CLI (`bunx create-oke@latest <name>`). Lives in `packages/create-oke` and ships Notes starters from `packages/create-oke/templates/{standard,advanced}`.
|
|
82
82
|
|
|
83
|
-
Engine: Bun `>=1.3`.
|
|
83
|
+
Engine: Bun `>=1.3.14`.
|
|
84
84
|
|
|
85
85
|
## Documentation authority
|
|
86
86
|
|
package/README.md
CHANGED
|
@@ -1,21 +1,36 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<picture>
|
|
3
|
-
<source
|
|
4
|
-
|
|
3
|
+
<source
|
|
4
|
+
media="(prefers-color-scheme: dark)"
|
|
5
|
+
srcset="https://shieldcn.dev/header/grid.svg?title=okengine&subtitle=One+law.+Eight+elements.+Ten+exports.&logo=https://raw.githubusercontent.com/omqkhafi/okengine/main/site/public/logo/OKE-W.svg&theme=zinc&size=banner&mode=dark&font=geist&align=left"
|
|
6
|
+
/>
|
|
7
|
+
<img
|
|
8
|
+
alt="okengine — One law. Eight elements. Ten exports."
|
|
9
|
+
src="https://shieldcn.dev/header/grid.svg?title=okengine&subtitle=One+law.+Eight+elements.+Ten+exports.&logo=https://raw.githubusercontent.com/omqkhafi/okengine/main/site/public/logo/OKE-B.svg&theme=zinc&size=banner&mode=light&font=geist&align=left"
|
|
10
|
+
width="750"
|
|
11
|
+
/>
|
|
5
12
|
</picture>
|
|
6
13
|
</p>
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="https://www.npmjs.com/package/okengine"><img alt="npm" src="https://shieldcn.dev/npm/okengine.svg?size=sm&variant=outline" /></a>
|
|
17
|
+
<a href="https://jsr.io/@omqkhafi/okengine"><img alt="JSR" src="https://shieldcn.dev/jsr/@omqkhafi/okengine.svg?size=sm&variant=outline" /></a>
|
|
18
|
+
<a href="https://opensource.org/licenses/MIT"><img alt="MIT" src="https://shieldcn.dev/npm/license/okengine.svg?size=sm&variant=outline" /></a>
|
|
19
|
+
<a href="https://bun.sh"><img alt="Bun >=1.3.14" src="https://shieldcn.dev/badge/Bun-%3E%3D1.3.14.svg?logo=bun&size=sm&variant=outline" /></a>
|
|
20
|
+
<a href="https://github.com/omqkhafi/okengine/actions"><img alt="CI" src="https://shieldcn.dev/github/ci/omqkhafi/okengine.svg?size=sm&variant=outline" /></a>
|
|
21
|
+
<a href="https://github.com/omqkhafi/okengine/commits"><img alt="last commit" src="https://shieldcn.dev/github/last-commit/omqkhafi/okengine.svg?size=sm&variant=outline" /></a>
|
|
22
|
+
</p>
|
|
11
23
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
[](https://opensource.org/licenses/MIT)
|
|
16
|
-
[](https://bun.sh)
|
|
24
|
+
<p align="center">
|
|
25
|
+
<em>Stop gluing APIs, jobs, and queues into one backend. One law collapses them — client, Console, and infra come free. Yours to host.</em>
|
|
26
|
+
</p>
|
|
17
27
|
|
|
18
|
-
|
|
28
|
+
<p align="center">
|
|
29
|
+
<a href="https://oke.omqkhafi.dev"><strong>Docs</strong></a> ·
|
|
30
|
+
<a href="https://oke.omqkhafi.dev/docs"><strong>Handbook</strong></a> ·
|
|
31
|
+
<a href="https://oke.omqkhafi.dev/llms.txt"><code>llms.txt</code></a> ·
|
|
32
|
+
<a href="https://www.npmjs.com/package/create-oke"><code>create-oke</code></a>
|
|
33
|
+
</p>
|
|
19
34
|
|
|
20
35
|
> [!WARNING]
|
|
21
36
|
> **Early development (`v0.x`) — not production-ready.**
|
|
@@ -24,16 +39,19 @@ _"Stop gluing APIs, jobs, and queues into one backend. One law collapses them
|
|
|
24
39
|
|
|
25
40
|
## Install
|
|
26
41
|
|
|
42
|
+
Requires [Bun](https://bun.sh) `>=1.3.14`.
|
|
43
|
+
|
|
27
44
|
```bash
|
|
45
|
+
bunx create-oke@latest my-app # scaffold (recommended)
|
|
28
46
|
bun add okengine # framework + `oke` CLI
|
|
47
|
+
bun install -g okengine # global `oke` on PATH
|
|
29
48
|
bunx jsr add @omqkhafi/okengine # JSR — library API only
|
|
30
|
-
bunx create-oke@latest my-app --template hello # scaffold
|
|
31
49
|
```
|
|
32
50
|
|
|
33
51
|
## Quick start
|
|
34
52
|
|
|
35
53
|
```bash
|
|
36
|
-
bunx create-oke@latest my-app
|
|
54
|
+
bunx create-oke@latest my-app
|
|
37
55
|
cd my-app
|
|
38
56
|
oke dev
|
|
39
57
|
# or: oke mode docker && oke dev # compose infra · app on host Bun
|
|
@@ -48,73 +66,82 @@ oke dev
|
|
|
48
66
|
|
|
49
67
|
Mnemonic: **O·K·E = 6·5·3**.
|
|
50
68
|
|
|
51
|
-
|
|
69
|
+
The starter already exports a health Flow — change it and save; App and Console update from the same Manifest:
|
|
52
70
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
```typescript
|
|
72
|
+
import { on, flow, http, gate } from "okengine";
|
|
73
|
+
import { z } from "zod";
|
|
74
|
+
|
|
75
|
+
export const health = on(
|
|
76
|
+
http.get("/health").gate(gate.public),
|
|
77
|
+
flow({
|
|
78
|
+
out: z.object({ ok: z.literal(true) }),
|
|
79
|
+
do: () => ({ ok: true as const }),
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
```
|
|
56
83
|
|
|
57
|
-
|
|
84
|
+
Call it with a typed client (contracts inferred — no separate codegen):
|
|
58
85
|
|
|
59
86
|
```typescript
|
|
60
|
-
import {
|
|
61
|
-
import {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Contracts derived from the schema — one source of truth.
|
|
66
|
-
const NewNote = createInsertSchema(notesTable, { title: (s) => s.min(1).max(120) }).omit({
|
|
67
|
-
id: true,
|
|
68
|
-
createdAt: true,
|
|
69
|
-
});
|
|
70
|
-
const Note = createSelectSchema(notesTable);
|
|
71
|
-
|
|
72
|
-
// One declarative resource: list (cursor + search + filters + order +
|
|
73
|
-
// select), create (201), get / update / remove with typed NotFound.
|
|
74
|
-
const notesR = store.resource(db, notesTable, {
|
|
75
|
-
in: NewNote,
|
|
76
|
-
out: Note,
|
|
77
|
-
update: NewNote.partial(),
|
|
78
|
-
list: {
|
|
79
|
-
cursor: [notesTable.createdAt, notesTable.id],
|
|
80
|
-
direction: "desc",
|
|
81
|
-
limit: 20,
|
|
82
|
-
maxLimit: 100,
|
|
83
|
-
search: [notesTable.title],
|
|
84
|
-
filter: "all",
|
|
85
|
-
order: "all",
|
|
86
|
-
},
|
|
87
|
-
unit: "notes",
|
|
88
|
-
breaking: true,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// Mount all five verbs: list/create on /notes, get/update/remove on /notes/:id.
|
|
92
|
-
// `on(http.resource(...))` returns the ops bag; spread it into named exports
|
|
93
|
-
// so `adopt({ notes })` registers each flow under the unit.
|
|
94
|
-
const mounted = on(http.resource("/notes", notesR.all()));
|
|
95
|
-
|
|
96
|
-
export const list = mounted.list;
|
|
97
|
-
export const create = mounted.create;
|
|
98
|
-
export const get = mounted.get;
|
|
99
|
-
export const update = mounted.update;
|
|
100
|
-
export const remove = mounted.remove;
|
|
87
|
+
import { createClient } from "okengine/client";
|
|
88
|
+
import type { App } from "./app";
|
|
89
|
+
|
|
90
|
+
const api = createClient<App>("http://localhost:6530");
|
|
91
|
+
const { data, error } = await api.main.health({});
|
|
101
92
|
```
|
|
102
93
|
|
|
103
94
|
Full walkthrough: [Basic usage](https://oke.omqkhafi.dev/docs/get-started/basic-usage).
|
|
104
95
|
|
|
96
|
+
## One law
|
|
97
|
+
|
|
98
|
+
Every backend behavior is a Flow: `on(Trigger) → Effects`. Endpoints, jobs, consumers, and webhooks are the same species — only the trigger changes.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { on, flow, signal, store, clock, gate, vault, channel, ai, plugin } from "okengine";
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
That is the entire public vocabulary. World access goes through `fx` — effects are inferred, so the Manifest can derive the typed client, Console, and MCP without a second source of truth.
|
|
105
|
+
|
|
106
|
+
| Element | Essence |
|
|
107
|
+
| ----------- | ----------------------------- |
|
|
108
|
+
| **Flow** | behavior |
|
|
109
|
+
| **Signal** | data in motion |
|
|
110
|
+
| **Store** | data at rest |
|
|
111
|
+
| **Clock** | time |
|
|
112
|
+
| **Gate** | permission to act |
|
|
113
|
+
| **Vault** | protected knowledge |
|
|
114
|
+
| **Channel** | reaching humans |
|
|
115
|
+
| **AI** | reaching machine intelligence |
|
|
116
|
+
|
|
117
|
+
Theory: [Introduction](https://oke.omqkhafi.dev/docs/get-started/introduction) · [Elements](https://oke.omqkhafi.dev/docs/elements) · [Why OKE](https://oke.omqkhafi.dev/docs/get-started/why).
|
|
118
|
+
|
|
119
|
+
## Next
|
|
120
|
+
|
|
121
|
+
| Want… | Go to |
|
|
122
|
+
| --------------------------- | ---------------------------------------------------------------------- |
|
|
123
|
+
| Scaffold details & modes | [Installation](https://oke.omqkhafi.dev/docs/get-started/installation) |
|
|
124
|
+
| Declarative CRUD | [`store.resource`](https://oke.omqkhafi.dev/docs/elements/store) |
|
|
125
|
+
| Auth, rate limits, sessions | [Gate](https://oke.omqkhafi.dev/docs/elements/gate) |
|
|
126
|
+
| Queues & live events | [Signal](https://oke.omqkhafi.dev/docs/elements/signal) |
|
|
127
|
+
| Email / SMS / push | [Channel](https://oke.omqkhafi.dev/docs/elements/channel) |
|
|
128
|
+
| `oke` commands | [CLI](https://oke.omqkhafi.dev/docs/reference/cli) |
|
|
129
|
+
|
|
130
|
+
Store apps sync schema with `oke db push|generate|migrate` (local `oke dev` auto-pushes). Prefer `store.schema.table` + `field.*`, or keep a hand-written Drizzle `schema.ts`.
|
|
131
|
+
|
|
105
132
|
## Documentation
|
|
106
133
|
|
|
107
|
-
|
|
134
|
+
| Resource | Link |
|
|
135
|
+
| ------------ | -------------------------------------------------------------- |
|
|
136
|
+
| Docs site | [oke.omqkhafi.dev](https://oke.omqkhafi.dev) |
|
|
137
|
+
| Get started | [/docs/get-started](https://oke.omqkhafi.dev/docs/get-started) |
|
|
138
|
+
| Handbook | [/docs](https://oke.omqkhafi.dev/docs) |
|
|
139
|
+
| Changelog | [/changelog](https://oke.omqkhafi.dev/changelog) |
|
|
140
|
+
| Agents index | [/llms.txt](https://oke.omqkhafi.dev/llms.txt) |
|
|
141
|
+
| Manifest | [`manifest.v1.schema.json`](manifest.v1.schema.json) |
|
|
142
|
+
| Budgets | [`BUDGETS.md`](BUDGETS.md) |
|
|
143
|
+
| Contributing | [`CONTRIBUTING.md`](CONTRIBUTING.md) |
|
|
108
144
|
|
|
109
|
-
|
|
110
|
-
| ------------- | ------------------------------------------------------------------ |
|
|
111
|
-
| Docs site | [oke.omqkhafi.dev](https://oke.omqkhafi.dev) |
|
|
112
|
-
| Handbook | [/docs](https://oke.omqkhafi.dev/docs) |
|
|
113
|
-
| Changelog | [/changelog](https://oke.omqkhafi.dev/changelog) |
|
|
114
|
-
| Agents index | [/llms.txt](https://oke.omqkhafi.dev/llms.txt) |
|
|
115
|
-
| CLI reference | [/docs/reference/cli](https://oke.omqkhafi.dev/docs/reference/cli) |
|
|
116
|
-
| Manifest | [`manifest.v1.schema.json`](manifest.v1.schema.json) |
|
|
117
|
-
| Budgets | [`BUDGETS.md`](BUDGETS.md) |
|
|
118
|
-
| Contributing | [`CONTRIBUTING.md`](CONTRIBUTING.md) |
|
|
145
|
+
## License
|
|
119
146
|
|
|
120
|
-
Pre-1.0. Published on [npm](https://www.npmjs.com/package/okengine) and [JSR](https://jsr.io/@omqkhafi/okengine). MIT.
|
|
147
|
+
Pre-1.0. Published on [npm](https://www.npmjs.com/package/okengine) and [JSR](https://jsr.io/@omqkhafi/okengine). [MIT](https://opensource.org/licenses/MIT).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "okengine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "One law. Eight elements. Ten exports. One package. One manifest. Every backend need is derived, never added.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"prepack": "bun run build",
|
|
59
59
|
"bench": "bun test ./src/runtime/cold-start.bench.ts src/client/budget.test.ts src/console/budget.test.ts src/kernel/budget.test.ts src/kernel/routing-budget.test.ts src/runs/bench.test.ts",
|
|
60
60
|
"budgets": "bun src/release/publish.ts",
|
|
61
|
-
"gate": "bun test src/cli/doc-staleness.test.ts src/cli/competitor-mention-removal.test.ts src/drivers/vault-driver-removal.test.ts src/kernel/errors.registry.test.ts src/upgrade/codemods.test.ts",
|
|
61
|
+
"gate": "PUBLISH_GATE=1 bun test src/cli/doc-staleness.test.ts src/cli/competitor-mention-removal.test.ts src/drivers/vault-driver-removal.test.ts src/kernel/errors.registry.test.ts src/upgrade/codemods.test.ts scripts/publish.gate.test.ts",
|
|
62
62
|
"dev": "bun run --cwd site dev",
|
|
63
63
|
"site:build": "bun run --cwd site build",
|
|
64
64
|
"ci": "bun scripts/ci.ts",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"ajv-formats": "^3.0.1",
|
|
74
74
|
"intl-messageformat": "^11.2.13",
|
|
75
75
|
"oxc-parser": "^0.142.0",
|
|
76
|
-
"sently": "
|
|
76
|
+
"sently": "1.0.0"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@base-ui/react": "^1.6.0",
|
|
@@ -150,6 +150,6 @@
|
|
|
150
150
|
}
|
|
151
151
|
},
|
|
152
152
|
"engines": {
|
|
153
|
-
"bun": ">=1.3"
|
|
153
|
+
"bun": ">=1.3.14"
|
|
154
154
|
}
|
|
155
155
|
}
|
|
@@ -14,15 +14,35 @@ Answers: **upcoming crons, sleeping durable flows, journal**
|
|
|
14
14
|
## What this panel shows
|
|
15
15
|
|
|
16
16
|
<Cards>
|
|
17
|
+
<Card title="Schedules" description="Drift, overdue, missed runs · catch-up one, lease holder." />
|
|
18
|
+
<Card title="Pending wakes" description="Every sleeping durable flow and when it wakes." />
|
|
17
19
|
<Card
|
|
18
|
-
title="
|
|
19
|
-
description="
|
|
20
|
+
title="DST warnings"
|
|
21
|
+
description="Gap or overlap badge — detect only, no schedule rewrite."
|
|
20
22
|
/>
|
|
21
|
-
<Card title="
|
|
22
|
-
<Card title="DST warnings" description="Only when the schedule falls in an ambiguous window." />
|
|
23
|
-
<Card title="Actions" description="Run now, wake early, pause, edit when overridable." />
|
|
23
|
+
<Card title="Actions" description="Run now, wake early, pause; edit when overridable." />
|
|
24
24
|
</Cards>
|
|
25
25
|
|
|
26
|
+
## Health numbers
|
|
27
|
+
|
|
28
|
+
Each cron row projects four numbers plus the lease holder:
|
|
29
|
+
|
|
30
|
+
| Field | Meaning |
|
|
31
|
+
| --------------- | ----------------------------------------------------------------- |
|
|
32
|
+
| Drift | How far the last run sat from the expected slot |
|
|
33
|
+
| Overdue | Whether the schedule is past due |
|
|
34
|
+
| Missed + policy | Slot count while down · always catch-up `"one"` (one fire, not N) |
|
|
35
|
+
| Lease | Which instance holds the leader lease (empty when none) |
|
|
36
|
+
|
|
37
|
+
## Actions
|
|
38
|
+
|
|
39
|
+
| Action | When |
|
|
40
|
+
| ------------- | ---------------------------------------------------------------------- |
|
|
41
|
+
| Pause | Any cron — not gated by `overridable` |
|
|
42
|
+
| Edit schedule | Only when the declaration has `overridable: true`; else loud refuse |
|
|
43
|
+
| Wake early | Sleeping durable run — advances `wakeAt` and can resume the journal |
|
|
44
|
+
| Run now | Still requires the leader lease across replicas that share a CronStore |
|
|
45
|
+
|
|
26
46
|
## Catalog
|
|
27
47
|
|
|
28
48
|
<Cards>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Signals"
|
|
3
|
-
description: "Queue depth, in-flight, DLQ, live monitors."
|
|
3
|
+
description: "Queue depth, in-flight, DLQ, live monitors, orphaned config."
|
|
4
4
|
icon: "Radio"
|
|
5
5
|
source: "docs/spec/console.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Answers: **queue depth, in-flight, DLQ, live monitors**
|
|
8
|
+
Answers: **queue depth, in-flight, DLQ, live monitors, orphaned config**
|
|
9
9
|
|
|
10
10
|
<Callout title="Governing rule">
|
|
11
11
|
One list grouped by delivery physics — not three tabs that re-split the element.
|
|
@@ -14,15 +14,24 @@ Answers: **queue depth, in-flight, DLQ, live monitors**
|
|
|
14
14
|
## What this panel shows
|
|
15
15
|
|
|
16
16
|
<Cards>
|
|
17
|
-
<Card title="once" description="Pending, in-flight, DLQ, retry policy." />
|
|
17
|
+
<Card title="once" description="Pending, in-flight (leased), DLQ, retry policy." />
|
|
18
18
|
<Card title="broadcast" description="Per-subscriber lag and targeted replay." />
|
|
19
|
-
<Card
|
|
19
|
+
<Card
|
|
20
|
+
title="live"
|
|
21
|
+
description="Connection count, throughput, newest-50 payload monitor (full history still retained)."
|
|
22
|
+
/>
|
|
20
23
|
<Card
|
|
21
24
|
title="DLQ repair"
|
|
22
25
|
description="Schema form, typed errors, causal chain, dry-run bulk replay."
|
|
23
26
|
/>
|
|
27
|
+
<Card
|
|
28
|
+
title="Orphaned"
|
|
29
|
+
description="Config still listed after the declaration left code — messages kept; dry-run refused."
|
|
30
|
+
/>
|
|
24
31
|
</Cards>
|
|
25
32
|
|
|
33
|
+
Dry-run is refused for orphaned signals and for signals with no Manifest consumer — unknown handler shapes are not invoked unsafely. Restore the `signal(…)` declaration (and a consumer) before replaying DLQ rows. Lease / at-least-once physics live on [Signal](/docs/elements/signal).
|
|
34
|
+
|
|
26
35
|
## Catalog
|
|
27
36
|
|
|
28
37
|
<Cards>
|
|
@@ -77,6 +77,8 @@ export const triage = smart.prompt("ticket-triage", {
|
|
|
77
77
|
| `ai.embed(name, opts)` | Embedding pipeline into a `store.index` (searched via `fx.search`) |
|
|
78
78
|
| `ai.agent(name, opts)` | Bounded agent whose tools are **your own flows** |
|
|
79
79
|
|
|
80
|
+
<AiBlocks />
|
|
81
|
+
|
|
80
82
|
### Prompt options
|
|
81
83
|
|
|
82
84
|
| Option | Type | Meaning |
|
|
@@ -86,6 +88,10 @@ export const triage = smart.prompt("ticket-triage", {
|
|
|
86
88
|
| `evals` | string | Path to a `.jsonl` eval set, regression-gated via `oke eval` |
|
|
87
89
|
| `budget` | object | `maxCostPerCall` — cost is a first-class dimension |
|
|
88
90
|
|
|
91
|
+
## Declared guardrails
|
|
92
|
+
|
|
93
|
+
Cost, egress, and model choice are contracts — not runtime hope. Each guardrail is declared up front; violating one fails the build or the run.
|
|
94
|
+
|
|
89
95
|
<AiGuardrails />
|
|
90
96
|
|
|
91
97
|
## Tools are flows (same `fx.call` path)
|
|
@@ -136,6 +142,8 @@ Use `keyBy: "ip"` on public unauthenticated AI edges. Cost caps stay on prompt/a
|
|
|
136
142
|
|
|
137
143
|
At build time, OKE checks which fields each `fx.ask` sends against your store classifications. If a PII field (`.pii()` in your schema) would reach a third-party model, the build fails:
|
|
138
144
|
|
|
145
|
+
<AiPiiEgress />
|
|
146
|
+
|
|
139
147
|
```text
|
|
140
148
|
build failed: flow "ticket-triage" sends pii field(s) [email] to a third-party model without allowPii
|
|
141
149
|
```
|
|
@@ -159,9 +167,28 @@ Because `fx.ask` is nondeterministic, the runtime adjusts around it: journaling
|
|
|
159
167
|
|
|
160
168
|
There is deliberately **no production default**: `prod` must name a driver, which keeps the model choice visible in `oke.config.ts` where review can see it.
|
|
161
169
|
|
|
170
|
+
### Setup wizard
|
|
171
|
+
|
|
172
|
+
```bash title="Terminal"
|
|
173
|
+
oke ai setup
|
|
174
|
+
oke ai setup --provider ollama --yes
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Writes `drivers.ai`, `.env.local`, and `src/ai.ts`.
|
|
178
|
+
|
|
179
|
+
Ollama: short quiz (use case · speed/quality · vision) → detect installed models
|
|
180
|
+
→ picks that fit your **machine tier** (~4GB OS/IDE headroom). Labels say
|
|
181
|
+
**≈16GB-class**, not “download 16GB” — pull size is usually much smaller than RAM.
|
|
182
|
+
|
|
183
|
+
Cloud: short chat list + **Other…** for any model id. **← Back** returns a step.
|
|
184
|
+
`create-oke` runs the same model wizard **before** install when you enable AI
|
|
185
|
+
(customize or `--ai`). Standalone `oke ai setup` remains for existing apps.
|
|
186
|
+
|
|
162
187
|
### Local Ollama
|
|
163
188
|
|
|
164
|
-
`ollama` is a local-model driver — any model
|
|
189
|
+
`ollama` is a local-model driver — any pulled model works. Pin `drivers.ai`;
|
|
190
|
+
optionally pin `images.ai` for compose. Native `complete`, `stream`, and
|
|
191
|
+
`embed` (`POST /api/embeddings`) — no OpenAI shim required for local RAG.
|
|
165
192
|
|
|
166
193
|
```typescript title="oke.config.ts"
|
|
167
194
|
import { defineConfig } from "okengine/config";
|
|
@@ -221,7 +248,7 @@ The one you declared. There is no fallback or guess — if `prod` has no AI driv
|
|
|
221
248
|
</Accordion>
|
|
222
249
|
<Accordion title="oke boot: ollama driver needs OKE_AI_URL">
|
|
223
250
|
|
|
224
|
-
Docker mode expects `OKE_AI_URL` from `docker/.env.docker` (written when `images.ai` is pinned). Locally, the default is `http://127.0.0.1:11434`. If asks fail with `OllamaUnavailableError`, confirm the server answers and that you have pulled the model you named (`ollama pull qwen3.5:9b`, or `qwen3.5:9b-mlx` on Apple Silicon, or your `OKE_AI_MODEL`).
|
|
251
|
+
Docker mode expects `OKE_AI_URL` from `docker/.env.docker` (written when `images.ai` is pinned). Locally, the default is `http://127.0.0.1:11434`. If asks fail with `OllamaUnavailableError`, confirm the server answers and that you have pulled the model you named (`ollama pull qwen3.5:9b`, or `qwen3.5:9b-mlx` on Apple Silicon, or your `OKE_AI_MODEL`). Prefer `oke ai setup` to wire driver, env, and `src/ai.ts` in one pass.
|
|
225
252
|
|
|
226
253
|
</Accordion>
|
|
227
254
|
<Accordion title="A prompt edit made answers worse — how do I catch that pre-merge?">
|
|
@@ -5,7 +5,7 @@ icon: "Mail"
|
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Channel is how your app **reaches humans**: the order-confirmation email, the OTP text, the WhatsApp notification. Reaching a person is not the same problem as moving data between machines — it needs consent (did they opt out?), locale (which language?), receipts (did it land?), and fallback (
|
|
8
|
+
Channel is how your app **reaches humans**: the order-confirmation email, the OTP text, the WhatsApp notification. Reaching a person is not the same problem as moving data between machines — it needs consent (did they opt out?), locale (which language?), receipts (did it land?), and fallback (first driver failed, try the next). Those physics are built into the element, so every flow gets them for free.
|
|
9
9
|
|
|
10
10
|
<Callout title="The one rule">
|
|
11
11
|
Sends go through `fx.send` and declared templates — never through a raw SMTP or provider client
|
|
@@ -83,49 +83,84 @@ Locally the `console` driver captures mail into an inbox instead of sending; in
|
|
|
83
83
|
|
|
84
84
|
### Consent is checked before sending
|
|
85
85
|
|
|
86
|
-
Opt-out is first-class: a subject who opted out of a medium is **suppressed** — the send resolves without contacting the provider, and the receipt
|
|
86
|
+
Opt-out is first-class: a subject who opted out of a medium is **suppressed** — the send resolves without contacting the provider, and the receipt status is `suppressed/opted-out`. You never hand-roll "did they unsubscribe?" checks.
|
|
87
87
|
|
|
88
|
-
### Locale resolves
|
|
88
|
+
### Locale resolves, then the catalog
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
`fx.send` picks a locale: explicit `locale` → `profileLocale` → first
|
|
91
|
+
`Accept-Language` tag → `defaultLocale`. The body is the catalog entry for
|
|
92
|
+
that exact key, else the default locale, else `en`.
|
|
92
93
|
|
|
93
94
|
Locales come from `oke.config.ts` `i18n`. Catalog bodies use `{{field}}` — not
|
|
94
95
|
ICU ([i18n](/docs/reference/i18n) / `fx.t` owns flow copy). Pass `locale` /
|
|
95
96
|
`profileLocale` / `acceptLanguage` on `fx.send`, or omit them for `fx.locale`.
|
|
96
97
|
|
|
97
|
-
### Fallback chains are
|
|
98
|
+
### Fallback chains are same-medium
|
|
98
99
|
|
|
99
|
-
`via` orders the
|
|
100
|
+
`via` orders **driver ids** for the template's medium — first success wins,
|
|
101
|
+
and **every** attempt is recorded:
|
|
100
102
|
|
|
101
103
|
```typescript
|
|
102
|
-
await fx.send(
|
|
103
|
-
|
|
104
|
+
await fx.send(orderConfirmed, {
|
|
105
|
+
to: user.email,
|
|
106
|
+
data: { name, orderId, total },
|
|
107
|
+
via: ["smtp", "resend"],
|
|
108
|
+
});
|
|
109
|
+
// smtp fails → resend tried → receipt status "fallback" with both attempts
|
|
104
110
|
```
|
|
105
111
|
|
|
106
112
|
### Receipts for everything
|
|
107
113
|
|
|
108
|
-
Each send records its attempts — driver, ok/error, timestamp, message id — so "did the user actually get it?" is a Console query, not a guess. In a dry run,
|
|
114
|
+
Each send records its attempts — driver, ok/error, timestamp, message id — so "did the user actually get it?" is a Console query, not a guess. In a dry run, the kernel records a would-have-fired send and never opens a provider — no Channel receipt is written.
|
|
109
115
|
|
|
110
116
|
## Per-environment drivers
|
|
111
117
|
|
|
118
|
+
Boot opens the email driver from `drivers.channel.email`, and appends an SMS
|
|
119
|
+
driver when `drivers.channel.sms` is set (other than `console`). WhatsApp and
|
|
120
|
+
push drivers are passed via `BootOptions.channel.drivers` when you need them.
|
|
121
|
+
|
|
112
122
|
```typescript title="oke.config.ts"
|
|
113
123
|
drivers: {
|
|
114
124
|
channel: {
|
|
115
125
|
email: { local: "console", docker: "smtp", test: "console", prod: "smtp" },
|
|
126
|
+
// optional — omit until you need SMS at boot
|
|
127
|
+
sms: { local: "console", docker: "taqnyat", test: "console", prod: "taqnyat" },
|
|
116
128
|
},
|
|
117
129
|
},
|
|
118
130
|
images: {
|
|
119
|
-
"channel.email": "axllent/mailpit:v1.22.3",
|
|
131
|
+
"channel.email": "axllent/mailpit:v1.22.3",
|
|
120
132
|
},
|
|
121
133
|
```
|
|
122
134
|
|
|
123
|
-
| Driver | Medium
|
|
124
|
-
| ---------- |
|
|
125
|
-
| `console` | any
|
|
126
|
-
| `smtp` | email
|
|
127
|
-
| `resend` | email
|
|
128
|
-
| `
|
|
135
|
+
| Driver | Medium | Behavior |
|
|
136
|
+
| ---------- | -------- | ------------------------------------------------------------------- |
|
|
137
|
+
| `console` | any | Captures into a readable inbox — local + tests |
|
|
138
|
+
| `smtp` | email | Real SMTP — Mailpit in docker, your provider in prod |
|
|
139
|
+
| `resend` | email | Resend HTTP API (`RESEND_API_KEY`) |
|
|
140
|
+
| `sndr` | email | SNDR HTTP API (`SNDR_API_KEY`) |
|
|
141
|
+
| `taqnyat` | sms | Taqnyat SMS (`TAQNYAT_BEARER_TOKEN` + `TAQNYAT_SENDER`) |
|
|
142
|
+
| `msegat` | sms | Msegat SMS (`MSEGAT_USERNAME` + `MSEGAT_API_KEY` + `MSEGAT_SENDER`) |
|
|
143
|
+
| `unifonic` | sms | Unifonic el.cloud SMS (`UNIFONIC_APPSID`) |
|
|
144
|
+
| `wa-cloud` | whatsapp | Meta WhatsApp Cloud API |
|
|
145
|
+
| `webpush` | push | Web Push + VAPID (`createPushSender` → `WebPushTransport`) |
|
|
146
|
+
| `fcm` | push | Firebase Cloud Messaging HTTP v1 |
|
|
147
|
+
|
|
148
|
+
Email fallback uses ordered email transports; SMS with multiple SMS drivers uses
|
|
149
|
+
the same failover path. Credentials for boot-opened drivers are listed under
|
|
150
|
+
[Environment Variables](/docs/reference/environment-variables).
|
|
151
|
+
|
|
152
|
+
`webpush` needs VAPID keys — open it yourself and include it in
|
|
153
|
+
`BootOptions.channel.drivers` (boot does not open push from env):
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { openWebPushChannel } from "okengine/drivers/channel-webpush";
|
|
157
|
+
|
|
158
|
+
openWebPushChannel({
|
|
159
|
+
vapidPublicKey: process.env.VAPID_PUBLIC_KEY!,
|
|
160
|
+
vapidPrivateKey: process.env.VAPID_PRIVATE_KEY!,
|
|
161
|
+
vapidSubject: process.env.VAPID_SUBJECT,
|
|
162
|
+
});
|
|
163
|
+
```
|
|
129
164
|
|
|
130
165
|
## Troubleshooting
|
|
131
166
|
|
|
@@ -142,12 +177,45 @@ Check consent first: if they opted out, sends to them are suppressed by design.
|
|
|
142
177
|
</Accordion>
|
|
143
178
|
<Accordion title="The Arabic version didn't render">
|
|
144
179
|
|
|
145
|
-
|
|
180
|
+
The send still succeeds: the body falls back to your default locale, then `en`, when the catalog misses that exact key. Check that an Arabic entry exists under the same tag you send (`ar` vs `ar-SA`) and that the template lists it in `locales`.
|
|
181
|
+
|
|
182
|
+
</Accordion>
|
|
183
|
+
<Accordion title="How do I ingest SNDR / Unifonic delivery webhooks?">
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import {
|
|
187
|
+
parseSndrWebhook,
|
|
188
|
+
verifySndrSignature,
|
|
189
|
+
parseUnifonicWebhook,
|
|
190
|
+
toDeliveryEvent,
|
|
191
|
+
} from "okengine/channel";
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Verify the signature when the provider sends one, parse the body, then call
|
|
195
|
+
`ingestOutcome` on your channel runtime with the bounce/complaint fields.
|
|
196
|
+
Console reads the receipt ledger — never raw webhook JSON.
|
|
197
|
+
|
|
198
|
+
</Accordion>
|
|
199
|
+
<Accordion title="How do I know which driver finally delivered?">
|
|
200
|
+
|
|
201
|
+
The receipt keeps every attempt in order with its outcome. A send that succeeded on a later driver reports status `fallback` — you can see the full chain in Console → Channels.
|
|
146
202
|
|
|
147
203
|
</Accordion>
|
|
148
|
-
<Accordion title="How do I
|
|
204
|
+
<Accordion title="How do I verify Web Push against a real browser?">
|
|
205
|
+
|
|
206
|
+
Subscribe in a real browser with sently’s `examples/webpush-interop` page
|
|
207
|
+
(paste the same `VAPID_PUBLIC_KEY`), copy the `PushSubscription` JSON, then:
|
|
208
|
+
|
|
209
|
+
```sh
|
|
210
|
+
export VAPID_PUBLIC_KEY='…'
|
|
211
|
+
export VAPID_PRIVATE_KEY='…'
|
|
212
|
+
bun scripts/webpush-interop.ts /tmp/push-sub.json
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
That path is Channel → `webpush` → sently `createPushSender` →
|
|
216
|
+
`WebPushTransport`, and it prints the push-service HTTP response.
|
|
149
217
|
|
|
150
|
-
|
|
218
|
+
Confirm a visible OS notification titled `okengine webpush interop`.
|
|
151
219
|
|
|
152
220
|
</Accordion>
|
|
153
221
|
</Accordions>
|
|
@@ -156,6 +224,8 @@ The receipt keeps every attempt in order with its outcome. A send that succeeded
|
|
|
156
224
|
|
|
157
225
|
- [Flow](/docs/elements/flow) — `fx.send` inside `do`
|
|
158
226
|
- [Console · Channels](/docs/console/channels) — receipts, attempts, suppression
|
|
227
|
+
- [Environment Variables](/docs/reference/environment-variables) — Channel boot credentials
|
|
228
|
+
- [Configuration](/docs/reference/configuration) — `drivers.channel.*` maps
|
|
159
229
|
- [Signal](/docs/elements/signal) — machine-to-machine messaging, the other side of the line
|
|
160
230
|
|
|
161
231
|
## Next
|