claude-code-templates 1.26.4 ā 1.28.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 +1 -0
- package/components/sandbox/docker/Dockerfile +38 -0
- package/components/sandbox/docker/README.md +453 -0
- package/components/sandbox/docker/docker-launcher.js +184 -0
- package/components/sandbox/docker/execute.js +251 -0
- package/components/sandbox/docker/package.json +26 -0
- package/package.json +2 -1
- package/src/index.js +294 -24
- package/src/skill-dashboard-web/index.html +326 -0
- package/src/skill-dashboard-web/script.js +445 -0
- package/src/skill-dashboard-web/styles.css +3469 -0
- package/src/skill-dashboard.js +441 -0
package/src/index.js
CHANGED
|
@@ -16,11 +16,48 @@ const { runAnalytics } = require('./analytics');
|
|
|
16
16
|
const { startChatsMobile } = require('./chats-mobile');
|
|
17
17
|
const { runHealthCheck } = require('./health-check');
|
|
18
18
|
const { runPluginDashboard } = require('./plugin-dashboard');
|
|
19
|
+
const { runSkillDashboard } = require('./skill-dashboard');
|
|
19
20
|
const { trackingService } = require('./tracking-service');
|
|
20
21
|
const { createGlobalAgent, listGlobalAgents, removeGlobalAgent, updateGlobalAgent } = require('./sdk/global-agent-manager');
|
|
21
22
|
const SessionSharing = require('./session-sharing');
|
|
22
23
|
const ConversationAnalyzer = require('./analytics/core/ConversationAnalyzer');
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Get platform-appropriate Python command candidates
|
|
27
|
+
* Returns array of commands to try in order
|
|
28
|
+
* @returns {string[]} Array of Python commands to try
|
|
29
|
+
*/
|
|
30
|
+
function getPlatformPythonCandidates() {
|
|
31
|
+
if (process.platform === 'win32') {
|
|
32
|
+
// Windows: Try py launcher (PEP 397) first, then python, then python3
|
|
33
|
+
return ['py', 'python', 'python3'];
|
|
34
|
+
} else {
|
|
35
|
+
// Unix/Linux/Mac: Try python3 first, then python
|
|
36
|
+
return ['python3', 'python'];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Replace python3 commands with platform-appropriate Python command in configuration
|
|
42
|
+
* Windows typically uses 'python' or 'py', while Unix/Linux uses 'python3'
|
|
43
|
+
* @param {Object} config - Configuration object to process
|
|
44
|
+
* @returns {Object} Processed configuration with platform-appropriate Python commands
|
|
45
|
+
*/
|
|
46
|
+
function replacePythonCommands(config) {
|
|
47
|
+
if (!config || typeof config !== 'object') {
|
|
48
|
+
return config;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// On Windows, replace python3 with python for better compatibility
|
|
52
|
+
if (process.platform === 'win32') {
|
|
53
|
+
const configString = JSON.stringify(config);
|
|
54
|
+
const replacedString = configString.replace(/python3\s/g, 'python ');
|
|
55
|
+
return JSON.parse(replacedString);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return config;
|
|
59
|
+
}
|
|
60
|
+
|
|
24
61
|
async function showMainMenu() {
|
|
25
62
|
console.log('');
|
|
26
63
|
|
|
@@ -213,6 +250,14 @@ async function createClaudeConfig(options = {}) {
|
|
|
213
250
|
return;
|
|
214
251
|
}
|
|
215
252
|
|
|
253
|
+
// Handle skills dashboard
|
|
254
|
+
if (options.skills) {
|
|
255
|
+
trackingService.trackCommandExecution('skills');
|
|
256
|
+
trackingService.trackAnalyticsDashboard({ page: 'skills', source: 'command_line' });
|
|
257
|
+
await runSkillDashboard(options);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
216
261
|
// Handle chats dashboard (now points to mobile chats interface)
|
|
217
262
|
if (options.chats) {
|
|
218
263
|
trackingService.trackCommandExecution('chats', { tunnel: options.tunnel || false });
|
|
@@ -692,7 +737,10 @@ async function installIndividualSetting(settingName, targetDir, options) {
|
|
|
692
737
|
}
|
|
693
738
|
|
|
694
739
|
const settingConfigText = await response.text();
|
|
695
|
-
|
|
740
|
+
let settingConfig = JSON.parse(settingConfigText);
|
|
741
|
+
|
|
742
|
+
// Replace python3 with platform-appropriate command for Windows compatibility
|
|
743
|
+
settingConfig = replacePythonCommands(settingConfig);
|
|
696
744
|
|
|
697
745
|
// Check if there are additional files to download (e.g., Python scripts)
|
|
698
746
|
const additionalFiles = {};
|
|
@@ -1021,7 +1069,10 @@ async function installIndividualHook(hookName, targetDir, options) {
|
|
|
1021
1069
|
}
|
|
1022
1070
|
|
|
1023
1071
|
const hookConfigText = await response.text();
|
|
1024
|
-
|
|
1072
|
+
let hookConfig = JSON.parse(hookConfigText);
|
|
1073
|
+
|
|
1074
|
+
// Replace python3 with platform-appropriate command for Windows compatibility
|
|
1075
|
+
hookConfig = replacePythonCommands(hookConfig);
|
|
1025
1076
|
|
|
1026
1077
|
// Check if there are additional files to download (e.g., Python scripts for hooks)
|
|
1027
1078
|
const additionalFiles = {};
|
|
@@ -2447,37 +2498,38 @@ async function launchClaudeCodeStudio(options, targetDir) {
|
|
|
2447
2498
|
}
|
|
2448
2499
|
|
|
2449
2500
|
async function executeSandbox(options, targetDir) {
|
|
2450
|
-
const { sandbox, command, mcp, setting, hook, e2bApiKey, anthropicApiKey } = options;
|
|
2501
|
+
const { sandbox, command, mcp, setting, hook, e2bApiKey, anthropicApiKey, yes } = options;
|
|
2451
2502
|
let { agent, prompt } = options;
|
|
2452
|
-
|
|
2503
|
+
|
|
2453
2504
|
// Validate sandbox provider
|
|
2454
|
-
if (sandbox !== 'e2b' && sandbox !== 'cloudflare') {
|
|
2505
|
+
if (sandbox !== 'e2b' && sandbox !== 'cloudflare' && sandbox !== 'docker') {
|
|
2455
2506
|
console.log(chalk.red('ā Error: Invalid sandbox provider'));
|
|
2456
|
-
console.log(chalk.yellow('š” Available providers: e2b, cloudflare'));
|
|
2507
|
+
console.log(chalk.yellow('š” Available providers: e2b, cloudflare, docker'));
|
|
2457
2508
|
console.log(chalk.gray(' Example: --sandbox e2b --prompt "Create a web app"'));
|
|
2458
2509
|
console.log(chalk.gray(' Example: --sandbox cloudflare --prompt "Calculate factorial of 5"'));
|
|
2510
|
+
console.log(chalk.gray(' Example: --sandbox docker --prompt "Write a function"'));
|
|
2459
2511
|
return;
|
|
2460
2512
|
}
|
|
2461
|
-
|
|
2462
|
-
// Interactive agent selection if not provided
|
|
2463
|
-
if (!agent) {
|
|
2513
|
+
|
|
2514
|
+
// Interactive agent selection if not provided and --yes not used
|
|
2515
|
+
if (!agent && !yes) {
|
|
2464
2516
|
const inquirer = require('inquirer');
|
|
2465
|
-
|
|
2517
|
+
|
|
2466
2518
|
console.log(chalk.blue('\nš¤ Agent Selection'));
|
|
2467
2519
|
console.log(chalk.cyan('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
2468
2520
|
console.log(chalk.gray('Select one or more agents for your task (use SPACE to select, ENTER to confirm).\n'));
|
|
2469
|
-
|
|
2521
|
+
|
|
2470
2522
|
// Fetch available agents
|
|
2471
2523
|
console.log(chalk.gray('ā³ Fetching available agents...'));
|
|
2472
2524
|
const agents = await getAvailableAgentsFromGitHub();
|
|
2473
|
-
|
|
2525
|
+
|
|
2474
2526
|
// Format agents for selection with full path
|
|
2475
2527
|
const agentChoices = agents.map(a => ({
|
|
2476
2528
|
name: `${a.path} ${chalk.gray(`- ${a.category}`)}`,
|
|
2477
2529
|
value: a.path, // This already includes folder/agent-name format
|
|
2478
2530
|
short: a.path
|
|
2479
2531
|
}));
|
|
2480
|
-
|
|
2532
|
+
|
|
2481
2533
|
// First ask if they want to select agents
|
|
2482
2534
|
const { wantAgents } = await inquirer.prompt([{
|
|
2483
2535
|
type: 'confirm',
|
|
@@ -2485,7 +2537,7 @@ async function executeSandbox(options, targetDir) {
|
|
|
2485
2537
|
message: 'Do you want to select specific agents for this task?',
|
|
2486
2538
|
default: true
|
|
2487
2539
|
}]);
|
|
2488
|
-
|
|
2540
|
+
|
|
2489
2541
|
if (wantAgents) {
|
|
2490
2542
|
const { selectedAgents } = await inquirer.prompt([{
|
|
2491
2543
|
type: 'checkbox',
|
|
@@ -2495,7 +2547,7 @@ async function executeSandbox(options, targetDir) {
|
|
|
2495
2547
|
pageSize: 15
|
|
2496
2548
|
// Removed validation - allow empty selection
|
|
2497
2549
|
}]);
|
|
2498
|
-
|
|
2550
|
+
|
|
2499
2551
|
if (selectedAgents && selectedAgents.length > 0) {
|
|
2500
2552
|
// Join multiple agents with comma
|
|
2501
2553
|
agent = selectedAgents.join(',');
|
|
@@ -2507,6 +2559,9 @@ async function executeSandbox(options, targetDir) {
|
|
|
2507
2559
|
} else {
|
|
2508
2560
|
console.log(chalk.yellow('ā ļø Continuing without specific agents'));
|
|
2509
2561
|
}
|
|
2562
|
+
} else if (!agent && yes) {
|
|
2563
|
+
// --yes flag used without --agent, proceed without agents
|
|
2564
|
+
console.log(chalk.yellow('ā ļø No agent specified, continuing without specific agents'));
|
|
2510
2565
|
}
|
|
2511
2566
|
|
|
2512
2567
|
// Get prompt from user if not provided
|
|
@@ -2603,6 +2658,19 @@ async function executeSandbox(options, targetDir) {
|
|
|
2603
2658
|
|
|
2604
2659
|
// Execute Cloudflare sandbox
|
|
2605
2660
|
await executeCloudflareSandbox({ sandbox, agent, prompt, command, mcp, setting, hook, anthropicKey }, targetDir);
|
|
2661
|
+
|
|
2662
|
+
} else if (sandbox === 'docker') {
|
|
2663
|
+
if (!anthropicKey) {
|
|
2664
|
+
console.log(chalk.red('ā Error: Anthropic API key is required for Docker sandbox'));
|
|
2665
|
+
console.log(chalk.yellow('š” Options:'));
|
|
2666
|
+
console.log(chalk.gray(' 1. Set environment variable: ANTHROPIC_API_KEY=your_key'));
|
|
2667
|
+
console.log(chalk.gray(' 2. Use CLI parameter: --anthropic-api-key your_key'));
|
|
2668
|
+
console.log(chalk.blue(' Get your key at: https://console.anthropic.com'));
|
|
2669
|
+
return;
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
// Execute Docker sandbox
|
|
2673
|
+
await executeDockerSandbox({ sandbox, agent, prompt, command, mcp, setting, hook, anthropicKey, yes: options.yes }, targetDir);
|
|
2606
2674
|
}
|
|
2607
2675
|
}
|
|
2608
2676
|
|
|
@@ -2817,6 +2885,199 @@ async function executeCloudflareSandbox(options, targetDir) {
|
|
|
2817
2885
|
}
|
|
2818
2886
|
}
|
|
2819
2887
|
|
|
2888
|
+
async function executeDockerSandbox(options, targetDir) {
|
|
2889
|
+
const { agent, command, mcp, setting, hook, prompt, anthropicKey, yes } = options;
|
|
2890
|
+
|
|
2891
|
+
console.log(chalk.blue('\nš³ Docker Sandbox Execution'));
|
|
2892
|
+
console.log(chalk.cyan('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
2893
|
+
|
|
2894
|
+
if (agent) {
|
|
2895
|
+
const agentList = agent.split(',');
|
|
2896
|
+
if (agentList.length > 1) {
|
|
2897
|
+
console.log(chalk.white(`š Agents (${agentList.length}):`));
|
|
2898
|
+
agentList.forEach(a => console.log(chalk.yellow(` ⢠${a.trim()}`)));
|
|
2899
|
+
} else {
|
|
2900
|
+
console.log(chalk.white(`š Agent: ${chalk.yellow(agent)}`));
|
|
2901
|
+
}
|
|
2902
|
+
} else {
|
|
2903
|
+
console.log(chalk.white(`š Agent: ${chalk.yellow('default')}`));
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
const truncatedPrompt = prompt.length > 80 ? prompt.substring(0, 80) + '...' : prompt;
|
|
2907
|
+
console.log(chalk.white(`š Prompt: ${chalk.cyan('"' + truncatedPrompt + '"')}`));
|
|
2908
|
+
console.log(chalk.white(`š³ Provider: ${chalk.green('Docker Local')}`));
|
|
2909
|
+
console.log(chalk.gray('\nš§ Execution details:'));
|
|
2910
|
+
console.log(chalk.gray(' ⢠Uses Claude Agent SDK for execution'));
|
|
2911
|
+
console.log(chalk.gray(' ⢠Executes in isolated Docker container'));
|
|
2912
|
+
console.log(chalk.gray(' ⢠Local execution with full filesystem access\n'));
|
|
2913
|
+
|
|
2914
|
+
// Skip confirmation prompt if --yes flag is used
|
|
2915
|
+
if (!yes) {
|
|
2916
|
+
const inquirer = require('inquirer');
|
|
2917
|
+
|
|
2918
|
+
const { shouldExecute } = await inquirer.prompt([{
|
|
2919
|
+
type: 'confirm',
|
|
2920
|
+
name: 'shouldExecute',
|
|
2921
|
+
message: 'Execute this prompt in Docker sandbox?',
|
|
2922
|
+
default: true
|
|
2923
|
+
}]);
|
|
2924
|
+
|
|
2925
|
+
if (!shouldExecute) {
|
|
2926
|
+
console.log(chalk.yellow('ā¹ļø Docker sandbox execution cancelled by user.'));
|
|
2927
|
+
return;
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2931
|
+
try {
|
|
2932
|
+
console.log(chalk.blue('š® Setting up Docker sandbox environment...'));
|
|
2933
|
+
|
|
2934
|
+
const spinner = ora('Installing Docker sandbox component...').start();
|
|
2935
|
+
|
|
2936
|
+
// Create .claude/sandbox/docker directory
|
|
2937
|
+
const sandboxDir = path.join(targetDir, '.claude', 'sandbox', 'docker');
|
|
2938
|
+
await fs.ensureDir(sandboxDir);
|
|
2939
|
+
|
|
2940
|
+
// Copy Docker component files
|
|
2941
|
+
const componentsDir = path.join(__dirname, '..', 'components', 'sandbox', 'docker');
|
|
2942
|
+
|
|
2943
|
+
try {
|
|
2944
|
+
if (await fs.pathExists(componentsDir)) {
|
|
2945
|
+
console.log(chalk.gray('š¦ Using local Docker component files...'));
|
|
2946
|
+
console.log(chalk.dim(` Source: ${componentsDir}`));
|
|
2947
|
+
console.log(chalk.dim(` Target: ${sandboxDir}`));
|
|
2948
|
+
|
|
2949
|
+
// Copy all files from docker directory
|
|
2950
|
+
await fs.copy(componentsDir, sandboxDir, {
|
|
2951
|
+
overwrite: true
|
|
2952
|
+
});
|
|
2953
|
+
|
|
2954
|
+
// Verify files were copied
|
|
2955
|
+
const copiedFiles = await fs.readdir(sandboxDir);
|
|
2956
|
+
console.log(chalk.dim(` Copied ${copiedFiles.length} items`));
|
|
2957
|
+
if (copiedFiles.length === 0) {
|
|
2958
|
+
throw new Error('No files were copied from Docker component directory');
|
|
2959
|
+
}
|
|
2960
|
+
} else {
|
|
2961
|
+
throw new Error(`Docker component files not found at: ${componentsDir}`);
|
|
2962
|
+
}
|
|
2963
|
+
} catch (error) {
|
|
2964
|
+
spinner.fail(`Failed to install Docker component: ${error.message}`);
|
|
2965
|
+
throw error;
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
spinner.succeed('Docker sandbox component installed successfully');
|
|
2969
|
+
|
|
2970
|
+
// Check for Docker
|
|
2971
|
+
const dockerSpinner = ora('Checking Docker environment...').start();
|
|
2972
|
+
|
|
2973
|
+
try {
|
|
2974
|
+
const { spawn } = require('child_process');
|
|
2975
|
+
|
|
2976
|
+
// Check Docker installation
|
|
2977
|
+
const checkDocker = () => {
|
|
2978
|
+
return new Promise((resolve) => {
|
|
2979
|
+
const check = spawn('docker', ['--version'], { stdio: 'pipe' });
|
|
2980
|
+
check.on('close', (code) => resolve(code === 0));
|
|
2981
|
+
check.on('error', () => resolve(false));
|
|
2982
|
+
});
|
|
2983
|
+
};
|
|
2984
|
+
|
|
2985
|
+
const dockerAvailable = await checkDocker();
|
|
2986
|
+
if (!dockerAvailable) {
|
|
2987
|
+
dockerSpinner.fail('Docker not found');
|
|
2988
|
+
console.log(chalk.red('ā Docker is required for Docker sandbox'));
|
|
2989
|
+
console.log(chalk.yellow('š” Please install Docker and try again'));
|
|
2990
|
+
console.log(chalk.blue(' Visit: https://docs.docker.com/get-docker/'));
|
|
2991
|
+
return;
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
// Check Docker daemon
|
|
2995
|
+
const checkDockerRunning = () => {
|
|
2996
|
+
return new Promise((resolve) => {
|
|
2997
|
+
const check = spawn('docker', ['ps'], { stdio: 'pipe' });
|
|
2998
|
+
check.on('close', (code) => resolve(code === 0));
|
|
2999
|
+
check.on('error', () => resolve(false));
|
|
3000
|
+
});
|
|
3001
|
+
};
|
|
3002
|
+
|
|
3003
|
+
const dockerRunning = await checkDockerRunning();
|
|
3004
|
+
if (!dockerRunning) {
|
|
3005
|
+
dockerSpinner.fail('Docker daemon not running');
|
|
3006
|
+
console.log(chalk.red('ā Docker daemon is not running'));
|
|
3007
|
+
console.log(chalk.yellow('š” Please start Docker and try again'));
|
|
3008
|
+
return;
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
dockerSpinner.succeed('Docker environment ready');
|
|
3012
|
+
|
|
3013
|
+
// Build components string for installation inside sandbox
|
|
3014
|
+
let componentsToInstall = '';
|
|
3015
|
+
if (agent) {
|
|
3016
|
+
const agentList = agent.split(',').map(a => `--agent ${a.trim()}`);
|
|
3017
|
+
componentsToInstall += agentList.join(' ');
|
|
3018
|
+
}
|
|
3019
|
+
if (command) {
|
|
3020
|
+
const commandList = command.split(',').map(c => ` --command ${c.trim()}`);
|
|
3021
|
+
componentsToInstall += commandList.join(' ');
|
|
3022
|
+
}
|
|
3023
|
+
if (mcp) {
|
|
3024
|
+
const mcpList = mcp.split(',').map(m => ` --mcp ${m.trim()}`);
|
|
3025
|
+
componentsToInstall += mcpList.join(' ');
|
|
3026
|
+
}
|
|
3027
|
+
if (setting) {
|
|
3028
|
+
const settingList = setting.split(',').map(s => ` --setting ${s.trim()}`);
|
|
3029
|
+
componentsToInstall += settingList.join(' ');
|
|
3030
|
+
}
|
|
3031
|
+
if (hook) {
|
|
3032
|
+
const hookList = hook.split(',').map(h => ` --hook ${h.trim()}`);
|
|
3033
|
+
componentsToInstall += hookList.join(' ');
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
// Execute Docker launcher
|
|
3037
|
+
const execSpinner = ora('Executing Docker sandbox...').start();
|
|
3038
|
+
|
|
3039
|
+
const launcherPath = path.join(sandboxDir, 'docker-launcher.js');
|
|
3040
|
+
|
|
3041
|
+
const dockerExec = spawn('node', [launcherPath, prompt, componentsToInstall.trim()], {
|
|
3042
|
+
cwd: sandboxDir,
|
|
3043
|
+
stdio: 'inherit',
|
|
3044
|
+
env: {
|
|
3045
|
+
...process.env,
|
|
3046
|
+
ANTHROPIC_API_KEY: anthropicKey
|
|
3047
|
+
}
|
|
3048
|
+
});
|
|
3049
|
+
|
|
3050
|
+
await new Promise((resolve, reject) => {
|
|
3051
|
+
dockerExec.on('close', (dockerCode) => {
|
|
3052
|
+
if (dockerCode === 0) {
|
|
3053
|
+
execSpinner.succeed('Docker sandbox execution completed successfully');
|
|
3054
|
+
console.log(chalk.green('\nā
Docker sandbox execution finished!'));
|
|
3055
|
+
console.log(chalk.white('š Output files are in the output/ directory'));
|
|
3056
|
+
resolve();
|
|
3057
|
+
} else {
|
|
3058
|
+
execSpinner.fail(`Docker sandbox execution failed with code ${dockerCode}`);
|
|
3059
|
+
reject(new Error(`Docker execution failed with code ${dockerCode}`));
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
|
|
3063
|
+
dockerExec.on('error', (error) => {
|
|
3064
|
+
execSpinner.fail('Failed to execute Docker sandbox');
|
|
3065
|
+
reject(error);
|
|
3066
|
+
});
|
|
3067
|
+
});
|
|
3068
|
+
|
|
3069
|
+
} catch (error) {
|
|
3070
|
+
dockerSpinner.fail('Failed to check Docker environment');
|
|
3071
|
+
console.log(chalk.red(`ā Error: ${error.message}`));
|
|
3072
|
+
throw error;
|
|
3073
|
+
}
|
|
3074
|
+
|
|
3075
|
+
} catch (error) {
|
|
3076
|
+
console.log(chalk.red(`ā Error setting up Docker sandbox: ${error.message}`));
|
|
3077
|
+
console.log(chalk.yellow('š” Please check your Docker installation and try again'));
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
|
|
2820
3081
|
async function executeE2BSandbox(options, targetDir) {
|
|
2821
3082
|
const { agent, prompt, command, mcp, setting, hook, e2bKey, anthropicKey } = options;
|
|
2822
3083
|
|
|
@@ -2948,21 +3209,30 @@ async function executeE2BSandbox(options, targetDir) {
|
|
|
2948
3209
|
check.on('error', () => resolve(false));
|
|
2949
3210
|
});
|
|
2950
3211
|
};
|
|
2951
|
-
|
|
2952
|
-
//
|
|
2953
|
-
let pythonCmd =
|
|
3212
|
+
|
|
3213
|
+
// Try to find Python 3.11 first (recommended for E2B)
|
|
3214
|
+
let pythonCmd = null;
|
|
2954
3215
|
const python311Available = await checkPythonVersion('python3.11');
|
|
2955
3216
|
if (python311Available) {
|
|
2956
3217
|
pythonCmd = 'python3.11';
|
|
2957
3218
|
console.log(chalk.blue('ā Using Python 3.11 (recommended for E2B)'));
|
|
2958
3219
|
} else {
|
|
2959
|
-
|
|
3220
|
+
// Fall back to platform-appropriate Python commands
|
|
3221
|
+
console.log(chalk.yellow('ā Python 3.11 not found, trying platform defaults...'));
|
|
3222
|
+
const candidates = getPlatformPythonCandidates();
|
|
3223
|
+
|
|
3224
|
+
for (const candidate of candidates) {
|
|
3225
|
+
if (await checkPythonVersion(candidate)) {
|
|
3226
|
+
pythonCmd = candidate;
|
|
3227
|
+
console.log(chalk.blue(`ā Using ${candidate} for E2B`));
|
|
3228
|
+
break;
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
2960
3231
|
}
|
|
2961
|
-
|
|
2962
|
-
// Verify
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
pythonSpinner.fail('Python 3 not found');
|
|
3232
|
+
|
|
3233
|
+
// Verify we found a working Python installation
|
|
3234
|
+
if (!pythonCmd) {
|
|
3235
|
+
pythonSpinner.fail('Python not found');
|
|
2966
3236
|
console.log(chalk.red('ā Python 3.11+ is required for E2B sandbox'));
|
|
2967
3237
|
console.log(chalk.yellow('š” Please install Python 3.11+ and try again'));
|
|
2968
3238
|
console.log(chalk.blue(' Visit: https://python.org/downloads'));
|