import assert from "node:assert/strict"; import test from "node:test"; import { createServer, type Server } from "node:http"; import type { WebSocketServer } from "ws"; import { formatFailure, startCraftos } from "./harness.js"; import { startOpencodeProxy } from "../src/opencode-proxy.js"; test("libhttpws drives a synchronous opencode call through the bridge proxy", async () => { const opencode = createServer((_req, res) => { res.writeHead(200, { "content-type": "application/json" }); res.end(JSON.stringify({ info: { finish: "stop" }, parts: [{ type: "text", text: "pong" }] })); }); await new Promise((resolve) => opencode.listen(0, "127.0.0.1", resolve)); const opencodeUrl = `http://127.0.0.1:${serverPort(opencode)}`; const proxy = startOpencodeProxy({ host: "127.0.0.1", port: 0, opencodeUrl }); await waitForListening(proxy); const proxyUrl = `ws://127.0.0.1:${wssPort(proxy)}`; const craftos = startCraftos("opencode-proxy-check.lua", { mountRepo: true, shellArgs: [proxyUrl], timeoutMs: 20_000, }); try { const result = await craftos.done; assert.match(result.output, /STATUS=200/, formatFailure("expected STATUS=200", result.output)); assert.match(result.output, /pong/, formatFailure("expected reply body to contain pong", result.output)); } finally { craftos.abort(); await craftos.done.catch(() => undefined); await closeWss(proxy); await new Promise((resolve) => opencode.close(() => resolve())); } }); function serverPort(server: Server): number { const address = server.address(); if (!address || typeof address === "string") { throw new Error("server has no TCP address"); } return address.port; } function wssPort(proxy: WebSocketServer): number { const address = proxy.address(); if (!address || typeof address === "string") { throw new Error("proxy has no TCP address"); } return address.port; } function waitForListening(proxy: WebSocketServer): Promise { return new Promise((resolve, reject) => { if (proxy.address()) { resolve(); return; } proxy.once("listening", () => resolve()); proxy.once("error", reject); }); } function closeWss(proxy: WebSocketServer): Promise { for (const client of proxy.clients) { client.terminate(); } return new Promise((resolve) => proxy.close(() => resolve())); }