Merge pull request #2844 from sergiofspedro/fix/windows-port-release

fix: kill orphaned process on Windows before OpenCode restart
This commit is contained in:
Bohdan Triapitsyn
2026-08-28 01:23:04 +03:00
committed by GitHub
3 changed files with 115 additions and 8 deletions
+43 -5
View File
@@ -112,8 +112,45 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
now = Date.now,
} = deps;
const killProcessOnPortWin32 = (port) => {
try {
// Get-NetTCPConnection reads the same locale-independent WinNT API
// netstat's display layer translates (e.g. "LISTENING" renders as
// "ABHÖREN"/"ÉCOUTE"/"ESCUTANDO" on non-English Windows), so this
// works regardless of the OS display language.
const result = spawnSync(
'powershell',
[
'-NoProfile',
'-NonInteractive',
'-Command',
`Get-NetTCPConnection -State Listen -LocalPort ${Number.parseInt(port, 10)} -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess`,
],
{ encoding: 'utf8', timeout: 5000, windowsHide: true }
);
const output = result.stdout || '';
const myPid = process.pid;
const pids = new Set();
for (const line of output.split(/\r?\n/)) {
const pid = Number.parseInt(line.trim(), 10);
if (pid && pid !== myPid) pids.add(pid);
}
for (const pid of pids) {
try {
spawnSync('taskkill', ['/PID', String(pid), '/F'], { stdio: 'ignore', timeout: 3000, windowsHide: true });
} catch {
}
}
} catch {
}
};
const killProcessOnPort = (port) => {
if (!port || process.platform === 'win32') return;
if (!port) return;
if (process.platform === 'win32') {
killProcessOnPortWin32(port);
return;
}
try {
const result = spawnSync('lsof', ['-ti', `:${port}`], { encoding: 'utf8', timeout: 5000, windowsHide: true });
const output = result.stdout || '';
@@ -860,10 +897,11 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
}
// The restart may have landed on a NEW port (the old one can remain
// occupied by an orphaned process, e.g. Windows killProcessOnPort is a
// no-op). Upstream event readers pinned to the old process would keep
// the UI silent forever, so rebind them to the current port. Best
// effort: a failure here must not fail the restart itself.
// occupied if killProcessOnPort/waitForPortRelease didn't free it in
// time, on any platform). Upstream event readers pinned to the old
// process would keep the UI silent forever, so rebind them to the
// current port. Best effort: a failure here must not fail the restart
// itself.
try {
onOpenCodeRestarted?.();
} catch (error) {