@kokorolx/ai-sandbox-wrapper 2.2.0 → 2.3.0-beta
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 +94 -679
- package/bin/ai-run +166 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,612 +1,136 @@
|
|
|
1
1
|
# 🔒 AI Sandbox Wrapper
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Run OpenCode and other AI coding agents in secure Docker containers.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Protect your SSH keys, API tokens, and system files while using AI tools that need filesystem access.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
*Last updated: February 6, 2026*
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## ✨ New in v2.3.0-beta: Web Mode & Port Exposure
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- **Web Auto-Detection**: `opencode web` automatically exposes port 4096 and injects `--hostname 0.0.0.0`
|
|
12
|
+
- **`--expose` Flag**: New way to expose ports (replaces deprecated `PORT` env var)
|
|
13
|
+
- **Port Conflict Detection**: Fails fast if port is already in use
|
|
12
14
|
|
|
13
|
-
## ✨ New in v2.2.0: Clipboard Fixes & Screenshot Detection
|
|
14
|
-
|
|
15
|
-
The **v2.2.0 release** solves the "air-gap" problem for text copying and streamlines screenshot workflows.
|
|
16
|
-
|
|
17
|
-
- ✅ **OSC 52 Clipboard Support**: Copy text from inside Linux containers directly to your macOS clipboard (works over SSH too!).
|
|
18
|
-
- ✅ **Auto-Detect Screenshot Folder**: Automatically finds where your Mac saves screenshots and offers to whitelist it. No more permission errors when dragging images into AI tools.
|
|
19
|
-
- ✅ **Seamless Drag & Drop**: Just drag screenshots into the terminal window.
|
|
20
|
-
|
|
21
|
-
## ✨ New in v2.1.0: Stability & Native Persistence
|
|
22
|
-
|
|
23
|
-
The **v2.1.0 release** focuses on architectural stability and a more intuitive persistence model.
|
|
24
|
-
|
|
25
|
-
- ✅ **Stable Node 22 LTS**: Switched to a robust Node 22 base image for maximum compatibility and performance.
|
|
26
|
-
- ✅ **Direct Mount Persistence**: Changes made *inside* the container (logins, settings, sessions) now save directly to your host's native folders.
|
|
27
|
-
- ✅ **Cache Isolation**: Heavy caches (`node_modules`, `.npm`, `.cache`) are isolated using anonymous volumes to prevent "cache poisoning" and runtime conflicts.
|
|
28
|
-
|
|
29
|
-
### Native Config Mapping
|
|
30
|
-
Your tool configurations are now directly linked from your Mac/PC:
|
|
31
|
-
- Host: `~/.config/opencode` ↔ Container: `/home/agent/.config/opencode`
|
|
32
|
-
- Host: `~/.local/share/opencode` ↔ Container: `/home/agent/.local/share/opencode`
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## ⚠️ Breaking Change: v2.0.0 - Config Directory Reorganization
|
|
37
|
-
|
|
38
|
-
**Version 2.0.0** reorganized the directory structure to a tool-centric layout and introduced a unified `config.json`.
|
|
39
|
-
|
|
40
|
-
## 🛡️ Why Use This?
|
|
41
|
-
|
|
42
|
-
Without sandbox:
|
|
43
|
-
- AI agents can read your SSH keys, API tokens, browser data
|
|
44
|
-
- Can execute arbitrary code with your user permissions
|
|
45
|
-
- Can access files outside your project
|
|
46
|
-
|
|
47
|
-
With AI Sandbox:
|
|
48
|
-
- ✅ AI only sees whitelisted workspace folders
|
|
49
|
-
- ✅ No access to host environment variables (API keys hidden)
|
|
50
|
-
- ✅ Read-only filesystem (except workspace)
|
|
51
|
-
- ✅ No network access to host services
|
|
52
|
-
- ✅ Runs as non-root user in container
|
|
53
|
-
- ✅ CAP_DROP=ALL (no elevated privileges)
|
|
54
|
-
|
|
55
|
-
## 🚀 Step-by-Step Installation
|
|
56
|
-
|
|
57
|
-
### Step 1: Prerequisites
|
|
58
|
-
Ensure you have Docker installed and running:
|
|
59
|
-
- **macOS:** [Install Docker Desktop](https://www.docker.com/products/docker-desktop) and start it
|
|
60
|
-
- **Linux:** Install Docker Engine with `curl -fsSL https://get.docker.com | sh`
|
|
61
|
-
- **Windows:** Use WSL2 with Docker Desktop
|
|
62
|
-
|
|
63
|
-
Verify Docker is working:
|
|
64
15
|
```bash
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Step 2: Run Setup
|
|
16
|
+
# Web mode - automatic port exposure
|
|
17
|
+
opencode web
|
|
70
18
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
npx @kokorolx/ai-sandbox-wrapper setup
|
|
74
|
-
```
|
|
19
|
+
# Custom port
|
|
20
|
+
opencode web --port 8080
|
|
75
21
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
git clone https://github.com/kokorolx/ai-sandbox-wrapper.git
|
|
79
|
-
cd ai-sandbox-wrapper
|
|
80
|
-
./setup.sh
|
|
22
|
+
# Expose additional ports
|
|
23
|
+
opencode --expose 3000,5555 web
|
|
81
24
|
```
|
|
82
25
|
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
npx @kokorolx/ai-sandbox-wrapper setup --no-cache
|
|
86
|
-
# or
|
|
87
|
-
./setup.sh --no-cache
|
|
88
|
-
```
|
|
26
|
+
## 🛡️ Why Use This?
|
|
89
27
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
28
|
+
| Without Sandbox | With AI Sandbox |
|
|
29
|
+
|-----------------|-----------------|
|
|
30
|
+
| AI reads SSH keys, API tokens | ✅ Only whitelisted folders visible |
|
|
31
|
+
| Full filesystem access | ✅ Read-only except workspace |
|
|
32
|
+
| Host environment exposed | ✅ API keys passed explicitly |
|
|
33
|
+
| Runs with your permissions | ✅ Non-root, CAP_DROP=ALL |
|
|
94
34
|
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
# Reload your shell to update PATH
|
|
98
|
-
source ~/.zshrc
|
|
35
|
+
## 🚀 Quick Start
|
|
99
36
|
|
|
100
|
-
|
|
101
|
-
nano ~/.ai-sandbox/env # Add ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.
|
|
102
|
-
```
|
|
37
|
+
**Prerequisites:** Docker Desktop (macOS/Windows) or Docker Engine (Linux)
|
|
103
38
|
|
|
104
|
-
### Step 5: Run Your First Tool
|
|
105
39
|
```bash
|
|
106
|
-
#
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
# Run a tool (the example below assumes you selected Claude during setup)
|
|
110
|
-
claude --version # or: ai-run claude --version
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## 📋 What You Need
|
|
114
|
-
|
|
115
|
-
**Required:**
|
|
116
|
-
- **Docker** - Docker Desktop (macOS/Windows) or Docker Engine (Linux)
|
|
117
|
-
- **Git** - For cloning the repository
|
|
118
|
-
- **Bash** - For running the setup script
|
|
119
|
-
|
|
120
|
-
**Optional (for specific tools):**
|
|
121
|
-
- **Python 3** - For tools like Aider
|
|
122
|
-
- **SSH keys** - For Git access in containers
|
|
123
|
-
|
|
124
|
-
## ✅ After Installation
|
|
40
|
+
# Install
|
|
41
|
+
npx @kokorolx/ai-sandbox-wrapper setup
|
|
125
42
|
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
# Reload your shell to get the new commands
|
|
43
|
+
# Reload shell
|
|
129
44
|
source ~/.zshrc
|
|
130
45
|
|
|
131
|
-
#
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# Test a tool you installed (replace 'claude' with your chosen tool)
|
|
135
|
-
claude --version
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Add More Projects Later (Optional)
|
|
139
|
-
If you want to give AI access to more project directories later:
|
|
140
|
-
```bash
|
|
141
|
-
# Add a new workspace
|
|
142
|
-
echo '/path/to/new/project' >> ~/.ai-sandbox/workspaces
|
|
143
|
-
|
|
144
|
-
# View current allowed directories
|
|
145
|
-
cat ~/.ai-sandbox/workspaces
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Configure API Keys (If Needed)
|
|
149
|
-
Some tools require API keys to work properly:
|
|
150
|
-
```bash
|
|
151
|
-
nano ~/.ai-sandbox/env
|
|
152
|
-
```
|
|
153
|
-
Then add your keys in the format: `KEY_NAME=your_actual_key_here`
|
|
154
|
-
Examples:
|
|
155
|
-
- `ANTHROPIC_API_KEY=your_key_here` (for Claude)
|
|
156
|
-
- `OPENAI_API_KEY=your_key_here` (for OpenAI tools)
|
|
157
|
-
|
|
158
|
-
## 🐳 Using Pre-Built Images
|
|
159
|
-
|
|
160
|
-
**Skip the build process!** Pull pre-built images directly from GitLab Container Registry:
|
|
161
|
-
|
|
162
|
-
```bash
|
|
163
|
-
# Pull a specific tool image
|
|
164
|
-
docker pull registry.gitlab.com/kokorolee/ai-sandbox-wrapper/ai-claude:latest
|
|
165
|
-
docker pull registry.gitlab.com/kokorolee/ai-sandbox-wrapper/ai-gemini:latest
|
|
166
|
-
docker pull registry.gitlab.com/kokorolee/ai-sandbox-wrapper/ai-aider:latest
|
|
167
|
-
|
|
168
|
-
# Or let setup.sh pull them automatically
|
|
169
|
-
./setup.sh # Select tools, images will be pulled if available
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
**Available pre-built images:**
|
|
173
|
-
- `ai-base:latest` - Base image with Node.js 22 LTS runtime
|
|
174
|
-
- `ai-amp:latest` - Sourcegraph Amp
|
|
175
|
-
- `ai-claude:latest` - Claude Code CLI
|
|
176
|
-
- `ai-droid:latest` - Factory CLI
|
|
177
|
-
- `ai-gemini:latest` - Google Gemini CLI
|
|
178
|
-
- `ai-kilo:latest` - Kilo Code (500+ models)
|
|
179
|
-
- `ai-codex:latest` - OpenAI Codex
|
|
180
|
-
- `ai-aider:latest` - AI pair programmer
|
|
181
|
-
- `ai-opencode:latest` - Open-source AI coding
|
|
182
|
-
- `ai-qwen:latest` - Alibaba Qwen (1M context)
|
|
183
|
-
- `ai-qoder:latest` - Qoder AI assistant
|
|
184
|
-
- `ai-auggie:latest` - Augment Auggie
|
|
185
|
-
- `ai-codebuddy:latest` - Tencent CodeBuddy
|
|
186
|
-
- `ai-jules:latest` - Google Jules
|
|
187
|
-
- `ai-shai:latest` - OVHcloud SHAI
|
|
188
|
-
|
|
189
|
-
**Benefits:**
|
|
190
|
-
- ⚡ **Faster setup** - No build time (seconds vs minutes)
|
|
191
|
-
- ✅ **CI-tested** - All images verified in GitLab CI
|
|
192
|
-
- 🔄 **Auto-updated** - Latest versions on every push to beta branch
|
|
193
|
-
|
|
194
|
-
## 📦 Supported Tools
|
|
195
|
-
|
|
196
|
-
### CLI Tools (Terminal-based)
|
|
197
|
-
|
|
198
|
-
| Tool | Status | Install Type | Description |
|
|
199
|
-
|------|--------|--------------|-------------|
|
|
200
|
-
| **claude** | ✅ | Native binary | Anthropic Claude Code |
|
|
201
|
-
| **opencode** | ✅ | Native Go | Open-source AI coding |
|
|
202
|
-
| **gemini** | ✅ | npm/Node | Google Gemini CLI (free tier) |
|
|
203
|
-
| **aider** | ✅ | Python | AI pair programmer (Git-native) |
|
|
204
|
-
| **kilo** | ✅ | npm/Node | Kilo Code (500+ models) |
|
|
205
|
-
| **codex** | ✅ | npm/Node | OpenAI Codex agent |
|
|
206
|
-
| **amp** | ✅ | npm/Node | Sourcegraph Amp |
|
|
207
|
-
| **qwen** | ✅ | npm/Node | Alibaba Qwen CLI (1M context) |
|
|
208
|
-
| **droid** | ✅ | Custom | Factory CLI |
|
|
209
|
-
|
|
210
|
-
> **Note:** GUI tools (VSCode, codeserver) have been removed in v2.0.1. Use your native IDE with AI tools running in the sandbox.
|
|
211
|
-
|
|
212
|
-
## ⚠️ Known Issues
|
|
213
|
-
|
|
214
|
-
### Native Tool Config Compatibility
|
|
215
|
-
|
|
216
|
-
In v2.1.0+, tool configurations are **directly bind-mounted** from your host. This ensures 100% compatibility with your native tool settings and authentications.
|
|
217
|
-
|
|
218
|
-
1. **Host Config**: `~/.config/<tool>/` or `~/.<tool>/`
|
|
219
|
-
2. **Container Mount**: `/home/agent/.config/<tool>` (Automatic)
|
|
220
|
-
|
|
221
|
-
**Currently Supported for Direct Mount:**
|
|
222
|
-
- ✅ `claude`
|
|
223
|
-
- ✅ `opencode`
|
|
224
|
-
- ✅ `amp`
|
|
225
|
-
- ✅ `gemini`
|
|
226
|
-
- ✅ `aider`
|
|
227
|
-
- ... and all other supported tools.
|
|
228
|
-
|
|
229
|
-
Please [open an issue](https://github.com/kokorolx/ai-sandbox-wrapper/issues) if you encounter problems with specific tools.
|
|
230
|
-
|
|
231
|
-
## 🖥️ Platform Support
|
|
232
|
-
|
|
233
|
-
| Platform | Status |
|
|
234
|
-
|----------|--------|
|
|
235
|
-
| macOS (Intel) | ✅ |
|
|
236
|
-
| macOS (Apple Silicon) | ✅ |
|
|
237
|
-
| Linux (x64) | ✅ |
|
|
238
|
-
| Linux (ARM64) | ✅ |
|
|
239
|
-
| Windows (Docker Desktop + WSL2) | ✅ |
|
|
240
|
-
|
|
241
|
-
## 📁 Directory Structure
|
|
242
|
-
|
|
243
|
-
AI Sandbox Wrapper creates and manages a single consolidated directory in your home folder:
|
|
244
|
-
|
|
245
|
-
| Directory | Purpose | Contents |
|
|
246
|
-
|-----------|---------|----------|
|
|
247
|
-
| `~/bin/` | Executables | `ai-run` wrapper and symlinks to tool scripts |
|
|
248
|
-
| `~/.ai-sandbox/` | All config | Consolidated configuration directory (see structure below) |
|
|
249
|
-
| `~/.ai-images/` | Local images | Locally built Docker images (if not using registry) |
|
|
250
|
-
|
|
251
|
-
### Sandbox Structure
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
~/.ai-sandbox/
|
|
255
|
-
├── config.json # Unified config (workspaces, git, networks)
|
|
256
|
-
├── tools/ # Isolated sandbox environments
|
|
257
|
-
│ └── <tool>/
|
|
258
|
-
│ └── home/ # Sandbox home directory (excludes native configs)
|
|
259
|
-
├── shared/ # Shared assets
|
|
260
|
-
│ └── git/ # Shared git config and keys
|
|
261
|
-
└── env # API keys (format: KEY=value)
|
|
46
|
+
# Run OpenCode
|
|
47
|
+
opencode
|
|
262
48
|
```
|
|
263
49
|
|
|
264
|
-
**
|
|
265
|
-
|
|
266
|
-
### Key Files
|
|
267
|
-
|
|
268
|
-
| File | Purpose |
|
|
269
|
-
|------|---------|
|
|
270
|
-
| `~/.ai-sandbox/config.json` | Unified config (workspaces, git access, networks) |
|
|
271
|
-
| `~/.ai-sandbox/env` | API keys (format: `KEY=value`, one per line) |
|
|
272
|
-
| `~/.ai-sandbox/workspaces` | Legacy workspace file (fallback) |
|
|
273
|
-
| `~/.ai-sandbox/git-allowed` | Legacy git-allowed file (fallback) |
|
|
50
|
+
During setup: select **opencode**, choose registry images (faster), whitelist your project directories.
|
|
274
51
|
|
|
275
52
|
## ⚙️ Configuration
|
|
276
53
|
|
|
277
|
-
### Tool-Specific Configuration
|
|
278
|
-
|
|
279
|
-
Each tool has its own persistent home directory inside `~/.ai-sandbox/tools/<tool>/home/`.
|
|
280
|
-
|
|
281
|
-
```bash
|
|
282
|
-
# View configuration paths for a specific tool (Recommended)
|
|
283
|
-
npx @kokorolx/ai-sandbox-wrapper config tool claude
|
|
284
|
-
|
|
285
|
-
# View configuration content
|
|
286
|
-
npx @kokorolx/ai-sandbox-wrapper config tool claude --show
|
|
287
|
-
```
|
|
288
|
-
|
|
289
54
|
### API Keys
|
|
290
55
|
```bash
|
|
291
|
-
# Edit environment file
|
|
292
56
|
nano ~/.ai-sandbox/env
|
|
293
57
|
```
|
|
294
|
-
|
|
295
|
-
### Workspace Management
|
|
296
|
-
```bash
|
|
297
|
-
# CLI commands (recommended)
|
|
298
|
-
npx @kokorolx/ai-sandbox-wrapper workspace list
|
|
299
|
-
npx @kokorolx/ai-sandbox-wrapper workspace add ~/projects/my-new-app
|
|
300
|
-
npx @kokorolx/ai-sandbox-wrapper workspace remove ~/old-project
|
|
301
|
-
|
|
302
|
-
# Interactive menu
|
|
303
|
-
npx @kokorolx/ai-sandbox-wrapper update
|
|
304
|
-
|
|
305
|
-
# Legacy (still works)
|
|
306
|
-
echo '/path/to/project' >> ~/.ai-sandbox/workspaces
|
|
307
|
-
cat ~/.ai-sandbox/workspaces
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
### Git Access Management
|
|
311
|
-
```bash
|
|
312
|
-
# CLI commands
|
|
313
|
-
npx @kokorolx/ai-sandbox-wrapper git status
|
|
314
|
-
npx @kokorolx/ai-sandbox-wrapper git enable ~/projects/myrepo
|
|
315
|
-
npx @kokorolx/ai-sandbox-wrapper git disable ~/projects/myrepo
|
|
316
|
-
|
|
317
|
-
# Interactive menu
|
|
318
|
-
npx @kokorolx/ai-sandbox-wrapper update
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### Network Configuration
|
|
322
|
-
|
|
323
|
-
AI containers can join Docker networks to communicate with other services (databases, APIs, MetaMCP).
|
|
324
|
-
|
|
325
|
-
#### Runtime Selection (Recommended)
|
|
326
|
-
|
|
327
|
-
```bash
|
|
328
|
-
# Interactive network selection
|
|
329
|
-
ai-run opencode -n
|
|
330
|
-
|
|
331
|
-
# Direct network specification
|
|
332
|
-
ai-run opencode -n metamcp_metamcp-network
|
|
333
|
-
ai-run opencode -n network1,network2,network3
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
#### Saved Configuration
|
|
337
|
-
|
|
338
|
-
Network selections are saved to `~/.ai-sandbox/config.json`:
|
|
339
|
-
- **Per-workspace**: Saved for specific project directories
|
|
340
|
-
- **Global**: Default for all workspaces
|
|
341
|
-
|
|
342
|
-
```bash
|
|
343
|
-
# CLI commands
|
|
344
|
-
npx @kokorolx/ai-sandbox-wrapper network list
|
|
345
|
-
npx @kokorolx/ai-sandbox-wrapper network add mynetwork --global
|
|
346
|
-
npx @kokorolx/ai-sandbox-wrapper network add dev-network --workspace ~/projects/myapp
|
|
347
|
-
npx @kokorolx/ai-sandbox-wrapper network remove mynetwork --global
|
|
348
|
-
|
|
349
|
-
# View current config
|
|
350
|
-
npx @kokorolx/ai-sandbox-wrapper config show
|
|
351
|
-
npx @kokorolx/ai-sandbox-wrapper config show --json
|
|
352
|
-
|
|
353
|
-
# Example config.json structure (v2)
|
|
354
|
-
{
|
|
355
|
-
"version": 2,
|
|
356
|
-
"workspaces": ["/Users/you/projects/my-app"],
|
|
357
|
-
"git": {
|
|
358
|
-
"allowedWorkspaces": ["/Users/you/projects/my-repo"],
|
|
359
|
-
"keySelections": {}
|
|
360
|
-
},
|
|
361
|
-
"networks": {
|
|
362
|
-
"global": ["shared-services"],
|
|
363
|
-
"workspaces": {
|
|
364
|
-
"/Users/you/projects/my-app": ["my-app_default", "redis_network"]
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
58
|
```
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
When running without the flag, saved networks are used silently:
|
|
373
|
-
- Workspace-specific config takes priority
|
|
374
|
-
- Falls back to global config
|
|
375
|
-
- Non-existent networks are skipped silently
|
|
376
|
-
|
|
377
|
-
### Environment Variables
|
|
378
|
-
|
|
379
|
-
All environment variables are configured in `~/.ai-sandbox/env` or passed at runtime:
|
|
380
|
-
|
|
381
|
-
#### Image Source
|
|
382
|
-
Choose between locally built images or pre-built GitLab registry images:
|
|
383
|
-
|
|
384
|
-
```bash
|
|
385
|
-
# Add to ~/.ai-sandbox/env
|
|
386
|
-
|
|
387
|
-
# Use locally built images (default)
|
|
388
|
-
AI_IMAGE_SOURCE=local
|
|
389
|
-
|
|
390
|
-
# Use pre-built images from GitLab registry
|
|
391
|
-
AI_IMAGE_SOURCE=registry
|
|
59
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
60
|
+
OPENAI_API_KEY=sk-...
|
|
392
61
|
```
|
|
393
62
|
|
|
394
|
-
|
|
63
|
+
### Workspaces
|
|
395
64
|
```bash
|
|
396
|
-
|
|
65
|
+
npx @kokorolx/ai-sandbox-wrapper workspace add ~/projects/my-app
|
|
66
|
+
# Or: echo '/path/to/project' >> ~/.ai-sandbox/workspaces
|
|
397
67
|
```
|
|
398
68
|
|
|
399
|
-
|
|
400
|
-
For ARM64 Macs or other platforms, specify the container platform:
|
|
69
|
+
### Port Exposure
|
|
401
70
|
|
|
402
71
|
```bash
|
|
403
|
-
#
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
#### Docker Connection
|
|
408
|
-
For remote Docker hosts or non-default configurations:
|
|
72
|
+
# New --expose flag (recommended)
|
|
73
|
+
opencode --expose 3000
|
|
74
|
+
opencode -e 3000,5555,5556
|
|
409
75
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
export DOCKER_HOST=unix:///var/run/docker.sock
|
|
76
|
+
# Expose to network
|
|
77
|
+
PORT_BIND=all opencode --expose 3000
|
|
413
78
|
|
|
414
|
-
#
|
|
415
|
-
|
|
79
|
+
# Legacy (deprecated)
|
|
80
|
+
PORT=3000 opencode
|
|
416
81
|
```
|
|
417
82
|
|
|
418
|
-
|
|
419
|
-
Expose container ports to the host for web development, APIs, and dev servers:
|
|
420
|
-
|
|
83
|
+
**Web Mode Auto-Detection:**
|
|
421
84
|
```bash
|
|
422
|
-
#
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
# Expose multiple ports
|
|
426
|
-
PORT=3000,5555,5556,5557 ai-run opencode
|
|
427
|
-
|
|
428
|
-
# Expose to network (use with caution)
|
|
429
|
-
PORT=3000 PORT_BIND=all ai-run opencode
|
|
85
|
+
opencode web # Auto-exposes 4096
|
|
86
|
+
opencode web --port 8080 # Auto-exposes 8080
|
|
87
|
+
opencode --expose 3000 web # Exposes both 3000 and 4096
|
|
430
88
|
```
|
|
431
89
|
|
|
432
|
-
|
|
433
|
-
|----------|--------|---------|-------------|
|
|
434
|
-
| `PORT` | Comma-separated ports | (none) | Ports to expose (e.g., `3000,5555`) |
|
|
435
|
-
| `PORT_BIND` | `localhost`, `all` | `localhost` | Bind to localhost only or all interfaces |
|
|
436
|
-
|
|
437
|
-
**Security Notes:**
|
|
438
|
-
- Default binding is `127.0.0.1` (localhost only) - only accessible from your machine
|
|
439
|
-
- Using `PORT_BIND=all` exposes ports to your network - a warning is displayed
|
|
440
|
-
- Invalid port numbers (outside 1-65535) are skipped with a warning
|
|
441
|
-
|
|
442
|
-
**Example: Rails Development**
|
|
443
|
-
```bash
|
|
444
|
-
# Start container with Rails default port exposed
|
|
445
|
-
PORT=3000 ai-run opencode --shell
|
|
446
|
-
|
|
447
|
-
# Inside container, start Rails server
|
|
448
|
-
rails server -b 0.0.0.0
|
|
449
|
-
|
|
450
|
-
# Access from host browser at http://localhost:3000
|
|
90
|
+
Output:
|
|
451
91
|
```
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
```bash
|
|
457
|
-
# Required for Claude tools
|
|
458
|
-
ANTHROPIC_API_KEY=sk-ant-api03-...
|
|
459
|
-
|
|
460
|
-
# Required for OpenAI-based tools
|
|
461
|
-
OPENAI_API_KEY=sk-...
|
|
462
|
-
|
|
463
|
-
# Optional for Gemini CLI
|
|
464
|
-
GOOGLE_API_KEY=AIza...
|
|
465
|
-
|
|
466
|
-
# Optional: disable specific keys
|
|
467
|
-
# ANTHROPIC_API_KEY=
|
|
468
|
-
# OPENAI_API_KEY=
|
|
469
|
-
|
|
470
|
-
### Per-Project Config
|
|
471
|
-
|
|
472
|
-
Each tool supports project-specific config files that override global settings. These files are located in your workspace and are accessible to the tool:
|
|
473
|
-
|
|
474
|
-
| Tool | Project Config | Native Global Config |
|
|
475
|
-
|------|----------------|----------------------|
|
|
476
|
-
| Claude | `.claude.json` | `~/.config/claude/` |
|
|
477
|
-
| Gemini | `.gemini.json` | `~/.config/gemini/` |
|
|
478
|
-
| Aider | `.aider.conf` | `~/.config/aider/` |
|
|
479
|
-
| Opencode | `.opencode.json` | `~/.config/opencode/` |
|
|
480
|
-
| Amp | `.amp.json` | `~/.config/amp/` |
|
|
481
|
-
|
|
482
|
-
**Persistence:** Since v2.1.0, changes to global settings *inside* the container are automatically saved back to your **Native Global Config** on the host.
|
|
483
|
-
|
|
484
|
-
**Priority:** Project config > Native Global config > Container defaults
|
|
485
|
-
|
|
486
|
-
```bash
|
|
487
|
-
# Example: Project-specific Claude config
|
|
488
|
-
cat > .claude.json << 'EOF'
|
|
489
|
-
{
|
|
490
|
-
"model": "sonnet-4-20250514",
|
|
491
|
-
"max_output_tokens": 8192,
|
|
492
|
-
"temperature": 0.7
|
|
493
|
-
}
|
|
494
|
-
EOF
|
|
92
|
+
🌐 Detected web command. Auto-exposing port 4096.
|
|
93
|
+
🔌 Port mappings: 4096
|
|
94
|
+
🌐 Web UI available at http://localhost:4096
|
|
495
95
|
```
|
|
496
96
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
All tool configs are consolidated under `~/.ai-sandbox/home/{tool}/`:
|
|
500
|
-
|
|
97
|
+
**Port Conflict Detection:**
|
|
501
98
|
```
|
|
502
|
-
|
|
503
|
-
├── .config/ # Tool configuration
|
|
504
|
-
│ └── {tool}/ # Per-tool config directory
|
|
505
|
-
├── .local/share/ # Tool data (cache, sessions)
|
|
506
|
-
├── .cache/ # Runtime cache
|
|
507
|
-
└── .claude/ # Claude-specific (for claude tool)
|
|
99
|
+
❌ ERROR: Port 3000 is already in use by node (PID: 12345)
|
|
508
100
|
```
|
|
509
101
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
### Additional Tools (Container-Only)
|
|
513
|
-
|
|
514
|
-
During setup, you can optionally install additional tools into the base Docker image. Tools are organized into two categories:
|
|
515
|
-
|
|
516
|
-
#### AI Enhancement Tools
|
|
517
|
-
|
|
518
|
-
| Tool | Description | Size Impact |
|
|
519
|
-
|------|-------------|-------------|
|
|
520
|
-
| spec-kit | Spec-driven development toolkit | ~50MB |
|
|
521
|
-
| ux-ui-promax | UI/UX design intelligence tool | ~30MB |
|
|
522
|
-
| openspec | OpenSpec - spec-driven development | ~20MB |
|
|
523
|
-
| playwright | Browser automation with Chromium/Firefox/WebKit | ~500MB |
|
|
524
|
-
|
|
525
|
-
**Playwright** is useful when AI tools need to:
|
|
526
|
-
- Run browser-based tests
|
|
527
|
-
- Scrape web content
|
|
528
|
-
- Verify UI changes
|
|
529
|
-
- Automate browser workflows
|
|
530
|
-
|
|
531
|
-
#### Language Runtimes
|
|
532
|
-
|
|
533
|
-
| Runtime | Description | Size Impact |
|
|
534
|
-
|---------|-------------|-------------|
|
|
535
|
-
| ruby | Ruby 3.3.0 + Rails 8.0.2 (via rbenv) | ~500MB |
|
|
536
|
-
|
|
537
|
-
**Ruby/Rails** is useful when:
|
|
538
|
-
- Developing Ruby on Rails applications
|
|
539
|
-
- Running Rails generators and migrations
|
|
540
|
-
- Using Bundler for dependency management
|
|
541
|
-
- Building Ruby-based APIs and web apps
|
|
542
|
-
|
|
543
|
-
#### Always Installed
|
|
544
|
-
|
|
545
|
-
- `typescript` + `typescript-language-server` - Required for AI coding assistants with LSP integration
|
|
546
|
-
|
|
547
|
-
#### Manual Installation
|
|
102
|
+
### Network Access
|
|
548
103
|
|
|
549
104
|
```bash
|
|
550
|
-
#
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
# Manual build with Ruby/Rails (if not selected during setup)
|
|
554
|
-
INSTALL_RUBY=1 bash lib/install-base.sh
|
|
555
|
-
|
|
556
|
-
# Verify Playwright in container
|
|
557
|
-
docker run --rm ai-base:latest npx playwright --version
|
|
558
|
-
|
|
559
|
-
# Verify Ruby/Rails in container
|
|
560
|
-
docker run --rm ai-base:latest ruby --version
|
|
561
|
-
docker run --rm ai-base:latest rails --version
|
|
562
|
-
|
|
563
|
-
# Verify TypeScript LSP
|
|
564
|
-
docker run --rm ai-base:latest tsc --version
|
|
105
|
+
# Join Docker networks (for databases, APIs, MetaMCP)
|
|
106
|
+
opencode -n mynetwork
|
|
107
|
+
opencode -n network1,network2
|
|
565
108
|
```
|
|
566
109
|
|
|
567
|
-
### Git
|
|
568
|
-
AI tools work **inside** containers without Git credentials by default (secure).
|
|
569
|
-
|
|
570
|
-
**Option 1: Secure (Default) - Review & Commit from Host**
|
|
571
|
-
```bash
|
|
572
|
-
# 1. AI tool makes changes
|
|
573
|
-
ai-run claude # Edits files in your workspace
|
|
574
|
-
|
|
575
|
-
# 2. Review changes on host
|
|
576
|
-
git diff
|
|
577
|
-
|
|
578
|
-
# 3. Commit from host (you have full control)
|
|
579
|
-
git add .
|
|
580
|
-
git commit -m "feat: changes suggested by AI"
|
|
581
|
-
git push
|
|
582
|
-
```
|
|
110
|
+
### Git Access
|
|
583
111
|
|
|
584
|
-
**
|
|
585
|
-
When you run an AI tool, you'll be prompted:
|
|
112
|
+
Git credentials are **not** shared by default. When you run a tool, you'll be prompted:
|
|
586
113
|
```
|
|
587
114
|
🔐 Git Access Control
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
1) Yes, allow once (this session only)
|
|
591
|
-
2) Yes, always allow for this workspace
|
|
115
|
+
1) Yes, allow once
|
|
116
|
+
2) Yes, always allow for this workspace
|
|
592
117
|
3) No, keep Git disabled (secure default)
|
|
593
118
|
```
|
|
594
119
|
|
|
595
|
-
|
|
596
|
-
```bash
|
|
597
|
-
# View allowed workspaces
|
|
598
|
-
cat ~/.ai-sandbox/git-allowed
|
|
120
|
+
## 📁 Directory Structure
|
|
599
121
|
|
|
600
|
-
|
|
601
|
-
|
|
122
|
+
```
|
|
123
|
+
~/.ai-sandbox/
|
|
124
|
+
├── config.json # Workspaces, git, networks
|
|
125
|
+
├── env # API keys
|
|
126
|
+
├── tools/ # Per-tool sandbox homes
|
|
127
|
+
│ └── opencode/home/
|
|
128
|
+
└── shared/git/ # Shared git credentials
|
|
602
129
|
```
|
|
603
130
|
|
|
604
|
-
|
|
605
|
-
-
|
|
606
|
-
-
|
|
607
|
-
- ✅ SSH keys mounted read-only
|
|
608
|
-
- ✅ You control which projects get Git access
|
|
609
|
-
- ✅ Easy to revoke access anytime
|
|
131
|
+
Native configs are bind-mounted:
|
|
132
|
+
- `~/.config/opencode` ↔ `/home/agent/.config/opencode`
|
|
133
|
+
- `~/.local/share/opencode` ↔ `/home/agent/.local/share/opencode`
|
|
610
134
|
|
|
611
135
|
## 🔐 Security Model
|
|
612
136
|
|
|
@@ -623,159 +147,50 @@ nano ~/.ai-sandbox/git-allowed # Delete the line
|
|
|
623
147
|
┌─────────────────────────────────────────────────┐
|
|
624
148
|
│ AI SANDBOX CONTAINER │
|
|
625
149
|
│ ✅ /workspace (whitelisted folders only) │
|
|
626
|
-
│ ✅ Passed API keys (explicit
|
|
627
|
-
│ ✅ Git config (
|
|
150
|
+
│ ✅ Passed API keys (explicit) │
|
|
151
|
+
│ ✅ Git config (opt-in per workspace) │
|
|
628
152
|
│ ❌ Everything else │
|
|
629
153
|
└─────────────────────────────────────────────────┘
|
|
630
154
|
```
|
|
631
155
|
|
|
632
|
-
## ❓ Troubleshooting
|
|
633
|
-
|
|
634
|
-
### Common Issues
|
|
635
|
-
|
|
636
|
-
**Docker not found**
|
|
637
|
-
- Make sure Docker Desktop is installed and running
|
|
638
|
-
- Check with: `docker --version` and `docker ps`
|
|
639
|
-
|
|
640
|
-
**"command not found: ai-run"**
|
|
641
|
-
- Reload your shell: `source ~/.zshrc`
|
|
642
|
-
- Verify setup completed: check if `~/bin/ai-run` exists
|
|
643
|
-
|
|
644
|
-
**"Workspaces not configured"** (Legacy)
|
|
645
|
-
- Note: This error is resolved in v2.1.0+.
|
|
646
|
-
- Run setup again: `./setup.sh` or simply run an AI tool in your project folder to trigger interactive whitelisting.
|
|
647
|
-
|
|
648
|
-
**"BunInstallFailedError"** (Resolved in v2.1.0)
|
|
649
|
-
- This was caused by stale caches. We now use **Cache Isolation** via anonymous volumes. If you still see this, run `./setup.sh --no-cache` to force a clean build.
|
|
650
|
-
|
|
651
|
-
**Tool doesn't start**
|
|
652
|
-
- Check if you selected the tool during setup
|
|
653
|
-
- Look for the Docker image: `docker images | grep ai-`
|
|
654
|
-
|
|
655
|
-
**"Outside whitelisted workspace" error**
|
|
656
|
-
- Add your current directory: `echo "$(pwd)" >> ~/.ai-sandbox/workspaces`
|
|
657
|
-
- Or navigate to a directory you whitelisted during setup
|
|
658
|
-
|
|
659
|
-
**API key errors**
|
|
660
|
-
- Check your keys in: `cat ~/.ai-sandbox/env`
|
|
661
|
-
- Make sure keys are in format: `KEY_NAME=actual_key_value`
|
|
662
|
-
|
|
663
|
-
### Getting Help
|
|
664
|
-
|
|
665
|
-
If you're still having issues:
|
|
666
|
-
1. Check that Docker is running
|
|
667
|
-
2. Re-run `./setup.sh` to reinstall
|
|
668
|
-
3. Look at the configuration files in `~/.ai-sandbox/`:
|
|
669
|
-
- `~/.ai-sandbox/workspaces` - should contain your project directories
|
|
670
|
-
- `~/.ai-sandbox/env` - should contain your API keys (if needed)
|
|
671
|
-
4. View Docker images: `docker images` to see if tools built successfully
|
|
672
|
-
|
|
673
156
|
## 📚 Quick Reference
|
|
674
157
|
|
|
675
|
-
### Main Commands
|
|
676
|
-
- `ai-run <tool>` - Run any tool in sandbox (e.g., `ai-run claude`)
|
|
677
|
-
- `ai-run <tool> --shell` - Start interactive shell mode (see [Shell Mode Guide](SHELL-MODE-USAGE.md))
|
|
678
|
-
- `<tool>` - Shortcut for tools you installed (e.g., `claude`, `aider`)
|
|
679
|
-
|
|
680
|
-
### Execution Modes
|
|
681
|
-
|
|
682
|
-
**Direct Mode (Default):**
|
|
683
|
-
```bash
|
|
684
|
-
ai-run opencode
|
|
685
|
-
# Tool runs directly, exits on Ctrl+C
|
|
686
|
-
```
|
|
687
|
-
|
|
688
|
-
**Shell Mode (Interactive):**
|
|
689
|
-
```bash
|
|
690
|
-
ai-run opencode --shell # or -s
|
|
691
|
-
# Starts bash shell, run tool manually
|
|
692
|
-
# Ctrl+C stops tool only, not container
|
|
693
|
-
# Perfect for development and debugging
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
See [SHELL-MODE-USAGE.md](SHELL-MODE-USAGE.md) for detailed examples and use cases.
|
|
697
|
-
|
|
698
|
-
### Configuration Files
|
|
699
|
-
- `~/.ai-sandbox/env` - Store API keys here
|
|
700
|
-
- `~/.ai-sandbox/workspaces` - Whitelisted project directories
|
|
701
|
-
- `~/.ai-sandbox/cache/` - Tool cache (persistent)
|
|
702
|
-
- `~/.ai-sandbox/home/` - Tool configurations (persistent)
|
|
703
|
-
|
|
704
|
-
### Common Tasks
|
|
705
158
|
```bash
|
|
706
|
-
#
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
#
|
|
710
|
-
ls ~/bin/
|
|
711
|
-
|
|
712
|
-
# Reload shell after setup
|
|
713
|
-
source ~/.zshrc
|
|
714
|
-
|
|
715
|
-
# Update to latest version
|
|
716
|
-
npx @kokorolx/ai-sandbox-wrapper@latest setup
|
|
717
|
-
|
|
718
|
-
# Clean up caches and configs
|
|
719
|
-
npx @kokorolx/ai-sandbox-wrapper clean
|
|
720
|
-
```
|
|
159
|
+
# Run OpenCode
|
|
160
|
+
opencode # Direct mode
|
|
161
|
+
opencode --shell # Interactive shell
|
|
162
|
+
opencode web # Web UI mode
|
|
721
163
|
|
|
722
|
-
|
|
164
|
+
# Port exposure
|
|
165
|
+
opencode --expose 3000 # Expose port
|
|
166
|
+
opencode -e 3000,4000 # Multiple ports
|
|
723
167
|
|
|
724
|
-
|
|
168
|
+
# Network
|
|
169
|
+
opencode -n mynetwork # Join Docker network
|
|
725
170
|
|
|
726
|
-
|
|
171
|
+
# Management
|
|
172
|
+
npx @kokorolx/ai-sandbox-wrapper workspace list
|
|
727
173
|
npx @kokorolx/ai-sandbox-wrapper clean
|
|
728
174
|
```
|
|
729
175
|
|
|
730
|
-
|
|
731
|
-
- Two-level menu: First select category, then specific tools/items
|
|
732
|
-
- Shows directory sizes before deletion
|
|
733
|
-
- Groups items by risk level (🟢 Safe, 🟡 Medium, 🔴 Critical)
|
|
734
|
-
- Requires typing "yes" to confirm deletion
|
|
735
|
-
|
|
736
|
-
**Categories:**
|
|
737
|
-
| Category | Contents | Risk |
|
|
738
|
-
|----------|----------|------|
|
|
739
|
-
| Tool caches | `~/.ai-sandbox/cache/{tool}/` | 🟢 Safe to delete |
|
|
740
|
-
| Tool configs | `~/.ai-sandbox/home/{tool}/` | 🟡 Loses settings |
|
|
741
|
-
| Global config | `~/.ai-sandbox/workspaces`, `~/.ai-sandbox/env`, etc. | 🟡🔴 Mixed |
|
|
742
|
-
| Everything | `~/.ai-sandbox/` | 🔴 Full reset |
|
|
743
|
-
|
|
744
|
-
**Example:**
|
|
745
|
-
```
|
|
746
|
-
🧹 AI Sandbox Cleanup
|
|
747
|
-
|
|
748
|
-
What would you like to clean?
|
|
749
|
-
1. Tool caches (~/.ai-sandbox/cache/) - Safe to delete
|
|
750
|
-
2. Tool configs (~/.ai-sandbox/home/) - Loses settings
|
|
751
|
-
3. Global config files - Loses preferences
|
|
752
|
-
4. Everything (~/.ai-sandbox/) - Full reset
|
|
753
|
-
|
|
754
|
-
Enter selection (or 'q' to quit): 1
|
|
755
|
-
|
|
756
|
-
📁 Tool Caches (~/.ai-sandbox/cache/)
|
|
757
|
-
|
|
758
|
-
Select tools to clear:
|
|
759
|
-
1. claude/ (45.2 MB)
|
|
760
|
-
2. opencode/ (120.5 MB)
|
|
761
|
-
|
|
762
|
-
Enter selection (comma-separated, 'all', or 'b' to go back): 1
|
|
763
|
-
|
|
764
|
-
You are about to delete:
|
|
765
|
-
- ~/.ai-sandbox/cache/claude/ (45.2 MB)
|
|
176
|
+
## ❓ Troubleshooting
|
|
766
177
|
|
|
767
|
-
|
|
178
|
+
| Issue | Solution |
|
|
179
|
+
|-------|----------|
|
|
180
|
+
| `command not found: opencode` | Run `source ~/.zshrc` |
|
|
181
|
+
| `Outside whitelisted workspace` | `echo "$(pwd)" >> ~/.ai-sandbox/workspaces` |
|
|
182
|
+
| Port already in use | Stop the process or use different port |
|
|
183
|
+
| Docker not found | Install and start Docker Desktop |
|
|
768
184
|
|
|
769
|
-
|
|
185
|
+
## 📦 Other Tools
|
|
770
186
|
|
|
771
|
-
|
|
187
|
+
This sandbox also supports **Claude, Gemini, Aider, Kilo, Codex, Amp, Qwen**, and more.
|
|
772
188
|
|
|
773
|
-
|
|
774
|
-
```
|
|
189
|
+
See [TOOLS.md](TOOLS.md) for the full list and tool-specific configuration.
|
|
775
190
|
|
|
776
191
|
## 🤝 Contributing
|
|
777
192
|
|
|
778
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
193
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
779
194
|
|
|
780
195
|
## 📝 License
|
|
781
196
|
|
package/bin/ai-run
CHANGED
|
@@ -8,6 +8,7 @@ shift
|
|
|
8
8
|
SHELL_MODE=false
|
|
9
9
|
NETWORK_FLAG=false
|
|
10
10
|
NETWORK_ARG=""
|
|
11
|
+
EXPOSE_ARG=""
|
|
11
12
|
TOOL_ARGS=()
|
|
12
13
|
|
|
13
14
|
while [[ $# -gt 0 ]]; do
|
|
@@ -25,6 +26,13 @@ while [[ $# -gt 0 ]]; do
|
|
|
25
26
|
shift
|
|
26
27
|
fi
|
|
27
28
|
;;
|
|
29
|
+
--expose|-e)
|
|
30
|
+
shift
|
|
31
|
+
if [[ $# -gt 0 && ! "$1" =~ ^- ]]; then
|
|
32
|
+
EXPOSE_ARG="$1"
|
|
33
|
+
shift
|
|
34
|
+
fi
|
|
35
|
+
;;
|
|
28
36
|
*)
|
|
29
37
|
TOOL_ARGS+=("$1")
|
|
30
38
|
shift
|
|
@@ -118,7 +126,7 @@ migrate_to_v2() {
|
|
|
118
126
|
[[ -d "$SANDBOX_DIR/cache" ]] && needs_migration=true
|
|
119
127
|
[[ -d "$SANDBOX_DIR/git-keys" ]] && needs_migration=true
|
|
120
128
|
|
|
121
|
-
[[ "$needs_migration" == "false" ]] && { touch "$V2_MARKER"; return 0; }
|
|
129
|
+
[[ "$needs_migration" == "false" ]] && { mkdir -p "$SANDBOX_DIR" && touch "$V2_MARKER"; return 0; }
|
|
122
130
|
|
|
123
131
|
echo "🔄 Migrating to v2 folder structure..."
|
|
124
132
|
mkdir -p "$SANDBOX_DIR/tools" "$SANDBOX_DIR/shared/git"
|
|
@@ -1213,31 +1221,172 @@ if [[ -n "$TTY_FLAGS" ]]; then
|
|
|
1213
1221
|
CONTAINER_NAME="--name $(generate_container_name)"
|
|
1214
1222
|
fi
|
|
1215
1223
|
|
|
1216
|
-
#
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
PORT_BIND="${PORT_BIND:-localhost}"
|
|
1220
|
-
BIND_ADDR="127.0.0.1"
|
|
1224
|
+
# ============================================================================
|
|
1225
|
+
# WEB COMMAND DETECTION AND PORT EXPOSURE
|
|
1226
|
+
# ============================================================================
|
|
1221
1227
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1228
|
+
# Detect if running opencode web command
|
|
1229
|
+
detect_opencode_web() {
|
|
1230
|
+
[[ "$TOOL" == "opencode" ]] || return 1
|
|
1231
|
+
for arg in "${TOOL_ARGS[@]}"; do
|
|
1232
|
+
[[ "$arg" == "web" ]] && return 0
|
|
1233
|
+
done
|
|
1234
|
+
return 1
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
# Parse --port value from TOOL_ARGS (supports --port N and --port=N)
|
|
1238
|
+
parse_port_from_args() {
|
|
1239
|
+
local i=0
|
|
1240
|
+
while [[ $i -lt ${#TOOL_ARGS[@]} ]]; do
|
|
1241
|
+
local arg="${TOOL_ARGS[$i]}"
|
|
1242
|
+
if [[ "$arg" == "--port" ]]; then
|
|
1243
|
+
((i++))
|
|
1244
|
+
if [[ $i -lt ${#TOOL_ARGS[@]} ]]; then
|
|
1245
|
+
echo "${TOOL_ARGS[$i]}"
|
|
1246
|
+
return 0
|
|
1247
|
+
fi
|
|
1248
|
+
elif [[ "$arg" =~ ^--port=(.+)$ ]]; then
|
|
1249
|
+
echo "${BASH_REMATCH[1]}"
|
|
1250
|
+
return 0
|
|
1251
|
+
fi
|
|
1252
|
+
((i++))
|
|
1253
|
+
done
|
|
1254
|
+
return 1
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
# Check if --hostname is already in TOOL_ARGS
|
|
1258
|
+
has_hostname_arg() {
|
|
1259
|
+
for arg in "${TOOL_ARGS[@]}"; do
|
|
1260
|
+
[[ "$arg" == "--hostname" || "$arg" =~ ^--hostname= ]] && return 0
|
|
1261
|
+
done
|
|
1262
|
+
return 1
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
# Check if port is in use (lsof with netstat fallback)
|
|
1266
|
+
check_port_in_use() {
|
|
1267
|
+
local port="$1"
|
|
1268
|
+
if command -v lsof &>/dev/null; then
|
|
1269
|
+
lsof -i ":$port" &>/dev/null && return 0
|
|
1270
|
+
elif command -v netstat &>/dev/null; then
|
|
1271
|
+
netstat -tuln 2>/dev/null | grep -q ":$port " && return 0
|
|
1272
|
+
else
|
|
1273
|
+
return 2
|
|
1274
|
+
fi
|
|
1275
|
+
|
|
1276
|
+
docker ps --format "{{.Ports}}" 2>/dev/null | grep -q ":$port->" && return 0
|
|
1277
|
+
return 1
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
# Get process info for port
|
|
1281
|
+
get_port_process_info() {
|
|
1282
|
+
local port="$1"
|
|
1283
|
+
if command -v lsof &>/dev/null; then
|
|
1284
|
+
lsof -i ":$port" -sTCP:LISTEN 2>/dev/null | awk 'NR==2 {print $1 " (PID: " $2 ")"}'
|
|
1285
|
+
elif command -v netstat &>/dev/null; then
|
|
1286
|
+
echo "unknown process"
|
|
1287
|
+
else
|
|
1288
|
+
echo "unknown"
|
|
1225
1289
|
fi
|
|
1290
|
+
}
|
|
1226
1291
|
|
|
1292
|
+
# Initialize port list (bash 3.x compatible - no associative arrays)
|
|
1293
|
+
EXPOSE_PORTS_LIST=""
|
|
1294
|
+
WEB_PORT=""
|
|
1295
|
+
|
|
1296
|
+
# Helper: add port to list if not already present
|
|
1297
|
+
add_port_to_list() {
|
|
1298
|
+
local port="$1"
|
|
1299
|
+
local source="$2"
|
|
1300
|
+
if [[ ! " $EXPOSE_PORTS_LIST " =~ " $port " ]]; then
|
|
1301
|
+
EXPOSE_PORTS_LIST="$EXPOSE_PORTS_LIST $port"
|
|
1302
|
+
if [[ "${AI_RUN_DEBUG:-}" == "1" ]]; then
|
|
1303
|
+
echo "🔧 Debug: Added port $port from $source"
|
|
1304
|
+
fi
|
|
1305
|
+
fi
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
# Parse --expose flag
|
|
1309
|
+
if [[ -n "$EXPOSE_ARG" ]]; then
|
|
1310
|
+
IFS=',' read -ra EXPOSE_PORTS <<< "$EXPOSE_ARG"
|
|
1311
|
+
for port in "${EXPOSE_PORTS[@]}"; do
|
|
1312
|
+
port=$(echo "$port" | tr -d ' ')
|
|
1313
|
+
if [[ "$port" =~ ^[0-9]+$ ]] && [ "$port" -ge 1 ] && [ "$port" -le 65535 ]; then
|
|
1314
|
+
add_port_to_list "$port" "--expose"
|
|
1315
|
+
else
|
|
1316
|
+
echo "⚠️ WARNING: Invalid port number in --expose: $port (skipped)"
|
|
1317
|
+
fi
|
|
1318
|
+
done
|
|
1319
|
+
fi
|
|
1320
|
+
|
|
1321
|
+
# Web command detection and auto-exposure
|
|
1322
|
+
WEB_DETECTED=false
|
|
1323
|
+
if detect_opencode_web; then
|
|
1324
|
+
WEB_DETECTED=true
|
|
1325
|
+
WEB_PORT=$(parse_port_from_args)
|
|
1326
|
+
if [[ -z "$WEB_PORT" ]]; then
|
|
1327
|
+
WEB_PORT=4096
|
|
1328
|
+
fi
|
|
1329
|
+
|
|
1330
|
+
add_port_to_list "$WEB_PORT" "auto-detected"
|
|
1331
|
+
echo "🌐 Detected web command. Auto-exposing port $WEB_PORT."
|
|
1332
|
+
|
|
1333
|
+
if ! has_hostname_arg; then
|
|
1334
|
+
TOOL_ARGS+=("--hostname" "0.0.0.0")
|
|
1335
|
+
fi
|
|
1336
|
+
fi
|
|
1337
|
+
|
|
1338
|
+
# Handle legacy PORT environment variable
|
|
1339
|
+
if [[ -n "${PORT:-}" ]]; then
|
|
1340
|
+
echo "⚠️ WARNING: PORT environment variable is deprecated. Use --expose flag instead."
|
|
1227
1341
|
IFS=',' read -ra PORTS <<< "$PORT"
|
|
1228
1342
|
for port in "${PORTS[@]}"; do
|
|
1229
|
-
# Trim whitespace
|
|
1230
1343
|
port=$(echo "$port" | tr -d ' ')
|
|
1231
|
-
# Validate port number (1-65535)
|
|
1232
1344
|
if [[ "$port" =~ ^[0-9]+$ ]] && [ "$port" -ge 1 ] && [ "$port" -le 65535 ]; then
|
|
1233
|
-
|
|
1345
|
+
add_port_to_list "$port" "PORT env"
|
|
1234
1346
|
else
|
|
1235
|
-
echo "⚠️ WARNING: Invalid port number: $port (skipped)"
|
|
1347
|
+
echo "⚠️ WARNING: Invalid port number in PORT: $port (skipped)"
|
|
1348
|
+
fi
|
|
1349
|
+
done
|
|
1350
|
+
fi
|
|
1351
|
+
|
|
1352
|
+
# Trim leading space from port list
|
|
1353
|
+
EXPOSE_PORTS_LIST=$(echo "$EXPOSE_PORTS_LIST" | sed 's/^ //')
|
|
1354
|
+
|
|
1355
|
+
# Port conflict detection
|
|
1356
|
+
PORT_CHECK_AVAILABLE=true
|
|
1357
|
+
if ! command -v lsof &>/dev/null && ! command -v netstat &>/dev/null; then
|
|
1358
|
+
echo "⚠️ WARNING: Cannot check port availability (lsof/netstat not found)"
|
|
1359
|
+
PORT_CHECK_AVAILABLE=false
|
|
1360
|
+
fi
|
|
1361
|
+
|
|
1362
|
+
if [[ "$PORT_CHECK_AVAILABLE" == "true" && -n "$EXPOSE_PORTS_LIST" ]]; then
|
|
1363
|
+
for port in $EXPOSE_PORTS_LIST; do
|
|
1364
|
+
if check_port_in_use "$port"; then
|
|
1365
|
+
PROCESS_INFO=$(get_port_process_info "$port")
|
|
1366
|
+
echo "❌ ERROR: Port $port is already in use by $PROCESS_INFO"
|
|
1367
|
+
exit 1
|
|
1236
1368
|
fi
|
|
1237
1369
|
done
|
|
1370
|
+
fi
|
|
1371
|
+
|
|
1372
|
+
# Build PORT_MAPPINGS from EXPOSE_PORTS_LIST
|
|
1373
|
+
if [[ -n "$EXPOSE_PORTS_LIST" ]]; then
|
|
1374
|
+
PORT_BIND="${PORT_BIND:-localhost}"
|
|
1375
|
+
BIND_ADDR="127.0.0.1"
|
|
1376
|
+
|
|
1377
|
+
if [[ "$PORT_BIND" == "all" ]]; then
|
|
1378
|
+
BIND_ADDR="0.0.0.0"
|
|
1379
|
+
echo "⚠️ WARNING: Ports will be accessible from network (PORT_BIND=all)"
|
|
1380
|
+
fi
|
|
1381
|
+
|
|
1382
|
+
for port in $EXPOSE_PORTS_LIST; do
|
|
1383
|
+
PORT_MAPPINGS="$PORT_MAPPINGS -p $BIND_ADDR:$port:$port"
|
|
1384
|
+
done
|
|
1238
1385
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1386
|
+
echo "🔌 Port mappings: $EXPOSE_PORTS_LIST"
|
|
1387
|
+
|
|
1388
|
+
if [[ "$WEB_DETECTED" == "true" ]]; then
|
|
1389
|
+
echo "🌐 Web UI available at http://localhost:$WEB_PORT"
|
|
1241
1390
|
fi
|
|
1242
1391
|
fi
|
|
1243
1392
|
|
|
@@ -1251,6 +1400,8 @@ if [[ "${AI_RUN_DEBUG:-}" == "1" ]]; then
|
|
|
1251
1400
|
echo "🔧 Debug: PORT='${PORT:-}'"
|
|
1252
1401
|
echo "🔧 Debug: PORT_BIND='${PORT_BIND:-localhost}'"
|
|
1253
1402
|
echo "🔧 Debug: PORT_MAPPINGS='$PORT_MAPPINGS'"
|
|
1403
|
+
echo "🔧 Debug: WEB_DETECTED='$WEB_DETECTED'"
|
|
1404
|
+
echo "🔧 Debug: EXPOSE_PORTS_LIST='$EXPOSE_PORTS_LIST'"
|
|
1254
1405
|
fi
|
|
1255
1406
|
|
|
1256
1407
|
# Prepare command based on mode
|
package/package.json
CHANGED