# native process mode (called by start.ps1 when Docker is unavailable) param( [switch]$Rebuild, [switch]$NoBrowser, [switch]$ShowConsole # show service windows (debug); default: hidden ) $ErrorActionPreference = "Stop" $Root = $PSScriptRoot Set-Location -LiteralPath $Root $LogDir = Join-Path $Root ".runtime\logs" New-Item -ItemType Directory -Force -Path $LogDir | Out-Null # Always wipe previous native/docker leftovers before binding ports Write-Host "==> Pre-clean before native start" -ForegroundColor DarkGray & (Join-Path $Root "scripts\Stop-Stack.ps1") -Quiet Start-Sleep -Seconds 1 Write-Host "==> Native mode: need Go / Python / Node / PostgreSQL" -ForegroundColor Yellow if (-not $ShowConsole) { Write-Host " services run hidden; logs -> .runtime\logs\" -ForegroundColor DarkGray } function Clear-Port([int]$port) { $conns = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue foreach ($c in $conns) { $procId = $c.OwningProcess if ($procId -gt 0) { Write-Host " free port $port (pid=$procId)" & taskkill.exe /F /T /PID $procId 2>$null | Out-Null Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue } } } Write-Host "==> Clearing ports 8888/8001/8180/5173" foreach ($p in 8888, 8001, 8180, 5173) { Clear-Port $p } Start-Sleep -Seconds 1 function Reset-LogFile([string]$path) { try { if (Test-Path -LiteralPath $path) { # 句柄未释放时 Set-Content 会失败:先挪走旧文件 $prev = "$path.prev" Remove-Item -LiteralPath $prev -Force -ErrorAction SilentlyContinue Move-Item -LiteralPath $path -Destination $prev -Force -ErrorAction Stop } Set-Content -LiteralPath $path -Value "" -Encoding UTF8 return $path } catch { $alt = ($path -replace '\.log$', ".$(Get-Date -Format 'HHmmss').log") Set-Content -LiteralPath $alt -Value "" -Encoding UTF8 return $alt } } function Start-Svc($name, $workDir, $filePath, $argumentList) { $outLog = Reset-LogFile (Join-Path $LogDir "$name.out.log") $errLog = Reset-LogFile (Join-Path $LogDir "$name.err.log") $pidFile = Join-Path $LogDir "$name.pid" if ($ShowConsole) { $argStr = "" if ($argumentList) { $parts = @() foreach ($a in $argumentList) { if ($a -match '\s') { $parts += ('"' + $a + '"') } else { $parts += $a } } $argStr = $parts -join ' ' } $cmd = "Set-Location -LiteralPath '$workDir'; Write-Host '[$name]' -ForegroundColor Cyan; & '$filePath' $argStr" $p = Start-Process -FilePath "powershell.exe" -ArgumentList @( "-NoExit", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", $cmd ) -WorkingDirectory $workDir -PassThru } else { $p = Start-Process -FilePath $filePath ` -ArgumentList $argumentList ` -WorkingDirectory $workDir ` -WindowStyle Hidden ` -RedirectStandardOutput $outLog ` -RedirectStandardError $errLog ` -PassThru } if ($p) { Set-Content -LiteralPath $pidFile -Value $p.Id -Encoding ASCII } Write-Host " started $name (pid=$($p.Id))" -ForegroundColor DarkGray } $plat = Join-Path $Root "platform\platform.exe" $gw = Join-Path $Root "gateway\gateway.exe" # 默认重编译,避免改 Go 源码后 restart 仍跑旧 exe $needPlat = $Rebuild -or -not (Test-Path $plat) $needGw = $Rebuild -or -not (Test-Path $gw) if (-not $needPlat -and (Test-Path $plat)) { $exeTime = (Get-Item $plat).LastWriteTime $newer = Get-ChildItem -Path (Join-Path $Root "platform") -Recurse -Include *.go -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt $exeTime } | Select-Object -First 1 if ($newer) { $needPlat = $true } } if (-not $needGw -and (Test-Path $gw)) { $exeTime = (Get-Item $gw).LastWriteTime $newer = Get-ChildItem -Path (Join-Path $Root "gateway") -Recurse -Include *.go -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt $exeTime } | Select-Object -First 1 if ($newer) { $needGw = $true } } if ($needPlat) { Write-Host " building platform.exe ..." -ForegroundColor DarkGray Push-Location (Join-Path $Root "platform") go build -o platform.exe . Pop-Location } if ($needGw) { Write-Host " building gateway.exe ..." -ForegroundColor DarkGray Push-Location (Join-Path $Root "gateway") go build -o gateway.exe . Pop-Location } Write-Host "==> Ensure web frontend (vite /#/preview)" -ForegroundColor DarkGray & (Join-Path $Root "scripts\Ensure-Web.ps1") # $? 判断 PS 脚本成败;勿单靠 $LASTEXITCODE(会残留 docker/compose 等 native 失败码) if (-not $?) { throw "Ensure-Web failed" } $pyCmd = Get-Command python -ErrorAction SilentlyContinue if (-not $pyCmd) { $pyCmd = Get-Command py -ErrorAction SilentlyContinue } if (-not $pyCmd) { throw "python not found" } $py = $pyCmd.Source $npmCmd = Get-Command npm.cmd -ErrorAction SilentlyContinue if (-not $npmCmd) { $npmCmd = Get-Command npm -ErrorAction SilentlyContinue } if (-not $npmCmd) { throw "npm not found" } # AI 截图直达本机 Vite $env:WEB_BASE = "http://127.0.0.1:5173" $env:PLATFORM_BASE = "http://127.0.0.1:8888" $env:GATEWAY_BASE = "http://127.0.0.1:8180" $env:GENERATE_LOG_DIR = Join-Path $Root ".runtime\logs\generate" $env:FIDELITY_SHOT_DIR = Join-Path $Root ".runtime\fidelity_shots" Start-Svc "platform" (Join-Path $Root "platform") $plat @("-f", "etc/platform.yaml") Start-Sleep -Seconds 2 Start-Svc "ai-service" (Join-Path $Root "ai-service") $py @("-m", "uvicorn", "app:app", "--host", "127.0.0.1", "--port", "8001", "--reload") Start-Sleep -Seconds 1 Start-Svc "gateway" (Join-Path $Root "gateway") $gw @("-f", "etc/gateway.yaml") Start-Sleep -Seconds 1 Start-Svc "web" (Join-Path $Root "web") $npmCmd.Source @("run", "dev", "--", "--host", "127.0.0.1", "--port", "5173", "--strictPort") Write-Host "==> Waiting for services..." $deadline = (Get-Date).AddSeconds(45) do { $webOk = $false $gwOk = $false try { $r = Invoke-WebRequest -Uri "http://127.0.0.1:5173/" -UseBasicParsing -TimeoutSec 2 if ($r.StatusCode -lt 500) { $webOk = $true } } catch {} try { $r = Invoke-WebRequest -Uri "http://127.0.0.1:8180/gateway/health" -UseBasicParsing -TimeoutSec 2 if ($r.StatusCode -lt 500) { $gwOk = $true } } catch {} if ($webOk -and $gwOk) { break } Start-Sleep -Seconds 1 } while ((Get-Date) -lt $deadline) if ($webOk -and $gwOk) { Write-Host "OK Web http://127.0.0.1:5173 Gateway http://127.0.0.1:8180 account demo/demo123" -ForegroundColor Green Write-Host " logs $LogDir" -ForegroundColor DarkGray } else { Write-Host "WARN some services not ready yet. Check logs:" -ForegroundColor Yellow Write-Host " $LogDir" Write-Host " web=$webOk gateway=$gwOk" Write-Host " (debug with visible windows: .\start-host.ps1 -ShowConsole)" } # 后台看门狗:ai-service / gateway 挂掉自动拉起 $watchPidFile = Join-Path $LogDir "watchdog.pid" if (Test-Path $watchPidFile) { $oldWatch = Get-Content $watchPidFile -ErrorAction SilentlyContinue if ($oldWatch) { Stop-Process -Id ([int]$oldWatch) -Force -ErrorAction SilentlyContinue } } $watchScript = Join-Path $Root "watch-services.ps1" $watchOut = Join-Path $LogDir "watchdog.out.log" $watchErr = Join-Path $LogDir "watchdog.err.log" Set-Content -LiteralPath $watchOut -Value "" -Encoding UTF8 Set-Content -LiteralPath $watchErr -Value "" -Encoding UTF8 $wp = Start-Process -FilePath "powershell.exe" ` -ArgumentList @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $watchScript) ` -WorkingDirectory $Root ` -WindowStyle Hidden ` -RedirectStandardOutput $watchOut ` -RedirectStandardError $watchErr ` -PassThru if ($wp) { Set-Content -LiteralPath $watchPidFile -Value $wp.Id -Encoding ASCII Write-Host " started watchdog (pid=$($wp.Id)) - auto-restarts ai-service if down" -ForegroundColor DarkGray } if (-not $NoBrowser) { Start-Process "http://127.0.0.1:5173" }