gitarsenal-cli 1.1.2 → 1.1.4
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 +1 -1
- package/python/MODAL_PROXY_README.md +126 -0
- package/python/README.md +120 -0
- package/python/__pycache__/gitarsenal_proxy_client.cpython-313.pyc +0 -0
- package/python/gitarsenal.py +460 -95
- package/python/gitarsenal_proxy_client.py +408 -0
- package/python/modal_proxy_service.py +336 -0
- package/python/requirements.txt +3 -1
- package/python/test_modalSandboxScript.py +54 -1
package/python/gitarsenal.py
CHANGED
@@ -59,6 +59,55 @@ def check_modal_auth():
|
|
59
59
|
print(f"⚠️ Error checking Modal authentication: {e}")
|
60
60
|
return False
|
61
61
|
|
62
|
+
def check_proxy_config():
|
63
|
+
"""Check if Modal proxy configuration exists and is valid"""
|
64
|
+
try:
|
65
|
+
from gitarsenal_proxy_client import GitArsenalProxyClient
|
66
|
+
|
67
|
+
# Initialize client
|
68
|
+
client = GitArsenalProxyClient()
|
69
|
+
|
70
|
+
# Check if configuration exists
|
71
|
+
config = client.load_config()
|
72
|
+
|
73
|
+
if not config.get("proxy_url") or not config.get("api_key"):
|
74
|
+
print("⚠️ Modal proxy not configured. Setting up with default values...")
|
75
|
+
|
76
|
+
# Set default proxy URL to the ngrok URL
|
77
|
+
default_url = "https://e74889c63199.ngrok-free.app"
|
78
|
+
|
79
|
+
# Update configuration with default URL
|
80
|
+
config["proxy_url"] = default_url
|
81
|
+
client.save_config(config)
|
82
|
+
client.base_url = default_url
|
83
|
+
|
84
|
+
print(f"✅ Set default proxy URL: {default_url}")
|
85
|
+
|
86
|
+
# If API key is missing, prompt for configuration
|
87
|
+
if not config.get("api_key"):
|
88
|
+
print("⚠️ API key is still missing. Please configure:")
|
89
|
+
client.configure(interactive=True)
|
90
|
+
|
91
|
+
return False
|
92
|
+
|
93
|
+
# Verify connection to proxy
|
94
|
+
health = client.health_check()
|
95
|
+
if not health["success"]:
|
96
|
+
print(f"⚠️ Could not connect to Modal proxy at {config.get('proxy_url')}")
|
97
|
+
print(f" Error: {health.get('error', 'Unknown error')}")
|
98
|
+
print(" Run './gitarsenal.py proxy configure' to update configuration.")
|
99
|
+
return False
|
100
|
+
|
101
|
+
print(f"✅ Connected to Modal proxy at {config.get('proxy_url')}")
|
102
|
+
return True
|
103
|
+
|
104
|
+
except ImportError:
|
105
|
+
print("⚠️ GitArsenalProxyClient module not found")
|
106
|
+
return False
|
107
|
+
except Exception as e:
|
108
|
+
print(f"⚠️ Error checking proxy configuration: {e}")
|
109
|
+
return False
|
110
|
+
|
62
111
|
def main():
|
63
112
|
parser = argparse.ArgumentParser(description="GitArsenal CLI - GPU-accelerated cloud environments")
|
64
113
|
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
@@ -96,8 +145,10 @@ def main():
|
|
96
145
|
sandbox_parser.add_argument("--repo-name", type=str, help="Repository name override")
|
97
146
|
sandbox_parser.add_argument("--setup-commands", type=str, nargs="+", help="Setup commands to run")
|
98
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")
|
99
150
|
|
100
|
-
# SSH
|
151
|
+
# SSH command
|
101
152
|
ssh_parser = subparsers.add_parser("ssh", help="Create a Modal SSH container")
|
102
153
|
ssh_parser.add_argument("--gpu", type=str, default="A10G", choices=["A10G", "A100", "H100", "T4", "V100"],
|
103
154
|
help="GPU type (default: A10G)")
|
@@ -106,123 +157,437 @@ def main():
|
|
106
157
|
ssh_parser.add_argument("--setup-commands", type=str, nargs="+", help="Setup commands to run")
|
107
158
|
ssh_parser.add_argument("--volume-name", type=str, help="Name of the Modal volume for persistent storage")
|
108
159
|
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")
|
162
|
+
|
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")
|
166
|
+
|
167
|
+
# Proxy configure command
|
168
|
+
proxy_configure_parser = proxy_subparsers.add_parser("configure", help="Configure Modal proxy service")
|
169
|
+
|
170
|
+
# 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")
|
182
|
+
|
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)")
|
187
|
+
proxy_ssh_parser.add_argument("--repo-url", type=str, help="Repository URL to clone")
|
188
|
+
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")
|
191
|
+
proxy_ssh_parser.add_argument("--timeout", type=int, default=60, help="Container timeout in minutes (default: 60)")
|
192
|
+
proxy_ssh_parser.add_argument("--wait", action="store_true", help="Wait for container to be ready")
|
109
193
|
|
110
194
|
args = parser.parse_args()
|
111
195
|
|
112
|
-
# If no command is provided, show help
|
113
196
|
if not args.command:
|
114
197
|
parser.print_help()
|
115
|
-
return
|
116
|
-
|
117
|
-
# Handle credentials commands
|
118
|
-
if args.command == "credentials":
|
119
|
-
cred_args = []
|
120
|
-
|
121
|
-
if not args.cred_command:
|
122
|
-
cred_parser.print_help()
|
123
|
-
return 1
|
124
|
-
|
125
|
-
cred_args.append(args.cred_command)
|
126
|
-
|
127
|
-
if args.cred_command == "set" or args.cred_command == "get":
|
128
|
-
cred_args.append(args.key)
|
129
|
-
elif args.cred_command == "clear" and args.key != "all":
|
130
|
-
cred_args.append(args.key)
|
131
|
-
|
132
|
-
return run_script("manage_credentials.py", cred_args)
|
198
|
+
return 0
|
133
199
|
|
134
200
|
# For sandbox and SSH commands, check Modal authentication first
|
135
201
|
if args.command in ["sandbox", "ssh"]:
|
136
|
-
# Check if
|
137
|
-
if
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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":
|
142
243
|
try:
|
143
244
|
from credentials_manager import CredentialsManager
|
144
245
|
credentials_manager = CredentialsManager()
|
145
|
-
credentials = credentials_manager.load_credentials()
|
146
246
|
|
147
|
-
if "
|
148
|
-
# Set
|
149
|
-
|
150
|
-
|
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
|
151
341
|
|
152
|
-
# Try to authenticate with the token
|
153
|
-
try:
|
154
|
-
token_result = subprocess.run(
|
155
|
-
["modal", "token", "set", credentials["modal_token"]],
|
156
|
-
capture_output=True, text=True
|
157
|
-
)
|
158
|
-
if token_result.returncode == 0:
|
159
|
-
print("✅ Successfully authenticated with Modal")
|
160
|
-
except Exception as e:
|
161
|
-
print(f"⚠️ Error setting Modal token: {e}")
|
162
342
|
except ImportError:
|
163
|
-
print("
|
343
|
+
print("❌ Could not import credentials_manager module")
|
344
|
+
return 1
|
164
345
|
except Exception as e:
|
165
|
-
print(f"
|
346
|
+
print(f"❌ Error: {e}")
|
347
|
+
return 1
|
166
348
|
|
167
349
|
# Handle sandbox command
|
168
|
-
|
169
|
-
|
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,
|
386
|
+
repo_url=args.repo_url,
|
387
|
+
repo_name=args.repo_name,
|
388
|
+
setup_commands=args.setup_commands,
|
389
|
+
volume_name=args.volume_name
|
390
|
+
)
|
391
|
+
|
392
|
+
if not result:
|
393
|
+
print("❌ Failed to create sandbox")
|
394
|
+
return 1
|
395
|
+
|
396
|
+
print("✅ Sandbox created successfully")
|
170
397
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
if args.volume_name:
|
180
|
-
sandbox_args.extend(["--volume-name", args.volume_name])
|
181
|
-
|
182
|
-
return run_script("test_modalSandboxScript.py", sandbox_args)
|
183
|
-
|
184
|
-
# Handle SSH container command
|
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
|
185
406
|
elif args.command == "ssh":
|
186
|
-
|
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
|
+
|
444
|
+
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
|
450
|
+
else:
|
451
|
+
# Import SSH container creation function
|
452
|
+
from test_modalSandboxScript import create_modal_ssh_container
|
453
|
+
|
454
|
+
# Create SSH container directly
|
455
|
+
result = create_modal_ssh_container(
|
456
|
+
args.gpu,
|
457
|
+
repo_url=args.repo_url,
|
458
|
+
repo_name=args.repo_name,
|
459
|
+
setup_commands=args.setup_commands,
|
460
|
+
volume_name=args.volume_name,
|
461
|
+
timeout_minutes=args.timeout
|
462
|
+
)
|
463
|
+
|
464
|
+
if not result:
|
465
|
+
print("❌ Failed to create SSH container")
|
466
|
+
return 1
|
467
|
+
|
468
|
+
print("✅ SSH container created successfully")
|
187
469
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
if args.setup_commands:
|
195
|
-
ssh_args.extend(["--setup-commands"] + args.setup_commands)
|
196
|
-
if args.volume_name:
|
197
|
-
ssh_args.extend(["--volume-name", args.volume_name])
|
198
|
-
if args.timeout:
|
199
|
-
ssh_args.extend(["--timeout", str(args.timeout)])
|
200
|
-
|
201
|
-
# Use test_modalSandboxScript.py with SSH mode
|
202
|
-
ssh_args.extend(["--ssh"])
|
203
|
-
return run_script("test_modalSandboxScript.py", ssh_args)
|
204
|
-
|
205
|
-
return 0
|
206
|
-
|
207
|
-
def run_script(script_name, args):
|
208
|
-
"""Run a Python script with the given arguments"""
|
209
|
-
# Get the directory of the current script
|
210
|
-
script_dir = os.path.dirname(os.path.abspath(__file__))
|
211
|
-
script_path = os.path.join(script_dir, script_name)
|
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
|
212
476
|
|
213
|
-
#
|
214
|
-
|
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
|
503
|
+
result = client.create_sandbox(
|
504
|
+
gpu_type=args.gpu,
|
505
|
+
repo_url=args.repo_url,
|
506
|
+
repo_name=args.repo_name,
|
507
|
+
setup_commands=args.setup_commands,
|
508
|
+
volume_name=args.volume_name,
|
509
|
+
wait=args.wait
|
510
|
+
)
|
511
|
+
|
512
|
+
if not result:
|
513
|
+
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
|
545
|
+
result = client.create_ssh_container(
|
546
|
+
gpu_type=args.gpu,
|
547
|
+
repo_url=args.repo_url,
|
548
|
+
repo_name=args.repo_name,
|
549
|
+
setup_commands=args.setup_commands,
|
550
|
+
volume_name=args.volume_name,
|
551
|
+
timeout=args.timeout,
|
552
|
+
wait=args.wait
|
553
|
+
)
|
554
|
+
|
555
|
+
if not result:
|
556
|
+
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
|
+
else:
|
573
|
+
print(f"❌ Unknown proxy command: {args.proxy_command}")
|
574
|
+
proxy_parser.print_help()
|
575
|
+
return 1
|
576
|
+
|
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
|
215
584
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
return result.returncode
|
220
|
-
except KeyboardInterrupt:
|
221
|
-
print("\n⚠️ Command interrupted by user")
|
222
|
-
return 130
|
223
|
-
except Exception as e:
|
224
|
-
print(f"❌ Error running {script_name}: {e}")
|
585
|
+
else:
|
586
|
+
print(f"❌ Unknown command: {args.command}")
|
587
|
+
parser.print_help()
|
225
588
|
return 1
|
589
|
+
|
590
|
+
return 0
|
226
591
|
|
227
592
|
if __name__ == "__main__":
|
228
593
|
sys.exit(main())
|