okengine 0.3.5 → 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.
Files changed (144) hide show
  1. package/AGENTS.md +6 -0
  2. package/manifest.v1.schema.json +43 -1
  3. package/package.json +27 -12
  4. package/site/content/docs/ai/skills.mdx +11 -7
  5. package/site/content/docs/console/vault.mdx +4 -0
  6. package/site/content/docs/elements/ai.mdx +2 -0
  7. package/site/content/docs/elements/channel.mdx +7 -4
  8. package/site/content/docs/elements/clock.mdx +2 -4
  9. package/site/content/docs/elements/flow.mdx +19 -10
  10. package/site/content/docs/elements/gate.mdx +18 -11
  11. package/site/content/docs/elements/signal.mdx +9 -12
  12. package/site/content/docs/elements/store.mdx +69 -8
  13. package/site/content/docs/elements/vault.mdx +11 -12
  14. package/site/content/docs/get-started/basic-usage.mdx +76 -41
  15. package/site/content/docs/get-started/installation.mdx +95 -43
  16. package/site/content/docs/get-started/introduction.mdx +128 -75
  17. package/site/content/docs/get-started/meta.json +1 -1
  18. package/site/content/docs/get-started/why.mdx +141 -0
  19. package/site/content/docs/plugins/ip-allowlist.mdx +1 -2
  20. package/site/content/docs/plugins/security-headers.mdx +1 -1
  21. package/site/content/docs/reference/configuration.mdx +2 -2
  22. package/site/content/docs/reference/environment-variables.mdx +10 -0
  23. package/site/content/docs/reference/fx.mdx +56 -3
  24. package/site/content/docs/reference/plugins.mdx +18 -18
  25. package/src/cli/competitor-mention-removal.test.ts +117 -0
  26. package/src/cli/dev.ts +20 -0
  27. package/src/cli/meilisearch-local.test.ts +69 -0
  28. package/src/cli/meilisearch-local.ts +188 -0
  29. package/src/compiler/extract.test.ts +63 -0
  30. package/src/compiler/extract.ts +36 -15
  31. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +5 -1
  32. package/src/console/server/channels.ts +2 -0
  33. package/src/console/server/clock.ts +3 -0
  34. package/src/console/server/flows.ts +13 -0
  35. package/src/console/server/gates.ts +2 -0
  36. package/src/console/server/plugins.ts +20 -1
  37. package/src/console/server/signals.ts +5 -0
  38. package/src/console/server/store.test.ts +17 -0
  39. package/src/console/server/store.ts +35 -1
  40. package/src/console/ui/channels/types.ts +1 -0
  41. package/src/console/ui/clock/types.ts +1 -0
  42. package/src/console/ui/display.test.ts +14 -0
  43. package/src/console/ui/display.ts +9 -0
  44. package/src/console/ui/dist/assets/index-CjxwRGVv.js +10 -0
  45. package/src/console/ui/dist/assets/panel-access-BGv45snf.js +64 -0
  46. package/src/console/ui/dist/assets/{panel-ai-D_m6WQI8.js → panel-ai-B2S7LEii.js} +1 -1
  47. package/src/console/ui/dist/assets/{panel-architecture-CKnXFyUx.js → panel-architecture-D7UJh91v.js} +1 -1
  48. package/src/console/ui/dist/assets/panel-channels-9T3ybqRu.js +1 -0
  49. package/src/console/ui/dist/assets/panel-clock-Cb1UXGRQ.js +1 -0
  50. package/src/console/ui/dist/assets/{panel-diff-cdonmH8c.js → panel-diff-DmYbKWmN.js} +1 -1
  51. package/src/console/ui/dist/assets/panel-flows-PiHwT55z.js +48 -0
  52. package/src/console/ui/dist/assets/panel-gates-BQGYXvjT.js +1 -0
  53. package/src/console/ui/dist/assets/panel-overview-BBnRO18l.js +1 -0
  54. package/src/console/ui/dist/assets/panel-plugins-D0PsmVw2.js +1 -0
  55. package/src/console/ui/dist/assets/panel-runs-CWuRDe0r.js +1 -0
  56. package/src/console/ui/dist/assets/panel-signals-Bbg4ewpP.js +1 -0
  57. package/src/console/ui/dist/assets/panel-store-CPCbsDRa.js +1 -0
  58. package/src/console/ui/dist/assets/panel-traces-DVAzuA_S.js +1 -0
  59. package/src/console/ui/dist/assets/panel-vault-D1_MvOmo.js +1 -0
  60. package/src/console/ui/dist/assets/{rolldown-runtime-CNC7AqOf.js → rolldown-runtime-B0Z9INg1.js} +1 -1
  61. package/src/console/ui/dist/index.html +2 -2
  62. package/src/console/ui/gates/types.ts +1 -0
  63. package/src/console/ui/plugins/fixture.ts +7 -0
  64. package/src/console/ui/plugins/types.ts +3 -0
  65. package/src/console/ui/shell/client.ts +3 -0
  66. package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +7 -2
  67. package/src/console/ui/shell/panels/clock/ClockPanel.tsx +9 -2
  68. package/src/console/ui/shell/panels/gates/GatesPanel.tsx +12 -5
  69. package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +18 -4
  70. package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +13 -2
  71. package/src/console/ui/shell/panels/store/StorePanel.tsx +11 -4
  72. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +9 -3
  73. package/src/console/ui/signals/types.ts +1 -0
  74. package/src/console/ui/store/fixture.ts +5 -0
  75. package/src/console/ui/store/types.ts +2 -0
  76. package/src/docker/compose.ts +5 -0
  77. package/src/docker/docker.test.ts +41 -0
  78. package/src/docker/recipes/index.ts +10 -2
  79. package/src/docker/recipes/meilisearch.ts +31 -0
  80. package/src/drivers/conformance.test.ts +26 -0
  81. package/src/drivers/conformance.ts +40 -3
  82. package/src/drivers/drizzle-dialect.test.ts +4 -0
  83. package/src/drivers/drizzle-dialect.ts +8 -4
  84. package/src/drivers/index.ts +18 -2
  85. package/src/drivers/libsql.ts +179 -0
  86. package/src/drivers/meilisearch.integration.test.ts +77 -0
  87. package/src/drivers/meilisearch.test.ts +181 -0
  88. package/src/drivers/meilisearch.ts +208 -0
  89. package/src/drivers/memory.ts +4 -4
  90. package/src/drivers/pglite.ts +79 -0
  91. package/src/drivers/pgvector.ts +60 -25
  92. package/src/drivers/types.ts +106 -16
  93. package/src/drivers/vault-driver-removal.test.ts +6 -0
  94. package/src/drivers/vault-types.ts +4 -4
  95. package/src/elements/ai/runtime.ts +6 -0
  96. package/src/elements/ai.test.ts +22 -0
  97. package/src/elements/channel/declare.ts +5 -0
  98. package/src/elements/clock/declare.ts +5 -0
  99. package/src/elements/clock/durable.ts +7 -1
  100. package/src/elements/gate/declare.ts +28 -5
  101. package/src/elements/gate.ts +1 -0
  102. package/src/elements/signal/declare.ts +5 -0
  103. package/src/elements/store/declare.ts +10 -2
  104. package/src/elements/store/index-boot.test.ts +299 -0
  105. package/src/elements/store/runtime.ts +110 -17
  106. package/src/elements/store/schema-decl.ts +7 -0
  107. package/src/elements/store.ts +2 -0
  108. package/src/elements/vault.test.ts +27 -4
  109. package/src/elements/vault.ts +1 -1
  110. package/src/index.ts +4 -0
  111. package/src/kernel/abort-scope.ts +116 -0
  112. package/src/kernel/app.ts +12 -2
  113. package/src/kernel/boot-bind/store.test.ts +68 -1
  114. package/src/kernel/boot-bind/store.ts +92 -2
  115. package/src/kernel/concurrency.test.ts +294 -0
  116. package/src/kernel/concurrency.ts +220 -0
  117. package/src/kernel/flow.ts +9 -0
  118. package/src/kernel/fx.test.ts +23 -2
  119. package/src/kernel/fx.ts +80 -4
  120. package/src/kernel/index.ts +23 -0
  121. package/src/kernel/journal.ts +9 -0
  122. package/src/kernel/plugin/capabilities.test.ts +18 -0
  123. package/src/kernel/plugin.ts +11 -3
  124. package/src/kernel/redacted.ts +74 -0
  125. package/src/kernel/registry.ts +29 -6
  126. package/src/kernel/router.ts +3 -3
  127. package/src/manifest/types.ts +21 -0
  128. package/src/release/measure.ts +4 -0
  129. package/src/test/provisions.integration.test.ts +1 -1
  130. package/site/content/docs/get-started/comparison.mdx +0 -65
  131. package/src/console/ui/dist/assets/index-BWo8R7NR.js +0 -10
  132. package/src/console/ui/dist/assets/panel-access-C0J2D-a2.js +0 -64
  133. package/src/console/ui/dist/assets/panel-channels-BOmQ-onL.js +0 -1
  134. package/src/console/ui/dist/assets/panel-clock-giAq0Ccv.js +0 -1
  135. package/src/console/ui/dist/assets/panel-flows-DlCU5zjA.js +0 -45
  136. package/src/console/ui/dist/assets/panel-gates-XclZxWD5.js +0 -1
  137. package/src/console/ui/dist/assets/panel-overview-BznEOTnb.js +0 -1
  138. package/src/console/ui/dist/assets/panel-plugins-CcGM1g64.js +0 -1
  139. package/src/console/ui/dist/assets/panel-runs-CGWNHLR4.js +0 -1
  140. package/src/console/ui/dist/assets/panel-signals-CNywkdak.js +0 -1
  141. package/src/console/ui/dist/assets/panel-store-KmTbFHMH.js +0 -1
  142. package/src/console/ui/dist/assets/panel-traces-DBLx2ilD.js +0 -1
  143. package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +0 -1
  144. package/src/drivers/vault-infisical.ts +0 -57
