From fe061877630f1769556cc99e32b229eb6d1483f0 Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 10 Mar 2026 10:07:40 +0100 Subject: [PATCH 1/2] Fix broker PID persistence and orphaned kill matching --- packages/sdk/src/client.ts | 4 ++++ src/cli/commands/core.ts | 3 +++ src/cli/lib/broker-lifecycle.ts | 25 +++++++++++++++++++++---- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/sdk/src/client.ts b/packages/sdk/src/client.ts index 641476e87..3ebb5532c 100644 --- a/packages/sdk/src/client.ts +++ b/packages/sdk/src/client.ts @@ -222,6 +222,10 @@ export class AgentRelayClient { }; } + get brokerPid(): number | undefined { + return this.child?.pid; + } + async start(): Promise { if (this.child) { return; diff --git a/src/cli/commands/core.ts b/src/cli/commands/core.ts index 5e33e504c..fd8dea0b8 100644 --- a/src/cli/commands/core.ts +++ b/src/cli/commands/core.ts @@ -76,6 +76,8 @@ export interface CoreRelay { onBrokerStderr?: (listener: (line: string) => void) => () => void; /** Relaycast workspace API key, available after the hello handshake. */ workspaceKey?: string; + /** PID of the underlying broker process, when available. */ + brokerPid?: number; } export interface CoreFileSystem { @@ -259,6 +261,7 @@ function createDefaultRelay(cwd: string, apiPort = 0): CoreRelay { shutdown: () => client.shutdown(), onBrokerStderr: (listener: (line: string) => void) => client.onBrokerStderr(listener), get workspaceKey() { return client.workspaceKey; }, + get brokerPid() { return client.brokerPid; }, }; return relay; } diff --git a/src/cli/lib/broker-lifecycle.ts b/src/cli/lib/broker-lifecycle.ts index ed31c211d..a264b5452 100644 --- a/src/cli/lib/broker-lifecycle.ts +++ b/src/cli/lib/broker-lifecycle.ts @@ -173,9 +173,26 @@ function isProcessRunning(pid: number, deps: CoreDependencies): boolean { async function killOrphanedBrokerProcesses(projectRoot: string, deps: CoreDependencies): Promise { try { const shellQuote = (s: string): string => "'" + s.replace(/'/g, "'\\''") + "'"; - const { stdout } = await deps.execCommand( - `ps aux | grep '[a]gent-relay-broker' | grep -F ${shellQuote(projectRoot)}` - ); + const brokerName = path.basename(projectRoot) || 'project'; + let stdout = ''; + try { + const byName = await deps.execCommand( + `ps aux | grep '[a]gent-relay-broker' | grep -e ${shellQuote('--name ' + brokerName)}` + ); + stdout = byName.stdout; + } catch { + // Name filter may not match older process invocations; try legacy path-based filter. + } + if (!stdout.trim()) { + try { + const byPath = await deps.execCommand( + `ps aux | grep '[a]gent-relay-broker' | grep -F ${shellQuote(projectRoot)}` + ); + stdout = byPath.stdout; + } catch { + // Expected when no orphaned processes are matched by either strategy. + } + } const lines = stdout.trim().split('\n').filter(Boolean); for (const line of lines) { const parts = line.trim().split(/\s+/); @@ -882,7 +899,7 @@ export async function runUpCommand(options: UpOptions, deps: CoreDependencies): ); relay = started.relay; apiPort = started.apiPort; - writeBrokerPid(brokerPidPath, deps.pid, deps); + writeBrokerPid(brokerPidPath, relay.brokerPid ?? deps.pid, deps); const dashboardRelayUrl = resolveDashboardRelayUrl(apiPort, deps); const expectedRelayUrl = getDefaultDashboardRelayUrl(apiPort); if ( From 7117acb08e949da05c32f85e31804c59038a93be Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 10 Mar 2026 10:13:46 +0100 Subject: [PATCH 2/2] fix: use fixed-string grep for orphaned broker name matching Change grep -e to grep -F when matching broker names in killOrphanedBrokerProcesses to avoid regex metacharacter issues in project directory names containing dots or other special chars. Co-Authored-By: Claude Opus 4.6 --- src/cli/lib/broker-lifecycle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/lib/broker-lifecycle.ts b/src/cli/lib/broker-lifecycle.ts index a264b5452..ab9fac6b9 100644 --- a/src/cli/lib/broker-lifecycle.ts +++ b/src/cli/lib/broker-lifecycle.ts @@ -177,7 +177,7 @@ async function killOrphanedBrokerProcesses(projectRoot: string, deps: CoreDepend let stdout = ''; try { const byName = await deps.execCommand( - `ps aux | grep '[a]gent-relay-broker' | grep -e ${shellQuote('--name ' + brokerName)}` + `ps aux | grep '[a]gent-relay-broker' | grep -F ${shellQuote('--name ' + brokerName)}` ); stdout = byName.stdout; } catch {