gitarsenal-cli 1.1.9 → 1.1.11

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.9",
3
+ "version": "1.1.11",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -46,7 +46,7 @@ The Modal proxy service allows users to access Modal services (like GPU-accelera
46
46
 
47
47
  This will provide you with a public URL that you can share with users.
48
48
 
49
- Current ngrok URL: `https://cf487e07fd45.ngrok-free.app`
49
+ Current ngrok URL: `https://932ffd63ad3d.ngrok-free.app`
50
50
 
51
51
  ## Client-Side Setup
52
52
 
@@ -56,7 +56,7 @@ The Modal proxy service allows users to access Modal services (like GPU-accelera
56
56
  ```
57
57
 
58
58
  You'll be prompted to enter:
59
- - The proxy URL (the default is now set to `https://cf487e07fd45.ngrok-free.app`)
59
+ - The proxy URL (the default is now set to `https://932ffd63ad3d.ngrok-free.app`)
60
60
  - Your API key
61
61
 
62
62
  2. **Check proxy service status**:
@@ -74,7 +74,7 @@ def check_proxy_config():
74
74
  print("⚠️ Modal proxy not configured. Setting up with default values...")
75
75
 
76
76
  # Set default proxy URL to the ngrok URL
77
- default_url = "https://cf487e07fd45.ngrok-free.app"
77
+ default_url = "https://932ffd63ad3d.ngrok-free.app"
78
78
 
79
79
  # Update configuration with default URL
80
80
  config["proxy_url"] = default_url
@@ -109,483 +109,184 @@ def check_proxy_config():
109
109
  return False
110
110
 
111
111
  def main():
112
- parser = argparse.ArgumentParser(description="GitArsenal CLI - GPU-accelerated cloud environments")
112
+ """Main entry point for the GitArsenal CLI"""
113
+ parser = argparse.ArgumentParser(description="GitArsenal CLI - Tools for AI development")
113
114
  subparsers = parser.add_subparsers(dest="command", help="Command to run")
114
115
 
115
- # Credentials command
116
- cred_parser = subparsers.add_parser("credentials", help="Manage credentials")
117
- cred_subparsers = cred_parser.add_subparsers(dest="cred_command", help="Credentials command")
118
-
119
- # Credentials setup command
120
- cred_setup_parser = cred_subparsers.add_parser("setup", help="Set up all credentials")
121
-
122
- # Credentials set command
123
- cred_set_parser = cred_subparsers.add_parser("set", help="Set a specific credential")
124
- cred_set_parser.add_argument("key", choices=["openai_api_key", "modal_token", "huggingface_token", "wandb_api_key"],
125
- help="The credential to set")
126
-
127
- # Credentials get command
128
- cred_get_parser = cred_subparsers.add_parser("get", help="Get a specific credential")
129
- cred_get_parser.add_argument("key", choices=["openai_api_key", "modal_token", "huggingface_token", "wandb_api_key"],
130
- help="The credential to get")
131
-
132
- # Credentials clear command
133
- cred_clear_parser = cred_subparsers.add_parser("clear", help="Clear credentials")
134
- cred_clear_parser.add_argument("key", nargs="?", choices=["openai_api_key", "modal_token", "huggingface_token", "wandb_api_key", "all"],
135
- default="all", help="The credential to clear (default: all)")
136
-
137
- # Credentials list command
138
- cred_list_parser = cred_subparsers.add_parser("list", help="List all saved credentials")
139
-
140
- # Sandbox command
141
- sandbox_parser = subparsers.add_parser("sandbox", help="Create a Modal sandbox")
142
- sandbox_parser.add_argument("--gpu", type=str, default="A10G", choices=["A10G", "A100", "H100", "T4", "V100"],
143
- help="GPU type (default: A10G)")
144
- sandbox_parser.add_argument("--repo-url", type=str, help="Repository URL to clone")
145
- sandbox_parser.add_argument("--repo-name", type=str, help="Repository name override")
146
- sandbox_parser.add_argument("--setup-commands", type=str, nargs="+", help="Setup commands to run")
147
- sandbox_parser.add_argument("--volume-name", type=str, help="Name of the Modal volume for persistent storage")
148
- sandbox_parser.add_argument("--use-api", action="store_true", help="Use API for setup commands")
149
- sandbox_parser.add_argument("--use-proxy", action="store_true", help="Use Modal proxy service instead of direct Modal access")
150
-
151
116
  # SSH command
152
117
  ssh_parser = subparsers.add_parser("ssh", help="Create a Modal SSH container")
153
- ssh_parser.add_argument("--gpu", type=str, default="A10G", choices=["A10G", "A100", "H100", "T4", "V100"],
154
- help="GPU type (default: A10G)")
118
+ ssh_parser.add_argument("--gpu", type=str, default="A10G", help="GPU type (default: A10G)")
155
119
  ssh_parser.add_argument("--repo-url", type=str, help="Repository URL to clone")
156
120
  ssh_parser.add_argument("--repo-name", type=str, help="Repository name override")
157
- ssh_parser.add_argument("--setup-commands", type=str, nargs="+", help="Setup commands to run")
158
- ssh_parser.add_argument("--volume-name", type=str, help="Name of the Modal volume for persistent storage")
121
+ ssh_parser.add_argument("--volume-name", type=str, help="Volume name for persistent storage")
159
122
  ssh_parser.add_argument("--timeout", type=int, default=60, help="Container timeout in minutes (default: 60)")
160
- ssh_parser.add_argument("--use-api", action="store_true", help="Use API for setup commands")
161
- ssh_parser.add_argument("--use-proxy", action="store_true", help="Use Modal proxy service instead of direct Modal access")
123
+ ssh_parser.add_argument("--use-proxy", action="store_true", help="Use Modal proxy service instead of direct Modal API")
124
+ ssh_parser.add_argument("--wait", action="store_true", help="Wait for container to be ready")
125
+
126
+ # Sandbox command
127
+ sandbox_parser = subparsers.add_parser("sandbox", help="Create a Modal sandbox")
128
+ sandbox_parser.add_argument("--gpu", type=str, default="A10G", help="GPU type (default: A10G)")
129
+ sandbox_parser.add_argument("--repo-url", type=str, help="Repository URL to clone")
130
+ sandbox_parser.add_argument("--repo-name", type=str, help="Repository name override")
131
+ sandbox_parser.add_argument("--volume-name", type=str, help="Volume name for persistent storage")
132
+ sandbox_parser.add_argument("--use-proxy", action="store_true", help="Use Modal proxy service instead of direct Modal API")
133
+ sandbox_parser.add_argument("--wait", action="store_true", help="Wait for container to be ready")
162
134
 
163
- # Proxy command
164
- proxy_parser = subparsers.add_parser("proxy", help="Configure and use Modal proxy service")
165
- proxy_subparsers = proxy_parser.add_subparsers(dest="proxy_command", help="Proxy command")
135
+ # Proxy commands
136
+ proxy_parser = subparsers.add_parser("proxy", help="Modal proxy service commands")
137
+ proxy_subparsers = proxy_parser.add_subparsers(dest="proxy_command", help="Proxy command to run")
166
138
 
167
139
  # Proxy configure command
168
- proxy_configure_parser = proxy_subparsers.add_parser("configure", help="Configure Modal proxy service")
140
+ proxy_configure_parser = proxy_subparsers.add_parser("configure", help="Configure proxy settings")
169
141
 
170
142
  # Proxy status command
171
- proxy_status_parser = proxy_subparsers.add_parser("status", help="Check Modal proxy service status")
172
-
173
- # Proxy sandbox command
174
- proxy_sandbox_parser = proxy_subparsers.add_parser("sandbox", help="Create a sandbox through Modal proxy")
175
- proxy_sandbox_parser.add_argument("--gpu", type=str, default="A10G", choices=["A10G", "A100", "H100", "T4", "V100"],
176
- help="GPU type (default: A10G)")
177
- proxy_sandbox_parser.add_argument("--repo-url", type=str, help="Repository URL to clone")
178
- proxy_sandbox_parser.add_argument("--repo-name", type=str, help="Repository name override")
179
- proxy_sandbox_parser.add_argument("--setup-commands", type=str, nargs="+", help="Setup commands to run")
180
- proxy_sandbox_parser.add_argument("--volume-name", type=str, help="Name of the Modal volume for persistent storage")
181
- proxy_sandbox_parser.add_argument("--wait", action="store_true", help="Wait for sandbox to be ready")
143
+ proxy_status_parser = proxy_subparsers.add_parser("status", help="Check proxy status")
182
144
 
183
- # Proxy ssh command
184
- proxy_ssh_parser = proxy_subparsers.add_parser("ssh", help="Create an SSH container through Modal proxy")
185
- proxy_ssh_parser.add_argument("--gpu", type=str, default="A10G", choices=["A10G", "A100", "H100", "T4", "V100"],
186
- help="GPU type (default: A10G)")
145
+ # Proxy SSH command
146
+ proxy_ssh_parser = proxy_subparsers.add_parser("ssh", help="Create SSH container through proxy")
147
+ proxy_ssh_parser.add_argument("--gpu", type=str, default="A10G", help="GPU type (default: A10G)")
187
148
  proxy_ssh_parser.add_argument("--repo-url", type=str, help="Repository URL to clone")
188
149
  proxy_ssh_parser.add_argument("--repo-name", type=str, help="Repository name override")
189
- proxy_ssh_parser.add_argument("--setup-commands", type=str, nargs="+", help="Setup commands to run")
190
- proxy_ssh_parser.add_argument("--volume-name", type=str, help="Name of the Modal volume for persistent storage")
150
+ proxy_ssh_parser.add_argument("--volume-name", type=str, help="Volume name for persistent storage")
191
151
  proxy_ssh_parser.add_argument("--timeout", type=int, default=60, help="Container timeout in minutes (default: 60)")
192
152
  proxy_ssh_parser.add_argument("--wait", action="store_true", help="Wait for container to be ready")
193
153
 
154
+ # Parse arguments
194
155
  args = parser.parse_args()
195
156
 
196
- if not args.command:
197
- parser.print_help()
198
- return 0
199
-
200
- # For sandbox and SSH commands, check Modal authentication first
201
- if args.command in ["sandbox", "ssh"]:
202
- # Check if using proxy service
203
- if hasattr(args, 'use_proxy') and args.use_proxy:
204
- # Check if proxy is configured
205
- if not check_proxy_config():
206
- print("\n⚠️ Modal proxy service is not configured.")
207
- print("Please run './gitarsenal.py proxy configure' first.")
208
- return 1
209
- else:
210
- # Check if Modal is authenticated
211
- if not check_modal_auth():
212
- print("\n⚠️ Please authenticate with Modal before proceeding.")
213
- return 1
214
-
215
- # Try to load credentials and set Modal token if available
216
- try:
217
- from credentials_manager import CredentialsManager
218
- credentials_manager = CredentialsManager()
219
- credentials = credentials_manager.load_credentials()
220
-
221
- if "modal_token" in credentials:
222
- # Set the Modal token in the environment
223
- os.environ["MODAL_TOKEN_ID"] = credentials["modal_token"]
224
- print("✅ Using Modal token from credentials")
225
-
226
- # Try to authenticate with the token
227
- try:
228
- token_result = subprocess.run(
229
- ["modal", "token", "set", credentials["modal_token"]],
230
- capture_output=True, text=True
231
- )
232
- if token_result.returncode == 0:
233
- print("✅ Successfully authenticated with Modal")
234
- except Exception as e:
235
- print(f"⚠️ Error setting Modal token: {e}")
236
- except ImportError:
237
- print("⚠️ Could not load credentials manager")
238
- except Exception as e:
239
- print(f"⚠️ Error loading credentials: {e}")
240
-
241
- # Handle credentials commands
242
- if args.command == "credentials":
243
- try:
244
- from credentials_manager import CredentialsManager
245
- credentials_manager = CredentialsManager()
246
-
247
- if args.cred_command == "setup":
248
- # Set up all credentials
249
- print("🔧 Setting up all credentials")
250
-
251
- # Modal token
252
- modal_token = credentials_manager.get_modal_token()
253
- if modal_token:
254
- print("✅ Modal token set")
255
- else:
256
- print("⚠️ Modal token not set")
257
-
258
- # OpenAI API key
259
- openai_api_key = credentials_manager.get_openai_api_key()
260
- if openai_api_key:
261
- print("✅ OpenAI API key set")
262
- else:
263
- print("⚠️ OpenAI API key not set")
264
-
265
- # Hugging Face token
266
- huggingface_token = credentials_manager.get_huggingface_token()
267
- if huggingface_token:
268
- print("✅ Hugging Face token set")
269
- else:
270
- print("⚠️ Hugging Face token not set")
271
-
272
- # Weights & Biases API key
273
- wandb_api_key = credentials_manager.get_wandb_api_key()
274
- if wandb_api_key:
275
- print("✅ Weights & Biases API key set")
276
- else:
277
- print("⚠️ Weights & Biases API key not set")
278
-
279
- elif args.cred_command == "set":
280
- # Set a specific credential
281
- if args.key == "modal_token":
282
- modal_token = credentials_manager.get_modal_token()
283
- if modal_token:
284
- print("✅ Modal token set")
285
- else:
286
- print("⚠️ Modal token not set")
287
- elif args.key == "openai_api_key":
288
- openai_api_key = credentials_manager.get_openai_api_key()
289
- if openai_api_key:
290
- print("✅ OpenAI API key set")
291
- else:
292
- print("⚠️ OpenAI API key not set")
293
- elif args.key == "huggingface_token":
294
- huggingface_token = credentials_manager.get_huggingface_token()
295
- if huggingface_token:
296
- print("✅ Hugging Face token set")
297
- else:
298
- print("⚠️ Hugging Face token not set")
299
- elif args.key == "wandb_api_key":
300
- wandb_api_key = credentials_manager.get_wandb_api_key()
301
- if wandb_api_key:
302
- print("✅ Weights & Biases API key set")
303
- else:
304
- print("⚠️ Weights & Biases API key not set")
305
-
306
- elif args.cred_command == "get":
307
- # Get a specific credential
308
- credentials = credentials_manager.load_credentials()
309
- if args.key in credentials:
310
- # Mask the credential for security
311
- value = credentials[args.key]
312
- masked_value = value[:4] + "*" * (len(value) - 8) + value[-4:] if len(value) > 8 else "****"
313
- print(f"{args.key}: {masked_value}")
314
- else:
315
- print(f"⚠️ {args.key} not found")
316
-
317
- elif args.cred_command == "clear":
318
- # Clear credentials
319
- if args.key == "all":
320
- credentials_manager.clear_all_credentials()
321
- print("✅ All credentials cleared")
322
- else:
323
- if credentials_manager.clear_credential(args.key):
324
- print(f"✅ {args.key} cleared")
325
- else:
326
- print(f"⚠️ {args.key} not found")
327
-
328
- elif args.cred_command == "list":
329
- # List all credentials
330
- credentials = credentials_manager.load_credentials()
331
- if credentials:
332
- print("📋 Saved credentials:")
333
- for key in credentials:
334
- print(f" - {key}")
335
- else:
336
- print("⚠️ No credentials found")
337
-
338
- else:
339
- print("⚠️ Unknown credentials command")
340
- return 1
341
-
342
- except ImportError:
343
- print("❌ Could not import credentials_manager module")
344
- return 1
345
- except Exception as e:
346
- print(f"❌ Error: {e}")
347
- return 1
348
-
349
- # Handle sandbox command
350
- elif args.command == "sandbox":
351
- try:
352
- # Check if using proxy service
353
- if args.use_proxy:
354
- # Import proxy client
355
- try:
356
- from gitarsenal_proxy_client import GitArsenalProxyClient
357
- client = GitArsenalProxyClient()
358
-
359
- # Create sandbox through proxy
360
- result = client.create_sandbox(
361
- gpu_type=args.gpu,
362
- repo_url=args.repo_url,
363
- repo_name=args.repo_name,
364
- setup_commands=args.setup_commands,
365
- volume_name=args.volume_name,
366
- wait=True # Always wait for sandbox to be ready
367
- )
368
-
369
- if not result:
370
- print("❌ Failed to create sandbox through proxy service")
371
- return 1
372
-
373
- print("✅ Sandbox created successfully through proxy service")
374
-
375
- except ImportError:
376
- print("❌ Could not import gitarsenal_proxy_client module")
377
- print("Please make sure gitarsenal_proxy_client.py is in the same directory")
378
- return 1
379
- else:
380
- # Import sandbox creation function
381
- from test_modalSandboxScript import create_modal_sandbox
382
-
383
- # Create sandbox directly
384
- result = create_modal_sandbox(
385
- args.gpu,
157
+ if args.command == "ssh":
158
+ if args.use_proxy:
159
+ # Use proxy service for SSH container
160
+ print("🚀 Creating SSH container through proxy...")
161
+ if check_proxy_config():
162
+ from gitarsenal_proxy_client import GitArsenalProxyClient
163
+ client = GitArsenalProxyClient()
164
+ result = client.create_ssh_container(
165
+ gpu_type=args.gpu,
386
166
  repo_url=args.repo_url,
387
167
  repo_name=args.repo_name,
388
- setup_commands=args.setup_commands,
389
- volume_name=args.volume_name
168
+ volume_name=args.volume_name,
169
+ timeout=args.timeout,
170
+ wait=args.wait
390
171
  )
391
-
392
- if not result:
393
- print("❌ Failed to create sandbox")
394
- return 1
395
-
396
- print("✅ Sandbox created successfully")
397
-
398
- except ImportError as e:
399
- print(f"❌ Could not import required module: {e}")
400
- return 1
401
- except Exception as e:
402
- print(f"❌ Error: {e}")
403
- return 1
404
-
405
- # Handle SSH command
406
- elif args.command == "ssh":
407
- try:
408
- # Check if using proxy service
409
- if args.use_proxy:
410
- # Import proxy client
411
- try:
412
- from gitarsenal_proxy_client import GitArsenalProxyClient
413
- client = GitArsenalProxyClient()
414
-
415
- # Ensure proxy is configured
416
- config = client.load_config()
417
- if not config.get("proxy_url") or not config.get("api_key"):
418
- print("\n⚠️ Modal proxy service is not fully configured.")
419
- print("Running configuration wizard...")
420
- client.configure(interactive=True)
421
- # Reload config after configuration
422
- config = client.load_config()
423
- if not config.get("proxy_url") or not config.get("api_key"):
424
- print("❌ Proxy configuration failed or was cancelled.")
425
- return 1
426
- print("✅ Proxy configuration completed successfully.")
427
-
428
- # Create SSH container through proxy
429
- print(f"🚀 Creating SSH container through proxy service ({config.get('proxy_url')})")
430
- result = client.create_ssh_container(
431
- gpu_type=args.gpu,
432
- repo_url=args.repo_url,
433
- repo_name=args.repo_name,
434
- setup_commands=args.setup_commands,
435
- volume_name=args.volume_name,
436
- timeout=args.timeout,
437
- wait=True # Always wait for container to be ready
438
- )
439
-
440
- if not result:
441
- print("❌ Failed to create SSH container through proxy service")
442
- return 1
443
-
172
+ if result:
444
173
  print("✅ SSH container created successfully through proxy service")
445
-
446
- except ImportError:
447
- print("❌ Could not import gitarsenal_proxy_client module")
448
- print("Please make sure gitarsenal_proxy_client.py is in the same directory")
449
- return 1
174
+ print(f"📋 Container ID: {result.get('container_id')}")
175
+ print(f"🔐 SSH Password: {result.get('ssh_password')}")
176
+ if not args.wait:
177
+ print("\n⚠️ Container creation is asynchronous. Use --wait flag to wait for it to be ready.")
178
+ print(" You can check the status later using the container ID above.")
179
+ else:
180
+ print("❌ Failed to create SSH container through proxy service")
450
181
  else:
451
- # Import SSH container creation function
182
+ print("❌ Proxy configuration failed or was cancelled.")
183
+ else:
184
+ # Use direct Modal API for SSH container
185
+ print("🚀 Creating SSH container...")
186
+ if check_modal_auth():
187
+ # Import the SSH container creation function
452
188
  from test_modalSandboxScript import create_modal_ssh_container
453
-
454
- # Create SSH container directly
455
189
  result = create_modal_ssh_container(
456
190
  args.gpu,
457
191
  repo_url=args.repo_url,
458
192
  repo_name=args.repo_name,
459
- setup_commands=args.setup_commands,
460
193
  volume_name=args.volume_name,
461
194
  timeout_minutes=args.timeout
462
195
  )
463
-
464
- if not result:
196
+ if result:
197
+ print("✅ SSH container created successfully")
198
+ else:
465
199
  print("❌ Failed to create SSH container")
466
- return 1
467
-
468
- print("✅ SSH container created successfully")
469
-
470
- except ImportError as e:
471
- print(f"❌ Could not import required module: {e}")
472
- return 1
473
- except Exception as e:
474
- print(f"❌ Error: {e}")
475
- return 1
200
+ else:
201
+ print("❌ Modal authentication failed or was cancelled.")
476
202
 
477
- # Handle proxy commands
478
- elif args.command == "proxy":
479
- if not args.proxy_command:
480
- proxy_parser.print_help()
481
- return 0
482
-
483
- try:
484
- # Import proxy client
485
- from gitarsenal_proxy_client import GitArsenalProxyClient
486
- client = GitArsenalProxyClient()
487
-
488
- if args.proxy_command == "configure":
489
- # Configure proxy service
490
- client.configure(interactive=True)
491
-
492
- elif args.proxy_command == "status":
493
- # Check proxy service status
494
- response = client.health_check()
495
- if response["success"]:
496
- print(f"✅ Proxy service is running: {response['data']['message']}")
497
- else:
498
- print(f"❌ Proxy service health check failed: {response['error']}")
499
- return 1
500
-
501
- elif args.proxy_command == "sandbox":
502
- # Create sandbox through proxy
203
+ elif args.command == "sandbox":
204
+ if args.use_proxy:
205
+ # Use proxy service for sandbox
206
+ print("🚀 Creating sandbox through proxy...")
207
+ if check_proxy_config():
208
+ from gitarsenal_proxy_client import GitArsenalProxyClient
209
+ client = GitArsenalProxyClient()
503
210
  result = client.create_sandbox(
504
211
  gpu_type=args.gpu,
505
212
  repo_url=args.repo_url,
506
213
  repo_name=args.repo_name,
507
- setup_commands=args.setup_commands,
508
214
  volume_name=args.volume_name,
509
215
  wait=args.wait
510
216
  )
511
-
512
- if not result:
217
+ if result:
218
+ print("✅ Sandbox created successfully through proxy service")
219
+ else:
513
220
  print("❌ Failed to create sandbox through proxy service")
514
- return 1
515
-
516
- print("✅ Sandbox created successfully through proxy service")
517
-
518
- elif args.proxy_command == "ssh":
519
- # Create SSH container through proxy
520
- print("🚀 Creating SSH container through proxy...")
521
-
522
- # Verify proxy configuration is complete
523
- config = client.load_config()
524
- if not config.get("proxy_url") or not config.get("api_key"):
525
- print("⚠️ Proxy configuration incomplete. Running configuration wizard...")
526
- client.configure(interactive=True)
527
- # Reload config after configuration
528
- config = client.load_config()
529
- if not config.get("proxy_url") or not config.get("api_key"):
530
- print("❌ Proxy configuration failed or was cancelled.")
531
- return 1
532
- print("✅ Proxy configuration completed successfully.")
533
-
534
- # Check proxy service health
535
- health = client.health_check()
536
- if not health["success"]:
537
- print(f"❌ Could not connect to proxy service at {config.get('proxy_url')}")
538
- print(f" Error: {health.get('error', 'Unknown error')}")
539
- print(" Please check if the proxy service is running.")
540
- return 1
541
-
542
- print(f"✅ Connected to proxy service at {config.get('proxy_url')}")
543
-
544
- # Create SSH container
221
+ else:
222
+ print("❌ Proxy configuration failed or was cancelled.")
223
+ else:
224
+ # Use direct Modal API for sandbox
225
+ print("🚀 Creating sandbox...")
226
+ if check_modal_auth():
227
+ # Import the sandbox creation function
228
+ from test_modalSandboxScript import create_modal_sandbox
229
+ result = create_modal_sandbox(
230
+ args.gpu,
231
+ repo_url=args.repo_url,
232
+ repo_name=args.repo_name,
233
+ volume_name=args.volume_name
234
+ )
235
+ if result:
236
+ print(" Sandbox created successfully")
237
+ else:
238
+ print("❌ Failed to create sandbox")
239
+ else:
240
+ print("❌ Modal authentication failed or was cancelled.")
241
+
242
+ elif args.command == "proxy":
243
+ if args.proxy_command == "configure":
244
+ # Configure proxy settings
245
+ from gitarsenal_proxy_client import GitArsenalProxyClient
246
+ client = GitArsenalProxyClient()
247
+ client.configure(interactive=True)
248
+
249
+ elif args.proxy_command == "status":
250
+ # Check proxy status
251
+ from gitarsenal_proxy_client import GitArsenalProxyClient
252
+ client = GitArsenalProxyClient()
253
+ response = client.health_check()
254
+ if response["success"]:
255
+ print(f"✅ Proxy service is running: {response['data']['message']}")
256
+ else:
257
+ print(f"❌ Proxy service health check failed: {response['error']}")
258
+
259
+ elif args.proxy_command == "ssh":
260
+ # Create SSH container through proxy
261
+ print("🚀 Creating SSH container through proxy...")
262
+ if check_proxy_config():
263
+ from gitarsenal_proxy_client import GitArsenalProxyClient
264
+ client = GitArsenalProxyClient()
545
265
  result = client.create_ssh_container(
546
266
  gpu_type=args.gpu,
547
267
  repo_url=args.repo_url,
548
268
  repo_name=args.repo_name,
549
- setup_commands=args.setup_commands,
550
269
  volume_name=args.volume_name,
551
270
  timeout=args.timeout,
552
271
  wait=args.wait
553
272
  )
554
-
555
- if not result:
273
+ if result:
274
+ print("✅ SSH container created successfully through proxy service")
275
+ print(f"📋 Container ID: {result.get('container_id')}")
276
+ print(f"🔐 SSH Password: {result.get('ssh_password')}")
277
+ if not args.wait:
278
+ print("\n⚠️ Container creation is asynchronous. Use --wait flag to wait for it to be ready.")
279
+ print(" You can check the status later using the container ID above.")
280
+ else:
556
281
  print("❌ Failed to create SSH container through proxy service")
557
- return 1
558
-
559
- # Print connection information if available
560
- if isinstance(result, dict):
561
- if "container_id" in result:
562
- print(f"📋 Container ID: {result['container_id']}")
563
- if "ssh_password" in result:
564
- print(f"🔐 SSH Password: {result['ssh_password']}")
565
-
566
- print("✅ SSH container creation process initiated through proxy service")
567
-
568
- if not args.wait:
569
- print("\n⚠️ Container creation is asynchronous. Use --wait flag to wait for it to be ready.")
570
- print(" You can check the status later using the container ID above.")
571
-
572
282
  else:
573
- print(f"❌ Unknown proxy command: {args.proxy_command}")
574
- proxy_parser.print_help()
575
- return 1
283
+ print("❌ Proxy configuration failed or was cancelled.")
576
284
 
577
- except ImportError:
578
- print("❌ Could not import gitarsenal_proxy_client module")
579
- print("Please make sure gitarsenal_proxy_client.py is in the same directory")
580
- return 1
581
- except Exception as e:
582
- print(f"❌ Error: {e}")
583
- return 1
285
+ else:
286
+ print("❌ Unknown proxy command. Available commands: configure, status, ssh")
584
287
 
585
288
  else:
586
- print(f"❌ Unknown command: {args.command}")
587
289
  parser.print_help()
588
- return 1
589
290
 
590
291
  return 0
591
292
 
@@ -32,7 +32,7 @@ class GitArsenalProxyClient:
32
32
 
33
33
  # If still no URL, use default
34
34
  if not self.base_url:
35
- self.base_url = "https://cf487e07fd45.ngrok-free.app" # Default to new ngrok URL
35
+ self.base_url = "https://932ffd63ad3d.ngrok-free.app" # Default to new ngrok URL
36
36
 
37
37
  # Warn if no API key
38
38
  if not self.api_key: