From 5389bae465ed1cf41f66b1be79927a320d1e7421 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Fri, 16 Jan 2026 02:38:09 +0200 Subject: [PATCH] feat: enhance process cleanup to avoid orphaned processes and improve reliability --- CHANGELOG.md | 2 +- packages/desktop/src-tauri/Cargo.lock | 2 +- .../desktop/src-tauri/src/opencode_manager.rs | 20 ++++++++++++++++--- packages/vscode/src/opencode.ts | 15 ++++++++++++-- packages/web/server/index.js | 17 ++++++++++++---- 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22752e4f..151c4f4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file. ## [1.5.1] - 2026-01-16 - Desktop: fixed orphaned OpenCode processes not being cleaned up on restart or exit. - +- Opencode: fixed issue with reloading configuration was killing the app ## [1.5.0] - 2026-01-16 diff --git a/packages/desktop/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock index 17d1522f..044dfa27 100644 --- a/packages/desktop/src-tauri/Cargo.lock +++ b/packages/desktop/src-tauri/Cargo.lock @@ -2977,7 +2977,7 @@ dependencies = [ [[package]] name = "openchamber-desktop" -version = "1.5.0" +version = "1.5.1" dependencies = [ "anyhow", "axum", diff --git a/packages/desktop/src-tauri/src/opencode_manager.rs b/packages/desktop/src-tauri/src/opencode_manager.rs index 08ed6ac3..43b8241b 100644 --- a/packages/desktop/src-tauri/src/opencode_manager.rs +++ b/packages/desktop/src-tauri/src/opencode_manager.rs @@ -526,9 +526,23 @@ fn kill_process_on_port(port: Option) { #[cfg(unix)] { use std::process::Command; - let _ = Command::new("sh") - .args(["-c", &format!("lsof -ti:{} | xargs kill -9 2>/dev/null || true", port)]) - .output(); + // First get PIDs, then kill them separately to avoid xargs issues + if let Ok(output) = Command::new("lsof") + .args(["-ti", &format!(":{}", port)]) + .output() + { + let pids = String::from_utf8_lossy(&output.stdout); + for pid in pids.split_whitespace() { + if let Ok(pid_num) = pid.trim().parse::() { + // Don't kill our own process + if pid_num != std::process::id() as i32 { + let _ = Command::new("kill") + .args(["-9", &pid_num.to_string()]) + .output(); + } + } + } + } } } diff --git a/packages/vscode/src/opencode.ts b/packages/vscode/src/opencode.ts index ff8e8e26..2966759f 100644 --- a/packages/vscode/src/opencode.ts +++ b/packages/vscode/src/opencode.ts @@ -302,10 +302,21 @@ export function createOpenCodeManager(_context: vscode.ExtensionContext): OpenCo // Kill any process listening on our port to clean up orphaned children. if (portToKill) { try { - execSync(`lsof -ti:${portToKill} | xargs kill -9 2>/dev/null || true`, { - stdio: 'ignore', + const lsofOutput = execSync(`lsof -ti:${portToKill} 2>/dev/null || true`, { + encoding: 'utf8', timeout: 5000 }); + const myPid = process.pid; + for (const pidStr of lsofOutput.split(/\s+/)) { + const pid = parseInt(pidStr.trim(), 10); + if (pid && pid !== myPid) { + try { + execSync(`kill -9 ${pid} 2>/dev/null || true`, { stdio: 'ignore', timeout: 2000 }); + } catch { + // Ignore + } + } + } } catch { // Ignore - process may already be dead } diff --git a/packages/web/server/index.js b/packages/web/server/index.js index cea91f90..f90a1922 100644 --- a/packages/web/server/index.js +++ b/packages/web/server/index.js @@ -1434,10 +1434,19 @@ function killProcessOnPort(port) { try { // SDK's proc.kill() only kills the Node wrapper, not the actual opencode binary. // Kill any process listening on our port to clean up orphaned children. - spawnSync('sh', ['-c', `lsof -ti:${port} | xargs kill -9 2>/dev/null || true`], { - stdio: 'ignore', - timeout: 5000 - }); + const result = spawnSync('lsof', ['-ti', `:${port}`], { encoding: 'utf8', timeout: 5000 }); + const output = result.stdout || ''; + const myPid = process.pid; + for (const pidStr of output.split(/\s+/)) { + const pid = parseInt(pidStr.trim(), 10); + if (pid && pid !== myPid) { + try { + spawnSync('kill', ['-9', String(pid)], { stdio: 'ignore', timeout: 2000 }); + } catch { + // Ignore + } + } + } } catch { // Ignore - process may already be dead }