okengine 0.3.6 → 0.4.3
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 -0
- package/package.json +12 -12
- package/site/content/docs/ai/skills.mdx +5 -3
- package/site/content/docs/elements/ai.mdx +2 -0
- package/site/content/docs/elements/channel.mdx +2 -0
- package/site/content/docs/elements/clock.mdx +1 -4
- package/site/content/docs/elements/flow.mdx +4 -10
- package/site/content/docs/elements/gate.mdx +2 -0
- package/site/content/docs/elements/signal.mdx +1 -5
- package/site/content/docs/elements/store.mdx +27 -6
- package/site/content/docs/elements/vault.mdx +10 -11
- package/site/content/docs/get-started/basic-usage.mdx +76 -41
- package/site/content/docs/get-started/installation.mdx +95 -43
- package/site/content/docs/get-started/introduction.mdx +128 -75
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/why.mdx +141 -0
- package/site/content/docs/plugins/ip-allowlist.mdx +1 -2
- package/site/content/docs/plugins/security-headers.mdx +1 -1
- package/site/content/docs/reference/configuration.mdx +1 -1
- package/site/content/docs/reference/environment-variables.mdx +5 -3
- package/site/content/docs/reference/fx.mdx +16 -3
- package/src/cli/competitor-mention-removal.test.ts +117 -0
- package/src/cli/dev.ts +20 -0
- package/src/cli/meilisearch-local.test.ts +69 -0
- package/src/cli/meilisearch-local.ts +188 -0
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +5 -1
- package/src/console/server/store.test.ts +1 -1
- package/src/console/server/store.ts +11 -1
- package/src/console/ui/dist/assets/index-CjxwRGVv.js +10 -0
- package/src/console/ui/dist/assets/panel-access-BGv45snf.js +64 -0
- package/src/console/ui/dist/assets/{panel-ai-D_m6WQI8.js → panel-ai-B2S7LEii.js} +1 -1
- package/src/console/ui/dist/assets/{panel-architecture-CKnXFyUx.js → panel-architecture-D7UJh91v.js} +1 -1
- package/src/console/ui/dist/assets/{panel-channels-DCDd4WAC.js → panel-channels-9T3ybqRu.js} +1 -1
- package/src/console/ui/dist/assets/panel-clock-Cb1UXGRQ.js +1 -0
- package/src/console/ui/dist/assets/{panel-diff-cdonmH8c.js → panel-diff-DmYbKWmN.js} +1 -1
- package/src/console/ui/dist/assets/panel-flows-PiHwT55z.js +48 -0
- package/src/console/ui/dist/assets/{panel-gates-B5eTE8XH.js → panel-gates-BQGYXvjT.js} +1 -1
- package/src/console/ui/dist/assets/panel-overview-BBnRO18l.js +1 -0
- package/src/console/ui/dist/assets/{panel-plugins-Cj7DK1er.js → panel-plugins-D0PsmVw2.js} +1 -1
- package/src/console/ui/dist/assets/panel-runs-CWuRDe0r.js +1 -0
- package/src/console/ui/dist/assets/{panel-signals-whmDXIg3.js → panel-signals-Bbg4ewpP.js} +1 -1
- package/src/console/ui/dist/assets/{panel-store-CEMHLvaw.js → panel-store-CPCbsDRa.js} +1 -1
- package/src/console/ui/dist/assets/panel-traces-DVAzuA_S.js +1 -0
- package/src/console/ui/dist/assets/{panel-vault-C9wjbki8.js → panel-vault-D1_MvOmo.js} +1 -1
- package/src/console/ui/dist/assets/{rolldown-runtime-CNC7AqOf.js → rolldown-runtime-B0Z9INg1.js} +1 -1
- package/src/console/ui/dist/index.html +2 -2
- package/src/docker/compose.ts +5 -0
- package/src/docker/docker.test.ts +41 -0
- package/src/docker/recipes/index.ts +10 -2
- package/src/docker/recipes/meilisearch.ts +31 -0
- package/src/drivers/conformance.test.ts +16 -1
- package/src/drivers/conformance.ts +40 -3
- package/src/drivers/index.ts +14 -2
- package/src/drivers/libsql.ts +4 -4
- package/src/drivers/meilisearch.integration.test.ts +77 -0
- package/src/drivers/meilisearch.test.ts +181 -0
- package/src/drivers/meilisearch.ts +208 -0
- package/src/drivers/memory.ts +4 -4
- package/src/drivers/pgvector.ts +6 -6
- package/src/drivers/types.ts +93 -12
- package/src/drivers/vault-driver-removal.test.ts +6 -0
- package/src/drivers/vault-types.ts +4 -4
- package/src/elements/ai/runtime.ts +6 -0
- package/src/elements/ai.test.ts +22 -0
- package/src/elements/store/index-boot.test.ts +49 -7
- package/src/elements/store/runtime.ts +50 -15
- package/src/elements/store.ts +2 -0
- package/src/elements/vault.test.ts +27 -4
- package/src/elements/vault.ts +1 -1
- package/src/index.ts +4 -0
- package/src/kernel/boot-bind/store.test.ts +9 -0
- package/src/kernel/boot-bind/store.ts +30 -2
- package/src/kernel/concurrency.test.ts +58 -0
- package/src/kernel/concurrency.ts +48 -0
- package/src/kernel/fx.test.ts +11 -2
- package/src/kernel/fx.ts +37 -5
- package/src/kernel/index.ts +10 -1
- package/src/kernel/redacted.ts +74 -0
- package/src/kernel/router.ts +3 -3
- package/src/test/provisions.integration.test.ts +1 -1
- package/site/content/docs/get-started/comparison.mdx +0 -65
- package/src/console/ui/dist/assets/index-CrKMmO__.js +0 -10
- package/src/console/ui/dist/assets/panel-access-C0J2D-a2.js +0 -64
- package/src/console/ui/dist/assets/panel-clock-DjGGFPzr.js +0 -1
- package/src/console/ui/dist/assets/panel-flows-DlCU5zjA.js +0 -45
- package/src/console/ui/dist/assets/panel-overview-BsFvDdts.js +0 -1
- package/src/console/ui/dist/assets/panel-runs-C0gmnoYL.js +0 -1
- package/src/console/ui/dist/assets/panel-traces-BDiAuVSK.js +0 -1
- package/src/drivers/vault-infisical.ts +0 -57
package/AGENTS.md
CHANGED
|
@@ -96,3 +96,5 @@ Engine: Bun `>=1.3`.
|
|
|
96
96
|
## After every implementation
|
|
97
97
|
|
|
98
98
|
Before claiming work done: run [`.agents/skills/oke-ship`](.agents/skills/oke-ship/SKILL.md) — append notes to `changelog.md` under `## Unreleased` (never under a shipped `## v…` section), and update site docs via [`.agents/skills/oke-docs`](.agents/skills/oke-docs/SKILL.md) for any user-facing surface. Version bump is separate: `bun run bump` promotes Unreleased into the next `## vX.Y.Z`.
|
|
99
|
+
|
|
100
|
+
Dependency bumps (one package, one `package.json`, or all): [`.agents/skills/oke-deps`](.agents/skills/oke-deps/SKILL.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "okengine",
|
|
3
|
-
"version": "0.3
|
|
3
|
+
"version": "0.4.3",
|
|
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": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"prepack": "bun run build",
|
|
57
57
|
"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",
|
|
58
58
|
"budgets": "bun src/release/publish.ts",
|
|
59
|
-
"gate": "bun test src/cli/doc-staleness.test.ts src/drivers/vault-driver-removal.test.ts src/kernel/errors.registry.test.ts src/upgrade/codemods.test.ts",
|
|
59
|
+
"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",
|
|
60
60
|
"dev": "bun run --cwd site dev",
|
|
61
61
|
"site:build": "bun run --cwd site build",
|
|
62
62
|
"ci": "bun scripts/ci.ts",
|
|
@@ -66,10 +66,10 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@clack/prompts": "^1.7.0",
|
|
69
|
-
"@duckdb/node-api": "^1.5.5-r.
|
|
69
|
+
"@duckdb/node-api": "^1.5.5-r.2",
|
|
70
70
|
"ajv": "^8.20.0",
|
|
71
71
|
"ajv-formats": "^3.0.1",
|
|
72
|
-
"oxc-parser": "^0.
|
|
72
|
+
"oxc-parser": "^0.142.0",
|
|
73
73
|
"sently": "^0.8.0"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
@@ -78,20 +78,20 @@
|
|
|
78
78
|
"@codemirror/lang-json": "^6.0.2",
|
|
79
79
|
"@codemirror/state": "^6.7.1",
|
|
80
80
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
81
|
-
"@codemirror/view": "^6.43.
|
|
81
|
+
"@codemirror/view": "^6.43.7",
|
|
82
82
|
"@electric-sql/pglite": "^0.5.4",
|
|
83
83
|
"@electric-sql/pglite-pgvector": "^0.0.5",
|
|
84
84
|
"@libsql/client": "^0.17.4",
|
|
85
|
-
"@oxc-project/types": "^0.
|
|
86
|
-
"@playwright/test": "^1.62.
|
|
85
|
+
"@oxc-project/types": "^0.142.0",
|
|
86
|
+
"@playwright/test": "^1.62.1",
|
|
87
87
|
"@sinclair/typebox": "^0.34.52",
|
|
88
88
|
"@tailwindcss/vite": "^4.3.3",
|
|
89
89
|
"@tanstack/react-query": "^5.101.4",
|
|
90
90
|
"@tanstack/react-router": "^1.170.18",
|
|
91
91
|
"@types/bun": "latest",
|
|
92
|
-
"@types/react": "^19.2.
|
|
93
|
-
"@types/react-dom": "^19.2.
|
|
94
|
-
"@vitejs/plugin-react": "^6.0.
|
|
92
|
+
"@types/react": "^19.2.18",
|
|
93
|
+
"@types/react-dom": "^19.2.4",
|
|
94
|
+
"@vitejs/plugin-react": "^6.0.5",
|
|
95
95
|
"arktype": "^2.2.3",
|
|
96
96
|
"axe-core": "^4.12.1",
|
|
97
97
|
"clsx": "^2.1.1",
|
|
@@ -105,13 +105,13 @@
|
|
|
105
105
|
"oxfmt": "^0.61.0",
|
|
106
106
|
"oxlint": "^1.76.0",
|
|
107
107
|
"oxlint-tsgolint": "^7.0.2001",
|
|
108
|
-
"playwright": "^1.62.
|
|
108
|
+
"playwright": "^1.62.1",
|
|
109
109
|
"react": "^19.2.8",
|
|
110
110
|
"react-dom": "^19.2.8",
|
|
111
111
|
"tailwindcss": "^4.3.3",
|
|
112
112
|
"typescript": "^7.0.2",
|
|
113
113
|
"valibot": "^1.4.2",
|
|
114
|
-
"vite": "^8.
|
|
114
|
+
"vite": "^8.2.0",
|
|
115
115
|
"zod": "^4.4.3"
|
|
116
116
|
},
|
|
117
117
|
"peerDependencies": {
|
|
@@ -7,14 +7,15 @@ source: "docs/spec/unified-theory.md"
|
|
|
7
7
|
|
|
8
8
|
Tools alone don't make a good operator — an agent also needs to know the _vocabulary_: what a Flow is, why `fx` is the only door, which driver ids are legal. OKE ships that knowledge as contracts and skills that agents load automatically, so sessions start aligned instead of drifting and getting corrected.
|
|
9
9
|
|
|
10
|
-
## The
|
|
10
|
+
## The layers
|
|
11
11
|
|
|
12
12
|
| Layer | Path | Loaded when | Teaches |
|
|
13
13
|
| ------------------ | -------------------------- | ------------------------------------------------- | --------------------------------------------------------------------- |
|
|
14
14
|
| **Agent contract** | `AGENTS.md` (repo root) | Every agent session, automatically | The one law, eight elements, ten exports, the fx rule, ports, budgets |
|
|
15
15
|
| **Element skill** | `.agents/skills/oke/` | Building or changing an okengine app | The element contract in depth — declaration patterns per element |
|
|
16
16
|
| **Docs skill** | `.agents/skills/oke-docs/` | Writing or editing docs under `site/content/docs` | The documentation information-architecture standard and its gates |
|
|
17
|
-
| **Ship skill** | `.agents/skills/oke-ship/` | After any implementation, before claiming done | Changelog under
|
|
17
|
+
| **Ship skill** | `.agents/skills/oke-ship/` | After any implementation, before claiming done | Changelog under `## Unreleased` + docs sync via `oke-docs` |
|
|
18
|
+
| **Deps skill** | `.agents/skills/oke-deps/` | Updating `package.json` dependencies | Scoped bumps, Bun install, pins (Drizzle RC, fumadocs alias, …) |
|
|
18
19
|
|
|
19
20
|
## AGENTS.md — the root contract
|
|
20
21
|
|
|
@@ -22,13 +23,14 @@ Every OKE app's repo carries an `AGENTS.md` that agents (Cursor, Claude Code, an
|
|
|
22
23
|
|
|
23
24
|
## Skills — installable know-how
|
|
24
25
|
|
|
25
|
-
Skills are `SKILL.md` packages an agent loads when the work matches their description.
|
|
26
|
+
Skills are `SKILL.md` packages an agent loads when the work matches their description. OKE ships:
|
|
26
27
|
|
|
27
28
|
| Skill | Use it for | Inside |
|
|
28
29
|
| ---------- | -------------------------------------------- | ------------------------------------------------------------------------- |
|
|
29
30
|
| `oke` | App work — flows, elements, drivers | The agent contract, element patterns, the fx invariants |
|
|
30
31
|
| `oke-docs` | Docs work — new pages, rewrites | The page skeleton, verification sources, the density gate |
|
|
31
32
|
| `oke-ship` | Closing an implementation — changelog + docs | Append under `## Unreleased`; `bun run bump` promotes it into `## vX.Y.Z` |
|
|
33
|
+
| `oke-deps` | Dependency updates — one package or all | Scope map, `ncu` + Bun, reject downgrades / protect RC and aliases |
|
|
32
34
|
|
|
33
35
|
All live in the repo under `.agents/skills/`, so they travel with the code and stay versioned with what they describe.
|
|
34
36
|
|
|
@@ -86,6 +86,8 @@ export const triage = smart.prompt("ticket-triage", {
|
|
|
86
86
|
| `evals` | string | Path to a `.jsonl` eval set, regression-gated via `oke eval` |
|
|
87
87
|
| `budget` | object | `maxCostPerCall` — cost is a first-class dimension |
|
|
88
88
|
|
|
89
|
+
<AiGuardrails />
|
|
90
|
+
|
|
89
91
|
## Agents with real guardrails
|
|
90
92
|
|
|
91
93
|
An agent's tools are your flows — each carrying its own gates, effects, and typed errors, so the agent can never do anything a flow couldn't:
|
|
@@ -79,6 +79,8 @@ Locally the `console` driver captures mail into an inbox instead of sending; in
|
|
|
79
79
|
|
|
80
80
|
## The human physics
|
|
81
81
|
|
|
82
|
+
<ChannelPhysics />
|
|
83
|
+
|
|
82
84
|
### Consent is checked before sending
|
|
83
85
|
|
|
84
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 says so. You never hand-roll "did they unsubscribe?" checks.
|
|
@@ -83,10 +83,7 @@ do: async (input, fx) => {
|
|
|
83
83
|
|
|
84
84
|
## Two kinds of schedules
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
| ------------------- | ---------------- | --------------- | ------------------ | --------------------------------- |
|
|
88
|
-
| `every("1h")` | no | no | no | Simple fixed intervals |
|
|
89
|
-
| `clock(name, opts)` | yes | yes | when `overridable` | Business schedules operators tune |
|
|
86
|
+
<ClockSchedules />
|
|
90
87
|
|
|
91
88
|
Both are triggers consumed with the same `on(trigger, flow)` — the flow underneath does not know the difference.
|
|
92
89
|
|
|
@@ -96,13 +96,7 @@ Every response follows one envelope — success `{ data, error: null }`, failure
|
|
|
96
96
|
|
|
97
97
|
## The five triggers
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
| ---------------------------- | ---------------------------- | ------------------ |
|
|
101
|
-
| `http.post("/orders")` | a request arrives | endpoint · handler |
|
|
102
|
-
| `orderPlaced` (a signal) | another flow emits | queue consumer |
|
|
103
|
-
| `every("1h")` | time passes | cron job |
|
|
104
|
-
| `db.table(orders).changed()` | a row changes | CDC pipeline |
|
|
105
|
-
| — none | another flow calls `fx.call` | "private" helper |
|
|
99
|
+
<FlowTriggers />
|
|
106
100
|
|
|
107
101
|
### http — a request arrives
|
|
108
102
|
|
|
@@ -192,7 +186,7 @@ Everything a flow may touch, on one object:
|
|
|
192
186
|
| `fx.ask(prompt, input)` | ask | Call a versioned AI prompt |
|
|
193
187
|
| `fx.run(agent, input)` | ask | Run a bounded agent |
|
|
194
188
|
| `fx.call(flow, input)` | call | Invoke another flow |
|
|
195
|
-
| `fx.vault(contract)` | read | Read a secret (
|
|
189
|
+
| `fx.vault(contract)` | read | Read a secret (`Redacted`; logs masked) |
|
|
196
190
|
| `fx.clock.now()` / `.sleep(…)` | — | Injected time / durable sleep |
|
|
197
191
|
| `fx.cache.get/set` | — | Shared cache with effect-aware invalidation |
|
|
198
192
|
| `fx.step(name, fn)` | — | Named durable step — never re-runs on replay |
|
|
@@ -218,12 +212,12 @@ export const chargeOrder = flow({
|
|
|
218
212
|
out: z.boolean(),
|
|
219
213
|
do: async ({ orderId }, fx) => {
|
|
220
214
|
const intent = await fx.step("create-intent", () =>
|
|
221
|
-
stripe(fx.vault(stripeKey)).create(orderId),
|
|
215
|
+
stripe(fx.vault(stripeKey).reveal()).create(orderId),
|
|
222
216
|
);
|
|
223
217
|
|
|
224
218
|
await fx.clock.sleep("verify-window", "2m"); // survives restart and deploy
|
|
225
219
|
|
|
226
|
-
return fx.step("confirm", () => stripe(fx.vault(stripeKey)).confirm(intent));
|
|
220
|
+
return fx.step("confirm", () => stripe(fx.vault(stripeKey).reveal()).confirm(intent));
|
|
227
221
|
},
|
|
228
222
|
});
|
|
229
223
|
```
|
|
@@ -76,11 +76,7 @@ That's the loop: declare → `fx.emit` → `on(signal, flow)`. The runtime handl
|
|
|
76
76
|
|
|
77
77
|
## The three delivery physics
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
| ------------- | ------------------------------------------------------ | ---------------------------------------------------------- |
|
|
81
|
-
| `"once"` | Queue: competing consumers, retries, dead-letter queue | Jobs that must happen exactly once — emails, payments sync |
|
|
82
|
-
| `"broadcast"` | Pub/sub: **every** subscriber receives a copy | Cache invalidation, cross-service events |
|
|
83
|
-
| `"live"` | Stream: client-subscribable and replayable | Live feeds, dashboards, progress updates |
|
|
79
|
+
<SignalDelivery />
|
|
84
80
|
|
|
85
81
|
The declaration is identical in shape for all three — switching physics later is a one-word change, not a migration to another library.
|
|
86
82
|
|
|
@@ -90,12 +90,7 @@ export const createNote = on(
|
|
|
90
90
|
|
|
91
91
|
Pick the facet that matches the physics of your data. Each declaration is one line; the runtime handle shows what flows can do with it.
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
| -------------------------- | ----------------- | ------------------------------------- | ------------------------------------------------------ |
|
|
95
|
-
| `store.sql(name, opts)` | a SQL database | domain tables, relations, constraints | `select` · `insert` · `update` · `delete` · `findById` |
|
|
96
|
-
| `store.kv(name, opts?)` | a key-value space | cache, sessions, rate limits | `get` · `set(key, value, ttl?)` · `delete` · `list` |
|
|
97
|
-
| `store.files(name, opts?)` | a blob bucket | uploads, exports, attachments | `put` · `get` · `delete` · `list(prefix?)` |
|
|
98
|
-
| `store.index(name, opts)` | a vector index | semantic search / RAG (`dims`) | `upsert` · `search(vector, topK?)` · `delete` |
|
|
93
|
+
<StoreFacets />
|
|
99
94
|
|
|
100
95
|
Every facet accepts optional `description` — a human title in the Console
|
|
101
96
|
(falls back to the store name). `store.index` also takes `dims`.
|
|
@@ -282,6 +277,32 @@ drivers: {
|
|
|
282
277
|
|
|
283
278
|
**Consequence:** a configured SQL-backed index that cannot reach its engine — missing peer, missing `vector` extension, wrong sql driver — fails loudly at first use. It never silently falls back to `memory`.
|
|
284
279
|
|
|
280
|
+
### Full-text search — `meilisearch` (opt-in)
|
|
281
|
+
|
|
282
|
+
`meilisearch` is a fourth `store.index` backend, opt-in only — `memory` stays the default. It is a genuinely different capability from the vector ANN drivers: typo-tolerant, faceted full-text relevance, not cosine similarity. The two models are kept apart by a **discriminated union** on `driverId`, so TypeScript rejects a vector query against a text index (and vice versa) at compile time — never at runtime:
|
|
283
|
+
|
|
284
|
+
| `driverId` | `upsert` | `search` | `score` means |
|
|
285
|
+
| -------------------------------- | --------------------- | ---------------------------------------- | --------------------------- |
|
|
286
|
+
| `memory` / `pgvector` / `libsql` | `(id, vector, meta?)` | `search(vector, topK?)` → `IndexHit[]` | cosine similarity |
|
|
287
|
+
| `meilisearch` | `(id, document)` | `search(q, opts?)` → `{ hits, facets? }` | relevance (`_rankingScore`) |
|
|
288
|
+
|
|
289
|
+
```typescript title="oke.config.ts"
|
|
290
|
+
drivers: {
|
|
291
|
+
store: {
|
|
292
|
+
index: { local: "meilisearch", docker: "meilisearch", prod: "meilisearch" },
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
images: {
|
|
296
|
+
"store.index": "getmeili/meilisearch:v1.37",
|
|
297
|
+
},
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
- **Local mode** needs the `meilisearch` binary on `PATH` (a documented prerequisite, like Docker for `--docker` — OKE never auto-downloads binaries). `oke dev` spawns it with a generated master key persisted under `.oke/meilisearch/` (`0700`/`0600`).
|
|
301
|
+
- **Docker / prod** uses the image recipe; the app gets `OKE_STORE_INDEX_URL` + `OKE_STORE_INDEX_KEY` from Compose — a standalone HTTP service, never the shared `sqlUrl`.
|
|
302
|
+
- **Fail-loud:** a configured meilisearch that is unreachable or unhealthy throws `MeilisearchUnavailableError` — never a silent memory fallback.
|
|
303
|
+
- **Not for vectors:** `ai.embed` / `fx.search` stay vector-only; pointing an embed `into` a meilisearch index fails loud (embeddings don't apply).
|
|
304
|
+
- **Alpine caveat:** the raw glibc binary fails on Alpine/musl _hosts_; the official image is musl-clean. Install the binary for your platform (e.g. Homebrew / install script), or use `--docker`.
|
|
305
|
+
|
|
285
306
|
## Privacy built in
|
|
286
307
|
|
|
287
308
|
Columns tagged `.pii()` or `.sensitive()` are masked at the store boundary — flows, logs, and the Console see a mask, not the value. Revealing cleartext PII requires an explicit `pii:reveal` gate on the flow, so access is a permission, not a convention.
|
|
@@ -55,7 +55,9 @@ oke vault set STRIPE_KEY sk_test_your_key_here
|
|
|
55
55
|
<Step>
|
|
56
56
|
### Read it in a Flow
|
|
57
57
|
|
|
58
|
-
Flows read secrets through `fx.vault` — never through `process.env
|
|
58
|
+
Flows read secrets through `fx.vault` — never through `process.env`. `fx.vault` returns a
|
|
59
|
+
`Redacted<string>`: printing, logging, or serializing it yields a placeholder, never the value.
|
|
60
|
+
Call `.reveal()` once at the boundary that needs the real credential:
|
|
59
61
|
|
|
60
62
|
```typescript title="src/flows/billing/charge.ts"
|
|
61
63
|
export const charge = on(
|
|
@@ -64,14 +66,17 @@ export const charge = on(
|
|
|
64
66
|
in: ChargeInput,
|
|
65
67
|
out: z.object({ id: z.string() }),
|
|
66
68
|
do: async (input, fx) => {
|
|
67
|
-
const key = fx.vault(stripeKey); //
|
|
68
|
-
const intent = await stripe(key).create(input);
|
|
69
|
+
const key = fx.vault(stripeKey); // Redacted — safe to pass to fx.log
|
|
70
|
+
const intent = await stripe(key.reveal()).create(input);
|
|
69
71
|
return { id: intent.id };
|
|
70
72
|
},
|
|
71
73
|
}),
|
|
72
74
|
);
|
|
73
75
|
```
|
|
74
76
|
|
|
77
|
+
`fx.log` masks any `Redacted` found in the logged data (nested included). A revealed string that
|
|
78
|
+
still reaches `fx.log` is scrubbed by the boot redactor as before.
|
|
79
|
+
|
|
75
80
|
</Step>
|
|
76
81
|
|
|
77
82
|
</Steps>
|
|
@@ -111,13 +116,7 @@ export const dbUrl = vault.secret("DATABASE_URL", {
|
|
|
111
116
|
|
|
112
117
|
At boot, each contract is resolved through this chain — **first hit wins**:
|
|
113
118
|
|
|
114
|
-
|
|
115
|
-
| --- | -------------------- | ------------------------------------------------- |
|
|
116
|
-
| 1 | `process.env` | Real environment (CI, hosting platform) |
|
|
117
|
-
| 2 | `.env.local` | Your machine's local overrides (gitignored) |
|
|
118
|
-
| 3 | `docker/.env.docker` | Generated compose stack credentials |
|
|
119
|
-
| 4 | vault driver | OpenBao in docker/prod mode |
|
|
120
|
-
| 5 | `dev` fallback | The `dev:` option on the contract (never in prod) |
|
|
119
|
+
<VaultResolution />
|
|
121
120
|
|
|
122
121
|
If every layer misses a contract, boot **fails** before any request is served:
|
|
123
122
|
|
|
@@ -167,7 +166,7 @@ vault: {
|
|
|
167
166
|
| `openbao` | OpenBao (KV v2) | Docker + prod — durable, access-controlled |
|
|
168
167
|
| `memory` | in-process map | Tests |
|
|
169
168
|
|
|
170
|
-
Other drivers (`
|
|
169
|
+
Other drivers (`managed`) implement the same `VaultDriver` interface — adding one is a driver exercise, not an architecture change.
|
|
171
170
|
|
|
172
171
|
## OpenBao in docker and prod
|
|
173
172
|
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Basic Usage
|
|
3
|
-
description: Scaffold the standard starter,
|
|
3
|
+
description: Scaffold the standard starter, call the health Flow, and inspect it in the Console.
|
|
4
4
|
source: packages/create-oke/template
|
|
5
5
|
icon: SquareTerminal
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Write your first Flow, call it from a typed client, and prove it with
|
|
9
|
+
`bun:test` — using the same standard starter `create-oke` scaffolds.
|
|
10
|
+
|
|
11
|
+
<Callout title="The one rule">
|
|
12
|
+
All reads, writes, emits, sends, and external calls belong behind `fx`. That is what makes effects
|
|
13
|
+
inspectable and tests deterministic.
|
|
14
|
+
</Callout>
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
|
|
20
|
+
<Step>
|
|
21
|
+
### Scaffold and run
|
|
9
22
|
|
|
10
23
|
```bash title="Terminal"
|
|
11
24
|
bunx create-oke@latest my-app
|
|
@@ -13,26 +26,16 @@ cd my-app
|
|
|
13
26
|
oke dev
|
|
14
27
|
```
|
|
15
28
|
|
|
16
|
-
|
|
29
|
+
<Surfaces />
|
|
17
30
|
|
|
18
|
-
|
|
31
|
+
</Step>
|
|
19
32
|
|
|
20
|
-
|
|
33
|
+
<Step>
|
|
34
|
+
### Read the health Flow
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
on(Trigger) → Effects
|
|
24
|
-
```
|
|
36
|
+
The standard starter already exports a health Flow:
|
|
25
37
|
|
|
26
|
-
|
|
27
|
-
2. **Contracts** — `in`, `out`, and typed `errors`
|
|
28
|
-
3. **`do`** — the body; world access goes only through `fx`
|
|
29
|
-
4. **Effects** — inferred from the `fx` calls
|
|
30
|
-
|
|
31
|
-
## Your first Flow
|
|
32
|
-
|
|
33
|
-
The standard starter includes a health Flow:
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
38
|
+
```typescript title="flows/main"
|
|
36
39
|
import { on, flow, http } from "okengine";
|
|
37
40
|
import { z } from "zod";
|
|
38
41
|
|
|
@@ -45,57 +48,65 @@ export const health = on(
|
|
|
45
48
|
);
|
|
46
49
|
```
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
Change the route or output and save. App and Console update together from the
|
|
52
|
+
same Manifest.
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
All reads, writes, emits, sends, and external calls belong behind `fx`. This is what makes effects
|
|
53
|
-
inspectable and tests deterministic.
|
|
54
|
-
</Callout>
|
|
54
|
+
</Step>
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
<Step>
|
|
57
|
+
### Call it from a typed client
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
Adopted modules become namespaces on the client:
|
|
59
60
|
|
|
60
|
-
```typescript
|
|
61
|
+
```typescript title="app"
|
|
61
62
|
import { oke } from "okengine";
|
|
62
63
|
import * as main from "./flows/main";
|
|
63
64
|
|
|
64
|
-
export const app = oke({ name: "
|
|
65
|
+
export const app = oke({ name: "standard" }).adopt({ main });
|
|
65
66
|
export type App = typeof app;
|
|
66
67
|
```
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
a method.
|
|
70
|
-
|
|
71
|
-
## Typed client
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
69
|
+
```typescript title="client"
|
|
74
70
|
import { createClient } from "okengine/client";
|
|
75
|
-
import type { App } from "
|
|
71
|
+
import type { App } from "./app";
|
|
76
72
|
|
|
77
73
|
const api = createClient<App>("http://localhost:6530");
|
|
78
74
|
const { data, error } = await api.main.health({});
|
|
79
75
|
```
|
|
80
76
|
|
|
81
|
-
`data` and `error` are inferred from the Flow contracts
|
|
82
|
-
schema or
|
|
77
|
+
`data` and `error` are inferred from the Flow contracts — no separate client
|
|
78
|
+
schema or codegen project.
|
|
79
|
+
|
|
80
|
+
</Step>
|
|
81
|
+
|
|
82
|
+
</Steps>
|
|
83
|
+
|
|
84
|
+
## Mental model
|
|
85
|
+
|
|
86
|
+
Every backend behavior has one shape:
|
|
87
|
+
|
|
88
|
+
<FlowShape />
|
|
89
|
+
|
|
90
|
+
## From Flow to client
|
|
91
|
+
|
|
92
|
+
Export → adopt → call or test. Same App type — no separate codegen project.
|
|
93
|
+
|
|
94
|
+
<ClientLoop />
|
|
83
95
|
|
|
84
96
|
## Test
|
|
85
97
|
|
|
86
|
-
|
|
98
|
+
Boot the same app with test drivers:
|
|
87
99
|
|
|
88
|
-
```typescript
|
|
100
|
+
```typescript title="tests/standard.test.ts"
|
|
89
101
|
import { expect, test } from "bun:test";
|
|
90
102
|
import { createTestApp } from "okengine/test";
|
|
91
103
|
import { app } from "../src/app";
|
|
92
104
|
|
|
93
|
-
test("health", async () => {
|
|
105
|
+
test("boots — health flow", async () => {
|
|
94
106
|
const t = await createTestApp(app);
|
|
95
107
|
const { data, error } = await t.api.main.health({});
|
|
96
108
|
expect(error).toBeNull();
|
|
97
109
|
expect(data).toEqual({ ok: true });
|
|
98
|
-
await app.stop();
|
|
99
110
|
});
|
|
100
111
|
```
|
|
101
112
|
|
|
@@ -103,6 +114,30 @@ test("health", async () => {
|
|
|
103
114
|
bun test
|
|
104
115
|
```
|
|
105
116
|
|
|
117
|
+
## Troubleshooting
|
|
118
|
+
|
|
119
|
+
<Accordions>
|
|
120
|
+
|
|
121
|
+
<Accordion title="api.main.health is not a function / type error">
|
|
122
|
+
Confirm the Flow is `export`ed from the module you `.adopt({main})`, and that `createClient` is
|
|
123
|
+
typed with your `App` type. Restart `oke dev` after renaming exports so the Manifest refreshes.
|
|
124
|
+
</Accordion>
|
|
125
|
+
|
|
126
|
+
<Accordion title="createTestApp fails to boot">
|
|
127
|
+
The starter patches test env and stores on the app options. Keep the template's side-imports
|
|
128
|
+
(vault, gates, channels, core db) — removing them can leave Vault or Store unresolved under test
|
|
129
|
+
drivers.
|
|
130
|
+
</Accordion>
|
|
131
|
+
|
|
132
|
+
</Accordions>
|
|
133
|
+
|
|
134
|
+
## Learn more
|
|
135
|
+
|
|
136
|
+
- [Flow](/docs/elements/flow) — triggers, contracts, effects, and composition
|
|
137
|
+
- [Store](/docs/elements/store) — SQL, KV, files, index
|
|
138
|
+
- [fx](/docs/reference/fx) — the full effect surface
|
|
139
|
+
- [Console · Overview](/docs/console/overview) — Manifest-derived panels
|
|
140
|
+
|
|
106
141
|
## Next
|
|
107
142
|
|
|
108
143
|
<Cards>
|