@@ -163,12 +163,103 @@ def run_ui() -> None:
163163 return
164164
165165
166+ @run_app .command (name = "ui-dev" )
167+ def run_ui_dev () -> None :
168+ """Run the Skyvern UI server in development mode (npm run start-local)."""
169+ console .print (Panel ("[bold blue]Starting Skyvern UI Server (dev mode)...[/bold blue]" , border_style = "blue" ))
170+ try :
171+ with console .status ("[bold green]Checking for existing process on port 8080..." ) as status :
172+ pids = get_pids_on_port (8080 )
173+ if pids :
174+ status .stop ()
175+ response = Confirm .ask ("Process already running on port 8080. [yellow]Kill it?[/yellow]" )
176+ if response :
177+ kill_pids (pids )
178+ console .print ("✅ [green]Process killed.[/green]" )
179+ else :
180+ console .print ("[yellow]UI server not started. Process already running on port 8080.[/yellow]" )
181+ return
182+ status .stop ()
183+ except Exception as e : # pragma: no cover - CLI safeguards
184+ console .print (f"[red]Error checking for process: { e } [/red]" )
185+
186+ frontend_env_path = resolve_frontend_env_path ()
187+ if frontend_env_path is None :
188+ console .print ("[bold red]ERROR: Skyvern Frontend directory not found.[/bold red]" )
189+ return
190+
191+ frontend_dir = frontend_env_path .parent
192+
193+ os .chdir (frontend_dir )
194+
195+ try :
196+ console .print ("📦 [bold blue]Running npm ci...[/bold blue]" )
197+ subprocess .run ("npm ci" , shell = True , check = True )
198+ console .print ("✅ [green]npm ci complete.[/green]" )
199+ console .print ("🚀 [bold blue]Starting npm UI server (start-local)...[/bold blue]" )
200+ subprocess .run ("npm run start-local" , shell = True , check = True )
201+ except subprocess .CalledProcessError as e :
202+ console .print (f"[bold red]Error running UI server: { e } [/bold red]" )
203+ return
204+
205+
166206@run_app .command (name = "all" )
167207def run_all () -> None :
168208 """Run the Skyvern API server and UI server in parallel."""
169209 asyncio .run (start_services ())
170210
171211
212+ @run_app .command (name = "dev" )
213+ def run_dev () -> None :
214+ """Run the Skyvern API server and UI server in the background (detached).
215+
216+ This command starts both services and immediately returns control to your terminal.
217+ Use 'skyvern stop all' to stop the services.
218+ """
219+ load_dotenv (resolve_backend_env_path ())
220+ from skyvern .config import settings as skyvern_settings # noqa: PLC0415
221+
222+ console .print (Panel ("[bold green]Starting Skyvern in development mode...[/bold green]" , border_style = "green" ))
223+
224+ # Start server in background (detached) - call uvicorn directly
225+ server_process = subprocess .Popen (
226+ [
227+ "uvicorn" ,
228+ "skyvern.forge.api_app:create_api_app" ,
229+ "--host" ,
230+ "0.0.0.0" ,
231+ "--port" ,
232+ str (skyvern_settings .PORT ),
233+ "--factory" ,
234+ ],
235+ stdout = subprocess .DEVNULL ,
236+ stderr = subprocess .DEVNULL ,
237+ start_new_session = True ,
238+ )
239+ console .print (f"✅ [green]Server started in background (PID: { server_process .pid } )[/green]" )
240+
241+ # Start UI (dev mode) in background (detached) - call npm directly
242+ frontend_env_path = resolve_frontend_env_path ()
243+ if frontend_env_path is None :
244+ console .print ("[bold red]ERROR: Skyvern Frontend directory not found.[/bold red]" )
245+ return
246+ frontend_dir = frontend_env_path .parent
247+
248+ ui_process = subprocess .Popen (
249+ ["npm" , "run" , "start-local" ],
250+ cwd = frontend_dir ,
251+ stdout = subprocess .DEVNULL ,
252+ stderr = subprocess .DEVNULL ,
253+ start_new_session = True ,
254+ )
255+ console .print (f"✅ [green]UI (dev mode) started in background (PID: { ui_process .pid } )[/green]" )
256+
257+ console .print ("\n 🎉 [bold green]Skyvern is starting![/bold green]" )
258+ console .print (f"🌐 [bold]API server:[/bold] [cyan]http://localhost:{ skyvern_settings .PORT } [/cyan]" )
259+ console .print ("🖥️ [bold]UI:[/bold] [cyan]http://localhost:8080[/cyan]" )
260+ console .print ("\n [dim]Use 'skyvern stop all' to stop the services.[/dim]" )
261+
262+
172263@run_app .command (name = "mcp" )
173264def run_mcp () -> None :
174265 """Run the MCP server."""
0 commit comments