From 335f66f723bfb47a3a6330b6c1900564755a39c9 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 16 Jan 2026 16:32:50 -0800 Subject: [PATCH 1/3] fix(desktop): await PTY ready in daemon to fix port detection Port detection was not working for terminals when terminal persistence (daemon mode) was enabled. The root cause was that createOrAttach returned the response before the PTY finished spawning, so session.pid was null. The null PID was registered with the port manager, and sessions with null PIDs are skipped during port scanning. The fix awaits the PTY ready state before returning, ensuring the PID is available. This also simplifies the initial commands section since the PTY is guaranteed to be ready. --- .../src/main/terminal-host/terminal-host.ts | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/apps/desktop/src/main/terminal-host/terminal-host.ts b/apps/desktop/src/main/terminal-host/terminal-host.ts index ae0f57a09c4..566ace239fd 100644 --- a/apps/desktop/src/main/terminal-host/terminal-host.ts +++ b/apps/desktop/src/main/terminal-host/terminal-host.ts @@ -128,38 +128,39 @@ export class TerminalHost { env: request.env, }); - // Hold the spawn permit until the PTY is ready (or times out). This spreads - // out large bursts of session creation and avoids CPU spikes from dozens - // of shells starting simultaneously. - void promiseWithTimeout(session.waitForReady(), SPAWN_READY_TIMEOUT_MS) - .catch(() => {}) - .finally(() => { - releaseSpawn(); - }); + // Wait for the PTY to be ready before continuing. This ensures: + // 1. We have the PID for port scanning + // 2. Concurrency is controlled (spawn permit held until ready) + // 3. CPU spikes from simultaneous shell starts are avoided + try { + await promiseWithTimeout(session.waitForReady(), SPAWN_READY_TIMEOUT_MS); + } catch { + // Timeout waiting for PTY ready - continue anyway, PID will be null + console.warn( + `[TerminalHost] Timeout waiting for PTY ready for session ${sessionId}`, + ); + } finally { + releaseSpawn(); + } } catch (error) { releaseSpawn(); throw error; } - // Run initial commands if provided (after PTY is ready) + // Run initial commands if provided (PTY is already ready from above await) if (request.initialCommands && request.initialCommands.length > 0) { - const initialCommands = request.initialCommands; - // Wait for PTY to be ready, then run commands - session.waitForReady().then(() => { - // Double-check session is still alive after await - if (session?.isAlive) { - try { - const cmdString = `${initialCommands.join(" && ")}\n`; - session.write(cmdString); - } catch (error) { - // Log but don't crash - initialCommands are best-effort - console.error( - `[TerminalHost] Failed to run initial commands for ${sessionId}:`, - error, - ); - } + if (session.isAlive) { + try { + const cmdString = `${request.initialCommands.join(" && ")}\n`; + session.write(cmdString); + } catch (error) { + // Log but don't crash - initialCommands are best-effort + console.error( + `[TerminalHost] Failed to run initial commands for ${sessionId}:`, + error, + ); } - }); + } } this.sessions.set(sessionId, session); From e13af9fd441eb25b83db27730b9a99d79d02b507 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 16 Jan 2026 16:45:47 -0800 Subject: [PATCH 2/3] chore: clean up comments --- apps/desktop/src/main/terminal-host/terminal-host.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/desktop/src/main/terminal-host/terminal-host.ts b/apps/desktop/src/main/terminal-host/terminal-host.ts index 566ace239fd..2900fb21711 100644 --- a/apps/desktop/src/main/terminal-host/terminal-host.ts +++ b/apps/desktop/src/main/terminal-host/terminal-host.ts @@ -128,14 +128,10 @@ export class TerminalHost { env: request.env, }); - // Wait for the PTY to be ready before continuing. This ensures: - // 1. We have the PID for port scanning - // 2. Concurrency is controlled (spawn permit held until ready) - // 3. CPU spikes from simultaneous shell starts are avoided + // Wait for PTY ready to ensure PID is available for port scanning try { await promiseWithTimeout(session.waitForReady(), SPAWN_READY_TIMEOUT_MS); } catch { - // Timeout waiting for PTY ready - continue anyway, PID will be null console.warn( `[TerminalHost] Timeout waiting for PTY ready for session ${sessionId}`, ); @@ -147,14 +143,13 @@ export class TerminalHost { throw error; } - // Run initial commands if provided (PTY is already ready from above await) + // Run initial commands if provided if (request.initialCommands && request.initialCommands.length > 0) { if (session.isAlive) { try { const cmdString = `${request.initialCommands.join(" && ")}\n`; session.write(cmdString); } catch (error) { - // Log but don't crash - initialCommands are best-effort console.error( `[TerminalHost] Failed to run initial commands for ${sessionId}:`, error, From 80e11cc18787a6fcd838fb872d3b731914c4bd17 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Fri, 16 Jan 2026 16:51:21 -0800 Subject: [PATCH 3/3] lint --- apps/desktop/src/main/terminal-host/terminal-host.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/main/terminal-host/terminal-host.ts b/apps/desktop/src/main/terminal-host/terminal-host.ts index 2900fb21711..6433250491a 100644 --- a/apps/desktop/src/main/terminal-host/terminal-host.ts +++ b/apps/desktop/src/main/terminal-host/terminal-host.ts @@ -130,7 +130,10 @@ export class TerminalHost { // Wait for PTY ready to ensure PID is available for port scanning try { - await promiseWithTimeout(session.waitForReady(), SPAWN_READY_TIMEOUT_MS); + await promiseWithTimeout( + session.waitForReady(), + SPAWN_READY_TIMEOUT_MS, + ); } catch { console.warn( `[TerminalHost] Timeout waiting for PTY ready for session ${sessionId}`,