// Local UI-control HTTP bridge: a loopback server exposing the legacy // /snapshot, /actions and /execute routes plus the semantic /context, /query // and /command surface. Dispatched to the renderer's window.__openworkControl. // Extracted from main.mjs; state and lifecycle live in this factory // (createRuntimeManager pattern). import { randomBytes } from "node:crypto"; import { createServer } from "node:http"; import { rm, writeFile } from "node:fs/promises"; import path from "node:path"; export function createUiControlServer({ app, appName, appIdentifier, getWindow }) { let uiControlServer = null; let uiControlDiscoveryPath = null; const uiControlToken = randomBytes(32).toString("hex"); function sendJsonResponse(response, statusCode, payload) { response.writeHead(statusCode, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store", }); response.end(JSON.stringify(payload)); } function readJsonRequestBody(request) { return new Promise((resolve, reject) => { let raw = ""; request.setEncoding("utf8"); request.on("data", (chunk) => { raw += chunk; if (raw.length > 128_000) { reject(new Error("Request body too large")); request.destroy(); } }); request.on("end", () => { if (!raw.trim()) { resolve({}); return; } try { resolve(JSON.parse(raw)); } catch { reject(new Error("Request body must be JSON")); } }); request.on("error", reject); }); } function authorizedUiControlRequest(request) { const auth = request.headers.authorization ?? ""; return auth === `Bearer ${uiControlToken}`; } function jsonForJavaScript(value) { return JSON.stringify(JSON.stringify(value ?? {})); } async function evaluateOpenworkControl(expression) { const win = await getWindow(); // Commands mutate renderer state directly and do not require the desktop // window to become active. Foreground activation must be an explicit // affordance, never an implicit side effect of remote control. return win.webContents.executeJavaScript(expression, true); } async function runOpenworkControlCommand(command, args = {}) { const argsJsonLiteral = jsonForJavaScript(args); if (command === "snapshot") { return evaluateOpenworkControl(`(async () => { const control = window.__openworkControl; if (!control) return { ok: false, error: "OpenWork control surface is not available yet." }; control.setEnabled?.(true); return { ok: true, ...control.snapshot() }; })()`); } if (command === "actions") { return evaluateOpenworkControl(`(async () => { const control = window.__openworkControl; if (!control) return { ok: false, error: "OpenWork control surface is not available yet." }; control.setEnabled?.(true); return { ok: true, actions: control.listActions() }; })()`); } if (command === "context") { return evaluateOpenworkControl(`(async () => { const control = window.__openworkControl; if (!control) return { ok: false, error: "OpenWork control surface is not available yet." }; return { ok: true, context: control.context() }; })()`); } if (command === "query" || command === "command") { return evaluateOpenworkControl(`(async () => { const control = window.__openworkControl; const input = JSON.parse(${argsJsonLiteral}); if (!control) return { ok: false, error: "OpenWork control surface is not available yet." }; if (!input || typeof input.id !== "string" || !input.id.trim()) { return { ok: false, error: "Missing OpenWork affordance id." }; } return control[${JSON.stringify(command)}](input); })()`); } if (command === "execute") { return evaluateOpenworkControl(`(async () => { const control = window.__openworkControl; const input = JSON.parse(${argsJsonLiteral}); if (!control) return { ok: false, error: "OpenWork control surface is not available yet." }; if (!input || typeof input.actionId !== "string" || !input.actionId.trim()) { return { ok: false, error: "Missing OpenWork actionId." }; } control.setEnabled?.(true); return control.execute(input.actionId, input.args ?? {}); })()`); } return { ok: false, error: `Unknown OpenWork control command: ${command}` }; } async function start() { if (uiControlServer) return; uiControlServer = createServer(async (request, response) => { try { const url = new URL(request.url ?? "/", "http://127.0.0.1"); if (request.method === "GET" && url.pathname === "/health") { sendJsonResponse(response, 200, { ok: true, app: appName, version: 2 }); return; } if (!authorizedUiControlRequest(request)) { sendJsonResponse(response, 401, { ok: false, error: "Unauthorized" }); return; } if (request.method === "GET" && url.pathname === "/snapshot") { sendJsonResponse(response, 200, await runOpenworkControlCommand("snapshot")); return; } if (request.method === "GET" && url.pathname === "/actions") { sendJsonResponse(response, 200, await runOpenworkControlCommand("actions")); return; } if (request.method === "GET" && url.pathname === "/context") { sendJsonResponse(response, 200, await runOpenworkControlCommand("context")); return; } if (request.method === "POST" && url.pathname === "/query") { sendJsonResponse(response, 200, await runOpenworkControlCommand("query", await readJsonRequestBody(request))); return; } if (request.method === "POST" && url.pathname === "/command") { sendJsonResponse(response, 200, await runOpenworkControlCommand("command", await readJsonRequestBody(request))); return; } if (request.method === "POST" && url.pathname === "/execute") { sendJsonResponse(response, 200, await runOpenworkControlCommand("execute", await readJsonRequestBody(request))); return; } sendJsonResponse(response, 404, { ok: false, error: "Not found" }); } catch (error) { console.error("[ui-control] request failed", error); sendJsonResponse(response, 500, { ok: false, error: "OpenWork UI control request failed." }); } }); await new Promise((resolve, reject) => { uiControlServer.once("error", reject); uiControlServer.listen(0, "127.0.0.1", () => resolve(undefined)); }); const address = uiControlServer.address(); const port = typeof address === "object" && address ? address.port : null; if (!port) throw new Error("Could not start OpenWork UI control bridge."); uiControlDiscoveryPath = path.join(app.getPath("userData"), "openwork-ui-control.json"); await writeFile( uiControlDiscoveryPath, `${JSON.stringify({ version: 2, app: appName, identifier: appIdentifier, platform: process.platform, baseUrl: `http://127.0.0.1:${port}`, token: uiControlToken }, null, 2)}\n`, "utf8", ); // Make the discovery path available to child processes (server → managed OpenCode → plugin). process.env.OPENWORK_UI_CONTROL_DISCOVERY = uiControlDiscoveryPath; } async function stop() { if (uiControlDiscoveryPath) { await rm(uiControlDiscoveryPath, { force: true }).catch(() => undefined); uiControlDiscoveryPath = null; } if (!uiControlServer) return; await new Promise((resolve) => uiControlServer.close(() => resolve(undefined))); uiControlServer = null; } return { start, stop }; }