Files
ai_site/scripts/Stop-Stack.ps1
whm b04b180d30 feat: harden loose-offline sync for user JWT, schema, and console ops
Enable Binding-scoped agent push/pull, empty-table schema ensure, SyncPage inspect/drop-table, default module import, and agent-bound publish docs from the 宇恒联调意见.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 09:47:35 +08:00

195 lines
6.8 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Unified stack teardown: Docker containers (if any) + ALL native leftovers.
# Used by stop.ps1 / restart.ps1 / start.ps1 / start-host.ps1.
# Console messages are ASCII-only to avoid GBK mojibake.
param(
[switch]$Quiet
)
$ErrorActionPreference = "SilentlyContinue"
$Root = Split-Path -Parent $PSScriptRoot
if (-not (Test-Path (Join-Path $Root "start.ps1"))) {
# allow calling from repo root as -File scripts\Stop-Stack.ps1
if (Test-Path (Join-Path $PSScriptRoot "..\start.ps1")) {
$Root = Resolve-Path (Join-Path $PSScriptRoot "..")
}
}
Set-Location -LiteralPath $Root
$LogDir = Join-Path $Root ".runtime\logs"
function Write-Info([string]$msg) {
if (-not $Quiet) { Write-Host $msg }
}
function Kill-Tree([int]$ProcessId) {
if ($ProcessId -le 0) { return }
# Skip self / critical system
if ($ProcessId -eq $PID) { return }
& taskkill.exe /F /T /PID $ProcessId 2>$null | Out-Null
Stop-Process -Id $ProcessId -Force -ErrorAction SilentlyContinue
}
function Kill-Port([int]$Port) {
$pids = @()
Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue | ForEach-Object {
$pids += [int]$_.OwningProcess
}
$net = & netstat.exe -ano 2>$null
foreach ($line in $net) {
if ($line -notmatch [regex]::Escape(":${Port}")) { continue }
if ($line -notmatch "LISTENING") { continue }
$parts = ($line -split "\s+") | Where-Object { $_ }
$procId = 0
if ([int]::TryParse($parts[-1], [ref]$procId) -and $procId -gt 0) {
$pids += $procId
}
}
foreach ($procId in ($pids | Select-Object -Unique)) {
try {
$proc = Get-Process -Id $procId -ErrorAction Stop
} catch {
continue
}
Write-Info (" free port {0} (pid={1} {2})" -f $Port, $procId, $proc.ProcessName)
Kill-Tree $procId
}
}
function Kill-ByCommand([string]$ProcessName, [string]$Pattern) {
# WMI/CIM 在 Docker 半挂或本机负载高时会卡死;用限时 Job超时则跳过
$job = Start-Job -ScriptBlock {
param($ProcessName, $Pattern)
Get-CimInstance Win32_Process -Filter ("Name='{0}'" -f $ProcessName) -ErrorAction SilentlyContinue | Where-Object {
$_.CommandLine -and ($_.CommandLine -match $Pattern)
} | ForEach-Object { [int]$_.ProcessId }
} -ArgumentList $ProcessName, $Pattern
if (-not (Wait-Job $job -Timeout 5)) {
Stop-Job $job -ErrorAction SilentlyContinue
Remove-Job $job -Force -ErrorAction SilentlyContinue
return
}
$ids = @(Receive-Job $job -ErrorAction SilentlyContinue)
Remove-Job $job -Force -ErrorAction SilentlyContinue
foreach ($procId in $ids) {
if ($procId -gt 0) {
Write-Info (" kill {0} pid={1}" -f $ProcessName, $procId)
Kill-Tree $procId
}
}
}
# ----- 1) Docker first (do NOT return; native may still be running) -----
# Docker Desktop 半挂时 `docker info` 会无限阻塞,必须限时。
$dockerOk = $false
try {
$outFile = Join-Path $env:TEMP "aijz-docker-info.out"
$errFile = Join-Path $env:TEMP "aijz-docker-info.err"
$p = Start-Process -FilePath "docker" -ArgumentList @("info") -NoNewWindow -PassThru `
-RedirectStandardOutput $outFile -RedirectStandardError $errFile
if (-not $p.WaitForExit(8000)) {
try { Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue } catch {}
Write-Info " docker info timeout (8s); skip compose down"
$dockerOk = $false
} else {
$dockerOk = ($p.ExitCode -eq 0)
}
} catch {
$dockerOk = $false
}
if ($dockerOk) {
Write-Info "==> docker compose down --remove-orphans"
& docker compose down --remove-orphans 2>$null
# also stop any leftover named containers from older compose projects
$names = @("ai-web-1", "ai-ai-1", "ai-gateway-1", "ai-platform-1", "ai-postgres-1")
foreach ($n in $names) {
& docker rm -f $n 2>$null | Out-Null
}
}
# ----- 2) Watchdog first (otherwise it revives ai) -----
Write-Info "==> stopping native processes..."
$watchPidFile = Join-Path $LogDir "watchdog.pid"
if (Test-Path $watchPidFile) {
$wid = Get-Content $watchPidFile -ErrorAction SilentlyContinue
if ($wid) {
Write-Info (" kill watchdog pid={0}" -f $wid)
Kill-Tree ([int]$wid)
}
Remove-Item $watchPidFile -Force -ErrorAction SilentlyContinue
}
Kill-ByCommand "powershell.exe" "watch-services\.ps1"
Kill-ByCommand "pwsh.exe" "watch-services\.ps1"
Kill-ByCommand "powershell.exe" "start-host\.ps1"
Kill-ByCommand "powershell.exe" "Restart-AiOnly|restart_ai_only|hard_restart"
# ----- 3) Pid files -----
foreach ($name in @("ai-service.pid", "platform.pid", "gateway.pid", "web.pid", "watchdog.pid")) {
$pf = Join-Path $LogDir $name
if (Test-Path $pf) {
$pidText = Get-Content $pf -ErrorAction SilentlyContinue
if ($pidText) {
Write-Info (" kill {0} -> {1}" -f $name, $pidText)
Kill-Tree ([int]$pidText)
}
Remove-Item $pf -Force -ErrorAction SilentlyContinue
}
}
# ----- 4) By process name / cmdline -----
Get-Process -Name "platform", "gateway" -ErrorAction SilentlyContinue | ForEach-Object {
Write-Info (" kill {0} pid={1}" -f $_.ProcessName, $_.Id)
Kill-Tree ([int]$_.Id)
}
# uvicorn reloader = parent + workers
Kill-ByCommand "python.exe" "uvicorn"
Kill-ByCommand "python.exe" "app:app"
Kill-ByCommand "python.exe" "shot_worker\.py"
Kill-ByCommand "python.exe" "fidelity_loop|FIDELITY_SHOT"
Kill-ByCommand "python.exe" "ai-service"
# vite / node tooling for this repo
Kill-ByCommand "node.exe" "vite"
Kill-ByCommand "node.exe" "esbuild"
Kill-ByCommand "cmd.exe" "npm.*vite|vite\.js"
# Playwright Chromium leftovers from fidelity shots限时避免 CIM 卡死)
Kill-ByCommand "chrome.exe" "ms-playwright|playwright"
Kill-ByCommand "chromium.exe" "ms-playwright|playwright"
# ----- 5) Ports (authoritative) -----
foreach ($port in 8888, 8001, 8180, 5173) {
Kill-Port $port
}
Start-Sleep -Seconds 2
# second pass for stubborn reloaders
Kill-ByCommand "python.exe" "uvicorn|app:app|shot_worker"
Kill-ByCommand "node.exe" "vite"
foreach ($port in 8888, 8001, 8180, 5173) {
Kill-Port $port
}
Start-Sleep -Seconds 1
# ----- 6) Verify (only live processes count) -----
$still = @()
foreach ($port in 8888, 8001, 8180, 5173) {
Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue | ForEach-Object {
$proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
if ($proc) {
$still += (":{0} pid={1} {2}" -f $port, $_.OwningProcess, $proc.ProcessName)
}
}
}
if ($still.Count -gt 0) {
Write-Host "WARN still listening after stop:" -ForegroundColor Yellow
$still | ForEach-Object { Write-Host (" {0}" -f $_) }
exit 1
}
Write-Info "OK stopped (docker + native; ports 8888/8001/8180/5173 free)"
exit 0