gitarsenal-cli 1.1.8 → 1.1.10

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.8",
3
+ "version": "1.1.10",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -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
 
@@ -211,6 +211,7 @@ def create_ssh_container():
211
211
  try:
212
212
  # Set the token directly in environment
213
213
  os.environ["MODAL_TOKEN_ID"] = MODAL_TOKEN
214
+ os.environ["MODAL_TOKEN"] = MODAL_TOKEN
214
215
  logger.info(f"Set MODAL_TOKEN_ID in environment (length: {len(MODAL_TOKEN)})")
215
216
 
216
217
  # Try to set token via CLI as well
@@ -256,8 +257,29 @@ def create_ssh_container():
256
257
  try:
257
258
  # Set token in environment for this thread
258
259
  os.environ["MODAL_TOKEN_ID"] = MODAL_TOKEN
260
+ os.environ["MODAL_TOKEN"] = MODAL_TOKEN
259
261
  logger.info(f"Set MODAL_TOKEN_ID in thread (length: {len(MODAL_TOKEN)})")
260
262
 
263
+ # Create a copy of the environment variables for the container creation
264
+ env_copy = os.environ.copy()
265
+ env_copy["MODAL_TOKEN_ID"] = MODAL_TOKEN
266
+ env_copy["MODAL_TOKEN"] = MODAL_TOKEN
267
+
268
+ # Try to set token via CLI again in this thread
269
+ try:
270
+ import subprocess
271
+ subprocess.run(
272
+ ["modal", "token", "set", MODAL_TOKEN],
273
+ capture_output=True, text=True, check=False
274
+ )
275
+ logger.info("Set Modal token via CLI in thread")
276
+ except Exception as e:
277
+ logger.warning(f"Failed to set token via CLI in thread: {e}")
278
+
279
+ # Explicitly print token status for debugging
280
+ logger.info(f"MODAL_TOKEN_ID in thread env: {os.environ.get('MODAL_TOKEN_ID')}")
281
+ logger.info(f"MODAL_TOKEN in thread env: {os.environ.get('MODAL_TOKEN')}")
282
+
261
283
  result = create_modal_ssh_container(
262
284
  gpu_type,
263
285
  repo_url=repo_url,
@@ -2069,26 +2069,40 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
2069
2069
 
2070
2070
  # Check if Modal is authenticated
2071
2071
  try:
2072
+ # Print all environment variables for debugging
2073
+ print("🔍 DEBUG: Checking environment variables")
2074
+ modal_token_id = os.environ.get("MODAL_TOKEN_ID")
2075
+ modal_token = os.environ.get("MODAL_TOKEN")
2076
+ print(f"🔍 MODAL_TOKEN_ID exists: {'Yes' if modal_token_id else 'No'}")
2077
+ print(f"🔍 MODAL_TOKEN exists: {'Yes' if modal_token else 'No'}")
2078
+ if modal_token_id:
2079
+ print(f"🔍 MODAL_TOKEN_ID length: {len(modal_token_id)}")
2080
+ if modal_token:
2081
+ print(f"🔍 MODAL_TOKEN length: {len(modal_token)}")
2082
+
2072
2083
  # Try to access Modal token to check authentication
2073
2084
  try:
2074
2085
  # Check if token is set in environment
2075
- modal_token = os.environ.get("MODAL_TOKEN_ID")
2076
- if not modal_token:
2086
+ modal_token_id = os.environ.get("MODAL_TOKEN_ID")
2087
+ if not modal_token_id:
2077
2088
  print("⚠️ MODAL_TOKEN_ID not found in environment.")
2078
2089
  # Try to get from MODAL_TOKEN
2079
2090
  modal_token = os.environ.get("MODAL_TOKEN")
2080
2091
  if modal_token:
2081
2092
  print("✅ Found token in MODAL_TOKEN environment variable")
2082
2093
  os.environ["MODAL_TOKEN_ID"] = modal_token
2094
+ modal_token_id = modal_token
2095
+ print(f"✅ Set MODAL_TOKEN_ID from MODAL_TOKEN (length: {len(modal_token)})")
2083
2096
 
2084
- if modal_token:
2085
- print(f"✅ Modal token found (length: {len(modal_token)})")
2097
+ if modal_token_id:
2098
+ print(f"✅ Modal token found (length: {len(modal_token_id)})")
2086
2099
 
2087
2100
  # Use the Modal CLI to set the token
2088
2101
  try:
2089
2102
  import subprocess
2103
+ print(f"🔄 Setting token via Modal CLI (token length: {len(modal_token_id)})")
2090
2104
  result = subprocess.run(
2091
- ["modal", "token", "set", modal_token],
2105
+ ["modal", "token", "set", modal_token_id],
2092
2106
  capture_output=True, text=True
2093
2107
  )
2094
2108
  if result.returncode == 0:
@@ -2099,20 +2113,51 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
2099
2113
  print(f"⚠️ Error setting token via CLI: {e}")
2100
2114
  else:
2101
2115
  print("❌ No Modal token found in environment variables")
2102
- return None
2116
+ # Try to get from file as a last resort
2117
+ try:
2118
+ home_dir = os.path.expanduser("~")
2119
+ modal_dir = os.path.join(home_dir, ".modal")
2120
+ token_file = os.path.join(modal_dir, "token.json")
2121
+ if os.path.exists(token_file):
2122
+ print(f"🔍 Found Modal token file at {token_file}")
2123
+ with open(token_file, 'r') as f:
2124
+ import json
2125
+ token_data = json.load(f)
2126
+ if "token_id" in token_data:
2127
+ modal_token_id = token_data["token_id"]
2128
+ os.environ["MODAL_TOKEN_ID"] = modal_token_id
2129
+ os.environ["MODAL_TOKEN"] = modal_token_id
2130
+ print(f"✅ Loaded token from file (length: {len(modal_token_id)})")
2131
+ else:
2132
+ print("❌ Token file does not contain token_id")
2133
+ else:
2134
+ print("❌ Modal token file not found")
2135
+ except Exception as e:
2136
+ print(f"❌ Error loading token from file: {e}")
2137
+
2138
+ if not os.environ.get("MODAL_TOKEN_ID"):
2139
+ print("❌ Could not find Modal token in any location")
2140
+ return None
2103
2141
 
2104
2142
  except Exception as e:
2105
2143
  print(f"⚠️ Error checking Modal token: {e}")
2106
2144
  # Try to use the token from environment
2107
- modal_token = os.environ.get("MODAL_TOKEN_ID") or os.environ.get("MODAL_TOKEN")
2108
- if not modal_token:
2145
+ modal_token_id = os.environ.get("MODAL_TOKEN_ID")
2146
+ modal_token = os.environ.get("MODAL_TOKEN")
2147
+ if modal_token_id:
2148
+ print(f"🔄 Using MODAL_TOKEN_ID from environment (length: {len(modal_token_id)})")
2149
+ elif modal_token:
2150
+ print(f"🔄 Using MODAL_TOKEN from environment (length: {len(modal_token)})")
2151
+ os.environ["MODAL_TOKEN_ID"] = modal_token
2152
+ modal_token_id = modal_token
2153
+ else:
2109
2154
  print("❌ No Modal token available. Cannot proceed.")
2110
2155
  return None
2111
2156
 
2112
- print(f"🔄 Using Modal token from environment (length: {len(modal_token)})")
2113
2157
  # Set it in both environment variables
2114
- os.environ["MODAL_TOKEN_ID"] = modal_token
2115
- os.environ["MODAL_TOKEN"] = modal_token
2158
+ os.environ["MODAL_TOKEN_ID"] = modal_token_id
2159
+ os.environ["MODAL_TOKEN"] = modal_token_id
2160
+ print("✅ Set both MODAL_TOKEN_ID and MODAL_TOKEN environment variables")
2116
2161
  except Exception as e:
2117
2162
  print(f"⚠️ Error checking Modal authentication: {e}")
2118
2163
  print("Continuing anyway, but Modal operations may fail")