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:
@@ -116,8 +116,22 @@ terminal:
|
||||
# timeout: 180
|
||||
# lifetime_seconds: 300
|
||||
# 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
|
||||
# allocated to the sandbox and whether its filesystem persists across sessions.
|
||||
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)")
|
||||
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)
|
||||
if shutil.which("node"):
|
||||
check_ok("Node.js")
|
||||
|
||||
@@ -980,19 +980,20 @@ def run_setup_wizard(args):
|
||||
|
||||
terminal_choices.extend([
|
||||
"Modal (cloud execution, GPU access, serverless)",
|
||||
"Daytona (cloud sandboxes, persistent workspaces)",
|
||||
"SSH (run commands on a remote server)",
|
||||
f"Keep current ({current_backend})"
|
||||
])
|
||||
|
||||
# Build index map based on available choices
|
||||
if is_linux:
|
||||
backend_to_idx = {'local': 0, 'docker': 1, 'singularity': 2, 'modal': 3, 'ssh': 4}
|
||||
idx_to_backend = {0: 'local', 1: 'docker', 2: 'singularity', 3: 'modal', 4: 'ssh'}
|
||||
keep_current_idx = 5
|
||||
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: 'daytona', 5: 'ssh'}
|
||||
keep_current_idx = 6
|
||||
else:
|
||||
backend_to_idx = {'local': 0, 'docker': 1, 'modal': 2, 'ssh': 3}
|
||||
idx_to_backend = {0: 'local', 1: 'docker', 2: 'modal', 3: 'ssh'}
|
||||
keep_current_idx = 4
|
||||
backend_to_idx = {'local': 0, 'docker': 1, 'modal': 2, 'daytona': 3, 'ssh': 4}
|
||||
idx_to_backend = {0: 'local', 1: 'docker', 2: 'modal', 3: 'daytona', 4: 'ssh'}
|
||||
keep_current_idx = 5
|
||||
if current_backend == 'singularity':
|
||||
print_warning("Singularity is only available on Linux - please select a different backend")
|
||||
|
||||
@@ -1067,7 +1068,7 @@ def run_setup_wizard(args):
|
||||
|
||||
print()
|
||||
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):
|
||||
print_warning(" SECURITY WARNING: Sudo password will be stored in plaintext")
|
||||
@@ -1151,7 +1152,52 @@ def run_setup_wizard(args):
|
||||
|
||||
_prompt_container_resources(config)
|
||||
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':
|
||||
config.setdefault('terminal', {})['backend'] = 'ssh'
|
||||
print_info("SSH Remote Execution Configuration:")
|
||||
@@ -1181,7 +1227,7 @@ def run_setup_wizard(args):
|
||||
|
||||
print()
|
||||
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")
|
||||
# else: Keep current (selected_backend is None)
|
||||
|
||||
@@ -1192,6 +1238,9 @@ def run_setup_wizard(args):
|
||||
docker_image = config.get('terminal', {}).get('docker_image')
|
||||
if 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
|
||||
|
||||
@@ -163,6 +163,9 @@ def show_status(args):
|
||||
elif terminal_env == "docker":
|
||||
docker_image = os.getenv("TERMINAL_DOCKER_IMAGE", "python:3.11-slim")
|
||||
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", "")
|
||||
print(f" Sudo: {check_mark(bool(sudo_password))} {'enabled' if sudo_password else 'disabled'}")
|
||||
|
||||
Reference in New Issue
Block a user