okengine 0.1.4 → 0.2.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.
Files changed (84) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +48 -15
  3. package/docs/spec/{complete-example.md → example.md} +0 -1
  4. package/docs/spec/unified-theory.md +1 -1
  5. package/package.json +21 -8
  6. package/src/auth/index.ts +1 -0
  7. package/src/cli/dev.ts +3 -1
  8. package/src/cli/doc-drift.ts +54 -21
  9. package/src/client/index.ts +1 -0
  10. package/src/config/index.ts +1 -0
  11. package/src/console/index.ts +26 -13
  12. package/src/console/server/app.ts +44 -7
  13. package/src/console/server/flows.ts +2 -6
  14. package/src/console/server/index.ts +2 -1
  15. package/src/console/server/lazy-panels.test.ts +27 -0
  16. package/src/console/server/panel-load.ts +28 -0
  17. package/src/console/server/plugin.ts +1 -1
  18. package/src/console/server/plugins.ts +7 -6
  19. package/src/console/server/public-flows.ts +12 -0
  20. package/src/console/server/state.ts +159 -122
  21. package/src/console/server/store.ts +13 -10
  22. package/src/drivers/index.ts +2 -6
  23. package/src/drivers/vault-sops.ts +20 -1
  24. package/src/elements/ai.ts +1 -0
  25. package/src/elements/channel.ts +1 -0
  26. package/src/elements/clock.ts +1 -0
  27. package/src/elements/gate.ts +1 -0
  28. package/src/elements/signal.ts +1 -0
  29. package/src/elements/store.ts +1 -0
  30. package/src/elements/vault.ts +1 -0
  31. package/src/index.ts +9 -0
  32. package/src/kernel/app.ts +1 -1
  33. package/src/kernel/boot-bind/ai.ts +31 -0
  34. package/src/kernel/boot-bind/channel.ts +27 -0
  35. package/src/kernel/boot-bind/clock.ts +74 -0
  36. package/src/kernel/boot-bind/gate.ts +28 -0
  37. package/src/kernel/boot-bind/runs.ts +28 -0
  38. package/src/kernel/boot-bind/signal.ts +68 -0
  39. package/src/kernel/boot-bind/store.ts +47 -0
  40. package/src/kernel/boot-bind/vault.ts +36 -0
  41. package/src/kernel/boot.test.ts +85 -1
  42. package/src/kernel/boot.ts +250 -212
  43. package/src/kernel/index.ts +2 -0
  44. package/src/mcp/data.ts +1 -0
  45. package/src/mcp/docs-index.ts +252 -0
  46. package/src/mcp/docs-mcp.test.ts +176 -0
  47. package/src/mcp/docs-server.ts +233 -0
  48. package/src/mcp/docs-tools.ts +143 -0
  49. package/src/mcp/index.ts +30 -5
  50. package/src/mcp/protocol.ts +2 -1
  51. package/src/release/exports.test.ts +71 -0
  52. package/src/release/exports.ts +156 -0
  53. package/src/release/index.ts +21 -0
  54. package/src/release/limits.ts +9 -0
  55. package/src/release/measure.exports.test.ts +82 -0
  56. package/src/release/measure.ts +297 -14
  57. package/src/release/publish.ts +14 -3
  58. package/src/release/readme.test.ts +61 -0
  59. package/src/release/readme.ts +13 -0
  60. package/src/runs/index.ts +1 -0
  61. package/src/runtime/index.ts +1 -0
  62. package/src/runtime/security.test.ts +6 -1
  63. package/src/runtime/types.ts +6 -0
  64. package/src/test/create-test-app.ts +2 -0
  65. package/src/test/index.ts +1 -0
  66. package/src/console/ui/dist/assets/index-B71Yl_SS.js +0 -10
  67. package/src/console/ui/dist/assets/panel-access-Dd37LU2c.js +0 -64
  68. package/src/console/ui/dist/assets/panel-ai-CC7LR6-J.js +0 -1
  69. package/src/console/ui/dist/assets/panel-architecture-B5b3iKCz.js +0 -1
  70. package/src/console/ui/dist/assets/panel-channels-CeNjTKXp.js +0 -1
  71. package/src/console/ui/dist/assets/panel-clock-DgFTLoHV.js +0 -1
  72. package/src/console/ui/dist/assets/panel-diff-DxehccqB.js +0 -1
  73. package/src/console/ui/dist/assets/panel-flows-BtrVn-Eg.js +0 -45
  74. package/src/console/ui/dist/assets/panel-gates-Z9MKRGdH.js +0 -1
  75. package/src/console/ui/dist/assets/panel-overview-Bd48d9km.js +0 -1
  76. package/src/console/ui/dist/assets/panel-plugins-DWd0TowH.js +0 -1
  77. package/src/console/ui/dist/assets/panel-runs-BwsWqKeB.js +0 -1
  78. package/src/console/ui/dist/assets/panel-signals-9najbZY2.js +0 -1
  79. package/src/console/ui/dist/assets/panel-store-OHkP2pDp.js +0 -1
  80. package/src/console/ui/dist/assets/panel-traces-tn2JoY8U.js +0 -1
  81. package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +0 -1
  82. package/src/console/ui/dist/assets/rolldown-runtime-CNC7AqOf.js +0 -1
  83. package/src/console/ui/dist/assets/style-Cnl7WLya.css +0 -3
  84. package/src/console/ui/dist/index.html +0 -14
