claude-code-templates 1.20.3 ā 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-claude-config.js +5 -1
- package/package.json +1 -1
- package/src/analytics-web/components/ActivityHeatmap.js +50 -6
- package/src/analytics-web/components/DashboardPage.js +4 -0
- package/src/analytics-web/services/DataService.js +7 -0
- package/src/analytics.js +67 -20
- package/src/index.js +443 -6
- package/src/sandbox-server.js +555 -0
- package/src/test-activity-data.json +1094 -0
package/src/index.js
CHANGED
|
@@ -115,6 +115,18 @@ async function createClaudeConfig(options = {}) {
|
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
// Handle Claude Code Studio launch
|
|
119
|
+
if (options.studio) {
|
|
120
|
+
await launchClaudeCodeStudio(options, targetDir);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Handle sandbox execution FIRST (before individual components)
|
|
125
|
+
if (options.sandbox) {
|
|
126
|
+
await executeSandbox(options, targetDir);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
118
130
|
// Handle multiple components installation (new approach)
|
|
119
131
|
if (options.agent || options.command || options.mcp || options.setting || options.hook) {
|
|
120
132
|
// If --workflow is used with components, treat it as YAML
|
|
@@ -155,6 +167,8 @@ async function createClaudeConfig(options = {}) {
|
|
|
155
167
|
return;
|
|
156
168
|
}
|
|
157
169
|
|
|
170
|
+
// (Sandbox execution handled earlier)
|
|
171
|
+
|
|
158
172
|
// Handle command stats analysis (both singular and plural)
|
|
159
173
|
if (options.commandStats || options.commandsStats) {
|
|
160
174
|
await runCommandStats(options);
|
|
@@ -362,8 +376,8 @@ async function createClaudeConfig(options = {}) {
|
|
|
362
376
|
await runPostInstallationValidation(targetDir, templateConfig);
|
|
363
377
|
}
|
|
364
378
|
|
|
365
|
-
// Handle prompt execution if provided
|
|
366
|
-
if (options.prompt) {
|
|
379
|
+
// Handle prompt execution if provided (but not in sandbox mode)
|
|
380
|
+
if (options.prompt && !options.sandbox) {
|
|
367
381
|
await handlePromptExecution(options.prompt, targetDir);
|
|
368
382
|
}
|
|
369
383
|
}
|
|
@@ -1444,8 +1458,8 @@ async function installMultipleComponents(options, targetDir) {
|
|
|
1444
1458
|
|
|
1445
1459
|
// Note: Individual components are already tracked separately in their installation functions
|
|
1446
1460
|
|
|
1447
|
-
// Handle prompt execution if provided
|
|
1448
|
-
if (options.prompt) {
|
|
1461
|
+
// Handle prompt execution if provided (but not in sandbox mode)
|
|
1462
|
+
if (options.prompt && !options.sandbox) {
|
|
1449
1463
|
await handlePromptExecution(options.prompt, targetDir);
|
|
1450
1464
|
}
|
|
1451
1465
|
|
|
@@ -1613,8 +1627,8 @@ async function installWorkflow(workflowHash, targetDir, options) {
|
|
|
1613
1627
|
target_directory: path.relative(process.cwd(), targetDir)
|
|
1614
1628
|
});
|
|
1615
1629
|
|
|
1616
|
-
// Handle prompt execution if provided
|
|
1617
|
-
if (options.prompt) {
|
|
1630
|
+
// Handle prompt execution if provided (but not in sandbox mode)
|
|
1631
|
+
if (options.prompt && !options.sandbox) {
|
|
1618
1632
|
await handlePromptExecution(options.prompt, targetDir);
|
|
1619
1633
|
}
|
|
1620
1634
|
|
|
@@ -2077,4 +2091,427 @@ async function handlePromptExecution(prompt, targetDir) {
|
|
|
2077
2091
|
}
|
|
2078
2092
|
}
|
|
2079
2093
|
|
|
2094
|
+
async function launchClaudeCodeStudio(options, targetDir) {
|
|
2095
|
+
console.log(chalk.blue('\nšØ Claude Code Studio'));
|
|
2096
|
+
console.log(chalk.cyan('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
2097
|
+
console.log(chalk.white('š Starting Claude Code Studio interface...'));
|
|
2098
|
+
console.log(chalk.gray('š” This interface supports both local and cloud execution'));
|
|
2099
|
+
|
|
2100
|
+
const { spawn } = require('child_process');
|
|
2101
|
+
const open = require('open');
|
|
2102
|
+
const path = require('path');
|
|
2103
|
+
|
|
2104
|
+
// Start the studio server
|
|
2105
|
+
const serverPath = path.join(__dirname, 'sandbox-server.js');
|
|
2106
|
+
const serverProcess = spawn('node', [serverPath], {
|
|
2107
|
+
stdio: 'inherit'
|
|
2108
|
+
});
|
|
2109
|
+
|
|
2110
|
+
// Wait a moment for server to start, then open browser
|
|
2111
|
+
setTimeout(async () => {
|
|
2112
|
+
try {
|
|
2113
|
+
await open('http://localhost:3444');
|
|
2114
|
+
console.log(chalk.green('ā
Claude Code Studio launched at http://localhost:3444'));
|
|
2115
|
+
console.log(chalk.gray('š” Choose between Local Machine or E2B Cloud execution'));
|
|
2116
|
+
} catch (error) {
|
|
2117
|
+
console.log(chalk.yellow('š” Please manually open: http://localhost:3444'));
|
|
2118
|
+
}
|
|
2119
|
+
}, 2000);
|
|
2120
|
+
|
|
2121
|
+
// Handle process cleanup
|
|
2122
|
+
process.on('SIGINT', () => {
|
|
2123
|
+
console.log(chalk.yellow('\nš Shutting down Claude Code Studio...'));
|
|
2124
|
+
serverProcess.kill();
|
|
2125
|
+
process.exit(0);
|
|
2126
|
+
});
|
|
2127
|
+
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
async function executeSandbox(options, targetDir) {
|
|
2132
|
+
const { sandbox, command, mcp, setting, hook, e2bApiKey, anthropicApiKey } = options;
|
|
2133
|
+
let { agent, prompt } = options;
|
|
2134
|
+
|
|
2135
|
+
// Validate sandbox provider
|
|
2136
|
+
if (sandbox !== 'e2b') {
|
|
2137
|
+
console.log(chalk.red('ā Error: Only E2B sandbox is currently supported'));
|
|
2138
|
+
console.log(chalk.yellow('š” Available providers: e2b'));
|
|
2139
|
+
console.log(chalk.gray(' Example: --sandbox e2b --prompt "Create a web app"'));
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
// Interactive agent selection if not provided
|
|
2144
|
+
if (!agent) {
|
|
2145
|
+
const inquirer = require('inquirer');
|
|
2146
|
+
|
|
2147
|
+
console.log(chalk.blue('\nš¤ Agent Selection'));
|
|
2148
|
+
console.log(chalk.cyan('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
2149
|
+
console.log(chalk.gray('Select an agent for your task, or continue without one.\n'));
|
|
2150
|
+
|
|
2151
|
+
// Fetch available agents
|
|
2152
|
+
console.log(chalk.gray('ā³ Fetching available agents...'));
|
|
2153
|
+
const agents = await getAvailableAgentsFromGitHub();
|
|
2154
|
+
|
|
2155
|
+
// Format agents for selection
|
|
2156
|
+
const agentChoices = agents.map(a => ({
|
|
2157
|
+
name: `${a.path} ${chalk.gray(`- ${a.category}`)}`,
|
|
2158
|
+
value: a.path,
|
|
2159
|
+
short: a.path
|
|
2160
|
+
}));
|
|
2161
|
+
|
|
2162
|
+
// Add option to continue without agent
|
|
2163
|
+
agentChoices.unshift({
|
|
2164
|
+
name: chalk.yellow('ā” Continue without agent (use default Claude)'),
|
|
2165
|
+
value: null,
|
|
2166
|
+
short: 'No agent'
|
|
2167
|
+
});
|
|
2168
|
+
|
|
2169
|
+
const { selectedAgent } = await inquirer.prompt([{
|
|
2170
|
+
type: 'list',
|
|
2171
|
+
name: 'selectedAgent',
|
|
2172
|
+
message: 'Select an agent for your task:',
|
|
2173
|
+
choices: agentChoices,
|
|
2174
|
+
pageSize: 15
|
|
2175
|
+
}]);
|
|
2176
|
+
|
|
2177
|
+
if (selectedAgent) {
|
|
2178
|
+
agent = selectedAgent;
|
|
2179
|
+
console.log(chalk.green(`ā
Selected agent: ${chalk.cyan(agent)}`));
|
|
2180
|
+
} else {
|
|
2181
|
+
console.log(chalk.yellow('ā ļø Continuing without specific agent'));
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
// Get prompt from user if not provided
|
|
2186
|
+
if (!prompt) {
|
|
2187
|
+
console.log(chalk.blue('\nš Project Requirements'));
|
|
2188
|
+
console.log(chalk.cyan('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
2189
|
+
console.log(chalk.gray('Describe what you want to create in detail. The more specific you are,'));
|
|
2190
|
+
console.log(chalk.gray('the better Claude Code will understand your requirements.\n'));
|
|
2191
|
+
|
|
2192
|
+
const inquirer = require('inquirer');
|
|
2193
|
+
|
|
2194
|
+
const { userPrompt } = await inquirer.prompt([{
|
|
2195
|
+
type: 'input',
|
|
2196
|
+
name: 'userPrompt',
|
|
2197
|
+
message: 'What would you like to create?',
|
|
2198
|
+
validate: (input) => {
|
|
2199
|
+
if (!input || input.trim().length < 10) {
|
|
2200
|
+
return 'Please provide a more detailed description (at least 10 characters)';
|
|
2201
|
+
}
|
|
2202
|
+
return true;
|
|
2203
|
+
}
|
|
2204
|
+
}]);
|
|
2205
|
+
|
|
2206
|
+
prompt = userPrompt.trim();
|
|
2207
|
+
console.log(chalk.green('ā
Project requirements captured!'));
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
// Load .env file if it exists (for API keys)
|
|
2211
|
+
try {
|
|
2212
|
+
const fs = require('fs');
|
|
2213
|
+
const path = require('path');
|
|
2214
|
+
const envPath = path.join(targetDir, '.env');
|
|
2215
|
+
|
|
2216
|
+
if (fs.existsSync(envPath)) {
|
|
2217
|
+
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
2218
|
+
const envVars = envContent.split('\n')
|
|
2219
|
+
.filter(line => line.trim() && !line.startsWith('#'))
|
|
2220
|
+
.reduce((acc, line) => {
|
|
2221
|
+
const [key, ...valueParts] = line.split('=');
|
|
2222
|
+
if (key && valueParts.length > 0) {
|
|
2223
|
+
const value = valueParts.join('=').trim();
|
|
2224
|
+
acc[key.trim()] = value;
|
|
2225
|
+
}
|
|
2226
|
+
return acc;
|
|
2227
|
+
}, {});
|
|
2228
|
+
|
|
2229
|
+
// Set environment variables if not already set
|
|
2230
|
+
Object.keys(envVars).forEach(key => {
|
|
2231
|
+
if (!process.env[key]) {
|
|
2232
|
+
process.env[key] = envVars[key];
|
|
2233
|
+
}
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
} catch (error) {
|
|
2237
|
+
// Ignore .env loading errors
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
// Check for API keys (either from CLI parameters or environment variables)
|
|
2241
|
+
const e2bKey = e2bApiKey || process.env.E2B_API_KEY;
|
|
2242
|
+
const anthropicKey = anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
|
2243
|
+
|
|
2244
|
+
if (!e2bKey) {
|
|
2245
|
+
console.log(chalk.red('ā Error: E2B API key is required'));
|
|
2246
|
+
console.log(chalk.yellow('š” Options:'));
|
|
2247
|
+
console.log(chalk.gray(' 1. Set environment variable: E2B_API_KEY=your_key'));
|
|
2248
|
+
console.log(chalk.gray(' 2. Use CLI parameter: --e2b-api-key your_key'));
|
|
2249
|
+
console.log(chalk.blue(' Get your key at: https://e2b.dev/dashboard'));
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
if (!anthropicKey) {
|
|
2254
|
+
console.log(chalk.red('ā Error: Anthropic API key is required'));
|
|
2255
|
+
console.log(chalk.yellow('š” Options:'));
|
|
2256
|
+
console.log(chalk.gray(' 1. Set environment variable: ANTHROPIC_API_KEY=your_key'));
|
|
2257
|
+
console.log(chalk.gray(' 2. Use CLI parameter: --anthropic-api-key your_key'));
|
|
2258
|
+
console.log(chalk.blue(' Get your key at: https://console.anthropic.com'));
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// Sandbox execution confirmation
|
|
2263
|
+
console.log(chalk.blue('\nāļø E2B Sandbox Execution'));
|
|
2264
|
+
console.log(chalk.cyan('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
2265
|
+
console.log(chalk.white(`š Agent: ${chalk.yellow(agent || 'default')}`));
|
|
2266
|
+
const truncatedPrompt = prompt.length > 80 ? prompt.substring(0, 80) + '...' : prompt;
|
|
2267
|
+
console.log(chalk.white(`š Prompt: ${chalk.cyan('"' + truncatedPrompt + '"')}`));
|
|
2268
|
+
console.log(chalk.white(`š Provider: ${chalk.green('E2B Cloud')}`));
|
|
2269
|
+
console.log(chalk.gray('\nš§ Execution details:'));
|
|
2270
|
+
console.log(chalk.gray(' ⢠Execution logs will be displayed in real-time'));
|
|
2271
|
+
console.log(chalk.gray(` ⢠Files will be downloaded to: ${chalk.cyan(targetDir)}`));
|
|
2272
|
+
console.log(chalk.gray(' ⢠Extended timeout: 15 minutes for complex operations'));
|
|
2273
|
+
console.log(chalk.yellow(' ⢠Press ESC anytime to cancel execution\n'));
|
|
2274
|
+
|
|
2275
|
+
const inquirer = require('inquirer');
|
|
2276
|
+
|
|
2277
|
+
const { shouldExecuteSandbox } = await inquirer.prompt([{
|
|
2278
|
+
type: 'confirm',
|
|
2279
|
+
name: 'shouldExecuteSandbox',
|
|
2280
|
+
message: `Execute this agent in E2B sandbox?`,
|
|
2281
|
+
default: true
|
|
2282
|
+
}]);
|
|
2283
|
+
|
|
2284
|
+
if (!shouldExecuteSandbox) {
|
|
2285
|
+
console.log(chalk.yellow('ā¹ļø E2B sandbox execution cancelled by user.'));
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
try {
|
|
2290
|
+
console.log(chalk.blue('š® Setting up E2B sandbox environment...'));
|
|
2291
|
+
|
|
2292
|
+
// Install E2B sandbox component
|
|
2293
|
+
const spinner = ora('Installing E2B sandbox component...').start();
|
|
2294
|
+
|
|
2295
|
+
// Create .claude/sandbox directory
|
|
2296
|
+
const sandboxDir = path.join(targetDir, '.claude', 'sandbox');
|
|
2297
|
+
await fs.ensureDir(sandboxDir);
|
|
2298
|
+
|
|
2299
|
+
// Download E2B component files from new structure
|
|
2300
|
+
const baseUrl = 'https://raw.githubusercontent.com/davila7/claude-code-templates/main/cli-tool/components/sandbox/e2b';
|
|
2301
|
+
|
|
2302
|
+
// Download launcher script
|
|
2303
|
+
const launcherResponse = await fetch(`${baseUrl}/e2b-launcher.py`);
|
|
2304
|
+
if (!launcherResponse.ok) {
|
|
2305
|
+
throw new Error(`Failed to download e2b-launcher.py: ${launcherResponse.status} ${launcherResponse.statusText}`);
|
|
2306
|
+
}
|
|
2307
|
+
const launcherContent = await launcherResponse.text();
|
|
2308
|
+
await fs.writeFile(path.join(sandboxDir, 'e2b-launcher.py'), launcherContent, { mode: 0o755 });
|
|
2309
|
+
|
|
2310
|
+
// Download requirements.txt
|
|
2311
|
+
const requirementsResponse = await fetch(`${baseUrl}/requirements.txt`);
|
|
2312
|
+
if (!requirementsResponse.ok) {
|
|
2313
|
+
throw new Error(`Failed to download requirements.txt: ${requirementsResponse.status} ${requirementsResponse.statusText}`);
|
|
2314
|
+
}
|
|
2315
|
+
const requirementsContent = await requirementsResponse.text();
|
|
2316
|
+
await fs.writeFile(path.join(sandboxDir, 'requirements.txt'), requirementsContent);
|
|
2317
|
+
|
|
2318
|
+
// Download .env.example
|
|
2319
|
+
const envExampleResponse = await fetch(`${baseUrl}/.env.example`);
|
|
2320
|
+
if (!envExampleResponse.ok) {
|
|
2321
|
+
throw new Error(`Failed to download .env.example: ${envExampleResponse.status} ${envExampleResponse.statusText}`);
|
|
2322
|
+
}
|
|
2323
|
+
const envExampleContent = await envExampleResponse.text();
|
|
2324
|
+
await fs.writeFile(path.join(sandboxDir, '.env.example'), envExampleContent);
|
|
2325
|
+
|
|
2326
|
+
spinner.succeed('E2B sandbox component installed successfully');
|
|
2327
|
+
|
|
2328
|
+
// Check for Python and install dependencies
|
|
2329
|
+
const pythonSpinner = ora('Checking Python environment...').start();
|
|
2330
|
+
|
|
2331
|
+
try {
|
|
2332
|
+
const { spawn } = require('child_process');
|
|
2333
|
+
|
|
2334
|
+
// Helper function to check Python version availability
|
|
2335
|
+
const checkPythonVersion = (pythonCmd) => {
|
|
2336
|
+
return new Promise((resolve) => {
|
|
2337
|
+
const check = spawn(pythonCmd, ['--version'], { stdio: 'pipe' });
|
|
2338
|
+
check.on('close', (code) => resolve(code === 0));
|
|
2339
|
+
check.on('error', () => resolve(false));
|
|
2340
|
+
});
|
|
2341
|
+
};
|
|
2342
|
+
|
|
2343
|
+
// Check for Python 3.11 first, fallback to python3
|
|
2344
|
+
let pythonCmd = 'python3';
|
|
2345
|
+
const python311Available = await checkPythonVersion('python3.11');
|
|
2346
|
+
if (python311Available) {
|
|
2347
|
+
pythonCmd = 'python3.11';
|
|
2348
|
+
console.log(chalk.blue('ā Using Python 3.11 (recommended for E2B)'));
|
|
2349
|
+
} else {
|
|
2350
|
+
console.log(chalk.yellow('ā Python 3.11 not found, using python3 (may have package restrictions)'));
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
// Verify chosen Python version works
|
|
2354
|
+
const pythonAvailable = await checkPythonVersion(pythonCmd);
|
|
2355
|
+
if (!pythonAvailable) {
|
|
2356
|
+
pythonSpinner.fail('Python 3 not found');
|
|
2357
|
+
console.log(chalk.red('ā Python 3.11+ is required for E2B sandbox'));
|
|
2358
|
+
console.log(chalk.yellow('š” Please install Python 3.11+ and try again'));
|
|
2359
|
+
console.log(chalk.blue(' Visit: https://python.org/downloads'));
|
|
2360
|
+
return;
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
pythonSpinner.succeed(`Python environment ready (${pythonCmd})`);
|
|
2364
|
+
|
|
2365
|
+
// Install Python dependencies
|
|
2366
|
+
const depSpinner = ora('Installing E2B Python SDK...').start();
|
|
2367
|
+
|
|
2368
|
+
const pipInstall = spawn(pythonCmd, ['-m', 'pip', 'install', '-r', path.join(sandboxDir, 'requirements.txt')], {
|
|
2369
|
+
cwd: sandboxDir,
|
|
2370
|
+
stdio: 'pipe'
|
|
2371
|
+
});
|
|
2372
|
+
|
|
2373
|
+
let pipOutput = '';
|
|
2374
|
+
let pipError = '';
|
|
2375
|
+
|
|
2376
|
+
pipInstall.stdout.on('data', (data) => {
|
|
2377
|
+
pipOutput += data.toString();
|
|
2378
|
+
});
|
|
2379
|
+
|
|
2380
|
+
pipInstall.stderr.on('data', (data) => {
|
|
2381
|
+
pipError += data.toString();
|
|
2382
|
+
});
|
|
2383
|
+
|
|
2384
|
+
pipInstall.on('close', async (pipCode) => {
|
|
2385
|
+
if (pipCode === 0) {
|
|
2386
|
+
depSpinner.succeed('E2B Python SDK installed successfully');
|
|
2387
|
+
|
|
2388
|
+
// Build components string for installation inside sandbox
|
|
2389
|
+
let componentsToInstall = '';
|
|
2390
|
+
if (agent) componentsToInstall += ` --agent ${agent}`;
|
|
2391
|
+
if (command) componentsToInstall += ` --command ${command}`;
|
|
2392
|
+
if (mcp) componentsToInstall += ` --mcp ${mcp}`;
|
|
2393
|
+
if (setting) componentsToInstall += ` --setting ${setting}`;
|
|
2394
|
+
if (hook) componentsToInstall += ` --hook ${hook}`;
|
|
2395
|
+
|
|
2396
|
+
// Execute sandbox
|
|
2397
|
+
console.log(chalk.blue('š Launching E2B sandbox with Claude Code...'));
|
|
2398
|
+
console.log(chalk.gray(`š Prompt: "${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}"`));
|
|
2399
|
+
console.log(chalk.cyan('ā±ļø Extended timeout: 15 minutes for complex operations'));
|
|
2400
|
+
|
|
2401
|
+
if (componentsToInstall) {
|
|
2402
|
+
console.log(chalk.gray(`š¦ Components to install:${componentsToInstall}`));
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
// Execute sandbox and wait for completion
|
|
2406
|
+
console.log(chalk.blue('š Starting E2B sandbox execution...'));
|
|
2407
|
+
console.log(chalk.yellow('š” Press ESC anytime to cancel the execution'));
|
|
2408
|
+
|
|
2409
|
+
await new Promise((resolve, reject) => {
|
|
2410
|
+
const sandboxExecution = spawn(pythonCmd, [
|
|
2411
|
+
path.join(sandboxDir, 'e2b-launcher.py'),
|
|
2412
|
+
prompt,
|
|
2413
|
+
componentsToInstall.trim(),
|
|
2414
|
+
e2bKey,
|
|
2415
|
+
anthropicKey
|
|
2416
|
+
], {
|
|
2417
|
+
cwd: targetDir, // Run from user's current directory to download files there
|
|
2418
|
+
stdio: 'inherit',
|
|
2419
|
+
timeout: 900000, // 15 minutes timeout for complex operations
|
|
2420
|
+
env: {
|
|
2421
|
+
...process.env,
|
|
2422
|
+
E2B_API_KEY: e2bKey,
|
|
2423
|
+
ANTHROPIC_API_KEY: anthropicKey
|
|
2424
|
+
}
|
|
2425
|
+
});
|
|
2426
|
+
|
|
2427
|
+
// Setup ESC key listener for cancellation
|
|
2428
|
+
process.stdin.setRawMode(true);
|
|
2429
|
+
process.stdin.resume();
|
|
2430
|
+
process.stdin.setEncoding('utf8');
|
|
2431
|
+
|
|
2432
|
+
const keyListener = (key) => {
|
|
2433
|
+
// ESC key (ASCII 27)
|
|
2434
|
+
if (key === '\u001b') {
|
|
2435
|
+
console.log(chalk.yellow('\nā¹ļø Cancelling E2B sandbox execution...'));
|
|
2436
|
+
sandboxExecution.kill('SIGTERM');
|
|
2437
|
+
|
|
2438
|
+
// Cleanup
|
|
2439
|
+
process.stdin.setRawMode(false);
|
|
2440
|
+
process.stdin.pause();
|
|
2441
|
+
process.stdin.removeListener('data', keyListener);
|
|
2442
|
+
|
|
2443
|
+
resolve(); // Resolve to prevent hanging
|
|
2444
|
+
}
|
|
2445
|
+
};
|
|
2446
|
+
|
|
2447
|
+
process.stdin.on('data', keyListener);
|
|
2448
|
+
|
|
2449
|
+
sandboxExecution.on('close', (sandboxCode) => {
|
|
2450
|
+
// Cleanup stdin listener
|
|
2451
|
+
process.stdin.setRawMode(false);
|
|
2452
|
+
process.stdin.pause();
|
|
2453
|
+
process.stdin.removeListener('data', keyListener);
|
|
2454
|
+
|
|
2455
|
+
if (sandboxCode === 0) {
|
|
2456
|
+
console.log(chalk.green('š Sandbox execution completed successfully!'));
|
|
2457
|
+
console.log(chalk.blue('š” Files were created inside the E2B sandbox environment'));
|
|
2458
|
+
resolve();
|
|
2459
|
+
} else if (sandboxCode === null) {
|
|
2460
|
+
console.log(chalk.yellow('ā¹ļø Sandbox execution was cancelled'));
|
|
2461
|
+
resolve();
|
|
2462
|
+
} else {
|
|
2463
|
+
console.log(chalk.yellow(`ā ļø Sandbox execution finished with exit code ${sandboxCode}`));
|
|
2464
|
+
console.log(chalk.gray('š” Check the output above for any error details'));
|
|
2465
|
+
resolve(); // Still resolve even with non-zero exit code
|
|
2466
|
+
}
|
|
2467
|
+
});
|
|
2468
|
+
|
|
2469
|
+
sandboxExecution.on('error', (error) => {
|
|
2470
|
+
// Cleanup stdin listener
|
|
2471
|
+
process.stdin.setRawMode(false);
|
|
2472
|
+
process.stdin.pause();
|
|
2473
|
+
process.stdin.removeListener('data', keyListener);
|
|
2474
|
+
|
|
2475
|
+
if (error.code === 'TIMEOUT') {
|
|
2476
|
+
console.log(chalk.yellow('ā±ļø Sandbox execution timed out after 15 minutes'));
|
|
2477
|
+
console.log(chalk.gray('š” This may happen with very complex prompts or large projects'));
|
|
2478
|
+
console.log(chalk.blue('š” Try breaking down your prompt into smaller, more specific requests'));
|
|
2479
|
+
} else {
|
|
2480
|
+
console.log(chalk.red(`ā Error executing sandbox: ${error.message}`));
|
|
2481
|
+
console.log(chalk.yellow('š” Make sure you have set E2B_API_KEY and ANTHROPIC_API_KEY environment variables'));
|
|
2482
|
+
console.log(chalk.gray(' Create a .env file in the .claude/sandbox directory with your API keys'));
|
|
2483
|
+
}
|
|
2484
|
+
reject(error);
|
|
2485
|
+
});
|
|
2486
|
+
});
|
|
2487
|
+
|
|
2488
|
+
} else {
|
|
2489
|
+
depSpinner.fail('Failed to install E2B Python SDK');
|
|
2490
|
+
console.log(chalk.red(`ā pip install failed with exit code ${pipCode}`));
|
|
2491
|
+
if (pipError) {
|
|
2492
|
+
console.log(chalk.red('Error output:'));
|
|
2493
|
+
console.log(chalk.gray(pipError.trim()));
|
|
2494
|
+
}
|
|
2495
|
+
if (pipOutput) {
|
|
2496
|
+
console.log(chalk.blue('Full output:'));
|
|
2497
|
+
console.log(chalk.gray(pipOutput.trim()));
|
|
2498
|
+
}
|
|
2499
|
+
console.log(chalk.yellow('š” Please install dependencies manually:'));
|
|
2500
|
+
console.log(chalk.gray(` cd ${sandboxDir}`));
|
|
2501
|
+
console.log(chalk.gray(` ${pythonCmd} -m pip install -r requirements.txt`));
|
|
2502
|
+
console.log(chalk.gray(` ${pythonCmd} -m pip install e2b`));
|
|
2503
|
+
}
|
|
2504
|
+
});
|
|
2505
|
+
|
|
2506
|
+
} catch (error) {
|
|
2507
|
+
pythonSpinner.fail('Failed to check Python environment');
|
|
2508
|
+
console.log(chalk.red(`ā Error: ${error.message}`));
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
} catch (error) {
|
|
2512
|
+
console.log(chalk.red(`ā Error setting up sandbox: ${error.message}`));
|
|
2513
|
+
console.log(chalk.yellow('š” Please check your internet connection and try again'));
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2080
2517
|
module.exports = { createClaudeConfig, showMainMenu };
|