-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient.py
More file actions
230 lines (200 loc) · 8.62 KB
/
client.py
File metadata and controls
230 lines (200 loc) · 8.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
"""
Claude SDK Client Configuration
===============================
Functions for creating and configuring the Claude Agent SDK client.
"""
import json
import os
import shutil
import sys
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient
from claude_agent_sdk.types import HookMatcher
from dotenv import load_dotenv
from security import bash_security_hook
# Load environment variables from .env file if present
load_dotenv()
# Default Playwright headless mode - can be overridden via PLAYWRIGHT_HEADLESS env var
# When True, browser runs invisibly in background
# When False, browser window is visible (default - useful for monitoring agent progress)
DEFAULT_PLAYWRIGHT_HEADLESS = False
# Environment variables to pass through to Claude CLI for API configuration
# These allow using alternative API endpoints (e.g., GLM via z.ai) without
# affecting the user's global Claude Code settings
API_ENV_VARS = [
"ANTHROPIC_BASE_URL", # Custom API endpoint (e.g., https://api.z.ai/api/anthropic)
"ANTHROPIC_AUTH_TOKEN", # API authentication token
"API_TIMEOUT_MS", # Request timeout in milliseconds
"ANTHROPIC_DEFAULT_SONNET_MODEL", # Model override for Sonnet
"ANTHROPIC_DEFAULT_OPUS_MODEL", # Model override for Opus
"ANTHROPIC_DEFAULT_HAIKU_MODEL", # Model override for Haiku
]
def get_playwright_headless() -> bool:
"""
Get the Playwright headless mode setting.
Reads from PLAYWRIGHT_HEADLESS environment variable, defaults to False.
Returns True for headless mode (invisible browser), False for visible browser.
"""
value = os.getenv("PLAYWRIGHT_HEADLESS", "false").lower()
# Accept various truthy/falsy values
return value in ("true", "1", "yes", "on")
# Feature MCP tools for feature/test management
FEATURE_MCP_TOOLS = [
# Core feature operations
"mcp__features__feature_get_stats",
"mcp__features__feature_get_next",
"mcp__features__feature_claim_next", # Atomic get+claim for parallel execution
"mcp__features__feature_get_for_regression",
"mcp__features__feature_mark_in_progress",
"mcp__features__feature_mark_passing",
"mcp__features__feature_mark_failing", # Mark regression detected
"mcp__features__feature_skip",
"mcp__features__feature_create_bulk",
"mcp__features__feature_create",
"mcp__features__feature_clear_in_progress",
# Dependency management
"mcp__features__feature_add_dependency",
"mcp__features__feature_remove_dependency",
"mcp__features__feature_set_dependencies",
# Parallel execution support
"mcp__features__feature_get_ready",
"mcp__features__feature_get_blocked",
"mcp__features__feature_get_graph",
]
# NOTE: Playwright CLI is now used for browser automation instead of MCP server
# The agent invokes `playwright-cli` commands via bash (e.g., `playwright-cli open http://localhost:3000`)
# See .claude/skills/playwright-cli/SKILL.md for documentation
# This is simpler than the MCP server approach and has fewer moving parts
# Built-in tools
BUILTIN_TOOLS = [
"Read",
"Write",
"Edit",
"Glob",
"Grep",
"Bash",
"WebFetch",
"WebSearch",
]
def create_client(
project_dir: Path,
model: str,
yolo_mode: bool = False,
agent_id: str | None = None,
):
"""
Create a Claude Agent SDK client with multi-layered security.
Args:
project_dir: Directory for the project
model: Claude model to use
yolo_mode: If True, skip Playwright MCP server for rapid prototyping
agent_id: Optional unique identifier for browser isolation in parallel mode.
When provided, each agent gets its own browser profile.
Returns:
Configured ClaudeSDKClient (from claude_agent_sdk)
Security layers (defense in depth):
1. Sandbox - OS-level bash command isolation prevents filesystem escape
2. Permissions - File operations restricted to project_dir only
3. Security hooks - Bash commands validated against an allowlist
(see security.py for ALLOWED_COMMANDS)
Note: Authentication is handled by start.bat/start.sh before this runs.
The Claude SDK auto-detects credentials from the Claude CLI configuration
"""
# Build allowed tools list
# Note: Playwright CLI is invoked via bash commands, not MCP tools
allowed_tools = [*BUILTIN_TOOLS, *FEATURE_MCP_TOOLS]
# Build permissions list
permissions_list = [
# Allow all file operations within the project directory
"Read(./**)",
"Write(./**)",
"Edit(./**)",
"Glob(./**)",
"Grep(./**)",
# Bash permission granted here, but actual commands are validated
# by the bash_security_hook (see security.py for allowed commands)
"Bash(*)",
# Allow web tools for documentation lookup
"WebFetch",
"WebSearch",
# Allow Feature MCP tools for feature management
*FEATURE_MCP_TOOLS,
]
# Note: Playwright CLI is invoked via bash commands, not MCP tools
# Create comprehensive security settings
# Note: Using relative paths ("./**") restricts access to project directory
# since cwd is set to project_dir
security_settings = {
"sandbox": {"enabled": True, "autoAllowBashIfSandboxed": True},
"permissions": {
"defaultMode": "acceptEdits", # Auto-approve edits within allowed directories
"allow": permissions_list,
},
}
# Ensure project directory exists before creating settings file
project_dir.mkdir(parents=True, exist_ok=True)
# Write settings to a file in the project directory
settings_file = project_dir / ".claude_settings.json"
with open(settings_file, "w") as f:
json.dump(security_settings, f, indent=2)
print(f"Created security settings at {settings_file}")
print(" - Sandbox enabled (OS-level bash isolation)")
print(f" - Filesystem restricted to: {project_dir.resolve()}")
print(" - Bash commands restricted to allowlist (see security.py)")
print(" - MCP servers: features (database)")
print(" - Browser automation: playwright-cli (invoked via bash)")
print(" - Project settings enabled (skills, commands, CLAUDE.md)")
print()
# Use system Claude CLI instead of bundled one (avoids Bun runtime crash on Windows)
system_cli = shutil.which("claude")
if system_cli:
print(f" - Using system CLI: {system_cli}")
else:
print(" - Warning: System 'claude' CLI not found, using bundled CLI")
# Build MCP servers config - only features MCP server is needed
# Browser automation now uses playwright-cli via bash commands
mcp_servers = {
"features": {
"command": sys.executable, # Use the same Python that's running this script
"args": ["-m", "mcp_server.feature_mcp"],
"env": {
# Only specify variables the MCP server needs
# (subprocess inherits parent environment automatically)
"PROJECT_DIR": str(project_dir.resolve()),
"PYTHONPATH": str(Path(__file__).parent.resolve()),
},
},
}
# Build environment overrides for API endpoint configuration
# These override system env vars for the Claude CLI subprocess,
# allowing AutoCoder to use alternative APIs (e.g., GLM) without
# affecting the user's global Claude Code settings
sdk_env = {}
for var in API_ENV_VARS:
value = os.getenv(var)
if value:
sdk_env[var] = value
if sdk_env:
print(f" - API overrides: {', '.join(sdk_env.keys())}")
if "ANTHROPIC_BASE_URL" in sdk_env:
print(f" - GLM Mode: Using {sdk_env['ANTHROPIC_BASE_URL']}")
return ClaudeSDKClient(
options=ClaudeAgentOptions(
model=model,
cli_path=system_cli, # Use system CLI to avoid bundled Bun crash (exit code 3)
system_prompt="You are an expert full-stack developer building a production-quality web application.",
setting_sources=["project"], # Enable skills, commands, and CLAUDE.md from project dir
max_buffer_size=10 * 1024 * 1024, # 10MB for large Playwright screenshots
allowed_tools=allowed_tools,
mcp_servers=mcp_servers,
hooks={
"PreToolUse": [
HookMatcher(matcher="Bash", hooks=[bash_security_hook]),
],
},
max_turns=1000,
cwd=str(project_dir.resolve()),
settings=str(settings_file.resolve()), # Use absolute path
env=sdk_env, # Pass API configuration overrides to CLI subprocess
)
)