package/AGENTS.md CHANGED
@@ -76,7 +76,7 @@ A regression fails the build. Claims we cannot measure, we do not make.
76
76
 
77
77
  Published packages:
78
78
  - `okengine` — framework. Subpath exports: `.`, `./client`, `./test`, `./config`, `./auth`, `./drivers/*`. `"sideEffects": false`. CLI binary: `oke`.
79
- - `create-oke` — scaffold CLI (`bunx create-oke@latest <name>`). Lives in `packages/create-oke`; templates are the `examples/*` trees.
79
+ - `create-oke` — scaffold CLI (`bunx create-oke@latest <name>`). Lives in `packages/create-oke`; clean starters are `templates/*`, teaching apps stay in `examples/*` (`--from-example`).
80
80
 
81
81
  Engine: Bun `>=1.3`.
82
82
 
package/README.md CHANGED
@@ -2,13 +2,21 @@
2
2
 
3
3
  *"Encore's batteries and dashboard, Elysia's speed and DX, Hono's portability — without the Rust lock-in, the cloud gravity, or the source-available license."*
4
4
 
5
+ [![npm](https://img.shields.io/npm/v/okengine.svg)](https://www.npmjs.com/package/okengine)
6
+ [![JSR](https://jsr.io/badges/@omqkhafi/okengine)](https://jsr.io/@omqkhafi/okengine)
7
+ [![npm](https://img.shields.io/npm/v/create-oke.svg)](https://www.npmjs.com/package/create-oke)
5
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
9
  [![Bun >=1.3](https://img.shields.io/badge/Bun-%3E%3D1.3-black.svg)](https://bun.sh)
7
10
 
8
- **Package:** `okengine` · **CLI:** `oke` · **License:** MIT
11
+ **Framework:** [`okengine`](https://www.npmjs.com/package/okengine) · **CLI:** `oke` · **Scaffold:** [`create-oke`](https://www.npmjs.com/package/create-oke) · **Docs:** [oke.dev](https://oke.dev) · **License:** MIT
9
12
 
10
- JSR (`@omqkhafi/okengine`) does not support npm-style `exports` wildcards — import deep drivers (`okengine/drivers/…`) from the npm package, not JSR. The `okengine/drivers` index is available on both.
13
+ ### Install
11
14
 
15
+ ```bash
16
+ bun add okengine # npm (recommended — ships the `oke` CLI)
17
+ bunx jsr add @omqkhafi/okengine # JSR — library API only
18
+ bunx create-oke@latest my-app --template hello # scaffold from npm
19
+ ```
12
20
  ---
13
21
 
14
22
  ## Start here
@@ -27,14 +35,14 @@ Eight elements cover what a backend needs. Ten exports are the whole public voca
27
35
  import { on, flow, signal, store, clock, gate, vault, channel, ai, plugin } from "okengine";
28
36
  ```
29
37
 
30
- Batteries included for the Bun era: contract-first APIs, typed client, infrastructure primitives, Console, auth — pure TypeScript, MIT, self-hostable, no cloud lock-in. Detail and comparisons live in [`docs/spec/unified-theory.md`](docs/spec/unified-theory.md).
38
+ Batteries included for the Bun era: contract-first APIs, typed client, infrastructure primitives, Console, auth — pure TypeScript, MIT, self-hostable, no cloud lock-in. Handbook: [oke.dev/docs](https://oke.dev/docs). Spec depth: [`docs/spec/unified-theory.md`](https://github.com/omqkhafi/okengine/blob/main/docs/spec/unified-theory.md).
31
39
 
32
40
  ---
33
41
 
34
42
  ## Run something in two minutes
35
43
 
36
44
  ```bash
37
- bunx create-oke@latest my-app
45
+ bunx create-oke@latest my-app --template hello
38
46
  cd my-app
39
47
  oke dev
40
48
  ```
@@ -43,9 +51,13 @@ oke dev
43
51
  |---|---|
44
52
  | `:6530` | your app |
45
53
  | `:6533` | Console |
46
- | `:6535` | MCP |
54
+ | `:6535` | MCP (your live Manifest, for agents) |
55
+
56
+ Mnemonic: **O·K·E = 6·5·3**. Docs MCP for the handbook is a separate surface on `:6536` (search/fetch only — see [Documentation](#documentation)).
57
+
58
+ That scaffolds the **hello** starter — one flow, no Store, the fastest true "it works" moment. Default without `--template` is **standard** (full recommended file layout, empty scaffolding). Open the Console — flows, contracts, effects, and an architecture diagram are already there. Derived, not configured.
47
59
 
48
- That scaffolds the **Notes** app (same tree as [`examples/notes`](examples/notes)). Open the Console — flows, contracts, effects, and an architecture diagram are already there. Derived, not configured.
60
+ Prefer a worked teaching app (business logic + comments)? Use `--from-example notes|linkly|provisions|skyport` instead.
49
61
 
50
62
  ### The idea, in one file
51
63
 
@@ -119,7 +131,9 @@ The rest of the scaffold is small and ordinary: `oke.config.ts` (drivers by prot
119
131
 
120
132
  ## How to learn (read in order)
121
133
 
122
- Each app adds the smallest next set of ideas. Do not skip ahead.
134
+ Start on the site if you want the handbook path: [Introduction](https://oke.dev/docs/get-started/introduction) [Installation](https://oke.dev/docs/get-started/installation) [Basic usage](https://oke.dev/docs/get-started/basic-usage).
135
+
136
+ Each teaching app adds the smallest next set of ideas. Do not skip ahead.
123
137
 
124
138
  1. **Notes** — `oke`, `on`, `flow`, `http`, `store.sql`, `fx`, typed errors, typed client
125
139
  → [`examples/notes`](examples/notes)
@@ -133,7 +147,17 @@ Each app adds the smallest next set of ideas. Do not skip ahead.
133
147
  4. **Skyport** — `ai`, multi-tenancy, SLOs, distributed topology, scaling axes
134
148
  → [`examples/skyport`](examples/skyport)
135
149
 
136
- Spec that teaches the same path: [`docs/spec/four-applications.md`](docs/spec/four-applications.md).
150
+ Same path in the spec: [`docs/spec/four-applications.md`](docs/spec/four-applications.md).
151
+
152
+ ---
153
+
154
+ ## Documentation
155
+
156
+ - **Site** — [oke.dev](https://oke.dev) (landing) · [oke.dev/docs](https://oke.dev/docs) (handbook) · [changelog](https://oke.dev/changelog)
157
+ - **Agents on the site** — [`/llms.txt`](https://oke.dev/llms.txt) index · [`/llms-full.txt`](https://oke.dev/llms-full.txt) full text · `/llms.mdx/docs/...` per-page markdown
158
+ - **Docs MCP** — `:6536` · tools `oke.docs.search` / `oke.docs.get` (read-only; Host/Origin validated like the other surfaces)
159
+ - **App MCP** — `:6535` · live Manifest, schemas, effects, traces, Console-safe actions (Bearer + confirmation for writes)
160
+ - **Canonical specs in-repo** — [`docs/spec/`](docs/spec) (unified theory, four applications, Console)
137
161
 
138
162
  ---
139
163
 
@@ -180,7 +204,7 @@ flowchart TD
180
204
 
181
205
  Seventeen panels on `:6533` (app `:6530`, MCP `:6535`): Overview, Flows, Signals, Store, Clock, Gates, Vault, Channels, AI, Architecture, Traces, Runs, Manifest Diff, Access, Plugins — plus Privacy and Tenancy when those plugins are plugged.
182
206
 
183
- Operator and user planes stay separate. Every Console action is a real flow through `fx`, so the audit log is the trace. [`docs/spec/console.md`](docs/spec/console.md)
207
+ Operator and user planes stay separate. Every Console action is a real flow through `fx`, so the audit log is the trace. Spec: [`docs/spec/console.md`](docs/spec/console.md) · handbook: [oke.dev/docs/console](https://oke.dev/docs/console/overview).
184
208
 
185
209
  ---
186
210
 
@@ -302,7 +326,7 @@ Long form is canonical in docs; short form is convenience only. Shared letters f
302
326
 
303
327
  `.plug()` attaches a plugin. Scope is the attachment point: `app.plug()` app-wide, `unit.plug()` one unit, `flow.plug()` one flow. No `global: true`, no inheritance rule — [unified theory §14](docs/spec/unified-theory.md#14-plugins--the-extensibility-law).
304
328
 
305
- The Console is treated as internet-facing even on localhost. Host header validation, Origin validation, and authentication are mandatory on `:6530`, `:6533`, and `:6535` ([console.md §10](docs/spec/console.md#10-security-posture)): two-plane auth (operator vs user), capability-scoped tokens.
329
+ The Console is treated as internet-facing even on localhost. Host header validation, Origin validation, and authentication are mandatory on `:6530`, `:6533`, and `:6535` ([console.md §10](docs/spec/console.md#10-security-posture)): two-plane auth (operator vs user), capability-scoped tokens. Docs MCP `:6536` uses the same Host/Origin checks and stays read-only (no Bearer writes).
306
330
 
307
331
  ---
308
332
 
@@ -316,12 +340,21 @@ The Console is treated as internet-facing even on localhost. Host header validat
316
340
 
317
341
  ---
318
342
 
343
+ ## Budgets
344
+
345
+ Measured size and latency caps — see [`BUDGETS.md`](BUDGETS.md). Refresh with `bun run budgets`.
346
+
347
+ ---
348
+
319
349
  ## Status
320
350
 
321
- Pre-1.0. MIT. No `CONTRIBUTING` yet — issues and PRs welcome.
351
+ Pre-1.0. Published on [npm](https://www.npmjs.com/package/okengine) and [JSR](https://jsr.io/@omqkhafi/okengine) (`okengine` + `create-oke`, lockstep). Docs at [oke.dev](https://oke.dev). MIT. No `CONTRIBUTING` yet — issues and PRs welcome.
322
352
 
323
- | Spec | Path |
353
+ | Resource | Path |
324
354
  |---|---|
325
- | Unified theory | [`docs/spec/unified-theory.md`](docs/spec/unified-theory.md) |
326
- | Four applications | [`docs/spec/four-applications.md`](docs/spec/four-applications.md) |
327
- | Console | [`docs/spec/console.md`](docs/spec/console.md) |
355
+ | Docs site | [oke.dev](https://oke.dev) · [handbook](https://oke.dev/docs) · [changelog](https://oke.dev/changelog) |
356
+ | Unified theory | [`docs/spec/unified-theory.md`](https://github.com/omqkhafi/okengine/blob/main/docs/spec/unified-theory.md) |
357
+ | Four applications | [`docs/spec/four-applications.md`](https://github.com/omqkhafi/okengine/blob/main/docs/spec/four-applications.md) |
358
+ | Console | [`docs/spec/console.md`](https://github.com/omqkhafi/okengine/blob/main/docs/spec/console.md) |
359
+ | Scaffold | [`packages/create-oke`](https://github.com/omqkhafi/okengine/tree/main/packages/create-oke) |
360
+ | Changelog source | [`docs/changelog.md`](docs/changelog.md) |
@@ -1149,7 +1149,6 @@ oke images pin # tags → digests in oke.images.lock
1149
1149
 
1150
1150
  oke build --target edge # < 15 kB kernel profile
1151
1151
  oke eval # run prompt eval sets; fails CI on regression
1152
- oke ai cost --since 7d # cost per flow, per tenant, per prompt version
1153
1152
  oke branch prod --at "yesterday" # fork journaled state into a sandbox
1154
1153
  oke privacy erase --subject <id> # crypto-shredding: deletes the key, not the terabytes
1155
1154
  oke upgrade # run codemods for a breaking change, print the diff
@@ -416,7 +416,7 @@ Plus `oke import express|hono|elysia` codemods for the mechanical parts. Migrati
416
416
  |---|---|
417
417
  | Kernel (edge profile) | < 15 kB |
418
418
  | Client runtime | < 3 kB |
419
- | Cold start on Bun | < 75 ms |
419
+ | Cold start on Bun | < 75 ms in CI (GitHub Actions runners are less predictable than a dev machine); measured ~25–30 ms on real hardware |
420
420
  | p99 routing overhead | < 1 ms |
421
421
 
422
422
  A regression fails OKE's own build. Claims we cannot measure, we do not make.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okengine",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
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
  "type": "module",
@@ -10,7 +10,9 @@
10
10
  "url": "git+https://github.com/omqkhafi/okengine.git"
11
11
  },
12
12
  "workspaces": [
13
- "examples/*"
13
+ "packages/*",
14
+ "examples/*",
15
+ "site"
14
16
  ],
15
17
  "scripts": {
16
18
  "typecheck": "tsc --noEmit",
@@ -44,10 +46,13 @@
44
46
  "gate:errors": "bun test src/kernel/errors.registry.test.ts",
45
47
  "gate:codemods": "bun test src/upgrade/codemods.test.ts",
46
48
  "test:examples": "bun test examples/notes && bun test examples/linkly && bun test examples/provisions && bun test examples/skyport",
47
- "test:create-oke": "bun run --cwd packages/create-oke typecheck && bun test packages/create-oke/src && CREATE_OKE_INTEGRATION=1 bun test packages/create-oke/tests/scaffold.integration.test.ts packages/create-oke/tests/dev.integration.test.ts",
49
+ "test:create-oke": "bun run --cwd packages/create-oke typecheck && bun test packages/create-oke/src && CREATE_OKE_INTEGRATION=1 bun test --timeout 180000 packages/create-oke/tests/scaffold.integration.test.ts packages/create-oke/tests/dev.integration.test.ts",
48
50
  "test:doc-drift": "bun src/cli/doc-drift.ts",
51
+ "site:sync": "bun run --cwd site sync",
52
+ "site:dev": "bun run --cwd site dev",
53
+ "site:build": "bun run --cwd site build",
49
54
  "test:skyport-docker": "bun test examples/skyport/tests/docker.test.ts",
50
- "ci": "bun run bench:cold-start && bun run typecheck && bun run test && bun run budgets && bun run gate:doctor-diff && bun run gate:errors && bun run gate:codemods && bun run test:doc-drift && bun run test:examples && bun run test:skyport-docker && bun run test:create-oke",
55
+ "ci": "bun run bench:cold-start && bun run typecheck && bun run test && bun run budgets && bun run gate:doctor-diff && bun run gate:errors && bun run gate:codemods && bun run test:doc-drift && bun run site:build && bun run test:examples && bun run test:skyport-docker && bun run test:create-oke",
51
56
  "gate:publish": "bun test scripts/publish.gate.test.ts",
52
57
  "bump": "bun run scripts/bump-version.ts",
53
58
  "publish:all": "bun run scripts/publish.ts",
@@ -60,13 +65,13 @@
60
65
  },
61
66
  "dependencies": {
62
67
  "@duckdb/node-api": "^1.5.5-r.1",
63
- "age-encryption": "^0.3.0",
64
68
  "ajv": "^8.20.0",
65
69
  "ajv-formats": "^3.0.1",
66
70
  "oxc-parser": "^0.141.0",
67
71
  "sently": "^0.8.0"
68
72
  },
69
73
  "devDependencies": {
74
+ "age-encryption": "^0.3.0",
70
75
  "@base-ui/react": "^1.6.0",
71
76
  "@codemirror/commands": "^6.10.4",
72
77
  "@codemirror/lang-json": "^6.0.2",
@@ -103,11 +108,16 @@
103
108
  "zod": "^4.4.3"
104
109
  },
105
110
  "peerDependencies": {
111
+ "age-encryption": "^0.3.0",
106
112
  "drizzle-kit": ">=1.0.0-rc.0",
107
113
  "drizzle-orm": ">=1.0.0-rc.0",
108
- "drizzle-seed": ">=0.3.0"
114
+ "drizzle-seed": ">=0.3.0",
115
+ "zod": ">=3.23.0"
109
116
  },
110
117
  "peerDependenciesMeta": {
118
+ "age-encryption": {
119
+ "optional": true
120
+ },
111
121
  "drizzle-kit": {
112
122
  "optional": true
113
123
  },
@@ -116,13 +126,16 @@
116
126
  },
117
127
  "drizzle-seed": {
118
128
  "optional": true
129
+ },
130
+ "zod": {
131
+ "optional": true
119
132
  }
120
133
  },
121
134
  "engines": {
122
135
  "bun": ">=1.3"
123
136
  },
124
137
  "bin": {
125
- "oke": "./src/cli/index.ts"
138
+ "oke": "src/cli/index.ts"
126
139
  },
127
140
  "exports": {
128
141
  ".": "./src/index.ts",
@@ -149,4 +162,4 @@
149
162
  "AGENTS.md",
150
163
  "docs/spec"
151
164
  ]
152
- }
165
+ }
package/src/auth/index.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  * plus a provider seam. Subpath: `okengine/auth`.
4
4
  *
5
5
  * Two planes — `fx.operator` vs `fx.auth` — permanently separated.
6
+ * @module
6
7
  */
7
8
 
8
9
  export { auth, type AuthPluginOptions } from "./plugin.ts";
package/src/cli/dev.ts CHANGED
@@ -500,7 +500,9 @@ async function startAppHot(
500
500
  if (stopped) return;
501
501
  stopped = true;
502
502
  try {
503
- proc.kill();
503
+ // SIGKILL so `bun --hot` cannot linger and hold the project directory
504
+ // open (create-oke afterEach `rmSync` otherwise races the child exit).
505
+ proc.kill("SIGKILL");
504
506
  } catch {
505
507
  // already exited
506
508
  }
@@ -14,6 +14,10 @@
14
14
  * Usage:
15
15
  * bun src/cli/doc-drift.ts
16
16
  * bun src/cli/doc-drift.ts docs/spec/four-applications.md README.md
17
+ * bun src/cli/doc-drift.ts site/content/docs/learn/notes.md
18
+ *
19
+ * Default paths: four-applications.md, README.md, and every markdown/MDX file
20
+ * under site/content/docs (claimed fences only — prose-only pages skip).
17
21
  */
18
22
 
19
23
  import { resolve } from "node:path";
@@ -21,8 +25,27 @@ import { Window } from "happy-dom";
21
25
 
22
26
  const ROOT = resolve(import.meta.dir, "../..");
23
27
 
24
- /** Default docs checked when no CLI paths are passed. */
25
- const DEFAULT_DOCS: readonly string[] = [
28
+ /**
29
+ * Discover site content markdown that may contain claimed fences.
30
+ *
31
+ * @param root - Repo root
32
+ */
33
+ async function discoverSiteDocs(root: string): Promise<string[]> {
34
+ const docsDir = resolve(root, "site/content/docs");
35
+ const out: string[] = [];
36
+ try {
37
+ const glob = new Bun.Glob("**/*.{md,mdx}");
38
+ for await (const rel of glob.scan({ cwd: docsDir, onlyFiles: true })) {
39
+ out.push(resolve(docsDir, rel));
40
+ }
41
+ } catch {
42
+ // site/ not present yet — skip
43
+ }
44
+ return out.sort();
45
+ }
46
+
47
+ /** Core docs that must contain claimed fences. */
48
+ const REQUIRED_DOCS: readonly string[] = [
26
49
  resolve(ROOT, "docs/spec/four-applications.md"),
27
50
  resolve(ROOT, "README.md"),
28
51
  ];
@@ -315,6 +338,9 @@ export async function checkDocDrift(
315
338
  /**
316
339
  * Run doc-drift over one or more markdown files.
317
340
  *
341
+ * Site content under `site/` may have zero claimed fences (prose-only pages);
342
+ * only `docs/spec/four-applications.md` and `README.md` require at least one.
343
+ *
318
344
  * @param paths - Absolute or repo-relative markdown paths
319
345
  */
320
346
  export async function runDocDrift(
@@ -324,32 +350,36 @@ export async function runDocDrift(
324
350
  let ok = true;
325
351
  let total = 0;
326
352
  let mermaidTotal = 0;
353
+ let checkedFiles = 0;
327
354
 
328
355
  for (const path of paths) {
329
356
  const abs = resolve(ROOT, path);
330
357
  const label = abs.startsWith(ROOT + "/")
331
358
  ? abs.slice(ROOT.length + 1)
332
359
  : abs;
360
+ const requireClaimed = !label.startsWith("site/");
333
361
  const markdown = await Bun.file(abs).text();
334
362
  const fences = parseClaimedFences(markdown);
335
363
  if (fences.length === 0) {
336
- messages.push(`doc-drift: no claimed fences found in ${label}`);
337
- ok = false;
338
- continue;
339
- }
340
-
341
- const result = await checkDocDrift(fences);
342
- total += fences.length;
343
- if (!result.ok) {
344
- ok = false;
345
- messages.push(
346
- `doc-drift: ${result.failures.length} failure(s) in ${label}`,
347
- );
348
- for (const f of result.failures) messages.push(` · ${f}`);
364
+ if (requireClaimed) {
365
+ messages.push(`doc-drift: no claimed fences found in ${label}`);
366
+ ok = false;
367
+ }
349
368
  } else {
350
- messages.push(
351
- `doc-drift: ok ${fences.length} claimed fence(s) in ${label}`,
352
- );
369
+ checkedFiles++;
370
+ const result = await checkDocDrift(fences);
371
+ total += fences.length;
372
+ if (!result.ok) {
373
+ ok = false;
374
+ messages.push(
375
+ `doc-drift: ${result.failures.length} failure(s) in ${label}`,
376
+ );
377
+ for (const f of result.failures) messages.push(` · ${f}`);
378
+ } else {
379
+ messages.push(
380
+ `doc-drift: ok — ${fences.length} claimed fence(s) in ${label}`,
381
+ );
382
+ }
353
383
  }
354
384
 
355
385
  const mermaidFences = parseMermaidFences(markdown);
@@ -370,11 +400,11 @@ export async function runDocDrift(
370
400
  }
371
401
  }
372
402
 
373
- if (ok && paths.length > 1) {
403
+ if (ok && checkedFiles > 1) {
374
404
  const mermaidNote =
375
405
  mermaidTotal > 0 ? ` · ${mermaidTotal} mermaid fence(s)` : "";
376
406
  messages.push(
377
- `doc-drift: ok — ${total} claimed fence(s) across ${paths.length} file(s)${mermaidNote}`,
407
+ `doc-drift: ok — ${total} claimed fence(s) across ${checkedFiles} file(s)${mermaidNote}`,
378
408
  );
379
409
  }
380
410
 
@@ -383,7 +413,10 @@ export async function runDocDrift(
383
413
 
384
414
  async function main(): Promise<number> {
385
415
  const args = process.argv.slice(2);
386
- const paths = args.length > 0 ? args : DEFAULT_DOCS;
416
+ const paths =
417
+ args.length > 0
418
+ ? args
419
+ : [...REQUIRED_DOCS, ...(await discoverSiteDocs(ROOT))];
387
420
  const { ok, messages } = await runDocDrift(paths);
388
421
  for (const m of messages) {
389
422
  if (ok) console.log(m);
@@ -2,6 +2,7 @@
2
2
  * Typed client (+ live). Subpath: `okengine/client`.
3
3
  *
4
4
  * First Manifest derivation: `type App` → fully typed client, zero codegen.
5
+ * @module
5
6
  */
6
7
 
7
8
  export { createClient, flattenRoutes } from "./create.ts";
@@ -2,6 +2,7 @@
2
2
  * `oke.config.ts` surface. Subpath: `okengine/config`.
3
3
  *
4
4
  * Driver maps are protocol-named; vendor/image choice lives in `images`.
5
+ * @module
5
6
  */
6
7
 
7
8
  /** Environment role keys used in driver maps. */
@@ -3,28 +3,41 @@
3
3
  *
4
4
  * Built on `createClient<ConsoleApp>`. Every action is a real flow through `fx`;
5
5
  * the audit log is the trace (docs/spec/console.md).
6
+ *
7
+ * Imports leaf modules (not `server/index`) so unused panel projectors stay out
8
+ * of the `okengine/console` graph when tree-shaken.
9
+ *
10
+ * @module
6
11
  */
7
12
 
8
13
  export {
9
14
  bootConsoleApp,
15
+ createConsoleApp,
16
+ type ConsoleApp,
17
+ type ConsoleAppHandle,
18
+ type CreateConsoleAppOptions,
19
+ } from "./server/app.ts";
20
+ export {
10
21
  CLAIM_TTL_MS,
22
+ mintClaimCode,
23
+ printClaimCodeOnce,
24
+ verifyClaimCode,
25
+ } from "./server/claim.ts";
26
+ export { feedManifest } from "./server/live.ts";
27
+ export { consolePlugin } from "./server/plugin.ts";
28
+ export {
11
29
  CONSOLE_COOKIES,
12
30
  CONSOLE_CSP,
13
31
  PLUGIN_IFRAME_SANDBOX,
14
- consolePlugin,
15
- createConsoleApp,
16
- createConsoleState,
17
- feedManifest,
18
- mintClaimCode,
19
- printClaimCodeOnce,
32
+ withConsoleSecurityHeaders,
33
+ } from "./server/security-headers.ts";
34
+ export {
20
35
  serveConsole,
21
36
  startConsoleApp,
22
- verifyClaimCode,
23
- withConsoleSecurityHeaders,
24
- type ConsoleApp,
25
- type ConsoleAppHandle,
26
37
  type ConsoleServerHandle,
27
- type ConsoleState,
28
- type CreateConsoleAppOptions,
29
38
  type ServeConsoleOptions,
30
- } from "./server/index.ts";
39
+ } from "./server/serve.ts";
40
+ export {
41
+ createConsoleState,
42
+ type ConsoleState,
43
+ } from "./server/state.ts";
@@ -140,16 +140,53 @@ export async function bootConsoleApp(
140
140
  if (!runs) return [];
141
141
  return runs.all();
142
142
  };
143
- await bindManifestSignalBus(handle.state);
144
- await bindManifestStoreRuntime(handle.state);
145
- await bindManifestVaultRuntime(handle.state);
146
- await bindManifestGateRuntime(handle.state);
147
- await bindManifestClockRuntime(handle.state);
148
- bindManifestAiRuntime(handle.state);
149
- bindManifestChannelRuntime(handle.state);
143
+ // Element runtimes bind lazily on first panel access (see ensure* below
144
+ // via list* methods / bindManifest* callers) — not all seventeen at boot.
150
145
  return handle.app;
151
146
  }
152
147
 
148
+ /**
149
+ * Ensure Manifest-backed element runtimes for a panel are bound (idempotent).
150
+ *
151
+ * @param state - Console state
152
+ * @param panel - Panel id that was visited
153
+ */
154
+ export async function ensureConsolePanelRuntimes(
155
+ state: ConsoleState,
156
+ panel:
157
+ | "signals"
158
+ | "store"
159
+ | "vault"
160
+ | "gates"
161
+ | "clock"
162
+ | "ai"
163
+ | "channels",
164
+ ): Promise<void> {
165
+ switch (panel) {
166
+ case "signals":
167
+ await bindManifestSignalBus(state);
168
+ break;
169
+ case "store":
170
+ await bindManifestStoreRuntime(state);
171
+ break;
172
+ case "vault":
173
+ await bindManifestVaultRuntime(state);
174
+ break;
175
+ case "gates":
176
+ await bindManifestGateRuntime(state);
177
+ break;
178
+ case "clock":
179
+ await bindManifestClockRuntime(state);
180
+ break;
181
+ case "ai":
182
+ bindManifestAiRuntime(state);
183
+ break;
184
+ case "channels":
185
+ bindManifestChannelRuntime(state);
186
+ break;
187
+ }
188
+ }
189
+
153
190
  /**
154
191
  * Bind a ChannelRuntime + console inbox from the Manifest when no host
155
192
  * runtime is attached — Console surfaces real receipts / inbox, not mocks.
@@ -30,14 +30,10 @@ import {
30
30
  } from "./clock.ts";
31
31
  import { createFileDiff, emitStructuralDiff } from "./structural.ts";
32
32
  import type { ConsoleState } from "./state.ts";
33
+ import { PUBLIC_CONSOLE_FLOWS } from "./public-flows.ts";
33
34
  import { tenancyDeclared } from "./store.ts";
34
35
 
35
- /** Flows that may run without an operator session. */
36
- export const PUBLIC_CONSOLE_FLOWS = new Set([
37
- "console.setup.status",
38
- "console.setup.claim",
39
- "console.session.login",
40
- ]);
36
+ export { PUBLIC_CONSOLE_FLOWS };
41
37
 
42
38
  const SetupStatusOut = z.object({
43
39
  setupClosed: z.boolean(),
@@ -30,6 +30,7 @@ export {
30
30
  bindManifestVaultRuntime,
31
31
  bootConsoleApp,
32
32
  createConsoleApp,
33
+ ensureConsolePanelRuntimes,
33
34
  type ConsoleApp,
34
35
  type ConsoleAppHandle,
35
36
  type CreateConsoleAppOptions,
@@ -72,8 +73,8 @@ export {
72
73
  } from "./claim.ts";
73
74
  export {
74
75
  createConsoleBindings,
75
- PUBLIC_CONSOLE_FLOWS,
76
76
  } from "./flows.ts";
77
+ export { PUBLIC_CONSOLE_FLOWS } from "./public-flows.ts";
77
78
  export {
78
79
  createLiveWebsocket,
79
80
  feedManifest,
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Console per-panel backends construct lazily on first access.
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { createConsoleState } from "./state.ts";
7
+
8
+ describe("console — lazy panel construction", () => {
9
+ test("createConsoleState constructs no panel backends", () => {
10
+ const state = createConsoleState({ silentClaim: true, secret: "x" });
11
+ expect([...state.constructedPanels]).toEqual([]);
12
+ });
13
+
14
+ test("only the visited panel is constructed", async () => {
15
+ const state = createConsoleState({ silentClaim: true, secret: "x" });
16
+ expect([...state.constructedPanels]).toEqual([]);
17
+
18
+ await state.listVault();
19
+ expect([...state.constructedPanels]).toEqual(["vault"]);
20
+
21
+ await state.listStores();
22
+ expect([...state.constructedPanels].sort()).toEqual(["store", "vault"]);
23
+
24
+ await state.listVault();
25
+ expect([...state.constructedPanels].sort()).toEqual(["store", "vault"]);
26
+ });
27
+ });
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Lazy Console panel module loader — expression `new URL` keeps panel
3
+ * backends out of the createConsoleState / createConsoleApp graph until
4
+ * first access (mirrors kernel boot-bind).
5
+ */
6
+
7
+ /** Panel ids that own server-side projection / mutation modules. */
8
+ export type ConsolePanelId =
9
+ | "access"
10
+ | "gates"
11
+ | "signals"
12
+ | "store"
13
+ | "clock"
14
+ | "vault"
15
+ | "ai"
16
+ | "channels"
17
+ | "plugins"
18
+ | "diff";
19
+
20
+ /**
21
+ * Load a Console panel module by id without bundling it into the parent.
22
+ *
23
+ * @param id - Panel module stem (`store`, `vault`, …)
24
+ */
25
+ export async function loadConsolePanel<T>(id: ConsolePanelId): Promise<T> {
26
+ const url = new URL(`./${id}.ts`, import.meta.url);
27
+ return import(url.href) as Promise<T>;
28
+ }
@@ -10,7 +10,7 @@ import {
10
10
  type Fx,
11
11
  type PluginDef,
12
12
  } from "../../kernel/index.ts";
13
- import { PUBLIC_CONSOLE_FLOWS } from "./flows.ts";
13
+ import { PUBLIC_CONSOLE_FLOWS } from "./public-flows.ts";
14
14
 
15
15
  /**
16
16
  * Builtin Console plugin.