gitarsenal-cli 1.9.63 โ†’ 1.9.65

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-13T19:02:38.976Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
1
+ {"created":"2025-08-14T08:39:09.974Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitarsenal-cli",
3
- "version": "1.9.63",
3
+ "version": "1.9.65",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -197,7 +197,7 @@ def get_stored_credentials():
197
197
 
198
198
  # Now modify the create_modal_ssh_container function to use the PersistentShell
199
199
  def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_commands=None,
200
- volume_name=None, timeout_minutes=60, ssh_password=None, interactive=False):
200
+ volume_name=None, timeout_minutes=60, ssh_password=None, interactive=False, gpu_count=1):
201
201
  """Create a Modal SSH container with GPU support and tunneling"""
202
202
 
203
203
  # Use interactive mode if specified
@@ -295,7 +295,18 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
295
295
  gpu_type = 'A10G'
296
296
 
297
297
  gpu_spec = gpu_configs[gpu_type]
298
- print(f"๐Ÿš€ Creating SSH container with {gpu_spec['gpu']} GPU ({gpu_spec['memory']}GB VRAM)")
298
+
299
+ # Configure GPU string for Modal (support multiple GPUs)
300
+ if gpu_count > 1:
301
+ modal_gpu_spec = f"{gpu_spec['gpu']}:{gpu_count}"
302
+ total_memory = gpu_spec['memory'] * gpu_count
303
+ print(f"๐Ÿš€ Creating SSH container with {gpu_count}x {gpu_spec['gpu']} GPUs ({total_memory}GB total VRAM)")
304
+ else:
305
+ modal_gpu_spec = gpu_spec['gpu']
306
+ print(f"๐Ÿš€ Creating SSH container with {gpu_spec['gpu']} GPU ({gpu_spec['memory']}GB VRAM)")
307
+
308
+ # Store the modal GPU specification for the decorator
309
+ gpu_spec['modal_gpu'] = modal_gpu_spec
299
310
 
300
311
  # Generate or use provided SSH password
301
312
  if not ssh_password:
@@ -394,7 +405,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
394
405
  # Define the SSH container function (remove image from decorator)
395
406
  @app.function(
396
407
  timeout=timeout_minutes * 60, # Convert to seconds
397
- gpu="A10G",
408
+ gpu=gpu_spec['modal_gpu'], # Use the user-selected GPU type and count
398
409
  serialized=True,
399
410
  volumes=volumes_config if volumes_config else None,
400
411
  )
@@ -1262,6 +1273,12 @@ def show_usage_examples():
1262
1273
  print("โ”‚ --volume-name my-persistent-volume โ”‚")
1263
1274
  print("โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜\n")
1264
1275
 
1276
+ print("With Multiple GPUs")
1277
+ print("โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”")
1278
+ print("โ”‚ gitarsenal --gpu A100-80GB --gpu-count 4 \\ โ”‚")
1279
+ print("โ”‚ --repo-url https://github.com/username/repo.git โ”‚")
1280
+ print("โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜\n")
1281
+
1265
1282
  print("With GitIngest API (default)")
1266
1283
  print("โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”")
1267
1284
  print("โ”‚ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git โ”‚")
@@ -1286,6 +1303,7 @@ def show_usage_examples():
1286
1303
 
1287
1304
  print("Available GPU Options:")
1288
1305
  print(" T4, L4, A10G, A100-40GB, A100-80GB, L40S, H100, H200, B200")
1306
+ print(" Use --gpu-count to specify multiple GPUs (1-8)")
1289
1307
  print()
1290
1308
  print("Authentication Behavior:")
1291
1309
  print(" โ€ข First time: Interactive registration/login required")
@@ -1377,8 +1395,6 @@ def get_setup_commands_from_gitingest(repo_url):
1377
1395
  api_endpoints = [
1378
1396
  "https://www.gitarsenal.dev/api/gitingest-setup-commands",
1379
1397
  "https://gitarsenal.dev/api/gitingest-setup-commands",
1380
- "https://www.gitarsenal.dev/api/analyze-with-gitingest",
1381
- "http://localhost:3000/api/gitingest-setup-commands"
1382
1398
  ]
1383
1399
 
1384
1400
  # Generate basic gitingest data
@@ -1979,6 +1995,7 @@ if __name__ == "__main__":
1979
1995
  parser.add_argument('--proxy-url', help='URL of the proxy server')
1980
1996
  parser.add_argument('--proxy-api-key', help='API key for the proxy server')
1981
1997
  parser.add_argument('--gpu', default='A10G', help='GPU type to use')
1998
+ parser.add_argument('--gpu-count', type=int, default=1, help='Number of GPUs to use (default: 1)')
1982
1999
  parser.add_argument('--repo-url', help='Repository URL')
1983
2000
 
1984
2001
  # Authentication-related arguments
@@ -2024,6 +2041,8 @@ if __name__ == "__main__":
2024
2041
  print("โ”‚ 9. B200 โ”‚ 141GB โ”‚")
2025
2042
  print("โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜")
2026
2043
  print("Use --gpu <type> to specify a GPU type")
2044
+ print("Use --gpu-count <number> to specify multiple GPUs (1-8)")
2045
+ print("\nExample: --gpu A100-80GB --gpu-count 4 (for 4x A100-80GB GPUs)")
2027
2046
  sys.exit(0)
2028
2047
 
2029
2048
  # If no arguments or only --show-examples is provided, show usage examples
@@ -2093,7 +2112,11 @@ if __name__ == "__main__":
2093
2112
  # Display configuration after GPU selection
2094
2113
  print("\n๐Ÿ“‹ Container Configuration:")
