From f857cbcf35846414d4413628712d455fa0257d9c Mon Sep 17 00:00:00 2001 From: Suchintan Date: Fri, 30 Jan 2026 22:13:39 -0500 Subject: [PATCH] Add skyvern run dev command and superset workspace setup (#4593) Co-authored-by: Suchintan Singh --- skyvern/cli/run_commands.py | 91 +++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/skyvern/cli/run_commands.py b/skyvern/cli/run_commands.py index e2521a94..8b599eca 100644 --- a/skyvern/cli/run_commands.py +++ b/skyvern/cli/run_commands.py @@ -163,12 +163,103 @@ def run_ui() -> None: return +@run_app.command(name="ui-dev") +def run_ui_dev() -> None: + """Run the Skyvern UI server in development mode (npm run start-local).""" + console.print(Panel("[bold blue]Starting Skyvern UI Server (dev mode)...[/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]") + + frontend_env_path = resolve_frontend_env_path() + if frontend_env_path is None: + console.print("[bold red]ERROR: Skyvern Frontend directory not found.[/bold red]") + return + + frontend_dir = frontend_env_path.parent + + os.chdir(frontend_dir) + + try: + console.print("šŸ“¦ [bold blue]Running npm ci...[/bold blue]") + subprocess.run("npm ci", shell=True, check=True) + console.print("āœ… [green]npm ci complete.[/green]") + console.print("šŸš€ [bold blue]Starting npm UI server (start-local)...[/bold blue]") + subprocess.run("npm run start-local", 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="all") def run_all() -> None: """Run the Skyvern API server and UI server in parallel.""" asyncio.run(start_services()) +@run_app.command(name="dev") +def run_dev() -> None: + """Run the Skyvern API server and UI server in the background (detached). + + This command starts both services and immediately returns control to your terminal. + Use 'skyvern stop all' to stop the services. + """ + load_dotenv(resolve_backend_env_path()) + from skyvern.config import settings as skyvern_settings # noqa: PLC0415 + + console.print(Panel("[bold green]Starting Skyvern in development mode...[/bold green]", border_style="green")) + + # Start server in background (detached) - call uvicorn directly + server_process = subprocess.Popen( + [ + "uvicorn", + "skyvern.forge.api_app:create_api_app", + "--host", + "0.0.0.0", + "--port", + str(skyvern_settings.PORT), + "--factory", + ], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + start_new_session=True, + ) + console.print(f"āœ… [green]Server started in background (PID: {server_process.pid})[/green]") + + # Start UI (dev mode) in background (detached) - call npm directly + frontend_env_path = resolve_frontend_env_path() + if frontend_env_path is None: + console.print("[bold red]ERROR: Skyvern Frontend directory not found.[/bold red]") + return + frontend_dir = frontend_env_path.parent + + ui_process = subprocess.Popen( + ["npm", "run", "start-local"], + cwd=frontend_dir, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + start_new_session=True, + ) + console.print(f"āœ… [green]UI (dev mode) started in background (PID: {ui_process.pid})[/green]") + + console.print("\nšŸŽ‰ [bold green]Skyvern is starting![/bold green]") + console.print(f"🌐 [bold]API server:[/bold] [cyan]http://localhost:{skyvern_settings.PORT}[/cyan]") + console.print("šŸ–„ļø [bold]UI:[/bold] [cyan]http://localhost:8080[/cyan]") + console.print("\n[dim]Use 'skyvern stop all' to stop the services.[/dim]") + + @run_app.command(name="mcp") def run_mcp() -> None: """Run the MCP server."""