gitarsenal-cli 1.1.20 → 1.1.22

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitarsenal-cli",
3
- "version": "1.1.20",
3
+ "version": "1.1.22",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -9,6 +9,7 @@ import os
9
9
  import sys
10
10
  import json
11
11
  import requests
12
+ import subprocess
12
13
  from pathlib import Path
13
14
 
14
15
  # Default tokens to use if we can't fetch from the server
@@ -29,17 +30,23 @@ def fetch_tokens_from_proxy(proxy_url=None, api_key=None):
29
30
  # Use environment variables if not provided
30
31
  if not proxy_url:
31
32
  proxy_url = os.environ.get("MODAL_PROXY_URL")
33
+ if proxy_url:
34
+ print(f"šŸ“‹ Using proxy URL from environment: {proxy_url}")
32
35
 
33
36
  if not api_key:
34
37
  api_key = os.environ.get("MODAL_PROXY_API_KEY")
38
+ if api_key:
39
+ print(f"šŸ“‹ Using API key from environment (length: {len(api_key)})")
35
40
 
36
41
  # Check if we have the necessary information
37
42
  if not proxy_url:
38
43
  print("āŒ No proxy URL provided or found in environment")
44
+ print("šŸ’” Set MODAL_PROXY_URL environment variable or use --proxy-url argument")
39
45
  return None, None
40
46
 
41
47
  if not api_key:
42
48
  print("āŒ No API key provided or found in environment")
49
+ print("šŸ’” Set MODAL_PROXY_API_KEY environment variable or use --proxy-api-key argument")
43
50
  return None, None
44
51
 
45
52
  # Ensure the URL ends with a slash
@@ -79,6 +86,7 @@ def fetch_tokens_from_proxy(proxy_url=None, api_key=None):
79
86
  def get_tokens():
