import crypto from "node:crypto"; import net from "node:net"; export type ExecHostRequest = { command: string[]; rawCommand?: string | null; cwd?: string | null; env?: Record | null; timeoutMs?: number | null; needsScreenRecording?: boolean | null; agentId?: string | null; sessionKey?: string | null; approvalDecision?: "allow-once" | "allow-always" | null; }; export type ExecHostRunResult = { exitCode?: number; timedOut: boolean; success: boolean; stdout: string; stderr: string; error?: string | null; }; export type ExecHostError = { code: string; message: string; reason?: string; }; export type ExecHostResponse = | { ok: true; payload: ExecHostRunResult } | { ok: false; error: ExecHostError }; export async function requestExecHostViaSocket(params: { socketPath: string; token: string; request: ExecHostRequest; timeoutMs?: number; }): Promise { const { socketPath, token, request } = params; if (!socketPath || !token) return null; const timeoutMs = params.timeoutMs ?? 20_000; return await new Promise((resolve) => { const client = new net.Socket(); let settled = false; let buffer = ""; const finish = (value: ExecHostResponse | null) => { if (settled) return; settled = true; try { client.destroy(); } catch { // ignore } resolve(value); }; const requestJson = JSON.stringify(request); const nonce = crypto.randomBytes(16).toString("hex"); const ts = Date.now(); const hmac = crypto .createHmac("sha256", token) .update(`${nonce}:${ts}:${requestJson}`) .digest("hex"); const payload = JSON.stringify({ type: "exec", id: crypto.randomUUID(), nonce, ts, hmac, requestJson, }); const timer = setTimeout(() => finish(null), timeoutMs); client.on("error", () => finish(null)); client.connect(socketPath, () => { client.write(`${payload}\n`); }); client.on("data", (data) => { buffer += data.toString("utf8"); let idx = buffer.indexOf("\n"); while (idx !== -1) { const line = buffer.slice(0, idx).trim(); buffer = buffer.slice(idx + 1); idx = buffer.indexOf("\n"); if (!line) continue; try { const msg = JSON.parse(line) as { type?: string; ok?: boolean; payload?: unknown; error?: unknown; }; if (msg?.type === "exec-res") { clearTimeout(timer); if (msg.ok === true && msg.payload) { finish({ ok: true, payload: msg.payload as ExecHostRunResult }); return; } if (msg.ok === false && msg.error) { finish({ ok: false, error: msg.error as ExecHostError }); return; } finish(null); return; } } catch { // ignore } } }); }); }