fix(devtools): reduce memory usage and defer connection (#24496)

This commit is contained in:
Sandy Tao
2026-04-22 15:21:58 -07:00
committed by GitHub
parent 2e12c34009
commit 9c0a6864da
5 changed files with 14 additions and 83 deletions
+1 -1
View File
@@ -532,7 +532,7 @@ export async function main() {
const { setupInitialActivityLogger } = await import(
'./utils/devtoolsService.js'
);
await setupInitialActivityLogger(config);
setupInitialActivityLogger(config);
}
// Register config for telemetry shutdown
+1 -1
View File
@@ -80,7 +80,7 @@ export async function runNonInteractive(
const { setupInitialActivityLogger } = await import(
'./utils/devtoolsService.js'
);
await setupInitialActivityLogger(config);
setupInitialActivityLogger(config);
}
const { stdout: workingStdout } = createWorkingStdio();
+8 -55
View File
@@ -123,69 +123,22 @@ describe('devtoolsService', () => {
});
describe('setupInitialActivityLogger', () => {
it('stays in buffer mode when no existing server found', async () => {
it('stays in buffer mode (no probe attempted)', () => {
const config = createMockConfig();
const promise = setupInitialActivityLogger(config);
// Probe fires immediately — no server running
await vi.waitFor(() => expect(MockWebSocket.instances.length).toBe(1));
MockWebSocket.instances[0].simulateError();
await promise;
setupInitialActivityLogger(config);
expect(mockInitActivityLogger).toHaveBeenCalledWith(config, {
mode: 'buffer',
});
expect(mockAddNetworkTransport).not.toHaveBeenCalled();
// No WebSocket probe on startup
expect(MockWebSocket.instances.length).toBe(0);
});
it('attaches transport when existing server found at startup', async () => {
const config = createMockConfig();
const promise = setupInitialActivityLogger(config);
await vi.waitFor(() => expect(MockWebSocket.instances.length).toBe(1));
MockWebSocket.instances[0].simulateOpen();
await promise;
expect(mockInitActivityLogger).toHaveBeenCalledWith(config, {
mode: 'buffer',
});
expect(mockAddNetworkTransport).toHaveBeenCalledWith(
config,
'127.0.0.1',
25417,
expect.any(Function),
);
expect(
mockActivityLoggerInstance.enableNetworkLogging,
).toHaveBeenCalled();
});
it('F12 short-circuits when startup already connected', async () => {
const config = createMockConfig();
// Startup: probe succeeds
const setupPromise = setupInitialActivityLogger(config);
await vi.waitFor(() => expect(MockWebSocket.instances.length).toBe(1));
MockWebSocket.instances[0].simulateOpen();
await setupPromise;
mockAddNetworkTransport.mockClear();
mockActivityLoggerInstance.enableNetworkLogging.mockClear();
// F12: should return URL immediately
const url = await startDevToolsServer(config);
expect(url).toBe('http://localhost:25417');
expect(mockAddNetworkTransport).not.toHaveBeenCalled();
expect(mockDevToolsInstance.start).not.toHaveBeenCalled();
});
it('initializes in file mode when target env var is set', async () => {
it('initializes in file mode when target env var is set', () => {
process.env['GEMINI_CLI_ACTIVITY_LOG_TARGET'] = '/tmp/test.jsonl';
const config = createMockConfig();
await setupInitialActivityLogger(config);
setupInitialActivityLogger(config);
expect(mockInitActivityLogger).toHaveBeenCalledWith(config, {
mode: 'file',
@@ -195,10 +148,10 @@ describe('devtoolsService', () => {
expect(MockWebSocket.instances.length).toBe(0);
});
it('does nothing in file mode when config.storage is missing', async () => {
it('does nothing in file mode when config.storage is missing', () => {
process.env['GEMINI_CLI_ACTIVITY_LOG_TARGET'] = '/tmp/test.jsonl';
const config = createMockConfig({ storage: undefined });
await setupInitialActivityLogger(config);
setupInitialActivityLogger(config);
expect(mockInitActivityLogger).not.toHaveBeenCalled();
expect(MockWebSocket.instances.length).toBe(0);
+3 -25
View File
@@ -116,39 +116,17 @@ async function handlePromotion(config: Config) {
/**
* Initializes the activity logger.
* Interception starts immediately in buffering mode.
* If an existing DevTools server is found, attaches transport eagerly.
* Transport is only attached when the user presses F12.
*/
export async function setupInitialActivityLogger(config: Config) {
export function setupInitialActivityLogger(config: Config) {
const target = process.env['GEMINI_CLI_ACTIVITY_LOG_TARGET'];
if (target) {
if (!config.storage) return;
initActivityLogger(config, { mode: 'file', filePath: target });
} else {
// Start in buffering mode (no transport attached yet)
// Start in buffering mode transport attached later on F12
initActivityLogger(config, { mode: 'buffer' });
// Eagerly probe for an existing DevTools server
try {
const existing = await probeDevTools(
DEFAULT_DEVTOOLS_HOST,
DEFAULT_DEVTOOLS_PORT,
);
if (existing) {
const onReconnectFailed = () => handlePromotion(config);
addNetworkTransport(
config,
DEFAULT_DEVTOOLS_HOST,
DEFAULT_DEVTOOLS_PORT,
onReconnectFailed,
);
ActivityLogger.getInstance().enableNetworkLogging();
connectedUrl = `http://localhost:${DEFAULT_DEVTOOLS_PORT}`;
debugLogger.log(`DevTools (existing) at startup: ${connectedUrl}`);
}
} catch {
// Probe failed silently — stay in buffer mode
}
}
}
+1 -1
View File
@@ -124,7 +124,7 @@ export class DevTools extends EventEmitter {
chunks: payload.chunk ? [payload.chunk] : undefined,
} as NetworkLog;
this.logs.push(entry);
if (this.logs.length > 2000) this.logs.shift();
if (this.logs.length > 10) this.logs.shift();
this.emit('update', entry);
}
}