claude-code-templates 1.21.5 → 1.21.7
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/components/sandbox/README.md +169 -0
- package/components/sandbox/e2b/.env.example +10 -0
- package/components/sandbox/e2b/SANDBOX_DEBUGGING.md +203 -0
- package/components/sandbox/e2b/claude-code-sandbox.md +110 -0
- package/components/sandbox/e2b/e2b-launcher.py +426 -0
- package/components/sandbox/e2b/e2b-monitor.py +229 -0
- package/components/sandbox/e2b/requirements.txt +1 -0
- package/package.json +2 -1
- package/src/index.js +59 -24
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Claude Code Sandbox Components
|
|
2
|
+
|
|
3
|
+
Execute Claude Code in isolated cloud environments for secure code generation and development.
|
|
4
|
+
|
|
5
|
+
## Available Sandbox Providers
|
|
6
|
+
|
|
7
|
+
### E2B Sandbox (`e2b`)
|
|
8
|
+
Run Claude Code in E2B's secure cloud environment with pre-configured development tools.
|
|
9
|
+
|
|
10
|
+
**Component**: `e2b/claude-code-sandbox.md`
|
|
11
|
+
|
|
12
|
+
**Files Created**:
|
|
13
|
+
- `.claude/sandbox/e2b-launcher.py` - Python launcher script
|
|
14
|
+
- `.claude/sandbox/requirements.txt` - Python dependencies
|
|
15
|
+
- `.claude/sandbox/.env.example` - Environment variables template
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Simple execution with API keys as parameters (recommended)
|
|
21
|
+
npx claude-code-templates@latest --sandbox e2b \
|
|
22
|
+
--e2b-api-key your_e2b_key \
|
|
23
|
+
--anthropic-api-key your_anthropic_key \
|
|
24
|
+
--prompt "Create a React todo app"
|
|
25
|
+
|
|
26
|
+
# With components installation
|
|
27
|
+
npx claude-code-templates@latest --sandbox e2b \
|
|
28
|
+
--e2b-api-key your_e2b_key \
|
|
29
|
+
--anthropic-api-key your_anthropic_key \
|
|
30
|
+
--agent frontend-developer \
|
|
31
|
+
--command setup-react \
|
|
32
|
+
--prompt "Create a modern todo app with TypeScript"
|
|
33
|
+
|
|
34
|
+
# Or use environment variables (set E2B_API_KEY and ANTHROPIC_API_KEY)
|
|
35
|
+
npx claude-code-templates@latest --sandbox e2b --prompt "Create a React todo app"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Environment Setup
|
|
39
|
+
|
|
40
|
+
1. **Get API Keys**:
|
|
41
|
+
- E2B API Key: https://e2b.dev/dashboard
|
|
42
|
+
- Anthropic API Key: https://console.anthropic.com
|
|
43
|
+
|
|
44
|
+
2. **Create Environment File**:
|
|
45
|
+
```bash
|
|
46
|
+
# In your project/.claude/sandbox/.env
|
|
47
|
+
E2B_API_KEY=your_e2b_api_key_here
|
|
48
|
+
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
3. **Install Python Requirements** (handled automatically):
|
|
52
|
+
- Python 3.11+
|
|
53
|
+
- E2B Python SDK
|
|
54
|
+
- python-dotenv
|
|
55
|
+
|
|
56
|
+
## How It Works
|
|
57
|
+
|
|
58
|
+
1. **Component Download**: Downloads sandbox launcher and requirements
|
|
59
|
+
2. **Environment Check**: Validates Python 3.11+ installation
|
|
60
|
+
3. **Dependencies**: Installs E2B Python SDK automatically
|
|
61
|
+
4. **Sandbox Creation**: Creates E2B sandbox with Claude Code template
|
|
62
|
+
5. **Component Installation**: Installs any specified agents/commands/mcps/settings/hooks inside sandbox
|
|
63
|
+
6. **Prompt Execution**: Runs your prompt through Claude Code in the isolated environment
|
|
64
|
+
7. **Result Display**: Shows complete output and generated files
|
|
65
|
+
8. **Cleanup**: Automatically destroys sandbox after execution
|
|
66
|
+
|
|
67
|
+
## Usage Examples
|
|
68
|
+
|
|
69
|
+
### Basic Web Development
|
|
70
|
+
```bash
|
|
71
|
+
npx claude-code-templates@latest --sandbox e2b --prompt "Create an HTML page with modern CSS animations"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Full Stack with Components
|
|
75
|
+
```bash
|
|
76
|
+
npx claude-code-templates@latest --sandbox e2b \
|
|
77
|
+
--agent fullstack-developer \
|
|
78
|
+
--command setup-node \
|
|
79
|
+
--prompt "Create a Node.js API with JWT authentication"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Data Analysis
|
|
83
|
+
```bash
|
|
84
|
+
npx claude-code-templates@latest --sandbox e2b \
|
|
85
|
+
--agent data-scientist \
|
|
86
|
+
--prompt "Analyze this CSV data and create visualizations"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Security Audit
|
|
90
|
+
```bash
|
|
91
|
+
npx claude-code-templates@latest --sandbox e2b \
|
|
92
|
+
--agent security-auditor \
|
|
93
|
+
--command security-audit \
|
|
94
|
+
--prompt "Review this codebase for security vulnerabilities"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Security Benefits
|
|
98
|
+
|
|
99
|
+
- ✅ **Complete Isolation**: Code runs in separate cloud environment
|
|
100
|
+
- ✅ **No Local Impact**: Zero risk to your local system or files
|
|
101
|
+
- ✅ **Temporary Environment**: Sandbox destroyed after execution
|
|
102
|
+
- ✅ **Controlled Access**: Only specified components are installed
|
|
103
|
+
- ✅ **API Key Security**: Keys never leave your local environment
|
|
104
|
+
|
|
105
|
+
## Future Sandbox Providers
|
|
106
|
+
|
|
107
|
+
## ✅ Production Ready Features (v1.20.3+)
|
|
108
|
+
|
|
109
|
+
### Enhanced E2B Integration
|
|
110
|
+
- **Automatic File Download**: Generated files are automatically downloaded to local `./e2b-output/` directory
|
|
111
|
+
- **Extended Timeouts**: 15-minute sandbox lifetime with intelligent timeout management
|
|
112
|
+
- **Detailed Logging**: Step-by-step execution monitoring with debugging information
|
|
113
|
+
- **Environment Verification**: Automatic checks for Claude Code installation and permissions
|
|
114
|
+
- **Error Recovery**: Retry logic for connection issues and comprehensive error handling
|
|
115
|
+
|
|
116
|
+
### Advanced Debugging Tools
|
|
117
|
+
- **Real-time Monitor** (`e2b-monitor.py`): System resource monitoring and performance analysis
|
|
118
|
+
- **Debug Guide** (`SANDBOX_DEBUGGING.md`): Comprehensive troubleshooting documentation
|
|
119
|
+
- **Sandbox State Tracking**: Live monitoring of file system changes and process execution
|
|
120
|
+
|
|
121
|
+
The system is designed to support multiple sandbox providers:
|
|
122
|
+
|
|
123
|
+
- **E2B** (`--sandbox e2b`) - ✅ **Fully Implemented** - Cloud-based isolated execution environment
|
|
124
|
+
- **Docker** (`--sandbox docker`) - 🔄 Future - Local containerized execution
|
|
125
|
+
- **AWS CodeBuild** (`--sandbox aws`) - 🔄 Future - AWS-based sandbox environment
|
|
126
|
+
- **GitHub Codespaces** (`--sandbox github`) - 🔄 Future - GitHub's cloud development environment
|
|
127
|
+
- **Custom** (`--sandbox custom`) - 🔄 Future - User-defined sandbox configurations
|
|
128
|
+
|
|
129
|
+
## Troubleshooting
|
|
130
|
+
|
|
131
|
+
### Python Not Found
|
|
132
|
+
```bash
|
|
133
|
+
# Install Python 3.11+
|
|
134
|
+
brew install python3 # macOS
|
|
135
|
+
# or visit https://python.org/downloads
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### API Keys Not Set
|
|
139
|
+
```bash
|
|
140
|
+
# Create .env file in .claude/sandbox/
|
|
141
|
+
echo "E2B_API_KEY=your_key_here" >> .claude/sandbox/.env
|
|
142
|
+
echo "ANTHROPIC_API_KEY=your_key_here" >> .claude/sandbox/.env
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Dependencies Installation Failed
|
|
146
|
+
```bash
|
|
147
|
+
# Manual installation
|
|
148
|
+
cd .claude/sandbox
|
|
149
|
+
pip3 install -r requirements.txt
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Component Architecture
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
claude-code-templates/
|
|
156
|
+
└── cli-tool/
|
|
157
|
+
└── components/
|
|
158
|
+
└── sandbox/
|
|
159
|
+
├── e2b/ # E2B provider
|
|
160
|
+
│ ├── claude-code-sandbox.md # Component documentation
|
|
161
|
+
│ ├── e2b-launcher.py # Python launcher script
|
|
162
|
+
│ ├── requirements.txt # Python dependencies
|
|
163
|
+
│ └── .env.example # Environment template
|
|
164
|
+
├── docker/ # Future: Docker provider
|
|
165
|
+
├── aws/ # Future: AWS provider
|
|
166
|
+
└── README.md # This file
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The sandbox system integrates seamlessly with the existing Claude Code Templates component architecture, allowing any combination of agents, commands, MCPs, settings, and hooks to be installed and used within the secure sandbox environment.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# E2B API Configuration
|
|
2
|
+
# Get your API key from: https://e2b.dev/dashboard
|
|
3
|
+
E2B_API_KEY=your_e2b_api_key_here
|
|
4
|
+
|
|
5
|
+
# Anthropic API Configuration
|
|
6
|
+
# Get your API key from: https://console.anthropic.com
|
|
7
|
+
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
|
8
|
+
|
|
9
|
+
# Optional: Sandbox timeout in seconds (default: 300)
|
|
10
|
+
# E2B_TIMEOUT=300
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# E2B Sandbox Debugging Guide
|
|
2
|
+
|
|
3
|
+
## 🔍 Herramientas de Monitoreo Disponibles
|
|
4
|
+
|
|
5
|
+
### 1. Launcher Principal con Logging Mejorado
|
|
6
|
+
**Archivo**: `e2b-launcher.py`
|
|
7
|
+
- Logging detallado de cada paso
|
|
8
|
+
- Verificación de instalación de Claude Code
|
|
9
|
+
- Monitoreo de permisos y ambiente
|
|
10
|
+
- Timeouts extendidos para operaciones largas
|
|
11
|
+
- Descarga automática de archivos generados
|
|
12
|
+
|
|
13
|
+
### 2. Monitor de Sandbox en Tiempo Real
|
|
14
|
+
**Archivo**: `e2b-monitor.py`
|
|
15
|
+
- Monitoreo de recursos del sistema
|
|
16
|
+
- Tracking de file system en tiempo real
|
|
17
|
+
- Análisis de performance y memory usage
|
|
18
|
+
- Logging con timestamps detallados
|
|
19
|
+
|
|
20
|
+
### 3. Simulador Demo
|
|
21
|
+
Para testing sin API keys válidos, crea un archivo demo que simule el flujo completo.
|
|
22
|
+
|
|
23
|
+
## 🚨 Troubleshooting Común
|
|
24
|
+
|
|
25
|
+
### Problema: "Sandbox timeout"
|
|
26
|
+
**Síntomas**:
|
|
27
|
+
```
|
|
28
|
+
❌ Error: The sandbox was not found: This error is likely due to sandbox timeout
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Soluciones**:
|
|
32
|
+
1. **Aumentar timeout del sandbox**:
|
|
33
|
+
```python
|
|
34
|
+
sbx = Sandbox.create(timeout=600) # 10 minutos
|
|
35
|
+
sbx.set_timeout(900) # Extender a 15 minutos
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. **Usar el monitor para ver qué consume tiempo**:
|
|
39
|
+
```bash
|
|
40
|
+
python e2b-monitor.py "Your prompt here" "" your_e2b_key your_anthropic_key
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Problema: "Claude not found"
|
|
44
|
+
**Síntomas**:
|
|
45
|
+
```
|
|
46
|
+
❌ Claude not found, checking PATH...
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Debugging Steps**:
|
|
50
|
+
1. **Verificar template correcto**:
|
|
51
|
+
```python
|
|
52
|
+
template="anthropic-claude-code" # Debe ser exactamente este
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
2. **Verificar instalación en sandbox**:
|
|
56
|
+
```bash
|
|
57
|
+
# El launcher ejecuta automáticamente:
|
|
58
|
+
which claude
|
|
59
|
+
claude --version
|
|
60
|
+
echo $PATH
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Problema: "Permission denied"
|
|
64
|
+
**Síntomas**:
|
|
65
|
+
```
|
|
66
|
+
❌ Write permission issue
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Soluciones**:
|
|
70
|
+
1. **Verificar directorio de trabajo**:
|
|
71
|
+
```bash
|
|
72
|
+
pwd
|
|
73
|
+
whoami
|
|
74
|
+
ls -la
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
2. **Cambiar a directorio con permisos**:
|
|
78
|
+
```python
|
|
79
|
+
sbx.commands.run("cd /home/user && mkdir workspace && cd workspace")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Problema: API Key Issues
|
|
83
|
+
**Síntomas**:
|
|
84
|
+
```
|
|
85
|
+
❌ Error: 401: Invalid API key
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Debugging**:
|
|
89
|
+
1. **Verificar formato de API key**:
|
|
90
|
+
- E2B keys: formato específico de E2B
|
|
91
|
+
- Anthropic keys: empiezan con "sk-ant-"
|
|
92
|
+
|
|
93
|
+
2. **Verificar permisos**:
|
|
94
|
+
- Verificar que la key tenga permisos de sandbox
|
|
95
|
+
- Verificar quota/límites de la cuenta
|
|
96
|
+
|
|
97
|
+
## 📊 Usando el Monitor para Debugging
|
|
98
|
+
|
|
99
|
+
### Comando Básico:
|
|
100
|
+
```bash
|
|
101
|
+
python e2b-monitor.py "Create a React app" "" your_e2b_key your_anthropic_key
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Output del Monitor:
|
|
105
|
+
```
|
|
106
|
+
[14:32:15] INFO: 🚀 Starting enhanced E2B sandbox with monitoring
|
|
107
|
+
[14:32:16] INFO: ✅ Sandbox created: abc123xyz
|
|
108
|
+
[14:32:17] INFO: 🔍 System resources check
|
|
109
|
+
[14:32:17] INFO: Memory usage:
|
|
110
|
+
[14:32:17] INFO: total used free
|
|
111
|
+
[14:32:17] INFO: Mem: 2.0Gi 512Mi 1.5Gi
|
|
112
|
+
[14:32:18] INFO: 📁 Initial file system state
|
|
113
|
+
[14:32:18] INFO: Current directory: /home/user
|
|
114
|
+
[14:32:19] INFO: 🤖 Executing Claude Code with monitoring
|
|
115
|
+
[14:32:19] INFO: Starting monitored execution: echo 'Create a React app'...
|
|
116
|
+
[14:32:22] INFO: Command completed in 3.45 seconds
|
|
117
|
+
[14:32:22] INFO: Exit code: 0
|
|
118
|
+
[14:32:22] INFO: STDOUT length: 2847 characters
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## 🎯 Casos de Uso Específicos
|
|
122
|
+
|
|
123
|
+
### 1. **Debugging Timeouts**
|
|
124
|
+
```bash
|
|
125
|
+
# Usar el monitor para ver exactamente dónde se cuelga
|
|
126
|
+
python e2b-monitor.py "Complex prompt that times out"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 2. **Verificar Generación de Archivos**
|
|
130
|
+
El launcher automáticamente descarga archivos generados:
|
|
131
|
+
```
|
|
132
|
+
💾 DOWNLOADING FILES TO LOCAL MACHINE:
|
|
133
|
+
✅ Downloaded: ./index.html → ./e2b-output/index.html
|
|
134
|
+
✅ Downloaded: ./styles.css → ./e2b-output/styles.css
|
|
135
|
+
|
|
136
|
+
📁 All files downloaded to: /path/to/project/e2b-output
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 3. **Monitoreo de Performance**
|
|
140
|
+
```
|
|
141
|
+
[14:33:20] INFO: Top processes:
|
|
142
|
+
[14:33:20] INFO: USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
|
|
143
|
+
[14:33:20] INFO: user 1234 5.2 2.1 98765 43210 pts/0 S+ 14:32 0:01 claude
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 🛠 Configuración Avanzada
|
|
147
|
+
|
|
148
|
+
### Variables de Ambiente Útiles:
|
|
149
|
+
```bash
|
|
150
|
+
export E2B_DEBUG=1 # Debug mode
|
|
151
|
+
export ANTHROPIC_API_KEY=your_key # Claude API key
|
|
152
|
+
export E2B_API_KEY=your_key # E2B API key
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Configuración de Timeout Personalizada:
|
|
156
|
+
```python
|
|
157
|
+
# Para operaciones muy largas (ej: compilación completa)
|
|
158
|
+
sbx = Sandbox.create(timeout=1800) # 30 minutos
|
|
159
|
+
sbx.set_timeout(3600) # 1 hora máximo
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## 📋 Checklist de Debugging
|
|
163
|
+
|
|
164
|
+
### Antes de Reportar un Issue:
|
|
165
|
+
- [ ] API keys válidos y con permisos correctos
|
|
166
|
+
- [ ] Template correcto: "anthropic-claude-code"
|
|
167
|
+
- [ ] Timeout suficiente para la operación
|
|
168
|
+
- [ ] Ejecutar con el monitor para logs detallados
|
|
169
|
+
- [ ] Verificar que Claude Code esté instalado en sandbox
|
|
170
|
+
- [ ] Revisar permisos de escritura en directorio
|
|
171
|
+
- [ ] Comprobar memoria/recursos disponibles
|
|
172
|
+
|
|
173
|
+
### Información a Incluir en Reports:
|
|
174
|
+
- Output completo del launcher o monitor
|
|
175
|
+
- Sandbox ID si está disponible
|
|
176
|
+
- Prompt exacto que causa el problema
|
|
177
|
+
- Componentes instalados (si aplica)
|
|
178
|
+
- Tiempo de ejecución antes del fallo
|
|
179
|
+
|
|
180
|
+
## 🚀 Funcionalidades del Sistema
|
|
181
|
+
|
|
182
|
+
### Descarga Automática de Archivos
|
|
183
|
+
El launcher descarga automáticamente todos los archivos generados:
|
|
184
|
+
- HTML, CSS, JS, TS, TSX, Python, JSON, Markdown
|
|
185
|
+
- Se guardan en directorio local `./e2b-output/`
|
|
186
|
+
- Excluye archivos internos de Claude Code
|
|
187
|
+
- Preserva nombres de archivo originales
|
|
188
|
+
|
|
189
|
+
### Logging Detallado
|
|
190
|
+
- Verificación de instalación de Claude Code
|
|
191
|
+
- Monitoreo de permisos y ambiente del sandbox
|
|
192
|
+
- Tracking de exit codes y output length
|
|
193
|
+
- Timestamps para análisis de performance
|
|
194
|
+
|
|
195
|
+
### Timeouts Inteligentes
|
|
196
|
+
- 10 minutos timeout inicial para creación
|
|
197
|
+
- 15 minutos total extendido automáticamente
|
|
198
|
+
- 5 minutos timeout para ejecución de Claude Code
|
|
199
|
+
- Timeouts cortos para verificaciones (5-10 segundos)
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
**Con estas herramientas puedes monitorear exactamente qué está pasando dentro del sandbox E2B y debuggear cualquier problema que surja.**
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# E2B Claude Code Sandbox
|
|
2
|
+
|
|
3
|
+
Execute Claude Code in an isolated E2B cloud sandbox environment.
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
This component sets up E2B (E2B.dev) integration to run Claude Code in a secure, isolated cloud environment. Perfect for executing code safely without affecting your local system.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Isolated Execution**: Run Claude Code in a secure cloud sandbox
|
|
12
|
+
- **Pre-configured Environment**: Ships with Claude Code already installed
|
|
13
|
+
- **API Integration**: Seamless connection to Anthropic's Claude API
|
|
14
|
+
- **Safe Code Execution**: Execute prompts without local system risks
|
|
15
|
+
- **Component Installation**: Automatically installs any components specified with CLI flags
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- E2B API Key (get from https://e2b.dev/dashboard)
|
|
20
|
+
- Anthropic API Key
|
|
21
|
+
- Python 3.11+ (for E2B SDK)
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Execute a prompt in E2B sandbox (requires API keys as environment variables or CLI parameters)
|
|
27
|
+
npx claude-code-templates@latest --sandbox e2b --prompt "Create a React todo app"
|
|
28
|
+
|
|
29
|
+
# Pass API keys directly as parameters
|
|
30
|
+
npx claude-code-templates@latest --sandbox e2b \
|
|
31
|
+
--e2b-api-key your_e2b_key \
|
|
32
|
+
--anthropic-api-key your_anthropic_key \
|
|
33
|
+
--prompt "Create a React todo app"
|
|
34
|
+
|
|
35
|
+
# Install components and execute in sandbox
|
|
36
|
+
npx claude-code-templates@latest --sandbox e2b \
|
|
37
|
+
--agent frontend-developer \
|
|
38
|
+
--command setup-react \
|
|
39
|
+
--e2b-api-key your_e2b_key \
|
|
40
|
+
--anthropic-api-key your_anthropic_key \
|
|
41
|
+
--prompt "Create a modern todo app with TypeScript"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Environment Setup
|
|
45
|
+
|
|
46
|
+
The component will create:
|
|
47
|
+
- `.claude/sandbox/e2b-launcher.py` - Python script to launch E2B sandbox
|
|
48
|
+
- `.claude/sandbox/requirements.txt` - Python dependencies
|
|
49
|
+
- `.claude/sandbox/.env.example` - Environment variables template
|
|
50
|
+
|
|
51
|
+
## API Key Configuration
|
|
52
|
+
|
|
53
|
+
You can provide API keys in two ways:
|
|
54
|
+
|
|
55
|
+
### Option 1: CLI Parameters (Recommended)
|
|
56
|
+
```bash
|
|
57
|
+
# Pass keys directly as command parameters
|
|
58
|
+
npx claude-code-templates@latest --sandbox e2b \
|
|
59
|
+
--e2b-api-key your_e2b_api_key \
|
|
60
|
+
--anthropic-api-key your_anthropic_api_key \
|
|
61
|
+
--prompt "Your prompt here"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Option 2: Environment Variables
|
|
65
|
+
Set these environment variables in your shell or `.env` file:
|
|
66
|
+
```bash
|
|
67
|
+
export E2B_API_KEY=your_e2b_api_key_here
|
|
68
|
+
export ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
|
69
|
+
|
|
70
|
+
# Or create .claude/sandbox/.env file:
|
|
71
|
+
E2B_API_KEY=your_e2b_api_key_here
|
|
72
|
+
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Note**: CLI parameters take precedence over environment variables.
|
|
76
|
+
|
|
77
|
+
## How it Works
|
|
78
|
+
|
|
79
|
+
1. Creates E2B sandbox with `anthropic-claude-code` template
|
|
80
|
+
2. Installs any specified components (agents, commands, etc.)
|
|
81
|
+
3. Executes your prompt using Claude Code inside the sandbox
|
|
82
|
+
4. Returns the complete output and any generated files
|
|
83
|
+
5. Automatically cleans up the sandbox after execution
|
|
84
|
+
|
|
85
|
+
## Security Benefits
|
|
86
|
+
|
|
87
|
+
- **Isolation**: Code runs in a separate cloud environment
|
|
88
|
+
- **No Local Impact**: No risk to your local system or files
|
|
89
|
+
- **Temporary**: Sandbox is destroyed after execution
|
|
90
|
+
- **Controlled**: Only specified components and prompts are executed
|
|
91
|
+
|
|
92
|
+
## Examples
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Simple web app creation
|
|
96
|
+
npx claude-code-templates@latest --sandbox e2b --prompt "Create an HTML page with CSS animations"
|
|
97
|
+
|
|
98
|
+
# Full stack development
|
|
99
|
+
npx claude-code-templates@latest --sandbox e2b --agent fullstack-developer --prompt "Create a Node.js API with authentication"
|
|
100
|
+
|
|
101
|
+
# Data analysis
|
|
102
|
+
npx claude-code-templates@latest --sandbox e2b --agent data-scientist --prompt "Analyze this CSV data and create visualizations"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Template Information
|
|
106
|
+
|
|
107
|
+
- **Provider**: E2B (https://e2b.dev)
|
|
108
|
+
- **Base Template**: anthropic-claude-code
|
|
109
|
+
- **Timeout**: 5 minutes (configurable)
|
|
110
|
+
- **Environment**: Ubuntu with Claude Code pre-installed
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
#!/usr/bin/env python3.11
|
|
2
|
+
"""
|
|
3
|
+
E2B Claude Code Sandbox Launcher
|
|
4
|
+
Executes Claude Code prompts in isolated E2B cloud sandbox
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
import datetime
|
|
11
|
+
import re
|
|
12
|
+
import threading
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
# Debug: Print Python path information
|
|
16
|
+
print(f"Python executable: {sys.executable}")
|
|
17
|
+
print(f"Python version: {sys.version}")
|
|
18
|
+
print(f"Python path: {sys.path[:3]}...") # Show first 3 paths
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
from e2b import Sandbox
|
|
22
|
+
print("✓ E2B imported successfully")
|
|
23
|
+
except ImportError as e:
|
|
24
|
+
print(f"✗ E2B import failed: {e}")
|
|
25
|
+
print("Trying to install E2B...")
|
|
26
|
+
import subprocess
|
|
27
|
+
# Try different installation methods for different Python environments
|
|
28
|
+
install_commands = [
|
|
29
|
+
[sys.executable, '-m', 'pip', 'install', '--user', 'e2b'], # User install first
|
|
30
|
+
[sys.executable, '-m', 'pip', 'install', '--break-system-packages', 'e2b'], # System packages
|
|
31
|
+
[sys.executable, '-m', 'pip', 'install', 'e2b'] # Default fallback
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
result = None
|
|
35
|
+
for cmd in install_commands:
|
|
36
|
+
print(f"Trying: {' '.join(cmd)}")
|
|
37
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
38
|
+
if result.returncode == 0:
|
|
39
|
+
print("✓ Installation successful")
|
|
40
|
+
break
|
|
41
|
+
else:
|
|
42
|
+
print(f"✗ Failed: {result.stderr.strip()[:100]}...")
|
|
43
|
+
|
|
44
|
+
if result is None:
|
|
45
|
+
result = subprocess.run([sys.executable, '-m', 'pip', 'install', 'e2b'],
|
|
46
|
+
capture_output=True, text=True)
|
|
47
|
+
print(f"Install result: {result.returncode}")
|
|
48
|
+
if result.stdout:
|
|
49
|
+
print(f"Install stdout: {result.stdout}")
|
|
50
|
+
if result.stderr:
|
|
51
|
+
print(f"Install stderr: {result.stderr}")
|
|
52
|
+
|
|
53
|
+
# Try importing again
|
|
54
|
+
try:
|
|
55
|
+
from e2b import Sandbox
|
|
56
|
+
print("✓ E2B imported successfully after install")
|
|
57
|
+
except ImportError as e2:
|
|
58
|
+
print(f"✗ E2B still failed after install: {e2}")
|
|
59
|
+
sys.exit(1)
|
|
60
|
+
|
|
61
|
+
# Try to import and use dotenv if available, but don't fail if it's not
|
|
62
|
+
try:
|
|
63
|
+
from dotenv import load_dotenv
|
|
64
|
+
load_dotenv()
|
|
65
|
+
except ImportError:
|
|
66
|
+
# dotenv is optional since we can get keys from command line arguments
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
def main():
|
|
70
|
+
|
|
71
|
+
# Parse command line arguments
|
|
72
|
+
if len(sys.argv) < 2:
|
|
73
|
+
print("Usage: python e2b-launcher.py <prompt> [components_to_install] [e2b_api_key] [anthropic_api_key]")
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
|
|
76
|
+
prompt = sys.argv[1]
|
|
77
|
+
components_to_install = sys.argv[2] if len(sys.argv) > 2 else ""
|
|
78
|
+
|
|
79
|
+
# Get API keys from command line arguments or environment variables
|
|
80
|
+
e2b_api_key = sys.argv[3] if len(sys.argv) > 3 else os.getenv('E2B_API_KEY')
|
|
81
|
+
anthropic_api_key = sys.argv[4] if len(sys.argv) > 4 else os.getenv('ANTHROPIC_API_KEY')
|
|
82
|
+
|
|
83
|
+
if not e2b_api_key:
|
|
84
|
+
print("Error: E2B API key is required")
|
|
85
|
+
print("Provide via command line argument or E2B_API_KEY environment variable")
|
|
86
|
+
sys.exit(1)
|
|
87
|
+
|
|
88
|
+
if not anthropic_api_key:
|
|
89
|
+
print("Error: Anthropic API key is required")
|
|
90
|
+
print("Provide via command line argument or ANTHROPIC_API_KEY environment variable")
|
|
91
|
+
sys.exit(1)
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
# Create E2B sandbox with Claude Code template with retry logic
|
|
95
|
+
print("🚀 Creating E2B sandbox with Claude Code...")
|
|
96
|
+
|
|
97
|
+
# Try creating sandbox with retries for WebSocket issues
|
|
98
|
+
max_retries = 3
|
|
99
|
+
retry_count = 0
|
|
100
|
+
sbx = None
|
|
101
|
+
|
|
102
|
+
while retry_count < max_retries and sbx is None:
|
|
103
|
+
try:
|
|
104
|
+
if retry_count > 0:
|
|
105
|
+
print(f"🔄 Retry {retry_count}/{max_retries - 1} - WebSocket connection...")
|
|
106
|
+
|
|
107
|
+
sbx = Sandbox.create(
|
|
108
|
+
template="anthropic-claude-code",
|
|
109
|
+
api_key=e2b_api_key,
|
|
110
|
+
envs={
|
|
111
|
+
'ANTHROPIC_API_KEY': anthropic_api_key,
|
|
112
|
+
},
|
|
113
|
+
timeout=600, # 10 minutes timeout for longer operations
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Keep sandbox alive during operations
|
|
117
|
+
print(f"🔄 Extending sandbox timeout to prevent early termination...")
|
|
118
|
+
sbx.set_timeout(900) # 15 minutes total
|
|
119
|
+
print(f"✅ Sandbox created: {sbx.sandbox_id}")
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
except Exception as e:
|
|
123
|
+
error_msg = str(e).lower()
|
|
124
|
+
if "websocket" in error_msg or "connection" in error_msg or "timeout" in error_msg:
|
|
125
|
+
retry_count += 1
|
|
126
|
+
if retry_count < max_retries:
|
|
127
|
+
print(f"⚠️ WebSocket connection failed (attempt {retry_count}), retrying in 3 seconds...")
|
|
128
|
+
import time
|
|
129
|
+
time.sleep(3)
|
|
130
|
+
continue
|
|
131
|
+
else:
|
|
132
|
+
print(f"❌ WebSocket connection failed after {max_retries} attempts")
|
|
133
|
+
print("💡 This might be due to:")
|
|
134
|
+
print(" • Network/firewall restrictions blocking WebSocket connections")
|
|
135
|
+
print(" • Temporary E2B service issues")
|
|
136
|
+
print(" • Corporate proxy blocking WebSocket traffic")
|
|
137
|
+
print("💡 Try:")
|
|
138
|
+
print(" • Running from a different network")
|
|
139
|
+
print(" • Checking your firewall/proxy settings")
|
|
140
|
+
print(" • Waiting a few minutes and trying again")
|
|
141
|
+
raise e
|
|
142
|
+
else:
|
|
143
|
+
# Non-WebSocket error, don't retry
|
|
144
|
+
raise e
|
|
145
|
+
|
|
146
|
+
if sbx is None:
|
|
147
|
+
raise Exception("Failed to create sandbox after all retry attempts")
|
|
148
|
+
|
|
149
|
+
# Install components if specified
|
|
150
|
+
if components_to_install:
|
|
151
|
+
print("📦 Installing specified components...")
|
|
152
|
+
install_result = sbx.commands.run(
|
|
153
|
+
f"npx claude-code-templates@latest {components_to_install}",
|
|
154
|
+
timeout=120, # 2 minutes for component installation
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if install_result.exit_code != 0:
|
|
158
|
+
print(f"⚠️ Component installation warnings:")
|
|
159
|
+
print(install_result.stderr)
|
|
160
|
+
else:
|
|
161
|
+
print("✅ Components installed successfully")
|
|
162
|
+
|
|
163
|
+
# Build enhanced prompt with instructions
|
|
164
|
+
# Parse components to extract agents
|
|
165
|
+
agents = []
|
|
166
|
+
if components_to_install:
|
|
167
|
+
# Split by '--' to get individual component types
|
|
168
|
+
parts = components_to_install.split('--')
|
|
169
|
+
for part in parts:
|
|
170
|
+
part = part.strip()
|
|
171
|
+
if part.startswith('agent '):
|
|
172
|
+
# Extract agent names after 'agent ' prefix
|
|
173
|
+
agent_names = part[6:].strip() # Remove 'agent ' prefix
|
|
174
|
+
if agent_names:
|
|
175
|
+
# Split by comma if multiple agents
|
|
176
|
+
agents.extend([a.strip() for a in agent_names.split(',')])
|
|
177
|
+
|
|
178
|
+
# Create enhanced prompt with proper instructions
|
|
179
|
+
if agents:
|
|
180
|
+
agent_list = ', '.join(agents)
|
|
181
|
+
enhanced_prompt = f"""You are Claude Code, an AI assistant specialized in software development.
|
|
182
|
+
|
|
183
|
+
IMPORTANT INSTRUCTIONS:
|
|
184
|
+
1. Execute the user's request immediately and create the requested code/files
|
|
185
|
+
2. You have access to the following specialized agents: {agent_list}
|
|
186
|
+
3. Use these agents in the order you deem most appropriate for completing the task
|
|
187
|
+
4. Generate all necessary files and code to fulfill the request
|
|
188
|
+
5. Be proactive and create a complete, working implementation
|
|
189
|
+
|
|
190
|
+
USER REQUEST: {prompt}
|
|
191
|
+
|
|
192
|
+
Now, please execute this request and create all necessary files."""
|
|
193
|
+
else:
|
|
194
|
+
enhanced_prompt = f"""You are Claude Code, an AI assistant specialized in software development.
|
|
195
|
+
|
|
196
|
+
IMPORTANT INSTRUCTIONS:
|
|
197
|
+
1. Execute the user's request immediately and create the requested code/files
|
|
198
|
+
2. Generate all necessary files and code to fulfill the request
|
|
199
|
+
3. Be proactive and create a complete, working implementation
|
|
200
|
+
4. Don't just acknowledge the request - actually create the implementation
|
|
201
|
+
|
|
202
|
+
USER REQUEST: {prompt}
|
|
203
|
+
|
|
204
|
+
Now, please execute this request and create all necessary files."""
|
|
205
|
+
|
|
206
|
+
# Execute Claude Code with the enhanced prompt
|
|
207
|
+
print(f"🤖 Executing Claude Code with prompt: '{prompt[:50]}{'...' if len(prompt) > 50 else ''}'")
|
|
208
|
+
if agents:
|
|
209
|
+
print(f"🤝 Using agents: {', '.join(agents)}")
|
|
210
|
+
|
|
211
|
+
# First, check if Claude Code is installed and available
|
|
212
|
+
print("🔍 Checking Claude Code installation...")
|
|
213
|
+
check_result = sbx.commands.run("which claude", timeout=10)
|
|
214
|
+
if check_result.exit_code == 0:
|
|
215
|
+
print(f"✅ Claude found at: {check_result.stdout.strip()}")
|
|
216
|
+
else:
|
|
217
|
+
print("❌ Claude not found, checking PATH...")
|
|
218
|
+
path_result = sbx.commands.run("echo $PATH", timeout=5)
|
|
219
|
+
print(f"PATH: {path_result.stdout}")
|
|
220
|
+
ls_result = sbx.commands.run("ls -la /usr/local/bin/ | grep claude", timeout=5)
|
|
221
|
+
print(f"Claude binaries: {ls_result.stdout}")
|
|
222
|
+
|
|
223
|
+
# Check current directory and permissions
|
|
224
|
+
print("🔍 Checking sandbox environment...")
|
|
225
|
+
pwd_result = sbx.commands.run("pwd", timeout=5)
|
|
226
|
+
print(f"Current directory: {pwd_result.stdout.strip()}")
|
|
227
|
+
|
|
228
|
+
whoami_result = sbx.commands.run("whoami", timeout=5)
|
|
229
|
+
print(f"Current user: {whoami_result.stdout.strip()}")
|
|
230
|
+
|
|
231
|
+
# Check if we can write to current directory
|
|
232
|
+
test_write = sbx.commands.run("touch test_write.tmp && rm test_write.tmp", timeout=5)
|
|
233
|
+
if test_write.exit_code == 0:
|
|
234
|
+
print("✅ Write permissions OK")
|
|
235
|
+
else:
|
|
236
|
+
print("❌ Write permission issue")
|
|
237
|
+
|
|
238
|
+
# Build Claude Code command with enhanced prompt and better error handling
|
|
239
|
+
# Escape single quotes in the enhanced prompt
|
|
240
|
+
escaped_prompt = enhanced_prompt.replace("'", "'\\''")
|
|
241
|
+
claude_command = f"echo '{escaped_prompt}' | claude -p --dangerously-skip-permissions"
|
|
242
|
+
|
|
243
|
+
# Show the original user prompt in the command display (not the enhanced version)
|
|
244
|
+
display_prompt = prompt[:100] + '...' if len(prompt) > 100 else prompt
|
|
245
|
+
print(f"🚀 Running command: echo '{display_prompt}' | claude -p --dangerously-skip-permissions")
|
|
246
|
+
|
|
247
|
+
# Show loading message with visual separation
|
|
248
|
+
print("")
|
|
249
|
+
print("=" * 60)
|
|
250
|
+
print("☁️ EXECUTING CLAUDE CODE IN SECURE CLOUD SANDBOX")
|
|
251
|
+
print("=" * 60)
|
|
252
|
+
print("")
|
|
253
|
+
print(" ⏳ Starting execution...")
|
|
254
|
+
print(" 🔒 Isolated E2B environment active")
|
|
255
|
+
print(" 📡 Streaming real-time output below:")
|
|
256
|
+
print("")
|
|
257
|
+
print("-" * 60)
|
|
258
|
+
print("📝 LIVE OUTPUT:")
|
|
259
|
+
print("-" * 60)
|
|
260
|
+
|
|
261
|
+
# Collect output for later use
|
|
262
|
+
stdout_buffer = []
|
|
263
|
+
stderr_buffer = []
|
|
264
|
+
|
|
265
|
+
# Track if we've received any output and last activity time
|
|
266
|
+
has_output = [False] # Use list to allow modification in nested function
|
|
267
|
+
last_activity = [time.time()]
|
|
268
|
+
execution_complete = [False]
|
|
269
|
+
|
|
270
|
+
# Progress indicator thread
|
|
271
|
+
def show_progress():
|
|
272
|
+
"""Show periodic progress updates if no output for a while"""
|
|
273
|
+
progress_messages = [
|
|
274
|
+
"⏳ Still processing...",
|
|
275
|
+
"🔄 Claude Code is working on your request...",
|
|
276
|
+
"⚙️ Analyzing requirements...",
|
|
277
|
+
"🛠️ Building solution...",
|
|
278
|
+
"📝 Generating code...",
|
|
279
|
+
"🔍 Reviewing implementation..."
|
|
280
|
+
]
|
|
281
|
+
message_index = 0
|
|
282
|
+
|
|
283
|
+
while not execution_complete[0]:
|
|
284
|
+
time.sleep(5) # Check every 5 seconds
|
|
285
|
+
|
|
286
|
+
# If no activity for 10 seconds and no output yet
|
|
287
|
+
if not has_output[0] and (time.time() - last_activity[0]) > 10:
|
|
288
|
+
print(f"\n {progress_messages[message_index % len(progress_messages)]}")
|
|
289
|
+
message_index += 1
|
|
290
|
+
last_activity[0] = time.time()
|
|
291
|
+
|
|
292
|
+
# Start progress thread
|
|
293
|
+
progress_thread = threading.Thread(target=show_progress, daemon=True)
|
|
294
|
+
progress_thread.start()
|
|
295
|
+
|
|
296
|
+
# Define callbacks for streaming output
|
|
297
|
+
def on_stdout(data):
|
|
298
|
+
"""Handle stdout output in real-time"""
|
|
299
|
+
if data:
|
|
300
|
+
# Mark that we've received output and update activity time
|
|
301
|
+
if not has_output[0]:
|
|
302
|
+
has_output[0] = True
|
|
303
|
+
print("\n🎯 Claude Code started responding:\n")
|
|
304
|
+
|
|
305
|
+
last_activity[0] = time.time()
|
|
306
|
+
|
|
307
|
+
# Print the data as it comes
|
|
308
|
+
print(data, end='', flush=True)
|
|
309
|
+
stdout_buffer.append(data)
|
|
310
|
+
|
|
311
|
+
def on_stderr(data):
|
|
312
|
+
"""Handle stderr output in real-time"""
|
|
313
|
+
if data:
|
|
314
|
+
# Mark that we've received output and update activity time
|
|
315
|
+
if not has_output[0]:
|
|
316
|
+
has_output[0] = True
|
|
317
|
+
print("\n🎯 Claude Code started responding:\n")
|
|
318
|
+
|
|
319
|
+
last_activity[0] = time.time()
|
|
320
|
+
|
|
321
|
+
# Print stderr with warning prefix
|
|
322
|
+
if data.strip():
|
|
323
|
+
print(f"⚠️ {data}", end='', flush=True)
|
|
324
|
+
stderr_buffer.append(data)
|
|
325
|
+
|
|
326
|
+
# Execute with streaming output and extended timeout
|
|
327
|
+
try:
|
|
328
|
+
result = sbx.commands.run(
|
|
329
|
+
claude_command,
|
|
330
|
+
timeout=600, # 10 minutes timeout for complex operations
|
|
331
|
+
on_stdout=on_stdout,
|
|
332
|
+
on_stderr=on_stderr
|
|
333
|
+
)
|
|
334
|
+
finally:
|
|
335
|
+
# Mark execution as complete to stop progress thread
|
|
336
|
+
execution_complete[0] = True
|
|
337
|
+
|
|
338
|
+
# Join collected output
|
|
339
|
+
full_stdout = ''.join(stdout_buffer)
|
|
340
|
+
full_stderr = ''.join(stderr_buffer)
|
|
341
|
+
|
|
342
|
+
# Print execution summary
|
|
343
|
+
print("")
|
|
344
|
+
print("-" * 60)
|
|
345
|
+
print(f"🔍 Command exit code: {result.exit_code}")
|
|
346
|
+
|
|
347
|
+
# Since we already streamed the output, just show summary
|
|
348
|
+
if full_stdout:
|
|
349
|
+
print(f"📤 Total stdout: {len(full_stdout)} characters")
|
|
350
|
+
if full_stderr:
|
|
351
|
+
print(f"⚠️ Total stderr: {len(full_stderr)} characters")
|
|
352
|
+
|
|
353
|
+
# List generated files
|
|
354
|
+
print("=" * 60)
|
|
355
|
+
print("📁 GENERATED FILES:")
|
|
356
|
+
print("=" * 60)
|
|
357
|
+
|
|
358
|
+
files_result = sbx.commands.run("find . -type f \\( -name '*.html' -o -name '*.js' -o -name '*.css' -o -name '*.py' -o -name '*.json' -o -name '*.md' -o -name '*.tsx' -o -name '*.ts' \\) ! -path '*/.claude/*' ! -path '*/node_modules/*' | head -20")
|
|
359
|
+
if files_result.stdout.strip():
|
|
360
|
+
print(files_result.stdout)
|
|
361
|
+
|
|
362
|
+
# Download important files to local machine
|
|
363
|
+
print("\n" + "=" * 60)
|
|
364
|
+
print("💾 DOWNLOADING FILES TO LOCAL MACHINE:")
|
|
365
|
+
print("=" * 60)
|
|
366
|
+
|
|
367
|
+
# Create project directory with sandbox ID in current working directory
|
|
368
|
+
project_dir = f"sandbox-{sbx.sandbox_id[:8]}" # Use first 8 chars of sandbox ID
|
|
369
|
+
local_output_dir = os.path.join(os.getcwd(), project_dir) # Use current working directory
|
|
370
|
+
|
|
371
|
+
# Ensure the project directory exists
|
|
372
|
+
os.makedirs(local_output_dir, exist_ok=True)
|
|
373
|
+
|
|
374
|
+
print(f"📂 Downloading files to project directory: {local_output_dir}")
|
|
375
|
+
print(f"📍 Current working directory: {os.getcwd()}")
|
|
376
|
+
|
|
377
|
+
files_to_download = files_result.stdout.strip().split('\n')
|
|
378
|
+
for file_path in files_to_download:
|
|
379
|
+
file_path = file_path.strip()
|
|
380
|
+
if file_path: # Already filtered out .claude and node_modules in find command
|
|
381
|
+
try:
|
|
382
|
+
# Read file content from sandbox
|
|
383
|
+
file_content = sbx.commands.run(f"cat '{file_path}'", timeout=30)
|
|
384
|
+
if file_content.exit_code == 0:
|
|
385
|
+
# Preserve directory structure by removing leading ./
|
|
386
|
+
relative_path = file_path.lstrip('./')
|
|
387
|
+
local_file = os.path.join(local_output_dir, relative_path)
|
|
388
|
+
|
|
389
|
+
# Create directory structure if needed
|
|
390
|
+
os.makedirs(os.path.dirname(local_file), exist_ok=True)
|
|
391
|
+
|
|
392
|
+
# Write file locally
|
|
393
|
+
with open(local_file, 'w', encoding='utf-8') as f:
|
|
394
|
+
f.write(file_content.stdout)
|
|
395
|
+
|
|
396
|
+
print(f"✅ Downloaded: {file_path} → {local_file}")
|
|
397
|
+
else:
|
|
398
|
+
print(f"❌ Failed to read: {file_path}")
|
|
399
|
+
except Exception as e:
|
|
400
|
+
print(f"❌ Error downloading {file_path}: {e}")
|
|
401
|
+
|
|
402
|
+
print(f"\n📁 All files downloaded to: {os.path.abspath(local_output_dir)}")
|
|
403
|
+
|
|
404
|
+
else:
|
|
405
|
+
print("No common files generated")
|
|
406
|
+
|
|
407
|
+
print("=" * 60)
|
|
408
|
+
print(f"✅ Execution completed successfully")
|
|
409
|
+
print(f"🗂️ Sandbox ID: {sbx.sandbox_id}")
|
|
410
|
+
print("💡 Note: Sandbox will be automatically destroyed")
|
|
411
|
+
|
|
412
|
+
except Exception as e:
|
|
413
|
+
print(f"❌ Error executing Claude Code in sandbox: {str(e)}")
|
|
414
|
+
sys.exit(1)
|
|
415
|
+
|
|
416
|
+
finally:
|
|
417
|
+
# Cleanup sandbox
|
|
418
|
+
try:
|
|
419
|
+
if 'sbx' in locals():
|
|
420
|
+
sbx.kill()
|
|
421
|
+
print("🧹 Sandbox cleaned up")
|
|
422
|
+
except Exception as cleanup_error:
|
|
423
|
+
print(f"⚠️ Cleanup warning: {cleanup_error}")
|
|
424
|
+
|
|
425
|
+
if __name__ == "__main__":
|
|
426
|
+
main()
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env python3.11
|
|
2
|
+
"""
|
|
3
|
+
E2B Sandbox Real-time Monitor
|
|
4
|
+
Provides real-time monitoring and debugging of E2B sandbox operations
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
import json
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
def log_with_timestamp(message, level="INFO"):
|
|
14
|
+
timestamp = datetime.now().strftime("%H:%M:%S")
|
|
15
|
+
print(f"[{timestamp}] {level}: {message}")
|
|
16
|
+
|
|
17
|
+
def monitor_sandbox_execution(sbx, command, timeout=600):
|
|
18
|
+
"""
|
|
19
|
+
Monitor sandbox command execution with real-time feedback
|
|
20
|
+
"""
|
|
21
|
+
log_with_timestamp(f"Starting monitored execution: {command[:100]}...")
|
|
22
|
+
|
|
23
|
+
# Start command execution
|
|
24
|
+
start_time = time.time()
|
|
25
|
+
result = None
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
# Execute command with monitoring
|
|
29
|
+
log_with_timestamp("Command started, monitoring execution...")
|
|
30
|
+
|
|
31
|
+
# For real implementation, you would run the command and monitor
|
|
32
|
+
# This is a template for when you have valid API keys
|
|
33
|
+
result = sbx.commands.run(command, timeout=timeout)
|
|
34
|
+
|
|
35
|
+
elapsed = time.time() - start_time
|
|
36
|
+
log_with_timestamp(f"Command completed in {elapsed:.2f} seconds")
|
|
37
|
+
|
|
38
|
+
# Log execution results
|
|
39
|
+
log_with_timestamp(f"Exit code: {result.exit_code}")
|
|
40
|
+
if result.stdout:
|
|
41
|
+
log_with_timestamp(f"STDOUT length: {len(result.stdout)} characters")
|
|
42
|
+
if len(result.stdout) < 500:
|
|
43
|
+
log_with_timestamp(f"STDOUT preview: {result.stdout[:200]}...")
|
|
44
|
+
if result.stderr:
|
|
45
|
+
log_with_timestamp(f"STDERR length: {len(result.stderr)} characters", "WARNING")
|
|
46
|
+
log_with_timestamp(f"STDERR: {result.stderr}", "ERROR")
|
|
47
|
+
|
|
48
|
+
return result
|
|
49
|
+
|
|
50
|
+
except Exception as e:
|
|
51
|
+
elapsed = time.time() - start_time
|
|
52
|
+
log_with_timestamp(f"Command failed after {elapsed:.2f} seconds: {e}", "ERROR")
|
|
53
|
+
raise e
|
|
54
|
+
|
|
55
|
+
def monitor_file_system(sbx, description="Monitoring file system"):
|
|
56
|
+
"""
|
|
57
|
+
Monitor sandbox file system state
|
|
58
|
+
"""
|
|
59
|
+
log_with_timestamp(f"📁 {description}")
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
# Check current directory
|
|
63
|
+
pwd_result = sbx.commands.run("pwd", timeout=10)
|
|
64
|
+
log_with_timestamp(f"Current directory: {pwd_result.stdout.strip()}")
|
|
65
|
+
|
|
66
|
+
# List files
|
|
67
|
+
ls_result = sbx.commands.run("ls -la", timeout=10)
|
|
68
|
+
log_with_timestamp("Directory contents:")
|
|
69
|
+
for line in ls_result.stdout.split('\n')[:10]: # Show first 10 files
|
|
70
|
+
if line.strip():
|
|
71
|
+
log_with_timestamp(f" {line}")
|
|
72
|
+
|
|
73
|
+
# Check disk usage
|
|
74
|
+
du_result = sbx.commands.run("du -sh .", timeout=10)
|
|
75
|
+
log_with_timestamp(f"Directory size: {du_result.stdout.strip()}")
|
|
76
|
+
|
|
77
|
+
# Check for specific file types
|
|
78
|
+
find_result = sbx.commands.run("find . -type f -name '*.html' -o -name '*.js' -o -name '*.css' -o -name '*.json' | head -10", timeout=15)
|
|
79
|
+
if find_result.stdout.strip():
|
|
80
|
+
log_with_timestamp("Generated files found:")
|
|
81
|
+
for file in find_result.stdout.split('\n')[:10]:
|
|
82
|
+
if file.strip():
|
|
83
|
+
log_with_timestamp(f" 📄 {file.strip()}")
|
|
84
|
+
|
|
85
|
+
except Exception as e:
|
|
86
|
+
log_with_timestamp(f"File system monitoring error: {e}", "ERROR")
|
|
87
|
+
|
|
88
|
+
def monitor_system_resources(sbx):
|
|
89
|
+
"""
|
|
90
|
+
Monitor sandbox system resources
|
|
91
|
+
"""
|
|
92
|
+
log_with_timestamp("🔍 System resources check")
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
# Memory usage
|
|
96
|
+
mem_result = sbx.commands.run("free -h", timeout=10)
|
|
97
|
+
log_with_timestamp("Memory usage:")
|
|
98
|
+
for line in mem_result.stdout.split('\n')[:3]:
|
|
99
|
+
if line.strip():
|
|
100
|
+
log_with_timestamp(f" {line}")
|
|
101
|
+
|
|
102
|
+
# CPU load
|
|
103
|
+
load_result = sbx.commands.run("uptime", timeout=10)
|
|
104
|
+
log_with_timestamp(f"System load: {load_result.stdout.strip()}")
|
|
105
|
+
|
|
106
|
+
# Process list (top 5 processes)
|
|
107
|
+
ps_result = sbx.commands.run("ps aux --sort=-%cpu | head -6", timeout=10)
|
|
108
|
+
log_with_timestamp("Top processes:")
|
|
109
|
+
lines = ps_result.stdout.split('\n')
|
|
110
|
+
for line in lines[:6]: # Header + top 5
|
|
111
|
+
if line.strip():
|
|
112
|
+
log_with_timestamp(f" {line}")
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
log_with_timestamp(f"System monitoring error: {e}", "ERROR")
|
|
116
|
+
|
|
117
|
+
def enhanced_sandbox_execution(prompt, components_to_install="", e2b_api_key=None, anthropic_api_key=None):
|
|
118
|
+
"""
|
|
119
|
+
Enhanced sandbox execution with full monitoring
|
|
120
|
+
This would be called instead of the basic launcher when you have valid API keys
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
log_with_timestamp("🚀 Starting enhanced E2B sandbox with monitoring")
|
|
124
|
+
log_with_timestamp("=" * 60)
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
from e2b import Sandbox
|
|
128
|
+
log_with_timestamp("✅ E2B SDK imported successfully")
|
|
129
|
+
except ImportError as e:
|
|
130
|
+
log_with_timestamp(f"❌ E2B import failed: {e}", "ERROR")
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
if not e2b_api_key or not anthropic_api_key:
|
|
134
|
+
log_with_timestamp("❌ Missing API keys", "ERROR")
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
# Create sandbox with monitoring
|
|
139
|
+
log_with_timestamp("Creating E2B sandbox...")
|
|
140
|
+
sbx = Sandbox.create(
|
|
141
|
+
template="anthropic-claude-code",
|
|
142
|
+
api_key=e2b_api_key,
|
|
143
|
+
envs={'ANTHROPIC_API_KEY': anthropic_api_key},
|
|
144
|
+
timeout=600
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
log_with_timestamp(f"✅ Sandbox created: {sbx.sandbox_id}")
|
|
148
|
+
sbx.set_timeout(900)
|
|
149
|
+
log_with_timestamp("⏱️ Sandbox timeout extended to 15 minutes")
|
|
150
|
+
|
|
151
|
+
# Initial system check
|
|
152
|
+
monitor_system_resources(sbx)
|
|
153
|
+
monitor_file_system(sbx, "Initial file system state")
|
|
154
|
+
|
|
155
|
+
# Install components if specified
|
|
156
|
+
if components_to_install:
|
|
157
|
+
log_with_timestamp(f"📦 Installing components: {components_to_install}")
|
|
158
|
+
install_command = f"npx claude-code-templates@latest {components_to_install}"
|
|
159
|
+
monitor_sandbox_execution(sbx, install_command, timeout=120)
|
|
160
|
+
monitor_file_system(sbx, "After components installation")
|
|
161
|
+
|
|
162
|
+
# Verify Claude Code installation
|
|
163
|
+
log_with_timestamp("🔍 Verifying Claude Code installation")
|
|
164
|
+
claude_check = monitor_sandbox_execution(sbx, "which claude", timeout=10)
|
|
165
|
+
if claude_check.exit_code == 0:
|
|
166
|
+
version_check = monitor_sandbox_execution(sbx, "claude --version", timeout=10)
|
|
167
|
+
log_with_timestamp(f"Claude version: {version_check.stdout.strip()}")
|
|
168
|
+
else:
|
|
169
|
+
log_with_timestamp("❌ Claude Code not found in PATH", "ERROR")
|
|
170
|
+
|
|
171
|
+
# Execute main prompt with monitoring
|
|
172
|
+
log_with_timestamp("🤖 Executing Claude Code with monitoring")
|
|
173
|
+
claude_command = f"echo '{prompt}' | claude -p --dangerously-skip-permissions"
|
|
174
|
+
|
|
175
|
+
result = monitor_sandbox_execution(sbx, claude_command, timeout=600)
|
|
176
|
+
|
|
177
|
+
# Final file system check
|
|
178
|
+
monitor_file_system(sbx, "Final file system state")
|
|
179
|
+
|
|
180
|
+
# Display results
|
|
181
|
+
log_with_timestamp("=" * 60)
|
|
182
|
+
log_with_timestamp("🎯 CLAUDE CODE RESULTS")
|
|
183
|
+
log_with_timestamp("=" * 60)
|
|
184
|
+
|
|
185
|
+
if result.stdout:
|
|
186
|
+
print(result.stdout)
|
|
187
|
+
|
|
188
|
+
if result.stderr:
|
|
189
|
+
log_with_timestamp("⚠️ STDERR OUTPUT", "WARNING")
|
|
190
|
+
print(result.stderr)
|
|
191
|
+
|
|
192
|
+
log_with_timestamp("✅ Execution completed successfully")
|
|
193
|
+
|
|
194
|
+
# Cleanup
|
|
195
|
+
sbx.kill()
|
|
196
|
+
log_with_timestamp("🧹 Sandbox cleaned up")
|
|
197
|
+
return True
|
|
198
|
+
|
|
199
|
+
except Exception as e:
|
|
200
|
+
log_with_timestamp(f"❌ Execution failed: {e}", "ERROR")
|
|
201
|
+
return False
|
|
202
|
+
|
|
203
|
+
def main():
|
|
204
|
+
if len(sys.argv) < 2:
|
|
205
|
+
print("E2B Sandbox Monitor")
|
|
206
|
+
print("Usage: python e2b-monitor.py <prompt> [components] [e2b_key] [anthropic_key]")
|
|
207
|
+
print()
|
|
208
|
+
print("This tool provides enhanced monitoring and debugging for E2B sandbox operations.")
|
|
209
|
+
print("Use this when you have valid API keys and want detailed insight into sandbox execution.")
|
|
210
|
+
sys.exit(1)
|
|
211
|
+
|
|
212
|
+
prompt = sys.argv[1]
|
|
213
|
+
components = sys.argv[2] if len(sys.argv) > 2 else ""
|
|
214
|
+
e2b_key = sys.argv[3] if len(sys.argv) > 3 else os.getenv('E2B_API_KEY')
|
|
215
|
+
anthropic_key = sys.argv[4] if len(sys.argv) > 4 else os.getenv('ANTHROPIC_API_KEY')
|
|
216
|
+
|
|
217
|
+
log_with_timestamp("🎬 E2B Sandbox Monitor Starting")
|
|
218
|
+
log_with_timestamp("=" * 60)
|
|
219
|
+
|
|
220
|
+
success = enhanced_sandbox_execution(prompt, components, e2b_key, anthropic_key)
|
|
221
|
+
|
|
222
|
+
if success:
|
|
223
|
+
log_with_timestamp("🎉 Monitoring session completed successfully")
|
|
224
|
+
else:
|
|
225
|
+
log_with_timestamp("❌ Monitoring session failed")
|
|
226
|
+
sys.exit(1)
|
|
227
|
+
|
|
228
|
+
if __name__ == "__main__":
|
|
229
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
e2b>=2.0.2
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-templates",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.7",
|
|
4
4
|
"description": "CLI tool to setup Claude Code configurations with framework-specific commands, automation hooks and MCP Servers for your projects",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
"files": [
|
|
87
87
|
"bin/",
|
|
88
88
|
"src/",
|
|
89
|
+
"components/sandbox/e2b/",
|
|
89
90
|
"README.md"
|
|
90
91
|
],
|
|
91
92
|
"devDependencies": {
|
package/src/index.js
CHANGED
|
@@ -2392,32 +2392,67 @@ async function executeSandbox(options, targetDir) {
|
|
|
2392
2392
|
const sandboxDir = path.join(targetDir, '.claude', 'sandbox');
|
|
2393
2393
|
await fs.ensureDir(sandboxDir);
|
|
2394
2394
|
|
|
2395
|
-
//
|
|
2396
|
-
const
|
|
2395
|
+
// Copy E2B component files from the installed package
|
|
2396
|
+
const componentsDir = path.join(__dirname, '..', 'components', 'sandbox', 'e2b');
|
|
2397
2397
|
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2398
|
+
try {
|
|
2399
|
+
// Check if files exist locally (from npm package)
|
|
2400
|
+
if (await fs.pathExists(componentsDir)) {
|
|
2401
|
+
// Copy files from local package
|
|
2402
|
+
console.log(chalk.gray('📦 Using local E2B component files...'));
|
|
2403
|
+
|
|
2404
|
+
const launcherPath = path.join(componentsDir, 'e2b-launcher.py');
|
|
2405
|
+
const requirementsPath = path.join(componentsDir, 'requirements.txt');
|
|
2406
|
+
const envExamplePath = path.join(componentsDir, '.env.example');
|
|
2407
|
+
|
|
2408
|
+
if (await fs.pathExists(launcherPath)) {
|
|
2409
|
+
await fs.copyFile(launcherPath, path.join(sandboxDir, 'e2b-launcher.py'));
|
|
2410
|
+
await fs.chmod(path.join(sandboxDir, 'e2b-launcher.py'), 0o755);
|
|
2411
|
+
} else {
|
|
2412
|
+
throw new Error('e2b-launcher.py not found in package');
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
if (await fs.pathExists(requirementsPath)) {
|
|
2416
|
+
await fs.copyFile(requirementsPath, path.join(sandboxDir, 'requirements.txt'));
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
if (await fs.pathExists(envExamplePath)) {
|
|
2420
|
+
await fs.copyFile(envExamplePath, path.join(sandboxDir, '.env.example'));
|
|
2421
|
+
}
|
|
2422
|
+
} else {
|
|
2423
|
+
// Fallback to downloading from GitHub if not found locally
|
|
2424
|
+
console.log(chalk.gray('📥 Downloading E2B component files from GitHub...'));
|
|
2425
|
+
|
|
2426
|
+
const baseUrl = 'https://raw.githubusercontent.com/davila7/claude-code-templates/main/cli-tool/components/sandbox/e2b';
|
|
2427
|
+
|
|
2428
|
+
// Download launcher script
|
|
2429
|
+
const launcherResponse = await fetch(`${baseUrl}/e2b-launcher.py`);
|
|
2430
|
+
if (!launcherResponse.ok) {
|
|
2431
|
+
throw new Error(`Failed to download e2b-launcher.py: ${launcherResponse.status} ${launcherResponse.statusText}`);
|
|
2432
|
+
}
|
|
2433
|
+
const launcherContent = await launcherResponse.text();
|
|
2434
|
+
await fs.writeFile(path.join(sandboxDir, 'e2b-launcher.py'), launcherContent, { mode: 0o755 });
|
|
2435
|
+
|
|
2436
|
+
// Download requirements.txt
|
|
2437
|
+
const requirementsResponse = await fetch(`${baseUrl}/requirements.txt`);
|
|
2438
|
+
if (!requirementsResponse.ok) {
|
|
2439
|
+
throw new Error(`Failed to download requirements.txt: ${requirementsResponse.status} ${requirementsResponse.statusText}`);
|
|
2440
|
+
}
|
|
2441
|
+
const requirementsContent = await requirementsResponse.text();
|
|
2442
|
+
await fs.writeFile(path.join(sandboxDir, 'requirements.txt'), requirementsContent);
|
|
2443
|
+
|
|
2444
|
+
// Download .env.example
|
|
2445
|
+
const envExampleResponse = await fetch(`${baseUrl}/.env.example`);
|
|
2446
|
+
if (!envExampleResponse.ok) {
|
|
2447
|
+
throw new Error(`Failed to download .env.example: ${envExampleResponse.status} ${envExampleResponse.statusText}`);
|
|
2448
|
+
}
|
|
2449
|
+
const envExampleContent = await envExampleResponse.text();
|
|
2450
|
+
await fs.writeFile(path.join(sandboxDir, '.env.example'), envExampleContent);
|
|
2451
|
+
}
|
|
2452
|
+
} catch (error) {
|
|
2453
|
+
spinner.fail(`Failed to install E2B component: ${error.message}`);
|
|
2454
|
+
throw error;
|
|
2418
2455
|
}
|
|
2419
|
-
const envExampleContent = await envExampleResponse.text();
|
|
2420
|
-
await fs.writeFile(path.join(sandboxDir, '.env.example'), envExampleContent);
|
|
2421
2456
|
|
|
2422
2457
|
spinner.succeed('E2B sandbox component installed successfully');
|
|
2423
2458
|
|