okengine 0.6.0 → 0.6.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 (38) hide show
  1. package/README.md +148 -13
  2. package/package.json +3 -3
  3. package/site/content/docs/elements/channel.mdx +71 -7
  4. package/site/content/docs/reference/configuration.mdx +5 -4
  5. package/site/content/docs/reference/environment-variables.mdx +32 -8
  6. package/src/cli/openbao-restart.integration.test.ts +106 -97
  7. package/src/docker/dockerfile.integration.test.ts +126 -119
  8. package/src/docker/stack.integration.test.ts +118 -102
  9. package/src/drivers/ai-ollama-tools.integration.test.ts +8 -6
  10. package/src/drivers/ai-ollama.integration.test.ts +3 -19
  11. package/src/drivers/channel-fcm.ts +49 -53
  12. package/src/drivers/channel-msegat.ts +61 -0
  13. package/src/drivers/channel-sently-map.ts +57 -0
  14. package/src/drivers/channel-sently.test.ts +99 -0
  15. package/src/drivers/channel-sndr.ts +28 -0
  16. package/src/drivers/channel-taqnyat.ts +57 -0
  17. package/src/drivers/channel-types.ts +79 -2
  18. package/src/drivers/channel-unifonic.ts +26 -43
  19. package/src/drivers/channel-wa-cloud.ts +33 -47
  20. package/src/drivers/channel-webpush.ts +39 -239
  21. package/src/drivers/index.ts +4 -0
  22. package/src/elements/channel/costs.test.ts +2 -2
  23. package/src/elements/channel/costs.ts +14 -2
  24. package/src/elements/channel/mime.ts +11 -0
  25. package/src/elements/channel/runtime.ts +94 -0
  26. package/src/elements/channel/sndr-webhooks.test.ts +26 -0
  27. package/src/elements/channel.ts +10 -1
  28. package/src/elements/index.ts +9 -0
  29. package/src/kernel/boot-bind/channel.test.ts +68 -3
  30. package/src/kernel/boot-bind/channel.ts +93 -2
  31. package/src/plugins/auth-delivery.mailpit.integration.test.ts +10 -4
  32. package/src/release/exports.test.ts +26 -0
  33. package/src/release/exports.ts +64 -5
  34. package/src/release/index.ts +5 -0
  35. package/src/release/measure.exports.test.ts +13 -1
  36. package/src/release/measure.ts +76 -13
  37. package/src/release/official-plugins.ts +46 -0
  38. package/src/release/readme.test.ts +30 -2
@@ -1,5 +1,7 @@
1
1
  /**
2
2
  * Integration: generated Dockerfile builds (and optionally runs).
3
+ *
4
+ * Opt-in via `OKE_TEST_DOCKER=1` plus a live Docker daemon. Never an empty pass.
3
5
  */
4
6
 
5
7
  import { describe, expect, test } from "bun:test";
@@ -8,47 +10,50 @@ import { tmpdir } from "node:os";
8
10
  import { join } from "node:path";
9
11
  import { deriveInfrastructure, writeDerivedFiles } from "./index.ts";
10
12
 
