|
| 1 | +Param([switch]$Headless) |
| 2 | + |
| 3 | +# --- SOTA Headless Standard --- |
| 4 | +if ($Headless -and ($Host.UI.RawUI.WindowTitle -notmatch 'Hidden')) { |
| 5 | + Start-Process pwsh -ArgumentList '-NoProfile', '-File', $PSCommandPath, '-Headless' -WindowStyle Hidden |
| 6 | + exit |
| 7 | +} |
| 8 | +$WindowStyle = if ($Headless) { 'Hidden' } else { 'Normal' } |
| 9 | +# ------------------------------ |
| 10 | + |
| 11 | +# Webapp Start - Standardized SOTA (Auto-Repaired V2.5) |
| 12 | +$WebPort = 10700 |
| 13 | +$BackendPort = 10701 |
| 14 | +$ProjectRoot = Split-Path -Parent $PSScriptRoot |
| 15 | + |
| 16 | +# 1. Kill any process squatting on the ports |
| 17 | +Write-Host "Checking for port squatters on $WebPort and $BackendPort..." -ForegroundColor Yellow |
| 18 | +$pids = Get-NetTCPConnection -LocalPort $WebPort, $BackendPort -ErrorAction SilentlyContinue | Where-Object { $_.OwningProcess -gt 4 } | Select-Object -ExpandProperty OwningProcess -Unique |
| 19 | +foreach ($p in $pids) { |
| 20 | + Write-Host "Found squatter (PID: $p). Terminating..." -ForegroundColor Red |
| 21 | + try { Stop-Process -Id $p -Force -ErrorAction Stop } catch { Write-Host "Warning: Could not terminate PID $p." -ForegroundColor Gray } |
| 22 | +} |
| 23 | + |
| 24 | +# 2. Setup |
| 25 | +Set-Location $PSScriptRoot |
| 26 | +if (Test-Path "frontend") { Set-Location "frontend" } |
| 27 | +if (-not (Test-Path "node_modules")) { npm install } |
| 28 | + |
| 29 | +# 3. Start the Python backend (Background) |
| 30 | +Write-Host "Starting Python backend on port $BackendPort ..." -ForegroundColor Cyan |
| 31 | + |
| 32 | +# uv --project finds package; CWD stays webapp (no repo-root run). |
| 33 | +$backendCmd = "Set-Location '$PSScriptRoot'; uv run --project '$ProjectRoot' uvicorn virtualization_mcp.web.app:app --host 127.0.0.1 --port $BackendPort --log-level info" |
| 34 | + |
| 35 | +Start-Process powershell -ArgumentList "-NoExit", "-Command", $backendCmd -WindowStyle Normal |
| 36 | + |
| 37 | +# 4. Wait for backend to be listening (avoid ECONNREFUSED when frontend loads) |
| 38 | +$healthUrl = "http://127.0.0.1:$BackendPort/api/v1/health" |
| 39 | +$maxAttempts = 15 |
| 40 | +$attempt = 0 |
| 41 | +Write-Host "Waiting for backend at $healthUrl ..." -ForegroundColor Cyan |
| 42 | +while ($attempt -lt $maxAttempts) { |
| 43 | + try { |
| 44 | + $null = Invoke-WebRequest -Uri $healthUrl -UseBasicParsing -TimeoutSec 2 -ErrorAction Stop |
| 45 | + Write-Host "Backend is up." -ForegroundColor Green |
| 46 | + break |
| 47 | + } catch { |
| 48 | + $attempt++ |
| 49 | + if ($attempt -ge $maxAttempts) { |
| 50 | + Write-Host "Backend did not respond after ${maxAttempts} attempts. Starting frontend anyway." -ForegroundColor Yellow |
| 51 | + break |
| 52 | + } |
| 53 | + Start-Sleep -Seconds 2 |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +# 5. Run server (Vite dev) |
| 58 | +Write-Host "Starting Vite frontend on port $WebPort ..." -ForegroundColor Green |
| 59 | +if (Test-Path "frontend") { Set-Location "frontend" } |
| 60 | + |
| 61 | +# 4b. Launch background task to open browser once frontend is ready (Auto-opened by Antigravity) |
| 62 | +$frontendUrl = "http://127.0.0.1:$WebPort/" |
| 63 | +$pollAndOpen = "for (`$i = 0; `$i -lt 60; `$i++) { try { `$null = Invoke-WebRequest -Uri '$frontendUrl' -TimeoutSec 2 -UseBasicParsing -ErrorAction Stop; Start-Process '$frontendUrl'; exit } catch { Start-Sleep -Seconds 1 } }" |
| 64 | +Start-Process powershell -ArgumentList "-NoProfile", "-WindowStyle", "Hidden", "-Command", $pollAndOpen |
| 65 | + |
| 66 | +Write-Host "Browser will open automatically when Vite is ready." -ForegroundColor Gray |
| 67 | +npm run dev -- --port $WebPort --host |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | + |
0 commit comments