import crypto from "node:crypto"; import { requestJsonlSocket } from "./jsonl-socket.js"; 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; 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, }); return await requestJsonlSocket({ socketPath, payload, timeoutMs, accept: (value) => { const msg = value as { type?: string; ok?: boolean; payload?: unknown; error?: unknown }; if (msg?.type !== "exec-res") { return undefined; } if (msg.ok === true && msg.payload) { return { ok: true, payload: msg.payload as ExecHostRunResult }; } if (msg.ok === false && msg.error) { return { ok: false, error: msg.error as ExecHostError }; } return null; }, }); }