11
- async function dockerAvailable(): Promise<boolean> {
13
+ function dockerAvailable(): boolean {
12
14
  try {
13
- const proc = Bun.spawn(["docker", "info"], {
14
- stdout: "pipe",
15
- stderr: "pipe",
16
- });
17
- const code = await proc.exited;
18
- return code === 0;
15
+ return Bun.spawnSync(["docker", "info"], { stdout: "pipe", stderr: "pipe" }).exitCode === 0;
19
16
  } catch {
20
17
  return false;
21
18
  }
22
19
  }
23
20
 
24
- describe("generated Dockerfile integration", () => {
25
- test("oke docker output builds and runs", async () => {
26
- if (!(await dockerAvailable())) {
27
- console.warn("skipping: docker daemon not available");
28
- return;
29
- }
21
+ const WANT = process.env.OKE_TEST_DOCKER === "1";
22
+ const DOCKER = WANT && dockerAvailable();
23
+ if (!DOCKER) {
24
+ console.log(
25
+ WANT
26
+ ? "skip: generated Dockerfile e2e (docker daemon not available)"
27
+ : "skip: generated Dockerfile e2e (OKE_TEST_DOCKER≠1)",
28
+ );
29
+ }
30
+ const live = DOCKER ? test : test.skip;
30
31
 
31
- const dir = await mkdtemp(join(tmpdir(), "oke-df-build-"));
32
- try {
33
- // Minimal app that `oke start` can import.
34
- await Bun.write(
35
- join(dir, "package.json"),
36
- JSON.stringify(
37
- {
38
- name: "oke-docker-fixture",
39
- private: true,
40
- type: "module",
41
- bin: { oke: "./oke.ts" },
42
- scripts: { start: "bun ./app.ts" },
43
- okengine: { entry: "./app.ts" },
44
- },
45
- null,
46
- 2,
47
- ),
48
- );
49
- await Bun.write(
50
- join(dir, "oke.ts"),
51
- `#!/usr/bin/env bun
32
+ describe("generated Dockerfile integration", () => {
33
+ live(
34
+ "oke docker output builds and runs",
35
+ async () => {
36
+ const dir = await mkdtemp(join(tmpdir(), "oke-df-build-"));
37
+ try {
38
+ // Minimal app that `oke start` can import.
39
+ await Bun.write(
40
+ join(dir, "package.json"),
41
+ JSON.stringify(
42
+ {
43
+ name: "oke-docker-fixture",
44
+ private: true,
45
+ type: "module",
46
+ bin: { oke: "./oke.ts" },
47
+ scripts: { start: "bun ./app.ts" },
48
+ okengine: { entry: "./app.ts" },
49
+ },
50
+ null,
51
+ 2,
52
+ ),
53
+ );
54
+ await Bun.write(
55
+ join(dir, "oke.ts"),
56
+ `#!/usr/bin/env bun
52
57
  const [cmd] = process.argv.slice(2);
53
58
  if (cmd === "start") {
54
59
  await import("./app.ts");
@@ -57,10 +62,10 @@ if (cmd === "start") {
57
62
  process.exit(1);
58
63
  }
59
64
  `,
60
- );
61
- await Bun.write(
62
- join(dir, "app.ts"),
63
- `const server = Bun.serve({
65
+ );
66
+ await Bun.write(
67
+ join(dir, "app.ts"),
68
+ `const server = Bun.serve({
64
69
  port: Number(process.env.PORT ?? 6530),
65
70
  hostname: "0.0.0.0",
66
71
  fetch() {
@@ -69,95 +74,97 @@ if (cmd === "start") {
69
74
  });
70
75
  console.log("listening", server.port);
71
76
  `,
72
- );
77
+ );
73
78
 
74
- // No lockfile — adjust Dockerfile install for the fixture.
75
- const derived = deriveInfrastructure({
76
- images: { "store.sql": "postgres:18-alpine" },
77
- credentials: {
78
- "store.sql": {
79
- user: "oke",
80
- password: "fixture-pass",
81
- database: "oke",
79
+ // No lockfile — adjust Dockerfile install for the fixture.
80
+ const derived = deriveInfrastructure({
81
+ images: { "store.sql": "postgres:18-alpine" },
82
+ credentials: {
83
+ "store.sql": {
84
+ user: "oke",
85
+ password: "fixture-pass",
86
+ database: "oke",
87
+ },
82
88
  },
83
- },
84
- app: "fixture",
85
- // Flat fixture root (build context `.`).
86
- composeDir: ".",
87
- });
88
- await writeDerivedFiles(derived, dir);
89
+ app: "fixture",
90
+ // Flat fixture root (build context `.`).
91
+ composeDir: ".",
92
+ });
93
+ await writeDerivedFiles(derived, dir);
89
94
 
90
- // Fixture has no bun.lock — rewrite install step.
91
- let df = await Bun.file(join(dir, "Dockerfile")).text();
92
- df = df.replace(
93
- "RUN bun install --frozen-lockfile --production",
94
- "RUN bun install --production",
95
- );
96
- await Bun.write(join(dir, "Dockerfile"), df);
95
+ // Fixture has no bun.lock — rewrite install step.
96
+ let df = await Bun.file(join(dir, "Dockerfile")).text();
97
+ df = df.replace(
98
+ "RUN bun install --frozen-lockfile --production",
99
+ "RUN bun install --production",
100
+ );
101
+ await Bun.write(join(dir, "Dockerfile"), df);
97
102
 
98
- const tag = `oke-docker-fixture:${Date.now()}`;
99
- const build = Bun.spawn(["docker", "build", "-t", tag, dir], {
100
- stdout: "pipe",
101
- stderr: "pipe",
102
- });
103
- const [buildOut, buildErr, buildCode] = await Promise.all([
104
- new Response(build.stdout).text(),
105
- new Response(build.stderr).text(),
106
- build.exited,
107
- ]);
108
- expect(buildCode).toBe(0);
109
- if (buildCode !== 0) {
110
- console.error(buildOut, buildErr);
111
- }
103
+ const tag = `oke-docker-fixture:${Date.now()}`;
104
+ const build = Bun.spawn(["docker", "build", "-t", tag, dir], {
105
+ stdout: "pipe",
106
+ stderr: "pipe",
107
+ });
108
+ const [buildOut, buildErr, buildCode] = await Promise.all([
109
+ new Response(build.stdout).text(),
110
+ new Response(build.stderr).text(),
111
+ build.exited,
112
+ ]);
113
+ expect(buildCode).toBe(0);
114
+ if (buildCode !== 0) {
115
+ console.error(buildOut, buildErr);
116
+ }
112
117
 
113
- const name = `oke-fixture-run-${Date.now()}`;
114
- const run = Bun.spawn(["docker", "run", "-d", "--name", name, "-p", "0:6530", tag], {
115
- stdout: "pipe",
116
- stderr: "pipe",
117
- });
118
- const [runOut, runErr, runCode] = await Promise.all([
119
- new Response(run.stdout).text(),
120
- new Response(run.stderr).text(),
121
- run.exited,
122
- ]);
123
- expect(runCode).toBe(0);
124
- if (runCode !== 0) console.error(runOut, runErr);
118
+ const name = `oke-fixture-run-${Date.now()}`;
119
+ const run = Bun.spawn(["docker", "run", "-d", "--name", name, "-p", "0:6530", tag], {
120
+ stdout: "pipe",
121
+ stderr: "pipe",
122
+ });
123
+ const [runOut, runErr, runCode] = await Promise.all([
124
+ new Response(run.stdout).text(),
125
+ new Response(run.stderr).text(),
126
+ run.exited,
127
+ ]);
128
+ expect(runCode).toBe(0);
129
+ if (runCode !== 0) console.error(runOut, runErr);
125
130
 
126
- // Resolve published port
127
- const portProc = Bun.spawn(["docker", "port", name, "6530"], {
128
- stdout: "pipe",
129
- stderr: "pipe",
130
- });
131
- const portOut = await new Response(portProc.stdout).text();
132
- await portProc.exited;
133
- const m = portOut.match(/:(\d+)/);
134
- expect(m).toBeTruthy();
135
- const hostPort = m![1]!;
131
+ // Resolve published port
132
+ const portProc = Bun.spawn(["docker", "port", name, "6530"], {
133
+ stdout: "pipe",
134
+ stderr: "pipe",
135
+ });
136
+ const portOut = await new Response(portProc.stdout).text();
137
+ await portProc.exited;
138
+ const m = portOut.match(/:(\d+)/);
139
+ expect(m).toBeTruthy();
140
+ const hostPort = m![1]!;
136
141
 
137
- let body: { ok?: boolean } | null = null;
138
- for (let i = 0; i < 20; i++) {
139
- try {
140
- const res = await fetch(`http://127.0.0.1:${hostPort}/`);
141
- if (res.ok) {
142
- body = (await res.json()) as { ok?: boolean };
143
- break;
142
+ let body: { ok?: boolean } | null = null;
143
+ for (let i = 0; i < 20; i++) {
144
+ try {
145
+ const res = await fetch(`http://127.0.0.1:${hostPort}/`);
146
+ if (res.ok) {
147
+ body = (await res.json()) as { ok?: boolean };
148
+ break;
149
+ }
150
+ } catch {
151
+ await Bun.sleep(250);
144
152
  }
145
- } catch {
146
- await Bun.sleep(250);
147
153
  }
148
- }
149
- expect(body?.ok).toBe(true);
154
+ expect(body?.ok).toBe(true);
150
155
 
151
- await Bun.spawn(["docker", "rm", "-f", name], {
152
- stdout: "pipe",
153
- stderr: "pipe",
154
- }).exited;
155
- await Bun.spawn(["docker", "rmi", "-f", tag], {
156
- stdout: "pipe",
157
- stderr: "pipe",
158
- }).exited;
159
- } finally {
160
- await rm(dir, { recursive: true, force: true }).catch(() => {});
161
- }
162
- }, 180_000);
156
+ await Bun.spawn(["docker", "rm", "-f", name], {
157
+ stdout: "pipe",
158
+ stderr: "pipe",
159
+ }).exited;
160
+ await Bun.spawn(["docker", "rmi", "-f", tag], {
161
+ stdout: "pipe",
162
+ stderr: "pipe",
163
+ }).exited;
164
+ } finally {
165
+ await rm(dir, { recursive: true, force: true }).catch(() => {});
166
+ }
167
+ },
168
+ 180_000,
169
+ );
163
170
  });
@@ -1,5 +1,7 @@
1
1
  /**
2
2
  * Integration: generated compose brings up postgres; app URL talks to it.
3
+ *
4
+ * Opt-in via `OKE_TEST_DOCKER=1` plus a live Docker daemon. Never an empty pass.
3
5
  */
4
6
 
5
7
  import { describe, expect, test } from "bun:test";
@@ -8,127 +10,141 @@ import { tmpdir } from "node:os";
8
10
  import { join } from "node:path";
9
11
  import { deriveInfrastructure, formatStackEnv, writeDerivedFiles } from "./index.ts";
10
12
 
11
- async function dockerAvailable(): Promise<boolean> {
13
+ function dockerAvailable(): boolean {
12
14
  try {
13
- const proc = Bun.spawn(["docker", "info"], {
14
- stdout: "pipe",
15
- stderr: "pipe",
16
- });
17
- return (await proc.exited) === 0;
15
+ return Bun.spawnSync(["docker", "info"], { stdout: "pipe", stderr: "pipe" }).exitCode === 0;
18
16
  } catch {
19
17
  return false;
20
18
  }
21
19
  }
22
20
 
23
- describe("oke dev --docker postgres integration", () => {
24
- test("compose brings up postgres and the app talks to it", async () => {
25
- if (!(await dockerAvailable())) {
26
- console.warn("skipping: docker daemon not available");
27
- return;
28
- }
29
-
30
- const dir = await mkdtemp(join(tmpdir(), "oke-stack-pg-"));
31
- const dockerDir = join(dir, "docker");
32
- const project = `oke-pg-${Date.now()}`;
33
- try {
34
- const derived = deriveInfrastructure({
35
- images: { "store.sql": "postgres:18-alpine" },
36
- credentials: {
37
- "store.sql": {
38
- user: "oke",
39
- password: "stack-integration-pass",
40
- database: "oke",
41
- },
42
- },
43
- app: "stacktest",
44
- host: "127.0.0.1",
45
- includeApp: false,
46
- composeDir: "docker",
47
- });
48
- await writeDerivedFiles(derived, dockerDir, {
49
- writeStackEnv: true,
50
- });
21
+ const WANT = process.env.OKE_TEST_DOCKER === "1";
22
+ const DOCKER = WANT && dockerAvailable();
23
+ if (!DOCKER) {
24
+ console.log(
25
+ WANT
26
+ ? "skip: docker postgres stack e2e (docker daemon not available)"
27
+ : "skip: docker postgres stack e2e (OKE_TEST_DOCKER≠1)",
28
+ );
29
+ }
30
+ const live = DOCKER ? test : test.skip;
51
31
 
52
- // Infra-only: network + role compose (no app build).
53
- const composeFiles = ["compose.yml", "compose.store.sql.yml"];
54
- const up = Bun.spawn(
55
- ["docker", "compose", "-p", project, ...composeFiles.flatMap((f) => ["-f", f]), "up", "-d"],
56
- {
57
- cwd: dockerDir,
58
- stdout: "pipe",
59
- stderr: "pipe",
60
- env: {
61
- ...process.env,
62
- ...derived.stackEnv,
32
+ describe("oke dev --docker postgres integration", () => {
33
+ live(
34
+ "compose brings up postgres and the app talks to it",
35
+ async () => {
36
+ const dir = await mkdtemp(join(tmpdir(), "oke-stack-pg-"));
37
+ const dockerDir = join(dir, "docker");
38
+ const project = `oke-pg-${Date.now()}`;
39
+ try {
40
+ const derived = deriveInfrastructure({
41
+ images: { "store.sql": "postgres:18-alpine" },
42
+ credentials: {
43
+ "store.sql": {
44
+ user: "oke",
45
+ password: "stack-integration-pass",
46
+ database: "oke",
47
+ },
63
48
  },
64
- },
65
- );
66
- const [upOut, upErr, upCode] = await Promise.all([
67
- new Response(up.stdout).text(),
68
- new Response(up.stderr).text(),
69
- up.exited,
70
- ]);
71
- expect(upCode).toBe(0);
72
- if (upCode !== 0) console.error(upOut, upErr);
49
+ app: "stacktest",
50
+ host: "127.0.0.1",
51
+ includeApp: false,
52
+ composeDir: "docker",
53
+ });
54
+ await writeDerivedFiles(derived, dockerDir, {
55
+ writeStackEnv: true,
56
+ });
73
57
 
74
- // Wait for healthy
75
- let healthy = false;
76
- for (let i = 0; i < 40; i++) {
77
- const ps = Bun.spawn(
58
+ // Infra-only: network + role compose (no app build).
59
+ const composeFiles = ["compose.yml", "compose.store.sql.yml"];
60
+ const up = Bun.spawn(
78
61
  [
79
62
  "docker",
80
63
  "compose",
81
64
  "-p",
82
65
  project,
83
66
  ...composeFiles.flatMap((f) => ["-f", f]),
84
- "ps",
85
- "--format",
86
- "json",
67
+ "up",
68
+ "-d",
87
69
  ],
88
- { cwd: dockerDir, stdout: "pipe", stderr: "pipe" },
70
+ {
71
+ cwd: dockerDir,
72
+ stdout: "pipe",
73
+ stderr: "pipe",
74
+ env: {
75
+ ...process.env,
76
+ ...derived.stackEnv,
77
+ },
78
+ },
89
79
  );
90
- const text = await new Response(ps.stdout).text();
91
- await ps.exited;
92
- if (/healthy/i.test(text)) {
93
- healthy = true;
94
- break;
80
+ const [upOut, upErr, upCode] = await Promise.all([
81
+ new Response(up.stdout).text(),
82
+ new Response(up.stderr).text(),
83
+ up.exited,
84
+ ]);
85
+ expect(upCode).toBe(0);
86
+ if (upCode !== 0) console.error(upOut, upErr);
87
+
88
+ // Wait for healthy
89
+ let healthy = false;
90
+ for (let i = 0; i < 40; i++) {
91
+ const ps = Bun.spawn(
92
+ [
93
+ "docker",
94
+ "compose",
95
+ "-p",
96
+ project,
97
+ ...composeFiles.flatMap((f) => ["-f", f]),
98
+ "ps",
99
+ "--format",
100
+ "json",
101
+ ],
102
+ { cwd: dockerDir, stdout: "pipe", stderr: "pipe" },
103
+ );
104
+ const text = await new Response(ps.stdout).text();
105
+ await ps.exited;
106
+ if (/healthy/i.test(text)) {
107
+ healthy = true;
108
+ break;
109
+ }
110
+ await Bun.sleep(500);
95
111
  }
96
- await Bun.sleep(500);
97
- }
98
- expect(healthy).toBe(true);
112
+ expect(healthy).toBe(true);
99
113
 
100
- const url = derived.stackEnv.DATABASE_URL!;
101
- // App talks to postgres via recipe URL (Bun.SQL) — kernel never sees env-var names.
102
- const sql = new Bun.SQL(url);
103
- try {
104
- const rows = (await sql`select 1::int as n`) as Array<{ n: number }>;
105
- expect(rows[0]?.n).toBe(1);
114
+ const url = derived.stackEnv.DATABASE_URL!;
115
+ // App talks to postgres via recipe URL (Bun.SQL) — kernel never sees env-var names.
116
+ const sql = new Bun.SQL(url);
117
+ try {
118
+ const rows = (await sql`select 1::int as n`) as Array<{ n: number }>;
119
+ expect(rows[0]?.n).toBe(1);
120
+ } finally {
121
+ await sql.close();
122
+ }
123
+
124
+ // Prove credentials live in docker/.env.docker, not YAML.
125
+ const yml = await Bun.file(join(dockerDir, "compose.store.sql.yml")).text();
126
+ expect(yml).not.toContain("stack-integration-pass");
127
+ expect(formatStackEnv(derived.stackEnv)).toContain("stack-integration-pass");
128
+ expect(await Bun.file(join(dockerDir, ".env.docker")).exists()).toBe(true);
106
129
  } finally {
107
- await sql.close();
130
+ await Bun.spawn(
131
+ [
132
+ "docker",
133
+ "compose",
134
+ "-p",
135
+ project,
136
+ "-f",
137
+ "compose.yml",
138
+ "-f",
139
+ "compose.store.sql.yml",
140
+ "down",
141
+ "-v",
142
+ ],
143
+ { cwd: dockerDir, stdout: "pipe", stderr: "pipe" },
144
+ ).exited.catch(() => {});
145
+ await rm(dir, { recursive: true, force: true }).catch(() => {});
108
146
  }
109
-
110
- // Prove credentials live in docker/.env.docker, not YAML.
111
- const yml = await Bun.file(join(dockerDir, "compose.store.sql.yml")).text();
112
- expect(yml).not.toContain("stack-integration-pass");
113
- expect(formatStackEnv(derived.stackEnv)).toContain("stack-integration-pass");
114
- expect(await Bun.file(join(dockerDir, ".env.docker")).exists()).toBe(true);
115
- } finally {
116
- await Bun.spawn(
117
- [
118
- "docker",
119
- "compose",
120
- "-p",
121
- project,
122
- "-f",
123
- "compose.yml",
124
- "-f",
125
- "compose.store.sql.yml",
126
- "down",
127
- "-v",
128
- ],
129
- { cwd: dockerDir, stdout: "pipe", stderr: "pipe" },
130
- ).exited.catch(() => {});
131
- await rm(dir, { recursive: true, force: true }).catch(() => {});
132
- }
133
- }, 120_000);
147
+ },
148
+ 120_000,
149
+ );
134
150
  });
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Live Ollama tool-calling through fx.ask → fx.call (capability + ledger).
3
3
  *
4
- * Same skip-visible convention as ai-ollama.integration.test.ts.
4
+ * Opt-in via `OKE_TEST_OLLAMA_URL` (reachable Ollama). A local daemon on
5
+ * :11434 is not enough — set the URL explicitly. Never an empty pass.
5
6
  */
6
7
 
7
8
  import { describe, expect, test } from "bun:test";
@@ -11,7 +12,6 @@ import { createGateRuntime, gate } from "../elements/gate.ts";
11
12
  import { createFxContext } from "../kernel/fx.ts";
12
13
  import { OLLAMA_DEFAULT_MODEL, openOllama } from "./ai-ollama.ts";
13
14
 
14
- const DEFAULT_LOCAL = "http://127.0.0.1:11434";
15
15
  const ENV_URL = process.env.OKE_TEST_OLLAMA_URL?.trim();
16
16
 
17
17
  async function probeOllama(url: string): Promise<boolean> {
@@ -23,12 +23,14 @@ async function probeOllama(url: string): Promise<boolean> {
23
23
  }
24
24
  }
25
25
 
26
- const localUp = ENV_URL ? await probeOllama(ENV_URL) : await probeOllama(DEFAULT_LOCAL);
27
- const canLive = Boolean(ENV_URL) || localUp;
26
+ const reachable = ENV_URL ? await probeOllama(ENV_URL) : false;
27
+ const canLive = Boolean(ENV_URL) && reachable;
28
28
 
29
29
  if (!canLive) {
30
30
  console.log(
31
- "skip: live ollama tool-calling e2e (OKE_TEST_OLLAMA_URL not set; no Ollama on :11434)",
31
+ ENV_URL
32
+ ? `skip: live ollama tool-calling e2e (unreachable at ${ENV_URL})`
33
+ : "skip: live ollama tool-calling e2e (OKE_TEST_OLLAMA_URL not set)",
32
34
  );
33
35
  }
34
36
  const live = canLive ? test : test.skip;
@@ -37,7 +39,7 @@ describe("ollama live — tool-calling via fx.call", () => {
37
39
  live(
38
40
  "gated ask → real tool call → capability ledger portal entry",
39
41
  async () => {
40
- const url = ENV_URL || DEFAULT_LOCAL;
42
+ const url = ENV_URL!;
41
43
  const model = process.env.OKE_AI_MODEL?.trim() || OLLAMA_DEFAULT_MODEL;
42
44
 
43
45
  const kv = await memoryKvDriver.open({ name: "ollama-tools-rate" });
@@ -3,11 +3,11 @@
3
3
  *
4
4
  * Opt-in (never auto-pull a multi-GB model in ordinary `bun test`):
5
5
  * 1. `OKE_TEST_OLLAMA_URL` — point at any reachable Ollama
6
- * 2. Ollama already listening on `http://127.0.0.1:11434`
7
- * 3. `OKE_TEST_OLLAMA_DOCKER=1` — start `ollama/ollama` via the recipe and pull
6
+ * 2. `OKE_TEST_OLLAMA_DOCKER=1` start `ollama/ollama` via the recipe and pull
8
7
  * `qwen3.5:9b` (slow first run)
9
8
  *
10
9
  * Without one of those, the suite skips with a visible reason (never an empty pass).
10
+ * A local daemon on :11434 is not enough — set `OKE_TEST_OLLAMA_URL` explicitly.
11
11
  */
12
12
 
13
13
  import { afterAll, describe, expect, test } from "bun:test";
@@ -20,7 +20,6 @@ import { bindAi } from "../kernel/boot-bind/ai.ts";
20
20
  import { OLLAMA_DEFAULT_MODEL, openOllama } from "./ai-ollama.ts";
21
21
 
22
22
  const OLLAMA_IMAGE = "ollama/ollama:latest";
23
- const DEFAULT_LOCAL = "http://127.0.0.1:11434";
24
23
  const ENV_URL = process.env.OKE_TEST_OLLAMA_URL?.trim();
25
24
  const WANT_DOCKER = process.env.OKE_TEST_OLLAMA_DOCKER === "1";
26
25
 
@@ -32,23 +31,12 @@ function dockerAvailable(): boolean {
32
31
  }
33
32
  }
34
33
 
35
- async function probeOllama(url: string): Promise<boolean> {
36
- try {
37
- const res = await fetch(`${url}/api/tags`, { signal: AbortSignal.timeout(1_500) });
38
- return res.ok;
39
- } catch {
40
- return false;
41
- }
42
- }
43
-
44
34
  const DOCKER = dockerAvailable();
45
- const localUp = ENV_URL ? false : await probeOllama(DEFAULT_LOCAL);
46
- const canLive = Boolean(ENV_URL) || localUp || (WANT_DOCKER && DOCKER);
35
+ const canLive = Boolean(ENV_URL) || (WANT_DOCKER && DOCKER);
47
36
 
48
37
  if (!canLive) {
49
38
  const reasons: string[] = [];
50
39
  if (!ENV_URL) reasons.push("OKE_TEST_OLLAMA_URL not set");
51
- if (!localUp) reasons.push("no Ollama on :11434");
52
40
  if (!WANT_DOCKER) reasons.push("OKE_TEST_OLLAMA_DOCKER≠1");
53
41
  else if (!DOCKER) reasons.push("docker daemon not available");
54
42
  console.log(`skip: live ollama e2e (${reasons.join("; ")})`);
@@ -94,10 +82,6 @@ async function resolveLiveUrl(): Promise<{ url: string; model: string }> {
94
82
  return { url: ENV_URL, model: process.env.OKE_AI_MODEL?.trim() || OLLAMA_DEFAULT_MODEL };
95
83
  }
96
84
 
97
- if (localUp) {
98
- return { url: DEFAULT_LOCAL, model: process.env.OKE_AI_MODEL?.trim() || OLLAMA_DEFAULT_MODEL };
99
- }
100
-
101
85
  const dir = await mkdtemp(join(tmpdir(), "oke-ollama-"));
102
86
  const dockerDir = join(dir, "docker");
103
87
  const project = `oke-ollama-${Date.now()}`;