80
87
  """
81
88
  Get Modal tokens, trying to fetch from the proxy server first.
89
+ Also sets the tokens in environment variables.
82
90
 
83
91
  Returns:
84
92
  tuple: (token_id, token_secret)
@@ -89,15 +97,48 @@ def get_tokens():
89
97
  # If we couldn't fetch from the server, use the default tokens
90
98
  if not token_id or not token_secret:
91
99
  print("āš ļø Using default tokens")
92
- return DEFAULT_TOKEN_ID, DEFAULT_TOKEN_SECRET
100
+ token_id = DEFAULT_TOKEN_ID
101
+ token_secret = DEFAULT_TOKEN_SECRET
102
+
103
+ # Set the tokens in environment variables
104
+ os.environ["MODAL_TOKEN_ID"] = token_id
105
+ os.environ["MODAL_TOKEN_SECRET"] = token_secret
106
+ print(f"āœ… Set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables")
93
107
 
94
108
  return token_id, token_secret
95
109
 
96
110
  if __name__ == "__main__":
111
+ # Parse command-line arguments if run directly
112
+ import argparse
113
+
114
+ parser = argparse.ArgumentParser(description='Fetch Modal tokens from the proxy server')
115
+ parser.add_argument('--proxy-url', help='URL of the proxy server')
116
+ parser.add_argument('--proxy-api-key', help='API key for the proxy server')
117
+ args = parser.parse_args()
118
+
119
+ # Set proxy URL and API key in environment variables if provided
120
+ if args.proxy_url:
121
+ os.environ["MODAL_PROXY_URL"] = args.proxy_url
122
+ print(f"āœ… Set MODAL_PROXY_URL from command line: {args.proxy_url}")
123
+
124
+ if args.proxy_api_key:
125
+ os.environ["MODAL_PROXY_API_KEY"] = args.proxy_api_key
126
+ print(f"āœ… Set MODAL_PROXY_API_KEY from command line")
127
+
128
+ # Get tokens
97
129
  token_id, token_secret = get_tokens()
98
130
  print(f"Token ID: {token_id}")
99
131
  print(f"Token Secret: {token_secret}")
100
132
 
133
+ # Check if tokens are set in environment variables
134
+ print(f"\nšŸ” DEBUG: Checking environment variables")
135
+ print(f"šŸ” MODAL_TOKEN_ID exists: {'Yes' if os.environ.get('MODAL_TOKEN_ID') else 'No'}")
136
+ print(f"šŸ” MODAL_TOKEN_SECRET exists: {'Yes' if os.environ.get('MODAL_TOKEN_SECRET') else 'No'}")
137
+ if os.environ.get('MODAL_TOKEN_ID'):
138
+ print(f"šŸ” MODAL_TOKEN_ID length: {len(os.environ.get('MODAL_TOKEN_ID'))}")
139
+ if os.environ.get('MODAL_TOKEN_SECRET'):
140
+ print(f"šŸ” MODAL_TOKEN_SECRET length: {len(os.environ.get('MODAL_TOKEN_SECRET'))}")
141
+
101
142
  # Write the tokens to a file for use by other scripts
102
143
  tokens_file = Path(__file__).parent / "modal_tokens.json"
103
144
  with open(tokens_file, 'w') as f:
@@ -105,4 +146,38 @@ if __name__ == "__main__":
105
146
  "token_id": token_id,
106
147
  "token_secret": token_secret
107
148
  }, f)
108
- print(f"āœ… Tokens written to {tokens_file}")
149
+ print(f"\nāœ… Tokens written to {tokens_file}")
150
+
151
+ # Create token files in standard locations
152
+ modal_dir = Path.home() / ".modal"
153
+ modal_dir.mkdir(exist_ok=True)
154
+ token_file = modal_dir / "token.json"
155
+ with open(token_file, 'w') as f:
156
+ json.dump({
157
+ "token_id": token_id,
158
+ "token_secret": token_secret
159
+ }, f)
160
+ print(f"āœ… Created token file at {token_file}")
161
+
162
+ modalconfig_file = Path.home() / ".modalconfig"
163
+ with open(modalconfig_file, 'w') as f:
164
+ f.write(f"token_id = {token_id}\n")
165
+ f.write(f"token_secret = {token_secret}\n")
166
+ print(f"āœ… Created .modalconfig file at {modalconfig_file}")
167
+
168
+ # Try to use the Modal CLI to set the token
169
+ try:
170
+ print(f"\nšŸ”„ Setting token via Modal CLI...")
171
+ result = subprocess.run(
172
+ ["modal", "token", "set", "--token-id", token_id, "--token-secret", token_secret, "--profile=fr8mafia", "--no-verify"],
173
+ capture_output=True, text=True
174
+ )
175
+
176
+ if result.returncode == 0:
177
+ print(f"āœ… Successfully set token via Modal CLI")
178
+ else:
179
+ print(f"āŒ Failed to set token via Modal CLI: {result.stderr}")
180
+ except Exception as e:
181
+ print(f"āŒ Error using Modal CLI: {e}")
182
+
183
+ print(f"\nāœ… All token setup completed successfully")
@@ -9,8 +9,30 @@ import getpass
9
9
  import requests
10
10
  import secrets
11
11
  import string
12
+ import argparse
12
13
  from pathlib import Path
13
14
 
15
+ # Parse command-line arguments
16
+ parser = argparse.ArgumentParser(description='Launch a Modal sandbox')
17
+ parser.add_argument('--proxy-url', help='URL of the proxy server')
18
+ parser.add_argument('--proxy-api-key', help='API key for the proxy server')
19
+ parser.add_argument('--gpu', default='A10G', help='GPU type to use')
20
+ parser.add_argument('--repo-url', help='Repository URL')
21
+ parser.add_argument('--volume-name', help='Volume name')
22
+ parser.add_argument('--use-api', action='store_true', help='Use API to fetch setup commands')
23
+
24
+ # Parse only known args to avoid conflicts with other arguments
25
+ args, unknown = parser.parse_known_args()
26
+
27
+ # Set proxy URL and API key in environment variables if provided
28
+ if args.proxy_url:
29
+ os.environ["MODAL_PROXY_URL"] = args.proxy_url
30
+ print(f"āœ… Set MODAL_PROXY_URL from command line: {args.proxy_url}")
31
+
32
+ if args.proxy_api_key:
33
+ os.environ["MODAL_PROXY_API_KEY"] = args.proxy_api_key
34
+ print(f"āœ… Set MODAL_PROXY_API_KEY from command line")
35
+
14
36
  # First, try to fetch tokens from the proxy server
15
37
  try:
16
38
  # Import the fetch_modal_tokens module
@@ -19,6 +41,13 @@ try:
19
41
  token_id, token_secret = get_tokens()
20
42
  print(f"āœ… Modal tokens fetched successfully")
21
43
 
44
+ # Explicitly set the environment variables again to be sure
45
+ os.environ["MODAL_TOKEN_ID"] = token_id
46
+ os.environ["MODAL_TOKEN_SECRET"] = token_secret
47
+
48
+ # Also set the old environment variable for backward compatibility
49
+ os.environ["MODAL_TOKEN"] = token_id
50
+
22
51
  # Set token variables for later use
23
52
  token = token_id # For backward compatibility
24
53
  except Exception as e:
@@ -77,9 +106,12 @@ except Exception as e:
77
106
  # Print debug info
78
107
  print(f"šŸ” DEBUG: Checking environment variables")
79
108
  print(f"šŸ” MODAL_TOKEN_ID exists: {'Yes' if os.environ.get('MODAL_TOKEN_ID') else 'No'}")
109
+ print(f"šŸ” MODAL_TOKEN_SECRET exists: {'Yes' if os.environ.get('MODAL_TOKEN_SECRET') else 'No'}")
80
110
  print(f"šŸ” MODAL_TOKEN exists: {'Yes' if os.environ.get('MODAL_TOKEN') else 'No'}")
81
111
  if os.environ.get('MODAL_TOKEN_ID'):
82
112
  print(f"šŸ” MODAL_TOKEN_ID length: {len(os.environ.get('MODAL_TOKEN_ID'))}")
113
+ if os.environ.get('MODAL_TOKEN_SECRET'):
114
+ print(f"šŸ” MODAL_TOKEN_SECRET length: {len(os.environ.get('MODAL_TOKEN_SECRET'))}")
83
115
  if os.environ.get('MODAL_TOKEN'):
84
116
  print(f"šŸ” MODAL_TOKEN length: {len(os.environ.get('MODAL_TOKEN'))}")
85
117
  print(f"āœ… Modal token setup completed")
@@ -2462,7 +2494,7 @@ def fetch_setup_commands_from_api(repo_url):
2462
2494
  import shutil
2463
2495
  import json
2464
2496
 
2465
- api_url = "http://git-arsenal.vercel.app/api/analyze-with-gitingest"
2497
+ api_url = "https://git-arsenal.vercel.app/api/analyze-with-gitingest"
2466
2498
 
2467
2499
  print(f"šŸ” Fetching setup commands from API for repository: {repo_url}")
2468
2500
 
@@ -2573,11 +2605,32 @@ def fetch_setup_commands_from_api(repo_url):
2573
2605
  commands = data["setupInstructions"]["commands"]
2574
2606
  print(f"āœ… Successfully fetched {len(commands)} setup commands from API")
2575
2607
 
2576
- # Print the commands for reference
2608
+ # Print the original commands for reference
2609
+ print("šŸ“‹ Original commands from API:")
2577
2610
  for i, cmd in enumerate(commands, 1):
2578
2611
  print(f" {i}. {cmd}")
2579
2612
 
2580
- return commands
2613
+ # Fix the commands by removing placeholders and comments
2614
+ fixed_commands = fix_setup_commands(commands)
2615
+
2616
+ # If we have a temp_dir with the cloned repo, try to find the entry point
2617
+ # and replace any placeholder entry points
2618
+ for i, cmd in enumerate(fixed_commands):
2619
+ if "python main.py" in cmd or "python3 main.py" in cmd:
2620
+ try:
2621
+ entry_point = find_entry_point(temp_dir)
2622
+ if entry_point and entry_point != "main.py":
2623
+ fixed_commands[i] = cmd.replace("main.py", entry_point)
2624
+ print(f"šŸ”„ Replaced main.py with detected entry point: {entry_point}")
2625
+ except Exception as e:
2626
+ print(f"āš ļø Error finding entry point: {e}")
2627
+
2628
+ # Print the fixed commands
2629
+ print("\nšŸ“‹ Fixed commands:")
2630
+ for i, cmd in enumerate(fixed_commands, 1):
2631
+ print(f" {i}. {cmd}")
2632
+
2633
+ return fixed_commands
2581
2634
  else:
2582
2635
  print("āš ļø API response did not contain setupInstructions.commands field")
2583
2636
  print("šŸ“‹ Available fields in response:")
@@ -2723,11 +2776,14 @@ def generate_fallback_commands(gitingest_data):
2723
2776
  # Combine all commands
2724
2777
  all_commands = default_commands + language_commands
2725
2778
 
2779
+ # Fix the commands
2780
+ fixed_commands = fix_setup_commands(all_commands)
2781
+
2726
2782
  print("\nšŸ“‹ Generated fallback setup commands:")
2727
- for i, cmd in enumerate(all_commands, 1):
2783
+ for i, cmd in enumerate(fixed_commands, 1):
2728
2784
  print(f" {i}. {cmd}")
2729
2785
 
2730
- return all_commands
2786
+ return fixed_commands
2731
2787
 
2732
2788
  def generate_basic_repo_analysis_from_url(repo_url):
2733
2789
  """Generate basic repository analysis data from a repository URL."""
@@ -2915,9 +2971,21 @@ def get_setup_commands_from_local_api(repo_url, gitingest_data):
2915
2971
  if "setupInstructions" in data and "commands" in data["setupInstructions"]:
2916
2972
  commands = data["setupInstructions"]["commands"]
2917
2973
  print(f"āœ… Successfully fetched {len(commands)} setup commands from local API")
2974
+
2975
+ # Print the original commands
2976
+ print("šŸ“‹ Original commands from local API:")
2918
2977
  for i, cmd in enumerate(commands, 1):
2919
2978
  print(f" {i}. {cmd}")
2920
- return commands
2979
+
2980
+ # Fix the commands
2981
+ fixed_commands = fix_setup_commands(commands)
2982
+
2983
+ # Print the fixed commands
2984
+ print("\nšŸ“‹ Fixed commands:")
2985
+ for i, cmd in enumerate(fixed_commands, 1):
2986
+ print(f" {i}. {cmd}")
2987
+
2988
+ return fixed_commands
2921
2989
  except Exception as e:
2922
2990
  print(f"āŒ Error connecting to local API: {e}")
2923
2991
 
@@ -3041,6 +3109,58 @@ def create_ssh_container_function(gpu_type="a10g", timeout_minutes=60, volume=No
3041
3109
  # Return the configured function
3042
3110
  return ssh_container, app_name
3043
3111
 
3112
+ def fix_setup_commands(commands):
3113
+ """Fix setup commands by removing placeholders and comments."""
3114
+ fixed_commands = []
3115
+
3116
+ for cmd in commands:
3117
+ # Remove placeholders like "(or the appropriate entry point...)"
3118
+ cmd = re.sub(r'\([^)]*\)', '', cmd).strip()
3119
+
3120
+ # Skip empty commands or pure comments
3121
+ if not cmd or cmd.startswith('#'):
3122
+ continue
3123
+
3124
+ # Remove trailing comments
3125
+ cmd = re.sub(r'#.*$', '', cmd).strip()
3126
+
3127
+ if cmd:
3128
+ fixed_commands.append(cmd)
3129
+
3130
+ return fixed_commands
3131
+
3132
+ def find_entry_point(repo_dir):
3133
+ """Find the entry point script for a repository."""
3134
+ # Common entry point files to check
3135
+ common_entry_points = [
3136
+ "main.py", "app.py", "run.py", "train.py", "start.py",
3137
+ "server.py", "cli.py", "demo.py", "example.py"
3138
+ ]
3139
+
3140
+ # Check if any of the common entry points exist
3141
+ for entry_point in common_entry_points:
3142
+ if os.path.exists(os.path.join(repo_dir, entry_point)):
3143
+ return entry_point
3144
+
3145
+ # Look for Python files in the root directory
3146
+ python_files = [f for f in os.listdir(repo_dir) if f.endswith('.py')]
3147
+ if python_files:
3148
+ # Prioritize files with main function or if_name_main pattern
3149
+ for py_file in python_files:
3150
+ file_path = os.path.join(repo_dir, py_file)
3151
+ try:
3152
+ with open(file_path, 'r') as f:
3153
+ content = f.read()
3154
+ if "def main" in content or "if __name__ == '__main__'" in content or 'if __name__ == "__main__"' in content:
3155
+ return py_file
3156
+ except:
3157
+ pass
3158
+
3159
+ # If no main function found, return the first Python file
3160
+ return python_files[0]
3161
+
3162
+ return None
3163
+
3044
3164
  if __name__ == "__main__":
3045
3165
  # Parse command line arguments when script is run directly
3046
3166
  import argparse