gitarsenal-cli 1.6.8 → 1.6.9

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.6.8",
3
+ "version": "1.6.9",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,54 @@
1
+ import subprocess
2
+ import time
3
+ import secrets
4
+ import string
5
+ import modal
6
+
7
+ def generate_random_password(length=16):
8
+ """Generate a random password for SSH access"""
9
+ alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
10
+ password = ''.join(secrets.choice(alphabet) for i in range(length))
11
+ return password
12
+
13
+ image = (
14
+ modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
15
+ .apt_install("openssh-server", "sudo", "curl", "wget", "git", "vim", "htop", "tmux", "nvtop")
16
+ .pip_install("cupy-cuda12x", "torch", "transformers")
17
+ .run_commands(
18
+ "mkdir -p /var/run/sshd",
19
+ "ssh-keygen -A",
20
+ "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
21
+ "sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
22
+ "echo 'export PATH=/usr/local/cuda/bin:$PATH' >> /root/.bashrc"
23
+ )
24
+ )
25
+
26
+ app = modal.App("cuda-ssh-container", image=image)
27
+
28
+ @app.function(gpu="A10G", timeout=3600)
29
+ def start_ssh():
30
+ # Generate SSH password
31
+ password = generate_random_password()
32
+ subprocess.run(["bash", "-c", f"echo 'root:{password}' | chpasswd"], check=True)
33
+
34
+ # Start SSH server
35
+ subprocess.Popen(["/usr/sbin/sshd", "-D"])
36
+ time.sleep(2)
37
+
38
+ # Test CUDA
39
+ subprocess.run(["nvidia-smi"])
40
+ subprocess.run(["nvcc", "--version"])
41
+
42
+ # Forward SSH port
43
+ with modal.forward(port=22, unencrypted=True) as tunnel:
44
+ hostname, port = tunnel.tcp_socket
45
+ print(f"SSH: ssh -p {port} root@{hostname}")
46
+ print(f"Password: {password}")
47
+
48
+ # Keep alive
49
+ while True:
50
+ time.sleep(60)
51
+
52
+ if __name__ == "__main__":
53
+ with app.run():
54
+ start_ssh.remote()
@@ -0,0 +1,54 @@
1
+ import subprocess
2
+ import time
3
+ import secrets
4
+ import string
5
+ import modal
6
+
7
+ def generate_random_password(length=16):
8
+ """Generate a random password for SSH access"""
9
+ alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
10
+ password = ''.join(secrets.choice(alphabet) for i in range(length))
11
+ return password
12
+
13
+ image = (
14
+ modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
15
+ .apt_install("openssh-server", "sudo", "curl", "wget", "git", "vim", "htop", "tmux", "nvtop")
16
+ .pip_install("cupy-cuda12x", "torch", "transformers")
17
+ .run_commands(
18
+ "mkdir -p /var/run/sshd",
19
+ "ssh-keygen -A",
20
+ "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
21
+ "sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
22
+ "echo 'export PATH=/usr/local/cuda/bin:$PATH' >> /root/.bashrc"
23
+ )
24
+ )
25
+
26
+ app = modal.App("cuda-ssh-container", image=image)
27
+
28
+ @app.function(gpu="A10G", timeout=3600)
29
+ def start_ssh():
30
+ # Generate SSH password
31
+ password = generate_random_password()
32
+ subprocess.run(["bash", "-c", f"echo 'root:{password}' | chpasswd"], check=True)
33
+
34
+ # Start SSH server
35
+ subprocess.Popen(["/usr/sbin/sshd", "-D"])
36
+ time.sleep(2)
37
+
38
+ # Test CUDA
39
+ subprocess.run(["nvidia-smi"])
40
+ subprocess.run(["nvcc", "--version"])
41
+
42
+ # Forward SSH port
43
+ with modal.forward(port=22, unencrypted=True) as tunnel:
44
+ hostname, port = tunnel.tcp_socket
45
+ print(f"SSH: ssh -p {port} root@{hostname}")
46
+ print(f"Password: {password}")
47
+
48
+ # Keep alive
49
+ while True:
50
+ time.sleep(60)
51
+
52
+ if __name__ == "__main__":
53
+ with app.run():
54
+ start_ssh.remote()
@@ -1183,23 +1183,22 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1183
1183
  # Create SSH-enabled image
