@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 +133 -2
- package/bin/ai-run +521 -1
- package/dockerfiles/base/Dockerfile +71 -5
- package/dockerfiles/opencode/Dockerfile +0 -1
- package/lib/install-base.sh +71 -4
- package/lib/install-opencode.sh +21 -6
- package/package.json +1 -1
- package/setup.sh +45 -3
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
|
|
7
|
+
*Last updated: February 9, 2026*
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
RUN
|
|
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
|
package/lib/install-base.sh
CHANGED
|
@@ -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
|
package/lib/install-opencode.sh
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
15
|
-
cat
|
|
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
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
|
-
|
|
325
|
-
|
|
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
|