@cotestdev/mcp_playwright 0.0.50 → 0.0.52
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/lib/mcp/browser/browserContextFactory.js +11 -4
- package/lib/mcp/browser/browserServerBackend.js +2 -4
- package/lib/mcp/browser/config.js +71 -47
- package/lib/mcp/browser/context.js +65 -4
- package/lib/mcp/browser/logFile.js +96 -0
- package/lib/mcp/browser/response.js +107 -104
- package/lib/mcp/browser/sessionLog.js +1 -1
- package/lib/mcp/browser/tab.js +73 -18
- package/lib/mcp/browser/tools/config.js +41 -0
- package/lib/mcp/browser/tools/console.js +6 -2
- package/lib/mcp/browser/tools/cookies.js +152 -0
- package/lib/mcp/browser/tools/install.js +1 -0
- package/lib/mcp/browser/tools/network.js +25 -11
- package/lib/mcp/browser/tools/pdf.js +3 -4
- package/lib/mcp/browser/tools/route.js +140 -0
- package/lib/mcp/browser/tools/runCode.js +0 -2
- package/lib/mcp/browser/tools/screenshot.js +6 -7
- package/lib/mcp/browser/tools/storage.js +3 -4
- package/lib/mcp/browser/tools/tracing.js +10 -9
- package/lib/mcp/browser/tools/utils.js +0 -6
- package/lib/mcp/browser/tools/video.js +31 -13
- package/lib/mcp/browser/tools/webstorage.js +223 -0
- package/lib/mcp/browser/tools.js +11 -3
- package/lib/mcp/extension/cdpRelay.js +7 -7
- package/lib/mcp/extension/extensionContextFactory.js +4 -2
- package/lib/mcp/program.js +19 -12
- package/lib/mcp/terminal/cli.js +23 -2
- package/lib/mcp/terminal/command.js +34 -30
- package/lib/mcp/terminal/commands.js +310 -38
- package/lib/mcp/terminal/daemon.js +23 -38
- package/lib/mcp/terminal/helpGenerator.js +8 -6
- package/lib/mcp/terminal/program.js +482 -199
- package/lib/mcp/terminal/socketConnection.js +17 -2
- package/package.json +2 -2
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var cookies_exports = {};
|
|
20
|
+
__export(cookies_exports, {
|
|
21
|
+
default: () => cookies_default
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(cookies_exports);
|
|
24
|
+
var import_mcpBundle = require("../../../mcpBundle");
|
|
25
|
+
var import_tool = require("./tool");
|
|
26
|
+
const cookieList = (0, import_tool.defineTool)({
|
|
27
|
+
capability: "storage",
|
|
28
|
+
schema: {
|
|
29
|
+
name: "browser_cookie_list",
|
|
30
|
+
title: "List cookies",
|
|
31
|
+
description: "List all cookies (optionally filtered by domain/path)",
|
|
32
|
+
inputSchema: import_mcpBundle.z.object({
|
|
33
|
+
domain: import_mcpBundle.z.string().optional().describe("Filter cookies by domain"),
|
|
34
|
+
path: import_mcpBundle.z.string().optional().describe("Filter cookies by path")
|
|
35
|
+
}),
|
|
36
|
+
type: "readOnly"
|
|
37
|
+
},
|
|
38
|
+
handle: async (context, params, response) => {
|
|
39
|
+
const browserContext = await context.ensureBrowserContext();
|
|
40
|
+
let cookies = await browserContext.cookies();
|
|
41
|
+
if (params.domain)
|
|
42
|
+
cookies = cookies.filter((c) => c.domain.includes(params.domain));
|
|
43
|
+
if (params.path)
|
|
44
|
+
cookies = cookies.filter((c) => c.path.startsWith(params.path));
|
|
45
|
+
if (cookies.length === 0)
|
|
46
|
+
response.addTextResult("No cookies found");
|
|
47
|
+
else
|
|
48
|
+
response.addTextResult(cookies.map((c) => `${c.name}=${c.value} (domain: ${c.domain}, path: ${c.path})`).join("\n"));
|
|
49
|
+
response.addCode(`await page.context().cookies();`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const cookieGet = (0, import_tool.defineTool)({
|
|
53
|
+
capability: "storage",
|
|
54
|
+
schema: {
|
|
55
|
+
name: "browser_cookie_get",
|
|
56
|
+
title: "Get cookie",
|
|
57
|
+
description: "Get a specific cookie by name",
|
|
58
|
+
inputSchema: import_mcpBundle.z.object({
|
|
59
|
+
name: import_mcpBundle.z.string().describe("Cookie name to get")
|
|
60
|
+
}),
|
|
61
|
+
type: "readOnly"
|
|
62
|
+
},
|
|
63
|
+
handle: async (context, params, response) => {
|
|
64
|
+
const browserContext = await context.ensureBrowserContext();
|
|
65
|
+
const cookies = await browserContext.cookies();
|
|
66
|
+
const cookie = cookies.find((c) => c.name === params.name);
|
|
67
|
+
if (!cookie)
|
|
68
|
+
response.addTextResult(`Cookie '${params.name}' not found`);
|
|
69
|
+
else
|
|
70
|
+
response.addTextResult(`${cookie.name}=${cookie.value} (domain: ${cookie.domain}, path: ${cookie.path}, httpOnly: ${cookie.httpOnly}, secure: ${cookie.secure}, sameSite: ${cookie.sameSite})`);
|
|
71
|
+
response.addCode(`await page.context().cookies();`);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
const cookieSet = (0, import_tool.defineTool)({
|
|
75
|
+
capability: "storage",
|
|
76
|
+
schema: {
|
|
77
|
+
name: "browser_cookie_set",
|
|
78
|
+
title: "Set cookie",
|
|
79
|
+
description: "Set a cookie with optional flags (domain, path, expires, httpOnly, secure, sameSite)",
|
|
80
|
+
inputSchema: import_mcpBundle.z.object({
|
|
81
|
+
name: import_mcpBundle.z.string().describe("Cookie name"),
|
|
82
|
+
value: import_mcpBundle.z.string().describe("Cookie value"),
|
|
83
|
+
domain: import_mcpBundle.z.string().optional().describe("Cookie domain"),
|
|
84
|
+
path: import_mcpBundle.z.string().optional().describe("Cookie path"),
|
|
85
|
+
expires: import_mcpBundle.z.number().optional().describe("Cookie expiration as Unix timestamp"),
|
|
86
|
+
httpOnly: import_mcpBundle.z.boolean().optional().describe("Whether the cookie is HTTP only"),
|
|
87
|
+
secure: import_mcpBundle.z.boolean().optional().describe("Whether the cookie is secure"),
|
|
88
|
+
sameSite: import_mcpBundle.z.enum(["Strict", "Lax", "None"]).optional().describe("Cookie SameSite attribute")
|
|
89
|
+
}),
|
|
90
|
+
type: "action"
|
|
91
|
+
},
|
|
92
|
+
handle: async (context, params, response) => {
|
|
93
|
+
const browserContext = await context.ensureBrowserContext();
|
|
94
|
+
const tab = await context.ensureTab();
|
|
95
|
+
const url = new URL(tab.page.url());
|
|
96
|
+
const cookie = {
|
|
97
|
+
name: params.name,
|
|
98
|
+
value: params.value,
|
|
99
|
+
domain: params.domain || url.hostname,
|
|
100
|
+
path: params.path || "/"
|
|
101
|
+
};
|
|
102
|
+
if (params.expires !== void 0)
|
|
103
|
+
cookie.expires = params.expires;
|
|
104
|
+
if (params.httpOnly !== void 0)
|
|
105
|
+
cookie.httpOnly = params.httpOnly;
|
|
106
|
+
if (params.secure !== void 0)
|
|
107
|
+
cookie.secure = params.secure;
|
|
108
|
+
if (params.sameSite !== void 0)
|
|
109
|
+
cookie.sameSite = params.sameSite;
|
|
110
|
+
await browserContext.addCookies([cookie]);
|
|
111
|
+
response.addCode(`await page.context().addCookies([${JSON.stringify(cookie)}]);`);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
const cookieDelete = (0, import_tool.defineTool)({
|
|
115
|
+
capability: "storage",
|
|
116
|
+
schema: {
|
|
117
|
+
name: "browser_cookie_delete",
|
|
118
|
+
title: "Delete cookie",
|
|
119
|
+
description: "Delete a specific cookie",
|
|
120
|
+
inputSchema: import_mcpBundle.z.object({
|
|
121
|
+
name: import_mcpBundle.z.string().describe("Cookie name to delete")
|
|
122
|
+
}),
|
|
123
|
+
type: "action"
|
|
124
|
+
},
|
|
125
|
+
handle: async (context, params, response) => {
|
|
126
|
+
const browserContext = await context.ensureBrowserContext();
|
|
127
|
+
await browserContext.clearCookies({ name: params.name });
|
|
128
|
+
response.addCode(`await page.context().clearCookies({ name: '${params.name}' });`);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
const cookieClear = (0, import_tool.defineTool)({
|
|
132
|
+
capability: "storage",
|
|
133
|
+
schema: {
|
|
134
|
+
name: "browser_cookie_clear",
|
|
135
|
+
title: "Clear cookies",
|
|
136
|
+
description: "Clear all cookies",
|
|
137
|
+
inputSchema: import_mcpBundle.z.object({}),
|
|
138
|
+
type: "action"
|
|
139
|
+
},
|
|
140
|
+
handle: async (context, params, response) => {
|
|
141
|
+
const browserContext = await context.ensureBrowserContext();
|
|
142
|
+
await browserContext.clearCookies();
|
|
143
|
+
response.addCode(`await page.context().clearCookies();`);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
var cookies_default = [
|
|
147
|
+
cookieList,
|
|
148
|
+
cookieGet,
|
|
149
|
+
cookieSet,
|
|
150
|
+
cookieDelete,
|
|
151
|
+
cookieClear
|
|
152
|
+
];
|
|
@@ -62,6 +62,7 @@ const install = (0, import_tool.defineTool)({
|
|
|
62
62
|
reject(new Error(`Failed to install browser: ${output.join("")}`));
|
|
63
63
|
});
|
|
64
64
|
});
|
|
65
|
+
response.addTextResult(`Browser ${channel} installed.`);
|
|
65
66
|
const tabHeaders = await Promise.all(context.tabs().map((tab) => tab.headerSnapshot()));
|
|
66
67
|
const result = (0, import_response.renderTabsMarkdown)(tabHeaders);
|
|
67
68
|
response.addTextResult(result.join("\n"));
|
|
@@ -18,7 +18,9 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var network_exports = {};
|
|
20
20
|
__export(network_exports, {
|
|
21
|
-
default: () => network_default
|
|
21
|
+
default: () => network_default,
|
|
22
|
+
isFetch: () => isFetch,
|
|
23
|
+
renderRequest: () => renderRequest
|
|
22
24
|
});
|
|
23
25
|
module.exports = __toCommonJS(network_exports);
|
|
24
26
|
var import_mcpBundle = require("../../../mcpBundle");
|
|
@@ -39,11 +41,11 @@ const requests = (0, import_tool.defineTabTool)({
|
|
|
39
41
|
const requests2 = await tab.requests();
|
|
40
42
|
const text = [];
|
|
41
43
|
for (const request of requests2) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
if (!params.includeStatic && !isFetch(request) && isSuccessfulResponse(request))
|
|
45
|
+
continue;
|
|
46
|
+
text.push(await renderRequest(request));
|
|
45
47
|
}
|
|
46
|
-
response.addResult("Network", text.join("\n"), { prefix: "network", ext: "log", suggestedFilename: params.filename });
|
|
48
|
+
await response.addResult("Network", text.join("\n"), { prefix: "network", ext: "log", suggestedFilename: params.filename });
|
|
47
49
|
}
|
|
48
50
|
});
|
|
49
51
|
const networkClear = (0, import_tool.defineTabTool)({
|
|
@@ -60,19 +62,31 @@ const networkClear = (0, import_tool.defineTabTool)({
|
|
|
60
62
|
await tab.clearRequests();
|
|
61
63
|
}
|
|
62
64
|
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
function isSuccessfulResponse(request) {
|
|
66
|
+
if (request.failure())
|
|
67
|
+
return false;
|
|
68
|
+
const response = request.existingResponse();
|
|
69
|
+
return !!response && response.status() < 400;
|
|
70
|
+
}
|
|
71
|
+
function isFetch(request) {
|
|
72
|
+
return ["fetch", "xhr"].includes(request.resourceType());
|
|
73
|
+
}
|
|
74
|
+
async function renderRequest(request) {
|
|
75
|
+
const response = request.existingResponse();
|
|
69
76
|
const result = [];
|
|
70
77
|
result.push(`[${request.method().toUpperCase()}] ${request.url()}`);
|
|
71
78
|
if (response)
|
|
72
79
|
result.push(`=> [${response.status()}] ${response.statusText()}`);
|
|
80
|
+
else if (request.failure())
|
|
81
|
+
result.push(`=> [FAILED] ${request.failure()?.errorText ?? "Unknown error"}`);
|
|
73
82
|
return result.join(" ");
|
|
74
83
|
}
|
|
75
84
|
var network_default = [
|
|
76
85
|
requests,
|
|
77
86
|
networkClear
|
|
78
87
|
];
|
|
88
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
89
|
+
0 && (module.exports = {
|
|
90
|
+
isFetch,
|
|
91
|
+
renderRequest
|
|
92
|
+
});
|
|
@@ -24,7 +24,6 @@ module.exports = __toCommonJS(pdf_exports);
|
|
|
24
24
|
var import_mcpBundle = require("../../../mcpBundle");
|
|
25
25
|
var import_utils = require("playwright-core/lib/utils");
|
|
26
26
|
var import_tool = require("./tool");
|
|
27
|
-
var import_utils2 = require("./utils");
|
|
28
27
|
const pdfSchema = import_mcpBundle.z.object({
|
|
29
28
|
filename: import_mcpBundle.z.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified. Prefer relative file names to stay within the output directory.")
|
|
30
29
|
});
|
|
@@ -39,9 +38,9 @@ const pdf = (0, import_tool.defineTabTool)({
|
|
|
39
38
|
},
|
|
40
39
|
handle: async (tab, params, response) => {
|
|
41
40
|
const data = await tab.page.pdf();
|
|
42
|
-
const
|
|
43
|
-
response.
|
|
44
|
-
response.addCode(`await page.pdf(${(0, import_utils.formatObject)({ path:
|
|
41
|
+
const result = await response.resolveClientFile({ prefix: "page", ext: "pdf", suggestedFilename: params.filename }, "Page as pdf");
|
|
42
|
+
await response.addFileResult(result, data);
|
|
43
|
+
response.addCode(`await page.pdf(${(0, import_utils.formatObject)({ path: result.relativeName })});`);
|
|
45
44
|
}
|
|
46
45
|
});
|
|
47
46
|
var pdf_default = [
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var route_exports = {};
|
|
20
|
+
__export(route_exports, {
|
|
21
|
+
default: () => route_default
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(route_exports);
|
|
24
|
+
var import_mcpBundle = require("../../../mcpBundle");
|
|
25
|
+
var import_tool = require("./tool");
|
|
26
|
+
const route = (0, import_tool.defineTool)({
|
|
27
|
+
capability: "network",
|
|
28
|
+
schema: {
|
|
29
|
+
name: "browser_route",
|
|
30
|
+
title: "Mock network requests",
|
|
31
|
+
description: "Set up a route to mock network requests matching a URL pattern",
|
|
32
|
+
inputSchema: import_mcpBundle.z.object({
|
|
33
|
+
pattern: import_mcpBundle.z.string().describe('URL pattern to match (e.g., "**/api/users", "**/*.{png,jpg}")'),
|
|
34
|
+
status: import_mcpBundle.z.number().optional().describe("HTTP status code to return (default: 200)"),
|
|
35
|
+
body: import_mcpBundle.z.string().optional().describe("Response body (text or JSON string)"),
|
|
36
|
+
contentType: import_mcpBundle.z.string().optional().describe('Content-Type header (e.g., "application/json", "text/html")'),
|
|
37
|
+
headers: import_mcpBundle.z.array(import_mcpBundle.z.string()).optional().describe('Headers to add in "Name: Value" format'),
|
|
38
|
+
removeHeaders: import_mcpBundle.z.string().optional().describe("Comma-separated list of header names to remove from request")
|
|
39
|
+
}),
|
|
40
|
+
type: "action"
|
|
41
|
+
},
|
|
42
|
+
handle: async (context, params, response) => {
|
|
43
|
+
const addHeaders = params.headers ? Object.fromEntries(params.headers.map((h) => {
|
|
44
|
+
const colonIndex = h.indexOf(":");
|
|
45
|
+
return [h.substring(0, colonIndex).trim(), h.substring(colonIndex + 1).trim()];
|
|
46
|
+
})) : void 0;
|
|
47
|
+
const removeHeaders = params.removeHeaders ? params.removeHeaders.split(",").map((h) => h.trim()) : void 0;
|
|
48
|
+
const handler = async (route2) => {
|
|
49
|
+
if (params.body !== void 0 || params.status !== void 0) {
|
|
50
|
+
await route2.fulfill({
|
|
51
|
+
status: params.status ?? 200,
|
|
52
|
+
contentType: params.contentType,
|
|
53
|
+
body: params.body
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const headers = { ...route2.request().headers() };
|
|
58
|
+
if (addHeaders) {
|
|
59
|
+
for (const [key, value] of Object.entries(addHeaders))
|
|
60
|
+
headers[key] = value;
|
|
61
|
+
}
|
|
62
|
+
if (removeHeaders) {
|
|
63
|
+
for (const header of removeHeaders)
|
|
64
|
+
delete headers[header.toLowerCase()];
|
|
65
|
+
}
|
|
66
|
+
await route2.continue({ headers });
|
|
67
|
+
};
|
|
68
|
+
const entry = {
|
|
69
|
+
pattern: params.pattern,
|
|
70
|
+
status: params.status,
|
|
71
|
+
body: params.body,
|
|
72
|
+
contentType: params.contentType,
|
|
73
|
+
addHeaders,
|
|
74
|
+
removeHeaders,
|
|
75
|
+
handler
|
|
76
|
+
};
|
|
77
|
+
await context.addRoute(entry);
|
|
78
|
+
response.addTextResult(`Route added for pattern: ${params.pattern}`);
|
|
79
|
+
response.addCode(`await page.context().route('${params.pattern}', async route => { /* route handler */ });`);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
const routeList = (0, import_tool.defineTool)({
|
|
83
|
+
capability: "network",
|
|
84
|
+
schema: {
|
|
85
|
+
name: "browser_route_list",
|
|
86
|
+
title: "List network routes",
|
|
87
|
+
description: "List all active network routes",
|
|
88
|
+
inputSchema: import_mcpBundle.z.object({}),
|
|
89
|
+
type: "readOnly"
|
|
90
|
+
},
|
|
91
|
+
handle: async (context, params, response) => {
|
|
92
|
+
const routes = context.routes();
|
|
93
|
+
if (routes.length === 0) {
|
|
94
|
+
response.addTextResult("No active routes");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const lines = [];
|
|
98
|
+
for (let i = 0; i < routes.length; i++) {
|
|
99
|
+
const route2 = routes[i];
|
|
100
|
+
const details = [];
|
|
101
|
+
if (route2.status !== void 0)
|
|
102
|
+
details.push(`status=${route2.status}`);
|
|
103
|
+
if (route2.body !== void 0)
|
|
104
|
+
details.push(`body=${route2.body.length > 50 ? route2.body.substring(0, 50) + "..." : route2.body}`);
|
|
105
|
+
if (route2.contentType)
|
|
106
|
+
details.push(`contentType=${route2.contentType}`);
|
|
107
|
+
if (route2.addHeaders)
|
|
108
|
+
details.push(`addHeaders=${JSON.stringify(route2.addHeaders)}`);
|
|
109
|
+
if (route2.removeHeaders)
|
|
110
|
+
details.push(`removeHeaders=${route2.removeHeaders.join(",")}`);
|
|
111
|
+
const detailsStr = details.length ? ` (${details.join(", ")})` : "";
|
|
112
|
+
lines.push(`${i + 1}. ${route2.pattern}${detailsStr}`);
|
|
113
|
+
}
|
|
114
|
+
response.addTextResult(lines.join("\n"));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
const unroute = (0, import_tool.defineTool)({
|
|
118
|
+
capability: "network",
|
|
119
|
+
schema: {
|
|
120
|
+
name: "browser_unroute",
|
|
121
|
+
title: "Remove network routes",
|
|
122
|
+
description: "Remove network routes matching a pattern (or all routes if no pattern specified)",
|
|
123
|
+
inputSchema: import_mcpBundle.z.object({
|
|
124
|
+
pattern: import_mcpBundle.z.string().optional().describe("URL pattern to unroute (omit to remove all routes)")
|
|
125
|
+
}),
|
|
126
|
+
type: "action"
|
|
127
|
+
},
|
|
128
|
+
handle: async (context, params, response) => {
|
|
129
|
+
const removed = await context.removeRoute(params.pattern);
|
|
130
|
+
if (params.pattern)
|
|
131
|
+
response.addTextResult(`Removed ${removed} route(s) for pattern: ${params.pattern}`);
|
|
132
|
+
else
|
|
133
|
+
response.addTextResult(`Removed all ${removed} route(s)`);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
var route_default = [
|
|
137
|
+
route,
|
|
138
|
+
routeList,
|
|
139
|
+
unroute
|
|
140
|
+
];
|
|
@@ -35,7 +35,6 @@ var import_vm = __toESM(require("vm"));
|
|
|
35
35
|
var import_utils = require("playwright-core/lib/utils");
|
|
36
36
|
var import_mcpBundle = require("../../../mcpBundle");
|
|
37
37
|
var import_ai_runner_fake = require("@cotestdev/ai-runner-fake");
|
|
38
|
-
var import_test = require("@playwright/test");
|
|
39
38
|
var import_tool = require("./tool");
|
|
40
39
|
var import_schema = require("./schema");
|
|
41
40
|
const codeSchema = import_schema.baseSchema.extend({
|
|
@@ -92,7 +91,6 @@ const runScript = (0, import_tool.defineTabTool)({
|
|
|
92
91
|
handle: async (tab, params, response) => {
|
|
93
92
|
response.setIncludeSnapshot();
|
|
94
93
|
const runner = import_ai_runner_fake.Runner.NewInstance(params.projectId, params.testId);
|
|
95
|
-
runner.setExternalContext({ "expect": import_test.expect });
|
|
96
94
|
await runner.init(tab.page, tab.page.context(), params.params);
|
|
97
95
|
const result = await runner.runScript(params.testId, params.code);
|
|
98
96
|
const code = `// Returns the out parameters of the reusable test
|
|
@@ -27,7 +27,6 @@ var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
|
27
27
|
var import_utils2 = require("playwright-core/lib/utils");
|
|
28
28
|
var import_mcpBundle = require("../../../mcpBundle");
|
|
29
29
|
var import_tool = require("./tool");
|
|
30
|
-
var import_utils3 = require("./utils");
|
|
31
30
|
var import_schema = require("./schema");
|
|
32
31
|
const screenshotSchema = import_schema.baseSchema.extend({
|
|
33
32
|
type: import_mcpBundle.z.enum(["png", "jpeg"]).default("png").describe("Image format for the screenshot. Default is png."),
|
|
@@ -58,14 +57,14 @@ const screenshot = (0, import_tool.defineTabTool)({
|
|
|
58
57
|
const screenshotTarget = params.ref ? params.element || "element" : params.fullPage ? "full page" : "viewport";
|
|
59
58
|
const ref = params.ref ? await tab.refLocator({ element: params.element || "", ref: params.ref }) : null;
|
|
60
59
|
const data = ref ? await ref.locator.screenshot(options) : await tab.page.screenshot(options);
|
|
61
|
-
const
|
|
62
|
-
response.addCode(`// Screenshot ${screenshotTarget} and save it as ${
|
|
60
|
+
const resolvedFile = await response.resolveClientFile({ prefix: ref ? "element" : "page", ext: fileType, suggestedFilename: params.filename }, `Screenshot of ${screenshotTarget}`);
|
|
61
|
+
response.addCode(`// Screenshot ${screenshotTarget} and save it as ${resolvedFile.relativeName}`);
|
|
63
62
|
if (ref)
|
|
64
|
-
response.addCode(`await page.${ref.resolved}.screenshot(${(0, import_utils2.formatObject)({ ...options, path:
|
|
63
|
+
response.addCode(`await page.${ref.resolved}.screenshot(${(0, import_utils2.formatObject)({ ...options, path: resolvedFile.relativeName })});`);
|
|
65
64
|
else
|
|
66
|
-
response.addCode(`await page.screenshot(${(0, import_utils2.formatObject)({ ...options, path:
|
|
67
|
-
|
|
68
|
-
response.
|
|
65
|
+
response.addCode(`await page.screenshot(${(0, import_utils2.formatObject)({ ...options, path: resolvedFile.relativeName })});`);
|
|
66
|
+
await response.addFileResult(resolvedFile, data);
|
|
67
|
+
await response.registerImageResult(data, fileType);
|
|
69
68
|
}
|
|
70
69
|
});
|
|
71
70
|
function scaleImageToFitMessage(buffer, imageType) {
|
|
@@ -23,7 +23,6 @@ __export(storage_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(storage_exports);
|
|
24
24
|
var import_mcpBundle = require("../../../mcpBundle");
|
|
25
25
|
var import_tool = require("./tool");
|
|
26
|
-
var import_utils = require("./utils");
|
|
27
26
|
const storageState = (0, import_tool.defineTool)({
|
|
28
27
|
capability: "storage",
|
|
29
28
|
schema: {
|
|
@@ -38,10 +37,10 @@ const storageState = (0, import_tool.defineTool)({
|
|
|
38
37
|
handle: async (context, params, response) => {
|
|
39
38
|
const browserContext = await context.ensureBrowserContext();
|
|
40
39
|
const state = await browserContext.storageState();
|
|
41
|
-
const suggestedFilename = params.filename || (0, import_utils.dateAsFileName)("storage-state", "json");
|
|
42
40
|
const serializedState = JSON.stringify(state, null, 2);
|
|
43
|
-
response.
|
|
44
|
-
response.addCode(`await page.context().storageState({ path: '${
|
|
41
|
+
const resolvedFile = await response.resolveClientFile({ prefix: "storage-state", ext: "json", suggestedFilename: params.filename }, "Storage state");
|
|
42
|
+
response.addCode(`await page.context().storageState({ path: '${resolvedFile.relativeName}' });`);
|
|
43
|
+
await response.addFileResult(resolvedFile, serializedState);
|
|
45
44
|
}
|
|
46
45
|
});
|
|
47
46
|
const setStorageState = (0, import_tool.defineTool)({
|
|
@@ -34,7 +34,7 @@ const tracingStart = (0, import_tool.defineTool)({
|
|
|
34
34
|
},
|
|
35
35
|
handle: async (context, params, response) => {
|
|
36
36
|
const browserContext = await context.ensureBrowserContext();
|
|
37
|
-
const tracesDir = await context.outputFile(`traces`,
|
|
37
|
+
const tracesDir = await context.outputFile({ prefix: "", suggestedFilename: `traces`, ext: "" }, { origin: "code" });
|
|
38
38
|
const name = "trace-" + Date.now();
|
|
39
39
|
await browserContext.tracing.start({
|
|
40
40
|
name,
|
|
@@ -42,12 +42,11 @@ const tracingStart = (0, import_tool.defineTool)({
|
|
|
42
42
|
snapshots: true,
|
|
43
43
|
_live: true
|
|
44
44
|
});
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
response.
|
|
49
|
-
|
|
50
|
-
browserContext.tracing[traceLegendSymbol] = traceLegend;
|
|
45
|
+
response.addTextResult(`Trace recording started`);
|
|
46
|
+
response.addFileLink("Action log", `${tracesDir}/${name}.trace`);
|
|
47
|
+
response.addFileLink("Network log", `${tracesDir}/${name}.network`);
|
|
48
|
+
response.addFileLink("Resources", `${tracesDir}/resources`);
|
|
49
|
+
browserContext.tracing[traceLegendSymbol] = { tracesDir, name };
|
|
51
50
|
}
|
|
52
51
|
});
|
|
53
52
|
const tracingStop = (0, import_tool.defineTool)({
|
|
@@ -63,8 +62,10 @@ const tracingStop = (0, import_tool.defineTool)({
|
|
|
63
62
|
const browserContext = await context.ensureBrowserContext();
|
|
64
63
|
await browserContext.tracing.stop();
|
|
65
64
|
const traceLegend = browserContext.tracing[traceLegendSymbol];
|
|
66
|
-
response.addTextResult(`
|
|
67
|
-
|
|
65
|
+
response.addTextResult(`Trace recording stopped.`);
|
|
66
|
+
response.addFileLink("Trace", `${traceLegend.tracesDir}/${traceLegend.name}.trace`);
|
|
67
|
+
response.addFileLink("Network log", `${traceLegend.tracesDir}/${traceLegend.name}.network`);
|
|
68
|
+
response.addFileLink("Resources", `${traceLegend.tracesDir}/resources`);
|
|
68
69
|
}
|
|
69
70
|
});
|
|
70
71
|
var tracing_default = [
|
|
@@ -19,7 +19,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var utils_exports = {};
|
|
20
20
|
__export(utils_exports, {
|
|
21
21
|
callOnPageNoTrace: () => callOnPageNoTrace,
|
|
22
|
-
dateAsFileName: () => dateAsFileName,
|
|
23
22
|
eventWaiter: () => eventWaiter,
|
|
24
23
|
waitForCompletion: () => waitForCompletion
|
|
25
24
|
});
|
|
@@ -62,10 +61,6 @@ async function waitForCompletion(tab, callback) {
|
|
|
62
61
|
async function callOnPageNoTrace(page, callback) {
|
|
63
62
|
return await page._wrapApiCall(() => callback(page), { internal: true });
|
|
64
63
|
}
|
|
65
|
-
function dateAsFileName(prefix, extension) {
|
|
66
|
-
const date = /* @__PURE__ */ new Date();
|
|
67
|
-
return `${prefix}-${date.toISOString().replace(/[:.]/g, "-")}.${extension}`;
|
|
68
|
-
}
|
|
69
64
|
function eventWaiter(page, event, timeout) {
|
|
70
65
|
const disposables = [];
|
|
71
66
|
const eventPromise = new Promise((resolve, reject) => {
|
|
@@ -88,7 +83,6 @@ function eventWaiter(page, event, timeout) {
|
|
|
88
83
|
// Annotate the CommonJS export names for ESM import in node:
|
|
89
84
|
0 && (module.exports = {
|
|
90
85
|
callOnPageNoTrace,
|
|
91
|
-
dateAsFileName,
|
|
92
86
|
eventWaiter,
|
|
93
87
|
waitForCompletion
|
|
94
88
|
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,16 +17,24 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
var video_exports = {};
|
|
20
30
|
__export(video_exports, {
|
|
21
31
|
default: () => video_default
|
|
22
32
|
});
|
|
23
33
|
module.exports = __toCommonJS(video_exports);
|
|
34
|
+
var import_path = __toESM(require("path"));
|
|
24
35
|
var import_mcpBundle = require("../../../mcpBundle");
|
|
25
36
|
var import_tool = require("./tool");
|
|
26
|
-
|
|
27
|
-
const startVideo = (0, import_tool.defineTabTool)({
|
|
37
|
+
const startVideo = (0, import_tool.defineTool)({
|
|
28
38
|
capability: "devtools",
|
|
29
39
|
schema: {
|
|
30
40
|
name: "browser_start_video",
|
|
@@ -38,12 +48,12 @@ const startVideo = (0, import_tool.defineTabTool)({
|
|
|
38
48
|
}),
|
|
39
49
|
type: "readOnly"
|
|
40
50
|
},
|
|
41
|
-
handle: async (
|
|
42
|
-
await
|
|
51
|
+
handle: async (context, params, response) => {
|
|
52
|
+
await context.startVideoRecording({ size: params.size });
|
|
43
53
|
response.addTextResult("Video recording started.");
|
|
44
54
|
}
|
|
45
55
|
});
|
|
46
|
-
const stopVideo = (0, import_tool.
|
|
56
|
+
const stopVideo = (0, import_tool.defineTool)({
|
|
47
57
|
capability: "devtools",
|
|
48
58
|
schema: {
|
|
49
59
|
name: "browser_stop_video",
|
|
@@ -54,15 +64,23 @@ const stopVideo = (0, import_tool.defineTabTool)({
|
|
|
54
64
|
}),
|
|
55
65
|
type: "readOnly"
|
|
56
66
|
},
|
|
57
|
-
handle: async (
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
handle: async (context, params, response) => {
|
|
68
|
+
const videos = await context.stopVideoRecording();
|
|
69
|
+
if (!videos.size) {
|
|
70
|
+
response.addTextResult("No videos were recorded.");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
for (const [index, video] of [...videos].entries()) {
|
|
74
|
+
const suffix = index ? `-${index}` : "";
|
|
75
|
+
let suggestedFilename = params.filename;
|
|
76
|
+
if (suggestedFilename && suffix) {
|
|
77
|
+
const ext = import_path.default.extname(suggestedFilename);
|
|
78
|
+
suggestedFilename = import_path.default.basename(suggestedFilename, ext) + suffix + ext;
|
|
79
|
+
}
|
|
80
|
+
const resolvedFile = await response.resolveClientFile({ prefix: "video" + suffix, ext: "webm", suggestedFilename }, "Video");
|
|
81
|
+
await video.saveAs(resolvedFile.fileName);
|
|
82
|
+
await response.addFileResult(resolvedFile, null);
|
|
62
83
|
}
|
|
63
|
-
await tab.page.video().stop({ path: videoPath });
|
|
64
|
-
const tmpPath = await tab.page.video().path();
|
|
65
|
-
response.addTextResult(`Video recording stopped: ${videoPath ?? tmpPath}`);
|
|
66
84
|
}
|
|
67
85
|
});
|
|
68
86
|
var video_default = [
|