/** * Browser Manager - Handles Playwright browser lifecycle */ import { chromium, Browser, BrowserContext, Page } from 'playwright'; import { config } from './config.js'; export class BrowserManager { private browser: Browser | null = null; private context: BrowserContext | null = null; private page: Page | null = null; async initialize(): Promise { try { // Try to connect to an existing browser first (Remote Debugging) this.browser = await chromium.connectOverCDP('http://localhost:9222'); this.context = this.browser.contexts()[0]; this.page = this.context.pages()[0] || await this.context.newPage(); console.error('Connected to existing browser via Remote Debugging!'); } catch (error) { console.error('Could not connect to existing browser, launching new instance...'); if (config.browser.userDataDir) { // launchPersistentContext launches a browser and context in one go this.context = await chromium.launchPersistentContext( config.browser.userDataDir, { channel: config.browser.type === 'msedge' ? 'msedge' : undefined, headless: config.browser.headless, args: [ `--profile-directory=${config.browser.profile}`, ], } ); this.page = this.context.pages()[0] || await this.context.newPage(); } else { this.browser = await chromium.launch({ channel: config.browser.type === 'msedge' ? 'msedge' : undefined, headless: config.browser.headless, }); this.context = await this.browser.newContext(); this.page = await this.context.newPage(); } } // Set default timeouts if (this.page) { this.page.setDefaultTimeout(config.timeouts.action); this.page.setDefaultNavigationTimeout(config.timeouts.navigation); } } async getPage(): Promise { if (!this.page) { await this.initialize(); } return this.page!; } async close(): Promise { if (this.context) { await this.context.close(); } if (this.browser) { await this.browser.close(); } this.browser = null; this.context = null; this.page = null; } async setActivePage(page: Page): Promise { this.page = page; this.page.setDefaultTimeout(config.timeouts.action); this.page.setDefaultNavigationTimeout(config.timeouts.navigation); } isInitialized(): boolean { return this.page !== null; } } // Singleton instance export const browserManager = new BrowserManager();