1184
1184
  try:
1185
1185
  print("📦 Building SSH-enabled image...")
1186
+
1187
+ # Use a more stable CUDA base image and avoid problematic packages
1186
1188
  ssh_image = (
1187
- modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
1189
+ modal.Image.from_registry("nvidia/cuda:12.4.0-runtime-ubuntu22.04", add_python="3.11")
1188
1190
  .apt_install(
1189
1191
  "openssh-server", "sudo", "curl", "wget", "vim", "htop", "git",
1190
1192
  "python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
1191
- "gpg", "ca-certificates", "software-properties-common", "nvtop"
1193
+ "gpg", "ca-certificates", "software-properties-common"
1192
1194
  )
1193
- .pip_install("uv", "modal", "requests", "openai") # Fast Python package installer and Modal
1195
+ .pip_install("uv", "modal", "requests", "openai") # Remove problematic CUDA packages
1194
1196
  .run_commands(
1195
1197
  # Create SSH directory
1196
1198
  "mkdir -p /var/run/sshd",
1197
1199
  "mkdir -p /root/.ssh",
1198
1200
  "chmod 700 /root/.ssh",
1199
1201
 
1200
- # Generate SSH host keys
1201
- "ssh-keygen -A",
1202
-
1203
1202
  # Configure SSH server
1204
1203
  "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
1205
1204
  "sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
@@ -1209,9 +1208,8 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1209
1208
  "echo 'ClientAliveInterval 60' >> /etc/ssh/sshd_config",
1210
1209
  "echo 'ClientAliveCountMax 3' >> /etc/ssh/sshd_config",
1211
1210
 
1212
- # Set up CUDA environment
1213
- "echo 'export PATH=/usr/local/cuda/bin:$PATH' >> /root/.bashrc",
1214
- "echo 'export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH' >> /root/.bashrc",
1211
+ # Generate SSH host keys
1212
+ "ssh-keygen -A",
1215
1213
 
1216
1214
  # Set up a nice bash prompt
1217
1215
  "echo 'export PS1=\"\\[\\e[1;32m\\]modal:\\[\\e[1;34m\\]\\w\\[\\e[0m\\]$ \"' >> /root/.bashrc",
@@ -1226,18 +1224,25 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1226
1224
  volumes_config = {}
1227
1225
  if volume:
1228
1226
  volumes_config[volume_mount_path] = volume
1227
+
1228
+ # Create app with image passed directly (THIS IS THE KEY CHANGE)
1229
+ try:
1230
+ print("🔍 Testing app creation...")
1231
+ app = modal.App(app_name, image=ssh_image) # Pass image here
1232
+ print("✅ Created app successfully")
1233
+ except Exception as e:
1234
+ print(f"❌ Error creating app: {e}")
1235
+ return None
1229
1236
 
1230
- # Define the SSH container function - simplified like the example
1237
+ # Define the SSH container function (remove image from decorator)
1231
1238
  @app.function(
1232
- image=ssh_image,
1233
1239
  timeout=timeout_minutes * 60, # Convert to seconds
1234
1240
  gpu=gpu_spec['gpu'],
1235
1241
  cpu=2,
1236
- memory=8192,
1237
1242
  serialized=True,
1238
1243
  volumes=volumes_config if volumes_config else None,
1239
1244
  )
1240
- def start_ssh():
1245
+ def ssh_container_function(ssh_password=None, repo_url=None, repo_name=None, setup_commands=None, openai_api_key=None):
1241
1246
  """Start SSH container with password authentication and optional setup."""
1242
1247
  import subprocess
1243
1248
  import time
@@ -1247,25 +1252,14 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1247
1252
  subprocess.run(["bash", "-c", f"echo 'root:{ssh_password}' | chpasswd"], check=True)
1248
1253
 
1249
1254
  # Set OpenAI API key if provided
1250
- openai_api_key = os.environ.get("OPENAI_API_KEY")
1251
1255
  if openai_api_key:
1252
1256
  os.environ['OPENAI_API_KEY'] = openai_api_key
1253
1257
  print(f"✅ Set OpenAI API key in container environment (length: {len(openai_api_key)})")
1254
1258
  else:
1255
1259
  print("⚠️ No OpenAI API key provided to container")
1256
1260
 
1257
- # Start SSH service using Popen (non-blocking) like in the example
1258
- subprocess.Popen(["/usr/sbin/sshd", "-D"])
1259
- time.sleep(2) # Give SSH time to start
1260
-
1261
- # Test CUDA setup
1262
- try:
1263
- print("🔧 Testing CUDA setup...")
1264
- subprocess.run(["nvidia-smi"], check=True)
1265
- subprocess.run(["nvcc", "--version"], check=True)
1266
- print("✅ CUDA setup verified")
1267
- except subprocess.CalledProcessError as e:
1268
- print(f"⚠️ CUDA test failed: {e}")
1261
+ # Start SSH service
1262
+ subprocess.run(["service", "ssh", "start"], check=True)
1269
1263
 
1270
1264
  # Clone repository if provided
1271
1265
  if repo_url:
@@ -1393,28 +1387,43 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1393
1387
  print(f"🔍 Analyzing directory structure after failed cd command...")
1394
1388
  subprocess.run("pwd && ls -la && echo '--- Parent directory ---' && ls -la ..", shell=True, check=False)
1395
1389
 
1396
- # Forward SSH port and keep container alive - exactly like the example
1397
- with modal.forward(port=22, unencrypted=True) as tunnel:
1398
- hostname, port = tunnel.tcp_socket
1390
+ # Create SSH tunnel
1391
+ with modal.forward(22, unencrypted=True) as tunnel:
1392
+ host, port = tunnel.tcp_socket
1399
1393
 
1400
1394
  print("\n" + "=" * 80)
1401
1395
  print("🎉 SSH CONTAINER IS READY!")
1402
1396
  print("=" * 80)
1403
- print(f"SSH: ssh -p {port} root@{hostname}")
1404
- print(f"Password: {ssh_password}")
1397
+ print(f"🌐 SSH Host: {host}")
1398
+ print(f"🔌 SSH Port: {port}")
1399
+ print(f"👤 Username: root")
1400
+ print(f"🔐 Password: {ssh_password}")
1401
+ print()
1402
+ print("🔗 CONNECT USING THIS COMMAND:")
1403
+ print(f"ssh -p {port} root@{host}")
1405
1404
  print("=" * 80)
1406
1405
 
1407
- # Keep alive - simplified like the example
1406
+ # Keep the container running
1408
1407
  while True:
1409
- time.sleep(60)
1408
+ time.sleep(30)
1409
+ # Check if SSH service is still running
1410
+ try:
1411
+ subprocess.run(["service", "ssh", "status"], check=True,
1412
+ capture_output=True)
1413
+ except subprocess.CalledProcessError:
1414
+ print("⚠️ SSH service stopped, restarting...")
1415
+ subprocess.run(["service", "ssh", "start"], check=True)
1410
1416
 
1411
1417
  # Run the container
1412
1418
  try:
1413
1419
  print("⏳ Starting container... This may take 1-2 minutes...")
1414
1420
 
1415
- # Start the container - simplified like the example
1416
- with app.run():
1417
- start_ssh.remote()
1421
+ # Start the container in a new thread to avoid blocking
1422
+ with modal.enable_output():
1423
+ with app.run():
1424
+ # Get the API key from environment
1425
+ api_key = os.environ.get("OPENAI_API_KEY")
1426
+ ssh_container_function.remote(ssh_password, repo_url, repo_name, setup_commands, api_key)
1418
1427
 
1419
1428
  # Clean up Modal token after container is successfully created
1420
1429
  cleanup_modal_token()
@@ -1183,23 +1183,22 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1183
1183
  # Create SSH-enabled image
1184
1184
  try:
1185
1185
  print("📦 Building SSH-enabled image...")
1186
+
1187
+ # Use a more stable CUDA base image and avoid problematic packages
1186
1188
  ssh_image = (
1187
- modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04", add_python="3.11")
1189
+ modal.Image.from_registry("nvidia/cuda:12.4.0-runtime-ubuntu22.04", add_python="3.11")
1188
1190
  .apt_install(
1189
1191
  "openssh-server", "sudo", "curl", "wget", "vim", "htop", "git",
1190
1192
  "python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
1191
- "gpg", "ca-certificates", "software-properties-common", "nvtop"
1193
+ "gpg", "ca-certificates", "software-properties-common"
1192
1194
  )
1193
- .pip_install("uv", "modal", "requests", "openai") # Fast Python package installer and Modal
1195
+ .pip_install("uv", "modal", "requests", "openai") # Remove problematic CUDA packages
1194
1196
  .run_commands(
1195
1197
  # Create SSH directory
1196
1198
  "mkdir -p /var/run/sshd",
1197
1199
  "mkdir -p /root/.ssh",
1198
1200
  "chmod 700 /root/.ssh",
1199
1201
 
1200
- # Generate SSH host keys
1201
- "ssh-keygen -A",
1202
-
1203
1202
  # Configure SSH server
1204
1203
  "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
1205
1204
  "sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
@@ -1209,9 +1208,8 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1209
1208
  "echo 'ClientAliveInterval 60' >> /etc/ssh/sshd_config",
1210
1209
  "echo 'ClientAliveCountMax 3' >> /etc/ssh/sshd_config",
1211
1210
 
1212
- # Set up CUDA environment
1213
- "echo 'export PATH=/usr/local/cuda/bin:$PATH' >> /root/.bashrc",
1214
- "echo 'export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH' >> /root/.bashrc",
1211
+ # Generate SSH host keys
1212
+ "ssh-keygen -A",
1215
1213
 
1216
1214
  # Set up a nice bash prompt
1217
1215
  "echo 'export PS1=\"\\[\\e[1;32m\\]modal:\\[\\e[1;34m\\]\\w\\[\\e[0m\\]$ \"' >> /root/.bashrc",
@@ -1226,18 +1224,25 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1226
1224
  volumes_config = {}
1227
1225
  if volume:
1228
1226
  volumes_config[volume_mount_path] = volume
1227
+
1228
+ # Create app with image passed directly (THIS IS THE KEY CHANGE)
1229
+ try:
1230
+ print("🔍 Testing app creation...")
1231
+ app = modal.App(app_name, image=ssh_image) # Pass image here
1232
+ print("✅ Created app successfully")
1233
+ except Exception as e:
1234
+ print(f"❌ Error creating app: {e}")
1235
+ return None
1229
1236
 
1230
- # Define the SSH container function - simplified like the example
1237
+ # Define the SSH container function (remove image from decorator)
1231
1238
  @app.function(
1232
- image=ssh_image,
1233
1239
  timeout=timeout_minutes * 60, # Convert to seconds
1234
1240
  gpu=gpu_spec['gpu'],
1235
1241
  cpu=2,
1236
- memory=8192,
1237
1242
  serialized=True,
1238
1243
  volumes=volumes_config if volumes_config else None,
1239
1244
  )
1240
- def start_ssh():
1245
+ def ssh_container_function(ssh_password=None, repo_url=None, repo_name=None, setup_commands=None, openai_api_key=None):
1241
1246
  """Start SSH container with password authentication and optional setup."""
1242
1247
  import subprocess
1243
1248
  import time
@@ -1247,25 +1252,14 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1247
1252
  subprocess.run(["bash", "-c", f"echo 'root:{ssh_password}' | chpasswd"], check=True)
1248
1253
 
1249
1254
  # Set OpenAI API key if provided
1250
- openai_api_key = os.environ.get("OPENAI_API_KEY")
1251
1255
  if openai_api_key:
1252
1256
  os.environ['OPENAI_API_KEY'] = openai_api_key
1253
1257
  print(f"✅ Set OpenAI API key in container environment (length: {len(openai_api_key)})")
1254
1258
  else:
1255
1259
  print("⚠️ No OpenAI API key provided to container")
1256
1260
 
1257
- # Start SSH service using Popen (non-blocking) like in the example
1258
- subprocess.Popen(["/usr/sbin/sshd", "-D"])
1259
- time.sleep(2) # Give SSH time to start
1260
-
1261
- # Test CUDA setup
1262
- try:
1263
- print("🔧 Testing CUDA setup...")
1264
- subprocess.run(["nvidia-smi"], check=True)
1265
- subprocess.run(["nvcc", "--version"], check=True)
1266
- print("✅ CUDA setup verified")
1267
- except subprocess.CalledProcessError as e:
1268
- print(f"⚠️ CUDA test failed: {e}")
1261
+ # Start SSH service
1262
+ subprocess.run(["service", "ssh", "start"], check=True)
1269
1263
 
1270
1264
  # Clone repository if provided
1271
1265
  if repo_url:
@@ -1393,28 +1387,43 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
1393
1387
  print(f"🔍 Analyzing directory structure after failed cd command...")
1394
1388
  subprocess.run("pwd && ls -la && echo '--- Parent directory ---' && ls -la ..", shell=True, check=False)
1395
1389
 
1396
- # Forward SSH port and keep container alive - exactly like the example
1397
- with modal.forward(port=22, unencrypted=True) as tunnel:
1398
- hostname, port = tunnel.tcp_socket
1390
+ # Create SSH tunnel
1391
+ with modal.forward(22, unencrypted=True) as tunnel:
1392
+ host, port = tunnel.tcp_socket
1399
1393
 
1400
1394
  print("\n" + "=" * 80)
1401
1395
  print("🎉 SSH CONTAINER IS READY!")
1402
1396
  print("=" * 80)
1403
- print(f"SSH: ssh -p {port} root@{hostname}")
1404
- print(f"Password: {ssh_password}")
1397
+ print(f"🌐 SSH Host: {host}")
1398
+ print(f"🔌 SSH Port: {port}")
1399
+ print(f"👤 Username: root")
1400
+ print(f"🔐 Password: {ssh_password}")
1401
+ print()
1402
+ print("🔗 CONNECT USING THIS COMMAND:")
1403
+ print(f"ssh -p {port} root@{host}")
1405
1404
  print("=" * 80)
1406
1405
 
1407
- # Keep alive - simplified like the example
1406
+ # Keep the container running
1408
1407
  while True:
1409
- time.sleep(60)
1408
+ time.sleep(30)
1409
+ # Check if SSH service is still running
1410
+ try:
1411
+ subprocess.run(["service", "ssh", "status"], check=True,
1412
+ capture_output=True)
1413
+ except subprocess.CalledProcessError:
1414
+ print("⚠️ SSH service stopped, restarting...")
1415
+ subprocess.run(["service", "ssh", "start"], check=True)
1410
1416
 
1411
1417
  # Run the container
1412
1418
  try:
1413
1419
  print("⏳ Starting container... This may take 1-2 minutes...")
1414
1420
 
1415
- # Start the container - simplified like the example
1416
- with app.run():
1417
- start_ssh.remote()
1421
+ # Start the container in a new thread to avoid blocking
1422
+ with modal.enable_output():
1423
+ with app.run():
1424
+ # Get the API key from environment
1425
+ api_key = os.environ.get("OPENAI_API_KEY")
1426
+ ssh_container_function.remote(ssh_password, repo_url, repo_name, setup_commands, api_key)
1418
1427
 
1419
1428
  # Clean up Modal token after container is successfully created
1420
1429
  cleanup_modal_token()