Skip to main content
Glama

Perplexity MCP Server

BrowserManager.ts4.96 kB
/** * BrowserManager - Handles all Puppeteer browser operations * Focused, testable module for browser automation */ import type { Browser, Page } from "puppeteer"; import type { IBrowserManager, PuppeteerContext } from "../../types/index.js"; import { logError, logInfo, logWarn } from "../../utils/logging.js"; import { checkForCaptcha, initializeBrowser, navigateToPerplexity, recoveryProcedure, resetIdleTimeout, waitForSearchInput, } from "../../utils/puppeteer.js"; export class BrowserManager implements IBrowserManager { public browser: Browser | null = null; public page: Page | null = null; public isInitializing = false; public searchInputSelector = 'textarea[placeholder*="Ask"]'; public readonly lastSearchTime = 0; public idleTimeout: NodeJS.Timeout | null = null; public operationCount = 0; public readonly IDLE_TIMEOUT_MS = 5 * 60 * 1000; public getPuppeteerContext(): PuppeteerContext { return { browser: this.browser, page: this.page, isInitializing: this.isInitializing, searchInputSelector: this.searchInputSelector, lastSearchTime: this.lastSearchTime, idleTimeout: this.idleTimeout, operationCount: this.operationCount, log: this.log.bind(this), setBrowser: (browser) => { this.browser = browser; }, setPage: (page) => { this.page = page; }, setIsInitializing: (val) => { this.isInitializing = val; }, setSearchInputSelector: (selector) => { this.searchInputSelector = selector; }, setIdleTimeout: (timeout) => { this.idleTimeout = timeout; }, incrementOperationCount: () => ++this.operationCount, determineRecoveryLevel: this.determineRecoveryLevel.bind(this), IDLE_TIMEOUT_MS: this.IDLE_TIMEOUT_MS, }; } private log(level: "info" | "error" | "warn", message: string) { switch (level) { case "info": logInfo(message); break; case "warn": logWarn(message); break; case "error": logError(message); break; default: logInfo(message); } } private determineRecoveryLevel(error?: Error): number { if (!error) return 1; const errorMessage = error.message.toLowerCase(); // Level 3: Critical errors requiring full browser restart if ( errorMessage.includes("detached") || errorMessage.includes("crashed") || errorMessage.includes("disconnected") || errorMessage.includes("protocol error") ) { return 3; } // Level 2: Navigation/page errors requiring page restart if ( errorMessage.includes("navigation") || errorMessage.includes("timeout") || errorMessage.includes("net::err") ) { return 2; } // Level 1: Minor errors requiring simple recovery return 1; } async initialize(): Promise<void> { if (this.isInitializing) { logInfo("Browser initialization already in progress..."); return; } try { const ctx = this.getPuppeteerContext(); await initializeBrowser(ctx); logInfo("BrowserManager initialized successfully"); } catch (error) { logError("BrowserManager initialization failed:", { error: error instanceof Error ? error.message : String(error), }); throw error; } } async navigateToPerplexity(): Promise<void> { const ctx = this.getPuppeteerContext(); await navigateToPerplexity(ctx); } async waitForSearchInput(): Promise<string | null> { const ctx = this.getPuppeteerContext(); const selector = await waitForSearchInput(ctx); return selector; } async checkForCaptcha(): Promise<boolean> { const ctx = this.getPuppeteerContext(); return await checkForCaptcha(ctx); } async performRecovery(error?: Error): Promise<void> { const ctx = this.getPuppeteerContext(); await recoveryProcedure(ctx, error); } isReady(): boolean { return !!(this.browser && this.page && !this.page.isClosed() && !this.isInitializing); } async cleanup(): Promise<void> { try { if (this.idleTimeout) { clearTimeout(this.idleTimeout); this.idleTimeout = null; } if (this.page && !this.page.isClosed()) { await this.page.close(); } if (this.browser?.isConnected()) { await this.browser.close(); } this.page = null; this.browser = null; this.isInitializing = false; logInfo("BrowserManager cleanup completed"); } catch (error) { logError("Error during BrowserManager cleanup:", { error: error instanceof Error ? error.message : String(error), }); } } getPage(): Page | null { return this.page; } getBrowser(): Browser | null { return this.browser; } resetIdleTimeout(): void { const ctx = this.getPuppeteerContext(); resetIdleTimeout(ctx); } }

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/wysh3/perplexity-mcp-zerver'

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