Files
Dorod-Sky/skyvern/cli/run_commands.py
2025-05-22 03:12:32 -04:00

129 lines
4.5 KiB
Python

import os
import shutil
import subprocess
from pathlib import Path
from typing import List
import psutil
import typer
import uvicorn
from dotenv import load_dotenv, set_key
from mcp.server.fastmcp import FastMCP
from rich.panel import Panel
from rich.prompt import Confirm
from skyvern.utils import detect_os
from .console import console
run_app = typer.Typer(help="Commands to run Skyvern services such as the API server or UI.")
mcp = FastMCP("Skyvern")
def get_pids_on_port(port: int) -> List[int]:
"""Return a list of PIDs listening on the given port."""
pids = []
try:
for conn in psutil.net_connections(kind="inet"):
if conn.laddr and conn.laddr.port == port and conn.pid:
pids.append(conn.pid)
except Exception:
pass
return list(set(pids))
def kill_pids(pids: List[int]) -> None:
"""Kill the given list of PIDs in a cross-platform way."""
host_system = detect_os()
for pid in pids:
try:
if host_system in {"windows", "wsl"}:
subprocess.run(f"taskkill /PID {pid} /F", shell=True, check=False)
else:
os.kill(pid, 9)
except Exception:
console.print(f"[red]Failed to kill process {pid}[/red]")
@run_app.command(name="server")
def run_server() -> None:
"""Run the Skyvern API server."""
load_dotenv()
load_dotenv(".env")
from skyvern.config import settings
port = settings.PORT
console.print(Panel(f"[bold green]Starting Skyvern API Server on port {port}...", border_style="green"))
uvicorn.run(
"skyvern.forge.api_app:app",
host="0.0.0.0",
port=port,
log_level="info",
)
@run_app.command(name="ui")
def run_ui() -> None:
"""Run the Skyvern UI server."""
console.print(Panel("[bold blue]Starting Skyvern UI Server...[/bold blue]", border_style="blue"))
try:
with console.status("[bold green]Checking for existing process on port 8080...") as status:
pids = get_pids_on_port(8080)
if pids:
status.stop()
response = Confirm.ask("Process already running on port 8080. [yellow]Kill it?[/yellow]")
if response:
kill_pids(pids)
console.print("✅ [green]Process killed.[/green]")
else:
console.print("[yellow]UI server not started. Process already running on port 8080.[/yellow]")
return
status.stop()
except Exception as e: # pragma: no cover - CLI safeguards
console.print(f"[red]Error checking for process: {e}[/red]")
current_dir = Path(__file__).parent.parent.parent
frontend_dir = current_dir / "skyvern-frontend"
if not frontend_dir.exists():
console.print(
f"[bold red]ERROR: Skyvern Frontend directory not found at [path]{frontend_dir}[/path]. Are you in the right repo?[/bold red]"
)
return
if not (frontend_dir / ".env").exists():
console.print("[bold blue]Setting up frontend .env file...[/bold blue]")
shutil.copy(frontend_dir / ".env.example", frontend_dir / ".env")
main_env_path = current_dir / ".env"
if main_env_path.exists():
load_dotenv(main_env_path)
skyvern_api_key = os.getenv("SKYVERN_API_KEY")
if skyvern_api_key:
frontend_env_path = frontend_dir / ".env"
set_key(str(frontend_env_path), "VITE_SKYVERN_API_KEY", skyvern_api_key)
else:
console.print("[red]ERROR: SKYVERN_API_KEY not found in .env file[/red]")
else:
console.print("[red]ERROR: .env file not found[/red]")
console.print("✅ [green]Successfully set up frontend .env file[/green]")
os.chdir(frontend_dir)
try:
console.print("📦 [bold blue]Running npm install...[/bold blue]")
subprocess.run("npm install --silent", shell=True, check=True)
console.print("✅ [green]npm install complete.[/green]")
console.print("🚀 [bold blue]Starting npm UI server...[/bold blue]")
subprocess.run("npm run start", shell=True, check=True)
except subprocess.CalledProcessError as e:
console.print(f"[bold red]Error running UI server: {e}[/bold red]")
return
@run_app.command(name="mcp")
def run_mcp() -> None:
"""Run the MCP server."""
console.print(Panel("[bold green]Starting MCP Server...[/bold green]", border_style="green"))
mcp.run(transport="stdio")