From 4e49cd5418adece7fde8d7d516e927d7b7c4ab3e Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Mon, 24 Nov 2025 20:46:45 +0100 Subject: [PATCH] less resets --- playwriter/src/mcp.ts | 102 +++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/playwriter/src/mcp.ts b/playwriter/src/mcp.ts index fecbf8b..ad036ea 100644 --- a/playwriter/src/mcp.ts +++ b/playwriter/src/mcp.ts @@ -53,11 +53,7 @@ interface VMContext { }) => Promise getLocatorStringForElement: (element: any) => Promise resetPlaywright: () => Promise<{ page: Page; context: BrowserContext }> - getLatestLogs: (options?: { - page?: Page - count?: number - searchFilter?: string | RegExp - }) => Promise + getLatestLogs: (options?: { page?: Page; count?: number; searchFilter?: string | RegExp }) => Promise clearAllLogs: () => void require: NodeRequire import: (specifier: string) => Promise @@ -110,7 +106,7 @@ async function sendLogToRelayServer(level: string, ...args: any[]) { async function isPortTaken(port: number): Promise { try { const response = await fetch(`http://localhost:${port}/`, { - signal: AbortSignal.timeout(100) + signal: AbortSignal.timeout(100), }) return response.ok } catch { @@ -173,7 +169,7 @@ async function ensureConnection(): Promise<{ browser: Browser; page: Page }> { const page = pages[0] // Set up console listener for all existing pages - context.pages().forEach(p => setupPageConsoleListener(p)) + context.pages().forEach((p) => setupPageConsoleListener(p)) state.browser = browser state.page = page @@ -261,7 +257,7 @@ async function getCurrentPage(timeout = 5000) { if (pages.length > 0) { const page = pages[0] - page.waitForEvent('load', {timeout}) + page.waitForEvent('load', { timeout }) await page.emulateMedia({ colorScheme: null }) return page } @@ -306,7 +302,7 @@ async function resetConnection(): Promise<{ browser: Browser; page: Page; contex const page = pages[0] // Set up console listener for all existing pages - context.pages().forEach(p => setupPageConsoleListener(p)) + context.pages().forEach((p) => setupPageConsoleListener(p)) state.browser = browser state.page = page @@ -372,10 +368,7 @@ server.tool( const { page: targetPage, searchString, contextLines = 10 } = options if ((targetPage as any)._snapshotForAI) { const snapshot = await (targetPage as any)._snapshotForAI() - const snapshotStr = - typeof snapshot === 'string' - ? snapshot - : JSON.stringify(snapshot, null, 2) + const snapshotStr = typeof snapshot === 'string' ? snapshot : JSON.stringify(snapshot, null, 2) if (!searchString) { return snapshotStr @@ -416,9 +409,7 @@ server.tool( const getLocatorStringForElement = async (element: any) => { if (!element || typeof element.evaluate !== 'function') { - throw new Error( - 'getLocatorStringForElement: argument must be a Playwright Locator or ElementHandle', - ) + throw new Error('getLocatorStringForElement: argument must be a Playwright Locator or ElementHandle') } return await element.evaluate(async (el: any) => { @@ -433,19 +424,14 @@ server.tool( toLocator: module.toLocator, } } - const { createSelectorGenerator, toLocator } = - WIN.__selectorGenerator as SelectorGenerator + const { createSelectorGenerator, toLocator } = WIN.__selectorGenerator as SelectorGenerator const generator = createSelectorGenerator(WIN) const result = generator(el) return toLocator(result.selector, 'javascript') }) } - const getLatestLogs = async (options?: { - page?: Page - count?: number - searchFilter?: string | RegExp - }) => { + const getLatestLogs = async (options?: { page?: Page; count?: number; searchFilter?: string | RegExp }) => { const { page: filterPage, count, searchFilter } = options || {} let allLogs: string[] = [] @@ -464,7 +450,7 @@ server.tool( // Filter by search string or regex if (searchFilter) { - allLogs = allLogs.filter(log => { + allLogs = allLogs.filter((log) => { if (typeof searchFilter === 'string') { return log.includes(searchFilter) } else if (searchFilter instanceof RegExp) { @@ -506,15 +492,15 @@ server.tool( resetPlaywright: vmContextObj.resetPlaywright, require, import: vmContextObj.import, - ...usefulGlobals + ...usefulGlobals, } - Object.keys(vmContextObj).forEach(key => delete (vmContextObj as any)[key]) + Object.keys(vmContextObj).forEach((key) => delete (vmContextObj as any)[key]) Object.assign(vmContextObj, resetObj) return { page: newPage, context: newContext } }, require, import: (specifier: string) => import(specifier), - ...usefulGlobals + ...usefulGlobals, } const vmContext = vm.createContext(vmContextObj) @@ -563,7 +549,9 @@ server.tool( const MAX_LENGTH = 6000 let finalText = responseText.trim() if (finalText.length > MAX_LENGTH) { - finalText = finalText.slice(0, MAX_LENGTH) + `\n\n[Truncated to ${MAX_LENGTH} characters. Better manage your logs or paginate them to read the full logs]` + finalText = + finalText.slice(0, MAX_LENGTH) + + `\n\n[Truncated to ${MAX_LENGTH} characters. Better manage your logs or paginate them to read the full logs]` } return { @@ -577,30 +565,44 @@ server.tool( } catch (error: any) { const errorStack = error.stack || error.message console.error('Error in execute tool, attempting reset:', errorStack) - await sendLogToRelayServer('error', '[MCP] CRITICAL ERROR - Connection reset triggered:', errorStack) - - try { - await resetConnection() - return { - content: [ - { - type: 'text', - text: `Connection was reset due to error. Please retry your command.\n\nError: ${error.message}`, - }, - ], - } - } catch (resetError: any) { - await sendLogToRelayServer('error', '[MCP] CRITICAL ERROR - Reset failed:', resetError.stack || resetError.message) - return { - content: [ - { - type: 'text', - text: `Error executing code: ${error.message}\n${errorStack}`, - }, - ], - isError: true, + if (error.message.includes('duplicate target')) { + await sendLogToRelayServer('error', '[MCP] CRITICAL ERROR - Connection reset triggered:', errorStack) + try { + await resetConnection() + return { + content: [ + { + type: 'text', + text: `Connection was reset due to error. Please retry your command.\n\nError: ${error.message}`, + }, + ], + } + } catch (resetError: any) { + await sendLogToRelayServer( + 'error', + '[MCP] CRITICAL ERROR - Reset failed:', + resetError.stack || resetError.message, + ) + return { + content: [ + { + type: 'text', + text: `Error executing code: ${error.message}\n${errorStack}`, + }, + ], + isError: true, + } } } + return { + content: [ + { + type: 'text', + text: `Error executing code: ${error.message}\n${errorStack}`, + }, + ], + isError: true, + } } }, )