@@ -2,7 +2,7 @@
2
2
  * Vault element — protected knowledge.
3
3
  *
4
4
  * Physics: secrets · config · environment.
5
- * Drivers: `env` · `openbao` · `infisical` · `managed`.
5
+ * Drivers: `env` · `openbao` · `managed`.
6
6
  *
7
7
  * A declaration is a contract, never a value. Boot validates every contract
8
8
  * and lists all gaps at once. Logs/traces receive fingerprints and redaction
package/src/index.ts CHANGED
@@ -34,6 +34,10 @@ export {
34
34
  journey,
35
35
  unit,
36
36
  rateLimit,
37
+ Redacted,
38
+ isRedacted,
39
+ maskRedactedDeep,
40
+ REDACTED_PLACEHOLDER,
37
41
  type OkeApp,
38
42
  type OkeOptions,
39
43
  type FlowDef,
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Ambient AbortSignal scope for structured concurrency.
3
+ *
4
+ * Same ALS pattern as dry-run: `fx.all` / `fx.race` enter a child signal so
5
+ * cooperative branches (and future driver plumbing) can observe cancellation
6
+ * without every call site passing a signal argument.
7
+ */
8
+
9
+ import { AsyncLocalStorage } from "node:async_hooks";
10
+
11
+ /** Never-aborted signal used outside an `all` / `race` branch. */
12
+ const NEVER_ABORTED = new AbortController().signal;
13
+
14
+ const storage = new AsyncLocalStorage<AbortSignal>();
15
+
16
+ /**
17
+ * AbortSignal for the current async chain, or a never-aborted signal when
18
+ * no structured-concurrency scope is active.
19
+ */
20
+ export function currentAbortSignal(): AbortSignal {
21
+ return storage.getStore() ?? NEVER_ABORTED;
22
+ }
23
+
24
+ /**
25
+ * Run `fn` with `signal` as the ambient abort signal.
26
+ *
27
+ * @param signal - Branch / scope signal
28
+ * @param fn - Work that may read {@link currentAbortSignal}
29
+ */
30
+ export async function withAbortSignal<T>(
31
+ signal: AbortSignal,
32
+ fn: () => T | Promise<T>,
33
+ ): Promise<T> {
34
+ return storage.run(signal, fn);
35
+ }
36
+
37
+ /**
38
+ * When `parent` aborts, abort `child` with the same reason.
39
+ *
40
+ * @param parent - Outer signal
41
+ * @param child - Controller to abort
42
+ * @returns Unsubscribe
43
+ */
44
+ export function linkAbort(parent: AbortSignal, child: AbortController): () => void {
45
+ if (parent.aborted) {
46
+ child.abort(parent.reason);
47
+ return () => undefined;
48
+ }
49
+ const onAbort = (): void => {
50
+ child.abort(parent.reason);
51
+ };
52
+ parent.addEventListener("abort", onAbort);
53
+ return () => parent.removeEventListener("abort", onAbort);
54
+ }
55
+
56
+ /**
57
+ * True when `err` is an abort rejection (`AbortError` name).
58
+ *
59
+ * @param err - Unknown
60
+ */
61
+ export function isAbortError(err: unknown): boolean {
62
+ return err instanceof Error && err.name === "AbortError";
63
+ }
64
+
65
+ /**
66
+ * Create an `AbortError` matching Web / fetch abort rejections.
67
+ *
68
+ * @param reason - Optional abort reason
69
+ */
70
+ export function abortError(reason?: unknown): Error {
71
+ if (typeof DOMException === "function") {
72
+ const message =
73
+ reason instanceof Error
74
+ ? reason.message
75
+ : reason !== undefined
76
+ ? String(reason)
77
+ : "This operation was aborted";
78
+ return new DOMException(message, "AbortError");
79
+ }
80
+ const err = new Error(
81
+ reason instanceof Error
82
+ ? reason.message
83
+ : reason !== undefined
84
+ ? String(reason)
85
+ : "This operation was aborted",
86
+ );
87
+ err.name = "AbortError";
88
+ return err;
89
+ }
90
+
91
+ /**
92
+ * Sleep that rejects promptly when `signal` aborts.
93
+ *
94
+ * Not durable — retry backoff must not journal via `fx.clock.sleep`.
95
+ *
96
+ * @param ms - Milliseconds
97
+ * @param signal - Optional abort signal
98
+ */
99
+ export function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {
100
+ if (ms <= 0) return Promise.resolve();
101
+ return new Promise((resolve, reject) => {
102
+ if (signal?.aborted) {
103
+ reject(abortError(signal.reason));
104
+ return;
105
+ }
106
+ const timer = setTimeout(() => {
107
+ signal?.removeEventListener("abort", onAbort);
108
+ resolve();
109
+ }, ms);
110
+ const onAbort = (): void => {
111
+ clearTimeout(timer);
112
+ reject(abortError(signal?.reason));
113
+ };
114
+ signal?.addEventListener("abort", onAbort, { once: true });
115
+ });
116
+ }
package/src/kernel/app.ts CHANGED
@@ -33,6 +33,7 @@ import {
33
33
  } from "./auth-resolve.ts";
34
34
  import { runDurable } from "../elements/clock/durable.ts";
35
35
  import { isFlow, type AnyFlowDef } from "./flow.ts";
36
+ import { fxRetry } from "./concurrency.ts";
36
37
  import {
37
38
  createFxContext,
38
39
  resolveName,
@@ -680,13 +681,22 @@ export function oke(options: OkeOptions): OkeApp {
680
681
  hooks,
681
682
  async () => {
682
683
  try {
684
+ const invoke = async (input: unknown) => {
685
+ const run = () => {
686
+ // Flow-level retry re-enters `do` on the same journal session —
687
+ // rewind so completed steps/effects replay instead of re-executing.
688
+ journalSession?.rewind();
689
+ return flowDef.do(input as never, fx);
690
+ };
691
+ return flowDef.retry ? fxRetry(run, flowDef.retry) : run();
692
+ };
683
693
  if (!alreadyValidated) {
684
694
  const parsed = await validate(flowDef.in, ctx.input);
685
695
  if (!parsed.ok) return parsed.failure;
686
696
  ctx.input = parsed.value;
687
- return await flowDef.do(parsed.value as never, fx);
697
+ return await invoke(parsed.value);
688
698
  }
689
- return await flowDef.do(ctx.input as never, fx);
699
+ return await invoke(ctx.input);
690
700
  } catch (err) {
691
701
  // A durable sleep that has not yet elapsed suspends the run — this
692
702
  // is a park, not a pipeline failure, so it must not throw upward.
@@ -3,7 +3,13 @@
3
3
  */
4
4
 
5
5
  import { afterEach, describe, expect, test } from "bun:test";
6
- import { resolveFilesDriverId, resolveKvDriverId, resolveSqlDriverId } from "./store.ts";
6
+ import {
7
+ indexDriverFor,
8
+ resolveFilesDriverId,
9
+ resolveIndexDriverId,
10
+ resolveKvDriverId,
11
+ resolveSqlDriverId,
12
+ } from "./store.ts";
7
13
 
8
14
  describe("bindStore driver resolution", () => {
9
15
  const prev = {
@@ -11,6 +17,7 @@ describe("bindStore driver resolution", () => {
11
17
  sql: process.env.OKE_SQL_DRIVER,
12
18
  kv: process.env.OKE_KV_DRIVER,
13
19
  files: process.env.OKE_FILES_DRIVER,
20
+ index: process.env.OKE_INDEX_DRIVER,
14
21
  };
15
22
 
16
23
  afterEach(() => {
@@ -22,6 +29,8 @@ describe("bindStore driver resolution", () => {
22
29
  else process.env.OKE_KV_DRIVER = prev.kv;
23
30
  if (prev.files === undefined) delete process.env.OKE_FILES_DRIVER;
24
31
  else process.env.OKE_FILES_DRIVER = prev.files;
32
+ if (prev.index === undefined) delete process.env.OKE_INDEX_DRIVER;
33
+ else process.env.OKE_INDEX_DRIVER = prev.index;
25
34
  });
26
35
 
27
36
  test("local env keeps sqlite / memory from config", () => {
@@ -67,3 +76,61 @@ describe("bindStore driver resolution", () => {
67
76
  expect(resolveFilesDriverId({}, "docker", true)).toBe("s3");
68
77
  });
69
78
  });
79
+
80
+ describe("bindStore index driver resolution", () => {
81
+ const prevIndex = process.env.OKE_INDEX_DRIVER;
82
+
83
+ afterEach(() => {
84
+ if (prevIndex === undefined) delete process.env.OKE_INDEX_DRIVER;
85
+ else process.env.OKE_INDEX_DRIVER = prevIndex;
86
+ });
87
+
88
+ test("defaults to memory when no index driver is configured", () => {
89
+ expect(resolveIndexDriverId({}, "local", false)).toBe("memory");
90
+ expect(resolveIndexDriverId({}, "docker", true)).toBe("memory");
91
+ expect(indexDriverFor("memory").id).toBe("memory");
92
+ });
93
+
94
+ test("config drivers.store.index is honoured per env", () => {
95
+ const options = {
96
+ config: {
97
+ drivers: {
98
+ store: {
99
+ index: { local: "memory", docker: "pgvector", prod: "pgvector" },
100
+ },
101
+ },
102
+ },
103
+ };
104
+ expect(resolveIndexDriverId(options, "local", false)).toBe("memory");
105
+ expect(resolveIndexDriverId(options, "docker", true)).toBe("pgvector");
106
+ expect(resolveIndexDriverId(options, "prod", false)).toBe("pgvector");
107
+ });
108
+
109
+ test("libsql resolves from config", () => {
110
+ const options = {
111
+ config: { drivers: { store: { index: { local: "libsql" } } } },
112
+ };
113
+ expect(resolveIndexDriverId(options, "local", false)).toBe("libsql");
114
+ expect(indexDriverFor("libsql").id).toBe("libsql");
115
+ });
116
+
117
+ test("meilisearch resolves from config as a fourth id", () => {
118
+ const options = {
119
+ config: { drivers: { store: { index: { local: "meilisearch", docker: "meilisearch" } } } },
120
+ };
121
+ expect(resolveIndexDriverId(options, "local", false)).toBe("meilisearch");
122
+ expect(resolveIndexDriverId(options, "docker", true)).toBe("meilisearch");
123
+ expect(indexDriverFor("meilisearch").id).toBe("meilisearch");
124
+ });
125
+
126
+ test("docker mode honours OKE_INDEX_DRIVER override", () => {
127
+ process.env.OKE_INDEX_DRIVER = "pgvector";
128
+ expect(resolveIndexDriverId({}, "docker", true)).toBe("pgvector");
129
+ expect(resolveIndexDriverId({}, "local", false)).toBe("memory");
130
+ });
131
+
132
+ test("indexDriverFor returns the configured driver and throws on unknown ids", () => {
133
+ expect(indexDriverFor("pgvector").id).toBe("pgvector");
134
+ expect(() => indexDriverFor("chroma")).toThrow(/unknown index driver "chroma"/);
135
+ });
136
+ });
@@ -4,12 +4,16 @@
4
4
 
5
5
  import { resolveDomainDdlMode, resolveDriverId, type ConfigEnv } from "../../config/index.ts";
6
6
  import { fsDriver } from "../../drivers/fs.ts";
7
+ import { libsqlDriver, libsqlIndexDriver } from "../../drivers/libsql.ts";
8
+ import { meilisearchDriver } from "../../drivers/meilisearch.ts";
7
9
  import { memoryDrivers } from "../../drivers/memory.ts";
10
+ import { pgliteDriver } from "../../drivers/pglite.ts";
11
+ import { pgvectorDriver } from "../../drivers/pgvector.ts";
8
12
  import { postgresDriver } from "../../drivers/postgres.ts";
9
13
  import { redisDriver } from "../../drivers/redis.ts";
10
14
  import { s3Driver } from "../../drivers/s3.ts";
11
15
  import { sqliteDriver } from "../../drivers/sqlite.ts";
12
- import type { FilesDriver, KvDriver, SqlDriver } from "../../drivers/types.ts";
16
+ import type { FilesDriver, IndexDriver, KvDriver, SqlDriver } from "../../drivers/types.ts";
13
17
  import { createStoreRuntime, type StoreRuntime } from "../../elements/store.ts";
14
18
  import type { StoreDecl } from "../../elements/store/declare.ts";
15
19
  import type { BootOptions } from "../boot.ts"; // type-only — no cycle at runtime
@@ -34,6 +38,7 @@ export function bindStore(
34
38
  const sqlId = resolveSqlDriverId(options, env, docker);
35
39
  const kvId = resolveKvDriverId(options, env, docker);
36
40
  const filesId = resolveFilesDriverId(options, env, docker);
41
+ const indexId = resolveIndexDriverId(options, env, docker);
37
42
  const sqlUrl = sqlUrlFor(sqlId, docker);
38
43
  const kvUrl = kvUrlFor(kvId, docker);
39
44
  const filesRoot = filesRootFor(filesId);
@@ -41,6 +46,7 @@ export function bindStore(
41
46
  const sqlBindings: Record<string, { name: string; primary: { url: string } }> = {};
42
47
  const kvBindings: Record<string, { url?: string }> = {};
43
48
  const filesBindings: Record<string, { root?: string }> = {};
49
+ const indexBindings: Record<string, { url?: string; apiKey?: string }> = {};
44
50
 
45
51
  for (const decl of options.stores ?? []) {
46
52
  if (isSqlDecl(decl)) {
@@ -52,6 +58,17 @@ export function bindStore(
52
58
  kvBindings[decl.name] = kvUrl !== undefined ? { url: kvUrl } : {};
53
59
  } else if (isFilesDecl(decl)) {
54
60
  filesBindings[decl.name] = filesRoot !== undefined ? { root: filesRoot } : {};
61
+ } else if (isIndexDecl(decl)) {
62
+ // SQL-backed index drivers share the sql facet's URL — the runtime opens
63
+ // one connection and hands it to both (never a second, redundant one).
64
+ // Meilisearch is a standalone HTTP service: its own URL + key, never sqlUrl.
65
+ if (indexId === "memory") {
66
+ indexBindings[decl.name] = {};
67
+ } else if (indexId === "meilisearch") {
68
+ indexBindings[decl.name] = { url: indexUrlFor(docker), apiKey: indexApiKeyFor() };
69
+ } else {
70
+ indexBindings[decl.name] = { url: sqlUrl };
71
+ }
55
72
  }
56
73
  }
57
74
 
@@ -63,11 +80,12 @@ export function bindStore(
63
80
  sql: sqlDriverFor(sqlId),
64
81
  kv: kvDriverFor(kvId),
65
82
  files: filesDriverFor(filesId),
66
- index: memoryDrivers.index,
83
+ index: indexDriverFor(indexId),
67
84
  },
68
85
  sql: sqlBindings,
69
86
  kv: kvBindings,
70
87
  files: filesBindings,
88
+ index: indexBindings,
71
89
  now,
72
90
  domainDdl,
73
91
  });
@@ -132,12 +150,37 @@ export function resolveFilesDriverId(
132
150
  return docker ? "s3" : "memory";
133
151
  }
134
152
 
153
+ /**
154
+ * Index driver — same resolution chain as sql/kv/files. Defaults to `memory`;
155
+ * a configured `pgvector` / `libsql` is honoured at real boot (and fails loud
156
+ * if its SQL engine or peer is missing — never silently back to memory).
157
+ *
158
+ * @param options - Boot options
159
+ * @param env - Active env
160
+ * @param docker - Docker mode
161
+ */
162
+ export function resolveIndexDriverId(
163
+ options: BootOptions,
164
+ env: ConfigEnv,
165
+ docker: boolean,
166
+ ): string {
167
+ const fromEnv = process.env.OKE_INDEX_DRIVER?.trim();
168
+ if (docker && fromEnv) return fromEnv;
169
+ const resolved = resolveDriverId(options.config?.drivers?.store?.index, env);
170
+ if (resolved) return resolved;
171
+ return "memory";
172
+ }
173
+
135
174
  function sqlDriverFor(id: string): SqlDriver {
136
175
  switch (id) {
137
176
  case "postgres":
138
177
  return postgresDriver;
139
178
  case "sqlite":
140
179
  return sqliteDriver;
180
+ case "libsql":
181
+ return libsqlDriver;
182
+ case "pglite":
183
+ return pgliteDriver;
141
184
  case "memory":
142
185
  return memoryDrivers.sql;
143
186
  default:
@@ -169,6 +212,25 @@ function filesDriverFor(id: string): FilesDriver {
169
212
  }
170
213
  }
171
214
 
215
+ /**
216
+ * Single id → index driver switch — shared by `bindStore` and the Console's
217
+ * Manifest runtime so resolution can never drift into two maintained copies.
218
+ */
219
+ export function indexDriverFor(id: string): IndexDriver {
220
+ switch (id) {
221
+ case "memory":
222
+ return memoryDrivers.index;
223
+ case "pgvector":
224
+ return pgvectorDriver;
225
+ case "libsql":
226
+ return libsqlIndexDriver;
227
+ case "meilisearch":
228
+ return meilisearchDriver;
229
+ default:
230
+ throw new Error(`oke boot: unknown index driver "${id}"`);
231
+ }
232
+ }
233
+
172
234
  function sqlUrlFor(sqlId: string, docker: boolean): string {
173
235
  if (sqlId === "postgres") {
174
236
  const url = process.env.DATABASE_URL ?? process.env.OKE_STORE_SQL_URL ?? undefined;
@@ -184,6 +246,12 @@ function sqlUrlFor(sqlId: string, docker: boolean): string {
184
246
  if (sqlId === "sqlite") {
185
247
  return process.env.OKE_SQLITE_URL ?? ".oke/app.sqlite";
186
248
  }
249
+ if (sqlId === "libsql") {
250
+ return process.env.OKE_LIBSQL_URL ?? ".oke/app.libsql";
251
+ }
252
+ if (sqlId === "pglite") {
253
+ return process.env.OKE_PGLITE_URL ?? ".oke/pgdata";
254
+ }
187
255
  return ":memory:";
188
256
  }
189
257
 
@@ -205,6 +273,24 @@ function filesRootFor(filesId: string): string | undefined {
205
273
  return process.env.S3_BUCKET ?? process.env.OKE_STORE_FILES_DB ?? undefined;
206
274
  }
207
275
 
276
+ /** Meilisearch base URL — standalone HTTP service, fail loud when missing. */
277
+ function indexUrlFor(docker: boolean): string {
278
+ const url = process.env.OKE_STORE_INDEX_URL ?? undefined;
279
+ if (!url) {
280
+ throw new Error(
281
+ docker
282
+ ? "oke boot: meilisearch index needs OKE_STORE_INDEX_URL (did `oke dev -d` write docker/.env.docker?)"
283
+ : "oke boot: meilisearch index needs OKE_STORE_INDEX_URL",
284
+ );
285
+ }
286
+ return url;
287
+ }
288
+
289
+ /** Meilisearch API / master key. */
290
+ function indexApiKeyFor(): string | undefined {
291
+ return process.env.OKE_STORE_INDEX_KEY ?? process.env.MEILI_MASTER_KEY ?? undefined;
292
+ }
293
+
208
294
  function isSqlDecl(decl: StoreDecl): decl is Extract<StoreDecl, { facet: "sql" }> {
209
295
  return decl.facet === "sql";
210
296
  }
@@ -216,3 +302,7 @@ function isKvDecl(decl: StoreDecl): decl is Extract<StoreDecl, { facet: "kv" }>
216
302
  function isFilesDecl(decl: StoreDecl): decl is Extract<StoreDecl, { facet: "files" }> {
217
303
  return decl.facet === "files";
218
304
  }
305
+
306
+ function isIndexDecl(decl: StoreDecl): decl is Extract<StoreDecl, { facet: "index" }> {
307
+ return decl.facet === "index";
308
+ }