Skip to main content
Glama

mcp-whatsapp-web

browser-process-manager.ts5.89 kB
import fs from 'fs'; import path from 'path'; import { log } from './logger.js'; import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); /** * Interface representing a browser process entry */ interface BrowserProcess { pid: number; startTime: number; serverInstanceId: string; // Unique ID for this server instance } /** * Manages Chrome browser processes to prevent orphaned processes */ export class BrowserProcessManager { private pidFilePath: string; private serverInstanceId: string; /** * Creates a new BrowserProcessManager */ constructor() { this.pidFilePath = path.join(process.cwd(), '.chrome-pids.json'); // Generate a unique ID for this server instance this.serverInstanceId = Date.now().toString() + '-' + Math.random().toString(36).substring(2, 15); log.info(`Initialized BrowserProcessManager with instance ID: ${this.serverInstanceId}`); } /** * Read stored browser processes from file * @returns Array of browser processes */ readProcesses(): BrowserProcess[] { try { if (fs.existsSync(this.pidFilePath)) { const data = fs.readFileSync(this.pidFilePath, 'utf8'); return JSON.parse(data) as BrowserProcess[]; } } catch (error) { log.error('Error reading browser processes file:', error); } return []; } /** * Save browser processes to file * @param processes Array of browser processes to save */ saveProcesses(processes: BrowserProcess[]): void { try { fs.writeFileSync(this.pidFilePath, JSON.stringify(processes, null, 2)); } catch (error) { log.error('Error saving browser processes file:', error); } } /** * Register a new browser process * @param pid Process ID of the browser */ registerProcess(pid: number): void { if (!pid) { log.warn('Attempted to register invalid PID'); return; } log.info(`Registering browser process with PID: ${pid}`); const processes = this.readProcesses(); // Check if this PID is already registered const existingIndex = processes.findIndex(p => p.pid === pid); if (existingIndex >= 0) { // Update the existing entry processes[existingIndex] = { pid, startTime: Date.now(), serverInstanceId: this.serverInstanceId }; } else { // Add a new entry processes.push({ pid, startTime: Date.now(), serverInstanceId: this.serverInstanceId }); } this.saveProcesses(processes); } /** * Unregister a browser process * @param pid Process ID of the browser to unregister */ unregisterProcess(pid: number): void { if (!pid) { log.warn('Attempted to unregister invalid PID'); return; } log.info(`Unregistering browser process with PID: ${pid}`); const processes = this.readProcesses(); const filteredProcesses = processes.filter(p => p.pid !== pid); if (processes.length !== filteredProcesses.length) { this.saveProcesses(filteredProcesses); } } /** * Check if a process is still running * @param pid Process ID to check * @returns True if the process is running, false otherwise */ async isProcessRunning(pid: number): Promise<boolean> { try { if (process.platform === 'win32') { // Windows const { stdout } = await execAsync(`tasklist /FI "PID eq ${pid}" /NH`); return stdout.includes(pid.toString()); } else { // Unix-like (Linux, macOS) await execAsync(`ps -p ${pid} -o pid=`); return true; } } catch (error) { // Process not found return false; } } /** * Kill a process by its PID * @param pid Process ID to kill * @returns True if the process was killed successfully, false otherwise */ async killProcess(pid: number): Promise<boolean> { try { log.info(`Attempting to kill browser process with PID: ${pid}`); if (process.platform === 'win32') { // Windows await execAsync(`taskkill /F /PID ${pid}`); } else { // Unix-like (Linux, macOS) await execAsync(`kill -9 ${pid}`); } return true; } catch (error) { log.error(`Failed to kill process ${pid}:`, error); return false; } } /** * Clean up orphaned browser processes */ async cleanupOrphanedProcesses(): Promise<void> { log.info('Cleaning up orphaned browser processes...'); const processes = this.readProcesses(); const validProcesses: BrowserProcess[] = []; for (const process of processes) { const isRunning = await this.isProcessRunning(process.pid); if (isRunning) { // Process is still running, check if it's orphaned // We consider a process orphaned if it's not from this server instance // and it's been running for more than 10 minutes const isFromCurrentInstance = process.serverInstanceId === this.serverInstanceId; const processAgeMs = Date.now() - process.startTime; const isOld = processAgeMs > 10 * 60 * 1000; // 10 minutes if (!isFromCurrentInstance && isOld) { log.info(`Found orphaned browser process with PID: ${process.pid}`); const killed = await this.killProcess(process.pid); if (!killed) { // If we couldn't kill it, keep it in the list validProcesses.push(process); } } else { // Process is still valid validProcesses.push(process); } } // If not running, we don't add it to validProcesses } // Save the updated list this.saveProcesses(validProcesses); log.info(`Cleanup complete. ${processes.length - validProcesses.length} orphaned processes removed.`); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mario-andreschak/mcp-whatsapp-web'

If you have feedback or need assistance with the MCP directory API, please join our Discord server