Skip to content

Commit 0c790d4

Browse files
fix: pre-accept Claude Code ToS during image build
Claude Code CLI has first-run onboarding prompts that block non-interactive execution. Pre-configuring settings.json with hasCompletedOnboarding skips these prompts. Also removes verbose diagnostic tests and adds --dangerously-skip-permissions flag.
1 parent 0957c01 commit 0c790d4

1 file changed

Lines changed: 7 additions & 87 deletions

File tree

src/policyengine_api/agent_sandbox.py

Lines changed: 7 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
"export BUN_INSTALL=/root/.bun && export PATH=$BUN_INSTALL/bin:$PATH && "
1919
"ln -s $BUN_INSTALL/bin/bun /usr/local/bin/node && "
2020
"bun install -g @anthropic-ai/claude-code",
21+
# Pre-accept ToS and configure for non-interactive use
22+
"mkdir -p /root/.claude && "
23+
'echo \'{"hasCompletedOnboarding": true, "hasAcknowledgedCostThreshold": true}\' '
24+
"> /root/.claude/settings.json",
2125
)
2226
.env(
2327
{
@@ -44,8 +48,6 @@ def run_claude_code_in_sandbox(
4448
"""
4549
import logfire
4650

47-
from policyengine_api.config import settings
48-
4951
print("[SANDBOX] run_claude_code_in_sandbox starting", flush=True)
5052
logfire.info(
5153
"run_claude_code_in_sandbox: starting",
@@ -82,44 +84,6 @@ def run_claude_code_in_sandbox(
8284
print("[SANDBOX] sandbox created", flush=True)
8385
logfire.info("run_claude_code_in_sandbox: sandbox created")
8486

85-
# Log from inside the sandbox via Python
86-
logfire.info("run_claude_code_in_sandbox: logging from inside sandbox")
87-
escaped_question = question[:50].replace("'", "\\'").replace('"', '\\"')
88-
sandbox_log = sb.exec(
89-
"python",
90-
"-c",
91-
f"""
92-
import logfire
93-
logfire.configure(token='{settings.logfire_token}', service_name='modal-sandbox-inner')
94-
logfire.info('Inside Modal sandbox', question='{escaped_question}')
95-
print('Logfire configured inside sandbox')
96-
""",
97-
)
98-
sandbox_log.wait()
99-
logfire.info(
100-
"run_claude_code_in_sandbox: sandbox inner log result",
101-
stdout=sandbox_log.stdout.read()[:200],
102-
stderr=sandbox_log.stderr.read()[:200],
103-
returncode=sandbox_log.returncode,
104-
)
105-
106-
# Check environment inside sandbox
107-
logfire.info("run_claude_code_in_sandbox: checking sandbox environment")
108-
env_check = sb.exec(
109-
"sh",
110-
"-c",
111-
"which claude && echo PATH=$PATH && echo ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:0:10}...",
112-
)
113-
env_check.wait()
114-
env_stdout = env_check.stdout.read()
115-
env_stderr = env_check.stderr.read()
116-
logfire.info(
117-
"run_claude_code_in_sandbox: env check",
118-
stdout=env_stdout[:500] if env_stdout else None,
119-
stderr=env_stderr[:500] if env_stderr else None,
120-
returncode=env_check.returncode,
121-
)
122-
12387
# Write MCP config
12488
logfire.info("run_claude_code_in_sandbox: writing MCP config")
12589
sb.exec("mkdir", "-p", "/root/.claude")
@@ -132,53 +96,6 @@ def run_claude_code_in_sandbox(
13296
returncode=config_process.returncode,
13397
)
13498

135-
# Verify config was written
136-
verify_process = sb.exec("cat", "/root/.claude/mcp_servers.json")
137-
verify_process.wait()
138-
logfire.info(
139-
"run_claude_code_in_sandbox: MCP config contents",
140-
config=verify_process.stdout.read()[:500],
141-
)
142-
143-
# Test Claude CLI works at all
144-
print("[SANDBOX] Testing claude --version", flush=True)
145-
logfire.info("run_claude_code_in_sandbox: testing claude --version")
146-
version_check = sb.exec("claude", "--version")
147-
version_check.wait()
148-
version_stdout = version_check.stdout.read()
149-
version_stderr = version_check.stderr.read()
150-
print(f"[SANDBOX] claude --version: stdout={version_stdout}, stderr={version_stderr}, rc={version_check.returncode}", flush=True)
151-
logfire.info(
152-
"run_claude_code_in_sandbox: claude --version result",
153-
stdout=version_stdout[:200] if version_stdout else None,
154-
stderr=version_stderr[:500] if version_stderr else None,
155-
returncode=version_check.returncode,
156-
)
157-
158-
# Quick test: run Claude with a simple query WITHOUT MCP to verify it works
159-
print("[SANDBOX] Testing simple claude query (no MCP)", flush=True)
160-
logfire.info("run_claude_code_in_sandbox: testing simple query without MCP")
161-
simple_test = sb.exec(
162-
"claude",
163-
"-p",
164-
"Say hello in one word",
165-
"--output-format",
166-
"text",
167-
"--dangerously-skip-permissions",
168-
"--max-turns",
169-
"1",
170-
)
171-
simple_test.wait()
172-
simple_stdout = simple_test.stdout.read()
173-
simple_stderr = simple_test.stderr.read()
174-
print(f"[SANDBOX] Simple test result: stdout={simple_stdout[:200] if simple_stdout else 'empty'}, stderr={simple_stderr[:200] if simple_stderr else 'empty'}, rc={simple_test.returncode}", flush=True)
175-
logfire.info(
176-
"run_claude_code_in_sandbox: simple test result",
177-
stdout=simple_stdout[:500] if simple_stdout else None,
178-
stderr=simple_stderr[:500] if simple_stderr else None,
179-
returncode=simple_test.returncode,
180-
)
181-
18299
# Run Claude Code with the question
183100
# --dangerously-skip-permissions: auto-accept permission prompts (required for non-interactive)
184101
# --max-turns: limit execution to prevent runaway
@@ -242,6 +159,9 @@ def run_policy_analysis(
242159
"claude",
243160
"-p",
244161
question,
162+
"--dangerously-skip-permissions",
163+
"--max-turns",
164+
"10",
245165
"--allowedTools",
246166
"mcp__policyengine__*,Bash,Read,Grep,Glob,Write,Edit",
247167
],

0 commit comments

Comments
 (0)