diff --git a/CHANGELOG.md b/CHANGELOG.md index e9f7c5c..841645b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Added a `pi` provider for routing review, fix, revalidate, and agent map through the [pi coding agent](https://pi.dev) in non-interactive print mode, thanks @danielmarbach. - Added explicit Codex reasoning effort selection via `--reasoning-effort`, `CLAWPATCH_REASONING_EFFORT`, and provider config, with `doctor` reporting the active setting. - Added `--skip-git-repo-check` for Codex-backed map, review, fix, and revalidate commands so initialized non-Git roots can run Codex, thanks @im-zayan. +- Added `CLAWPATCH_CODEX_SANDBOX` for overriding Codex provider sandbox mode when the host already provides isolation, thanks @IAMSamuelRodda. - Added deterministic Express, Fastify, and Hono route mapping for Node projects, thanks @rohitjavvadi. - Fixed provider commands with relative `--root` paths by canonicalizing explicit roots before invoking Codex or other providers. - Added first-pass Elixir Mix/Phoenix mapping for project metadata, contexts, Phoenix web slices, runtime config, Ecto migrations, project scripts, ExUnit tests, and Mix validation defaults, thanks @tears-mysthrala. diff --git a/README.md b/README.md index 4e4538f..d833584 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,11 @@ Provider calls use `codex exec` with strict JSON schemas. Review and revalidate run read-only; fix planning runs with workspace-write because Codex may edit the working tree during the explicit fix command. +Set `CLAWPATCH_CODEX_SANDBOX` to override the Codex sandbox passed by +Clawpatch. Use any Codex sandbox mode, or `bypass`/`none` to pass +`--dangerously-bypass-approvals-and-sandbox` when the host environment already +provides isolation. + Supported provider names today: - `codex`: local Codex CLI diff --git a/src/provider.test.ts b/src/provider.test.ts index 93a4666..eb9197b 100644 --- a/src/provider.test.ts +++ b/src/provider.test.ts @@ -1,10 +1,11 @@ -import { describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it } from "vitest"; import { ClawpatchError } from "./errors.js"; import { __testing, extractJson, providerByName } from "./provider.js"; import { reviewOutputSchema } from "./types.js"; // eslint-disable-next-line no-underscore-dangle const { + addCodexSandboxArgs, addCodexModelArgs, acpxFailureMessage, extractAcpxJson, @@ -127,6 +128,52 @@ describe("parseCodexJson", () => { }); describe("Codex provider args", () => { + const originalCodexSandbox = process.env["CLAWPATCH_CODEX_SANDBOX"]; + + afterEach(() => { + if (originalCodexSandbox === undefined) { + delete process.env["CLAWPATCH_CODEX_SANDBOX"]; + } else { + process.env["CLAWPATCH_CODEX_SANDBOX"] = originalCodexSandbox; + } + }); + + it("uses the requested Codex sandbox by default", () => { + delete process.env["CLAWPATCH_CODEX_SANDBOX"]; + const args = ["exec"]; + + addCodexSandboxArgs(args, "read-only"); + + expect(args).toEqual(["exec", "--sandbox", "read-only"]); + }); + + it("allows Codex sandbox mode to be overridden by environment", () => { + process.env["CLAWPATCH_CODEX_SANDBOX"] = " danger-full-access "; + const args = ["exec"]; + + addCodexSandboxArgs(args, "read-only"); + + expect(args).toEqual(["exec", "--sandbox", "danger-full-access"]); + }); + + it("ignores blank Codex sandbox overrides", () => { + process.env["CLAWPATCH_CODEX_SANDBOX"] = " "; + const args = ["exec"]; + + addCodexSandboxArgs(args, "read-only"); + + expect(args).toEqual(["exec", "--sandbox", "read-only"]); + }); + + it("can bypass Codex sandboxing when the host already provides isolation", () => { + process.env["CLAWPATCH_CODEX_SANDBOX"] = " none "; + const args = ["exec"]; + + addCodexSandboxArgs(args, "read-only"); + + expect(args).toEqual(["exec", "--dangerously-bypass-approvals-and-sandbox"]); + }); + it("passes model and reasoning effort through explicit CLI config", () => { const args = ["exec"]; diff --git a/src/provider.ts b/src/provider.ts index 5bbae55..972edf3 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -452,13 +452,12 @@ async function runCodexJson( "exec", "--cd", root, - "--sandbox", - sandbox, "--output-schema", schemaPath, "--output-last-message", outputPath, ]; + addCodexSandboxArgs(args, sandbox); addCodexModelArgs(args, options); args.push("-"); const result = await runCommandArgs("codex", args, root, prompt); @@ -479,6 +478,15 @@ async function runCodexJson( } } +function addCodexSandboxArgs(args: string[], sandbox: string): void { + const override = process.env["CLAWPATCH_CODEX_SANDBOX"]?.trim(); + if (override === "bypass" || override === "none") { + args.push("--dangerously-bypass-approvals-and-sandbox"); + return; + } + args.push("--sandbox", override && override.length > 0 ? override : sandbox); +} + function addCodexModelArgs(args: string[], options: ProviderOptions): void { if (options.skipGitRepoCheck) { args.push("--skip-git-repo-check"); @@ -974,6 +982,7 @@ function acpxTimeoutMs(): number { export const __testing = { acpxFailureMessage, addCodexModelArgs, + addCodexSandboxArgs, extractAcpxJson, extractOpencodeJson, parseAcpxAgent,