// End-to-end on the fakes: a scripted bot on a real server and a real agents proxy shows a React page, the person // points at part of it and asks for a change, the bot replaces it with update_html, and the person shares it from the // page's card. One loopback stub stands in for the Admin's token route and the pages Worker; no model is called and // no external service is reached. import { randomBytes } from "node:crypto"; import { createServer, type Server } from "node:http"; import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { afterAll, beforeAll, expect, it } from "vitest"; import { launchVerificationServer, runControlOmb } from "../scripts/control-omb.ts"; import { BOT_HTML_CSP } from "../shared/app-frame/csp.ts"; import { CSP_META_PREFIX } from "../shared/app-frame/document.ts"; import { FAKE_DESKTOP_TOKEN, fakePagesService } from "./testing/fake-pages.ts"; const owner = randomBytes(32).toString("base64url"); const realFetch = globalThis.fetch; let stub: { server: Server; url: string; service: ReturnType }; let fixture: Awaited>; const PRICING = `import { useState } from "react"; import { Check } from "lucide-react"; const plans = [{ name: "Starter", price: 15 }, { name: "Pro", price: 49 }, { name: "Team", price: 99 }]; export default function Pricing() { const [picked, setPicked] = useState("Pro"); return
{plans.map((plan) => (

{plan.name}

\${plan.price}

))}

{picked}

; }`; const PRICING_V2 = PRICING.replace("rounded-2xl border p-5", "rounded-2xl border p-5 data-[plan=Pro]:ring-2").replace("
; const server = createServer((req, res) => { const chunks: Buffer[] = []; req.on("data", (chunk: Buffer) => chunks.push(chunk)); req.on("end", async () => { const body = Buffer.concat(chunks); const answer = await service.fetch(new Request(`${url}${req.url}`, { method: req.method, headers: req.headers as Record, ...(body.length ? { body } : {}) })).catch(() => new Response("{}", { status: 404 })); res.writeHead(answer.status, { "content-type": "application/json" }); res.end(Buffer.from(await answer.arrayBuffer())); }); }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); const address = server.address(); if (!address || typeof address === "string") throw new Error("no stub port"); const url = `http://127.0.0.1:${address.port}`; service = fakePagesService({ admin: url, upload: url }); return { server, url, service }; } beforeAll(async () => { stub = await serveStub(); globalThis.fetch = (input, init = {}) => realFetch(input, { ...init, headers: { ...Object.fromEntries(new Headers(init.headers)), "x-openmausbot-desktop-owner": owner } }); fixture = await launchVerificationServer({ ...process.env, OMB_TEST_DESKTOP_OWNER_TOKEN: owner, OMB_TEST_DESKTOP_PAGES_ACCESS: JSON.stringify({ url: `${stub.url}/api/cloud/pages/token`, token: FAKE_DESKTOP_TOKEN }) }, undefined, undefined, undefined, undefined, { scripted: true }); }, 90_000); afterAll(async () => { globalThis.fetch = realFetch; stub?.server.close(); await fixture?.close(); }); it("shows a jsx page, takes what the person pointed at to the bot as records, replaces the page, and publishes it as static HTML", async () => { const cli = (...args: string[]) => runControlOmb(args, { env: { OPENMAUSBOT_URL: fixture.info.url } }) as Promise; const api = async (path: string, init: RequestInit = {}) => { const response = await fetch(`${fixture.info.url}${path}`, { ...init, headers: { "content-type": "application/json" } }); return { status: response.status, body: await response.json() as any }; }; const planPath = join(fixture.info.dataDir, "room-plan.json"); const bot = (await cli("new-bot", "--name", "Designer")).bot; const threadId = bot.activeTaskId as string; const evidence = () => (existsSync(`${planPath}.evidence.jsonl`) ? readFileSync(`${planPath}.evidence.jsonl`, "utf8").trim().split("\n").filter(Boolean) : []) .map((line) => JSON.parse(line)).filter((entry: any) => entry.botId === bot.id); const plan = (steps: Array<{ tool: string; arguments: unknown; expectError?: boolean }>) => writeFileSync(planPath, JSON.stringify({ [bot.id]: { steps, reply: "Done." } })); const settled = async () => expect((await cli("wait", "--bot", bot.id, "--task", threadId, "--timeout", "30")).status).toBe("settled"); const results = (turn: any) => turn.evidence.filter((entry: any) => entry.step).map((entry: any) => entry.response.result.content[0].text as string); const transcript = async () => (await api(`/api/threads/${threadId}/messages?limit=50`)).body.messages as any[]; plan([ { tool: "show_html", arguments: { jsx: "export default function Page() {\n const total = ;\n}", title: "Broken" }, expectError: true }, { tool: "show_html", arguments: { jsx: PRICING, title: "Pricing" } }, ]); await cli("send", "--bot", bot.id, "--task", threadId, "--text", "Make me a pricing page with three plans."); await settled(); const [refused, shown] = results(evidence().at(-1)); expect(refused).toBe("jsx: line 2: Unexpected token. Fix it and send the whole file again."); const appId = /^Shown as app ([\w-]+)\./.exec(shown!)?.[1]; expect(appId, shown).toBeTruthy(); const row = (await transcript()).find((message) => message.id === appId); expect(row).toMatchObject({ kind: "app", text: "[App] Pricing", app: { source: "html", format: "jsx", rev: 0, title: "Pricing" } }); const first = await api(`/api/threads/${threadId}/messages/${appId}/app`); expect(first.status).toBe(200); expect(String(first.body.srcdoc).startsWith(`${CSP_META_PREFIX}${BOT_HTML_CSP}">`)).toBe(true); expect(first.body.srcdoc).toContain("