feat(cli): add Daytona to setup wizard, doctor, and status display
Add Daytona as a backend choice in the interactive setup wizard with SDK installation and API key prompts. Show Daytona image in status output and validate API key + SDK in doctor checks. Add OPTION 6 example in cli-config.yaml.example. Signed-off-by: rovle <lovre.pesut@gmail.com>
This commit is contained in:
parent
690b8bb563
commit
df61054a84
4 changed files with 91 additions and 10 deletions
|
|
@ -116,8 +116,22 @@ terminal:
|
||||||
# timeout: 180
|
# timeout: 180
|
||||||
# lifetime_seconds: 300
|
# lifetime_seconds: 300
|
||||||
# modal_image: "nikolaik/python-nodejs:python3.11-nodejs20"
|
# modal_image: "nikolaik/python-nodejs:python3.11-nodejs20"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# OPTION 6: Daytona cloud execution
|
||||||
|
# Commands run in Daytona cloud sandboxes
|
||||||
|
# Great for: Cloud dev environments, persistent workspaces, team collaboration
|
||||||
|
# Requires: pip install daytona, DAYTONA_API_KEY env var
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# terminal:
|
||||||
|
# backend: "daytona"
|
||||||
|
# cwd: "/home/daytona"
|
||||||
|
# timeout: 180
|
||||||
|
# lifetime_seconds: 300
|
||||||
|
# daytona_image: "nikolaik/python-nodejs:python3.11-nodejs20"
|
||||||
|
|
||||||
#
|
#
|
||||||
# --- Container resource limits (docker, singularity, modal -- ignored for local/ssh) ---
|
# --- Container resource limits (docker, singularity, modal, daytona -- ignored for local/ssh) ---
|
||||||
# These settings apply to all container backends. They control the resources
|
# These settings apply to all container backends. They control the resources
|
||||||
# allocated to the sandbox and whether its filesystem persists across sessions.
|
# allocated to the sandbox and whether its filesystem persists across sessions.
|
||||||
container_cpu: 1 # CPU cores
|
container_cpu: 1 # CPU cores
|
||||||
|
|
|
||||||
|
|
@ -355,6 +355,21 @@ def run_doctor(args):
|
||||||
check_fail("TERMINAL_SSH_HOST not set", "(required for TERMINAL_ENV=ssh)")
|
check_fail("TERMINAL_SSH_HOST not set", "(required for TERMINAL_ENV=ssh)")
|
||||||
issues.append("Set TERMINAL_SSH_HOST in .env")
|
issues.append("Set TERMINAL_SSH_HOST in .env")
|
||||||
|
|
||||||
|
# Daytona (if using daytona backend)
|
||||||
|
if terminal_env == "daytona":
|
||||||
|
daytona_key = os.getenv("DAYTONA_API_KEY")
|
||||||
|
if daytona_key:
|
||||||
|
check_ok("Daytona API key", "(configured)")
|
||||||
|
else:
|
||||||
|
check_fail("DAYTONA_API_KEY not set", "(required for TERMINAL_ENV=daytona)")
|
||||||
|
issues.append("Set DAYTONA_API_KEY environment variable")
|
||||||
|
try:
|
||||||
|
from daytona import Daytona
|
||||||
|
check_ok("daytona SDK", "(installed)")
|
||||||
|
except ImportError:
|
||||||
|
check_fail("daytona SDK not installed", "(pip install daytona)")
|
||||||
|
issues.append("Install daytona SDK: pip install daytona")
|
||||||
|
|
||||||
# Node.js + agent-browser (for browser automation tools)
|
# Node.js + agent-browser (for browser automation tools)
|
||||||
if shutil.which("node"):
|
if shutil.which("node"):
|
||||||
check_ok("Node.js")
|
check_ok("Node.js")
|
||||||
|
|
|
||||||
|
|
@ -980,19 +980,20 @@ def run_setup_wizard(args):
|
||||||
|
|
||||||
terminal_choices.extend([
|
terminal_choices.extend([
|
||||||
"Modal (cloud execution, GPU access, serverless)",
|
"Modal (cloud execution, GPU access, serverless)",
|
||||||
|
"Daytona (cloud sandboxes, persistent workspaces)",
|
||||||
"SSH (run commands on a remote server)",
|
"SSH (run commands on a remote server)",
|
||||||
f"Keep current ({current_backend})"
|
f"Keep current ({current_backend})"
|
||||||
])
|
])
|
||||||
|
|
||||||
# Build index map based on available choices
|
# Build index map based on available choices
|
||||||
if is_linux:
|
if is_linux:
|
||||||
backend_to_idx = {'local': 0, 'docker': 1, 'singularity': 2, 'modal': 3, 'ssh': 4}
|
backend_to_idx = {'local': 0, 'docker': 1, 'singularity': 2, 'modal': 3, 'daytona': 4, 'ssh': 5}
|
||||||
idx_to_backend = {0: 'local', 1: 'docker', 2: 'singularity', 3: 'modal', 4: 'ssh'}
|
idx_to_backend = {0: 'local', 1: 'docker', 2: 'singularity', 3: 'modal', 4: 'daytona', 5: 'ssh'}
|
||||||
keep_current_idx = 5
|
keep_current_idx = 6
|
||||||
else:
|
else:
|
||||||
backend_to_idx = {'local': 0, 'docker': 1, 'modal': 2, 'ssh': 3}
|
backend_to_idx = {'local': 0, 'docker': 1, 'modal': 2, 'daytona': 3, 'ssh': 4}
|
||||||
idx_to_backend = {0: 'local', 1: 'docker', 2: 'modal', 3: 'ssh'}
|
idx_to_backend = {0: 'local', 1: 'docker', 2: 'modal', 3: 'daytona', 4: 'ssh'}
|
||||||
keep_current_idx = 4
|
keep_current_idx = 5
|
||||||
if current_backend == 'singularity':
|
if current_backend == 'singularity':
|
||||||
print_warning("Singularity is only available on Linux - please select a different backend")
|
print_warning("Singularity is only available on Linux - please select a different backend")
|
||||||
|
|
||||||
|
|
@ -1067,7 +1068,7 @@ def run_setup_wizard(args):
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print_info("Note: Container resource settings (CPU, memory, disk, persistence)")
|
print_info("Note: Container resource settings (CPU, memory, disk, persistence)")
|
||||||
print_info("are in your config but only apply to Docker/Singularity/Modal backends.")
|
print_info("are in your config but only apply to Docker/Singularity/Modal/Daytona backends.")
|
||||||
|
|
||||||
if prompt_yes_no(" Enable sudo support? (allows agent to run sudo commands)", False):
|
if prompt_yes_no(" Enable sudo support? (allows agent to run sudo commands)", False):
|
||||||
print_warning(" SECURITY WARNING: Sudo password will be stored in plaintext")
|
print_warning(" SECURITY WARNING: Sudo password will be stored in plaintext")
|
||||||
|
|
@ -1152,6 +1153,51 @@ def run_setup_wizard(args):
|
||||||
_prompt_container_resources(config)
|
_prompt_container_resources(config)
|
||||||
print_success("Terminal set to Modal")
|
print_success("Terminal set to Modal")
|
||||||
|
|
||||||
|
elif selected_backend == 'daytona':
|
||||||
|
config.setdefault('terminal', {})['backend'] = 'daytona'
|
||||||
|
default_daytona = config.get('terminal', {}).get('daytona_image', 'nikolaik/python-nodejs:python3.11-nodejs20')
|
||||||
|
print_info("Daytona Cloud Configuration:")
|
||||||
|
print_info("Get your API key at: https://app.daytona.io/dashboard/keys")
|
||||||
|
|
||||||
|
# Check if daytona SDK is installed
|
||||||
|
try:
|
||||||
|
from daytona import Daytona
|
||||||
|
print_info("daytona SDK: installed ✓")
|
||||||
|
except ImportError:
|
||||||
|
print_info("Installing required package: daytona...")
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
uv_bin = shutil.which("uv")
|
||||||
|
if uv_bin:
|
||||||
|
result = subprocess.run(
|
||||||
|
[uv_bin, "pip", "install", "daytona"],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, "-m", "pip", "install", "daytona"],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
print_success("daytona SDK installed")
|
||||||
|
else:
|
||||||
|
print_warning("Failed to install daytona SDK — install manually:")
|
||||||
|
print_info(' pip install daytona')
|
||||||
|
|
||||||
|
daytona_image = prompt(" Container image", default_daytona)
|
||||||
|
config['terminal']['daytona_image'] = daytona_image
|
||||||
|
|
||||||
|
current_key = get_env_value('DAYTONA_API_KEY')
|
||||||
|
if current_key:
|
||||||
|
print_info(f" API Key: {current_key[:8]}... (configured)")
|
||||||
|
|
||||||
|
api_key = prompt(" Daytona API key", current_key or "", password=True)
|
||||||
|
if api_key:
|
||||||
|
save_env_value("DAYTONA_API_KEY", api_key)
|
||||||
|
|
||||||
|
_prompt_container_resources(config)
|
||||||
|
print_success("Terminal set to Daytona")
|
||||||
|
|
||||||
elif selected_backend == 'ssh':
|
elif selected_backend == 'ssh':
|
||||||
config.setdefault('terminal', {})['backend'] = 'ssh'
|
config.setdefault('terminal', {})['backend'] = 'ssh'
|
||||||
print_info("SSH Remote Execution Configuration:")
|
print_info("SSH Remote Execution Configuration:")
|
||||||
|
|
@ -1181,7 +1227,7 @@ def run_setup_wizard(args):
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print_info("Note: Container resource settings (CPU, memory, disk, persistence)")
|
print_info("Note: Container resource settings (CPU, memory, disk, persistence)")
|
||||||
print_info("are in your config but only apply to Docker/Singularity/Modal backends.")
|
print_info("are in your config but only apply to Docker/Singularity/Modal/Daytona backends.")
|
||||||
print_success("Terminal set to SSH")
|
print_success("Terminal set to SSH")
|
||||||
# else: Keep current (selected_backend is None)
|
# else: Keep current (selected_backend is None)
|
||||||
|
|
||||||
|
|
@ -1192,6 +1238,9 @@ def run_setup_wizard(args):
|
||||||
docker_image = config.get('terminal', {}).get('docker_image')
|
docker_image = config.get('terminal', {}).get('docker_image')
|
||||||
if docker_image:
|
if docker_image:
|
||||||
save_env_value("TERMINAL_DOCKER_IMAGE", docker_image)
|
save_env_value("TERMINAL_DOCKER_IMAGE", docker_image)
|
||||||
|
daytona_image = config.get('terminal', {}).get('daytona_image')
|
||||||
|
if daytona_image:
|
||||||
|
save_env_value("TERMINAL_DAYTONA_IMAGE", daytona_image)
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Step 5: Agent Settings
|
# Step 5: Agent Settings
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,9 @@ def show_status(args):
|
||||||
elif terminal_env == "docker":
|
elif terminal_env == "docker":
|
||||||
docker_image = os.getenv("TERMINAL_DOCKER_IMAGE", "python:3.11-slim")
|
docker_image = os.getenv("TERMINAL_DOCKER_IMAGE", "python:3.11-slim")
|
||||||
print(f" Docker Image: {docker_image}")
|
print(f" Docker Image: {docker_image}")
|
||||||
|
elif terminal_env == "daytona":
|
||||||
|
daytona_image = os.getenv("TERMINAL_DAYTONA_IMAGE", "nikolaik/python-nodejs:python3.11-nodejs20")
|
||||||
|
print(f" Daytona Image: {daytona_image}")
|
||||||
|
|
||||||
sudo_password = os.getenv("SUDO_PASSWORD", "")
|
sudo_password = os.getenv("SUDO_PASSWORD", "")
|
||||||
print(f" Sudo: {check_mark(bool(sudo_password))} {'enabled' if sudo_password else 'disabled'}")
|
print(f" Sudo: {check_mark(bool(sudo_password))} {'enabled' if sudo_password else 'disabled'}")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue