import type { ExecInput, ExecResult } from "./sandbox/interface.js"; import type { SpawnOptions } from "child_process"; import { process_patch } from "./apply-patch.js"; import { SandboxType } from "./sandbox/interface.js"; import { execWithSeatbelt } from "./sandbox/macos-seatbelt.js"; import { exec as rawExec } from "./sandbox/raw-exec.js"; import { formatCommandForDisplay } from "../../format-command.js"; import fs from "fs"; import os from "os"; const DEFAULT_TIMEOUT_MS = 10_000; // 10 seconds /** * This function should never return a rejected promise: errors should be * mapped to a non-zero exit code and the error message should be in stderr. */ export function exec( { cmd, workdir, timeoutInMillis, additionalWritableRoots, }: ExecInput & { additionalWritableRoots: ReadonlyArray }, sandbox: SandboxType, abortSignal?: AbortSignal, ): Promise { // This is a temporary measure to understand what are the common base commands // until we start persisting and uploading rollouts const execForSandbox = sandbox === SandboxType.MACOS_SEATBELT ? execWithSeatbelt : rawExec; const opts: SpawnOptions = { timeout: timeoutInMillis || DEFAULT_TIMEOUT_MS, ...(workdir ? { cwd: workdir } : {}), }; // Merge default writable roots with any user-specified ones. const writableRoots = [ process.cwd(), os.tmpdir(), ...additionalWritableRoots, ]; return execForSandbox(cmd, opts, writableRoots, abortSignal); } export function execApplyPatch(patchText: string): ExecResult { // This is a temporary measure to understand what are the common base commands // until we start persisting and uploading rollouts try { const result = process_patch( patchText, (p) => fs.readFileSync(p, "utf8"), (p, c) => fs.writeFileSync(p, c, "utf8"), (p) => fs.unlinkSync(p), ); return { stdout: result, stderr: "", exitCode: 0, }; } catch (error: unknown) { // @ts-expect-error error might not be an object or have a message property. const stderr = String(error.message ?? error); return { stdout: "", stderr: stderr, exitCode: 1, }; } } export function getBaseCmd(cmd: Array): string { const formattedCommand = formatCommandForDisplay(cmd); return formattedCommand.split(" ")[0] || cmd[0] || ""; }