refactor(cliproxy): remove orphaned bg keepalive wiring (#1353)

Red-team review of #1340 found that backgroundProbe / shouldKeepSessionProxiesAlive /
startKeepAliveWatcher and related types were never reachable: the sole caller
(claude-launcher.ts) passed no hasBackgroundWorkerUsingBaseUrl detector, so
backgroundProbe was always undefined and the keepalive branch never executed.

Remove the unreachable scaffold:
- Delete CLAUDE_BG_WORKER_ARGS, hasClaudeBackgroundWorkerUsingBaseUrl{,InProcessList},
  shouldKeepSessionProxiesAlive, ShouldKeepSessionProxiesAliveOptions, and the
  SessionProxyKeepAliveOptions interface from session-bridge.ts
- Remove the backgroundProbe/startKeepAliveWatcher block and keepalive exit branch
  from setupCleanupHandlers; the function now always calls stopSessionResources on
  Claude exit
- Remove backgroundKeepAliveBaseUrl wiring from claude-launcher.ts
- Remove the execFileSync import (was only used by the deleted ps-based detector)
- Update test file: remove tests for deleted exports, keep placeholder

A trusted bg-worker detector can be re-added in a future PR if the feature is
actually needed; the keepalive logic in shouldKeepSessionProxiesAlive can be
restored then.
This commit is contained in:
Kai (Tam Nhu) Tran
2026-05-23 17:29:18 -04:00
committed by GitHub
parent 348e9ab7f9
commit 5d27f10367
3 changed files with 16 additions and 157 deletions
+1 -5
View File
@@ -163,9 +163,6 @@ export async function launchClaude(context: ClaudeLaunchContext): Promise<ChildP
}
}
const hasSessionProxy = Boolean(codexReasoningProxy || toolSanitizationProxy || httpsTunnel);
const backgroundKeepAliveBaseUrl = hasSessionProxy ? tracedEnv.ANTHROPIC_BASE_URL : undefined;
// Wire cleanup handlers (process exit, SIGINT, SIGTERM, proxy teardown)
setupCleanupHandlers(
claude,
@@ -174,8 +171,7 @@ export async function launchClaude(context: ClaudeLaunchContext): Promise<ChildP
codexReasoningProxy,
toolSanitizationProxy,
httpsTunnel,
verbose,
{ backgroundKeepAliveBaseUrl }
verbose
);
return claude;
+2 -99
View File
@@ -8,7 +8,7 @@
* - Startup lock coordination
*/
import { ChildProcess, execFileSync } from 'child_process';
import { ChildProcess } from 'child_process';
import { info, warn } from '../../utils/ui';
import { getInstalledCliproxyVersion } from '../binary-manager';
import { CLIProxyBackend } from '../types';
@@ -33,70 +33,6 @@ export interface ProxySessionResult {
shouldSpawn: boolean;
}
export interface SessionProxyKeepAliveOptions {
backgroundKeepAliveBaseUrl?: string;
keepAlivePollIntervalMs?: number;
hasBackgroundWorkerUsingBaseUrl?: (baseUrl: string) => boolean;
}
export interface ShouldKeepSessionProxiesAliveOptions {
code: number | null;
signal: NodeJS.Signals | null;
backgroundKeepAliveBaseUrl?: string;
hasBackgroundWorkerUsingBaseUrl: (baseUrl: string) => boolean;
}
const CLAUDE_BG_WORKER_ARGS = ['--bg-spare', '--bg-pty-host'] as const;
export function hasClaudeBackgroundWorkerUsingBaseUrlInProcessList(
processList: string,
baseUrl: string
): boolean {
const envNeedle = `ANTHROPIC_BASE_URL=${baseUrl}`;
return processList
.split(/\r?\n/)
.some(
(line) => line.includes(envNeedle) && CLAUDE_BG_WORKER_ARGS.some((arg) => line.includes(arg))
);
}
export function hasClaudeBackgroundWorkerUsingBaseUrl(baseUrl: string): boolean {
if (process.platform === 'win32') {
return false;
}
for (const args of [['auxEww'], ['auxeww'], ['eww', '-axo', 'pid=,command=']]) {
try {
const processList = execFileSync('ps', args, {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
});
if (hasClaudeBackgroundWorkerUsingBaseUrlInProcessList(processList, baseUrl)) {
return true;
}
} catch {
continue;
}
}
return false;
}
export function shouldKeepSessionProxiesAlive(
options: ShouldKeepSessionProxiesAliveOptions
): boolean {
if (options.code !== 0 || options.signal) {
return false;
}
const baseUrl = options.backgroundKeepAliveBaseUrl;
if (!baseUrl) {
return false;
}
return options.hasBackgroundWorkerUsingBaseUrl(baseUrl);
}
/**
* Check for existing proxy and handle version mismatch, or determine if new spawn needed
*/
@@ -244,8 +180,7 @@ export function setupCleanupHandlers(
codexReasoningProxy: unknown,
toolSanitizationProxy: unknown,
httpsTunnel: unknown,
verbose: boolean,
keepAliveOptions: SessionProxyKeepAliveOptions = {}
verbose: boolean
): void {
const log = (msg: string) => {
if (verbose) {
@@ -275,20 +210,6 @@ export function setupCleanupHandlers(
}
};
// Security hardening: only allow keepalive when caller provides an explicit,
// trusted detector. Falling back to global process-list probing is spoofable.
const backgroundProbe = keepAliveOptions.hasBackgroundWorkerUsingBaseUrl;
const startKeepAliveWatcher = (baseUrl: string) => {
const timer = setInterval(() => {
if (backgroundProbe?.(baseUrl)) return;
log('No Claude bg worker is using the session proxy; cleaning up');
stopSessionResources();
process.exit(0);
}, keepAliveOptions.keepAlivePollIntervalMs ?? 30000);
timer.unref();
};
const cleanup = () => {
log('Parent signal received, cleaning up');
stopSessionResources();
@@ -297,24 +218,6 @@ export function setupCleanupHandlers(
claude.on('exit', (code, signal) => {
log(`Claude exited: code=${code}, signal=${signal}`);
const backgroundKeepAliveBaseUrl = keepAliveOptions.backgroundKeepAliveBaseUrl;
if (
backgroundProbe &&
shouldKeepSessionProxiesAlive({
code,
signal,
backgroundKeepAliveBaseUrl,
hasBackgroundWorkerUsingBaseUrl: backgroundProbe,
}) &&
backgroundKeepAliveBaseUrl
) {
stopQuotaMonitor();
log(`Keeping session proxy alive for Claude bg worker: ${backgroundKeepAliveBaseUrl}`);
startKeepAliveWatcher(backgroundKeepAliveBaseUrl);
return;
}
stopSessionResources();
if (signal) {
@@ -1,57 +1,17 @@
import { describe, it, expect } from 'bun:test';
import {
hasClaudeBackgroundWorkerUsingBaseUrlInProcessList,
shouldKeepSessionProxiesAlive,
} from '../../../src/cliproxy/executor/session-bridge';
/**
* The bg-keepalive feature (shouldKeepSessionProxiesAlive, hasClaudeBackgroundWorker*,
* backgroundProbe) was removed as dead code: the sole caller (claude-launcher.ts) never
* provided a trusted hasBackgroundWorkerUsingBaseUrl detector, so the keepalive path was
* unreachable. The ps-based detector was also spoofable (fixed in #1340). This test file
* is kept as a placeholder to prevent the test suite from failing on missing imports.
*
* If a trusted bg-worker detector is implemented in the future, new tests belong here.
*/
import { describe, it } from 'bun:test';
describe('session bridge background proxy keepalive', () => {
it('keeps per-session proxies alive after a clean Claude exit when a bg worker inherited the base URL', () => {
expect(
shouldKeepSessionProxiesAlive({
code: 0,
signal: null,
backgroundKeepAliveBaseUrl: 'http://127.0.0.1:64314/api/provider/codex',
hasBackgroundWorkerUsingBaseUrl: () => true,
})
).toBe(true);
});
it('cleans up per-session proxies when no bg worker inherited the base URL', () => {
expect(
shouldKeepSessionProxiesAlive({
code: 0,
signal: null,
backgroundKeepAliveBaseUrl: 'http://127.0.0.1:64314/api/provider/codex',
hasBackgroundWorkerUsingBaseUrl: () => false,
})
).toBe(false);
});
it('cleans up per-session proxies for signaled Claude exits', () => {
expect(
shouldKeepSessionProxiesAlive({
code: null,
signal: 'SIGTERM',
backgroundKeepAliveBaseUrl: 'http://127.0.0.1:64314/api/provider/codex',
hasBackgroundWorkerUsingBaseUrl: () => true,
})
).toBe(false);
});
it('detects Claude bg workers that inherited the exact base URL', () => {
const baseUrl = 'http://127.0.0.1:64314/api/provider/codex';
const processList = `
123 /opt/claude/claude.exe --bg-spare /tmp/claim.sock ANTHROPIC_BASE_URL=${baseUrl} ANTHROPIC_AUTH_TOKEN=ccs-internal-managed
456 /opt/claude/claude.exe --bg-spare /tmp/claim.sock ANTHROPIC_BASE_URL=http://127.0.0.1:62075/api/provider/codex
789 /opt/claude/claude.exe --print ANTHROPIC_BASE_URL=${baseUrl}
`;
expect(hasClaudeBackgroundWorkerUsingBaseUrlInProcessList(processList, baseUrl)).toBe(true);
expect(
hasClaudeBackgroundWorkerUsingBaseUrlInProcessList(
processList,
'http://127.0.0.1:65535/api/provider/codex'
)
).toBe(false);
it('bg keepalive wiring removed — no dead code to test', () => {
// No-op: feature scaffold removed per red-team finding on #1340.
});
});