@kokorolx/ai-sandbox-wrapper 2.3.0-beta → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,9 +4,36 @@
4
4
 
5
5
  Protect your SSH keys, API tokens, and system files while using AI tools that need filesystem access.
6
6
 
7
- *Last updated: February 6, 2026*
7
+ *Last updated: February 9, 2026*
8
8
 
9
- ## ✨ New in v2.3.0-beta: Web Mode & Port Exposure
9
+ ---
10
+
11
+ ## 📑 Table of Contents
12
+
13
+ - [What's New](#-whats-new)
14
+ - [Why Use This?](#ī¸-why-use-this)
15
+ - [Quick Start](#-quick-start)
16
+ - [Configuration](#ī¸-configuration)
17
+ - [API Keys](#api-keys)
18
+ - [Workspaces](#workspaces)
19
+ - [Port Exposure](#port-exposure)
20
+ - [Server Authentication](#server-authentication)
21
+ - [Network Access](#network-access)
22
+ - [Git Access](#git-access)
23
+ - [Clipboard](#clipboard)
24
+ - [Directory Structure](#-directory-structure)
25
+ - [Security Model](#-security-model)
26
+ - [Quick Reference](#-quick-reference)
27
+ - [Troubleshooting](#-troubleshooting)
28
+ - [Other Tools](#-other-tools)
29
+ - [Contributing](#-contributing)
30
+ - [License](#-license)
31
+
32
+ ---
33
+
34
+ ## ✨ What's New
35
+
36
+ ### v2.3.0-beta: Web Mode & Port Exposure
10
37
 
11
38
  - **Web Auto-Detection**: `opencode web` automatically exposes port 4096 and injects `--hostname 0.0.0.0`
12
39
  - **`--expose` Flag**: New way to expose ports (replaces deprecated `PORT` env var)
@@ -23,6 +50,8 @@ opencode web --port 8080
23
50
  opencode --expose 3000,5555 web
24
51
  ```
25
52
 
53
+ ---
54
+
26
55
  ## đŸ›Ąī¸ Why Use This?
27
56
 
28
57
  | Without Sandbox | With AI Sandbox |
@@ -32,6 +61,8 @@ opencode --expose 3000,5555 web
32
61
  | Host environment exposed | ✅ API keys passed explicitly |
33
62
  | Runs with your permissions | ✅ Non-root, CAP_DROP=ALL |
34
63
 
64
+ ---
65
+
35
66
  ## 🚀 Quick Start
36
67
 
37
68
  **Prerequisites:** Docker Desktop (macOS/Windows) or Docker Engine (Linux)
@@ -49,9 +80,12 @@ opencode
49
80
 
50
81
  During setup: select **opencode**, choose registry images (faster), whitelist your project directories.
51
82
 
83
+ ---
84
+
52
85
  ## âš™ī¸ Configuration
53
86
 
54
87
  ### API Keys
88
+
55
89
  ```bash
56
90
  nano ~/.ai-sandbox/env
57
91
  ```
@@ -61,6 +95,7 @@ OPENAI_API_KEY=sk-...
61
95
  ```
62
96
 
63
97
  ### Workspaces
98
+
64
99
  ```bash
65
100
  npx @kokorolx/ai-sandbox-wrapper workspace add ~/projects/my-app
66
101
  # Or: echo '/path/to/project' >> ~/.ai-sandbox/workspaces
@@ -94,6 +129,30 @@ Output:
94
129
  🌐 Web UI available at http://localhost:4096
95
130
  ```
96
131
 
132
+ ### Server Authentication
133
+
134
+ Control authentication for OpenCode web server:
135
+
136
+ ```bash
137
+ # Set password directly (visible in process list)
138
+ ai-run opencode web --password mysecret
139
+ ai-run opencode web -p mysecret
140
+
141
+ # Read password from environment variable (more secure)
142
+ MY_PASS=secret ai-run opencode web --password-env MY_PASS
143
+
144
+ # Explicitly allow unsecured mode (suppresses warning)
145
+ ai-run opencode web --allow-unsecured
146
+ ```
147
+
148
+ **Login credentials:**
149
+ - Username: `opencode` (default, override with `OPENCODE_SERVER_USERNAME` env var)
150
+ - Password: your configured password
151
+
152
+ **Precedence:** `--password` > `--password-env` > `OPENCODE_SERVER_PASSWORD` env > interactive prompt
153
+
154
+ Without flags, interactive mode shows a menu; non-interactive mode shows a security warning.
155
+
97
156
  **Port Conflict Detection:**
98
157
  ```
99
158
  ❌ ERROR: Port 3000 is already in use by node (PID: 12345)
@@ -117,6 +176,65 @@ Git credentials are **not** shared by default. When you run a tool, you'll be pr
117
176
  3) No, keep Git disabled (secure default)
118
177
  ```
119
178
 
179
+ ### Clipboard
180
+
181
+ Clipboard access in containers requires a terminal that supports **OSC52** protocol.
182
+
183
+ **Supported terminals:** iTerm2, Warp, Kitty, Alacritty, WezTerm, Windows Terminal, Ghostty
184
+
185
+ **Not supported:** GNOME Terminal, VS Code Terminal, Tilix, Terminator
186
+
187
+ Test if your terminal supports clipboard:
188
+ ```bash
189
+ printf "\033]52;c;$(printf "test" | base64)\a"
190
+ # Press Cmd+V / Ctrl+V - if you see "test", it works
191
+ ```
192
+
193
+ 📖 **Full details:** [CLIPBOARD_SUPPORT.md](CLIPBOARD_SUPPORT.md)
194
+
195
+ ### MCP Tools (Browser Automation)
196
+
197
+ During setup, you can optionally install MCP servers for AI agent browser automation:
198
+
199
+ | Tool | Maintainer | Features | Size |
200
+ |------|------------|----------|------|
201
+ | **Chrome DevTools MCP** | Google | Performance profiling, Core Web Vitals, detailed console/network inspection | ~400MB |
202
+ | **Playwright MCP** | Microsoft | Multi-browser (Chromium), TypeScript code generation, vision mode | ~300MB |
203
+
204
+ After installation, configure your MCP client (e.g., OpenCode) to use them:
205
+
206
+ **`~/.config/opencode/opencode.json`:**
207
+ ```json
208
+ {
209
+ "mcp": {
210
+ "chrome-devtools": {
211
+ "type": "local",
212
+ "command": [
213
+ "chrome-devtools-mcp",
214
+ "--headless",
215
+ "--isolated",
216
+ "--executablePath", "/opt/chromium",
217
+ "--chromeArg=--no-sandbox",
218
+ "--chromeArg=--disable-setuid-sandbox"
219
+ ]
220
+ },
221
+ "playwright": {
222
+ "type": "local",
223
+ "command": [
224
+ "npx", "@playwright/mcp@latest",
225
+ "--headless",
226
+ "--browser", "chromium",
227
+ "--no-sandbox"
228
+ ]
229
+ }
230
+ }
231
+ }
232
+ ```
233
+
234
+ > **Note:** The `--no-sandbox` flags are required when running in Docker containers. This is safe because the container itself provides isolation.
235
+
236
+ ---
237
+
120
238
  ## 📁 Directory Structure
121
239
 
122
240
  ```
@@ -132,6 +250,8 @@ Native configs are bind-mounted:
132
250
  - `~/.config/opencode` ↔ `/home/agent/.config/opencode`
133
251
  - `~/.local/share/opencode` ↔ `/home/agent/.local/share/opencode`
134
252
 
253
+ ---
254
+
135
255
  ## 🔐 Security Model
136
256
 
137
257
  ```
@@ -153,6 +273,8 @@ Native configs are bind-mounted:
153
273
  └─────────────────────────────────────────────────┘
154
274
  ```
155
275
 
276
+ ---
277
+
156
278
  ## 📚 Quick Reference
157
279
 
158
280
  ```bash
@@ -173,6 +295,8 @@ npx @kokorolx/ai-sandbox-wrapper workspace list
173
295
  npx @kokorolx/ai-sandbox-wrapper clean
174
296
  ```
175
297
 
298
+ ---
299
+
176
300
  ## ❓ Troubleshooting
177
301
 
178
302
  | Issue | Solution |
@@ -181,6 +305,9 @@ npx @kokorolx/ai-sandbox-wrapper clean
181
305
  | `Outside whitelisted workspace` | `echo "$(pwd)" >> ~/.ai-sandbox/workspaces` |
182
306
  | Port already in use | Stop the process or use different port |
183
307
  | Docker not found | Install and start Docker Desktop |
308
+ | Clipboard not working | Use OSC52-compatible terminal. See [CLIPBOARD_SUPPORT.md](CLIPBOARD_SUPPORT.md) |
309
+
310
+ ---
184
311
 
185
312
  ## đŸ“Ļ Other Tools
186
313
 
@@ -188,10 +315,14 @@ This sandbox also supports **Claude, Gemini, Aider, Kilo, Codex, Amp, Qwen**, an
188
315
 
189
316
  See [TOOLS.md](TOOLS.md) for the full list and tool-specific configuration.
190
317
 
318
+ ---
319
+
191
320
  ## 🤝 Contributing
192
321
 
193
322
  See [CONTRIBUTING.md](CONTRIBUTING.md).
194
323
 
324
+ ---
325
+
195
326
  ## 📝 License
196
327
 
197
328
  MIT
package/bin/ai-run CHANGED
@@ -1,14 +1,140 @@
1
1
  #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
+ # Show help if requested
5
+ show_help() {
6
+ cat << 'EOF'
7
+ Usage: ai-run <tool> [options] [-- tool-args...]
8
+
9
+ Run AI tools in a secure Docker sandbox.
10
+
11
+ Options:
12
+ -s, --shell Start interactive shell instead of running tool directly
13
+ -n, --network [name] Connect to Docker network(s) (comma-separated or interactive)
14
+ -e, --expose <ports> Expose container ports (comma-separated, e.g., 3000,5555)
15
+ -p, --password <value> Set OpenCode server password (for web/serve mode)
16
+ --password-env <VAR> Read OpenCode server password from environment variable
17
+ --allow-unsecured Allow OpenCode server to run without password (suppresses warning)
18
+ -h, --help Show this help message
19
+ --help-env Show environment variables reference
20
+
21
+ OpenCode Server Authentication (web/serve mode only):
22
+ When running 'opencode web' or 'opencode serve', you can control authentication:
23
+
24
+ # Set password directly (visible in process list)
25
+ ai-run opencode web --password mysecret
26
+
27
+ # Read password from environment variable (more secure)
28
+ MY_PASS=secret ai-run opencode web --password-env MY_PASS
29
+
30
+ # Explicitly allow unsecured mode (suppresses warning)
31
+ ai-run opencode web --allow-unsecured
32
+
33
+ Default username: opencode (override with OPENCODE_SERVER_USERNAME env var)
34
+ Precedence: --password > --password-env > OPENCODE_SERVER_PASSWORD env > interactive prompt
35
+
36
+ Examples:
37
+ ai-run claude # Run Claude in sandbox
38
+ ai-run opencode web -e 4096 # Run OpenCode web with port exposed
39
+ ai-run opencode web -p secret # Run with password
40
+ ai-run opencode --shell # Start shell, run tool manually
41
+ ai-run aider -n mynetwork # Connect to Docker network
42
+
43
+ Documentation: https://github.com/kokorolx/ai-sandbox-wrapper
44
+ EOF
45
+ exit 0
46
+ }
47
+
48
+ show_help_env() {
49
+ cat << 'EOF'
50
+ Environment Variables Reference
51
+ ===============================
52
+
53
+ Runtime Environment Variables (inline with ai-run):
54
+ ---------------------------------------------------
55
+ AI_IMAGE_SOURCE=registry Use pre-built images from GitLab registry instead of local
56
+ Example: AI_IMAGE_SOURCE=registry ai-run opencode
57
+
58
+ AI_RUN_DEBUG=1 Enable debug output (shows Docker command details)
59
+ Example: AI_RUN_DEBUG=1 ai-run opencode
60
+
61
+ AI_RUN_PLATFORM=linux/amd64 Force specific platform (useful for cross-arch)
62
+ Example: AI_RUN_PLATFORM=linux/amd64 ai-run opencode
63
+
64
+ PORT_BIND=all Bind exposed ports to all interfaces (0.0.0.0) instead of localhost
65
+ âš ī¸ Security risk - use only when needed
66
+ Example: PORT_BIND=all ai-run opencode web -e 4096
67
+
68
+ PORT=3000,4000 (Deprecated) Expose ports - use --expose flag instead
69
+ Example: PORT=3000 ai-run opencode
70
+
71
+ Build Environment Variables (inline with setup.sh or install scripts):
72
+ ----------------------------------------------------------------------
73
+ DOCKER_NO_CACHE=1 Force rebuild without Docker cache
74
+ Example: DOCKER_NO_CACHE=1 bash setup.sh
75
+
76
+ OPENCODE_VERSION=1.1.52 Install specific OpenCode version instead of latest
77
+ Example: OPENCODE_VERSION=1.1.52 bash lib/install-opencode.sh
78
+
79
+ INSTALL_CHROME_DEVTOOLS_MCP=1 Install Chrome DevTools MCP in base image
80
+ INSTALL_PLAYWRIGHT_MCP=1 Install Playwright MCP in base image
81
+ INSTALL_PLAYWRIGHT=1 Install Playwright browser automation
82
+ INSTALL_RUBY=1 Install Ruby 3.3.0 + Rails 8.0.2
83
+ INSTALL_SPEC_KIT=1 Install spec-kit (specify CLI)
84
+ INSTALL_UX_UI_PROMAX=1 Install uipro CLI
85
+ INSTALL_OPENSPEC=1 Install OpenSpec CLI
86
+
87
+ Container Environment Variables (passed to container via ~/.ai-sandbox/env):
88
+ -----------------------------------------------------------------------------
89
+ ANTHROPIC_API_KEY Anthropic API key for Claude
90
+ OPENAI_API_KEY OpenAI API key
91
+ OPENCODE_SERVER_PASSWORD Password for OpenCode web server
92
+ OPENCODE_SERVER_USERNAME Username for OpenCode web server (default: opencode)
93
+ GITHUB_TOKEN GitHub token for gh CLI authentication
94
+ GH_TOKEN Alternative GitHub token variable
95
+
96
+ Configuration Files:
97
+ --------------------
98
+ ~/.ai-sandbox/config.json Main configuration (workspaces, git, networks, MCP)
99
+ ~/.ai-sandbox/env API keys and secrets (KEY=value format)
100
+ ~/.ai-sandbox/workspaces Legacy workspace list (one path per line)
101
+
102
+ Examples:
103
+ # Debug mode with registry images
104
+ AI_RUN_DEBUG=1 AI_IMAGE_SOURCE=registry ai-run opencode
105
+
106
+ # Rebuild everything from scratch
107
+ DOCKER_NO_CACHE=1 bash setup.sh --no-cache
108
+
109
+ # Install specific OpenCode version
110
+ OPENCODE_VERSION=1.1.52 bash lib/install-opencode.sh
111
+
112
+ # Expose port to network (not just localhost)
113
+ PORT_BIND=all ai-run opencode web -e 4096
114
+ EOF
115
+ exit 0
116
+ }
117
+
118
+ # Check for help flag before anything else
119
+ if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
120
+ show_help
121
+ fi
122
+
123
+ if [[ "${1:-}" == "--help-env" ]]; then
124
+ show_help_env
125
+ fi
126
+
4
127
  TOOL="$1"
5
- shift
128
+ shift 2>/dev/null || { echo "❌ ERROR: No tool specified. Use 'ai-run --help' for usage."; exit 1; }
6
129
 
7
130
  # Parse flags
8
131
  SHELL_MODE=false
9
132
  NETWORK_FLAG=false
10
133
  NETWORK_ARG=""
11
134
  EXPOSE_ARG=""
135
+ SERVER_PASSWORD=""
136
+ PASSWORD_ENV_VAR=""
137
+ ALLOW_UNSECURED=false
12
138
  TOOL_ARGS=()
13
139
 
14
140
  while [[ $# -gt 0 ]]; do
@@ -33,6 +159,24 @@ while [[ $# -gt 0 ]]; do
33
159
  shift
34
160
  fi
35
161
  ;;
162
+ --password|-p)
163
+ shift
164
+ if [[ $# -gt 0 && ! "$1" =~ ^- ]]; then
165
+ SERVER_PASSWORD="$1"
166
+ shift
167
+ fi
168
+ ;;
169
+ --password-env)
170
+ shift
171
+ if [[ $# -gt 0 && ! "$1" =~ ^- ]]; then
172
+ PASSWORD_ENV_VAR="$1"
173
+ shift
174
+ fi
175
+ ;;
176
+ --allow-unsecured)
177
+ ALLOW_UNSECURED=true
178
+ shift
179
+ ;;
36
180
  *)
37
181
  TOOL_ARGS+=("$1")
38
182
  shift
@@ -1221,6 +1365,381 @@ if [[ -n "$TTY_FLAGS" ]]; then
1221
1365
  CONTAINER_NAME="--name $(generate_container_name)"
1222
1366
  fi
1223
1367
 
1368
+ # ============================================================================
1369
+ # OPENCODE SERVER PASSWORD HANDLING
1370
+ # ============================================================================
1371
+ OPENCODE_PASSWORD_ENV=""
1372
+
1373
+ # Detect if running opencode web or serve command
1374
+ is_opencode_web_mode() {
1375
+ [[ "$TOOL" == "opencode" ]] || return 1
1376
+ for arg in "${TOOL_ARGS[@]}"; do
1377
+ [[ "$arg" == "web" || "$arg" == "serve" ]] && return 0
1378
+ done
1379
+ return 1
1380
+ }
1381
+
1382
+ # Resolve password from CLI flags, env vars, or interactive prompt
1383
+ # Precedence: --password > --password-env > OPENCODE_SERVER_PASSWORD env > interactive/warning
1384
+ resolve_opencode_password() {
1385
+ # 1. CLI --password flag (highest priority)
1386
+ if [[ -n "$SERVER_PASSWORD" ]]; then
1387
+ OPENCODE_PASSWORD_ENV="-e OPENCODE_SERVER_PASSWORD=$SERVER_PASSWORD"
1388
+ return 0
1389
+ fi
1390
+
1391
+ # 2. CLI --password-env flag
1392
+ if [[ -n "$PASSWORD_ENV_VAR" ]]; then
1393
+ local env_value="${!PASSWORD_ENV_VAR:-}"
1394
+ if [[ -z "$env_value" ]]; then
1395
+ echo "❌ ERROR: Environment variable '$PASSWORD_ENV_VAR' is not set"
1396
+ exit 1
1397
+ fi
1398
+ OPENCODE_PASSWORD_ENV="-e OPENCODE_SERVER_PASSWORD=$env_value"
1399
+ return 0
1400
+ fi
1401
+
1402
+ # 3. Existing OPENCODE_SERVER_PASSWORD environment variable
1403
+ if [[ -n "${OPENCODE_SERVER_PASSWORD:-}" ]]; then
1404
+ OPENCODE_PASSWORD_ENV="-e OPENCODE_SERVER_PASSWORD=$OPENCODE_SERVER_PASSWORD"
1405
+ return 0
1406
+ fi
1407
+
1408
+ # 4. --allow-unsecured flag: skip prompt/warning, no password
1409
+ if [[ "$ALLOW_UNSECURED" == "true" ]]; then
1410
+ return 0
1411
+ fi
1412
+
1413
+ # 5. Interactive mode: show menu
1414
+ if [[ -t 0 ]]; then
1415
+ echo ""
1416
+ echo "🔐 OpenCode Web Server Security"
1417
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1418
+ echo "The web server requires a password for security."
1419
+ echo ""
1420
+ echo " 1) Generate random password (recommended)"
1421
+ echo " 2) Enter custom password"
1422
+ echo " 3) No password (âš ī¸ unsecured - localhost only)"
1423
+ echo ""
1424
+ read -p "Choice [1-3]: " password_choice
1425
+
1426
+ case "$password_choice" in
1427
+ 1)
1428
+ local generated_password=$(openssl rand -base64 24 | tr -d '/+=' | head -c 24)
1429
+ OPENCODE_PASSWORD_ENV="-e OPENCODE_SERVER_PASSWORD=$generated_password"
1430
+ echo ""
1431
+ echo "🔑 Generated password: $generated_password"
1432
+ echo "👤 Username: opencode (default)"
1433
+ echo ""
1434
+ echo "To connect from another terminal:"
1435
+ echo " OPENCODE_SERVER_PASSWORD='$generated_password' opencode attach http://localhost:4096"
1436
+ ;;
1437
+ 2)
1438
+ read -sp "Enter password: " custom_password
1439
+ echo ""
1440
+ if [[ -n "$custom_password" ]]; then
1441
+ OPENCODE_PASSWORD_ENV="-e OPENCODE_SERVER_PASSWORD=$custom_password"
1442
+ echo "✅ Custom password set"
1443
+ echo "👤 Username: opencode (default)"
1444
+ echo ""
1445
+ echo "To connect from another terminal:"
1446
+ echo " OPENCODE_SERVER_PASSWORD='<your-password>' opencode attach http://localhost:4096"
1447
+ else
1448
+ echo "âš ī¸ Empty password - server will be unsecured"
1449
+ fi
1450
+ ;;
1451
+ *)
1452
+ echo "âš ī¸ No password set - server is unsecured"
1453
+ echo " Only use this for localhost access!"
1454
+ ;;
1455
+ esac
1456
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1457
+ echo ""
1458
+ else
1459
+ # 6. Non-interactive: show warning
1460
+ echo ""
1461
+ echo "🔐 OpenCode Web Server Security"
1462
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1463
+ echo "âš ī¸ OPENCODE_SERVER_PASSWORD not set - server is unsecured"
1464
+ echo " Set OPENCODE_SERVER_PASSWORD environment variable for security"
1465
+ echo " Or use --allow-unsecured to suppress this warning"
1466
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1467
+ echo ""
1468
+ fi
1469
+ }
1470
+
1471
+ # Run password resolution if in web/serve mode
1472
+ if is_opencode_web_mode; then
1473
+ resolve_opencode_password
1474
+ fi
1475
+
1476
+ # ============================================================================
1477
+ # MCP AUTO-CONFIGURATION FOR OPENCODE
1478
+ # ============================================================================
1479
+
1480
+ # Check if MCP tool is installed (from config.json, set during setup.sh)
1481
+ is_mcp_installed() {
1482
+ local mcp_tool="$1"
1483
+ if has_jq && [[ -f "$AI_SANDBOX_CONFIG" ]]; then
1484
+ jq -e --arg tool "$mcp_tool" '.mcp.installed | index($tool) != null' "$AI_SANDBOX_CONFIG" &>/dev/null
1485
+ else
1486
+ return 1
1487
+ fi
1488
+ }
1489
+
1490
+ # Check if MCP is already configured in OpenCode config
1491
+ is_mcp_configured() {
1492
+ local mcp_name="$1"
1493
+ local config_file="$HOME/.config/opencode/opencode.json"
1494
+
1495
+ if [[ ! -f "$config_file" ]]; then
1496
+ return 1
1497
+ fi
1498
+
1499
+ if has_jq; then
1500
+ jq -e --arg name "$mcp_name" '.mcp[$name] // empty' "$config_file" &>/dev/null
1501
+ else
1502
+ grep -q "\"$mcp_name\"" "$config_file" 2>/dev/null
1503
+ fi
1504
+ }
1505
+
1506
+ # Add MCP configuration to OpenCode config
1507
+ add_mcp_config() {
1508
+ local mcp_name="$1"
1509
+ local mcp_command="$2"
1510
+ local force="${3:-false}"
1511
+ local config_file="$HOME/.config/opencode/opencode.json"
1512
+
1513
+ # Check if already configured and warn user
1514
+ if [[ "$force" != "true" ]] && is_mcp_configured "$mcp_name"; then
1515
+ echo ""
1516
+ echo " âš ī¸ WARNING: '$mcp_name' is already configured!"
1517
+ echo " Reconfiguring will overwrite your existing settings."
1518
+ echo " Any custom credentials or options will be lost."
1519
+ read -p " Are you sure you want to overwrite? [y/N]: " overwrite_choice
1520
+ if [[ ! "$overwrite_choice" =~ ^[Yy]$ ]]; then
1521
+ echo " â­ī¸ Kept existing configuration"
1522
+ return 1
1523
+ fi
1524
+ fi
1525
+
1526
+ mkdir -p "$(dirname "$config_file")"
1527
+
1528
+ if [[ ! -f "$config_file" ]]; then
1529
+ # Create new config with MCP
1530
+ echo "{\"mcp\": {\"$mcp_name\": {\"type\": \"local\", \"command\": $mcp_command}}}" > "$config_file"
1531
+ elif has_jq; then
1532
+ # Add MCP to existing config
1533
+ jq --arg name "$mcp_name" --argjson cmd "$mcp_command" \
1534
+ '.mcp = (.mcp // {}) | .mcp[$name] = {"type": "local", "command": $cmd}' \
1535
+ "$config_file" > "$config_file.tmp" && mv "$config_file.tmp" "$config_file"
1536
+ else
1537
+ echo "âš ī¸ jq not found. Please manually add MCP configuration to $config_file"
1538
+ return 1
1539
+ fi
1540
+ chmod 600 "$config_file"
1541
+ }
1542
+
1543
+ # Mark MCP as skipped for this workspace (don't ask again)
1544
+ mark_mcp_skipped() {
1545
+ local skip_file="$SANDBOX_DIR/.mcp-skip-$WORKSPACE_MD5"
1546
+ date -Iseconds > "$skip_file" 2>/dev/null || date > "$skip_file"
1547
+ }
1548
+
1549
+ # Check if MCP prompt was skipped for this workspace
1550
+ is_mcp_skipped() {
1551
+ local skip_file="$SANDBOX_DIR/.mcp-skip-$WORKSPACE_MD5"
1552
+ [[ -f "$skip_file" ]]
1553
+ }
1554
+
1555
+ # Configure MCP tools for OpenCode
1556
+ configure_opencode_mcp() {
1557
+ # Only run for opencode tool in interactive mode
1558
+ [[ "$TOOL" != "opencode" ]] && return 0
1559
+ [[ ! -t 0 ]] && return 0
1560
+
1561
+ local config_file="$HOME/.config/opencode/opencode.json"
1562
+
1563
+ # Generate workspace hash for skip tracking
1564
+ WORKSPACE_MD5=$(echo "$CURRENT_DIR" | md5sum | cut -c1-8)
1565
+
1566
+ # Check if already skipped for this workspace
1567
+ if is_mcp_skipped; then
1568
+ return 0
1569
+ fi
1570
+
1571
+ # Detect installed MCP tools (from config.json, set during setup.sh)
1572
+ local chrome_installed=false
1573
+ local playwright_installed=false
1574
+ local chrome_configured=false
1575
+ local playwright_configured=false
1576
+
1577
+ if is_mcp_installed "chrome-devtools"; then
1578
+ chrome_installed=true
1579
+ is_mcp_configured "chrome-devtools" && chrome_configured=true
1580
+ fi
1581
+
1582
+ if is_mcp_installed "playwright"; then
1583
+ playwright_installed=true
1584
+ is_mcp_configured "playwright" && playwright_configured=true
1585
+ fi
1586
+
1587
+ # If no MCP tools installed in image, return
1588
+ if [[ "$chrome_installed" == "false" && "$playwright_installed" == "false" ]]; then
1589
+ return 0
1590
+ fi
1591
+
1592
+ # If all installed tools are already configured, return silently
1593
+ local all_configured=true
1594
+ [[ "$chrome_installed" == "true" && "$chrome_configured" == "false" ]] && all_configured=false
1595
+ [[ "$playwright_installed" == "true" && "$playwright_configured" == "false" ]] && all_configured=false
1596
+
1597
+ if [[ "$all_configured" == "true" ]]; then
1598
+ return 0
1599
+ fi
1600
+
1601
+ # Build lists of configured and unconfigured tools
1602
+ local unconfigured=()
1603
+ local configured=()
1604
+ [[ "$chrome_installed" == "true" && "$chrome_configured" == "false" ]] && unconfigured+=("chrome-devtools")
1605
+ [[ "$chrome_installed" == "true" && "$chrome_configured" == "true" ]] && configured+=("chrome-devtools")
1606
+ [[ "$playwright_installed" == "true" && "$playwright_configured" == "false" ]] && unconfigured+=("playwright")
1607
+ [[ "$playwright_installed" == "true" && "$playwright_configured" == "true" ]] && configured+=("playwright")
1608
+
1609
+ # Show prompt
1610
+ echo ""
1611
+ echo "🔌 MCP Tools Detected"
1612
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1613
+
1614
+ # Show already configured tools
1615
+ if [[ ${#configured[@]} -gt 0 ]]; then
1616
+ echo "Already configured:"
1617
+ for tool in "${configured[@]}"; do
1618
+ case "$tool" in
1619
+ chrome-devtools)
1620
+ echo " ✓ Chrome DevTools MCP"
1621
+ ;;
1622
+ playwright)
1623
+ echo " ✓ Playwright MCP"
1624
+ ;;
1625
+ esac
1626
+ done
1627
+ echo ""
1628
+ fi
1629
+
1630
+ # Show unconfigured tools
1631
+ if [[ ${#unconfigured[@]} -gt 0 ]]; then
1632
+ echo "Not yet configured:"
1633
+ for tool in "${unconfigured[@]}"; do
1634
+ case "$tool" in
1635
+ chrome-devtools)
1636
+ echo " â€ĸ Chrome DevTools MCP - browser automation + performance profiling"
1637
+ ;;
1638
+ playwright)
1639
+ echo " â€ĸ Playwright MCP - multi-browser automation"
1640
+ ;;
1641
+ esac
1642
+ done
1643
+ echo ""
1644
+ fi
1645
+
1646
+ echo "Configure MCP tools in OpenCode?"
1647
+ echo " 1) Yes, configure all detected tools"
1648
+ echo " 2) Yes, let me choose which ones"
1649
+ echo " 3) No, skip (I'll configure manually)"
1650
+ echo " 4) No, don't ask again for this workspace"
1651
+ echo ""
1652
+ read -p "Choice [1-4]: " mcp_choice
1653
+
1654
+ local configured_any=false
1655
+
1656
+ case "$mcp_choice" in
1657
+ 1)
1658
+ # Configure all (both unconfigured and offer to reconfigure configured ones)
1659
+ local all_tools=()
1660
+ [[ "$chrome_installed" == "true" ]] && all_tools+=("chrome-devtools")
1661
+ [[ "$playwright_installed" == "true" ]] && all_tools+=("playwright")
1662
+
1663
+ for tool in "${all_tools[@]}"; do
1664
+ case "$tool" in
1665
+ chrome-devtools)
1666
+ if add_mcp_config "chrome-devtools" '["chrome-devtools-mcp", "--headless", "--isolated", "--executablePath", "/opt/chromium"]'; then
1667
+ echo " ✓ Configured Chrome DevTools MCP"
1668
+ configured_any=true
1669
+ fi
1670
+ ;;
1671
+ playwright)
1672
+ if add_mcp_config "playwright" '["npx", "@playwright/mcp@latest", "--headless", "--browser", "chromium"]'; then
1673
+ echo " ✓ Configured Playwright MCP"
1674
+ configured_any=true
1675
+ fi
1676
+ ;;
1677
+ esac
1678
+ done
1679
+ ;;
1680
+ 2)
1681
+ # Let user choose from all installed tools
1682
+ local all_tools=()
1683
+ [[ "$chrome_installed" == "true" ]] && all_tools+=("chrome-devtools")
1684
+ [[ "$playwright_installed" == "true" ]] && all_tools+=("playwright")
1685
+
1686
+ for tool in "${all_tools[@]}"; do
1687
+ local tool_desc=""
1688
+ local status_hint=""
1689
+ case "$tool" in
1690
+ chrome-devtools)
1691
+ tool_desc="Chrome DevTools MCP (browser automation + DevTools)"
1692
+ is_mcp_configured "chrome-devtools" && status_hint=" [already configured]"
1693
+ ;;
1694
+ playwright)
1695
+ tool_desc="Playwright MCP (multi-browser automation)"
1696
+ is_mcp_configured "playwright" && status_hint=" [already configured]"
1697
+ ;;
1698
+ esac
1699
+ read -p " Configure $tool_desc$status_hint? [y/N]: " tool_choice
1700
+ if [[ "$tool_choice" =~ ^[Yy]$ ]]; then
1701
+ case "$tool" in
1702
+ chrome-devtools)
1703
+ if add_mcp_config "chrome-devtools" '["chrome-devtools-mcp", "--headless", "--isolated", "--executablePath", "/opt/chromium"]'; then
1704
+ echo " ✓ Configured"
1705
+ configured_any=true
1706
+ fi
1707
+ ;;
1708
+ playwright)
1709
+ if add_mcp_config "playwright" '["npx", "@playwright/mcp@latest", "--headless", "--browser", "chromium"]'; then
1710
+ echo " ✓ Configured"
1711
+ configured_any=true
1712
+ fi
1713
+ ;;
1714
+ esac
1715
+ else
1716
+ echo " â­ī¸ Skipped"
1717
+ fi
1718
+ done
1719
+ ;;
1720
+ 4)
1721
+ mark_mcp_skipped
1722
+ echo "â„šī¸ Won't ask again for this workspace"
1723
+ ;;
1724
+ *)
1725
+ echo "â„šī¸ Skipped."
1726
+ ;;
1727
+ esac
1728
+
1729
+ # Show config file path for easy editing
1730
+ echo ""
1731
+ if [[ "$configured_any" == "true" ]]; then
1732
+ echo "✅ MCP configuration saved!"
1733
+ fi
1734
+ echo "📄 Config file: $config_file"
1735
+ echo " Edit this file to customize MCP settings or add credentials."
1736
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1737
+ echo ""
1738
+ }
1739
+
1740
+ # Run MCP configuration check for opencode
1741
+ configure_opencode_mcp
1742
+
1224
1743
  # ============================================================================
1225
1744
  # WEB COMMAND DETECTION AND PORT EXPOSURE
1226
1745
  # ============================================================================
@@ -1473,6 +1992,7 @@ docker run $CONTAINER_NAME --rm $TTY_FLAGS \
1473
1992
  $DISPLAY_FLAGS \
1474
1993
  $HOST_ACCESS_ARGS \
1475
1994
  $PORT_MAPPINGS \
1995
+ $OPENCODE_PASSWORD_ENV \
1476
1996
  -v "$HOME_DIR":/home/agent \
1477
1997
  -w "$CURRENT_DIR" \
1478
1998
  --env-file "$ENV_FILE" \
@@ -2,9 +2,9 @@ FROM node:22-bookworm-slim
2
2
 
3
3
  ARG AGENT_UID=1001
4
4
 
5
- # Install common dependencies
6
- # Note: python3-venv is needed for many tools, unzip for some installers
7
- RUN apt-get update && apt-get install -y --no-install-recommends git curl ssh ca-certificates jq python3 python3-pip python3-venv python3-dev python3-setuptools build-essential libopenblas-dev pipx unzip xclip wl-clipboard && curl -LsSf https://astral.sh/uv/install.sh | UV_INSTALL_DIR=/usr/local/bin sh && rm -rf /var/lib/apt/lists/* && pipx ensurepath
5
+ RUN apt-get update && apt-get install -y --no-install-recommends git curl ssh ca-certificates jq python3 python3-pip python3-venv python3-dev python3-setuptools build-essential libopenblas-dev pipx unzip xclip wl-clipboard ripgrep && curl -LsSf https://astral.sh/uv/install.sh | UV_INSTALL_DIR=/usr/local/bin sh && rm -rf /var/lib/apt/lists/* && pipx ensurepath
6
+
7
+ RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null && apt-get update && apt-get install -y gh && rm -rf /var/lib/apt/lists/*
8
8
 
9
9
  # Install pnpm globally using npm (not bun, for stability)
10
10
  RUN npm install -g pnpm
@@ -34,14 +34,80 @@ RUN mkdir -p /usr/local/lib/openspec && \
34
34
  ln -sf /usr/local/lib/openspec/node_modules/.bin/openspec /usr/local/bin/openspec && \
35
35
  chmod -R 755 /usr/local/lib/openspec && \
36
36
  chmod +x /usr/local/bin/openspec
37
+ # Install Playwright system dependencies
38
+ RUN apt-get update && apt-get install -y --no-install-recommends \
39
+ libglib2.0-0 \
40
+ libnspr4 \
41
+ libnss3 \
42
+ libdbus-1-3 \
43
+ libatk1.0-0 \
44
+ libatk-bridge2.0-0 \
45
+ libcups2 \
46
+ libxcb1 \
47
+ libxkbcommon0 \
48
+ libatspi2.0-0 \
49
+ libx11-6 \
50
+ libxcomposite1 \
51
+ libxdamage1 \
52
+ libxext6 \
53
+ libxfixes3 \
54
+ libxrandr2 \
55
+ libgbm1 \
56
+ libcairo2 \
57
+ libpango-1.0-0 \
58
+ libasound2 \
59
+ && rm -rf /var/lib/apt/lists/*
60
+ # Install Playwright and browsers via npm
61
+ RUN npm install -g playwright && npx playwright install
62
+ RUN apt-get update && apt-get install -y --no-install-recommends \
63
+ libglib2.0-0 \
64
+ libnspr4 \
65
+ libnss3 \
66
+ libdbus-1-3 \
67
+ libatk1.0-0 \
68
+ libatk-bridge2.0-0 \
69
+ libcups2 \
70
+ libxcb1 \
71
+ libxkbcommon0 \
72
+ libatspi2.0-0 \
73
+ libx11-6 \
74
+ libxcomposite1 \
75
+ libxdamage1 \
76
+ libxext6 \
77
+ libxfixes3 \
78
+ libxrandr2 \
79
+ libgbm1 \
80
+ libdrm2 \
81
+ libcairo2 \
82
+ libpango-1.0-0 \
83
+ libasound2 \
84
+ fonts-liberation \
85
+ libappindicator3-1 \
86
+ libu2f-udev \
87
+ libvulkan1 \
88
+ libxshmfence1 \
89
+ xdg-utils \
90
+ wget \
91
+ && rm -rf /var/lib/apt/lists/*
92
+ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
93
+ RUN mkdir -p /opt/playwright-browsers && \
94
+ npm install -g @playwright/mcp@latest && \
95
+ npx playwright-core install --no-shell chromium && \
96
+ npx playwright-core install-deps chromium && \
97
+ chmod -R 777 /opt/playwright-browsers && \
98
+ ln -sf $(ls -d /opt/playwright-browsers/chromium-*/chrome-linux/chrome | head -1) /opt/chromium
99
+ ENV CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS=1
100
+ RUN npm install -g chrome-devtools-mcp@latest && \
101
+ touch /opt/.mcp-chrome-devtools-installed
102
+ RUN touch /opt/.mcp-playwright-installed
37
103
 
38
104
  # Create workspace
39
105
  WORKDIR /workspace
40
106
 
41
- # Non-root user for security
42
107
  # Non-root user for security (match host UID)
43
108
  RUN useradd -m -u ${AGENT_UID} -d /home/agent agent && \
44
109
  mkdir -p /home/agent/.cache /home/agent/.npm /home/agent/.opencode /home/agent/.config && \
45
- chown -R agent:agent /home/agent/.cache /home/agent/.npm /home/agent/.opencode /home/agent/.config /workspace
110
+ chown -R agent:agent /home/agent/.cache /home/agent/.npm /home/agent/.opencode /home/agent/.config /workspace && \
111
+ ([ -d /opt/playwright-browsers ] && chown -R agent:agent /opt/playwright-browsers || true)
46
112
  USER agent
47
113
  ENV HOME=/home/agent
@@ -1,7 +1,6 @@
1
1
  FROM ai-base:latest
2
2
 
3
3
  USER root
4
- # Install OpenCode using official native installer
5
4
  RUN curl -fsSL https://opencode.ai/install | bash && \
6
5
  mv /home/agent/.opencode/bin/opencode /usr/local/bin/opencode && \
7
6
  rm -rf /home/agent/.opencode
@@ -104,13 +104,72 @@ RUN gem install rails -v 8.0.2 && gem install bundler && rbenv rehash
104
104
  '
105
105
  fi
106
106
 
107
+ # MCP Tools for AI agent browser automation
108
+ # Both tools share Playwright's Chromium (native ARM64/x86_64, avoids Puppeteer arch issues)
109
+ MCP_BROWSER_INSTALLED=false
110
+
111
+ if [[ "${INSTALL_CHROME_DEVTOOLS_MCP:-0}" -eq 1 ]] || [[ "${INSTALL_PLAYWRIGHT_MCP:-0}" -eq 1 ]]; then
112
+ MCP_BROWSER_INSTALLED=true
113
+ echo "đŸ“Ļ Installing shared Chromium browser for MCP tools"
114
+ ADDITIONAL_TOOLS_INSTALL+='RUN apt-get update && apt-get install -y --no-install-recommends \
115
+ libglib2.0-0 \
116
+ libnspr4 \
117
+ libnss3 \
118
+ libdbus-1-3 \
119
+ libatk1.0-0 \
120
+ libatk-bridge2.0-0 \
121
+ libcups2 \
122
+ libxcb1 \
123
+ libxkbcommon0 \
124
+ libatspi2.0-0 \
125
+ libx11-6 \
126
+ libxcomposite1 \
127
+ libxdamage1 \
128
+ libxext6 \
129
+ libxfixes3 \
130
+ libxrandr2 \
131
+ libgbm1 \
132
+ libdrm2 \
133
+ libcairo2 \
134
+ libpango-1.0-0 \
135
+ libasound2 \
136
+ fonts-liberation \
137
+ libappindicator3-1 \
138
+ libu2f-udev \
139
+ libvulkan1 \
140
+ libxshmfence1 \
141
+ xdg-utils \
142
+ wget \
143
+ && rm -rf /var/lib/apt/lists/*
144
+ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
145
+ RUN mkdir -p /opt/playwright-browsers && \
146
+ npm install -g @playwright/mcp@latest && \
147
+ npx playwright-core install --no-shell chromium && \
148
+ npx playwright-core install-deps chromium && \
149
+ chmod -R 777 /opt/playwright-browsers && \
150
+ ln -sf $(ls -d /opt/playwright-browsers/chromium-*/chrome-linux/chrome | head -1) /opt/chromium
151
+ '
152
+ fi
153
+
154
+ if [[ "${INSTALL_CHROME_DEVTOOLS_MCP:-0}" -eq 1 ]]; then
155
+ echo "đŸ“Ļ Chrome DevTools MCP will be installed in base image"
156
+ ADDITIONAL_TOOLS_INSTALL+='ENV CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS=1
157
+ RUN npm install -g chrome-devtools-mcp@latest && \
158
+ touch /opt/.mcp-chrome-devtools-installed
159
+ '
160
+ fi
161
+
162
+ if [[ "${INSTALL_PLAYWRIGHT_MCP:-0}" -eq 1 ]]; then
163
+ echo "đŸ“Ļ Playwright MCP will be installed in base image"
164
+ ADDITIONAL_TOOLS_INSTALL+='RUN touch /opt/.mcp-playwright-installed
165
+ '
166
+ fi
167
+
107
168
  cat > "dockerfiles/base/Dockerfile" <<EOF
108
169
  FROM node:22-bookworm-slim
109
170
 
110
171
  ARG AGENT_UID=1001
111
172
 
112
- # Install common dependencies
113
- # Note: python3-venv is needed for many tools, unzip for some installers
114
173
  RUN apt-get update && apt-get install -y --no-install-recommends \
115
174
  git \
116
175
  curl \
@@ -128,10 +187,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
128
187
  unzip \
129
188
  xclip \
130
189
  wl-clipboard \
190
+ ripgrep \
131
191
  && curl -LsSf https://astral.sh/uv/install.sh | UV_INSTALL_DIR=/usr/local/bin sh \
132
192
  && rm -rf /var/lib/apt/lists/* \
133
193
  && pipx ensurepath
134
194
 
195
+ RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
196
+ && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
197
+ && echo "deb [arch=\$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
198
+ && apt-get update \
199
+ && apt-get install -y gh \
200
+ && rm -rf /var/lib/apt/lists/*
201
+
135
202
  # Install pnpm globally using npm (not bun, for stability)
136
203
  RUN npm install -g pnpm
137
204
 
@@ -146,11 +213,11 @@ ${ADDITIONAL_TOOLS_INSTALL}
146
213
  # Create workspace
147
214
  WORKDIR /workspace
148
215
 
149
- # Non-root user for security
150
216
  # Non-root user for security (match host UID)
151
217
  RUN useradd -m -u \${AGENT_UID} -d /home/agent agent && \\
152
218
  mkdir -p /home/agent/.cache /home/agent/.npm /home/agent/.opencode /home/agent/.config && \\
153
- chown -R agent:agent /home/agent/.cache /home/agent/.npm /home/agent/.opencode /home/agent/.config /workspace
219
+ chown -R agent:agent /home/agent/.cache /home/agent/.npm /home/agent/.opencode /home/agent/.config /workspace && \\
220
+ ([ -d /opt/playwright-browsers ] && chown -R agent:agent /opt/playwright-browsers || true)
154
221
  USER agent
155
222
  ENV HOME=/home/agent
156
223
  EOF
@@ -1,22 +1,36 @@
1
1
  #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
- # OpenCode installer: Open-source AI coding tool (Native Go Binary)
5
4
  TOOL="opencode"
5
+ OPENCODE_VERSION="${OPENCODE_VERSION:-}"
6
6
 
7
- echo "Installing $TOOL (OpenCode AI - Native Go Binary)..."
7
+ if [[ -n "$OPENCODE_VERSION" ]]; then
8
+ echo "Installing $TOOL v$OPENCODE_VERSION (OpenCode AI - Native Go Binary)..."
9
+ else
10
+ echo "Installing $TOOL (OpenCode AI - Native Go Binary, latest)..."
11
+ fi
8
12
 
9
- # Create directories
10
13
  mkdir -p "dockerfiles/$TOOL"
11
14
  mkdir -p "$HOME/.ai-sandbox/tools/$TOOL/home/.cache"
12
15
  mkdir -p "$HOME/.ai-sandbox/tools/$TOOL/home"
13
16
 
14
- # Create Dockerfile using official native installer (Go binary)
15
- cat <<'EOF' > "dockerfiles/$TOOL/Dockerfile"
17
+ if [[ -n "$OPENCODE_VERSION" ]]; then
18
+ cat > "dockerfiles/$TOOL/Dockerfile" <<EOF
19
+ FROM ai-base:latest
20
+
21
+ USER root
22
+ RUN curl -fsSL https://opencode.ai/install | bash -s -- --version $OPENCODE_VERSION && \\
23
+ mv /home/agent/.opencode/bin/opencode /usr/local/bin/opencode && \\
24
+ rm -rf /home/agent/.opencode
25
+
26
+ USER agent
27
+ ENTRYPOINT ["opencode"]
28
+ EOF
29
+ else
30
+ cat <<'EOF' > "dockerfiles/$TOOL/Dockerfile"
16
31
  FROM ai-base:latest
17
32
 
18
33
  USER root
19
- # Install OpenCode using official native installer
20
34
  RUN curl -fsSL https://opencode.ai/install | bash && \
21
35
  mv /home/agent/.opencode/bin/opencode /usr/local/bin/opencode && \
22
36
  rm -rf /home/agent/.opencode
@@ -24,6 +38,7 @@ RUN curl -fsSL https://opencode.ai/install | bash && \
24
38
  USER agent
25
39
  ENTRYPOINT ["opencode"]
26
40
  EOF
41
+ fi
27
42
 
28
43
  # Build image
29
44
  echo "Building Docker image for $TOOL (native binary)..."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kokorolx/ai-sandbox-wrapper",
3
- "version": "2.3.0-beta",
3
+ "version": "2.6.0",
4
4
  "description": "Docker-based security sandbox for AI coding agents. Isolate Claude, Gemini, Aider, and other AI tools from your host system.",
5
5
  "keywords": [
6
6
  "ai",
package/setup.sh CHANGED
@@ -321,8 +321,21 @@ if [[ ${#CONTAINERIZED_TOOLS[@]} -gt 0 ]]; then
321
321
  echo "Language runtimes selected: ${LANGUAGE_RUNTIMES[*]}"
322
322
  fi
323
323
 
324
- # Combine both categories for processing
325
- ADDITIONAL_TOOLS=("${AI_ENHANCEMENT_TOOLS[@]}" "${LANGUAGE_RUNTIMES[@]}")
324
+ echo ""
325
+
326
+ # Category 3: MCP Tools (Browser automation for AI agents)
327
+ MCP_OPTIONS="chrome-devtools-mcp,playwright-mcp"
328
+ MCP_DESCS="Google Chrome DevTools MCP - performance profiling + debugging (~400MB),Microsoft Playwright MCP - multi-browser automation (~300MB)"
329
+
330
+ multi_select "Select MCP Tools for AI Agent Browser Automation" "$MCP_OPTIONS" "$MCP_DESCS"
331
+ MCP_TOOLS=("${SELECTED_ITEMS[@]}")
332
+
333
+ if [[ ${#MCP_TOOLS[@]} -gt 0 ]]; then
334
+ echo "MCP tools selected: ${MCP_TOOLS[*]}"
335
+ fi
336
+
337
+ # Combine all categories for processing
338
+ ADDITIONAL_TOOLS=("${AI_ENHANCEMENT_TOOLS[@]}" "${LANGUAGE_RUNTIMES[@]}" "${MCP_TOOLS[@]}")
326
339
  else
327
340
  ADDITIONAL_TOOLS=()
328
341
  echo "â„šī¸ No containerized AI tools selected. Skipping additional tools."
@@ -361,6 +374,8 @@ if [[ $NEEDS_BASE_IMAGE -eq 1 ]]; then
361
374
  INSTALL_OPENSPEC=0
362
375
  INSTALL_PLAYWRIGHT=0
363
376
  INSTALL_RUBY=0
377
+ INSTALL_CHROME_DEVTOOLS_MCP=0
378
+ INSTALL_PLAYWRIGHT_MCP=0
364
379
 
365
380
  for addon in "${ADDITIONAL_TOOLS[@]}"; do
366
381
  case "$addon" in
@@ -379,11 +394,32 @@ if [[ $NEEDS_BASE_IMAGE -eq 1 ]]; then
379
394
  ruby)
380
395
  INSTALL_RUBY=1
381
396
  ;;
397
+ chrome-devtools-mcp)
398
+ INSTALL_CHROME_DEVTOOLS_MCP=1
399
+ ;;
400
+ playwright-mcp)
401
+ INSTALL_PLAYWRIGHT_MCP=1
402
+ ;;
382
403
  esac
383
404
  done
384
405
 
385
- export INSTALL_SPEC_KIT INSTALL_UX_UI_PROMAX INSTALL_OPENSPEC INSTALL_PLAYWRIGHT INSTALL_RUBY
406
+ export INSTALL_SPEC_KIT INSTALL_UX_UI_PROMAX INSTALL_OPENSPEC INSTALL_PLAYWRIGHT INSTALL_RUBY INSTALL_CHROME_DEVTOOLS_MCP INSTALL_PLAYWRIGHT_MCP
386
407
  bash "$SCRIPT_DIR/lib/install-base.sh"
408
+
409
+ # Save MCP selections to ~/.ai-sandbox/config.json for ai-run auto-configuration
410
+ SANDBOX_CONFIG="$HOME/.ai-sandbox/config.json"
411
+ mkdir -p "$HOME/.ai-sandbox"
412
+ if command -v jq &>/dev/null; then
413
+ if [[ ! -f "$SANDBOX_CONFIG" ]]; then
414
+ echo '{"version":2,"workspaces":[],"git":{"allowedWorkspaces":[],"keySelections":{}},"networks":{"global":[],"workspaces":{}},"mcp":{"installed":[]}}' > "$SANDBOX_CONFIG"
415
+ fi
416
+ MCP_INSTALLED='[]'
417
+ [[ "$INSTALL_CHROME_DEVTOOLS_MCP" -eq 1 ]] && MCP_INSTALLED=$(echo "$MCP_INSTALLED" | jq '. + ["chrome-devtools"]')
418
+ [[ "$INSTALL_PLAYWRIGHT_MCP" -eq 1 ]] && MCP_INSTALLED=$(echo "$MCP_INSTALLED" | jq '. + ["playwright"]')
419
+ jq --argjson mcp "$MCP_INSTALLED" '.mcp.installed = $mcp' "$SANDBOX_CONFIG" > "$SANDBOX_CONFIG.tmp" && mv "$SANDBOX_CONFIG.tmp" "$SANDBOX_CONFIG"
420
+ chmod 600 "$SANDBOX_CONFIG"
421
+ echo "✅ MCP tool selections saved to config"
422
+ fi
387
423
  fi
388
424
 
389
425
  # Install selected tools
@@ -483,6 +519,12 @@ if [[ ${#ADDITIONAL_TOOLS[@]} -gt 0 ]]; then
483
519
  openspec)
484
520
  echo " openspec - OpenSpec CLI for spec-driven development"
485
521
  ;;
522
+ chrome-devtools-mcp)
523
+ echo " chrome-devtools-mcp - Google Chrome DevTools MCP server"
524
+ ;;
525
+ playwright-mcp)
526
+ echo " @playwright/mcp - Microsoft Playwright MCP server"
527
+ ;;
486
528
  esac
487
529
  done
488
530
  fi