okengine 0.1.6 → 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 (52) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +36 -29
  3. package/docs/spec/example.md +1187 -0
  4. package/docs/spec/unified-theory.md +1 -1
  5. package/package.json +19 -6
  6. package/src/cli/dev.ts +3 -1
  7. package/src/cli/doc-drift.ts +54 -21
  8. package/src/console/index.ts +25 -13
  9. package/src/console/server/app.ts +44 -7
  10. package/src/console/server/flows.ts +2 -6
  11. package/src/console/server/index.ts +2 -1
  12. package/src/console/server/lazy-panels.test.ts +27 -0
  13. package/src/console/server/panel-load.ts +28 -0
  14. package/src/console/server/plugin.ts +1 -1
  15. package/src/console/server/plugins.ts +7 -6
  16. package/src/console/server/public-flows.ts +12 -0
  17. package/src/console/server/state.ts +159 -122
  18. package/src/console/server/store.ts +13 -10
  19. package/src/drivers/index.ts +1 -6
  20. package/src/drivers/vault-sops.ts +20 -1
  21. package/src/kernel/app.ts +1 -1
  22. package/src/kernel/boot-bind/ai.ts +31 -0
  23. package/src/kernel/boot-bind/channel.ts +27 -0
  24. package/src/kernel/boot-bind/clock.ts +74 -0
  25. package/src/kernel/boot-bind/gate.ts +28 -0
  26. package/src/kernel/boot-bind/runs.ts +28 -0
  27. package/src/kernel/boot-bind/signal.ts +68 -0
  28. package/src/kernel/boot-bind/store.ts +47 -0
  29. package/src/kernel/boot-bind/vault.ts +36 -0
  30. package/src/kernel/boot.test.ts +85 -1
  31. package/src/kernel/boot.ts +250 -212
  32. package/src/kernel/index.ts +2 -0
  33. package/src/mcp/data.ts +1 -0
  34. package/src/mcp/docs-index.ts +252 -0
  35. package/src/mcp/docs-mcp.test.ts +176 -0
  36. package/src/mcp/docs-server.ts +233 -0
  37. package/src/mcp/docs-tools.ts +143 -0
  38. package/src/mcp/index.ts +29 -5
  39. package/src/mcp/protocol.ts +2 -1
  40. package/src/release/exports.test.ts +71 -0
  41. package/src/release/exports.ts +156 -0
  42. package/src/release/index.ts +21 -0
  43. package/src/release/limits.ts +9 -0
  44. package/src/release/measure.exports.test.ts +82 -0
  45. package/src/release/measure.ts +297 -14
  46. package/src/release/publish.ts +14 -3
  47. package/src/release/readme.test.ts +61 -0
  48. package/src/release/readme.ts +13 -0
  49. package/src/runtime/index.ts +1 -0
  50. package/src/runtime/security.test.ts +6 -1
  51. package/src/runtime/types.ts +6 -0
  52. package/src/test/create-test-app.ts +2 -0
@@ -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.6",
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,6 +126,9 @@
116
126
  },
117
127
  "drizzle-seed": {
118
128
  "optional": true
129
+ },
130
+ "zod": {
131
+ "optional": true
119
132
  }
120
133
  },
121
134
  "engines": {
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);
@@ -3,29 +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
+ *
6
10
  * @module
7
11
  */
8
12
 
9
13
  export {
10
14
  bootConsoleApp,
15
+ createConsoleApp,
16
+ type ConsoleApp,
17
+ type ConsoleAppHandle,
18
+ type CreateConsoleAppOptions,
19
+ } from "./server/app.ts";
20
+ export {
11
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 {
12
29
  CONSOLE_COOKIES,
13
30
  CONSOLE_CSP,
14
31
  PLUGIN_IFRAME_SANDBOX,
15
- consolePlugin,
16
- createConsoleApp,
17
- createConsoleState,
18
- feedManifest,
19
- mintClaimCode,
20
- printClaimCodeOnce,
32
+ withConsoleSecurityHeaders,
33
+ } from "./server/security-headers.ts";
34
+ export {
21
35
  serveConsole,
22
36
  startConsoleApp,
23
- verifyClaimCode,
24
- withConsoleSecurityHeaders,
25
- type ConsoleApp,
26
- type ConsoleAppHandle,
27
37
  type ConsoleServerHandle,
28
- type ConsoleState,
29
- type CreateConsoleAppOptions,
30
38
  type ServeConsoleOptions,
31
- } 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.
@@ -6,7 +6,6 @@
6
6
  * Never installs — hands a `bun add …` command for community packages.
7
7
  */
8
8
 
9
- import { runDoctorDiff } from "../../cli/doctor-diff.ts";
10
9
  import {
11
10
  allHookCostSummaries,
12
11
  type HookCostSummary,
@@ -23,11 +22,9 @@ import {
23
22
  isCorePluginOn,
24
23
  type PluginConfigProbe,
25
24
  } from "../../plugins/catalogue.ts";
26
- import {
27
- projectSupplyChain,
28
- projectSupplyChainSync,
29
- type PackageJsonProbe,
30
- type SupplyChainSignals,
25
+ import type {
26
+ PackageJsonProbe,
27
+ SupplyChainSignals,
31
28
  } from "../../plugins/supply-chain.ts";
32
29
  import type { ScanSourceFile } from "../../plugins/node-import-scan.ts";
33
30
 
@@ -308,6 +305,9 @@ async function buildRow(input: {
308
305
  fetchNpm: input.options.fetchNpm,
309
306
  };
310
307
 
308
+ const { projectSupplyChain, projectSupplyChainSync } = await import(
309
+ "../../plugins/supply-chain.ts"
310
+ );
311
311
  const supplyChain = input.options.syncSupplyChain
312
312
  ? projectSupplyChainSync(supplyOpts)
313
313
  : await projectSupplyChain(supplyOpts);
@@ -357,6 +357,7 @@ export async function loadCapabilityDiffByPlugin(
357
357
  manifest: Manifest | null,
358
358
  ): Promise<Readonly<Record<string, readonly ManifestChange[]>>> {
359
359
  try {
360
+ const { runDoctorDiff } = await import("../../cli/doctor-diff.ts");
360
361
  const result = await runDoctorDiff({
361
362
  cwd,
362
363
  ...(manifest ? { after: manifest } : {}),
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Console flows that may run without an operator session.
3
+ *
4
+ * Kept out of `flows.ts` so `consolePlugin` does not pull the full flow graph.
5
+ */
6
+
7
+ /** Public setup / session entry flows (no operator session required). */
8
+ export const PUBLIC_CONSOLE_FLOWS = new Set([
9
+ "console.setup.status",
10
+ "console.setup.claim",
11
+ "console.session.login",
12
+ ]);