/** * Kill whatever is listening on the Descrybe dev ports so `npm run dev` can bind. * Skip when DESCRYBE_DEV_PORTS_FREED=1 (parent already freed; children must not * re-kill the just-started sibling). * * Usage: node scripts/free-dev-ports.mjs */ import { spawnSync } from "node:child_process"; import { pathToFileURL } from "node:url"; import { DEV_API_PORT, DEV_WEB_PORT } from "./dev-ports.mjs"; export function freeDevPorts(ports = [DEV_API_PORT, DEV_WEB_PORT]) { if (process.env.DESCRYBE_DEV_PORTS_FREED === "1") { return; } for (const port of ports) { const pids = listenersOnPort(port); if (pids.length === 0) { console.log(`[dev-ports] ${port} free`); continue; } for (const pid of pids) { console.log(`[dev-ports] killing PID ${pid} on :${port}`); killPidTree(pid); } } } function listenersOnPort(port) { if (process.platform === "win32") { const ps = [ `$conns = @(Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue);`, `if ($conns.Count -eq 0) { exit 0 };`, `$conns | ForEach-Object { $_.OwningProcess } | Sort-Object -Unique | ForEach-Object { Write-Output $_ }`, ].join(" "); const r = spawnSync( "powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", ps], { encoding: "utf8" }, ); return parsePids(r.stdout || ""); } const r = spawnSync("lsof", ["-tiTCP:" + port, "-sTCP:LISTEN"], { encoding: "utf8", }); if (r.status !== 0) return []; return parsePids(r.stdout || ""); } function parsePids(text) { const seen = new Set(); const out = []; for (const raw of text.split(/\r?\n/)) { const n = Number.parseInt(raw.trim(), 10); if (!Number.isFinite(n) || n <= 0 || seen.has(n)) continue; if (n === process.pid) continue; seen.add(n); out.push(n); } return out; } function killPidTree(pid) { if (process.platform === "win32") { spawnSync("taskkill.exe", ["/T", "/F", "/PID", String(pid)], { stdio: "ignore", }); return; } try { process.kill(pid, "SIGKILL"); } catch { /* already gone */ } } const isDirect = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href; if (isDirect) { freeDevPorts(); }