@playwright/mcp 0.0.36-alpha-2025-09-04 → 0.0.37-alpha-2025-09-08
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -46
- package/cli.js +7 -1
- package/config.d.ts +24 -0
- package/index.js +1 -1
- package/package.json +14 -39
- package/lib/browser/browserContextFactory.js +0 -251
- package/lib/browser/browserServerBackend.js +0 -79
- package/lib/browser/codegen.js +0 -54
- package/lib/browser/config.js +0 -263
- package/lib/browser/context.js +0 -223
- package/lib/browser/response.js +0 -165
- package/lib/browser/sessionLog.js +0 -126
- package/lib/browser/tab.js +0 -251
- package/lib/browser/tools/common.js +0 -57
- package/lib/browser/tools/console.js +0 -35
- package/lib/browser/tools/dialogs.js +0 -49
- package/lib/browser/tools/evaluate.js +0 -88
- package/lib/browser/tools/files.js +0 -46
- package/lib/browser/tools/form.js +0 -92
- package/lib/browser/tools/install.js +0 -57
- package/lib/browser/tools/keyboard.js +0 -113
- package/lib/browser/tools/mouse.js +0 -101
- package/lib/browser/tools/navigate.js +0 -56
- package/lib/browser/tools/network.js +0 -43
- package/lib/browser/tools/pdf.js +0 -76
- package/lib/browser/tools/screenshot.js +0 -115
- package/lib/browser/tools/snapshot.js +0 -175
- package/lib/browser/tools/tabs.js +0 -61
- package/lib/browser/tools/tool.js +0 -37
- package/lib/browser/tools/utils.js +0 -79
- package/lib/browser/tools/verify.js +0 -172
- package/lib/browser/tools/wait.js +0 -57
- package/lib/browser/tools.js +0 -61
- package/lib/extension/cdpRelay.js +0 -395
- package/lib/extension/extensionContextFactory.js +0 -93
- package/lib/extension/protocol.js +0 -21
- package/lib/index.js +0 -75
- package/lib/log.js +0 -28
- package/lib/package.js +0 -24
- package/lib/program.js +0 -161
- package/lib/sdk/bundle.js +0 -79
- package/lib/sdk/http.js +0 -175
- package/lib/sdk/inProcessTransport.js +0 -67
- package/lib/sdk/manualPromise.js +0 -113
- package/lib/sdk/mdb.js +0 -237
- package/lib/sdk/proxyBackend.js +0 -141
- package/lib/sdk/server.js +0 -164
- package/lib/sdk/tool.js +0 -36
- package/lib/vscode/host.js +0 -199
- package/lib/vscode/main.js +0 -97
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) Microsoft Corporation.
|
|
4
|
-
*
|
|
5
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
* you may not use this file except in compliance with the License.
|
|
7
|
-
* You may obtain a copy of the License at
|
|
8
|
-
*
|
|
9
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
*
|
|
11
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
* See the License for the specific language governing permissions and
|
|
15
|
-
* limitations under the License.
|
|
16
|
-
*/
|
|
17
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
const bundle_1 = require("../../sdk/bundle");
|
|
19
|
-
const tool_1 = require("./tool");
|
|
20
|
-
const wait = (0, tool_1.defineTool)({
|
|
21
|
-
capability: 'core',
|
|
22
|
-
schema: {
|
|
23
|
-
name: 'browser_wait_for',
|
|
24
|
-
title: 'Wait for',
|
|
25
|
-
description: 'Wait for text to appear or disappear or a specified time to pass',
|
|
26
|
-
inputSchema: bundle_1.z.object({
|
|
27
|
-
time: bundle_1.z.number().optional().describe('The time to wait in seconds'),
|
|
28
|
-
text: bundle_1.z.string().optional().describe('The text to wait for'),
|
|
29
|
-
textGone: bundle_1.z.string().optional().describe('The text to wait for to disappear'),
|
|
30
|
-
}),
|
|
31
|
-
type: 'readOnly',
|
|
32
|
-
},
|
|
33
|
-
handle: async (context, params, response) => {
|
|
34
|
-
if (!params.text && !params.textGone && !params.time)
|
|
35
|
-
throw new Error('Either time, text or textGone must be provided');
|
|
36
|
-
if (params.time) {
|
|
37
|
-
response.addCode(`await new Promise(f => setTimeout(f, ${params.time} * 1000));`);
|
|
38
|
-
await new Promise(f => setTimeout(f, Math.min(30000, params.time * 1000)));
|
|
39
|
-
}
|
|
40
|
-
const tab = context.currentTabOrDie();
|
|
41
|
-
const locator = params.text ? tab.page.getByText(params.text).first() : undefined;
|
|
42
|
-
const goneLocator = params.textGone ? tab.page.getByText(params.textGone).first() : undefined;
|
|
43
|
-
if (goneLocator) {
|
|
44
|
-
response.addCode(`await page.getByText(${JSON.stringify(params.textGone)}).first().waitFor({ state: 'hidden' });`);
|
|
45
|
-
await goneLocator.waitFor({ state: 'hidden' });
|
|
46
|
-
}
|
|
47
|
-
if (locator) {
|
|
48
|
-
response.addCode(`await page.getByText(${JSON.stringify(params.text)}).first().waitFor({ state: 'visible' });`);
|
|
49
|
-
await locator.waitFor({ state: 'visible' });
|
|
50
|
-
}
|
|
51
|
-
response.addResult(`Waited for ${params.text || params.textGone || params.time}`);
|
|
52
|
-
response.setIncludeSnapshot();
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
exports.default = [
|
|
56
|
-
wait,
|
|
57
|
-
];
|
package/lib/browser/tools.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) Microsoft Corporation.
|
|
4
|
-
*
|
|
5
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
* you may not use this file except in compliance with the License.
|
|
7
|
-
* You may obtain a copy of the License at
|
|
8
|
-
*
|
|
9
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
*
|
|
11
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
* See the License for the specific language governing permissions and
|
|
15
|
-
* limitations under the License.
|
|
16
|
-
*/
|
|
17
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
18
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
|
-
};
|
|
20
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.allTools = void 0;
|
|
22
|
-
exports.filteredTools = filteredTools;
|
|
23
|
-
const common_1 = __importDefault(require("./tools/common"));
|
|
24
|
-
const console_1 = __importDefault(require("./tools/console"));
|
|
25
|
-
const dialogs_1 = __importDefault(require("./tools/dialogs"));
|
|
26
|
-
const evaluate_1 = __importDefault(require("./tools/evaluate"));
|
|
27
|
-
const files_1 = __importDefault(require("./tools/files"));
|
|
28
|
-
const form_1 = __importDefault(require("./tools/form"));
|
|
29
|
-
const install_1 = __importDefault(require("./tools/install"));
|
|
30
|
-
const keyboard_1 = __importDefault(require("./tools/keyboard"));
|
|
31
|
-
const mouse_1 = __importDefault(require("./tools/mouse"));
|
|
32
|
-
const navigate_1 = __importDefault(require("./tools/navigate"));
|
|
33
|
-
const network_1 = __importDefault(require("./tools/network"));
|
|
34
|
-
const pdf_1 = __importDefault(require("./tools/pdf"));
|
|
35
|
-
const snapshot_1 = __importDefault(require("./tools/snapshot"));
|
|
36
|
-
const tabs_1 = __importDefault(require("./tools/tabs"));
|
|
37
|
-
const screenshot_1 = __importDefault(require("./tools/screenshot"));
|
|
38
|
-
const wait_1 = __importDefault(require("./tools/wait"));
|
|
39
|
-
const verify_1 = __importDefault(require("./tools/verify"));
|
|
40
|
-
exports.allTools = [
|
|
41
|
-
...common_1.default,
|
|
42
|
-
...console_1.default,
|
|
43
|
-
...dialogs_1.default,
|
|
44
|
-
...evaluate_1.default,
|
|
45
|
-
...files_1.default,
|
|
46
|
-
...form_1.default,
|
|
47
|
-
...install_1.default,
|
|
48
|
-
...keyboard_1.default,
|
|
49
|
-
...navigate_1.default,
|
|
50
|
-
...network_1.default,
|
|
51
|
-
...mouse_1.default,
|
|
52
|
-
...pdf_1.default,
|
|
53
|
-
...screenshot_1.default,
|
|
54
|
-
...snapshot_1.default,
|
|
55
|
-
...tabs_1.default,
|
|
56
|
-
...wait_1.default,
|
|
57
|
-
...verify_1.default,
|
|
58
|
-
];
|
|
59
|
-
function filteredTools(config) {
|
|
60
|
-
return exports.allTools.filter(tool => { var _a; return tool.capability.startsWith('core') || ((_a = config.capabilities) === null || _a === void 0 ? void 0 : _a.includes(tool.capability)); });
|
|
61
|
-
}
|
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) Microsoft Corporation.
|
|
4
|
-
*
|
|
5
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
* you may not use this file except in compliance with the License.
|
|
7
|
-
* You may obtain a copy of the License at
|
|
8
|
-
*
|
|
9
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
*
|
|
11
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
* See the License for the specific language governing permissions and
|
|
15
|
-
* limitations under the License.
|
|
16
|
-
*/
|
|
17
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
-
if (k2 === undefined) k2 = k;
|
|
19
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
-
}
|
|
23
|
-
Object.defineProperty(o, k2, desc);
|
|
24
|
-
}) : (function(o, m, k, k2) {
|
|
25
|
-
if (k2 === undefined) k2 = k;
|
|
26
|
-
o[k2] = m[k];
|
|
27
|
-
}));
|
|
28
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
-
}) : function(o, v) {
|
|
31
|
-
o["default"] = v;
|
|
32
|
-
});
|
|
33
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
-
var ownKeys = function(o) {
|
|
35
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
-
var ar = [];
|
|
37
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
-
return ar;
|
|
39
|
-
};
|
|
40
|
-
return ownKeys(o);
|
|
41
|
-
};
|
|
42
|
-
return function (mod) {
|
|
43
|
-
if (mod && mod.__esModule) return mod;
|
|
44
|
-
var result = {};
|
|
45
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
-
__setModuleDefault(result, mod);
|
|
47
|
-
return result;
|
|
48
|
-
};
|
|
49
|
-
})();
|
|
50
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
51
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
52
|
-
};
|
|
53
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
-
exports.CDPRelayServer = void 0;
|
|
55
|
-
/**
|
|
56
|
-
* WebSocket server that bridges Playwright MCP and Chrome Extension
|
|
57
|
-
*
|
|
58
|
-
* Endpoints:
|
|
59
|
-
* - /cdp/guid - Full CDP interface for Playwright MCP
|
|
60
|
-
* - /extension/guid - Extension connection for chrome.debugger forwarding
|
|
61
|
-
*/
|
|
62
|
-
const child_process_1 = require("child_process");
|
|
63
|
-
const debug_1 = __importDefault(require("debug"));
|
|
64
|
-
const ws_1 = require("ws");
|
|
65
|
-
// @ts-ignore
|
|
66
|
-
const index_1 = require("playwright-core/lib/server/registry/index");
|
|
67
|
-
const http_1 = require("../sdk/http");
|
|
68
|
-
const log_1 = require("../log");
|
|
69
|
-
const manualPromise_1 = require("../sdk/manualPromise");
|
|
70
|
-
const protocol = __importStar(require("./protocol"));
|
|
71
|
-
const debugLogger = (0, debug_1.default)('pw:mcp:relay');
|
|
72
|
-
class CDPRelayServer {
|
|
73
|
-
constructor(server, browserChannel, userDataDir, executablePath) {
|
|
74
|
-
this._playwrightConnection = null;
|
|
75
|
-
this._extensionConnection = null;
|
|
76
|
-
this._nextSessionId = 1;
|
|
77
|
-
this._wsHost = (0, http_1.httpAddressToString)(server.address()).replace(/^http/, 'ws');
|
|
78
|
-
this._browserChannel = browserChannel;
|
|
79
|
-
this._userDataDir = userDataDir;
|
|
80
|
-
this._executablePath = executablePath;
|
|
81
|
-
const uuid = crypto.randomUUID();
|
|
82
|
-
this._cdpPath = `/cdp/${uuid}`;
|
|
83
|
-
this._extensionPath = `/extension/${uuid}`;
|
|
84
|
-
this._resetExtensionConnection();
|
|
85
|
-
this._wss = new ws_1.WebSocketServer({ server });
|
|
86
|
-
this._wss.on('connection', this._onConnection.bind(this));
|
|
87
|
-
}
|
|
88
|
-
cdpEndpoint() {
|
|
89
|
-
return `${this._wsHost}${this._cdpPath}`;
|
|
90
|
-
}
|
|
91
|
-
extensionEndpoint() {
|
|
92
|
-
return `${this._wsHost}${this._extensionPath}`;
|
|
93
|
-
}
|
|
94
|
-
async ensureExtensionConnectionForMCPContext(clientInfo, abortSignal, toolName) {
|
|
95
|
-
debugLogger('Ensuring extension connection for MCP context');
|
|
96
|
-
if (this._extensionConnection)
|
|
97
|
-
return;
|
|
98
|
-
this._connectBrowser(clientInfo, toolName);
|
|
99
|
-
debugLogger('Waiting for incoming extension connection');
|
|
100
|
-
await Promise.race([
|
|
101
|
-
this._extensionConnectionPromise,
|
|
102
|
-
new Promise((_, reject) => setTimeout(() => {
|
|
103
|
-
reject(new Error(`Extension connection timeout. Make sure the "Playwright MCP Bridge" extension is installed. See https://github.com/microsoft/playwright-mcp/blob/main/extension/README.md for installation instructions.`));
|
|
104
|
-
}, process.env.PWMCP_TEST_CONNECTION_TIMEOUT ? parseInt(process.env.PWMCP_TEST_CONNECTION_TIMEOUT, 10) : 5000)),
|
|
105
|
-
new Promise((_, reject) => abortSignal.addEventListener('abort', reject))
|
|
106
|
-
]);
|
|
107
|
-
debugLogger('Extension connection established');
|
|
108
|
-
}
|
|
109
|
-
_connectBrowser(clientInfo, toolName) {
|
|
110
|
-
var _a;
|
|
111
|
-
const mcpRelayEndpoint = `${this._wsHost}${this._extensionPath}`;
|
|
112
|
-
// Need to specify "key" in the manifest.json to make the id stable when loading from file.
|
|
113
|
-
const url = new URL('chrome-extension://jakfalbnbhgkpmoaakfflhflbfpkailf/connect.html');
|
|
114
|
-
url.searchParams.set('mcpRelayUrl', mcpRelayEndpoint);
|
|
115
|
-
const client = {
|
|
116
|
-
name: clientInfo.name,
|
|
117
|
-
version: clientInfo.version,
|
|
118
|
-
};
|
|
119
|
-
url.searchParams.set('client', JSON.stringify(client));
|
|
120
|
-
url.searchParams.set('protocolVersion', (_a = process.env.PWMCP_TEST_PROTOCOL_VERSION) !== null && _a !== void 0 ? _a : protocol.VERSION.toString());
|
|
121
|
-
if (toolName)
|
|
122
|
-
url.searchParams.set('newTab', String(toolName === 'browser_navigate'));
|
|
123
|
-
const href = url.toString();
|
|
124
|
-
let executablePath = this._executablePath;
|
|
125
|
-
if (!executablePath) {
|
|
126
|
-
const executableInfo = index_1.registry.findExecutable(this._browserChannel);
|
|
127
|
-
if (!executableInfo)
|
|
128
|
-
throw new Error(`Unsupported channel: "${this._browserChannel}"`);
|
|
129
|
-
executablePath = executableInfo.executablePath();
|
|
130
|
-
if (!executablePath)
|
|
131
|
-
throw new Error(`"${this._browserChannel}" executable not found. Make sure it is installed at a standard location.`);
|
|
132
|
-
}
|
|
133
|
-
const args = [];
|
|
134
|
-
if (this._userDataDir)
|
|
135
|
-
args.push(`--user-data-dir=${this._userDataDir}`);
|
|
136
|
-
args.push(href);
|
|
137
|
-
(0, child_process_1.spawn)(executablePath, args, {
|
|
138
|
-
windowsHide: true,
|
|
139
|
-
detached: true,
|
|
140
|
-
shell: false,
|
|
141
|
-
stdio: 'ignore',
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
stop() {
|
|
145
|
-
this.closeConnections('Server stopped');
|
|
146
|
-
this._wss.close();
|
|
147
|
-
}
|
|
148
|
-
closeConnections(reason) {
|
|
149
|
-
this._closePlaywrightConnection(reason);
|
|
150
|
-
this._closeExtensionConnection(reason);
|
|
151
|
-
}
|
|
152
|
-
_onConnection(ws, request) {
|
|
153
|
-
const url = new URL(`http://localhost${request.url}`);
|
|
154
|
-
debugLogger(`New connection to ${url.pathname}`);
|
|
155
|
-
if (url.pathname === this._cdpPath) {
|
|
156
|
-
this._handlePlaywrightConnection(ws);
|
|
157
|
-
}
|
|
158
|
-
else if (url.pathname === this._extensionPath) {
|
|
159
|
-
this._handleExtensionConnection(ws);
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
debugLogger(`Invalid path: ${url.pathname}`);
|
|
163
|
-
ws.close(4004, 'Invalid path');
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
_handlePlaywrightConnection(ws) {
|
|
167
|
-
if (this._playwrightConnection) {
|
|
168
|
-
debugLogger('Rejecting second Playwright connection');
|
|
169
|
-
ws.close(1000, 'Another CDP client already connected');
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
this._playwrightConnection = ws;
|
|
173
|
-
ws.on('message', async (data) => {
|
|
174
|
-
try {
|
|
175
|
-
const message = JSON.parse(data.toString());
|
|
176
|
-
await this._handlePlaywrightMessage(message);
|
|
177
|
-
}
|
|
178
|
-
catch (error) {
|
|
179
|
-
debugLogger(`Error while handling Playwright message\n${data.toString()}\n`, error);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
ws.on('close', () => {
|
|
183
|
-
if (this._playwrightConnection !== ws)
|
|
184
|
-
return;
|
|
185
|
-
this._playwrightConnection = null;
|
|
186
|
-
this._closeExtensionConnection('Playwright client disconnected');
|
|
187
|
-
debugLogger('Playwright WebSocket closed');
|
|
188
|
-
});
|
|
189
|
-
ws.on('error', error => {
|
|
190
|
-
debugLogger('Playwright WebSocket error:', error);
|
|
191
|
-
});
|
|
192
|
-
debugLogger('Playwright MCP connected');
|
|
193
|
-
}
|
|
194
|
-
_closeExtensionConnection(reason) {
|
|
195
|
-
var _a;
|
|
196
|
-
(_a = this._extensionConnection) === null || _a === void 0 ? void 0 : _a.close(reason);
|
|
197
|
-
this._extensionConnectionPromise.reject(new Error(reason));
|
|
198
|
-
this._resetExtensionConnection();
|
|
199
|
-
}
|
|
200
|
-
_resetExtensionConnection() {
|
|
201
|
-
this._connectedTabInfo = undefined;
|
|
202
|
-
this._extensionConnection = null;
|
|
203
|
-
this._extensionConnectionPromise = new manualPromise_1.ManualPromise();
|
|
204
|
-
void this._extensionConnectionPromise.catch(log_1.logUnhandledError);
|
|
205
|
-
}
|
|
206
|
-
_closePlaywrightConnection(reason) {
|
|
207
|
-
var _a;
|
|
208
|
-
if (((_a = this._playwrightConnection) === null || _a === void 0 ? void 0 : _a.readyState) === ws_1.WebSocket.OPEN)
|
|
209
|
-
this._playwrightConnection.close(1000, reason);
|
|
210
|
-
this._playwrightConnection = null;
|
|
211
|
-
}
|
|
212
|
-
_handleExtensionConnection(ws) {
|
|
213
|
-
if (this._extensionConnection) {
|
|
214
|
-
ws.close(1000, 'Another extension connection already established');
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
this._extensionConnection = new ExtensionConnection(ws);
|
|
218
|
-
this._extensionConnection.onclose = (c, reason) => {
|
|
219
|
-
debugLogger('Extension WebSocket closed:', reason, c === this._extensionConnection);
|
|
220
|
-
if (this._extensionConnection !== c)
|
|
221
|
-
return;
|
|
222
|
-
this._resetExtensionConnection();
|
|
223
|
-
this._closePlaywrightConnection(`Extension disconnected: ${reason}`);
|
|
224
|
-
};
|
|
225
|
-
this._extensionConnection.onmessage = this._handleExtensionMessage.bind(this);
|
|
226
|
-
this._extensionConnectionPromise.resolve();
|
|
227
|
-
}
|
|
228
|
-
_handleExtensionMessage(method, params) {
|
|
229
|
-
var _a;
|
|
230
|
-
switch (method) {
|
|
231
|
-
case 'forwardCDPEvent':
|
|
232
|
-
const sessionId = params.sessionId || ((_a = this._connectedTabInfo) === null || _a === void 0 ? void 0 : _a.sessionId);
|
|
233
|
-
this._sendToPlaywright({
|
|
234
|
-
sessionId,
|
|
235
|
-
method: params.method,
|
|
236
|
-
params: params.params
|
|
237
|
-
});
|
|
238
|
-
break;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
async _handlePlaywrightMessage(message) {
|
|
242
|
-
debugLogger('← Playwright:', `${message.method} (id=${message.id})`);
|
|
243
|
-
const { id, sessionId, method, params } = message;
|
|
244
|
-
try {
|
|
245
|
-
const result = await this._handleCDPCommand(method, params, sessionId);
|
|
246
|
-
this._sendToPlaywright({ id, sessionId, result });
|
|
247
|
-
}
|
|
248
|
-
catch (e) {
|
|
249
|
-
debugLogger('Error in the extension:', e);
|
|
250
|
-
this._sendToPlaywright({
|
|
251
|
-
id,
|
|
252
|
-
sessionId,
|
|
253
|
-
error: { message: e.message }
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
async _handleCDPCommand(method, params, sessionId) {
|
|
258
|
-
var _a;
|
|
259
|
-
switch (method) {
|
|
260
|
-
case 'Browser.getVersion': {
|
|
261
|
-
return {
|
|
262
|
-
protocolVersion: '1.3',
|
|
263
|
-
product: 'Chrome/Extension-Bridge',
|
|
264
|
-
userAgent: 'CDP-Bridge-Server/1.0.0',
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
case 'Browser.setDownloadBehavior': {
|
|
268
|
-
return {};
|
|
269
|
-
}
|
|
270
|
-
case 'Target.setAutoAttach': {
|
|
271
|
-
// Forward child session handling.
|
|
272
|
-
if (sessionId)
|
|
273
|
-
break;
|
|
274
|
-
// Simulate auto-attach behavior with real target info
|
|
275
|
-
const { targetInfo } = await this._extensionConnection.send('attachToTab', {});
|
|
276
|
-
this._connectedTabInfo = {
|
|
277
|
-
targetInfo,
|
|
278
|
-
sessionId: `pw-tab-${this._nextSessionId++}`,
|
|
279
|
-
};
|
|
280
|
-
debugLogger('Simulating auto-attach');
|
|
281
|
-
this._sendToPlaywright({
|
|
282
|
-
method: 'Target.attachedToTarget',
|
|
283
|
-
params: {
|
|
284
|
-
sessionId: this._connectedTabInfo.sessionId,
|
|
285
|
-
targetInfo: {
|
|
286
|
-
...this._connectedTabInfo.targetInfo,
|
|
287
|
-
attached: true,
|
|
288
|
-
},
|
|
289
|
-
waitingForDebugger: false
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
return {};
|
|
293
|
-
}
|
|
294
|
-
case 'Target.getTargetInfo': {
|
|
295
|
-
return (_a = this._connectedTabInfo) === null || _a === void 0 ? void 0 : _a.targetInfo;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
return await this._forwardToExtension(method, params, sessionId);
|
|
299
|
-
}
|
|
300
|
-
async _forwardToExtension(method, params, sessionId) {
|
|
301
|
-
var _a;
|
|
302
|
-
if (!this._extensionConnection)
|
|
303
|
-
throw new Error('Extension not connected');
|
|
304
|
-
// Top level sessionId is only passed between the relay and the client.
|
|
305
|
-
if (((_a = this._connectedTabInfo) === null || _a === void 0 ? void 0 : _a.sessionId) === sessionId)
|
|
306
|
-
sessionId = undefined;
|
|
307
|
-
return await this._extensionConnection.send('forwardCDPCommand', { sessionId, method, params });
|
|
308
|
-
}
|
|
309
|
-
_sendToPlaywright(message) {
|
|
310
|
-
var _a, _b;
|
|
311
|
-
debugLogger('→ Playwright:', `${(_a = message.method) !== null && _a !== void 0 ? _a : `response(id=${message.id})`}`);
|
|
312
|
-
(_b = this._playwrightConnection) === null || _b === void 0 ? void 0 : _b.send(JSON.stringify(message));
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
exports.CDPRelayServer = CDPRelayServer;
|
|
316
|
-
class ExtensionConnection {
|
|
317
|
-
constructor(ws) {
|
|
318
|
-
this._callbacks = new Map();
|
|
319
|
-
this._lastId = 0;
|
|
320
|
-
this._ws = ws;
|
|
321
|
-
this._ws.on('message', this._onMessage.bind(this));
|
|
322
|
-
this._ws.on('close', this._onClose.bind(this));
|
|
323
|
-
this._ws.on('error', this._onError.bind(this));
|
|
324
|
-
}
|
|
325
|
-
async send(method, params) {
|
|
326
|
-
if (this._ws.readyState !== ws_1.WebSocket.OPEN)
|
|
327
|
-
throw new Error(`Unexpected WebSocket state: ${this._ws.readyState}`);
|
|
328
|
-
const id = ++this._lastId;
|
|
329
|
-
this._ws.send(JSON.stringify({ id, method, params }));
|
|
330
|
-
const error = new Error(`Protocol error: ${method}`);
|
|
331
|
-
return new Promise((resolve, reject) => {
|
|
332
|
-
this._callbacks.set(id, { resolve, reject, error });
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
close(message) {
|
|
336
|
-
debugLogger('closing extension connection:', message);
|
|
337
|
-
if (this._ws.readyState === ws_1.WebSocket.OPEN)
|
|
338
|
-
this._ws.close(1000, message);
|
|
339
|
-
}
|
|
340
|
-
_onMessage(event) {
|
|
341
|
-
const eventData = event.toString();
|
|
342
|
-
let parsedJson;
|
|
343
|
-
try {
|
|
344
|
-
parsedJson = JSON.parse(eventData);
|
|
345
|
-
}
|
|
346
|
-
catch (e) {
|
|
347
|
-
debugLogger(`<closing ws> Closing websocket due to malformed JSON. eventData=${eventData} e=${e === null || e === void 0 ? void 0 : e.message}`);
|
|
348
|
-
this._ws.close();
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
try {
|
|
352
|
-
this._handleParsedMessage(parsedJson);
|
|
353
|
-
}
|
|
354
|
-
catch (e) {
|
|
355
|
-
debugLogger(`<closing ws> Closing websocket due to failed onmessage callback. eventData=${eventData} e=${e === null || e === void 0 ? void 0 : e.message}`);
|
|
356
|
-
this._ws.close();
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
_handleParsedMessage(object) {
|
|
360
|
-
var _a;
|
|
361
|
-
if (object.id && this._callbacks.has(object.id)) {
|
|
362
|
-
const callback = this._callbacks.get(object.id);
|
|
363
|
-
this._callbacks.delete(object.id);
|
|
364
|
-
if (object.error) {
|
|
365
|
-
const error = callback.error;
|
|
366
|
-
error.message = object.error;
|
|
367
|
-
callback.reject(error);
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
callback.resolve(object.result);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
else if (object.id) {
|
|
374
|
-
debugLogger('← Extension: unexpected response', object);
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
(_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, object.method, object.params);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
_onClose(event) {
|
|
381
|
-
var _a;
|
|
382
|
-
debugLogger(`<ws closed> code=${event.code} reason=${event.reason}`);
|
|
383
|
-
this._dispose();
|
|
384
|
-
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this, this, event.reason);
|
|
385
|
-
}
|
|
386
|
-
_onError(event) {
|
|
387
|
-
debugLogger(`<ws error> message=${event.message} type=${event.type} target=${event.target}`);
|
|
388
|
-
this._dispose();
|
|
389
|
-
}
|
|
390
|
-
_dispose() {
|
|
391
|
-
for (const callback of this._callbacks.values())
|
|
392
|
-
callback.reject(new Error('WebSocket closed'));
|
|
393
|
-
this._callbacks.clear();
|
|
394
|
-
}
|
|
395
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) Microsoft Corporation.
|
|
4
|
-
*
|
|
5
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
* you may not use this file except in compliance with the License.
|
|
7
|
-
* You may obtain a copy of the License at
|
|
8
|
-
*
|
|
9
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
*
|
|
11
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
* See the License for the specific language governing permissions and
|
|
15
|
-
* limitations under the License.
|
|
16
|
-
*/
|
|
17
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
-
if (k2 === undefined) k2 = k;
|
|
19
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
-
}
|
|
23
|
-
Object.defineProperty(o, k2, desc);
|
|
24
|
-
}) : (function(o, m, k, k2) {
|
|
25
|
-
if (k2 === undefined) k2 = k;
|
|
26
|
-
o[k2] = m[k];
|
|
27
|
-
}));
|
|
28
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
-
}) : function(o, v) {
|
|
31
|
-
o["default"] = v;
|
|
32
|
-
});
|
|
33
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
-
var ownKeys = function(o) {
|
|
35
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
-
var ar = [];
|
|
37
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
-
return ar;
|
|
39
|
-
};
|
|
40
|
-
return ownKeys(o);
|
|
41
|
-
};
|
|
42
|
-
return function (mod) {
|
|
43
|
-
if (mod && mod.__esModule) return mod;
|
|
44
|
-
var result = {};
|
|
45
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
-
__setModuleDefault(result, mod);
|
|
47
|
-
return result;
|
|
48
|
-
};
|
|
49
|
-
})();
|
|
50
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
51
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
52
|
-
};
|
|
53
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
-
exports.ExtensionContextFactory = void 0;
|
|
55
|
-
const debug_1 = __importDefault(require("debug"));
|
|
56
|
-
const playwright = __importStar(require("playwright"));
|
|
57
|
-
const http_1 = require("../sdk/http");
|
|
58
|
-
const cdpRelay_1 = require("./cdpRelay");
|
|
59
|
-
const debugLogger = (0, debug_1.default)('pw:mcp:relay');
|
|
60
|
-
class ExtensionContextFactory {
|
|
61
|
-
constructor(browserChannel, userDataDir, executablePath) {
|
|
62
|
-
this._browserChannel = browserChannel;
|
|
63
|
-
this._userDataDir = userDataDir;
|
|
64
|
-
this._executablePath = executablePath;
|
|
65
|
-
}
|
|
66
|
-
async createContext(clientInfo, abortSignal, toolName) {
|
|
67
|
-
const browser = await this._obtainBrowser(clientInfo, abortSignal, toolName);
|
|
68
|
-
return {
|
|
69
|
-
browserContext: browser.contexts()[0],
|
|
70
|
-
close: async () => {
|
|
71
|
-
debugLogger('close() called for browser context');
|
|
72
|
-
await browser.close();
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
async _obtainBrowser(clientInfo, abortSignal, toolName) {
|
|
77
|
-
const relay = await this._startRelay(abortSignal);
|
|
78
|
-
await relay.ensureExtensionConnectionForMCPContext(clientInfo, abortSignal, toolName);
|
|
79
|
-
return await playwright.chromium.connectOverCDP(relay.cdpEndpoint());
|
|
80
|
-
}
|
|
81
|
-
async _startRelay(abortSignal) {
|
|
82
|
-
const httpServer = await (0, http_1.startHttpServer)({});
|
|
83
|
-
if (abortSignal.aborted) {
|
|
84
|
-
httpServer.close();
|
|
85
|
-
throw new Error(abortSignal.reason);
|
|
86
|
-
}
|
|
87
|
-
const cdpRelayServer = new cdpRelay_1.CDPRelayServer(httpServer, this._browserChannel, this._userDataDir, this._executablePath);
|
|
88
|
-
abortSignal.addEventListener('abort', () => cdpRelayServer.stop());
|
|
89
|
-
debugLogger(`CDP relay server started, extension endpoint: ${cdpRelayServer.extensionEndpoint()}.`);
|
|
90
|
-
return cdpRelayServer;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
exports.ExtensionContextFactory = ExtensionContextFactory;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) Microsoft Corporation.
|
|
4
|
-
*
|
|
5
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
* you may not use this file except in compliance with the License.
|
|
7
|
-
* You may obtain a copy of the License at
|
|
8
|
-
*
|
|
9
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
*
|
|
11
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
* See the License for the specific language governing permissions and
|
|
15
|
-
* limitations under the License.
|
|
16
|
-
*/
|
|
17
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.VERSION = void 0;
|
|
19
|
-
// Whenever the commands/events change, the version must be updated. The latest
|
|
20
|
-
// extension version should be compatible with the old MCP clients.
|
|
21
|
-
exports.VERSION = 1;
|