gitarsenal-cli 1.9.39 ā 1.9.41
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/bin/gitarsenal.js +181 -87
- package/lib/sandbox.js +12 -1
- package/package.json +1 -1
- package/python/command_manager.py +18 -0
- package/python/test_modalSandboxScript.py +19 -3
package/.venv_status.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"created":"2025-08-
|
|
1
|
+
{"created":"2025-08-11T14:42:03.859Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
|
package/bin/gitarsenal.js
CHANGED
|
@@ -41,7 +41,6 @@ function activateVirtualEnvironment() {
|
|
|
41
41
|
if (fs.existsSync(statusFile)) {
|
|
42
42
|
try {
|
|
43
43
|
const status = JSON.parse(fs.readFileSync(statusFile, 'utf8'));
|
|
44
|
-
// console.log(chalk.gray(`ā
Virtual environment created: ${status.created}`));
|
|
45
44
|
console.log(chalk.gray(`š¦ Packages: ${status.packages.join(', ')}`));
|
|
46
45
|
} catch (error) {
|
|
47
46
|
console.log(chalk.gray('ā
Virtual environment found'));
|
|
@@ -105,19 +104,39 @@ function activateVirtualEnvironment() {
|
|
|
105
104
|
}
|
|
106
105
|
|
|
107
106
|
// Lightweight preview of GPU/Torch/CUDA recommendations prior to GPU selection
|
|
108
|
-
async function previewRecommendations(repoUrl) {
|
|
109
|
-
const
|
|
107
|
+
async function previewRecommendations(repoUrl, optsOrShowSummary = true) {
|
|
108
|
+
const showSummary = typeof optsOrShowSummary === 'boolean' ? optsOrShowSummary : (optsOrShowSummary?.showSummary ?? true);
|
|
109
|
+
const externalSignal = typeof optsOrShowSummary === 'object' ? optsOrShowSummary.abortSignal : undefined;
|
|
110
|
+
const hideSpinner = typeof optsOrShowSummary === 'object' ? optsOrShowSummary.hideSpinner : false;
|
|
111
|
+
|
|
112
|
+
const spinner = hideSpinner ? null : ora('Analyzing repository for GPU/Torch/CUDA recommendations...').start();
|
|
113
|
+
const previewTimeoutMs = Number(process.env.GITARSENAL_PREVIEW_TIMEOUT_MS || 90000);
|
|
114
|
+
const controller = new AbortController();
|
|
115
|
+
const abortOnExternal = () => controller.abort();
|
|
116
|
+
const timeoutId = setTimeout(() => controller.abort(), previewTimeoutMs);
|
|
117
|
+
|
|
118
|
+
// Add periodic spinner updates to show progress (only if we have a spinner)
|
|
119
|
+
let elapsedTime = 0;
|
|
120
|
+
const progressInterval = spinner ? setInterval(() => {
|
|
121
|
+
elapsedTime += 10;
|
|
122
|
+
const minutes = Math.floor(elapsedTime / 60);
|
|
123
|
+
const seconds = elapsedTime % 60;
|
|
124
|
+
const timeStr = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
125
|
+
spinner.text = `Analyzing repository for GPU/Torch/CUDA recommendations... (${timeStr})`;
|
|
126
|
+
}, 10000) : null; // Update every 10 seconds
|
|
127
|
+
|
|
110
128
|
try {
|
|
129
|
+
// Bridge external abort signal to our controller (for stopping spinner when full fetch returns)
|
|
130
|
+
if (externalSignal) {
|
|
131
|
+
if (externalSignal.aborted) controller.abort();
|
|
132
|
+
else externalSignal.addEventListener('abort', abortOnExternal, { once: true });
|
|
133
|
+
}
|
|
134
|
+
|
|
111
135
|
const envUrl = process.env.GITARSENAL_API_URL;
|
|
112
|
-
const endpoints = envUrl
|
|
113
|
-
? [envUrl]
|
|
114
|
-
: [
|
|
115
|
-
'https://www.gitarsenal.dev/api/best_gpu'
|
|
116
|
-
];
|
|
136
|
+
const endpoints = envUrl ? [envUrl] : ['https://www.gitarsenal.dev/api/best_gpu'];
|
|
117
137
|
|
|
118
138
|
const payload = {
|
|
119
139
|
repoUrl,
|
|
120
|
-
// Minimal GitIngest data to allow backend to run LLM analysis
|
|
121
140
|
gitingestData: {
|
|
122
141
|
system_info: {
|
|
123
142
|
platform: process.platform,
|
|
@@ -137,42 +156,22 @@ async function previewRecommendations(repoUrl) {
|
|
|
137
156
|
},
|
|
138
157
|
success: true
|
|
139
158
|
},
|
|
140
|
-
// Hint server to do lightweight preview if supported
|
|
141
159
|
preview: true
|
|
142
160
|
};
|
|
143
161
|
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
let data = null;
|
|
163
|
+
let lastErrorText = '';
|
|
146
164
|
|
|
147
|
-
const
|
|
148
|
-
const controller = new AbortController();
|
|
149
|
-
const id = setTimeout(() => controller.abort(), timeoutMs);
|
|
165
|
+
for (const url of endpoints) {
|
|
150
166
|
try {
|
|
167
|
+
if (spinner) spinner.text = `Analyzing (preview): ${url}`;
|
|
151
168
|
const res = await fetch(url, {
|
|
152
169
|
method: 'POST',
|
|
153
|
-
headers: {
|
|
154
|
-
|
|
155
|
-
'User-Agent': 'GitArsenal-CLI/1.0'
|
|
156
|
-
},
|
|
157
|
-
body: JSON.stringify(body),
|
|
170
|
+
headers: { 'Content-Type': 'application/json', 'User-Agent': 'GitArsenal-CLI/1.0' },
|
|
171
|
+
body: JSON.stringify(payload),
|
|
158
172
|
redirect: 'follow',
|
|
159
173
|
signal: controller.signal
|
|
160
174
|
});
|
|
161
|
-
clearTimeout(id);
|
|
162
|
-
return res;
|
|
163
|
-
} catch (e) {
|
|
164
|
-
clearTimeout(id);
|
|
165
|
-
throw e;
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
let data = null;
|
|
170
|
-
let lastErrorText = '';
|
|
171
|
-
|
|
172
|
-
for (const url of endpoints) {
|
|
173
|
-
try {
|
|
174
|
-
spinner.text = `Analyzing (preview): ${url}`;
|
|
175
|
-
const res = await fetchWithTimeout(url, payload, previewTimeoutMs);
|
|
176
175
|
if (!res.ok) {
|
|
177
176
|
const text = await res.text().catch(() => '');
|
|
178
177
|
lastErrorText = `${res.status} ${text.slice(0, 300)}`;
|
|
@@ -181,25 +180,37 @@ async function previewRecommendations(repoUrl) {
|
|
|
181
180
|
data = await res.json().catch(() => null);
|
|
182
181
|
if (data) break;
|
|
183
182
|
} catch (err) {
|
|
183
|
+
if (err && (err.name === 'AbortError' || err.code === 'ABORT_ERR')) {
|
|
184
|
+
// Silent stop on external abort (e.g., full fetch succeeded)
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
184
187
|
lastErrorText = err && err.message ? err.message : 'request failed';
|
|
185
188
|
continue;
|
|
186
189
|
}
|
|
187
190
|
}
|
|
188
191
|
|
|
189
|
-
spinner.stop();
|
|
190
|
-
|
|
191
192
|
if (!data) {
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
if (!hideSpinner) {
|
|
194
|
+
console.log(chalk.yellow('ā ļø Preview unavailable (timeout or server error).'));
|
|
195
|
+
if (lastErrorText) console.log(chalk.gray(`Reason: ${lastErrorText}`));
|
|
196
|
+
}
|
|
194
197
|
return null;
|
|
195
198
|
}
|
|
196
199
|
|
|
197
|
-
|
|
200
|
+
if (showSummary) {
|
|
201
|
+
printGpuTorchCudaSummary(data);
|
|
202
|
+
}
|
|
198
203
|
return data;
|
|
199
204
|
} catch (e) {
|
|
200
|
-
|
|
201
|
-
|
|
205
|
+
if (!(e && (e.name === 'AbortError' || e.code === 'ABORT_ERR')) && !hideSpinner) {
|
|
206
|
+
console.log(chalk.yellow(`ā ļø Preview failed: ${e.message}`));
|
|
207
|
+
}
|
|
202
208
|
return null;
|
|
209
|
+
} finally {
|
|
210
|
+
clearTimeout(timeoutId);
|
|
211
|
+
if (progressInterval) clearInterval(progressInterval);
|
|
212
|
+
if (spinner) spinner.stop();
|
|
213
|
+
if (externalSignal) externalSignal.removeEventListener('abort', abortOnExternal);
|
|
203
214
|
}
|
|
204
215
|
}
|
|
205
216
|
|
|
@@ -299,7 +310,7 @@ function printGpuTorchCudaSummary(result) {
|
|
|
299
310
|
// Full fetch to get both setup commands and recommendations in one request
|
|
300
311
|
async function fetchFullSetupAndRecs(repoUrl) {
|
|
301
312
|
const envUrl = process.env.GITARSENAL_API_URL;
|
|
302
|
-
const endpoints = envUrl ? [envUrl] : ['https://www.gitarsenal.dev/api/
|
|
313
|
+
const endpoints = envUrl ? [envUrl] : ['https://www.gitarsenal.dev/api/best_gpu'];
|
|
303
314
|
const payload = {
|
|
304
315
|
repoUrl,
|
|
305
316
|
gitingestData: {
|
|
@@ -387,7 +398,7 @@ async function sendUserData(userId, userName, userEmail) {
|
|
|
387
398
|
webhookUrl = process.env.GITARSENAL_WEBHOOK_URL;
|
|
388
399
|
}
|
|
389
400
|
|
|
390
|
-
console.log(chalk.gray(`š” Sending to: ${webhookUrl}`));
|
|
401
|
+
// console.log(chalk.gray(`š” Sending to: ${webhookUrl}`));
|
|
391
402
|
console.log(chalk.gray(`š¦ Data: ${data}`));
|
|
392
403
|
|
|
393
404
|
const urlObj = new URL(webhookUrl);
|
|
@@ -414,8 +425,6 @@ async function sendUserData(userId, userName, userEmail) {
|
|
|
414
425
|
responseData += chunk;
|
|
415
426
|
});
|
|
416
427
|
res.on('end', () => {
|
|
417
|
-
console.log(chalk.gray(`š Response status: ${res.statusCode}`));
|
|
418
|
-
console.log(chalk.gray(`š Response: ${responseData}`));
|
|
419
428
|
|
|
420
429
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
421
430
|
console.log(chalk.green('ā
User registered on GitArsenal dashboard'));
|
|
@@ -465,6 +474,35 @@ async function collectUserCredentials(options) {
|
|
|
465
474
|
try {
|
|
466
475
|
const config = JSON.parse(fs.readFileSync(userConfigPath, 'utf8'));
|
|
467
476
|
if (config.userId && config.userName && config.userEmail) {
|
|
477
|
+
// Check if the email is a fake one (contains @example.com)
|
|
478
|
+
if (config.userEmail.includes('@example.com')) {
|
|
479
|
+
console.log(chalk.yellow('ā ļø Detected placeholder email address. Please update your credentials.'));
|
|
480
|
+
console.log(chalk.gray('We need your real email address for proper registration.'));
|
|
481
|
+
|
|
482
|
+
// Prompt for real email address
|
|
483
|
+
const emailUpdate = await inquirer.prompt([
|
|
484
|
+
{
|
|
485
|
+
type: 'input',
|
|
486
|
+
name: 'userEmail',
|
|
487
|
+
message: 'Enter your real email address:',
|
|
488
|
+
validate: (input) => {
|
|
489
|
+
const email = input.trim();
|
|
490
|
+
if (email === '') return 'Email address is required';
|
|
491
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
492
|
+
return 'Please enter a valid email address (e.g., user@example.com)';
|
|
493
|
+
}
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
]);
|
|
498
|
+
|
|
499
|
+
// Update the config with real email
|
|
500
|
+
config.userEmail = emailUpdate.userEmail;
|
|
501
|
+
config.updatedAt = new Date().toISOString();
|
|
502
|
+
fs.writeFileSync(userConfigPath, JSON.stringify(config, null, 2));
|
|
503
|
+
console.log(chalk.green('ā
Email address updated successfully!'));
|
|
504
|
+
}
|
|
505
|
+
|
|
468
506
|
userId = config.userId;
|
|
469
507
|
userName = config.userName;
|
|
470
508
|
userEmail = config.userEmail;
|
|
@@ -496,10 +534,10 @@ async function collectUserCredentials(options) {
|
|
|
496
534
|
|
|
497
535
|
if (authChoice.action === 'register') {
|
|
498
536
|
console.log(chalk.blue('\nš Create New Account'));
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
537
|
+
const credentials = await inquirer.prompt([
|
|
538
|
+
{
|
|
539
|
+
type: 'input',
|
|
540
|
+
name: 'userId',
|
|
503
541
|
message: 'Choose a username:',
|
|
504
542
|
validate: (input) => {
|
|
505
543
|
const username = input.trim();
|
|
@@ -521,14 +559,14 @@ async function collectUserCredentials(options) {
|
|
|
521
559
|
}
|
|
522
560
|
return true;
|
|
523
561
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
type: 'input',
|
|
565
|
+
name: 'userName',
|
|
528
566
|
message: 'Enter your full name:',
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
567
|
+
validate: (input) => input.trim() !== '' ? true : 'Name is required'
|
|
568
|
+
},
|
|
569
|
+
{
|
|
532
570
|
type: 'password',
|
|
533
571
|
name: 'password',
|
|
534
572
|
message: 'Create a password (min 8 characters):',
|
|
@@ -545,11 +583,11 @@ async function collectUserCredentials(options) {
|
|
|
545
583
|
if (input !== answers.password) return 'Passwords do not match';
|
|
546
584
|
return true;
|
|
547
585
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
586
|
+
}
|
|
587
|
+
]);
|
|
588
|
+
|
|
589
|
+
userId = credentials.userId;
|
|
590
|
+
userName = credentials.userName;
|
|
553
591
|
userEmail = credentials.userEmail;
|
|
554
592
|
|
|
555
593
|
console.log(chalk.green('ā
Account created successfully!'));
|
|
@@ -558,9 +596,34 @@ async function collectUserCredentials(options) {
|
|
|
558
596
|
const credentials = await inquirer.prompt([
|
|
559
597
|
{
|
|
560
598
|
type: 'input',
|
|
561
|
-
name: '
|
|
562
|
-
message: 'Enter your username
|
|
563
|
-
validate: (input) =>
|
|
599
|
+
name: 'userId',
|
|
600
|
+
message: 'Enter your username:',
|
|
601
|
+
validate: (input) => {
|
|
602
|
+
const username = input.trim();
|
|
603
|
+
if (username === '') return 'Username is required';
|
|
604
|
+
if (username.length < 3) return 'Username must be at least 3 characters';
|
|
605
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(username)) return 'Username can only contain letters, numbers, _ and -';
|
|
606
|
+
return true;
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
type: 'input',
|
|
611
|
+
name: 'userEmail',
|
|
612
|
+
message: 'Enter your email address:',
|
|
613
|
+
validate: (input) => {
|
|
614
|
+
const email = input.trim();
|
|
615
|
+
if (email === '') return 'Email address is required';
|
|
616
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
617
|
+
return 'Please enter a valid email address (e.g., user@example.com)';
|
|
618
|
+
}
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
type: 'input',
|
|
624
|
+
name: 'userName',
|
|
625
|
+
message: 'Enter your full name:',
|
|
626
|
+
validate: (input) => input.trim() !== '' ? true : 'Name is required'
|
|
564
627
|
},
|
|
565
628
|
{
|
|
566
629
|
type: 'password',
|
|
@@ -570,12 +633,9 @@ async function collectUserCredentials(options) {
|
|
|
570
633
|
}
|
|
571
634
|
]);
|
|
572
635
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
userId = identifier.includes('@') ? identifier.split('@')[0] : identifier;
|
|
577
|
-
userName = `User ${userId}`; // Would be fetched from database
|
|
578
|
-
userEmail = identifier.includes('@') ? identifier : `${identifier}@example.com`;
|
|
636
|
+
userId = credentials.userId;
|
|
637
|
+
userName = credentials.userName;
|
|
638
|
+
userEmail = credentials.userEmail;
|
|
579
639
|
|
|
580
640
|
console.log(chalk.green('ā
Login successful!'));
|
|
581
641
|
}
|
|
@@ -587,15 +647,15 @@ async function collectUserCredentials(options) {
|
|
|
587
647
|
fs.mkdirSync(userConfigDir, { recursive: true });
|
|
588
648
|
}
|
|
589
649
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
650
|
+
const config = {
|
|
651
|
+
userId,
|
|
652
|
+
userName,
|
|
593
653
|
userEmail,
|
|
594
654
|
savedAt: new Date().toISOString()
|
|
595
|
-
|
|
655
|
+
};
|
|
596
656
|
fs.writeFileSync(userConfigPath, JSON.stringify(config, null, 2));
|
|
597
657
|
console.log(chalk.green('ā
Credentials saved locally'));
|
|
598
|
-
|
|
658
|
+
} catch (error) {
|
|
599
659
|
console.log(chalk.yellow('ā ļø Could not save credentials locally'));
|
|
600
660
|
}
|
|
601
661
|
}
|
|
@@ -742,18 +802,49 @@ async function runContainerCommand(options) {
|
|
|
742
802
|
repoUrl = answers.repoUrl;
|
|
743
803
|
}
|
|
744
804
|
|
|
745
|
-
// Attempt full fetch first to get both commands and recommendations;
|
|
805
|
+
// Attempt full fetch first to get both commands and recommendations; now start preview concurrently
|
|
746
806
|
if (useApi && repoUrl) {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
807
|
+
// Start a main spinner that will show overall progress
|
|
808
|
+
const mainSpinner = ora('Analyzing repository...').start();
|
|
809
|
+
|
|
810
|
+
try {
|
|
811
|
+
// Start preview immediately so we get early feedback; suppress summary here to avoid duplicates.
|
|
812
|
+
// Provide an AbortController so we can stop the preview spinner as soon as full fetch returns.
|
|
813
|
+
const previewAbort = new AbortController();
|
|
814
|
+
mainSpinner.text = 'Analyzing repository for GPU/Torch/CUDA recommendations...';
|
|
815
|
+
const previewPromise = previewRecommendations(repoUrl, { showSummary: false, abortSignal: previewAbort.signal, hideSpinner: true }).catch(() => null);
|
|
816
|
+
|
|
817
|
+
// Run full fetch in parallel; prefer its results if available.
|
|
818
|
+
mainSpinner.text = 'Finding the best machine for your code...';
|
|
819
|
+
const fullData = await fetchFullSetupAndRecs(repoUrl).catch(() => null);
|
|
820
|
+
|
|
821
|
+
if (fullData) {
|
|
822
|
+
// Stop preview spinner immediately since we have a response
|
|
823
|
+
previewAbort.abort();
|
|
824
|
+
mainSpinner.succeed('Analysis complete!');
|
|
825
|
+
printGpuTorchCudaSummary(fullData);
|
|
826
|
+
if (Array.isArray(fullData.commands) && fullData.commands.length) {
|
|
827
|
+
setupCommands = fullData.commands;
|
|
828
|
+
// Disable auto-detection since we already have commands
|
|
829
|
+
useApi = false;
|
|
830
|
+
}
|
|
831
|
+
} else {
|
|
832
|
+
// Full fetch failed, wait for preview and show its results
|
|
833
|
+
mainSpinner.text = 'Waiting for preview analysis to complete...';
|
|
834
|
+
const previewData = await previewPromise;
|
|
835
|
+
if (previewData) {
|
|
836
|
+
mainSpinner.succeed('Preview analysis complete!');
|
|
837
|
+
printGpuTorchCudaSummary(previewData);
|
|
838
|
+
} else {
|
|
839
|
+
mainSpinner.fail('Analysis failed - both preview and full analysis timed out or failed');
|
|
840
|
+
console.log(chalk.yellow('ā ļø Unable to analyze repository automatically.'));
|
|
841
|
+
console.log(chalk.gray('You can still proceed with manual setup commands.'));
|
|
842
|
+
}
|
|
754
843
|
}
|
|
755
|
-
}
|
|
756
|
-
|
|
844
|
+
} catch (error) {
|
|
845
|
+
mainSpinner.fail(`Analysis failed: ${error.message}`);
|
|
846
|
+
console.log(chalk.yellow('ā ļø Unable to analyze repository automatically.'));
|
|
847
|
+
console.log(chalk.gray('You can still proceed with manual setup commands.'));
|
|
757
848
|
}
|
|
758
849
|
}
|
|
759
850
|
|
|
@@ -883,7 +974,10 @@ async function runContainerCommand(options) {
|
|
|
883
974
|
volumeName,
|
|
884
975
|
setupCommands,
|
|
885
976
|
useApi,
|
|
886
|
-
yes: skipConfirmation
|
|
977
|
+
yes: skipConfirmation,
|
|
978
|
+
userId,
|
|
979
|
+
userName,
|
|
980
|
+
userEmail
|
|
887
981
|
});
|
|
888
982
|
|
|
889
983
|
} catch (containerError) {
|
package/lib/sandbox.js
CHANGED
|
@@ -44,7 +44,10 @@ async function runContainer(options) {
|
|
|
44
44
|
setupCommands = [],
|
|
45
45
|
useApi = true,
|
|
46
46
|
showExamples = false,
|
|
47
|
-
yes = false
|
|
47
|
+
yes = false,
|
|
48
|
+
userId,
|
|
49
|
+
userName,
|
|
50
|
+
userEmail
|
|
48
51
|
} = options;
|
|
49
52
|
|
|
50
53
|
// Get the path to the Python script
|
|
@@ -107,6 +110,14 @@ async function runContainer(options) {
|
|
|
107
110
|
console.log(chalk.gray(`š Debug: Adding --yes flag to Python script`));
|
|
108
111
|
}
|
|
109
112
|
|
|
113
|
+
// Add user credentials if provided
|
|
114
|
+
if (userId && userEmail && userName) {
|
|
115
|
+
args.push('--user-id', userEmail);
|
|
116
|
+
args.push('--user-name', userId);
|
|
117
|
+
args.push('--display-name', userName);
|
|
118
|
+
console.log(chalk.gray(`š Debug: Passing user credentials to Python script`));
|
|
119
|
+
}
|
|
120
|
+
|
|
110
121
|
// Handle manual setup commands if provided
|
|
111
122
|
if (setupCommands.length > 0) {
|
|
112
123
|
// Create a temporary file to store setup commands
|
package/package.json
CHANGED
|
@@ -253,6 +253,24 @@ class CommandListManager:
|
|
|
253
253
|
|
|
254
254
|
print("="*60)
|
|
255
255
|
|
|
256
|
+
def print_all_commands(self):
|
|
257
|
+
"""Print all commands in the list for debugging/transparency."""
|
|
258
|
+
if not self.commands:
|
|
259
|
+
print("š No commands in the list")
|
|
260
|
+
return
|
|
261
|
+
|
|
262
|
+
print("\n" + "="*80)
|
|
263
|
+
print("š ALL SETUP COMMANDS")
|
|
264
|
+
print("="*80)
|
|
265
|
+
for i, cmd in enumerate(self.commands):
|
|
266
|
+
status_icon = {
|
|
267
|
+
'pending': 'ā³',
|
|
268
|
+
'success': 'ā
',
|
|
269
|
+
'failed': 'ā'
|
|
270
|
+
}.get(cmd['status'], 'ā')
|
|
271
|
+
print(f" {i+1:2d}. {status_icon} {cmd['command']}")
|
|
272
|
+
print("="*80 + "\n")
|
|
273
|
+
|
|
256
274
|
def get_failed_commands_for_llm(self):
|
|
257
275
|
"""Get failed commands for LLM analysis."""
|
|
258
276
|
failed_commands = []
|
|
@@ -398,6 +398,14 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
|
398
398
|
processed_commands = preprocess_commands_with_llm(setup_commands, stored_credentials, api_key)
|
|
399
399
|
print(f"āļø Running {len(processed_commands)} preprocessed setup commands with dynamic command list...")
|
|
400
400
|
|
|
401
|
+
# Display all setup commands that will be executed
|
|
402
|
+
print("\n" + "="*80)
|
|
403
|
+
print("š SETUP COMMANDS TO BE EXECUTED")
|
|
404
|
+
print("="*80)
|
|
405
|
+
for i, cmd in enumerate(processed_commands, 1):
|
|
406
|
+
print(f" {i:2d}. {cmd}")
|
|
407
|
+
print("="*80 + "\n")
|
|
408
|
+
|
|
401
409
|
# Create command list manager with processed commands
|
|
402
410
|
cmd_manager = CommandListManager(processed_commands)
|
|
403
411
|
|
|
@@ -1611,7 +1619,7 @@ def get_setup_commands_from_gitingest(repo_url):
|
|
|
1611
1619
|
url=api_url,
|
|
1612
1620
|
payload=payload,
|
|
1613
1621
|
max_retries=2,
|
|
1614
|
-
timeout=180 # 3 minute timeout
|
|
1622
|
+
timeout=180, # 3 minute timeout
|
|
1615
1623
|
)
|
|
1616
1624
|
|
|
1617
1625
|
if not response:
|
|
@@ -2115,6 +2123,11 @@ if __name__ == "__main__":
|
|
|
2115
2123
|
parser.add_argument('--store-api-key', type=str, help='Store API key for a service (e.g., openai, modal)')
|
|
2116
2124
|
parser.add_argument('--skip-auth', action='store_true', help='Skip authentication check (for development)')
|
|
2117
2125
|
|
|
2126
|
+
# User credential arguments (passed from JavaScript CLI)
|
|
2127
|
+
parser.add_argument('--user-id', type=str, help='User email address (passed from JavaScript CLI)')
|
|
2128
|
+
parser.add_argument('--user-name', type=str, help='Username (passed from JavaScript CLI)')
|
|
2129
|
+
parser.add_argument('--display-name', type=str, help='Display name (passed from JavaScript CLI)')
|
|
2130
|
+
|
|
2118
2131
|
args = parser.parse_args()
|
|
2119
2132
|
|
|
2120
2133
|
# Initialize authentication manager
|
|
@@ -2149,8 +2162,11 @@ if __name__ == "__main__":
|
|
|
2149
2162
|
show_usage_examples()
|
|
2150
2163
|
sys.exit(0)
|
|
2151
2164
|
|
|
2152
|
-
#
|
|
2153
|
-
if
|
|
2165
|
+
# Authentication is handled by the JavaScript CLI when credentials are passed
|
|
2166
|
+
if args.user_id and args.user_name and args.display_name:
|
|
2167
|
+
print(f"ā
Authenticated as: {args.display_name} ({args.user_id})")
|
|
2168
|
+
elif not args.skip_auth:
|
|
2169
|
+
# Only perform authentication check if running Python script directly (not from CLI)
|
|
2154
2170
|
if not _check_authentication(auth_manager):
|
|
2155
2171
|
print("\nā Authentication required. Please login or register first.")
|
|
2156
2172
|
print("Use --login to login or --register to create an account.")
|