2095
2114
  print(f"Repository URL: {args.repo_url or 'Not specified'}")
2096
- print(f"GPU Type: {gpu_type}")
2115
+ gpu_count = getattr(args, 'gpu_count', 1)
2116
+ if gpu_count > 1:
2117
+ print(f"GPU Type: {gpu_count}x {gpu_type}")
2118
+ else:
2119
+ print(f"GPU Type: {gpu_type}")
2097
2120
  print(f"Volume: {args.volume_name or 'None'}")
2098
2121
  if args.use_api:
2099
2122
  print("Setup Commands: Auto-detect from repository")
@@ -2144,6 +2167,24 @@ if __name__ == "__main__":
2144
2167
  print("\n๐Ÿ›‘ Setup cancelled.")
2145
2168
  sys.exit(1)
2146
2169
 
2170
+ # Ask about GPU count if not specified
2171
+ gpu_count = getattr(args, 'gpu_count', 1)
2172
+ if not hasattr(args, 'gpu_count') or args.gpu_count == 1:
2173
+ try:
2174
+ gpu_count_input = input("? How many GPUs do you need? (1-8, default: 1): ").strip()
2175
+ if gpu_count_input:
2176
+ try:
2177
+ gpu_count = int(gpu_count_input)
2178
+ if gpu_count < 1 or gpu_count > 8:
2179
+ print("โš ๏ธ GPU count must be between 1 and 8. Using default: 1")
2180
+ gpu_count = 1
2181
+ except ValueError:
2182
+ print("โš ๏ธ Invalid GPU count. Using default: 1")
2183
+ gpu_count = 1
2184
+ except KeyboardInterrupt:
2185
+ print("\n๐Ÿ›‘ Setup cancelled.")
2186
+ sys.exit(1)
2187
+
2147
2188
  # Ask about setup commands
2148
2189
  use_gitingest = args.use_gitingest and not args.no_gitingest
2149
2190
  if not args.use_api and not args.setup_commands and not args.setup_commands_json:
@@ -2158,6 +2199,7 @@ if __name__ == "__main__":
2158
2199
  # Update args with interactive values
2159
2200
  args.repo_url = repo_url
2160
2201
  args.volume_name = volume_name
2202
+ args.gpu_count = gpu_count
2161
2203
  args.use_gitingest = use_gitingest
2162
2204
 
2163
2205
  try:
@@ -2168,27 +2210,9 @@ if __name__ == "__main__":
2168
2210
  if args.repo_url and (args.use_gitingest and not args.no_gitingest):
2169
2211
  # print("๐Ÿ”„ Using gitingest approach to fetch setup commands (default)")
2170
2212
  api_commands = get_setup_commands_from_gitingest(args.repo_url)
2171
- if api_commands:
2172
- setup_commands = api_commands
2173
- print(f"๐Ÿ“‹ Using {len(setup_commands)} commands from gitingest API")
2174
- else:
2175
- print("โš ๏ธ Failed to get commands from gitingest API")
2176
- if not args.use_api:
2177
- print("โš ๏ธ Falling back to basic setup commands")
2178
- setup_commands = generate_fallback_commands(None)
2179
- else:
2180
- setup_commands = []
2181
- # If --use-api flag is set and repo_url is provided, fetch setup commands from API
2182
- elif args.use_api and args.repo_url:
2183
- print("๐Ÿ”„ Using original API to fetch setup commands")
2184
- api_commands = fetch_setup_commands_from_api(args.repo_url)
2185
- if api_commands:
2186
- setup_commands = api_commands
2187
- print(f"๐Ÿ“‹ Using {len(setup_commands)} commands from original API")
2188
- else:
2189
- print("โš ๏ธ Failed to get commands from API, no fallback commands will be used")
2190
- # Do not fall back to basic setup commands
2191
- setup_commands = []
2213
+ setup_commands = api_commands
2214
+ print(f"๐Ÿ“‹ Using {len(setup_commands)} commands from gitingest API")
2215
+
2192
2216
 
2193
2217
  # Parse setup commands from JSON if provided
2194
2218
  if args.setup_commands_json:
@@ -2205,11 +2229,6 @@ if __name__ == "__main__":
2205
2229
  print(f"โš ๏ธ Error parsing JSON setup commands: {e}")
2206
2230
  print(f"Received JSON string: {args.setup_commands_json}")
2207
2231
 
2208
- # Print received setup commands for debugging
2209
- if setup_commands:
2210
- print(f"๐Ÿ“‹ Using {len(setup_commands)} setup commands:")
2211
- for i, cmd in enumerate(setup_commands, 1):
2212
- print(f" {i}. {cmd}")
2213
2232
 
2214
2233
  # Load commands from file if specified
2215
2234
  if args.commands_file and os.path.exists(args.commands_file):
@@ -2281,14 +2300,11 @@ if __name__ == "__main__":
2281
2300
  volume_name=args.volume_name,
2282
2301
  timeout_minutes=args.timeout,
2283
2302
  ssh_password=ssh_password,
2284
- interactive=args.interactive
2303
+ interactive=args.interactive,
2304
+ gpu_count=getattr(args, 'gpu_count', 1)
2285
2305
  )
2286
2306
  except KeyboardInterrupt:
2287
- # print("\n\n๐Ÿ›‘ Execution interrupted")
2288
- # print("๐Ÿงน Cleaning up resources...")
2289
2307
  cleanup_modal_token()
2290
2308
  sys.exit(1)
2291
2309
  except Exception as e:
2292
- # print(f"\nโŒ Error: {e}")
2293
- # print("๐Ÿงน Cleaning up resources...")
2294
2310
  cleanup_modal_token()