gitarsenal-cli 1.9.96 → 1.9.97
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/.venv_status.json +1 -1
- package/README.md +29 -0
- package/bin/gitarsenal.js +220 -127
- package/kill_claude/requirements.txt +1 -1
- package/lib/dependencies.js +130 -4
- package/lib/e2b-sandbox.js +158 -0
- package/lib/execAsync.js +12 -0
- package/lib/sandbox.js +97 -113
- package/package.json +2 -1
- package/python/__pycache__/credentials_manager.cpython-312.pyc +0 -0
- package/python/__pycache__/e2b_sandbox_agent.cpython-313.pyc +0 -0
- package/python/__pycache__/fetch_modal_tokens.cpython-312.pyc +0 -0
- package/python/credentials_manager.py +2 -1
- package/python/e2b_sandbox_agent.py +787 -0
- package/python/fetch_modal_tokens.py +47 -25
- package/python/requirements.txt +2 -1
- package/python/test_enhanced_sandbox_script.py +1429 -0
- package/python/test_modalSandboxScript.py +41 -5
- package/scripts/setup_e2b.js +162 -0
- package/kill_claude/.claude/settings.local.json +0 -9
- package/kill_claude/__pycache__/bash_output_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/bash_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/claude_code_agent.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/edit_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/exit_plan_mode_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/glob_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/grep_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/kill_bash_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/ls_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/multiedit_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/notebook_edit_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/read_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/task_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/todo_write_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/web_fetch_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/web_search_tool.cpython-313.pyc +0 -0
- package/kill_claude/__pycache__/write_tool.cpython-313.pyc +0 -0
package/.venv_status.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"created":"2025-09-
|
|
1
|
+
{"created":"2025-09-13T16:50:38.652Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
|
package/README.md
CHANGED
|
@@ -25,6 +25,9 @@ gitarsenal --gpu A10G --repo https://github.com/username/awesome-project.git
|
|
|
25
25
|
|
|
26
26
|
# With custom setup commands
|
|
27
27
|
gitarsenal --gpu A100 --repo https://github.com/username/awesome-project.git --setup-commands "pip install -r requirements.txt" "python setup.py install"
|
|
28
|
+
|
|
29
|
+
# Using E2B sandbox provider (faster startup, no GPU)
|
|
30
|
+
gitarsenal --sandbox-provider e2b --repo https://github.com/username/awesome-project.git
|
|
28
31
|
```
|
|
29
32
|
|
|
30
33
|
### Examples
|
|
@@ -65,6 +68,32 @@ gitarsenal keys delete --service openai
|
|
|
65
68
|
|
|
66
69
|
## Features
|
|
67
70
|
|
|
71
|
+
### Sandbox Providers
|
|
72
|
+
|
|
73
|
+
GitArsenal supports multiple sandbox providers to fit your needs:
|
|
74
|
+
|
|
75
|
+
#### Modal Sandbox (Default)
|
|
76
|
+
- Full GPU support (A10G, A100, H100, etc.)
|
|
77
|
+
- Persistent storage with volumes
|
|
78
|
+
- SSH access
|
|
79
|
+
- Longer startup time
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Explicitly use Modal sandbox
|
|
83
|
+
gitarsenal --sandbox-provider modal --repo https://github.com/username/project.git
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### E2B Sandbox
|
|
87
|
+
- Faster startup time
|
|
88
|
+
- Lightweight environment
|
|
89
|
+
- No GPU support
|
|
90
|
+
- Ideal for quick testing and development
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Use E2B sandbox for faster startup
|
|
94
|
+
gitarsenal --sandbox-provider e2b --repo https://github.com/username/project.git
|
|
95
|
+
```
|
|
96
|
+
|
|
68
97
|
### Automatic Environment Setup
|
|
69
98
|
The CLI automatically:
|
|
70
99
|
- Clones your repository
|
package/bin/gitarsenal.js
CHANGED
|
@@ -6,7 +6,7 @@ const inquirer = require('inquirer');
|
|
|
6
6
|
const ora = require('ora');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const { version } = require('../package.json');
|
|
9
|
-
const { checkDependencies } = require('../lib/dependencies');
|
|
9
|
+
const { checkDependencies, checkDependenciesExceptE2B } = require('../lib/dependencies');
|
|
10
10
|
const { runContainer } = require('../lib/sandbox');
|
|
11
11
|
const updateNotifier = require('update-notifier');
|
|
12
12
|
const pkg = require('../package.json');
|
|
@@ -16,6 +16,7 @@ const fs = require('fs');
|
|
|
16
16
|
const https = require('https');
|
|
17
17
|
const http = require('http');
|
|
18
18
|
const { fetchGitIngestData } = require('../gitingest-integration');
|
|
19
|
+
const { execAsync } = require('../lib/execAsync');
|
|
19
20
|
|
|
20
21
|
// Function to activate virtual environment
|
|
21
22
|
function activateVirtualEnvironment() {
|
|
@@ -1042,7 +1043,27 @@ program
|
|
|
1042
1043
|
.version(version)
|
|
1043
1044
|
.description('GitArsenal CLI - Create GPU-accelerated development environments');
|
|
1044
1045
|
|
|
1045
|
-
|
|
1046
|
+
// Add E2B setup command
|
|
1047
|
+
program
|
|
1048
|
+
.command('setup-e2b')
|
|
1049
|
+
.description('Set up E2B sandbox provider')
|
|
1050
|
+
.action(async () => {
|
|
1051
|
+
const setupPath = path.join(__dirname, '..', 'scripts', 'setup_e2b.js');
|
|
1052
|
+
if (fs.existsSync(setupPath)) {
|
|
1053
|
+
const child = spawn('node', [setupPath], {
|
|
1054
|
+
stdio: 'inherit'
|
|
1055
|
+
});
|
|
1056
|
+
child.on('exit', (code) => {
|
|
1057
|
+
if (code !== 0) {
|
|
1058
|
+
console.log(chalk.red('E2B setup failed.'));
|
|
1059
|
+
process.exit(code);
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
} else {
|
|
1063
|
+
console.log(chalk.red(`Setup script not found at ${setupPath}`));
|
|
1064
|
+
process.exit(1);
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1046
1067
|
|
|
1047
1068
|
// Keys management command
|
|
1048
1069
|
const keysCmd = program
|
|
@@ -1096,6 +1117,7 @@ program
|
|
|
1096
1117
|
.option('--show-examples', 'Show usage examples')
|
|
1097
1118
|
.option('--user-id <id>', 'User ID for tracking')
|
|
1098
1119
|
.option('--user-name <name>', 'User name for tracking')
|
|
1120
|
+
.option('--sandbox-provider <provider>', 'Sandbox provider (modal or e2b)', 'modal')
|
|
1099
1121
|
.action(async (options) => {
|
|
1100
1122
|
// If options are provided directly, run the container command
|
|
1101
1123
|
if (options.repo || options.showExamples || process.argv.length <= 3) {
|
|
@@ -1125,15 +1147,37 @@ async function runContainerCommand(options) {
|
|
|
1125
1147
|
// Send user data immediately so the dashboard records users
|
|
1126
1148
|
await sendUserData(userId, userName, userEmail);
|
|
1127
1149
|
|
|
1128
|
-
//
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
if (!
|
|
1133
|
-
|
|
1150
|
+
// Initialize sandbox provider from options or default to modal
|
|
1151
|
+
let sandboxProvider = options.sandboxProvider || 'modal';
|
|
1152
|
+
|
|
1153
|
+
// Prompt for sandbox provider if not specified
|
|
1154
|
+
if (!options.sandboxProvider) {
|
|
1155
|
+
const providerAnswers = await inquirer.prompt([
|
|
1156
|
+
{
|
|
1157
|
+
type: 'list',
|
|
1158
|
+
name: 'sandboxProvider',
|
|
1159
|
+
message: 'Select sandbox provider:',
|
|
1160
|
+
choices: [
|
|
1161
|
+
{ name: 'Modal (GPU support, persistent volumes)', value: 'modal' },
|
|
1162
|
+
{ name: 'E2B (Faster startup, no GPU)', value: 'e2b' }
|
|
1163
|
+
],
|
|
1164
|
+
default: 'modal'
|
|
1165
|
+
}
|
|
1166
|
+
]);
|
|
1167
|
+
sandboxProvider = providerAnswers.sandboxProvider;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// Check dependencies based on selected sandbox provider
|
|
1171
|
+
console.log(chalk.blue('⠋ Checking dependencies...'));
|
|
1172
|
+
const dependenciesMet = sandboxProvider === 'e2b'
|
|
1173
|
+
? await checkDependenciesExceptE2B()
|
|
1174
|
+
: await checkDependencies();
|
|
1175
|
+
|
|
1176
|
+
if (!dependenciesMet) {
|
|
1177
|
+
console.log(chalk.red('✖ Missing dependencies. Please install them and try again.'));
|
|
1134
1178
|
process.exit(1);
|
|
1135
1179
|
}
|
|
1136
|
-
|
|
1180
|
+
console.log(chalk.green('✔ Dependencies checked'));
|
|
1137
1181
|
|
|
1138
1182
|
// If repo URL not provided, prompt for it
|
|
1139
1183
|
let repoUrl = options.repoUrl || options.repo;
|
|
@@ -1142,6 +1186,14 @@ async function runContainerCommand(options) {
|
|
|
1142
1186
|
let volumeName = options.volumeName || options.volume;
|
|
1143
1187
|
let skipConfirmation = options.yes;
|
|
1144
1188
|
let setupCommands = options.setupCommands || [];
|
|
1189
|
+
let analysisData = null;
|
|
1190
|
+
let collectedApiKeys = {};
|
|
1191
|
+
|
|
1192
|
+
// Validate sandbox provider
|
|
1193
|
+
if (sandboxProvider !== 'modal' && sandboxProvider !== 'e2b') {
|
|
1194
|
+
console.log(chalk.yellow(`⚠️ Invalid sandbox provider: ${sandboxProvider}. Using 'modal' as default.`));
|
|
1195
|
+
sandboxProvider = 'modal';
|
|
1196
|
+
}
|
|
1145
1197
|
|
|
1146
1198
|
if (!repoUrl) {
|
|
1147
1199
|
const answers = await inquirer.prompt([
|
|
@@ -1155,135 +1207,149 @@ async function runContainerCommand(options) {
|
|
|
1155
1207
|
repoUrl = answers.repoUrl;
|
|
1156
1208
|
}
|
|
1157
1209
|
|
|
1158
|
-
//
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1210
|
+
// Always prompt for sandbox provider, even if specified via command line
|
|
1211
|
+
const providerAnswers = await inquirer.prompt([
|
|
1212
|
+
{
|
|
1213
|
+
type: 'list',
|
|
1214
|
+
name: 'sandboxProvider',
|
|
1215
|
+
message: 'Select sandbox provider:',
|
|
1216
|
+
choices: [
|
|
1217
|
+
{ name: 'Modal (GPU support, persistent volumes)', value: 'modal' },
|
|
1218
|
+
{ name: 'E2B (Faster startup, no GPU)', value: 'e2b' }
|
|
1219
|
+
],
|
|
1220
|
+
default: sandboxProvider // Use command line value as default if provided
|
|
1221
|
+
}
|
|
1222
|
+
]);
|
|
1223
|
+
sandboxProvider = providerAnswers.sandboxProvider;
|
|
1224
|
+
|
|
1225
|
+
// Load stored API keys for both providers
|
|
1226
|
+
console.log(chalk.blue('🔍 Loading stored API keys...'));
|
|
1227
|
+
const storedKeys = await loadStoredApiKeys();
|
|
1228
|
+
collectedApiKeys = storedKeys;
|
|
1229
|
+
|
|
1230
|
+
// Only perform repository analysis and GPU selection for Modal provider
|
|
1231
|
+
if (sandboxProvider === 'modal') {
|
|
1232
|
+
// Analyze repository for GPU recommendations and API key requirements
|
|
1233
|
+
if (repoUrl) {
|
|
1234
|
+
// Start a main spinner that will show overall progress
|
|
1235
|
+
const mainSpinner = ora('Analyzing repository...').start();
|
|
1180
1236
|
|
|
1181
|
-
|
|
1182
|
-
//
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1237
|
+
try {
|
|
1238
|
+
// Start preview immediately so we get early feedback; suppress summary here to avoid duplicates.
|
|
1239
|
+
// Provide an AbortController so we can stop the preview spinner as soon as full fetch returns.
|
|
1240
|
+
const previewAbort = new AbortController();
|
|
1241
|
+
mainSpinner.text = 'Analyzing repository for GPU/Torch/CUDA recommendations...';
|
|
1242
|
+
const previewPromise = previewRecommendations(repoUrl, { showSummary: false, abortSignal: previewAbort.signal, hideSpinner: true }).catch(() => null);
|
|
1243
|
+
|
|
1244
|
+
// Run full fetch in parallel with stored credentials; prefer its results if available.
|
|
1245
|
+
mainSpinner.text = 'Finding the best machine for your code and detecting API requirements...';
|
|
1246
|
+
const fullData = await fetchFullSetupAndRecs(repoUrl, storedKeys).catch(() => null);
|
|
1187
1247
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
analysisData = previewData;
|
|
1248
|
+
if (fullData) {
|
|
1249
|
+
// Stop preview spinner immediately since we have a response
|
|
1250
|
+
previewAbort.abort();
|
|
1251
|
+
mainSpinner.succeed('Analysis complete!');
|
|
1252
|
+
printGpuTorchCudaSummary(fullData);
|
|
1253
|
+
analysisData = fullData;
|
|
1254
|
+
|
|
1255
|
+
// Handle API key requirements
|
|
1256
|
+
if (fullData.requiredApiKeys && Array.isArray(fullData.requiredApiKeys) && fullData.requiredApiKeys.length > 0) {
|
|
1257
|
+
const missingKeys = await promptForMissingApiKeys(fullData.requiredApiKeys, storedKeys);
|
|
1258
|
+
collectedApiKeys = { ...storedKeys, ...missingKeys };
|
|
1259
|
+
} else {
|
|
1260
|
+
collectedApiKeys = storedKeys;
|
|
1261
|
+
}
|
|
1203
1262
|
} else {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1263
|
+
// Full fetch failed, wait for preview and show its results
|
|
1264
|
+
mainSpinner.text = 'Waiting for preview analysis to complete...';
|
|
1265
|
+
const previewData = await previewPromise;
|
|
1266
|
+
if (previewData) {
|
|
1267
|
+
mainSpinner.succeed('Preview analysis complete!');
|
|
1268
|
+
printGpuTorchCudaSummary(previewData);
|
|
1269
|
+
analysisData = previewData;
|
|
1270
|
+
} else {
|
|
1271
|
+
mainSpinner.fail('Analysis failed - both preview and full analysis timed out or failed');
|
|
1272
|
+
console.log(chalk.yellow('⚠️ Unable to analyze repository automatically.'));
|
|
1273
|
+
console.log(chalk.gray('Repository setup will still be handled by Agent in container.'));
|
|
1274
|
+
}
|
|
1207
1275
|
}
|
|
1208
|
-
|
|
1276
|
+
} catch (error) {
|
|
1277
|
+
mainSpinner.fail(`Analysis failed: ${error.message}`);
|
|
1278
|
+
console.log(chalk.yellow('⚠️ Unable to analyze repository automatically.'));
|
|
1279
|
+
console.log(chalk.gray('Repository setup will still be handled by Agent in container.'));
|
|
1209
1280
|
}
|
|
1210
|
-
} catch (error) {
|
|
1211
|
-
mainSpinner.fail(`Analysis failed: ${error.message}`);
|
|
1212
|
-
console.log(chalk.yellow('⚠️ Unable to analyze repository automatically.'));
|
|
1213
|
-
console.log(chalk.gray('Repository setup will still be handled by Agent in container.'));
|
|
1214
|
-
collectedApiKeys = await loadStoredApiKeys();
|
|
1215
1281
|
}
|
|
1216
|
-
}
|
|
1217
1282
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1283
|
+
// Prompt for GPU type if not specified
|
|
1284
|
+
if (!gpuType) {
|
|
1285
|
+
const gpuAnswers = await inquirer.prompt([
|
|
1286
|
+
{
|
|
1287
|
+
type: 'list',
|
|
1288
|
+
name: 'gpuType',
|
|
1289
|
+
message: 'Select GPU type:',
|
|
1290
|
+
choices: [
|
|
1291
|
+
{ name: 'T4 (16GB VRAM)', value: 'T4' },
|
|
1292
|
+
{ name: 'L4 (24GB VRAM)', value: 'L4' },
|
|
1293
|
+
{ name: 'A10G (24GB VRAM)', value: 'A10G' },
|
|
1294
|
+
{ name: 'A100-40 (40GB VRAM)', value: 'A100-40GB' },
|
|
1295
|
+
{ name: 'A100-80 (80GB VRAM)', value: 'A100-80GB' },
|
|
1296
|
+
{ name: 'L40S (48GB VRAM)', value: 'L40S' },
|
|
1297
|
+
{ name: 'H100 (80GB VRAM)', value: 'H100' },
|
|
1298
|
+
{ name: 'H200 (141GB VRAM)', value: 'H200' },
|
|
1299
|
+
{ name: 'B200 (141GB VRAM)', value: 'B200' }
|
|
1300
|
+
],
|
|
1301
|
+
default: 'A10G'
|
|
1302
|
+
}
|
|
1303
|
+
]);
|
|
1304
|
+
gpuType = gpuAnswers.gpuType;
|
|
1305
|
+
}
|
|
1241
1306
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1307
|
+
// Prompt for GPU count if not already specified via CLI
|
|
1308
|
+
if (!options.gpuCount) {
|
|
1309
|
+
const gpuCountAnswers = await inquirer.prompt([
|
|
1310
|
+
{
|
|
1311
|
+
type: 'list',
|
|
1312
|
+
name: 'gpuCount',
|
|
1313
|
+
message: 'How many GPUs do you need?',
|
|
1314
|
+
choices: [
|
|
1315
|
+
{ name: '1 GPU', value: 1 },
|
|
1316
|
+
{ name: '2 GPUs', value: 2 },
|
|
1317
|
+
{ name: '3 GPUs', value: 3 },
|
|
1318
|
+
{ name: '4 GPUs', value: 4 },
|
|
1319
|
+
{ name: '6 GPUs', value: 6 },
|
|
1320
|
+
{ name: '8 GPUs', value: 8 }
|
|
1321
|
+
],
|
|
1322
|
+
default: gpuCount
|
|
1323
|
+
}
|
|
1324
|
+
]);
|
|
1325
|
+
gpuCount = gpuCountAnswers.gpuCount;
|
|
1326
|
+
}
|
|
1262
1327
|
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1328
|
+
// Prompt for persistent volume
|
|
1329
|
+
if (!volumeName && !skipConfirmation) {
|
|
1330
|
+
const volumeAnswers = await inquirer.prompt([
|
|
1331
|
+
{
|
|
1332
|
+
type: 'confirm',
|
|
1333
|
+
name: 'useVolume',
|
|
1334
|
+
message: 'Use persistent volume for faster installs?',
|
|
1335
|
+
default: true
|
|
1336
|
+
},
|
|
1337
|
+
{
|
|
1338
|
+
type: 'input',
|
|
1339
|
+
name: 'volumeName',
|
|
1340
|
+
message: 'Enter volume name:',
|
|
1341
|
+
default: getDefaultVolumeName(repoUrl),
|
|
1342
|
+
when: (answers) => answers.useVolume
|
|
1343
|
+
}
|
|
1344
|
+
]);
|
|
1345
|
+
|
|
1346
|
+
if (volumeAnswers.useVolume) {
|
|
1347
|
+
volumeName = volumeAnswers.volumeName || getDefaultVolumeName(repoUrl);
|
|
1278
1348
|
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
if (volumeAnswers.useVolume) {
|
|
1349
|
+
} else if (!volumeName && skipConfirmation) {
|
|
1350
|
+
// If --yes flag is used and no volume specified, use default
|
|
1282
1351
|
volumeName = getDefaultVolumeName(repoUrl);
|
|
1283
1352
|
}
|
|
1284
|
-
} else if (!volumeName && skipConfirmation) {
|
|
1285
|
-
// If --yes flag is used and no volume specified, use default
|
|
1286
|
-
volumeName = getDefaultVolumeName(repoUrl);
|
|
1287
1353
|
}
|
|
1288
1354
|
|
|
1289
1355
|
// Prompt for custom setup commands only if no repo URL provided and no commands specified
|
|
@@ -1317,6 +1383,32 @@ async function runContainerCommand(options) {
|
|
|
1317
1383
|
|
|
1318
1384
|
// Confirm settings (configuration will be shown by Python script after GPU selection)
|
|
1319
1385
|
if (!skipConfirmation) {
|
|
1386
|
+
// Display configuration summary
|
|
1387
|
+
console.log(chalk.cyan('\n📋 Configuration Summary:'));
|
|
1388
|
+
console.log(chalk.dim('─────────────────────────────────────'));
|
|
1389
|
+
console.log(`${chalk.dim('Repository URL:')} ${chalk.white(repoUrl || 'Not specified')}`);
|
|
1390
|
+
console.log(`${chalk.dim('Sandbox Provider:')} ${chalk.white(sandboxProvider)}`);
|
|
1391
|
+
|
|
1392
|
+
if (sandboxProvider === 'modal') {
|
|
1393
|
+
if (gpuCount > 1) {
|
|
1394
|
+
console.log(`${chalk.dim('GPU Type:')} ${chalk.white(`${gpuCount}x ${gpuType}`)}`);
|
|
1395
|
+
} else {
|
|
1396
|
+
console.log(`${chalk.dim('GPU Type:')} ${chalk.white(gpuType)}`);
|
|
1397
|
+
}
|
|
1398
|
+
console.log(`${chalk.dim('Volume:')} ${chalk.white(volumeName || 'None')}`);
|
|
1399
|
+
} else {
|
|
1400
|
+
console.log(`${chalk.dim('Note:')} ${chalk.yellow('E2B sandboxes do not support GPU selection or volumes')}`);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
if (setupCommands.length > 0) {
|
|
1404
|
+
console.log(`${chalk.dim('Setup Commands:')} ${chalk.white(setupCommands.length)} custom commands`);
|
|
1405
|
+
} else if (repoUrl) {
|
|
1406
|
+
console.log(`${chalk.dim('Setup:')} ${chalk.white('Intelligent repository setup')}`);
|
|
1407
|
+
} else {
|
|
1408
|
+
console.log(`${chalk.dim('Setup Commands:')} ${chalk.white('None')}`);
|
|
1409
|
+
}
|
|
1410
|
+
console.log(chalk.dim('─────────────────────────────────────'));
|
|
1411
|
+
|
|
1320
1412
|
const confirmAnswers = await inquirer.prompt([
|
|
1321
1413
|
{
|
|
1322
1414
|
type: 'confirm',
|
|
@@ -1348,7 +1440,8 @@ async function runContainerCommand(options) {
|
|
|
1348
1440
|
userName,
|
|
1349
1441
|
userEmail,
|
|
1350
1442
|
apiKeys: collectedApiKeys,
|
|
1351
|
-
analysisData
|
|
1443
|
+
analysisData,
|
|
1444
|
+
sandboxProvider
|
|
1352
1445
|
});
|
|
1353
1446
|
|
|
1354
1447
|
} catch (containerError) {
|
package/lib/dependencies.js
CHANGED
|
@@ -2,9 +2,43 @@ const { promisify } = require('util');
|
|
|
2
2
|
const { exec } = require('child_process');
|
|
3
3
|
const which = require('which');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
5
7
|
|
|
6
8
|
const execAsync = promisify(exec);
|
|
7
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Get the Python executable path from the virtual environment
|
|
12
|
+
* @returns {string} - Path to Python executable or 'python3'
|
|
13
|
+
*/
|
|
14
|
+
function getPythonExecutable() {
|
|
15
|
+
// Check if PYTHON_EXECUTABLE is set by the virtual environment activation
|
|
16
|
+
if (process.env.PYTHON_EXECUTABLE && fs.existsSync(process.env.PYTHON_EXECUTABLE)) {
|
|
17
|
+
return process.env.PYTHON_EXECUTABLE;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Try to find the virtual environment Python
|
|
21
|
+
const venvPath = path.join(__dirname, '..', '.venv');
|
|
22
|
+
const isWindows = process.platform === 'win32';
|
|
23
|
+
|
|
24
|
+
// Check for uv-style virtual environment first
|
|
25
|
+
const uvPythonPath = path.join(venvPath, 'bin', 'python');
|
|
26
|
+
|
|
27
|
+
// Check for traditional venv structure
|
|
28
|
+
const traditionalPythonPath = isWindows ?
|
|
29
|
+
path.join(venvPath, 'Scripts', 'python.exe') :
|
|
30
|
+
path.join(venvPath, 'bin', 'python');
|
|
31
|
+
|
|
32
|
+
if (fs.existsSync(uvPythonPath)) {
|
|
33
|
+
return uvPythonPath;
|
|
34
|
+
} else if (fs.existsSync(traditionalPythonPath)) {
|
|
35
|
+
return traditionalPythonPath;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Fall back to system Python
|
|
39
|
+
return isWindows ? 'python' : 'python3';
|
|
40
|
+
}
|
|
41
|
+
|
|
8
42
|
/**
|
|
9
43
|
* Check if a command is available in the system
|
|
10
44
|
* @param {string} command - Command to check
|
|
@@ -24,8 +58,10 @@ async function commandExists(command) {
|
|
|
24
58
|
* @returns {Promise<{exists: boolean, version: string|null}>}
|
|
25
59
|
*/
|
|
26
60
|
async function checkPython() {
|
|
61
|
+
const pythonExecutable = getPythonExecutable();
|
|
62
|
+
|
|
27
63
|
try {
|
|
28
|
-
const { stdout } = await execAsync(
|
|
64
|
+
const { stdout } = await execAsync(`${pythonExecutable} --version`);
|
|
29
65
|
const version = stdout.trim().split(' ')[1];
|
|
30
66
|
return { exists: true, version };
|
|
31
67
|
} catch (error) {
|
|
@@ -44,9 +80,34 @@ async function checkPython() {
|
|
|
44
80
|
* @returns {Promise<boolean>}
|
|
45
81
|
*/
|
|
46
82
|
async function checkModal() {
|
|
83
|
+
const pythonExecutable = getPythonExecutable();
|
|
84
|
+
|
|
47
85
|
try {
|
|
48
|
-
|
|
86
|
+
// Check if modal is importable in the Python environment
|
|
87
|
+
await execAsync(`${pythonExecutable} -c "import modal"`);
|
|
49
88
|
return true;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
try {
|
|
91
|
+
// Fall back to checking the modal command
|
|
92
|
+
await execAsync('modal --version');
|
|
93
|
+
return true;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Check if E2B code interpreter is installed
|
|
102
|
+
* @returns {Promise<boolean>}
|
|
103
|
+
*/
|
|
104
|
+
async function checkE2B() {
|
|
105
|
+
const pythonExecutable = getPythonExecutable();
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// Use a more reliable method to check if the module is importable
|
|
109
|
+
const { stdout } = await execAsync(`${pythonExecutable} -c "import importlib.util; print(importlib.util.find_spec('e2b_code_interpreter') is not None)"`);
|
|
110
|
+
return stdout.trim() === 'True';
|
|
50
111
|
} catch (error) {
|
|
51
112
|
return false;
|
|
52
113
|
}
|
|
@@ -66,11 +127,20 @@ async function checkGit() {
|
|
|
66
127
|
}
|
|
67
128
|
|
|
68
129
|
async function checkGitingest() {
|
|
130
|
+
const pythonExecutable = getPythonExecutable();
|
|
131
|
+
|
|
69
132
|
try {
|
|
70
|
-
|
|
133
|
+
// Check if gitingest is importable in the Python environment
|
|
134
|
+
await execAsync(`${pythonExecutable} -c "import gitingest"`);
|
|
71
135
|
return true;
|
|
72
136
|
} catch (error) {
|
|
73
|
-
|
|
137
|
+
try {
|
|
138
|
+
// Fall back to checking the gitingest command
|
|
139
|
+
await execAsync('gitingest --help');
|
|
140
|
+
return true;
|
|
141
|
+
} catch (error) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
74
144
|
}
|
|
75
145
|
}
|
|
76
146
|
|
|
@@ -81,6 +151,7 @@ async function checkGitingest() {
|
|
|
81
151
|
async function checkDependencies() {
|
|
82
152
|
const pythonCheck = await checkPython();
|
|
83
153
|
const modalExists = await checkModal();
|
|
154
|
+
const e2bExists = await checkE2B();
|
|
84
155
|
const gitExists = await checkGit();
|
|
85
156
|
const gitingestExists = await checkGitingest();
|
|
86
157
|
|
|
@@ -102,6 +173,59 @@ async function checkDependencies() {
|
|
|
102
173
|
allDependenciesMet = false;
|
|
103
174
|
}
|
|
104
175
|
|
|
176
|
+
if (e2bExists) {
|
|
177
|
+
console.log(`${chalk.green('✓')} E2B Code Interpreter found`);
|
|
178
|
+
} else {
|
|
179
|
+
console.log(`${chalk.red('✗')} E2B Code Interpreter not found. Please install it with: pip install e2b-code-interpreter`);
|
|
180
|
+
allDependenciesMet = false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (gitingestExists) {
|
|
184
|
+
console.log(`${chalk.green('✓')} Gitingest CLI found`);
|
|
185
|
+
} else {
|
|
186
|
+
console.log(`${chalk.red('✗')} Gitingest CLI not found. Please install it with: pip install gitingest`);
|
|
187
|
+
allDependenciesMet = false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (gitExists) {
|
|
191
|
+
console.log(`${chalk.green('✓')} Git found`);
|
|
192
|
+
} else {
|
|
193
|
+
console.log(`${chalk.red('✗')} Git not found. Please install Git.`);
|
|
194
|
+
allDependenciesMet = false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
console.log('------------------------\n');
|
|
198
|
+
|
|
199
|
+
return allDependenciesMet;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Check all required dependencies except E2B
|
|
204
|
+
* @returns {Promise<boolean>} - True if all dependencies are met
|
|
205
|
+
*/
|
|
206
|
+
async function checkDependenciesExceptE2B() {
|
|
207
|
+
const pythonCheck = await checkPython();
|
|
208
|
+
const modalExists = await checkModal();
|
|
209
|
+
const gitExists = await checkGit();
|
|
210
|
+
const gitingestExists = await checkGitingest();
|
|
211
|
+
|
|
212
|
+
let allDependenciesMet = true;
|
|
213
|
+
|
|
214
|
+
console.log('\n--- Dependency Check ---');
|
|
215
|
+
|
|
216
|
+
if (pythonCheck.exists) {
|
|
217
|
+
console.log(`${chalk.green('✓')} Python ${pythonCheck.version} found`);
|
|
218
|
+
} else {
|
|
219
|
+
console.log(`${chalk.red('✗')} Python not found. Please install Python 3.8 or newer.`);
|
|
220
|
+
allDependenciesMet = false;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (modalExists) {
|
|
224
|
+
console.log(`${chalk.green('✓')} Modal CLI found`);
|
|
225
|
+
} else {
|
|
226
|
+
console.log(`${chalk.yellow('!')} Modal CLI not found. Not required for E2B sandbox.`);
|
|
227
|
+
}
|
|
228
|
+
|
|
105
229
|
if (gitingestExists) {
|
|
106
230
|
console.log(`${chalk.green('✓')} Gitingest CLI found`);
|
|
107
231
|
} else {
|
|
@@ -123,8 +247,10 @@ async function checkDependencies() {
|
|
|
123
247
|
|
|
124
248
|
module.exports = {
|
|
125
249
|
checkDependencies,
|
|
250
|
+
checkDependenciesExceptE2B,
|
|
126
251
|
commandExists,
|
|
127
252
|
checkPython,
|
|
128
253
|
checkModal,
|
|
254
|
+
checkE2B,
|
|
129
255
|
checkGit
|
|
130
256
|
};
|