import type { Browser, BrowserContext, Page } from "playwright"; import type { BrowserConfig } from "./config.js"; export interface PlaywrightLaunchers { chromium: { launch(opts: Record): Promise; }; firefox: { launch(opts: Record): Promise; }; webkit: { launch(opts: Record): Promise; }; } export interface NetworkRequest { index: number; url: string; method: string; status: number; statusText: string; headers: Record; responseHeaders: Record; postData?: string; body?: string; timestamp: number; } export declare class BrowserService { private browser; private context; private pages; private activePageIndex; private config; private consoleBuffer; private networkBuffer; private nextNetworkIndex; private pendingDialogs; private _recording; private closePromise; private launchLock; /** Unique marker identifying the process tree of the current launch. */ private launchToken; /** * Token of the current *or previous* launch — only cleared by the next * launch. Post-close fallbacks (`forceKillBrowserProcess()` / `browser_quit`) * still need it to find the process tree after `close()` nulled launchToken. * Every kill through this token is marker-verified first, so a recycled pid * is never signalled. */ private lastLaunchToken; /** * Root pid + full marker-matched tree, resolved once after a successful * launch (single `ps` call) so exit/signal handlers can SIGKILL without * running `execSync` from inside the handler. */ private browserPid; private browserPids; /** * Evidence for the synchronous teardown path: the pids that were matched by * *our* marker at launch time, when they were matched and under which token. * `killBrowserProcessSync()` may only signal pids listed here — a pid that we * never marker-verified (or that no longer carries the marker) can belong to * somebody else by now. */ private verifiedBrowserPids; private verifiedBrowserPidsAt; private verifiedBrowserPidsToken; constructor(config?: Partial); static getInstance(config?: Partial): BrowserService; static resetInstance(): void; /** * Close the active browser singleton (if any) and await completion. Unlike * getInstance().close(), this does NOT create a fresh empty service when the * instance was already nulled by a prior cleanup callback — it instead awaits * the pending close promise tracked from that cleanup. Use this on process / * server shutdown so the Chromium process is actually torn down before exit. */ static closeAll(): Promise; static isAvailable(): boolean; static preflight(launchers?: PlaywrightLaunchers): Promise; static unavailabilityReason(): string | null; ensureBrowser(): Promise<{ browser: Browser; context: BrowserContext; page: Page; }>; getBrowser(): Browser; getActiveContext(): BrowserContext; getActivePage(): Page; navigate(url: string): Promise<{ url: string; title: string; }>; navigateBack(): Promise<{ url: string; title: string; }>; closePage(): Promise; resize(width: number, height: number): Promise; listTabs(): Array<{ index: number; title: string; url: string; }>; createTab(): Promise<{ index: number; }>; closeTab(index: number): Promise; selectTab(index: number): Promise<{ index: number; }>; private installDialogInterceptor; getPendingDialogs(): Array<{ message: string; type: string; }>; handleDialog(accept: boolean, promptText?: string): Promise; getConsoleMessages(level?: "error" | "warning" | "info" | "debug", all?: boolean): Array<{ type: string; text: string; pageUrl: string; }>; clearConsoleMessages(): void; trackNetworkRequest(page: Page): void; getNetworkRequests(): NetworkRequest[]; getNetworkRequest(index: number): NetworkRequest | undefined; clearNetworkRequests(): void; close(): Promise; /** * Last-resort teardown used by the `browser_quit` tool: SIGKILL the browser * process without waiting for a graceful close. Safe to call at any time. */ forceKillBrowserProcess(): void; /** * SIGKILL the browser process behind `browser` (defaults to the live one). * `browser.close()` can hang, be rejected or be abandoned while the host * process is shutting down, which leaves a visible Chromium window behind. * * `opts.scanProcessTable === false` skips `findBrowserPids()` (a `ps -eo` * fork) — use it when nothing indicates a live browser. Only pids we already * know about (or the one `browser.process()` exposes) are then signalled, * each still marker-verified before the SIGKILL. */ killBrowserProcess(browser?: Browser | null, opts?: { scanProcessTable?: boolean; }): void; /** * Exit/signal-handler path: SIGKILL the pids resolved right after launch. * Deliberately execSync-free — spawning `ps` from inside an `exit` handler is * unreliable (and can silently no-op). * * Never signals a pid that may not be ours: only pids whose marker was * verified at launch time are eligible, and each candidate still has to pass * a synchronous identity check — a `process.kill(pid, 0)` probe plus, where * the platform allows it, a `/proc//cmdline` read. Where no synchronous * verification exists (no `/proc`), the launch-time evidence is trusted only * while it is fresh (`PID_VERIFY_TTL_MS`): an orphaned browser window is * preferable to SIGKILLing a recycled pid that now belongs to someone else. */ killBrowserProcessSync(): void; /** * Resolve the browser's pids once, right after a successful launch, so the * synchronous teardown path never has to inspect the process table again. */ private resolveBrowserPids; /** Find the pids of the browser processes we spawned (marker match). */ private findBrowserPids; startRecording(opts?: { outputDir?: string; filename?: string; }): Promise<{ outputPath: string; startedAt: number; }>; /** * @param opts.relaunch Re-open a fresh non-recording context afterwards * (default true, what `browser_record_stop` needs). Pass `false` when the * session is shutting down — otherwise a brand new Chromium window is * spawned only to be closed again immediately. */ stopRecording(opts?: { relaunch?: boolean; }): Promise<{ outputPath: string; durationSeconds: number; }>; getRecordStatus(): { active: boolean; outputPath?: string; startedAt?: number; elapsedSeconds?: number; }; resetBuffers(): void; getConfig(): BrowserConfig; } //# sourceMappingURL=browser-service.d.ts.map