From edb1db681d97b8b1b7323108663f68fc4bc2177c Mon Sep 17 00:00:00 2001 From: "Jonathan B. Coe" Date: Sat, 17 Oct 2026 12:14:34 +0100 Subject: [PATCH] Unconditionally seed agent config files and mount them for the agentic sandbox. The previous setup meant that running the sandbox and later starting an agent would run the agent without user-authentication. --- CONTRIBUTING.md | 4 +- scripts/agentic-sandbox.py | 79 ++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4370ffa..50a6417 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -253,8 +253,8 @@ plain shell in the container. `claude` starts in the permission mode set in your host `~/.claude/settings.json`, which is mounted into the container. The container keeps the agent away from the rest of the host file system, but the network is unrestricted and the -project and `~/.claude`, credentials included, are mounted read-write. Pass -`--skip-permissions` only when that is acceptable. +project and the host config of every supported agent, credentials included, +are mounted read-write. Pass `--skip-permissions` only when that is acceptable. ### Sandbox image inputs diff --git a/scripts/agentic-sandbox.py b/scripts/agentic-sandbox.py index 27f7876..e942e94 100644 --- a/scripts/agentic-sandbox.py +++ b/scripts/agentic-sandbox.py @@ -127,45 +127,46 @@ def _identity_args() -> list[str]: return args -def _agent_mount_args(agent: str | None) -> list[str]: - """Seed and mount host config so an agent CLI keeps its auth state.""" +def _seed_agent_config() -> None: + """Create default host config files and directories for agent mounts.""" # Use os.open with restrictive permissions in _seed_config_file to avoid # exposing credentials to other local users on multi-user systems. - if agent == "agy": - gemini_config_dir = os.path.expanduser("~/.gemini") - os.makedirs(gemini_config_dir, mode=0o700, exist_ok=True) - _seed_config_file( - os.path.join(gemini_config_dir, "trustedFolders.json"), - b'{"/workspace": "TRUST_FOLDER"}', - ) - _seed_config_file( - os.path.join(gemini_config_dir, "settings.json"), - b'{"selectedAuthType": "oauth-personal"}', - ) - return ["-v", f"{gemini_config_dir}:/home/vscode/.gemini"] - - if agent == "claude": - host_claude_dir = os.path.expanduser("~/.claude") - host_claude_json = os.path.expanduser("~/.claude.json") - os.makedirs(host_claude_dir, mode=0o700, exist_ok=True) - # Ensure the file exists on the host so Docker doesn't create it as a - # directory. - if os.path.exists(host_claude_json): - if not os.path.isfile(host_claude_json): - sys.exit( - f"Expected {host_claude_json} to be a regular file, but found " - "a different filesystem object. Remove or rename it and rerun." - ) - else: - _seed_config_file(host_claude_json, b"") - return [ - "-v", - f"{host_claude_dir}:/home/vscode/.claude", - "-v", - f"{host_claude_json}:/home/vscode/.claude.json", - ] - - return [] + gemini_config_dir = os.path.expanduser("~/.gemini") + os.makedirs(gemini_config_dir, mode=0o700, exist_ok=True) + _seed_config_file( + os.path.join(gemini_config_dir, "trustedFolders.json"), + b'{"/workspace": "TRUST_FOLDER"}', + ) + _seed_config_file( + os.path.join(gemini_config_dir, "settings.json"), + b'{"selectedAuthType": "oauth-personal"}', + ) + + host_claude_dir = os.path.expanduser("~/.claude") + host_claude_json = os.path.expanduser("~/.claude.json") + os.makedirs(host_claude_dir, mode=0o700, exist_ok=True) + # Ensure the file exists on the host so Docker doesn't create it as a + # directory. + if os.path.exists(host_claude_json): + if not os.path.isfile(host_claude_json): + sys.exit( + f"Expected {host_claude_json} to be a regular file, but found " + "a different filesystem object. Remove or rename it and rerun." + ) + else: + _seed_config_file(host_claude_json, b"") + + +def _agent_mount_args() -> list[str]: + """Mount host config so agent CLIs keep their auth state.""" + return [ + "-v", + f"{os.path.expanduser('~/.gemini')}:/home/vscode/.gemini", + "-v", + f"{os.path.expanduser('~/.claude')}:/home/vscode/.claude", + "-v", + f"{os.path.expanduser('~/.claude.json')}:/home/vscode/.claude.json", + ] def main() -> None: @@ -213,6 +214,8 @@ def main() -> None: if args.skip_permissions and args.agent != "claude": parser.error("--skip-permissions requires the claude agent") + _seed_agent_config() + def log(msg: str) -> None: if args.verbose: print(msg) @@ -273,7 +276,7 @@ def log(msg: str) -> None: if args.offline: run_args.extend(["--network", "none"]) - run_args.extend(_agent_mount_args(args.agent)) + run_args.extend(_agent_mount_args()) run_args.extend(_identity_args()) if "TERM" in os.environ: