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: