# Reorganize E:\wwwroot into admin\current and agent\current # Do not touch C:\yutongda. Keep ports 8080. Only recycle test agent 8910. $ErrorActionPreference = "Stop" $Root = "E:\wwwroot" $Stamp = Get-Date -Format "yyyyMMdd-HHmmss" $OldReleaseId = "scaffold-agent-20260912-152641" $OldAgentHome = Join-Path $Root "releases\$OldReleaseId\agent" $NginxConf = "C:\nginx-1.18.0\myconf\ai.conf" $IncomingConf = "C:\Users\ytd_extdev_0903\incoming\ai.conf.cutover-8910" function Step([string]$m) { Write-Output "=== $m ===" } if (-not (Test-Path $Root)) { throw "missing $Root" } Step "stop 8910" Get-NetTCPConnection -LocalPort 8910 -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue } Get-CimInstance Win32_Process -Filter "Name='cmd.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -and $_.CommandLine -like '*run-agent-http*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue } Start-Sleep -Seconds 2 Step "create layout" New-Item -ItemType Directory -Force -Path @( "$Root\admin\releases", "$Root\agent\releases", "$Root\ops", "$Root\logs" ) | Out-Null # --- admin: dist -> releases//dist + current junction --- $AdminRelId = "admin-$Stamp" $AdminRelDist = Join-Path $Root "admin\releases\$AdminRelId\dist" if (Test-Path "$Root\admin\dist") { New-Item -ItemType Directory -Force -Path (Split-Path $AdminRelDist) | Out-Null Move-Item "$Root\admin\dist" $AdminRelDist Write-Output "MOVED_ADMIN_DIST=$AdminRelDist" } elseif (Test-Path "$Root\admin\current") { # already current; ensure releases copy exists if (-not (Test-Path $AdminRelDist)) { New-Item -ItemType Directory -Force -Path (Split-Path $AdminRelDist) | Out-Null robocopy "$Root\admin\current" $AdminRelDist /E /NFL /NDL /NJH /NJS /nc /ns /np | Out-Null } } else { throw "admin dist/current missing" } $AdminCurrent = Join-Path $Root "admin\current" if (Test-Path $AdminCurrent) { cmd /c "rmdir `"$AdminCurrent`"" | Out-Null if (Test-Path $AdminCurrent) { Remove-Item $AdminCurrent -Recurse -Force } } cmd /c "mklink /J `"$AdminCurrent`" `"$AdminRelDist`"" if (-not (Test-Path (Join-Path $AdminCurrent "index.html"))) { throw "admin current index missing" } Write-Output "ADMIN_CURRENT_OK" # --- agent: old releases -> agent\releases + current junction --- $AgentRelRoot = Join-Path $Root "agent\releases\$OldReleaseId" $AgentHome = Join-Path $AgentRelRoot "agent" if (Test-Path $OldAgentHome) { if (-not (Test-Path $AgentRelRoot)) { New-Item -ItemType Directory -Force -Path (Split-Path $AgentRelRoot) | Out-Null Move-Item (Join-Path $Root "releases\$OldReleaseId") $AgentRelRoot Write-Output "MOVED_AGENT_RELEASE=$AgentRelRoot" } } elseif (-not (Test-Path $AgentHome)) { throw "agent home missing: $OldAgentHome and $AgentHome" } # move leftover top-level releases if empty leftovers $OldReleases = Join-Path $Root "releases" if (Test-Path $OldReleases) { Get-ChildItem $OldReleases -Directory -ErrorAction SilentlyContinue | ForEach-Object { $dest = Join-Path "$Root\agent\releases" $_.Name if (-not (Test-Path $dest)) { Move-Item $_.FullName $dest Write-Output "MOVED_EXTRA=$($_.Name)" } } $left = @(Get-ChildItem $OldReleases -Force -ErrorAction SilentlyContinue) if ($left.Count -eq 0) { Remove-Item $OldReleases -Force -ErrorAction SilentlyContinue } } $AgentCurrent = Join-Path $Root "agent\current" if (Test-Path $AgentCurrent) { cmd /c "rmdir `"$AgentCurrent`"" | Out-Null if (Test-Path $AgentCurrent) { Remove-Item $AgentCurrent -Recurse -Force } } cmd /c "mklink /J `"$AgentCurrent`" `"$AgentHome`"" if (-not (Test-Path (Join-Path $AgentCurrent "agent\http_app.py"))) { throw "agent current http_app missing" } Write-Output "AGENT_CURRENT_OK" Step "rewrite run-agent-http.cmd" $runCmd = @" @echo off cd /d "$AgentCurrent" set YTD_ENV=test set AGENT_HTTP_PORT=8910 :loop "$AgentCurrent\.venv\Scripts\python.exe" -m agent.http_app >> "$Root\logs\agent-http.out.log" 2>> "$Root\logs\agent-http.err.log" echo agent exited, restart in 3s >> "$Root\logs\agent-http.err.log" timeout /t 3 /nobreak >nul goto loop "@ Set-Content -Path "$Root\ops\run-agent-http.cmd" -Value $runCmd -Encoding ASCII $pointer = @{ root = $Root adminCurrent = $AdminCurrent adminRelease = $AdminRelDist agentCurrent = $AgentCurrent agentHome = $AgentHome releaseId = $OldReleaseId updatedAt = (Get-Date).ToString("o") pid = 0 } | ConvertTo-Json Set-Content -Path "$Root\ops\active-release.json" -Value $pointer -Encoding UTF8 # ensure .env next to agent current if ((Test-Path "$Root\.env") -and -not (Test-Path "$AgentCurrent\.env")) { Copy-Item "$Root\.env" "$AgentCurrent\.env" -Force } Step "patch nginx conf (no BOM)" if (-not (Test-Path $IncomingConf)) { throw "missing $IncomingConf" } Copy-Item $NginxConf "$NginxConf.bak-before-layout-$Stamp" -Force $bytes = [System.IO.File]::ReadAllBytes($IncomingConf) if ($bytes.Length -ge 3 -and $bytes[0] -eq 239 -and $bytes[1] -eq 187 -and $bytes[2] -eq 191) { $list = New-Object System.Collections.Generic.List[byte] for ($i = 3; $i -lt $bytes.Length; $i++) { [void]$list.Add($bytes[$i]) } $bytes = $list.ToArray() } [System.IO.File]::WriteAllBytes($NginxConf, $bytes) Set-Location "C:\nginx-1.18.0" & .\nginx.exe -t if ($LASTEXITCODE -ne 0) { throw "nginx -t failed" } # Prefer reload via nginx.exe -s reload; fallback bounce without hung Restart-Service $ngxOk = $false try { & .\nginx.exe -s reload Start-Sleep -Seconds 1 if (Get-Process nginx -ErrorAction SilentlyContinue) { $ngxOk = $true; Write-Output "NGINX_RELOADED" } } catch { Write-Output "RELOAD_FAIL" } if (-not $ngxOk) { Get-Process nginx -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 1 try { Start-Service Nginx1.18 -ErrorAction Stop; Start-Sleep -Seconds 2 } catch { } if (-not (Get-Process nginx -ErrorAction SilentlyContinue)) { Start-Process -FilePath "C:\nginx-1.18.0\nginx.exe" -WorkingDirectory "C:\nginx-1.18.0" -WindowStyle Hidden Start-Sleep -Seconds 2 } if (-not (Get-Process nginx -ErrorAction SilentlyContinue)) { throw "nginx not running" } Write-Output "NGINX_RESTARTED_EXE" } Step "start agent" $wmi = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = "cmd.exe /c `"$Root\ops\run-agent-http.cmd`"" } Write-Output "WMI_Return=$($wmi.ReturnValue) ProcessId=$($wmi.ProcessId)" Start-Sleep -Seconds 6 $h = Invoke-WebRequest -Uri "http://127.0.0.1:8910/health" -UseBasicParsing -TimeoutSec 8 Write-Output "HEALTH=$($h.StatusCode) $($h.Content)" $listen = Get-NetTCPConnection -LocalPort 8910 -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1 if ($listen) { $obj = Get-Content "$Root\ops\active-release.json" -Raw | ConvertFrom-Json $obj.pid = $listen.OwningProcess $obj | ConvertTo-Json | Set-Content "$Root\ops\active-release.json" -Encoding UTF8 } Step "tree top" Get-ChildItem $Root | Select-Object Mode, Name Get-Item "$Root\admin\current","$Root\agent\current" | Format-List FullName, LinkType, Target cmd /c "curl.exe -sk -H `"Host: ai.ytd-scm.com`" https://127.0.0.1/agent/health" Write-Output "" cmd /c "curl.exe -sk -o NUL -w root:%{http_code} -H `"Host: ai.ytd-scm.com`" https://127.0.0.1/" Write-Output "" Write-Output "LAYOUT_DONE"