param( [Parameter(Position = 0)] [string]$Action = "help" ) $ErrorActionPreference = "Stop" $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $rootDir = (Resolve-Path (Join-Path $scriptDir "..")).Path $exePath = Join-Path $rootDir "managerd.exe" $configPath = Join-Path $rootDir "managerd.json" $logPath = Join-Path $rootDir "managerd.local.log" $errLogPath = Join-Path $rootDir "managerd.local.err.log" function Write-Info { param([string]$Message) Write-Host "[managerd] $Message" } function Get-ManagerdProcess { return Get-Process -Name "managerd" -ErrorAction SilentlyContinue | Select-Object -First 1 } function Get-HealthInfo { $listen = "127.0.0.1:18080" if (Test-Path $configPath) { $cfg = Get-Content -Raw $configPath | ConvertFrom-Json if ($cfg.listen -and -not [string]::IsNullOrWhiteSpace([string]$cfg.listen)) { $listen = [string]$cfg.listen } } $parts = $listen.Split(":") $hostName = $parts[0] $port = $parts[-1] if ([string]::IsNullOrWhiteSpace($hostName) -or $hostName -in @("0.0.0.0", "::")) { $hostName = "127.0.0.1" } $url = "http://{0}:{1}/health" -f $hostName, $port try { $resp = Invoke-WebRequest -UseBasicParsing $url -TimeoutSec 3 if ($resp.StatusCode -eq 200 -and $resp.Content -match "ok") { return [pscustomobject]@{ State = "ok"; Url = $url } } return [pscustomobject]@{ State = "bad"; Url = $url } } catch { return [pscustomobject]@{ State = "down"; Url = $url } } } function Wait-ForHealthOk { param([int]$Retries = 8) for ($i = 0; $i -lt $Retries; $i++) { $health = Get-HealthInfo if ($health.State -eq "ok") { return $true } Start-Sleep -Seconds 1 } return $false } function Wait-ForStop { param([int]$Retries = 8) for ($i = 0; $i -lt $Retries; $i++) { if (-not (Get-ManagerdProcess)) { return $true } Start-Sleep -Seconds 1 } return $false } function Show-Health { $health = Get-HealthInfo switch ($health.State) { "ok" { Write-Info ("健康检查: ok ({0})" -f $health.Url) } "bad" { Write-Info ("健康检查: 响应异常 ({0})" -f $health.Url) } default { Write-Info ("健康检查: down ({0})" -f $health.Url) } } } function Invoke-Build { Write-Info "编译 managerd.exe ..." Push-Location $rootDir try { & go build -o $exePath .\cmd\managerd if ($LASTEXITCODE -ne 0) { throw "go build failed with exit code $LASTEXITCODE" } } finally { Pop-Location } Write-Info "编译完成: $exePath" } function Invoke-Start { if (-not (Test-Path $exePath)) { throw "未找到 managerd.exe,请先执行 build" } $proc = Get-ManagerdProcess if ($proc) { Write-Info ("已在运行,PID={0}" -f $proc.Id) Show-Health return } Write-Info "启动 managerd.exe ..." Start-Process -FilePath $exePath ` -ArgumentList $configPath ` -WorkingDirectory $rootDir ` -RedirectStandardOutput $logPath ` -RedirectStandardError $errLogPath ` -WindowStyle Hidden $null = Wait-ForHealthOk $proc = Get-ManagerdProcess if (-not $proc) { throw "进程未启动成功" } Write-Info ("已启动,PID={0}" -f $proc.Id) Show-Health } function Invoke-Stop { $proc = Get-ManagerdProcess if (-not $proc) { Write-Info "当前未运行" return } Write-Info ("停止 managerd.exe,PID={0} ..." -f $proc.Id) Stop-Process -Id $proc.Id -Force if (-not (Wait-ForStop)) { throw "进程停止超时" } Write-Info "已停止" } function Invoke-Restart { Invoke-Stop Invoke-Start } function Show-Status { $proc = Get-ManagerdProcess if ($proc) { Write-Info ("进程状态: running (PID={0})" -f $proc.Id) } else { Write-Info "进程状态: stopped" } Show-Health } function Show-Usage { @( "用法:" " managerd.bat build" " managerd.bat start" " managerd.bat stop" " managerd.bat restart" " managerd.bat status" " managerd.bat help" "" "说明:" " build 编译 .\cmd\managerd 到 managerd.exe" " start 启动现有 managerd.exe,并做健康检查" " stop 停止当前仓库对应的 managerd.exe" " restart 先 stop 再 start" " status 查看进程状态与 /health" " help 显示帮助" ) | ForEach-Object { Write-Host $_ } } try { switch ($Action.ToLowerInvariant()) { "build" { Invoke-Build } "start" { Invoke-Start } "stop" { Invoke-Stop } "restart" { Invoke-Restart } "status" { Show-Status } "help" { Show-Usage } default { Write-Host "未知动作: $Action" Show-Usage exit 1 } } } catch { Write-Error $_ exit 1 }