gitarsenal-cli 1.9.10 → 1.9.12

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 CHANGED
@@ -1 +1 @@
1
- {"created":"2025-08-04T14:17:42.697Z","packages":["modal","gitingest","requests"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
1
+ {"created":"2025-08-05T14:22:25.927Z","packages":["modal","gitingest","requests"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
package/bin/gitarsenal.js CHANGED
@@ -347,6 +347,7 @@ program.parse(process.argv);
347
347
 
348
348
  async function runContainerCommand(options) {
349
349
  try {
350
+ console.log(chalk.blue('šŸ” DEBUG: runContainerCommand called with options:'), options);
350
351
  // If show-examples flag is set, just show examples and exit
351
352
  if (options.showExamples) {
352
353
  await runContainer({
@@ -419,7 +420,7 @@ async function runContainerCommand(options) {
419
420
  }
420
421
 
421
422
  // Prompt for persistent volume
422
- if (!volumeName) {
423
+ if (!volumeName && !skipConfirmation) {
423
424
  const volumeAnswers = await inquirer.prompt([
424
425
  {
425
426
  type: 'confirm',
@@ -439,6 +440,9 @@ async function runContainerCommand(options) {
439
440
  if (volumeAnswers.useVolume) {
440
441
  volumeName = volumeAnswers.volumeName;
441
442
  }
443
+ } else if (!volumeName && skipConfirmation) {
444
+ // If --yes flag is used and no volume specified, use default
445
+ volumeName = 'gitarsenal-volume';
442
446
  }
443
447
 
444
448
  // Ask about setup command detection if not specified via CLI
@@ -453,6 +457,9 @@ async function runContainerCommand(options) {
453
457
  ]);
454
458
 
455
459
  useApi = apiAnswers.useApi;
460
+ } else if (options.yes) {
461
+ // If --yes flag is used, default to using API for setup command detection
462
+ useApi = true;
456
463
  }
457
464
 
458
465
  // Only prompt for custom commands if auto-detection is disabled and no commands provided
@@ -485,6 +492,7 @@ async function runContainerCommand(options) {
485
492
  }
486
493
 
487
494
  // Confirm settings (configuration will be shown by Python script after GPU selection)
495
+ console.log(chalk.gray(`šŸ” Debug: skipConfirmation = ${skipConfirmation}, options.yes = ${options.yes}`));
488
496
  if (!skipConfirmation) {
489
497
  const confirmAnswers = await inquirer.prompt([
490
498
  {
@@ -506,12 +514,14 @@ async function runContainerCommand(options) {
506
514
 
507
515
  // Run the container
508
516
  try {
517
+ console.log(chalk.gray(`šŸ” Debug: skipConfirmation = ${skipConfirmation}`));
509
518
  await runContainer({
510
519
  repoUrl,
511
520
  gpuType,
512
521
  volumeName,
513
522
  setupCommands,
514
- useApi
523
+ useApi,
524
+ yes: skipConfirmation
515
525
  });
516
526
 
517
527
  } catch (containerError) {
package/lib/sandbox.js CHANGED
@@ -43,7 +43,8 @@ async function runContainer(options) {
43
43
  volumeName,
44
44
  setupCommands = [],
45
45
  useApi = true,
46
- showExamples = false
46
+ showExamples = false,
47
+ yes = false
47
48
  } = options;
48
49
 
49
50
  // Get the path to the Python script
@@ -100,6 +101,12 @@ async function runContainer(options) {
100
101
  args.push('--use-api');
101
102
  }
102
103
 
104
+ // Add --yes flag to skip confirmation prompts
105
+ if (yes) {
106
+ args.push('--yes');
107
+ console.log(chalk.gray(`šŸ” Debug: Adding --yes flag to Python script`));
108
+ }
109
+
103
110
  // Handle manual setup commands if provided
104
111
  if (setupCommands.length > 0) {
105
112
  // Create a temporary file to store setup commands
@@ -110,6 +117,7 @@ async function runContainer(options) {
110
117
 
111
118
  // Log the command being executed
112
119
  console.log(chalk.dim(`\nExecuting: python ${args.join(' ')}`));
120
+ console.log(chalk.gray(`šŸ” Debug: yes parameter = ${yes}`));
113
121
 
114
122
  // Run the Python script without spinner to avoid terminal interference
115
123
  console.log(chalk.dim('Launching container...'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitarsenal-cli",
3
- "version": "1.9.10",
3
+ "version": "1.9.12",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,76 @@
1
+ def show_usage_examples():
2
+ """Display usage examples for the script."""
3
+ print("Usage Examples\n")
4
+
5
+ print("šŸ” Authentication Commands")
6
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
7
+ print("│ gitarsenal --register # Register new account │")
8
+ print("│ gitarsenal --login # Login to existing account │")
9
+ print("│ gitarsenal --logout # Logout from account │")
10
+ print("│ gitarsenal --user-info # Show current user information │")
11
+ print("│ gitarsenal --change-password # Change password │")
12
+ print("│ gitarsenal --delete-account # Delete account │")
13
+ print("│ gitarsenal --store-api-key openai # Store OpenAI API key │")
14
+ print("│ gitarsenal --auth # Interactive auth management │")
15
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
16
+
17
+ print("Basic Container Creation")
18
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
19
+ print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git │")
20
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
21
+
22
+ print("With Setup Commands")
23
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
24
+ print("│ gitarsenal --gpu A100 --repo-url https://github.com/username/repo.git \\ │")
25
+ print("│ --setup-commands \"pip install -r requirements.txt\" \"python setup.py install\" │")
26
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
27
+
28
+ print("With Persistent Storage")
29
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
30
+ print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git \\ │")
31
+ print("│ --volume-name my-persistent-volume │")
32
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
33
+
34
+ print("With GitIngest API (default)")
35
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
36
+ print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git │")
37
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
38
+
39
+ print("Without GitIngest API")
40
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
41
+ print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git \\ │")
42
+ print("│ --no-gitingest │")
43
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
44
+
45
+ print("With Original API")
46
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
47
+ print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git \\ │")
48
+ print("│ --use-api │")
49
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
50
+
51
+ print("Development Mode (Skip Authentication)")
52
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
53
+ print("│ gitarsenal --skip-auth --gpu A10G --repo-url https://github.com/username/repo.git │")
54
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
55
+
56
+ print("Available GPU Options:")
57
+ print(" T4, L4, A10G, A100-40GB, A100-80GB, L40S, H100, H200, B200")
58
+ print()
59
+ print("Authentication Behavior:")
60
+ print(" • First time: Interactive registration/login required")
61
+ print(" • Subsequent runs: Automatic login with stored session")
62
+ print(" • Use --skip-auth for development (bypasses auth)")
63
+ print()
64
+ print("GPU Selection Behavior:")
65
+ print(" • With --gpu: Uses specified GPU without prompting")
66
+ print(" • Without --gpu: Shows interactive GPU selection menu")
67
+ print()
68
+ print("Examples:")
69
+ print(" # First time setup (will prompt for registration):")
70
+ print(" gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git")
71
+ print()
72
+ print(" # Subsequent runs (automatic login):")
73
+ print(" gitarsenal --repo-url https://github.com/username/repo.git")
74
+ print()
75
+ print(" # Development mode (skip authentication):")
76
+ print(" gitarsenal --skip-auth --repo-url https://github.com/username/repo.git")
@@ -31,6 +31,7 @@ parser.add_argument('--gpu', default='A10G', help='GPU type to use')
31
31
  parser.add_argument('--repo-url', help='Repository URL')
32
32
  parser.add_argument('--volume-name', help='Volume name')
33
33
  parser.add_argument('--use-api', action='store_true', help='Use API to fetch setup commands')
34
+ parser.add_argument('--yes', action='store_true', help='Skip confirmation prompts')
34
35
 
35
36
  # Parse only known args to avoid conflicts with other arguments
36
37
  args, unknown = parser.parse_known_args()
@@ -520,7 +521,7 @@ class PersistentShell:
520
521
  client = openai.OpenAI(api_key=api_key)
521
522
 
522
523
  response = client.chat.completions.create(
523
- model="gpt-4o-mini",
524
+ model="gpt-4.1",
524
525
  messages=[
525
526
  {"role": "system", "content": "You are a helpful assistant that suggests alternative commands that don't require user input."},
526
527
  {"role": "user", "content": prompt}
@@ -573,20 +574,10 @@ class PersistentShell:
573
574
  return output.strip()
574
575
  return self.working_dir
575
576
 
576
- def get_virtual_env(self):
577
- """Get the currently activated virtual environment path."""
578
- return self.virtual_env_path
579
-
580
577
  def is_in_venv(self):
581
578
  """Check if we're currently in a virtual environment."""
582
579
  return self.virtual_env_path is not None and self.virtual_env_path != ""
583
580
 
584
- def get_venv_name(self):
585
- """Get the name of the current virtual environment if active."""
586
- if self.is_in_venv():
587
- return os.path.basename(self.virtual_env_path)
588
- return None
589
-
590
581
  def exec(self, *args, **kwargs):
591
582
  """Compatibility method to make PersistentShell work with call_openai_for_debug."""
592
583
  # Convert exec call to execute method
@@ -1128,7 +1119,7 @@ class CommandListManager:
1128
1119
  print("šŸ” Analyzing command list for optimizations...")
1129
1120
 
1130
1121
  response = client.chat.completions.create(
1131
- model="gpt-4o-mini", # Use a more capable model for this complex task
1122
+ model="gpt-4.1", # Use a more capable model for this complex task
1132
1123
  messages=[
1133
1124
  {"role": "system", "content": "You are a helpful assistant that analyzes and optimizes command lists."},
1134
1125
  {"role": "user", "content": prompt}
@@ -1246,7 +1237,6 @@ class CommandListManager:
1246
1237
  print(f"āš ļø Error analyzing command list: {e}")
1247
1238
  return False
1248
1239
 
1249
-
1250
1240
  # Import the fetch_modal_tokens module
1251
1241
  # print("šŸ”„ Fetching tokens from proxy server...")
1252
1242
  from fetch_modal_tokens import get_tokens
@@ -1666,13 +1656,9 @@ IMPORTANT GUIDELINES:
1666
1656
 
1667
1657
  Do not provide any explanations, just the exact command to run.
1668
1658
  """
1669
-
1670
- # Prepare the API request payload
1671
- # print("šŸ” DEBUG: Preparing API request...")
1672
-
1673
- # Try to use GPT-4 first, but fall back to other models if needed
1659
+
1674
1660
  models_to_try = [
1675
- "gpt-4o-mini", # First choice: GPT-4o (most widely available)
1661
+ "gpt-4.1", # First choice: GPT-4o (most widely available)
1676
1662
  ]
1677
1663
 
1678
1664
  # Check if we have a preferred model in environment
@@ -1680,19 +1666,12 @@ Do not provide any explanations, just the exact command to run.
1680
1666
  if preferred_model:
1681
1667
  # Insert the preferred model at the beginning of the list
1682
1668
  models_to_try.insert(0, preferred_model)
1683
- # print(f"āœ… Using preferred model from environment: {preferred_model}")
1684
1669
 
1685
1670
  # Remove duplicates while preserving order
1686
1671
  models_to_try = list(dict.fromkeys(models_to_try))
1687
- # print(f"šŸ” DEBUG: Models to try: {models_to_try}")
1688
1672
 
1689
1673
  # Function to make the API call with a specific model
1690
1674
  def try_api_call(model_name, retries=2, backoff_factor=1.5):
1691
- # print(f"šŸ” DEBUG: Attempting API call with model: {model_name}")
1692
- # print(f"šŸ” DEBUG: API key available: {'Yes' if api_key else 'No'}")
1693
- # if api_key:
1694
- # print(f"šŸ” DEBUG: API key length: {len(api_key)}")
1695
- # print(f"šŸ” DEBUG: API key starts with: {api_key[:10]}...")
1696
1675
 
1697
1676
  payload = {
1698
1677
  "model": model_name,
@@ -1795,7 +1774,6 @@ Do not provide any explanations, just the exact command to run.
1795
1774
  for model in models_to_try:
1796
1775
  result, error = try_api_call(model)
1797
1776
  if result:
1798
- # print(f"āœ… Successfully got response from {model}")
1799
1777
  break
1800
1778
  else:
1801
1779
  print(f"āš ļø Failed to get response from {model}: {error}")
@@ -1959,7 +1937,7 @@ Provide fixes for all {len(failed_commands)} failed commands:"""
1959
1937
  }
1960
1938
 
1961
1939
  payload = {
1962
- "model": "gpt-4o-mini", # Use a more capable model for batch analysis
1940
+ "model": "gpt-4.1", # Use a more capable model for batch analysis
1963
1941
  "messages": [
1964
1942
  {"role": "system", "content": "You are a debugging assistant. Analyze failed commands and provide specific fix commands. Return only the fix commands and reasons in the specified format."},
1965
1943
  {"role": "user", "content": prompt}
@@ -2016,43 +1994,6 @@ Provide fixes for all {len(failed_commands)} failed commands:"""
2016
1994
  print(f"āŒ Error during batch debugging: {e}")
2017
1995
  return []
2018
1996
 
2019
- def prompt_for_hf_token():
2020
- """Prompt user for Hugging Face token when needed"""
2021
- # Try to use credentials manager first
2022
- try:
2023
- from credentials_manager import CredentialsManager
2024
- credentials_manager = CredentialsManager()
2025
- token = credentials_manager.get_huggingface_token()
2026
- if token:
2027
- return token
2028
- except ImportError:
2029
- # Fall back to direct input if credentials_manager is not available
2030
- pass
2031
-
2032
- # Traditional direct input method as fallback
2033
- print("\n" + "="*60)
2034
- print("šŸ”‘ HUGGING FACE TOKEN REQUIRED")
2035
- print("="*60)
2036
- print("The training script requires a valid Hugging Face token.")
2037
- print("You can get your token from: https://huggingface.co/settings/tokens")
2038
- print("šŸ“ Please paste your Hugging Face token below:")
2039
- print(" (Your input will be hidden for security)")
2040
- print("-" * 60)
2041
-
2042
- try:
2043
- token = getpass.getpass("HF Token: ").strip()
2044
- if not token:
2045
- print("āŒ No token provided.")
2046
- return None
2047
- print("āœ… Token received successfully!")
2048
- return token
2049
- except KeyboardInterrupt:
2050
- print("\nāŒ Token input cancelled by user.")
2051
- return None
2052
- except Exception as e:
2053
- print(f"āŒ Error getting token: {e}")
2054
- return None
2055
-
2056
1997
  def generate_random_password(length=16):
2057
1998
  """Generate a random password for SSH access"""
2058
1999
  alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
@@ -2067,11 +2008,6 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
2067
2008
 
2068
2009
  # Use interactive mode if specified
2069
2010
  if interactive:
2070
- # If GPU type is not specified, prompt for it
2071
- if not gpu_type:
2072
- gpu_type = prompt_for_gpu()
2073
- else:
2074
- print(f"āœ… Using provided GPU type: {gpu_type}")
2075
2011
 
2076
2012
  # If repo URL is not specified, prompt for it
2077
2013
  if not repo_url:
@@ -2748,7 +2684,6 @@ def fetch_setup_commands_from_api(repo_url):
2748
2684
  print(f"āš ļø GitIngest CLI failed with exit code {result.returncode}")
2749
2685
  print(f"āš ļø Error output: {result.stderr}")
2750
2686
  print("Falling back to basic analysis")
2751
- gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
2752
2687
  else:
2753
2688
  print(f"āœ… GitIngest analysis completed successfully")
2754
2689
 
@@ -2832,13 +2767,13 @@ def fetch_setup_commands_from_api(repo_url):
2832
2767
  print(f"šŸ“ Processed GitIngest data saved to: {processed_file}")
2833
2768
  except FileNotFoundError:
2834
2769
  print(f"āš ļø Output file not found at {output_file}")
2835
- gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
2770
+
2836
2771
  except Exception as e:
2837
2772
  print(f"āš ļø Error reading GitIngest output: {e}")
2838
- gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
2773
+
2839
2774
  else:
2840
2775
  # Fall back to basic analysis if gitingest CLI is not available
2841
- gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
2776
+ gitingest_data = "{}"
2842
2777
 
2843
2778
  # Prepare the request payload with GitIngest data
2844
2779
  payload = {
@@ -3072,171 +3007,6 @@ def generate_fallback_commands(gitingest_data):
3072
3007
 
3073
3008
  return fixed_commands
3074
3009
 
3075
- def generate_basic_repo_analysis_from_url(repo_url):
3076
- """Generate basic repository analysis data from a repository URL."""
3077
- import tempfile
3078
- import subprocess
3079
- import os
3080
- import shutil
3081
-
3082
- # Create a temporary directory for cloning
3083
- temp_dir = tempfile.mkdtemp(prefix="repo_basic_analysis_")
3084
-
3085
- try:
3086
- print(f"šŸ“„ Cloning repository to {temp_dir} for basic analysis...")
3087
- clone_result = subprocess.run(
3088
- ["git", "clone", "--depth", "1", repo_url, temp_dir],
3089
- capture_output=True,
3090
- text=True
3091
- )
3092
-
3093
- if clone_result.returncode != 0:
3094
- print(f"āŒ Failed to clone repository: {clone_result.stderr}")
3095
- return {
3096
- "system_info": {
3097
- "platform": "linux",
3098
- "python_version": "3.10",
3099
- "detected_language": "Unknown",
3100
- "detected_technologies": [],
3101
- "file_count": 0,
3102
- "repo_stars": 0,
3103
- "repo_forks": 0,
3104
- "primary_package_manager": "Unknown",
3105
- "complexity_level": "low"
3106
- },
3107
- "repository_analysis": {
3108
- "summary": f"Repository analysis for {repo_url}",
3109
- "tree": "Failed to clone repository",
3110
- "content_preview": "No content available"
3111
- },
3112
- "success": False
3113
- }
3114
-
3115
- print(f"āœ… Repository cloned successfully for basic analysis")
3116
-
3117
- # Use the existing generate_basic_repo_analysis function
3118
- return generate_basic_repo_analysis(temp_dir)
3119
- finally:
3120
- # Clean up the temporary directory
3121
- print(f"🧹 Cleaning up temporary directory for basic analysis...")
3122
- shutil.rmtree(temp_dir, ignore_errors=True)
3123
-
3124
- def generate_basic_repo_analysis(repo_dir):
3125
- """Generate basic repository analysis when GitIngest is not available."""
3126
- import os
3127
- import subprocess
3128
-
3129
- # Detect language and technologies based on file extensions
3130
- file_extensions = {}
3131
- file_count = 0
3132
-
3133
- for root, _, files in os.walk(repo_dir):
3134
- for file in files:
3135
- file_count += 1
3136
- ext = os.path.splitext(file)[1].lower()
3137
- if ext:
3138
- file_extensions[ext] = file_extensions.get(ext, 0) + 1
3139
-
3140
- # Determine primary language
3141
- language_map = {
3142
- '.py': 'Python',
3143
- '.js': 'JavaScript',
3144
- '.ts': 'TypeScript',
3145
- '.jsx': 'JavaScript',
3146
- '.tsx': 'TypeScript',
3147
- '.java': 'Java',
3148
- '.cpp': 'C++',
3149
- '.c': 'C',
3150
- '.go': 'Go',
3151
- '.rs': 'Rust',
3152
- '.rb': 'Ruby',
3153
- '.php': 'PHP',
3154
- '.swift': 'Swift',
3155
- '.kt': 'Kotlin',
3156
- '.cs': 'C#'
3157
- }
3158
-
3159
- # Count files by language
3160
- language_counts = {}
3161
- for ext, count in file_extensions.items():
3162
- if ext in language_map:
3163
- lang = language_map[ext]
3164
- language_counts[lang] = language_counts.get(lang, 0) + count
3165
-
3166
- # Determine primary language
3167
- primary_language = max(language_counts.items(), key=lambda x: x[1])[0] if language_counts else "Unknown"
3168
-
3169
- # Detect package managers
3170
- package_managers = []
3171
- package_files = {
3172
- 'requirements.txt': 'pip',
3173
- 'setup.py': 'pip',
3174
- 'pyproject.toml': 'pip',
3175
- 'package.json': 'npm',
3176
- 'yarn.lock': 'yarn',
3177
- 'pnpm-lock.yaml': 'pnpm',
3178
- 'Cargo.toml': 'cargo',
3179
- 'go.mod': 'go',
3180
- 'Gemfile': 'bundler',
3181
- 'pom.xml': 'maven',
3182
- 'build.gradle': 'gradle',
3183
- 'composer.json': 'composer'
3184
- }
3185
-
3186
- for file, manager in package_files.items():
3187
- if os.path.exists(os.path.join(repo_dir, file)):
3188
- package_managers.append(manager)
3189
-
3190
- primary_package_manager = package_managers[0] if package_managers else "Unknown"
3191
-
3192
- # Get README content
3193
- readme_content = ""
3194
- for readme_name in ['README.md', 'README', 'README.txt', 'readme.md']:
3195
- readme_path = os.path.join(repo_dir, readme_name)
3196
- if os.path.exists(readme_path):
3197
- with open(readme_path, 'r', encoding='utf-8', errors='ignore') as f:
3198
- readme_content = f.read()
3199
- break
3200
-
3201
- # Try to get repository info
3202
- repo_info = {}
3203
- try:
3204
- # Get remote origin URL
3205
- cmd = ["git", "config", "--get", "remote.origin.url"]
3206
- result = subprocess.run(cmd, cwd=repo_dir, capture_output=True, text=True)
3207
- if result.returncode == 0:
3208
- repo_info["url"] = result.stdout.strip()
3209
-
3210
- # Get commit count as a proxy for activity
3211
- cmd = ["git", "rev-list", "--count", "HEAD"]
3212
- result = subprocess.run(cmd, cwd=repo_dir, capture_output=True, text=True)
3213
- if result.returncode == 0:
3214
- repo_info["commit_count"] = int(result.stdout.strip())
3215
- except Exception:
3216
- pass
3217
-
3218
- # Build the analysis data
3219
- return {
3220
- "system_info": {
3221
- "platform": "linux", # Assuming Linux for container environment
3222
- "python_version": "3.10", # Common Python version
3223
- "detected_language": primary_language,
3224
- "detected_technologies": list(language_counts.keys()),
3225
- "file_count": file_count,
3226
- "repo_stars": repo_info.get("stars", 0),
3227
- "repo_forks": repo_info.get("forks", 0),
3228
- "primary_package_manager": primary_package_manager,
3229
- "complexity_level": "medium" # Default assumption
3230
- },
3231
- "repository_analysis": {
3232
- "summary": f"Repository analysis for {repo_dir}",
3233
- "readme_content": readme_content[:5000] if readme_content else "No README found",
3234
- "package_managers": package_managers,
3235
- "file_extensions": list(file_extensions.keys())
3236
- },
3237
- "success": True
3238
- }
3239
-
3240
3010
  def fix_setup_commands(commands):
3241
3011
  """Fix setup commands by removing placeholders and comments."""
3242
3012
  fixed_commands = []
@@ -3746,152 +3516,6 @@ def get_setup_commands_from_gitingest(repo_url):
3746
3516
  print("āŒ All API endpoints failed")
3747
3517
  return generate_fallback_commands(gitingest_data)
3748
3518
 
3749
- def prompt_for_gpu():
3750
- """
3751
- Prompt the user to select a GPU type from available options using arrow keys.
3752
- Returns the selected GPU type.
3753
- """
3754
- import sys
3755
- import tty
3756
- import termios
3757
-
3758
- print("\nšŸ”§ GPU Selection Required")
3759
- print("No GPU type was specified with --gpu flag.")
3760
- print("Please select a GPU type for your container:")
3761
-
3762
- # Define available GPU types and their specifications
3763
- gpu_specs = {
3764
- 'T4': {'gpu': 'T4', 'memory': '16GB'},
3765
- 'L4': {'gpu': 'L4', 'memory': '24GB'},
3766
- 'A10G': {'gpu': 'A10G', 'memory': '24GB'},
3767
- 'A100-40': {'gpu': 'A100-40GB', 'memory': '40GB'},
3768
- 'A100-80': {'gpu': 'A100-80GB', 'memory': '80GB'},
3769
- 'L40S': {'gpu': 'L40S', 'memory': '48GB'},
3770
- 'H100': {'gpu': 'H100', 'memory': '80GB'},
3771
- 'H200': {'gpu': 'H200', 'memory': '141GB'},
3772
- 'B200': {'gpu': 'B200', 'memory': '141GB'}
3773
- }
3774
-
3775
- # Create a list of options
3776
- options = list(gpu_specs.keys())
3777
- selected_index = 2 # Default to A10G (index 2)
3778
-
3779
- def get_key():
3780
- """Get a single keypress from the user."""
3781
- fd = sys.stdin.fileno()
3782
- old_settings = termios.tcgetattr(fd)
3783
- try:
3784
- tty.setraw(sys.stdin.fileno())
3785
- ch = sys.stdin.read(1)
3786
- if ch == '\x1b': # Escape sequence
3787
- ch2 = sys.stdin.read(1)
3788
- if ch2 == '[':
3789
- ch3 = sys.stdin.read(1)
3790
- if ch3 == 'A':
3791
- return 'UP'
3792
- elif ch3 == 'B':
3793
- return 'DOWN'
3794
- elif ch == '\r' or ch == '\n':
3795
- return 'ENTER'
3796
- elif ch == '\x03': # Ctrl+C
3797
- return 'CTRL_C'
3798
- return ch
3799
- finally:
3800
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
3801
-
3802
- def display_menu():
3803
- """Display the GPU selection menu with current selection highlighted."""
3804
- print("\nšŸ“Š Available GPU Options:")
3805
- print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
3806
- print("│ GPU Type │ VRAM │")
3807
- print("ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤")
3808
-
3809
- for i, gpu_type in enumerate(options):
3810
- specs = gpu_specs[gpu_type]
3811
- # Calculate proper spacing for alignment
3812
- number_part = f"{i+1}."
3813
- if i == selected_index:
3814
- prefix = "> "
3815
- suffix = " ←"
3816
- else:
3817
- prefix = " "
3818
- suffix = ""
3819
-
3820
- # Ensure consistent width for GPU type column
3821
- gpu_display = f"{prefix}{number_part} {gpu_type}"
3822
- gpu_padded = f"{gpu_display:<12}" # Fixed width for GPU column
3823
-
3824
- print(f"│ {gpu_padded} │ {specs['memory']:<7} │{suffix}")
3825
-
3826
- print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜")
3827
- print("Use ↑/↓ arrows to select, Enter to confirm, Ctrl+C to cancel")
3828
-
3829
- # Clear screen and show initial menu
3830
- print("\033[2J\033[H", end="") # Clear screen and move cursor to top
3831
- display_menu()
3832
-
3833
- while True:
3834
- try:
3835
- key = get_key()
3836
-
3837
- if key == 'UP':
3838
- selected_index = (selected_index - 1) % len(options)
3839
- print("\033[2J\033[H", end="") # Clear screen
3840
- display_menu()
3841
- elif key == 'DOWN':
3842
- selected_index = (selected_index + 1) % len(options)
3843
- print("\033[2J\033[H", end="") # Clear screen
3844
- display_menu()
3845
- elif key == 'ENTER':
3846
- selected_gpu = options[selected_index]
3847
- print(f"\nāœ… Selected GPU: {selected_gpu}")
3848
- return selected_gpu
3849
- elif key == 'CTRL_C':
3850
- print("\nšŸ›‘ Selection cancelled.")
3851
- sys.exit(1)
3852
-
3853
- except KeyboardInterrupt:
3854
- print("\nšŸ›‘ Selection cancelled.")
3855
- sys.exit(1)
3856
- except Exception as e:
3857
- print(f"\nāŒ Error with interactive menu: {e}")
3858
- print("šŸ”„ Falling back to simple text input...")
3859
- # Fall back to simple input method
3860
- try:
3861
- print("\nšŸ“Š Available GPU Options:")
3862
- for i, gpu_type in enumerate(options, 1):
3863
- specs = gpu_specs[gpu_type]
3864
- print(f" {i}. {gpu_type} ({specs['memory']})")
3865
- print(f" Default: A10G")
3866
-
3867
- choice = input("\nšŸ” Select GPU type (number or name, default is A10G): ").strip()
3868
- if not choice:
3869
- print("āœ… Using default GPU: A10G")
3870
- return "A10G"
3871
- if choice.isdigit():
3872
- index = int(choice) - 1
3873
- if 0 <= index < len(options):
3874
- selected = options[index]
3875
- print(f"āœ… Selected GPU: {selected}")
3876
- return selected
3877
- else:
3878
- print(f"āš ļø Invalid number. Using default: A10G")
3879
- return "A10G"
3880
- elif choice in options:
3881
- print(f"āœ… Selected GPU: {choice}")
3882
- return choice
3883
- else:
3884
- print(f"āš ļø Invalid choice '{choice}'. Using default: A10G")
3885
- return "A10G"
3886
- except KeyboardInterrupt:
3887
- print("\nšŸ›‘ Selection cancelled.")
3888
- sys.exit(1)
3889
- except Exception as fallback_error:
3890
- print(f"āŒ Error in fallback input: {fallback_error}")
3891
- print("āœ… Using default GPU: A10G")
3892
- return "A10G"
3893
-
3894
-
3895
3519
 
3896
3520
  def preprocess_commands_with_llm(setup_commands, stored_credentials, api_key=None):
3897
3521
  """
@@ -4235,11 +3859,6 @@ if __name__ == "__main__":
4235
3859
  _handle_auth_commands(auth_manager, args)
4236
3860
  sys.exit(0)
4237
3861
 
4238
- # If --list-gpus is specified, just show GPU options and exit
4239
- if args.list_gpus:
4240
- prompt_for_gpu()
4241
- sys.exit(0)
4242
-
4243
3862
  # If no arguments or only --show-examples is provided, show usage examples
4244
3863
  if len(sys.argv) == 1 or args.show_examples:
4245
3864
  show_usage_examples()
@@ -4298,7 +3917,6 @@ if __name__ == "__main__":
4298
3917
  else:
4299
3918
  print("\nšŸ“‹ No GPU type specified with --gpu flag.")
4300
3919
  print("šŸ”„ Prompting for GPU selection...")
4301
- gpu_type = prompt_for_gpu()
4302
3920
  args.gpu = gpu_type
4303
3921
 
4304
3922
  # Display configuration after GPU selection
@@ -4314,14 +3932,17 @@ if __name__ == "__main__":
4314
3932
  print("Setup Commands: Auto-detect from repository")
4315
3933
 
4316
3934
  # Confirm settings
4317
- try:
4318
- proceed = input("Proceed with these settings? (Y/n): ").strip().lower()
4319
- if proceed in ('n', 'no'):
4320
- print("šŸ›‘ Operation cancelled by user.")
3935
+ if not args.yes:
3936
+ try:
3937
+ proceed = input("Proceed with these settings? (Y/n): ").strip().lower()
3938
+ if proceed in ('n', 'no'):
3939
+ print("šŸ›‘ Operation cancelled by user.")
3940
+ sys.exit(0)
3941
+ except KeyboardInterrupt:
3942
+ print("\nšŸ›‘ Operation cancelled by user.")
4321
3943
  sys.exit(0)
4322
- except KeyboardInterrupt:
4323
- print("\nšŸ›‘ Operation cancelled by user.")
4324
- sys.exit(0)
3944
+ else:
3945
+ print("āœ… Skipping confirmation prompt (--yes flag used)")
4325
3946
 
4326
3947
  # Interactive mode or missing required arguments
4327
3948
  if args.interactive or not args.repo_url or not args.volume_name:
@@ -4491,12 +4112,8 @@ if __name__ == "__main__":
4491
4112
  interactive=args.interactive
4492
4113
  )
4493
4114
  except KeyboardInterrupt:
4494
- # print("\n\nšŸ›‘ Execution interrupted")
4495
- # print("🧹 Cleaning up resources...")
4496
4115
  cleanup_modal_token()
4497
4116
  sys.exit(1)
4498
4117
  except Exception as e:
4499
- # print(f"\nāŒ Error: {e}")
4500
- # print("🧹 Cleaning up resources...")
4501
4118
  cleanup_modal_token()
4502
4119
  sys.exit(1)
@@ -31,6 +31,7 @@ parser.add_argument('--gpu', default='A10G', help='GPU type to use')
31
31
  parser.add_argument('--repo-url', help='Repository URL')
32
32
  parser.add_argument('--volume-name', help='Volume name')
33
33
  parser.add_argument('--use-api', action='store_true', help='Use API to fetch setup commands')
34
+ parser.add_argument('--yes', action='store_true', help='Skip confirmation prompts')
34
35
 
35
36
  # Parse only known args to avoid conflicts with other arguments
36
37
  args, unknown = parser.parse_known_args()
@@ -520,7 +521,7 @@ class PersistentShell:
520
521
  client = openai.OpenAI(api_key=api_key)
521
522
 
522
523
  response = client.chat.completions.create(
523
- model="gpt-4o-mini",
524
+ model="gpt-4.1",
524
525
  messages=[
525
526
  {"role": "system", "content": "You are a helpful assistant that suggests alternative commands that don't require user input."},
526
527
  {"role": "user", "content": prompt}
@@ -573,20 +574,10 @@ class PersistentShell:
573
574
  return output.strip()
574
575
  return self.working_dir
575
576
 
576
- def get_virtual_env(self):
577
- """Get the currently activated virtual environment path."""
578
- return self.virtual_env_path
579
-
580
577
  def is_in_venv(self):
581
578
  """Check if we're currently in a virtual environment."""
582
579
  return self.virtual_env_path is not None and self.virtual_env_path != ""
583
580
 
584
- def get_venv_name(self):
585
- """Get the name of the current virtual environment if active."""
586
- if self.is_in_venv():
587
- return os.path.basename(self.virtual_env_path)
588
- return None
589
-
590
581
  def exec(self, *args, **kwargs):
591
582
  """Compatibility method to make PersistentShell work with call_openai_for_debug."""
592
583
  # Convert exec call to execute method
@@ -1128,7 +1119,7 @@ class CommandListManager:
1128
1119
  print("šŸ” Analyzing command list for optimizations...")
1129
1120
 
1130
1121
  response = client.chat.completions.create(
1131
- model="gpt-4o-mini", # Use a more capable model for this complex task
1122
+ model="gpt-4.1", # Use a more capable model for this complex task
1132
1123
  messages=[
1133
1124
  {"role": "system", "content": "You are a helpful assistant that analyzes and optimizes command lists."},
1134
1125
  {"role": "user", "content": prompt}
@@ -1246,7 +1237,6 @@ class CommandListManager:
1246
1237
  print(f"āš ļø Error analyzing command list: {e}")
1247
1238
  return False
1248
1239
 
1249
-
1250
1240
  # Import the fetch_modal_tokens module
1251
1241
  # print("šŸ”„ Fetching tokens from proxy server...")
1252
1242
  from fetch_modal_tokens import get_tokens
@@ -1666,13 +1656,9 @@ IMPORTANT GUIDELINES:
1666
1656
 
1667
1657
  Do not provide any explanations, just the exact command to run.
1668
1658
  """
1669
-
1670
- # Prepare the API request payload
1671
- # print("šŸ” DEBUG: Preparing API request...")
1672
-
1673
- # Try to use GPT-4 first, but fall back to other models if needed
1659
+
1674
1660
  models_to_try = [
1675
- "gpt-4o-mini", # First choice: GPT-4o (most widely available)
1661
+ "gpt-4.1", # First choice: GPT-4o (most widely available)
1676
1662
  ]
1677
1663
 
1678
1664
  # Check if we have a preferred model in environment
@@ -1680,19 +1666,12 @@ Do not provide any explanations, just the exact command to run.
1680
1666
  if preferred_model:
1681
1667
  # Insert the preferred model at the beginning of the list
1682
1668
  models_to_try.insert(0, preferred_model)
1683
- # print(f"āœ… Using preferred model from environment: {preferred_model}")
1684
1669
 
1685
1670
  # Remove duplicates while preserving order
1686
1671
  models_to_try = list(dict.fromkeys(models_to_try))
1687
- # print(f"šŸ” DEBUG: Models to try: {models_to_try}")
1688
1672
 
1689
1673
  # Function to make the API call with a specific model
1690
1674
  def try_api_call(model_name, retries=2, backoff_factor=1.5):
1691
- # print(f"šŸ” DEBUG: Attempting API call with model: {model_name}")
1692
- # print(f"šŸ” DEBUG: API key available: {'Yes' if api_key else 'No'}")
1693
- # if api_key:
1694
- # print(f"šŸ” DEBUG: API key length: {len(api_key)}")
1695
- # print(f"šŸ” DEBUG: API key starts with: {api_key[:10]}...")
1696
1675
 
1697
1676
  payload = {
1698
1677
  "model": model_name,
@@ -1795,7 +1774,6 @@ Do not provide any explanations, just the exact command to run.
1795
1774
  for model in models_to_try:
1796
1775
  result, error = try_api_call(model)
1797
1776
  if result:
1798
- # print(f"āœ… Successfully got response from {model}")
1799
1777
  break
1800
1778
  else:
1801
1779
  print(f"āš ļø Failed to get response from {model}: {error}")
@@ -1959,7 +1937,7 @@ Provide fixes for all {len(failed_commands)} failed commands:"""
1959
1937
  }
1960
1938
 
1961
1939
  payload = {
1962
- "model": "gpt-4o-mini", # Use a more capable model for batch analysis
1940
+ "model": "gpt-4.1", # Use a more capable model for batch analysis
1963
1941
  "messages": [
1964
1942
  {"role": "system", "content": "You are a debugging assistant. Analyze failed commands and provide specific fix commands. Return only the fix commands and reasons in the specified format."},
1965
1943
  {"role": "user", "content": prompt}
@@ -2016,43 +1994,6 @@ Provide fixes for all {len(failed_commands)} failed commands:"""
2016
1994
  print(f"āŒ Error during batch debugging: {e}")
2017
1995
  return []
2018
1996
 
2019
- def prompt_for_hf_token():
2020
- """Prompt user for Hugging Face token when needed"""
2021
- # Try to use credentials manager first
2022
- try:
2023
- from credentials_manager import CredentialsManager
2024
- credentials_manager = CredentialsManager()
2025
- token = credentials_manager.get_huggingface_token()
2026
- if token:
2027
- return token
2028
- except ImportError:
2029
- # Fall back to direct input if credentials_manager is not available
2030
- pass
2031
-
2032
- # Traditional direct input method as fallback
2033
- print("\n" + "="*60)
2034
- print("šŸ”‘ HUGGING FACE TOKEN REQUIRED")
2035
- print("="*60)
2036
- print("The training script requires a valid Hugging Face token.")
2037
- print("You can get your token from: https://huggingface.co/settings/tokens")
2038
- print("šŸ“ Please paste your Hugging Face token below:")
2039
- print(" (Your input will be hidden for security)")
2040
- print("-" * 60)
2041
-
2042
- try:
2043
- token = getpass.getpass("HF Token: ").strip()
2044
- if not token:
2045
- print("āŒ No token provided.")
2046
- return None
2047
- print("āœ… Token received successfully!")
2048
- return token
2049
- except KeyboardInterrupt:
2050
- print("\nāŒ Token input cancelled by user.")
2051
- return None
2052
- except Exception as e:
2053
- print(f"āŒ Error getting token: {e}")
2054
- return None
2055
-
2056
1997
  def generate_random_password(length=16):
2057
1998
  """Generate a random password for SSH access"""
2058
1999
  alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
@@ -2067,11 +2008,6 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
2067
2008
 
2068
2009
  # Use interactive mode if specified
2069
2010
  if interactive:
2070
- # If GPU type is not specified, prompt for it
2071
- if not gpu_type:
2072
- gpu_type = prompt_for_gpu()
2073
- else:
2074
- print(f"āœ… Using provided GPU type: {gpu_type}")
2075
2011
 
2076
2012
  # If repo URL is not specified, prompt for it
2077
2013
  if not repo_url:
@@ -2748,7 +2684,6 @@ def fetch_setup_commands_from_api(repo_url):
2748
2684
  print(f"āš ļø GitIngest CLI failed with exit code {result.returncode}")
2749
2685
  print(f"āš ļø Error output: {result.stderr}")
2750
2686
  print("Falling back to basic analysis")
2751
- gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
2752
2687
  else:
2753
2688
  print(f"āœ… GitIngest analysis completed successfully")
2754
2689
 
@@ -2832,13 +2767,13 @@ def fetch_setup_commands_from_api(repo_url):
2832
2767
  print(f"šŸ“ Processed GitIngest data saved to: {processed_file}")
2833
2768
  except FileNotFoundError:
2834
2769
  print(f"āš ļø Output file not found at {output_file}")
2835
- gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
2770
+
2836
2771
  except Exception as e:
2837
2772
  print(f"āš ļø Error reading GitIngest output: {e}")
2838
- gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
2773
+
2839
2774
  else:
2840
2775
  # Fall back to basic analysis if gitingest CLI is not available
2841
- gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
2776
+ gitingest_data = "{}"
2842
2777
 
2843
2778
  # Prepare the request payload with GitIngest data
2844
2779
  payload = {
@@ -3072,171 +3007,6 @@ def generate_fallback_commands(gitingest_data):
3072
3007
 
3073
3008
  return fixed_commands
3074
3009
 
3075
- def generate_basic_repo_analysis_from_url(repo_url):
3076
- """Generate basic repository analysis data from a repository URL."""
3077
- import tempfile
3078
- import subprocess
3079
- import os
3080
- import shutil
3081
-
3082
- # Create a temporary directory for cloning
3083
- temp_dir = tempfile.mkdtemp(prefix="repo_basic_analysis_")
3084
-
3085
- try:
3086
- print(f"šŸ“„ Cloning repository to {temp_dir} for basic analysis...")
3087
- clone_result = subprocess.run(
3088
- ["git", "clone", "--depth", "1", repo_url, temp_dir],
3089
- capture_output=True,
3090
- text=True
3091
- )
3092
-
3093
- if clone_result.returncode != 0:
3094
- print(f"āŒ Failed to clone repository: {clone_result.stderr}")
3095
- return {
3096
- "system_info": {
3097
- "platform": "linux",
3098
- "python_version": "3.10",
3099
- "detected_language": "Unknown",
3100
- "detected_technologies": [],
3101
- "file_count": 0,
3102
- "repo_stars": 0,
3103
- "repo_forks": 0,
3104
- "primary_package_manager": "Unknown",
3105
- "complexity_level": "low"
3106
- },
3107
- "repository_analysis": {
3108
- "summary": f"Repository analysis for {repo_url}",
3109
- "tree": "Failed to clone repository",
3110
- "content_preview": "No content available"
3111
- },
3112
- "success": False
3113
- }
3114
-
3115
- print(f"āœ… Repository cloned successfully for basic analysis")
3116
-
3117
- # Use the existing generate_basic_repo_analysis function
3118
- return generate_basic_repo_analysis(temp_dir)
3119
- finally:
3120
- # Clean up the temporary directory
3121
- print(f"🧹 Cleaning up temporary directory for basic analysis...")
3122
- shutil.rmtree(temp_dir, ignore_errors=True)
3123
-
3124
- def generate_basic_repo_analysis(repo_dir):
3125
- """Generate basic repository analysis when GitIngest is not available."""
3126
- import os
3127
- import subprocess
3128
-
3129
- # Detect language and technologies based on file extensions
3130
- file_extensions = {}
3131
- file_count = 0
3132
-
3133
- for root, _, files in os.walk(repo_dir):
3134
- for file in files:
3135
- file_count += 1
3136
- ext = os.path.splitext(file)[1].lower()
3137
- if ext:
3138
- file_extensions[ext] = file_extensions.get(ext, 0) + 1
3139
-
3140
- # Determine primary language
3141
- language_map = {
3142
- '.py': 'Python',
3143
- '.js': 'JavaScript',
3144
- '.ts': 'TypeScript',
3145
- '.jsx': 'JavaScript',
3146
- '.tsx': 'TypeScript',
3147
- '.java': 'Java',
3148
- '.cpp': 'C++',
3149
- '.c': 'C',
3150
- '.go': 'Go',
3151
- '.rs': 'Rust',
3152
- '.rb': 'Ruby',
3153
- '.php': 'PHP',
3154
- '.swift': 'Swift',
3155
- '.kt': 'Kotlin',
3156
- '.cs': 'C#'
3157
- }
3158
-
3159
- # Count files by language
3160
- language_counts = {}
3161
- for ext, count in file_extensions.items():
3162
- if ext in language_map:
3163
- lang = language_map[ext]
3164
- language_counts[lang] = language_counts.get(lang, 0) + count
3165
-
3166
- # Determine primary language
3167
- primary_language = max(language_counts.items(), key=lambda x: x[1])[0] if language_counts else "Unknown"
3168
-
3169
- # Detect package managers
3170
- package_managers = []
3171
- package_files = {
3172
- 'requirements.txt': 'pip',
3173
- 'setup.py': 'pip',
3174
- 'pyproject.toml': 'pip',
3175
- 'package.json': 'npm',
3176
- 'yarn.lock': 'yarn',
3177
- 'pnpm-lock.yaml': 'pnpm',
3178
- 'Cargo.toml': 'cargo',
3179
- 'go.mod': 'go',
3180
- 'Gemfile': 'bundler',
3181
- 'pom.xml': 'maven',
3182
- 'build.gradle': 'gradle',
3183
- 'composer.json': 'composer'
3184
- }
3185
-
3186
- for file, manager in package_files.items():
3187
- if os.path.exists(os.path.join(repo_dir, file)):
3188
- package_managers.append(manager)
3189
-
3190
- primary_package_manager = package_managers[0] if package_managers else "Unknown"
3191
-
3192
- # Get README content
3193
- readme_content = ""
3194
- for readme_name in ['README.md', 'README', 'README.txt', 'readme.md']:
3195
- readme_path = os.path.join(repo_dir, readme_name)
3196
- if os.path.exists(readme_path):
3197
- with open(readme_path, 'r', encoding='utf-8', errors='ignore') as f:
3198
- readme_content = f.read()
3199
- break
3200
-
3201
- # Try to get repository info
3202
- repo_info = {}
3203
- try:
3204
- # Get remote origin URL
3205
- cmd = ["git", "config", "--get", "remote.origin.url"]
3206
- result = subprocess.run(cmd, cwd=repo_dir, capture_output=True, text=True)
3207
- if result.returncode == 0:
3208
- repo_info["url"] = result.stdout.strip()
3209
-
3210
- # Get commit count as a proxy for activity
3211
- cmd = ["git", "rev-list", "--count", "HEAD"]
3212
- result = subprocess.run(cmd, cwd=repo_dir, capture_output=True, text=True)
3213
- if result.returncode == 0:
3214
- repo_info["commit_count"] = int(result.stdout.strip())
3215
- except Exception:
3216
- pass
3217
-
3218
- # Build the analysis data
3219
- return {
3220
- "system_info": {
3221
- "platform": "linux", # Assuming Linux for container environment
3222
- "python_version": "3.10", # Common Python version
3223
- "detected_language": primary_language,
3224
- "detected_technologies": list(language_counts.keys()),
3225
- "file_count": file_count,
3226
- "repo_stars": repo_info.get("stars", 0),
3227
- "repo_forks": repo_info.get("forks", 0),
3228
- "primary_package_manager": primary_package_manager,
3229
- "complexity_level": "medium" # Default assumption
3230
- },
3231
- "repository_analysis": {
3232
- "summary": f"Repository analysis for {repo_dir}",
3233
- "readme_content": readme_content[:5000] if readme_content else "No README found",
3234
- "package_managers": package_managers,
3235
- "file_extensions": list(file_extensions.keys())
3236
- },
3237
- "success": True
3238
- }
3239
-
3240
3010
  def fix_setup_commands(commands):
3241
3011
  """Fix setup commands by removing placeholders and comments."""
3242
3012
  fixed_commands = []
@@ -3746,152 +3516,6 @@ def get_setup_commands_from_gitingest(repo_url):
3746
3516
  print("āŒ All API endpoints failed")
3747
3517
  return generate_fallback_commands(gitingest_data)
3748
3518
 
3749
- def prompt_for_gpu():
3750
- """
3751
- Prompt the user to select a GPU type from available options using arrow keys.
3752
- Returns the selected GPU type.
3753
- """
3754
- import sys
3755
- import tty
3756
- import termios
3757
-
3758
- print("\nšŸ”§ GPU Selection Required")
3759
- print("No GPU type was specified with --gpu flag.")
3760
- print("Please select a GPU type for your container:")
3761
-
3762
- # Define available GPU types and their specifications
3763
- gpu_specs = {
3764
- 'T4': {'gpu': 'T4', 'memory': '16GB'},
3765
- 'L4': {'gpu': 'L4', 'memory': '24GB'},
3766
- 'A10G': {'gpu': 'A10G', 'memory': '24GB'},
3767
- 'A100-40': {'gpu': 'A100-40GB', 'memory': '40GB'},
3768
- 'A100-80': {'gpu': 'A100-80GB', 'memory': '80GB'},
3769
- 'L40S': {'gpu': 'L40S', 'memory': '48GB'},
3770
- 'H100': {'gpu': 'H100', 'memory': '80GB'},
3771
- 'H200': {'gpu': 'H200', 'memory': '141GB'},
3772
- 'B200': {'gpu': 'B200', 'memory': '141GB'}
3773
- }
3774
-
3775
- # Create a list of options
3776
- options = list(gpu_specs.keys())
3777
- selected_index = 2 # Default to A10G (index 2)
3778
-
3779
- def get_key():
3780
- """Get a single keypress from the user."""
3781
- fd = sys.stdin.fileno()
3782
- old_settings = termios.tcgetattr(fd)
3783
- try:
3784
- tty.setraw(sys.stdin.fileno())
3785
- ch = sys.stdin.read(1)
3786
- if ch == '\x1b': # Escape sequence
3787
- ch2 = sys.stdin.read(1)
3788
- if ch2 == '[':
3789
- ch3 = sys.stdin.read(1)
3790
- if ch3 == 'A':
3791
- return 'UP'
3792
- elif ch3 == 'B':
3793
- return 'DOWN'
3794
- elif ch == '\r' or ch == '\n':
3795
- return 'ENTER'
3796
- elif ch == '\x03': # Ctrl+C
3797
- return 'CTRL_C'
3798
- return ch
3799
- finally:
3800
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
3801
-
3802
- def display_menu():
3803
- """Display the GPU selection menu with current selection highlighted."""
3804
- print("\nšŸ“Š Available GPU Options:")
3805
- print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
3806
- print("│ GPU Type │ VRAM │")
3807
- print("ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤")
3808
-
3809
- for i, gpu_type in enumerate(options):
3810
- specs = gpu_specs[gpu_type]
3811
- # Calculate proper spacing for alignment
3812
- number_part = f"{i+1}."
3813
- if i == selected_index:
3814
- prefix = "> "
3815
- suffix = " ←"
3816
- else:
3817
- prefix = " "
3818
- suffix = ""
3819
-
3820
- # Ensure consistent width for GPU type column
3821
- gpu_display = f"{prefix}{number_part} {gpu_type}"
3822
- gpu_padded = f"{gpu_display:<12}" # Fixed width for GPU column
3823
-
3824
- print(f"│ {gpu_padded} │ {specs['memory']:<7} │{suffix}")
3825
-
3826
- print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜")
3827
- print("Use ↑/↓ arrows to select, Enter to confirm, Ctrl+C to cancel")
3828
-
3829
- # Clear screen and show initial menu
3830
- print("\033[2J\033[H", end="") # Clear screen and move cursor to top
3831
- display_menu()
3832
-
3833
- while True:
3834
- try:
3835
- key = get_key()
3836
-
3837
- if key == 'UP':
3838
- selected_index = (selected_index - 1) % len(options)
3839
- print("\033[2J\033[H", end="") # Clear screen
3840
- display_menu()
3841
- elif key == 'DOWN':
3842
- selected_index = (selected_index + 1) % len(options)
3843
- print("\033[2J\033[H", end="") # Clear screen
3844
- display_menu()
3845
- elif key == 'ENTER':
3846
- selected_gpu = options[selected_index]
3847
- print(f"\nāœ… Selected GPU: {selected_gpu}")
3848
- return selected_gpu
3849
- elif key == 'CTRL_C':
3850
- print("\nšŸ›‘ Selection cancelled.")
3851
- sys.exit(1)
3852
-
3853
- except KeyboardInterrupt:
3854
- print("\nšŸ›‘ Selection cancelled.")
3855
- sys.exit(1)
3856
- except Exception as e:
3857
- print(f"\nāŒ Error with interactive menu: {e}")
3858
- print("šŸ”„ Falling back to simple text input...")
3859
- # Fall back to simple input method
3860
- try:
3861
- print("\nšŸ“Š Available GPU Options:")
3862
- for i, gpu_type in enumerate(options, 1):
3863
- specs = gpu_specs[gpu_type]
3864
- print(f" {i}. {gpu_type} ({specs['memory']})")
3865
- print(f" Default: A10G")
3866
-
3867
- choice = input("\nšŸ” Select GPU type (number or name, default is A10G): ").strip()
3868
- if not choice:
3869
- print("āœ… Using default GPU: A10G")
3870
- return "A10G"
3871
- if choice.isdigit():
3872
- index = int(choice) - 1
3873
- if 0 <= index < len(options):
3874
- selected = options[index]
3875
- print(f"āœ… Selected GPU: {selected}")
3876
- return selected
3877
- else:
3878
- print(f"āš ļø Invalid number. Using default: A10G")
3879
- return "A10G"
3880
- elif choice in options:
3881
- print(f"āœ… Selected GPU: {choice}")
3882
- return choice
3883
- else:
3884
- print(f"āš ļø Invalid choice '{choice}'. Using default: A10G")
3885
- return "A10G"
3886
- except KeyboardInterrupt:
3887
- print("\nšŸ›‘ Selection cancelled.")
3888
- sys.exit(1)
3889
- except Exception as fallback_error:
3890
- print(f"āŒ Error in fallback input: {fallback_error}")
3891
- print("āœ… Using default GPU: A10G")
3892
- return "A10G"
3893
-
3894
-
3895
3519
 
3896
3520
  def preprocess_commands_with_llm(setup_commands, stored_credentials, api_key=None):
3897
3521
  """
@@ -4198,7 +3822,6 @@ if __name__ == "__main__":
4198
3822
  parser = argparse.ArgumentParser()
4199
3823
  parser.add_argument('--gpu', type=str, help='GPU type (e.g., A10G, T4, A100-80GB). If not provided, will prompt for GPU selection.')
4200
3824
  parser.add_argument('--repo-url', type=str, help='Repository URL to clone')
4201
- parser.add_argument('--repo-name', type=str, help='Repository name override')
4202
3825
  parser.add_argument('--setup-commands', type=str, nargs='+', help='Setup commands to run (deprecated)')
4203
3826
  parser.add_argument('--setup-commands-json', type=str, help='Setup commands as JSON array')
4204
3827
  parser.add_argument('--commands-file', type=str, help='Path to file containing setup commands (one per line)')
@@ -4235,11 +3858,6 @@ if __name__ == "__main__":
4235
3858
  _handle_auth_commands(auth_manager, args)
4236
3859
  sys.exit(0)
4237
3860
 
4238
- # If --list-gpus is specified, just show GPU options and exit
4239
- if args.list_gpus:
4240
- prompt_for_gpu()
4241
- sys.exit(0)
4242
-
4243
3861
  # If no arguments or only --show-examples is provided, show usage examples
4244
3862
  if len(sys.argv) == 1 or args.show_examples:
4245
3863
  show_usage_examples()
@@ -4298,7 +3916,6 @@ if __name__ == "__main__":
4298
3916
  else:
4299
3917
  print("\nšŸ“‹ No GPU type specified with --gpu flag.")
4300
3918
  print("šŸ”„ Prompting for GPU selection...")
4301
- gpu_type = prompt_for_gpu()
4302
3919
  args.gpu = gpu_type
4303
3920
 
4304
3921
  # Display configuration after GPU selection
@@ -4314,14 +3931,17 @@ if __name__ == "__main__":
4314
3931
  print("Setup Commands: Auto-detect from repository")
4315
3932
 
4316
3933
  # Confirm settings
4317
- try:
4318
- proceed = input("Proceed with these settings? (Y/n): ").strip().lower()
4319
- if proceed in ('n', 'no'):
4320
- print("šŸ›‘ Operation cancelled by user.")
3934
+ if not args.yes:
3935
+ try:
3936
+ proceed = input("Proceed with these settings? (Y/n): ").strip().lower()
3937
+ if proceed in ('n', 'no'):
3938
+ print("šŸ›‘ Operation cancelled by user.")
3939
+ sys.exit(0)
3940
+ except KeyboardInterrupt:
3941
+ print("\nšŸ›‘ Operation cancelled by user.")
4321
3942
  sys.exit(0)
4322
- except KeyboardInterrupt:
4323
- print("\nšŸ›‘ Operation cancelled by user.")
4324
- sys.exit(0)
3943
+ else:
3944
+ print("āœ… Skipping confirmation prompt (--yes flag used)")
4325
3945
 
4326
3946
  # Interactive mode or missing required arguments
4327
3947
  if args.interactive or not args.repo_url or not args.volume_name:
package/eval_repos.py DELETED
@@ -1 +0,0 @@
1
-