got it to work
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 530 B |
|
After Width: | Height: | Size: 913 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 776 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Playwright MCP Bridge",
|
"name": "Playwriter",
|
||||||
"version": "0.0.47",
|
"version": "0.0.47",
|
||||||
"description": "Click to attach debugger to current tab",
|
"description": "Playwriter MCP",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"debugger",
|
"debugger",
|
||||||
"activeTab",
|
"activeTab",
|
||||||
@@ -16,12 +16,18 @@
|
|||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"default_title": "Click to attach debugger"
|
"default_title": "Click to attach debugger",
|
||||||
|
"default_icon": {
|
||||||
|
"16": "icons/icon-gray-16.png",
|
||||||
|
"32": "icons/icon-gray-32.png",
|
||||||
|
"48": "icons/icon-gray-48.png",
|
||||||
|
"128": "icons/icon-gray-128.png"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"icons": {
|
"icons": {
|
||||||
"16": "icons/icon-16.png",
|
"16": "icons/icon-gray-16.png",
|
||||||
"32": "icons/icon-32.png",
|
"32": "icons/icon-gray-32.png",
|
||||||
"48": "icons/icon-48.png",
|
"48": "icons/icon-gray-48.png",
|
||||||
"128": "icons/icon-128.png"
|
"128": "icons/icon-gray-128.png"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,31 @@
|
|||||||
{
|
{
|
||||||
"name": "@playwright/mcp-extension",
|
"name": "@playwright/mcp-extension",
|
||||||
"version": "0.0.46",
|
"version": "0.0.46",
|
||||||
"description": "Playwright MCP Browser Extension",
|
"description": "Playwright MCP Browser Extension",
|
||||||
"private": true,
|
"private": true,
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/microsoft/playwright-mcp.git"
|
"url": "git+https://github.com/microsoft/playwright-mcp.git"
|
||||||
},
|
},
|
||||||
"homepage": "https://playwright.dev",
|
"homepage": "https://playwright.dev",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Microsoft Corporation"
|
"name": "Microsoft Corporation"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc --project . && vite build --config vite.sw.config.mts",
|
"build": "tsc --project . && vite build --config vite.sw.config.mts",
|
||||||
"watch": "tsc --watch --project . & vite build --watch --config vite.sw.config.mts",
|
"watch": "concurrently \"tsc --watch --project .\" \"vite build --watch --config vite.sw.config.mts\"",
|
||||||
"test": "playwright test",
|
"test": "playwright test",
|
||||||
"clean": "rm -rf dist"
|
"clean": "rm -rf dist"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chrome": "^0.0.315",
|
"@types/chrome": "^0.0.315",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vite": "^5.4.21",
|
"concurrently": "^8.2.2",
|
||||||
"vite-plugin-static-copy": "^3.1.1"
|
"vite": "^5.4.21",
|
||||||
}
|
"vite-plugin-static-copy": "^3.1.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
import { RelayConnection, debugLog } from './relayConnection';
|
import { RelayConnection, debugLog } from './relayConnection';
|
||||||
|
|
||||||
// Relay URL - fixed port for MCP bridge
|
// Relay URL - fixed port for MCP bridge
|
||||||
const RELAY_URL = 'ws://localhost:9988';
|
const RELAY_URL = 'ws://localhost:9988/extension/550e8400-e29b-41d4-a716-446655440000';
|
||||||
|
|
||||||
class SimplifiedExtension {
|
class SimplifiedExtension {
|
||||||
private _connection: RelayConnection | undefined;
|
private _connection: RelayConnection | undefined;
|
||||||
@@ -133,12 +133,29 @@ class SimplifiedExtension {
|
|||||||
try {
|
try {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 'connected':
|
case 'connected':
|
||||||
await chrome.action.setBadgeText({ tabId, text: '✓' });
|
await chrome.action.setIcon({
|
||||||
await chrome.action.setBadgeBackgroundColor({ tabId, color: '#4CAF50' });
|
tabId,
|
||||||
|
path: {
|
||||||
|
'16': '/icons/icon-green-16.png',
|
||||||
|
'32': '/icons/icon-green-32.png',
|
||||||
|
'48': '/icons/icon-green-48.png',
|
||||||
|
'128': '/icons/icon-green-128.png'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await chrome.action.setBadgeText({ tabId, text: '' });
|
||||||
await chrome.action.setTitle({ tabId, title: 'Connected - Click to disconnect' });
|
await chrome.action.setTitle({ tabId, title: 'Connected - Click to disconnect' });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'connecting':
|
case 'connecting':
|
||||||
|
await chrome.action.setIcon({
|
||||||
|
tabId,
|
||||||
|
path: {
|
||||||
|
'16': '/icons/icon-gray-16.png',
|
||||||
|
'32': '/icons/icon-gray-32.png',
|
||||||
|
'48': '/icons/icon-gray-48.png',
|
||||||
|
'128': '/icons/icon-gray-128.png'
|
||||||
|
}
|
||||||
|
});
|
||||||
await chrome.action.setBadgeText({ tabId, text: '...' });
|
await chrome.action.setBadgeText({ tabId, text: '...' });
|
||||||
await chrome.action.setBadgeBackgroundColor({ tabId, color: '#FF9800' });
|
await chrome.action.setBadgeBackgroundColor({ tabId, color: '#FF9800' });
|
||||||
await chrome.action.setTitle({ tabId, title: 'Connecting...' });
|
await chrome.action.setTitle({ tabId, title: 'Connecting...' });
|
||||||
@@ -146,6 +163,15 @@ class SimplifiedExtension {
|
|||||||
|
|
||||||
case 'disconnected':
|
case 'disconnected':
|
||||||
default:
|
default:
|
||||||
|
await chrome.action.setIcon({
|
||||||
|
tabId,
|
||||||
|
path: {
|
||||||
|
'16': '/icons/icon-gray-16.png',
|
||||||
|
'32': '/icons/icon-gray-32.png',
|
||||||
|
'48': '/icons/icon-gray-48.png',
|
||||||
|
'128': '/icons/icon-gray-128.png'
|
||||||
|
}
|
||||||
|
});
|
||||||
await chrome.action.setBadgeText({ tabId, text: '' });
|
await chrome.action.setBadgeText({ tabId, text: '' });
|
||||||
await chrome.action.setTitle({ tabId, title: 'Click to attach debugger' });
|
await chrome.action.setTitle({ tabId, title: 'Click to attach debugger' });
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -14,28 +14,17 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { resolve } from 'path';
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, resolve } from 'path';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [react()],
|
||||||
react(),
|
|
||||||
viteStaticCopy({
|
|
||||||
targets: [
|
|
||||||
{
|
|
||||||
src: '../../icons/*',
|
|
||||||
dest: 'icons'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: '../../manifest.json',
|
|
||||||
dest: '.'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
root: resolve(__dirname, 'src/ui'),
|
root: resolve(__dirname, 'src/ui'),
|
||||||
build: {
|
build: {
|
||||||
outDir: resolve(__dirname, 'dist/'),
|
outDir: resolve(__dirname, 'dist/'),
|
||||||
|
|||||||
@@ -14,10 +14,29 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { resolve } from 'path';
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, resolve } from 'path';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
|
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
viteStaticCopy({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
src: resolve(__dirname, 'icons/*'),
|
||||||
|
dest: 'icons'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: resolve(__dirname, 'manifest.json'),
|
||||||
|
dest: '.'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
],
|
||||||
build: {
|
build: {
|
||||||
lib: {
|
lib: {
|
||||||
entry: resolve(__dirname, 'src/background.ts'),
|
entry: resolve(__dirname, 'src/background.ts'),
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import playwright from 'playwright-core';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const uuid = process.env.CDP_UUID || '550e8400-e29b-41d4-a716-446655440000';
|
||||||
|
const cdpEndpoint = `ws://localhost:9988/cdp/${uuid}`
|
||||||
|
const browser = await playwright.chromium.connectOverCDP(cdpEndpoint);
|
||||||
|
|
||||||
|
const contexts = browser.contexts();
|
||||||
|
console.log(`Found ${contexts.length} browser context(s)`);
|
||||||
|
|
||||||
|
for (const context of contexts) {
|
||||||
|
const pages = context.pages();
|
||||||
|
console.log(`Context has ${pages.length} page(s):`);
|
||||||
|
|
||||||
|
for (const page of pages) {
|
||||||
|
const url = page.url();
|
||||||
|
console.log(`\nPage URL: ${url}`);
|
||||||
|
|
||||||
|
const html = await page.content();
|
||||||
|
const lines = html.split('\n').slice(0, 3);
|
||||||
|
console.log('First 3 lines of HTML:');
|
||||||
|
lines.forEach((line, i) => {
|
||||||
|
console.log(` ${i + 1}: ${line}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -5,6 +5,6 @@ async function main() {
|
|||||||
const { browserContext, close } = await createExtensionContext(
|
const { browserContext, close } = await createExtensionContext(
|
||||||
controller.signal,
|
controller.signal,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export class CDPRelayServer {
|
|||||||
this._wsHost = httpAddressToString(server.address()).replace(/^http/, 'ws');
|
this._wsHost = httpAddressToString(server.address()).replace(/^http/, 'ws');
|
||||||
|
|
||||||
|
|
||||||
const uuid = crypto.randomUUID();
|
const uuid = process.env.CDP_UUID || crypto.randomUUID();
|
||||||
this._cdpPath = `/cdp/${uuid}`;
|
this._cdpPath = `/cdp/${uuid}`;
|
||||||
this._extensionPath = `/extension/${uuid}`;
|
this._extensionPath = `/extension/${uuid}`;
|
||||||
|
|
||||||
@@ -227,7 +227,9 @@ export class CDPRelayServer {
|
|||||||
if (sessionId)
|
if (sessionId)
|
||||||
break;
|
break;
|
||||||
// Simulate auto-attach behavior with real target info
|
// Simulate auto-attach behavior with real target info
|
||||||
const { targetInfo } = await this._extensionConnection!.send('attachToTab', { });
|
if (!this._extensionConnection)
|
||||||
|
throw new Error('Extension not connected. Please ensure the Chrome extension is installed and connected to the extension endpoint before connecting Playwright.');
|
||||||
|
const { targetInfo } = await this._extensionConnection.send('attachToTab', { });
|
||||||
this._connectedTabInfo = {
|
this._connectedTabInfo = {
|
||||||
targetInfo,
|
targetInfo,
|
||||||
sessionId: `pw-tab-${this._nextSessionId++}`,
|
sessionId: `pw-tab-${this._nextSessionId++}`,
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ export async function createExtensionContext(abortSignal: AbortSignal, ): Promis
|
|||||||
|
|
||||||
// TODO simply call fetch. it was creatign a full fledged browser just to fetch an url previously. CRAZY
|
// TODO simply call fetch. it was creatign a full fledged browser just to fetch an url previously. CRAZY
|
||||||
// await cdpRelayServer.ensureExtensionConnectionForMCPContext(clientInfo, abortSignal, toolName);
|
// await cdpRelayServer.ensureExtensionConnectionForMCPContext(clientInfo, abortSignal, toolName);
|
||||||
|
// await waitForExtension()
|
||||||
|
return {}
|
||||||
const browser = await playwright.chromium.connectOverCDP(cdpRelayServer.cdpEndpoint());
|
const browser = await playwright.chromium.connectOverCDP(cdpRelayServer.cdpEndpoint());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||