import { afterEach, describe, expect, test } from "bun:test"; import { mkdtemp, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { userPrincipal } from "../auth/planes.ts"; import { projectAiPanel } from "../console/server/ai.ts"; import type { ConsoleState } from "../console/server/state.ts"; import { resetAiDecls } from "../elements/ai.ts"; import { resetFlowSeq } from "../kernel/flow.ts"; import { resetBindings } from "../kernel/on.ts"; import { attachHostToConsole } from "./attach-host-console.ts"; const OKE = resolve(import.meta.dir, "../index.ts"); afterEach(() => { resetBindings(); resetFlowSeq(); resetAiDecls(); }); describe("attachHostToConsole", () => { test("wires invoke-as and the host store onto Console state", async () => { const dir = await mkdtemp(join(tmpdir(), "oke-attach-")); const entry = join(dir, "app.ts"); await writeFile( entry, ` import { oke, on, flow, http, gate, store } from ${JSON.stringify(OKE)}; const db = store.sql("db"); export const ping = on( http.get("/ping").public(), flow("main.ping", { do: () => ({ ok: true }) }), ); export const app = oke({ name: "attach-host", stores: [db], gate: { policies: [gate.public] }, }).adopt({ ping }); `, ); const state = { invokeUserFlow: null, storeRuntime: null } as unknown as ConsoleState; const ingested: Array<{ event?: { flow?: string } }> = []; const attached = await attachHostToConsole({ entry, cwd: dir, state, runsBridge: { url: "http://console.test/console/runs/ingest", secret: "test-ingest", fetch: async (_url, init) => { ingested.push(JSON.parse(String(init?.body ?? "{}")) as { event?: { flow?: string } }); return new Response(null, { status: 204 }); }, }, }); expect(attached).not.toBeNull(); expect(state.invokeUserFlow).toBeTypeOf("function"); expect(state.storeRuntime).toBeTruthy(); const result = await state.invokeUserFlow!({ flowId: "main.ping", body: {}, principal: userPrincipal({ userId: "u1", verified: true }), operatorId: "op", }); expect(result.status).toBe(200); expect(result.output).toEqual({ ok: true }); expect(ingested.some((row) => row.event?.flow === "main.ping")).toBe(true); await attached!.stop(); expect(state.invokeUserFlow).toBeNull(); expect(state.storeRuntime).toBeNull(); }); test("binds host aiRuntime so Console projection sees a journaled ask", async () => { const dir = await mkdtemp(join(tmpdir(), "oke-attach-ai-")); const entry = join(dir, "app.ts"); await writeFile( entry, ` import { oke, on, flow, http, gate, ai } from ${JSON.stringify(OKE)}; const smart = ai.model("smart", { provider: "mock" }); smart.prompt("ticket-triage", { version: 2 }); export const ping = on( http.get("/ping").public(), flow("main.ping", { effects: { asks: ["ticket-triage"] }, do: (_input, fx) => fx.ask("ticket-triage", { subject: "hello" }), }), ); export const app = oke({ name: "attach-ai", gate: { policies: [gate.public] }, }).adopt({ ping }); `, ); const state = { invokeUserFlow: null, storeRuntime: null, aiRuntime: null, } as unknown as ConsoleState; const attached = await attachHostToConsole({ entry, cwd: dir, state }); expect(attached).not.toBeNull(); expect(state.aiRuntime).toBeTruthy(); const result = await state.invokeUserFlow!({ flowId: "main.ping", body: {}, principal: userPrincipal({ userId: "u1", verified: true }), operatorId: "op", }); expect(result.status).toBe(200); const projection = projectAiPanel({ manifest: null, aiRuntime: state.aiRuntime, }); expect(projection.journal.length).toBeGreaterThan(0); expect(projection.journal[0]!.prompt).toBe("ticket-triage"); expect(projection.journal[0]!.inputTokens).toBeGreaterThan(0); expect(projection.versions.length).toBeGreaterThan(0); await attached!.stop(); }); test("Console Access uses the host apiKeyStore instance (not Console SQL)", async () => { const dir = await mkdtemp(join(tmpdir(), "oke-attach-keys-")); const entry = join(dir, "app.ts"); await writeFile( entry, ` import { oke, on, flow, http, gate, store } from ${JSON.stringify(OKE)}; const db = store.sql("db"); const member = gate.policy("member", ({ auth }) => !!auth.verified); export const ping = on( http.get("/ping").gate(member), flow("main.ping", { do: () => ({ ok: true }) }), ); export const app = oke({ name: "attach-keys", stores: [db], gate: { auth: { secret: "attach-host-key-pepper", http: false }, policies: [member], }, }).adopt({ ping }); `, ); const consoleKeys = { keys: new Map(), persist: async () => {} }; const state = { invokeUserFlow: null, storeRuntime: null, apiKeys: consoleKeys, } as unknown as ConsoleState; const attached = await attachHostToConsole({ entry, cwd: dir, state }); expect(attached).not.toBeNull(); expect(state.apiKeys).not.toBe(consoleKeys); expect(state.apiKeys.persist).not.toBe(consoleKeys.persist); await attached!.stop(); }); test("returns null when the entry is not a bootable app", async () => { const dir = await mkdtemp(join(tmpdir(), "oke-attach-empty-")); const entry = join(dir, "app.ts"); await writeFile(entry, "export {}\n"); const state = { invokeUserFlow: null, storeRuntime: null } as unknown as ConsoleState; const attached = await attachHostToConsole({ entry, cwd: dir, state }); expect(attached).toBeNull(); expect(state.invokeUserFlow).toBeNull(); }); });