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.
Files changed (37) hide show
  1. package/.venv_status.json +1 -1
  2. package/README.md +29 -0
  3. package/bin/gitarsenal.js +220 -127
  4. package/kill_claude/requirements.txt +1 -1
  5. package/lib/dependencies.js +130 -4
  6. package/lib/e2b-sandbox.js +158 -0
  7. package/lib/execAsync.js +12 -0
  8. package/lib/sandbox.js +97 -113
  9. package/package.json +2 -1
  10. package/python/__pycache__/credentials_manager.cpython-312.pyc +0 -0
  11. package/python/__pycache__/e2b_sandbox_agent.cpython-313.pyc +0 -0
  12. package/python/__pycache__/fetch_modal_tokens.cpython-312.pyc +0 -0
  13. package/python/credentials_manager.py +2 -1
  14. package/python/e2b_sandbox_agent.py +787 -0
  15. package/python/fetch_modal_tokens.py +47 -25
  16. package/python/requirements.txt +2 -1
  17. package/python/test_enhanced_sandbox_script.py +1429 -0
  18. package/python/test_modalSandboxScript.py +41 -5
  19. package/scripts/setup_e2b.js +162 -0
  20. package/kill_claude/.claude/settings.local.json +0 -9
  21. package/kill_claude/__pycache__/bash_output_tool.cpython-313.pyc +0 -0
  22. package/kill_claude/__pycache__/bash_tool.cpython-313.pyc +0 -0
  23. package/kill_claude/__pycache__/claude_code_agent.cpython-313.pyc +0 -0
  24. package/kill_claude/__pycache__/edit_tool.cpython-313.pyc +0 -0
  25. package/kill_claude/__pycache__/exit_plan_mode_tool.cpython-313.pyc +0 -0
  26. package/kill_claude/__pycache__/glob_tool.cpython-313.pyc +0 -0
  27. package/kill_claude/__pycache__/grep_tool.cpython-313.pyc +0 -0
  28. package/kill_claude/__pycache__/kill_bash_tool.cpython-313.pyc +0 -0
  29. package/kill_claude/__pycache__/ls_tool.cpython-313.pyc +0 -0
  30. package/kill_claude/__pycache__/multiedit_tool.cpython-313.pyc +0 -0
  31. package/kill_claude/__pycache__/notebook_edit_tool.cpython-313.pyc +0 -0
  32. package/kill_claude/__pycache__/read_tool.cpython-313.pyc +0 -0
  33. package/kill_claude/__pycache__/task_tool.cpython-313.pyc +0 -0
  34. package/kill_claude/__pycache__/todo_write_tool.cpython-313.pyc +0 -0
  35. package/kill_claude/__pycache__/web_fetch_tool.cpython-313.pyc +0 -0
  36. package/kill_claude/__pycache__/web_search_tool.cpython-313.pyc +0 -0
  37. package/kill_claude/__pycache__/write_tool.cpython-313.pyc +0 -0
@@ -1,3 +1,11 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Modal Sandbox Script for GitArsenal
5
+ --------------------------------
6
+ This script creates a Modal sandbox and sets up a repository.
7
+ """
8
+
1
9
  import os
2
10
  import sys
3
11
  import time
@@ -10,7 +18,15 @@ import secrets
10
18
  import string
11
19
  import argparse
12
20
  from pathlib import Path
13
- import modal
21
+
22
+ # Try to import modal, but don't attempt to install it
23
+ try:
24
+ import modal
25
+ except ImportError:
26
+ print("āŒ Error: Modal package not found.")
27
+ print("Please install the required packages manually with:")
28
+ print("pip install modal python-dotenv e2b-code-interpreter")
29
+ sys.exit(1)
14
30
 
15
31
 
16
32
  def generate_random_password(length=16):
@@ -284,7 +300,7 @@ def ssh_container_function(ssh_password=None, repo_url=None, repo_name=None, set
284
300
 
285
301
  # Call Agent directly as subprocess with real-time output
286
302
  claude_prompt = f"clone, setup and run {repo_url}. At the end of the setup process, print the ordered list of every shell command that actually ran successfully (exclude any commands that returned non-zero exit codes). Show each command exactly as executed, one per line."
287
- print(f"šŸš€ Executing the task: \"{claude_prompt}\"")
303
+ # print(f"šŸš€ Executing the task: \"{claude_prompt}\"")
288
304
  print("\n" + "="*60)
289
305
  print("šŸŽ‰ AGENT OUTPUT (LIVE)")
290
306
  print("="*60)
@@ -834,12 +850,24 @@ def show_usage_examples():
834
850
  print("│ gitarsenal --auth # Interactive auth management │")
835
851
  print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
836
852
 
853
+ print("Sandbox Provider Options")
854
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
855
+ print("│ gitarsenal --sandbox-provider modal # Use Modal sandbox (default, GPU support) │")
856
+ print("│ gitarsenal --sandbox-provider e2b # Use E2B sandbox (faster startup) │")
857
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
858
+
837
859
  print("Basic Container Creation with Agent")
838
860
  print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
839
861
  print("│ gitarsenal --gpu A10G --repo https://github.com/username/repo.git │")
840
862
  print("│ # Agent will intelligently clone and setup the repository │")
841
863
  print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
842
864
 
865
+ print("With E2B Sandbox Provider")
866
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
867
+ print("│ gitarsenal --sandbox-provider e2b --repo https://github.com/username/repo.git │")
868
+ print("│ # Uses E2B sandbox instead of Modal (no GPU but faster startup) │")
869
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
870
+
843
871
  print("With Persistent Storage")
844
872
  print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
845
873
  print("│ gitarsenal --gpu A10G --repo https://github.com/username/repo.git \\ │")
@@ -873,6 +901,10 @@ def show_usage_examples():
873
901
  print(" T4, L4, A10G, A100-40GB, A100-80GB, L40S, H100, H200, B200")
874
902
  print(" Use --gpu-count to specify multiple GPUs (1-8)")
875
903
  print()
904
+ print("Sandbox Provider Options:")
905
+ print(" modal: GPU support, persistent volumes (default)")
906
+ print(" e2b: Faster startup, no GPU support")
907
+ print()
876
908
  print("Authentication Behavior:")
877
909
  print(" • First time: Interactive registration/login required")
878
910
  print(" • Subsequent runs: Automatic login with stored session")
@@ -881,6 +913,7 @@ def show_usage_examples():
881
913
  print("GPU Selection Behavior:")
882
914
  print(" • With --gpu: Uses specified GPU without prompting")
883
915
  print(" • Without --gpu: Shows interactive GPU selection menu")
916
+ print(" • Not applicable for E2B sandbox provider")
884
917
  print()
885
918
  print("Repository Setup Behavior:")
886
919
  print(" • With --repo Agent intelligently clones and sets up repository")
@@ -888,9 +921,12 @@ def show_usage_examples():
888
921
  print(" • Legacy --setup-commands: Only used when --repo not provided")
889
922
  print()
890
923
  print("Examples:")
891
- print(" # Intelligent repository setup (recommended):")
924
+ print(" # Intelligent repository setup with Modal (recommended):")
892
925
  print(" gitarsenal --gpu A10G --repo https://github.com/username/repo.git")
893
926
  print()
927
+ print(" # Using E2B sandbox provider:")
928
+ print(" gitarsenal --sandbox-provider e2b --repo https://github.com/username/repo.git")
929
+ print()
894
930
  print(" # Development mode (skip authentication):")
895
931
  print(" gitarsenal --skip-auth --gpu A10G --repo https://github.com/username/repo.git")
896
932
  print()
@@ -1099,7 +1135,7 @@ if __name__ == "__main__":
1099
1135
 
1100
1136
  # Initialize tokens (import here to avoid container import issues)
1101
1137
  from fetch_modal_tokens import get_tokens
1102
- token_id, token_secret, openai_api_key, anthropic_api_key, openrouter_api_key, groq_api_key = get_tokens()
1138
+ token_id, token_secret, openai_api_key, e2b_api_key, anthropic_api_key, openrouter_api_key, groq_api_key = get_tokens()
1103
1139
 
1104
1140
  # Check if we got valid tokens
1105
1141
  if token_id is None or token_secret is None:
@@ -1383,7 +1419,7 @@ if __name__ == "__main__":
1383
1419
  else:
1384
1420
  ssh_password = generate_random_password()
1385
1421
  print(f"šŸ”‘ Generated random SSH password: {ssh_password}")
1386
-
1422
+
1387
1423
  # Extract repository name from URL if not provided
1388
1424
  repo_name = args.repo_name
1389
1425
  if not repo_name and args.repo_url:
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const inquirer = require('inquirer');
6
+ const chalk = require('chalk');
7
+ const os = require('os');
8
+
9
+ async function setupE2B() {
10
+ console.log(chalk.blue('šŸ”§ E2B Sandbox Setup'));
11
+ console.log(chalk.gray('This script will help you set up the E2B API key for GitArsenal.'));
12
+ console.log(chalk.gray('You can get your API key by signing up at https://e2b.dev'));
13
+ console.log();
14
+
15
+ // Check if E2B_API_KEY is already set in environment
16
+ const existingKey = process.env.E2B_API_KEY;
17
+ if (existingKey) {
18
+ console.log(chalk.green('āœ… E2B API key is already set in your environment.'));
19
+ console.log(chalk.gray(`Key: ${existingKey.substring(0, 4)}...${existingKey.substring(existingKey.length - 4)}`));
20
+
21
+ const { updateKey } = await inquirer.prompt([
22
+ {
23
+ type: 'confirm',
24
+ name: 'updateKey',
25
+ message: 'Do you want to update your E2B API key?',
26
+ default: false
27
+ }
28
+ ]);
29
+
30
+ if (!updateKey) {
31
+ console.log(chalk.green('āœ… Using existing E2B API key.'));
32
+ return;
33
+ }
34
+ }
35
+
36
+ // Prompt for E2B API key
37
+ const { apiKey } = await inquirer.prompt([
38
+ {
39
+ type: 'password',
40
+ name: 'apiKey',
41
+ message: 'Enter your E2B API key:',
42
+ mask: '*',
43
+ validate: (input) => {
44
+ if (!input.trim()) {
45
+ return 'API key is required';
46
+ }
47
+ return true;
48
+ }
49
+ }
50
+ ]);
51
+
52
+ // Determine where to store the API key
53
+ const { storeLocation } = await inquirer.prompt([
54
+ {
55
+ type: 'list',
56
+ name: 'storeLocation',
57
+ message: 'Where would you like to store the API key?',
58
+ choices: [
59
+ { name: 'Environment variable (this session only)', value: 'env' },
60
+ { name: '.env file in current directory', value: 'local' },
61
+ { name: '.env file in home directory', value: 'home' }
62
+ ],
63
+ default: 'env'
64
+ }
65
+ ]);
66
+
67
+ // Store the API key based on user's choice
68
+ switch (storeLocation) {
69
+ case 'env':
70
+ process.env.E2B_API_KEY = apiKey;
71
+ console.log(chalk.green('āœ… E2B API key set for this session.'));
72
+ console.log(chalk.yellow('āš ļø This setting will be lost when you close your terminal.'));
73
+ console.log(chalk.gray('To make it permanent, add the following to your shell profile:'));
74
+ console.log(chalk.cyan(`export E2B_API_KEY="${apiKey}"`));
75
+ break;
76
+
77
+ case 'local':
78
+ try {
79
+ const envFile = path.join(process.cwd(), '.env');
80
+ let content = '';
81
+
82
+ if (fs.existsSync(envFile)) {
83
+ content = fs.readFileSync(envFile, 'utf8');
84
+ // Replace existing E2B_API_KEY if it exists
85
+ if (content.match(/^E2B_API_KEY=.*/m)) {
86
+ content = content.replace(/^E2B_API_KEY=.*/m, `E2B_API_KEY="${apiKey}"`);
87
+ } else {
88
+ content += `\nE2B_API_KEY="${apiKey}"\n`;
89
+ }
90
+ } else {
91
+ content = `E2B_API_KEY="${apiKey}"\n`;
92
+ }
93
+
94
+ fs.writeFileSync(envFile, content);
95
+ console.log(chalk.green(`āœ… E2B API key saved to ${envFile}`));
96
+ } catch (error) {
97
+ console.error(chalk.red(`āŒ Failed to save API key to .env file: ${error.message}`));
98
+ }
99
+ break;
100
+
101
+ case 'home':
102
+ try {
103
+ const homeDir = os.homedir();
104
+ const envFile = path.join(homeDir, '.env');
105
+ let content = '';
106
+
107
+ if (fs.existsSync(envFile)) {
108
+ content = fs.readFileSync(envFile, 'utf8');
109
+ // Replace existing E2B_API_KEY if it exists
110
+ if (content.match(/^E2B_API_KEY=.*/m)) {
111
+ content = content.replace(/^E2B_API_KEY=.*/m, `E2B_API_KEY="${apiKey}"`);
112
+ } else {
113
+ content += `\nE2B_API_KEY="${apiKey}"\n`;
114
+ }
115
+ } else {
116
+ content = `E2B_API_KEY="${apiKey}"\n`;
117
+ }
118
+
119
+ fs.writeFileSync(envFile, content);
120
+ console.log(chalk.green(`āœ… E2B API key saved to ${envFile}`));
121
+ } catch (error) {
122
+ console.error(chalk.red(`āŒ Failed to save API key to home .env file: ${error.message}`));
123
+ }
124
+ break;
125
+ }
126
+
127
+ // Also store in GitArsenal credentials
128
+ try {
129
+ const gitarsenalDir = path.join(os.homedir(), '.gitarsenal');
130
+ if (!fs.existsSync(gitarsenalDir)) {
131
+ fs.mkdirSync(gitarsenalDir, { recursive: true });
132
+ }
133
+
134
+ const credentialsFile = path.join(gitarsenalDir, 'credentials.json');
135
+ let credentials = {};
136
+
137
+ if (fs.existsSync(credentialsFile)) {
138
+ try {
139
+ credentials = JSON.parse(fs.readFileSync(credentialsFile, 'utf8'));
140
+ } catch (error) {
141
+ console.log(chalk.yellow(`āš ļø Could not read existing credentials file: ${error.message}`));
142
+ }
143
+ }
144
+
145
+ credentials['e2b'] = apiKey;
146
+ fs.writeFileSync(credentialsFile, JSON.stringify(credentials, null, 2));
147
+ console.log(chalk.green('āœ… E2B API key also saved to GitArsenal credentials.'));
148
+
149
+ } catch (error) {
150
+ console.error(chalk.red(`āŒ Failed to save API key to GitArsenal credentials: ${error.message}`));
151
+ }
152
+
153
+ console.log(chalk.green('\nšŸŽ‰ E2B setup complete! You can now use the E2B sandbox provider.'));
154
+ console.log(chalk.cyan('To use E2B sandbox, run:'));
155
+ console.log(chalk.white(' gitarsenal --sandbox-provider e2b --repo <repository-url>'));
156
+ }
157
+
158
+ // Run the setup
159
+ setupE2B().catch(error => {
160
+ console.error(chalk.red(`āŒ Error: ${error.message}`));
161
+ process.exit(1);
162
+ });
@@ -1,9 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(python test:*)",
5
- "Bash(python:*)"
6
- ],
7
- "defaultMode": "acceptEdits"
8
- }
9
- }