browser-debugger-cli
Version:
DevTools telemetry in your terminal. For humans and agents. Direct WebSocket to Chrome's debugging port.
156 lines • 5.1 kB
JavaScript
/**
* Chrome PID cache management.
*
* Persistent cache for Chrome PID that survives session cleanup.
* WHY: Enables aggressive Chrome cleanup even after session ends (for `bdg cleanup --chrome`).
*/
import * as fs from 'fs';
import { createLogger } from '../ui/logging/index.js';
import { AtomicFileWriter } from '../utils/atomicFile.js';
import { getErrorMessage } from '../utils/errors.js';
import { isProcessAlive } from '../utils/process.js';
import { getSessionFilePath, ensureSessionDir } from './paths.js';
const log = createLogger('chrome');
/**
* Parse Chrome PID from cache file content.
*
* @param pidStr - PID string from cache file
* @returns Parsed PID or null if invalid
*/
function parseChromePid(pidStr) {
const pid = parseInt(pidStr.trim(), 10);
return Number.isNaN(pid) ? null : pid;
}
/**
* Remove Chrome PID cache file safely.
*
* @param cachePath - Path to cache file
* @param reason - Reason for removal (for logging)
*/
function removeChromePidCache(cachePath, reason) {
try {
fs.rmSync(cachePath, { force: true });
}
catch (error) {
log.debug(`Failed to remove ${reason} Chrome PID cache: ${getErrorMessage(error)}`);
}
}
/**
* Write Chrome PID to persistent cache.
*
* This cache survives session cleanup so aggressive cleanup can find Chrome later.
* WHY: Users may want to kill Chrome after session ends, especially for testing.
*
* @param chromePid - Chrome process ID to cache
*
* @example
* ```typescript
* const chrome = await launchChrome();
* writeChromePid(chrome.pid);
* ```
*/
export function writeChromePid(chromePid) {
ensureSessionDir();
const cachePath = getSessionFilePath('CHROME_PID');
AtomicFileWriter.writeSync(cachePath, chromePid.toString());
}
/**
* Read Chrome PID from persistent cache.
*
* Only returns the PID if the process is still alive.
* Automatically cleans up stale cache entries.
*
* @returns Chrome PID if cached and process is alive, null otherwise
*
* @example
* ```typescript
* const chromePid = readChromePid();
* if (chromePid) {
* console.log(`Chrome is running with PID ${chromePid}`);
* killChromeProcess(chromePid, 'SIGKILL');
* }
* ```
*/
export function readChromePid() {
const cachePath = getSessionFilePath('CHROME_PID');
if (!fs.existsSync(cachePath)) {
return null;
}
try {
const pidStr = fs.readFileSync(cachePath, 'utf-8');
const pid = parseChromePid(pidStr);
if (pid === null) {
removeChromePidCache(cachePath, 'corrupt');
return null;
}
if (!isProcessAlive(pid)) {
removeChromePidCache(cachePath, 'stale');
return null;
}
return pid;
}
catch {
return null;
}
}
/**
* Remove Chrome PID from persistent cache.
*
* Safe to call multiple times (idempotent).
*/
export function clearChromePid() {
const cachePath = getSessionFilePath('CHROME_PID');
try {
fs.rmSync(cachePath, { force: true });
}
catch (error) {
log.debug(`Failed to clear Chrome PID cache: ${getErrorMessage(error)}`);
}
}
/**
* Aggressively cleanup stale Chrome processes launched by bdg.
*
* Kills Chrome instances that were launched by bdg by:
* 1. Reading the Chrome PID from persistent cache
* 2. Killing that specific Chrome process using cross-platform kill logic
*
* The cache survives session cleanup, so this works even after a normal session end.
*
* Cross-platform killing:
* - Windows: Uses `taskkill /pid <pid> /T /F` to kill process tree
* - Unix/macOS: Uses `process.kill(-pid, 'SIGKILL')` to kill process group
*
* Note: We can't use chromeLauncher.killAll() because it only tracks instances
* created via chromeLauncher.launch(), but we use new chromeLauncher.Launcher()
* which doesn't register in that tracking set.
*
* @returns Number of errors encountered during cleanup
*/
export async function cleanupStaleChrome() {
const { cleanupChromeAttemptingMessage, cleanupChromePidNotFoundMessage, cleanupChromeKillingMessage, cleanupChromeSuccessMessage, cleanupChromeFailedMessage, cleanupChromeProcessFailedMessage, } = await import('../ui/messages/chrome.js');
console.error(cleanupChromeAttemptingMessage());
try {
const { killChromeProcess } = await import('../utils/process.js');
const chromePid = readChromePid();
if (!chromePid) {
console.error(cleanupChromePidNotFoundMessage());
return 0;
}
console.error(cleanupChromeKillingMessage(chromePid));
try {
killChromeProcess(chromePid, 'SIGKILL');
console.error(cleanupChromeSuccessMessage());
clearChromePid();
return 0;
}
catch (killError) {
console.error(cleanupChromeFailedMessage(getErrorMessage(killError)));
return 1;
}
}
catch (error) {
console.error(cleanupChromeProcessFailedMessage(getErrorMessage(error)));
return 1;
}
}
//# sourceMappingURL=chrome.js.map