From 7ee0c9c96b7c9e35ac83015176d59e240647b080 Mon Sep 17 00:00:00 2001 From: jackwener Date: Mon, 16 Mar 2026 12:54:44 +0800 Subject: [PATCH] fix: tab cleanup regex to match actual Playwright MCP tab format The tab list from Playwright MCP uses '- N: (current) [title](url)' format, but extractTabEntries only matched 'Tab N ...' format. This caused _initialTabIdentities to always be empty, so tabs were never cleaned up. Now supports both formats. --- package-lock.json | 3 +++ src/browser.test.ts | 11 +++++++++++ src/browser.ts | 23 +++++++++++++++++------ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 402b032..d7112a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,9 @@ "tsx": "^4.19.3", "typescript": "^5.8.2", "vitest": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@colors/colors": { diff --git a/src/browser.test.ts b/src/browser.test.ts index fbfe211..2b16dd7 100644 --- a/src/browser.test.ts +++ b/src/browser.test.ts @@ -20,6 +20,17 @@ describe('browser helpers', () => { ]); }); + it('extracts tab entries from MCP markdown format', () => { + const entries = __test__.extractTabEntries( + '- 0: (current) [Playwright MCP extension](chrome-extension://abc/connect.html)\n- 1: [知乎 - 首页](https://www.zhihu.com/)' + ); + + expect(entries).toEqual([ + { index: 0, identity: '(current) [Playwright MCP extension](chrome-extension://abc/connect.html)' }, + { index: 1, identity: '[知乎 - 首页](https://www.zhihu.com/)' }, + ]); + }); + it('closes only tabs that were opened during the session', () => { const tabsToClose = __test__.diffTabIndexes( ['https://example.com', 'Chrome Extension'], diff --git a/src/browser.ts b/src/browser.ts index 8cd45a8..368819d 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -610,12 +610,23 @@ function extractTabEntries(raw: any): Array<{ index: number; identity: string }> .map(line => line.trim()) .filter(Boolean) .map(line => { - const match = line.match(/Tab\s+(\d+)\s*(.*)$/); - if (!match) return null; - return { - index: parseInt(match[1], 10), - identity: match[2].trim() || `tab-${match[1]}`, - }; + // Match actual Playwright MCP format: "- 0: (current) [title](url)" or "- 1: [title](url)" + const mcpMatch = line.match(/^-\s+(\d+):\s*(.*)$/); + if (mcpMatch) { + return { + index: parseInt(mcpMatch[1], 10), + identity: mcpMatch[2].trim() || `tab-${mcpMatch[1]}`, + }; + } + // Legacy format: "Tab 0 ..." + const legacyMatch = line.match(/Tab\s+(\d+)\s*(.*)$/); + if (legacyMatch) { + return { + index: parseInt(legacyMatch[1], 10), + identity: legacyMatch[2].trim() || `tab-${legacyMatch[1]}`, + }; + } + return null; }) .filter((entry): entry is { index: number; identity: string } => entry !== null); }