Files
skills-win/langgraph-plan-review/scripts/manage_dev_server.ps1
T
2026-06-22 16:41:44 +08:00

198 lines
7.3 KiB
PowerShell

<#
.SYNOPSIS
Manage the langgraph dev server on Windows.
.DESCRIPTION
Start, stop, or check the status of the langgraph dev server.
The server starts from the skill root (where langgraph.json lives) so the
graph module is discovered correctly.
.PARAMETER Command
start | stop | status
.EXAMPLE
manage_dev_server.ps1 start # idempotent — safe to call if already running
manage_dev_server.ps1 stop # call once workflow returns {"status":"done"}
manage_dev_server.ps1 status # print running/stopped + Studio URL
#>
param([string]$Command = "status")
$SkillDir = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$PidFile = Join-Path $env:TEMP "langgraph_dev_server.pid"
$StdoutLog = Join-Path $env:TEMP "langgraph_dev_server.stdout.log"
$StderrLog = Join-Path $env:TEMP "langgraph_dev_server.stderr.log"
$ApiUrl = "http://127.0.0.1:2024"
$MaxWait = 45 # seconds to wait for the server to become ready
# --- helpers -----------------------------------------------------------
function Test-HttpOk([string]$Url) {
try {
$r = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 3 -ErrorAction Stop
return ($r.StatusCode -ge 200 -and $r.StatusCode -lt 300)
} catch {
return $false
}
}
function Get-StoredPid {
if (-not (Test-Path $PidFile)) { return 0 }
$raw = (Get-Content $PidFile -Raw -ErrorAction SilentlyContinue).Trim()
if (-not $raw) { return 0 }
[int]$raw
}
function Test-ServerRunning {
$storedPid = Get-StoredPid
if ($storedPid -eq 0) { return $false }
$proc = Get-Process -Id $storedPid -ErrorAction SilentlyContinue
if (-not $proc) { return $false }
return (Test-HttpOk "$ApiUrl/ok")
}
function Find-LangGraphExe {
$up = $env:USERPROFILE
$candidates = @(
"$up\.conda\envs\langgraph\Scripts\langgraph.exe",
"$up\miniconda3\envs\langgraph\Scripts\langgraph.exe",
"$up\anaconda3\envs\langgraph\Scripts\langgraph.exe",
"$up\miniforge3\envs\langgraph\Scripts\langgraph.exe",
"$up\AppData\Local\miniconda3\envs\langgraph\Scripts\langgraph.exe",
"C:\ProgramData\miniconda3\envs\langgraph\Scripts\langgraph.exe",
"C:\ProgramData\anaconda3\envs\langgraph\Scripts\langgraph.exe"
)
foreach ($c in $candidates) {
if (Test-Path $c) { return $c }
}
# Try PATH
$cmd = Get-Command langgraph -ErrorAction SilentlyContinue
if ($cmd) { return $cmd.Source }
# Try resolving via conda
$condaCandidates = @($env:CONDA_EXE, "C:\ProgramData\miniconda3\Scripts\conda.exe",
"$up\AppData\Local\miniconda3\Scripts\conda.exe")
foreach ($conda in $condaCandidates) {
if ($conda -and (Test-Path $conda)) {
try {
$lgPath = (& $conda run -n langgraph python -c "import shutil; print(shutil.which('langgraph') or '')" 2>$null).Trim()
if ($lgPath -and (Test-Path $lgPath)) { return $lgPath }
} catch {}
}
}
throw ("Cannot find langgraph.exe in the 'langgraph' conda env. " +
"Install it: conda run -n langgraph pip install 'langgraph-cli[inmem]'")
}
function Stop-ServerProcess([int]$PidValue) {
$stopped = $false
if ($PidValue -gt 0) {
$proc = Get-Process -Id $PidValue -ErrorAction SilentlyContinue
if ($proc) {
Stop-Process -Id $PidValue -Force -ErrorAction SilentlyContinue
$stopped = $true
}
}
# Fallback: kill whoever is listening on port 2024
if (-not $stopped) {
$conn = Get-NetTCPConnection -LocalPort 2024 -State Listen -ErrorAction SilentlyContinue |
Select-Object -First 1
if ($conn) {
Stop-Process -Id $conn.OwningProcess -Force -ErrorAction SilentlyContinue
Write-Host "[dev-server] Stopped process on port 2024 (PID $($conn.OwningProcess))"
$stopped = $true
}
}
return $stopped
}
# --- command dispatch ---------------------------------------------------
switch ($Command) {
"start" {
if (Test-ServerRunning) {
$storedPid = Get-StoredPid
Write-Host "[dev-server] Already running (PID $storedPid) at $ApiUrl"
Write-Host "[dev-server] Studio: https://smith.langchain.com/studio/?baseUrl=$ApiUrl"
exit 0
}
$lgExe = Find-LangGraphExe
Write-Host "[dev-server] Starting langgraph dev server..."
Write-Host "[dev-server] exe: $lgExe"
Write-Host "[dev-server] dir: $SkillDir"
# Remove stale logs
Remove-Item $StdoutLog -ErrorAction SilentlyContinue
Remove-Item $StderrLog -ErrorAction SilentlyContinue
# Force UTF-8 to avoid GBK decode errors on Chinese Windows systems
$env:PYTHONUTF8 = "1"
$env:PYTHONIOENCODING = "utf-8"
$proc = Start-Process -FilePath $lgExe `
-ArgumentList "dev", "--no-browser" `
-WorkingDirectory $SkillDir `
-RedirectStandardOutput $StdoutLog `
-RedirectStandardError $StderrLog `
-WindowStyle Hidden -PassThru
if (-not $proc) {
Write-Error "[dev-server] Failed to launch process"
exit 1
}
$proc.Id | Out-File -FilePath $PidFile -Encoding ascii -NoNewline
# Wait for server to respond
$elapsed = 0
while (-not (Test-HttpOk "$ApiUrl/ok") -and $elapsed -lt $MaxWait) {
Start-Sleep -Seconds 1
$elapsed++
if ($proc.HasExited) {
Write-Error "[dev-server] Process exited unexpectedly (code $($proc.ExitCode))"
Write-Host "--- stdout ($StdoutLog) ---"
if (Test-Path $StdoutLog) { Get-Content $StdoutLog -Tail 30 }
Write-Host "--- stderr ($StderrLog) ---"
if (Test-Path $StderrLog) { Get-Content $StderrLog -Tail 30 }
Remove-Item $PidFile -ErrorAction SilentlyContinue
exit 1
}
}
if ($elapsed -ge $MaxWait) {
Write-Error "[dev-server] Server did not become ready within ${MaxWait}s"
Write-Host "--- stdout ($StdoutLog) ---"
if (Test-Path $StdoutLog) { Get-Content $StdoutLog -Tail 30 }
Write-Host "--- stderr ($StderrLog) ---"
if (Test-Path $StderrLog) { Get-Content $StderrLog -Tail 30 }
exit 1
}
Write-Host "[dev-server] Ready in ${elapsed}s"
Write-Host "[dev-server] API: $ApiUrl"
Write-Host "[dev-server] Studio: https://smith.langchain.com/studio/?baseUrl=$ApiUrl"
Write-Host "[dev-server] Logs: $StdoutLog / $StderrLog"
}
"stop" {
$storedPid = Get-StoredPid
if ((Stop-ServerProcess -PidValue $storedPid)) {
Write-Host "[dev-server] Stopped (PID $storedPid)"
} else {
Write-Host "[dev-server] Not running (nothing to stop)"
}
Remove-Item $PidFile -ErrorAction SilentlyContinue
}
"status" {
if (Test-ServerRunning) {
$storedPid = Get-StoredPid
Write-Host "[dev-server] Running (PID $storedPid) at $ApiUrl"
Write-Host "[dev-server] Studio: https://smith.langchain.com/studio/?baseUrl=$ApiUrl"
} else {
Write-Host "[dev-server] Not running"
}
}
default {
Write-Error "Usage: manage_dev_server.ps1 {start|stop|status}"
exit 1
}
}