gitarsenal-cli 1.0.6 ā 1.0.7
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/test_modalSandboxScript.py +114 -112
package/package.json
CHANGED
@@ -1841,7 +1841,109 @@ def generate_random_password(length=16):
|
|
1841
1841
|
password = ''.join(secrets.choice(alphabet) for i in range(length))
|
1842
1842
|
return password
|
1843
1843
|
|
1844
|
+
# First, add the standalone ssh_container function at the module level, before the create_modal_ssh_container function
|
1844
1845
|
|
1846
|
+
# Define a module-level ssh container function
|
1847
|
+
ssh_app = modal.App("ssh-container-app")
|
1848
|
+
|
1849
|
+
@ssh_app.function(
|
1850
|
+
image=modal.Image.debian_slim()
|
1851
|
+
.apt_install(
|
1852
|
+
"openssh-server", "sudo", "curl", "wget", "vim", "htop", "git",
|
1853
|
+
"python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
|
1854
|
+
"gpg", "ca-certificates", "software-properties-common"
|
1855
|
+
)
|
1856
|
+
.pip_install("uv")
|
1857
|
+
.run_commands(
|
1858
|
+
# Create SSH directory
|
1859
|
+
"mkdir -p /var/run/sshd",
|
1860
|
+
"mkdir -p /root/.ssh",
|
1861
|
+
"chmod 700 /root/.ssh",
|
1862
|
+
|
1863
|
+
# Configure SSH server
|
1864
|
+
"sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
|
1865
|
+
"sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
|
1866
|
+
"sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config",
|
1867
|
+
|
1868
|
+
# SSH keep-alive settings
|
1869
|
+
"echo 'ClientAliveInterval 60' >> /etc/ssh/sshd_config",
|
1870
|
+
"echo 'ClientAliveCountMax 3' >> /etc/ssh/sshd_config",
|
1871
|
+
|
1872
|
+
# Generate SSH host keys
|
1873
|
+
"ssh-keygen -A",
|
1874
|
+
|
1875
|
+
# Set up a nice bash prompt
|
1876
|
+
"echo 'export PS1=\"\\[\\e[1;32m\\]modal:\\[\\e[1;34m\\]\\w\\[\\e[0m\\]$ \"' >> /root/.bashrc",
|
1877
|
+
),
|
1878
|
+
timeout=3600, # Default 1 hour timeout
|
1879
|
+
gpu="a10g", # Default GPU
|
1880
|
+
cpu=2,
|
1881
|
+
memory=8192,
|
1882
|
+
serialized=True,
|
1883
|
+
)
|
1884
|
+
def ssh_container_function(ssh_password, repo_url=None, repo_name=None, setup_commands=None):
|
1885
|
+
import subprocess
|
1886
|
+
import time
|
1887
|
+
import os
|
1888
|
+
|
1889
|
+
# Set root password
|
1890
|
+
subprocess.run(["bash", "-c", f"echo 'root:{ssh_password}' | chpasswd"], check=True)
|
1891
|
+
|
1892
|
+
# Start SSH service
|
1893
|
+
subprocess.run(["service", "ssh", "start"], check=True)
|
1894
|
+
|
1895
|
+
# Setup environment
|
1896
|
+
os.environ['PS1'] = r'\[\e[1;32m\]modal:\[\e[1;34m\]\w\[\e[0m\]$ '
|
1897
|
+
|
1898
|
+
# Clone repository if provided
|
1899
|
+
if repo_url:
|
1900
|
+
repo_name_from_url = repo_name or repo_url.split('/')[-1].replace('.git', '')
|
1901
|
+
print(f"š„ Cloning repository: {repo_url}")
|
1902
|
+
|
1903
|
+
try:
|
1904
|
+
subprocess.run(["git", "clone", repo_url], check=True, cwd="/root")
|
1905
|
+
print(f"ā
Repository cloned successfully: {repo_name_from_url}")
|
1906
|
+
|
1907
|
+
# Change to repository directory
|
1908
|
+
repo_dir = f"/root/{repo_name_from_url}"
|
1909
|
+
if os.path.exists(repo_dir):
|
1910
|
+
os.chdir(repo_dir)
|
1911
|
+
print(f"š Changed to repository directory: {repo_dir}")
|
1912
|
+
|
1913
|
+
except subprocess.CalledProcessError as e:
|
1914
|
+
print(f"ā Failed to clone repository: {e}")
|
1915
|
+
|
1916
|
+
# Run setup commands if provided
|
1917
|
+
if setup_commands:
|
1918
|
+
print(f"āļø Running {len(setup_commands)} setup commands...")
|
1919
|
+
for i, cmd in enumerate(setup_commands, 1):
|
1920
|
+
print(f"š Executing command {i}/{len(setup_commands)}: {cmd}")
|
1921
|
+
try:
|
1922
|
+
result = subprocess.run(cmd, shell=True, check=True,
|
1923
|
+
capture_output=True, text=True)
|
1924
|
+
if result.stdout:
|
1925
|
+
print(f"ā
Output: {result.stdout}")
|
1926
|
+
except subprocess.CalledProcessError as e:
|
1927
|
+
print(f"ā Command failed: {e}")
|
1928
|
+
if e.stderr:
|
1929
|
+
print(f"ā Error: {e.stderr}")
|
1930
|
+
|
1931
|
+
# Get container info
|
1932
|
+
print("š Container started successfully!")
|
1933
|
+
print(f"š Container ID: {os.environ.get('MODAL_TASK_ID', 'unknown')}")
|
1934
|
+
|
1935
|
+
# Keep the container running
|
1936
|
+
while True:
|
1937
|
+
time.sleep(30)
|
1938
|
+
# Check if SSH service is still running
|
1939
|
+
try:
|
1940
|
+
subprocess.run(["service", "ssh", "status"], check=True,
|
1941
|
+
capture_output=True)
|
1942
|
+
except subprocess.CalledProcessError:
|
1943
|
+
print("ā ļø SSH service stopped, restarting...")
|
1944
|
+
subprocess.run(["service", "ssh", "start"], check=True)
|
1945
|
+
|
1946
|
+
# Now modify the create_modal_ssh_container function to use the standalone ssh_container_function
|
1845
1947
|
|
1846
1948
|
def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_commands=None,
|
1847
1949
|
volume_name=None, timeout_minutes=60, ssh_password=None):
|
@@ -1897,125 +1999,25 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
1897
1999
|
print("ā ļø Continuing without persistent volume")
|
1898
2000
|
volume = None
|
1899
2001
|
|
1900
|
-
# Create SSH-enabled image
|
1901
|
-
ssh_image = (
|
1902
|
-
modal.Image.debian_slim()
|
1903
|
-
.apt_install(
|
1904
|
-
"openssh-server", "sudo", "curl", "wget", "vim", "htop", "git",
|
1905
|
-
"python3", "python3-pip", "build-essential", "tmux", "screen", "nano",
|
1906
|
-
"gpg", "ca-certificates", "software-properties-common"
|
1907
|
-
)
|
1908
|
-
.pip_install("uv") # Fast Python package installer
|
1909
|
-
.run_commands(
|
1910
|
-
# Create SSH directory
|
1911
|
-
"mkdir -p /var/run/sshd",
|
1912
|
-
"mkdir -p /root/.ssh",
|
1913
|
-
"chmod 700 /root/.ssh",
|
1914
|
-
|
1915
|
-
# Configure SSH server
|
1916
|
-
"sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
|
1917
|
-
"sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config",
|
1918
|
-
"sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config",
|
1919
|
-
|
1920
|
-
# SSH keep-alive settings
|
1921
|
-
"echo 'ClientAliveInterval 60' >> /etc/ssh/sshd_config",
|
1922
|
-
"echo 'ClientAliveCountMax 3' >> /etc/ssh/sshd_config",
|
1923
|
-
|
1924
|
-
# Generate SSH host keys
|
1925
|
-
"ssh-keygen -A",
|
1926
|
-
|
1927
|
-
# Set up a nice bash prompt
|
1928
|
-
"echo 'export PS1=\"\\[\\e[1;32m\\]modal:\\[\\e[1;34m\\]\\w\\[\\e[0m\\]$ \"' >> /root/.bashrc",
|
1929
|
-
)
|
1930
|
-
)
|
1931
|
-
|
1932
2002
|
# Create Modal app
|
1933
2003
|
with modal.enable_output():
|
1934
2004
|
print(f"š Creating Modal app: {app_name}")
|
1935
|
-
app = modal.App.lookup(app_name, create_if_missing=True)
|
1936
2005
|
|
1937
|
-
#
|
1938
|
-
|
1939
|
-
if volume:
|
1940
|
-
volumes[volume_mount_path] = volume
|
1941
|
-
print(f"š¦ Mounting volume '{volume_name}' at {volume_mount_path}")
|
2006
|
+
# Configure the global ssh_container_function with the correct parameters
|
2007
|
+
global ssh_container_function
|
1942
2008
|
|
1943
|
-
|
1944
|
-
|
1945
|
-
timeout=timeout_minutes * 60, # Convert to seconds
|
2009
|
+
# Update the function's configuration with our specific needs
|
2010
|
+
ssh_container_function = ssh_container_function.update(
|
1946
2011
|
gpu=gpu_spec['gpu'],
|
1947
|
-
|
1948
|
-
|
1949
|
-
serialized=True,
|
1950
|
-
volumes=volumes if volumes else None,
|
2012
|
+
timeout=timeout_minutes * 60,
|
2013
|
+
volumes={volume_mount_path: volume} if volume else None
|
1951
2014
|
)
|
1952
|
-
def ssh_container():
|
1953
|
-
import subprocess
|
1954
|
-
import time
|
1955
|
-
import os
|
1956
|
-
|
1957
|
-
# Set root password
|
1958
|
-
subprocess.run(["bash", "-c", f"echo 'root:{ssh_password}' | chpasswd"], check=True)
|
1959
|
-
|
1960
|
-
# Start SSH service
|
1961
|
-
subprocess.run(["service", "ssh", "start"], check=True)
|
1962
|
-
|
1963
|
-
# Setup environment
|
1964
|
-
os.environ['PS1'] = r'\[\e[1;32m\]modal:\[\e[1;34m\]\w\[\e[0m\]$ '
|
1965
|
-
|
1966
|
-
# Clone repository if provided
|
1967
|
-
if repo_url:
|
1968
|
-
repo_name_from_url = repo_name or repo_url.split('/')[-1].replace('.git', '')
|
1969
|
-
print(f"š„ Cloning repository: {repo_url}")
|
1970
|
-
|
1971
|
-
try:
|
1972
|
-
subprocess.run(["git", "clone", repo_url], check=True, cwd="/root")
|
1973
|
-
print(f"ā
Repository cloned successfully: {repo_name_from_url}")
|
1974
|
-
|
1975
|
-
# Change to repository directory
|
1976
|
-
repo_dir = f"/root/{repo_name_from_url}"
|
1977
|
-
if os.path.exists(repo_dir):
|
1978
|
-
os.chdir(repo_dir)
|
1979
|
-
print(f"š Changed to repository directory: {repo_dir}")
|
1980
|
-
|
1981
|
-
except subprocess.CalledProcessError as e:
|
1982
|
-
print(f"ā Failed to clone repository: {e}")
|
1983
|
-
|
1984
|
-
# Run setup commands if provided
|
1985
|
-
if setup_commands:
|
1986
|
-
print(f"āļø Running {len(setup_commands)} setup commands...")
|
1987
|
-
for i, cmd in enumerate(setup_commands, 1):
|
1988
|
-
print(f"š Executing command {i}/{len(setup_commands)}: {cmd}")
|
1989
|
-
try:
|
1990
|
-
result = subprocess.run(cmd, shell=True, check=True,
|
1991
|
-
capture_output=True, text=True)
|
1992
|
-
if result.stdout:
|
1993
|
-
print(f"ā
Output: {result.stdout}")
|
1994
|
-
except subprocess.CalledProcessError as e:
|
1995
|
-
print(f"ā Command failed: {e}")
|
1996
|
-
if e.stderr:
|
1997
|
-
print(f"ā Error: {e.stderr}")
|
1998
|
-
|
1999
|
-
# Get container info
|
2000
|
-
print("š Container started successfully!")
|
2001
|
-
print(f"š Container ID: {os.environ.get('MODAL_TASK_ID', 'unknown')}")
|
2002
|
-
|
2003
|
-
# Keep the container running
|
2004
|
-
while True:
|
2005
|
-
time.sleep(30)
|
2006
|
-
# Check if SSH service is still running
|
2007
|
-
try:
|
2008
|
-
subprocess.run(["service", "ssh", "status"], check=True,
|
2009
|
-
capture_output=True)
|
2010
|
-
except subprocess.CalledProcessError:
|
2011
|
-
print("ā ļø SSH service stopped, restarting...")
|
2012
|
-
subprocess.run(["service", "ssh", "start"], check=True)
|
2013
2015
|
|
2014
2016
|
# Start the container
|
2015
2017
|
print("š Starting SSH container...")
|
2016
2018
|
|
2017
2019
|
# Use spawn to run the container in the background
|
2018
|
-
container_handle =
|
2020
|
+
container_handle = ssh_container_function.spawn(ssh_password, repo_url, repo_name, setup_commands)
|
2019
2021
|
|
2020
2022
|
# Wait a moment for the container to start
|
2021
2023
|
print("ā³ Waiting for container to initialize...")
|
@@ -2037,7 +2039,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2037
2039
|
if containers and isinstance(containers, list):
|
2038
2040
|
# Find the most recent container
|
2039
2041
|
for container in containers:
|
2040
|
-
if container.get("App") ==
|
2042
|
+
if container.get("App") == "ssh-container-app":
|
2041
2043
|
container_id = container.get("Container ID")
|
2042
2044
|
break
|
2043
2045
|
|
@@ -2055,7 +2057,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2055
2057
|
if result.returncode == 0:
|
2056
2058
|
lines = result.stdout.split('\n')
|
2057
2059
|
for line in lines:
|
2058
|
-
if
|
2060
|
+
if "ssh-container-app" in line or ('ta-' in line and 'ā' in line):
|
2059
2061
|
parts = line.split('ā')
|
2060
2062
|
if len(parts) >= 2:
|
2061
2063
|
possible_id = parts[1].strip()
|
@@ -2079,7 +2081,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2079
2081
|
print("="*80)
|
2080
2082
|
print(f"š Container ID: {container_id}")
|
2081
2083
|
print(f"š SSH Password: {ssh_password}")
|
2082
|
-
print(f"š± App Name:
|
2084
|
+
print(f"š± App Name: ssh-container-app")
|
2083
2085
|
if volume:
|
2084
2086
|
print(f"š¾ Volume: {volume_name} (mounted at {volume_mount_path})")
|
2085
2087
|
print("\nš SSH Connection:")
|
@@ -2122,7 +2124,7 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
|
|
2122
2124
|
return {
|
2123
2125
|
"container_handle": container_handle,
|
2124
2126
|
"container_id": container_id,
|
2125
|
-
"app_name":
|
2127
|
+
"app_name": "ssh-container-app",
|
2126
2128
|
"ssh_password": ssh_password,
|
2127
2129
|
"volume_name": volume_name,
|
2128
2130
|
"volume_mount_path": volume_mount_path if volume else None
|
@@ -2687,7 +2689,7 @@ if __name__ == "__main__":
|
|
2687
2689
|
# Try alternative approach with iTerm2
|
2688
2690
|
try:
|
2689
2691
|
iterm_script = f'''
|
2690
|
-
tell application "iTerm"
|
2692
|
+
tell application "iTerm"
|
2691
2693
|
create window with default profile
|
2692
2694
|
tell current session of current window
|
2693
2695
|
write text "echo 'Connecting to Modal SSH container...'; echo 'Password: {ssh_password or 'unknown'}'; ssh root@{container_id}.modal.run"
|