@wordbricks/playwright-mcp 0.1.20 → 0.1.22
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/cli-wrapper.js +15 -14
- package/cli.js +1 -1
- package/config.d.ts +11 -6
- package/index.d.ts +7 -5
- package/index.js +1 -1
- package/lib/browserContextFactory.js +131 -58
- package/lib/browserServerBackend.js +14 -12
- package/lib/config.js +60 -46
- package/lib/context.js +41 -39
- package/lib/extension/cdpRelay.js +67 -61
- package/lib/extension/extensionContextFactory.js +10 -10
- package/lib/frameworkPatterns.js +21 -21
- package/lib/hooks/antiBotDetectionHook.js +59 -52
- package/lib/hooks/core.js +11 -10
- package/lib/hooks/eventConsumer.js +21 -21
- package/lib/hooks/events.js +3 -3
- package/lib/hooks/formatToolCallEvent.js +3 -7
- package/lib/hooks/frameworkStateHook.js +40 -40
- package/lib/hooks/grouping.js +3 -3
- package/lib/hooks/jsonLdDetectionHook.js +44 -37
- package/lib/hooks/networkFilters.js +17 -17
- package/lib/hooks/networkSetup.js +9 -7
- package/lib/hooks/networkTrackingHook.js +21 -21
- package/lib/hooks/pageHeightHook.js +9 -9
- package/lib/hooks/registry.js +15 -16
- package/lib/hooks/requireTabHook.js +3 -3
- package/lib/hooks/schema.js +38 -38
- package/lib/hooks/waitHook.js +7 -7
- package/lib/index.js +12 -10
- package/lib/mcp/inProcessTransport.js +3 -4
- package/lib/mcp/proxyBackend.js +43 -28
- package/lib/mcp/server.js +24 -19
- package/lib/mcp/tool.js +14 -8
- package/lib/mcp/transport.js +60 -53
- package/lib/playwrightTransformer.js +129 -106
- package/lib/program.js +54 -52
- package/lib/response.js +36 -30
- package/lib/sessionLog.js +19 -17
- package/lib/tab.js +41 -39
- package/lib/tools/common.js +19 -19
- package/lib/tools/console.js +11 -11
- package/lib/tools/dialogs.js +18 -15
- package/lib/tools/evaluate.js +26 -17
- package/lib/tools/extractFrameworkState.js +48 -37
- package/lib/tools/files.js +17 -14
- package/lib/tools/form.js +32 -23
- package/lib/tools/getSnapshot.js +14 -15
- package/lib/tools/getVisibleHtml.js +33 -17
- package/lib/tools/install.js +20 -20
- package/lib/tools/keyboard.js +29 -24
- package/lib/tools/mouse.js +29 -31
- package/lib/tools/navigate.js +19 -23
- package/lib/tools/network.js +12 -14
- package/lib/tools/networkDetail.js +58 -49
- package/lib/tools/networkSearch/bodySearch.js +46 -32
- package/lib/tools/networkSearch/grouping.js +15 -6
- package/lib/tools/networkSearch/helpers.js +4 -4
- package/lib/tools/networkSearch/searchHtml.js +25 -16
- package/lib/tools/networkSearch/urlSearch.js +56 -14
- package/lib/tools/networkSearch.js +46 -36
- package/lib/tools/pdf.js +13 -12
- package/lib/tools/repl.js +66 -54
- package/lib/tools/screenshot.js +57 -33
- package/lib/tools/scroll.js +29 -24
- package/lib/tools/snapshot.js +66 -49
- package/lib/tools/tabs.js +22 -19
- package/lib/tools/tool.js +5 -3
- package/lib/tools/utils.js +17 -13
- package/lib/tools/wait.js +24 -19
- package/lib/tools.js +21 -20
- package/lib/utils/adBlockFilter.js +29 -26
- package/lib/utils/codegen.js +20 -16
- package/lib/utils/extensionPath.js +4 -4
- package/lib/utils/fileUtils.js +17 -13
- package/lib/utils/graphql.js +69 -58
- package/lib/utils/guid.js +3 -3
- package/lib/utils/httpServer.js +9 -9
- package/lib/utils/log.js +3 -3
- package/lib/utils/manualPromise.js +7 -7
- package/lib/utils/networkFormat.js +7 -5
- package/lib/utils/package.js +4 -4
- package/lib/utils/sanitizeHtml.js +66 -34
- package/lib/utils/truncate.js +25 -25
- package/lib/utils/withTimeout.js +1 -1
- package/package.json +34 -57
- package/src/index.ts +27 -17
- package/LICENSE +0 -202
package/lib/tools/evaluate.js
CHANGED
|
@@ -13,29 +13,40 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { z } from
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import { generateLocator } from
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import * as javascript from "../utils/codegen.js";
|
|
18
|
+
import { defineTabTool } from "./tool.js";
|
|
19
|
+
import { generateLocator } from "./utils.js";
|
|
20
20
|
const evaluateSchema = z.object({
|
|
21
|
-
function: z
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
function: z
|
|
22
|
+
.string()
|
|
23
|
+
.describe("() => { /* code */ } or (element) => { /* code */ } when element is provided"),
|
|
24
|
+
element: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Human-readable element description used to obtain permission to interact with the element"),
|
|
28
|
+
ref: z
|
|
29
|
+
.string()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("Exact target element reference from the page snapshot"),
|
|
24
32
|
});
|
|
25
33
|
const evaluate = defineTabTool({
|
|
26
|
-
capability:
|
|
34
|
+
capability: "core",
|
|
27
35
|
schema: {
|
|
28
|
-
name:
|
|
29
|
-
title:
|
|
30
|
-
description:
|
|
36
|
+
name: "browser_evaluate",
|
|
37
|
+
title: "Evaluate JavaScript",
|
|
38
|
+
description: "Evaluate JavaScript expression on page or element",
|
|
31
39
|
inputSchema: evaluateSchema,
|
|
32
|
-
type:
|
|
40
|
+
type: "action",
|
|
33
41
|
},
|
|
34
42
|
handle: async (tab, params, response) => {
|
|
35
43
|
response.setIncludeSnapshot();
|
|
36
44
|
let locator;
|
|
37
45
|
if (params.ref && params.element) {
|
|
38
|
-
locator = await tab.refLocator({
|
|
46
|
+
locator = await tab.refLocator({
|
|
47
|
+
ref: params.ref,
|
|
48
|
+
element: params.element,
|
|
49
|
+
});
|
|
39
50
|
response.addCode(`await page.${await generateLocator(locator)}.evaluate(${javascript.quote(params.function)});`);
|
|
40
51
|
}
|
|
41
52
|
else {
|
|
@@ -44,10 +55,8 @@ const evaluate = defineTabTool({
|
|
|
44
55
|
await tab.waitForCompletion(async () => {
|
|
45
56
|
const receiver = locator ?? tab.page;
|
|
46
57
|
const result = await receiver._evaluateFunction(params.function);
|
|
47
|
-
response.addResult(JSON.stringify(result, null, 2) ||
|
|
58
|
+
response.addResult(JSON.stringify(result, null, 2) || "undefined");
|
|
48
59
|
});
|
|
49
60
|
},
|
|
50
61
|
});
|
|
51
|
-
export default [
|
|
52
|
-
evaluate,
|
|
53
|
-
];
|
|
62
|
+
export default [evaluate];
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { FRAMEWORK_STATE_PATTERNS, MAX_DISPLAY_ITEMS, } from "../frameworkPatterns.js";
|
|
3
|
+
import { truncate } from "../utils/truncate.js";
|
|
4
|
+
import { defineTabTool } from "./tool.js";
|
|
5
5
|
const extractFrameworkStateSchema = z.object({
|
|
6
|
-
framework: z
|
|
7
|
-
|
|
6
|
+
framework: z
|
|
7
|
+
.string()
|
|
8
|
+
.optional()
|
|
9
|
+
.describe('Optional: specific key to extract from (e.g., "__remixContext")'),
|
|
10
|
+
path: z
|
|
11
|
+
.string()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Path to extract (e.g., "state.loaderData")'),
|
|
8
14
|
});
|
|
9
15
|
const extractFrameworkState = defineTabTool({
|
|
10
|
-
capability:
|
|
16
|
+
capability: "core",
|
|
11
17
|
schema: {
|
|
12
|
-
name:
|
|
13
|
-
title:
|
|
14
|
-
description:
|
|
18
|
+
name: "browser_extract_framework_state",
|
|
19
|
+
title: "Extract framework state",
|
|
20
|
+
description: "Extract framework state data from web pages (React, Redux, Remix, etc.)",
|
|
15
21
|
inputSchema: extractFrameworkStateSchema,
|
|
16
|
-
type:
|
|
22
|
+
type: "readOnly",
|
|
17
23
|
},
|
|
18
24
|
handle: async (tab, params, response) => {
|
|
19
25
|
const { framework, // Optional: specific key to extract from
|
|
@@ -30,7 +36,7 @@ const extractFrameworkState = defineTabTool({
|
|
|
30
36
|
try {
|
|
31
37
|
let data = window[key];
|
|
32
38
|
// If data is string, try to parse as JSON
|
|
33
|
-
if (typeof data ===
|
|
39
|
+
if (typeof data === "string") {
|
|
34
40
|
try {
|
|
35
41
|
data = JSON.parse(data);
|
|
36
42
|
}
|
|
@@ -43,14 +49,14 @@ const extractFrameworkState = defineTabTool({
|
|
|
43
49
|
// Split path while respecting bracket-quoted segments like ['a.b']
|
|
44
50
|
const splitPath = (p) => {
|
|
45
51
|
const parts = [];
|
|
46
|
-
let cur =
|
|
52
|
+
let cur = "";
|
|
47
53
|
let inBracket = false;
|
|
48
54
|
let inQuote = null;
|
|
49
55
|
for (let i = 0; i < p.length; i++) {
|
|
50
56
|
const ch = p[i];
|
|
51
57
|
if (inQuote) {
|
|
52
58
|
cur += ch;
|
|
53
|
-
if (ch === inQuote && p[i - 1] !==
|
|
59
|
+
if (ch === inQuote && p[i - 1] !== "\\")
|
|
54
60
|
inQuote = null;
|
|
55
61
|
continue;
|
|
56
62
|
}
|
|
@@ -59,26 +65,26 @@ const extractFrameworkState = defineTabTool({
|
|
|
59
65
|
if (ch === '"' || ch === "'") {
|
|
60
66
|
inQuote = ch;
|
|
61
67
|
}
|
|
62
|
-
else if (ch ===
|
|
68
|
+
else if (ch === "]") {
|
|
63
69
|
inBracket = false;
|
|
64
70
|
parts.push(cur);
|
|
65
|
-
cur =
|
|
71
|
+
cur = "";
|
|
66
72
|
}
|
|
67
73
|
continue;
|
|
68
74
|
}
|
|
69
|
-
if (ch ===
|
|
75
|
+
if (ch === ".") {
|
|
70
76
|
if (cur) {
|
|
71
77
|
parts.push(cur);
|
|
72
|
-
cur =
|
|
78
|
+
cur = "";
|
|
73
79
|
}
|
|
74
80
|
continue;
|
|
75
81
|
}
|
|
76
|
-
if (ch ===
|
|
82
|
+
if (ch === "[") {
|
|
77
83
|
if (cur) {
|
|
78
84
|
parts.push(cur);
|
|
79
|
-
cur =
|
|
85
|
+
cur = "";
|
|
80
86
|
}
|
|
81
|
-
cur =
|
|
87
|
+
cur = "[";
|
|
82
88
|
inBracket = true;
|
|
83
89
|
continue;
|
|
84
90
|
}
|
|
@@ -90,20 +96,22 @@ const extractFrameworkState = defineTabTool({
|
|
|
90
96
|
};
|
|
91
97
|
const pathParts = splitPath(targetPath);
|
|
92
98
|
for (const part of pathParts) {
|
|
93
|
-
if (data && typeof data ===
|
|
99
|
+
if (data && typeof data === "object") {
|
|
94
100
|
// Handle array index with prop: prop[123]
|
|
95
101
|
const arrayMatch = part.match(/^(.+)\[(\d+)\]$/);
|
|
96
102
|
if (arrayMatch) {
|
|
97
103
|
const prop = arrayMatch[1];
|
|
98
|
-
const index = parseInt(arrayMatch[2], 10);
|
|
104
|
+
const index = Number.parseInt(arrayMatch[2], 10);
|
|
99
105
|
const propData = data[prop];
|
|
100
|
-
data = Array.isArray(propData)
|
|
106
|
+
data = Array.isArray(propData)
|
|
107
|
+
? propData[index]
|
|
108
|
+
: undefined;
|
|
101
109
|
}
|
|
102
110
|
else if (part.match(/^\[(\d+)\]$/)) {
|
|
103
111
|
// Handle standalone array index: [123]
|
|
104
112
|
const indexMatch = part.match(/^\[(\d+)\]$/);
|
|
105
113
|
if (indexMatch) {
|
|
106
|
-
const index = parseInt(indexMatch[1], 10);
|
|
114
|
+
const index = Number.parseInt(indexMatch[1], 10);
|
|
107
115
|
data = data?.[index];
|
|
108
116
|
}
|
|
109
117
|
}
|
|
@@ -151,14 +159,14 @@ const extractFrameworkState = defineTabTool({
|
|
|
151
159
|
throw new Error(`Path "${path}" not found in framework state '${framework}'.`);
|
|
152
160
|
}
|
|
153
161
|
throw new Error(`Key '${framework}' not found in window. ` +
|
|
154
|
-
|
|
162
|
+
"Available framework states can be detected by the frameworkStateHook.");
|
|
155
163
|
}
|
|
156
|
-
throw new Error(
|
|
157
|
-
|
|
164
|
+
throw new Error("No framework state found. " +
|
|
165
|
+
"Available framework states can be detected by the frameworkStateHook.");
|
|
158
166
|
}
|
|
159
167
|
// Prepare final data
|
|
160
168
|
let finalData;
|
|
161
|
-
const extractedKeys = result.states.map(s => s.key);
|
|
169
|
+
const extractedKeys = result.states.map((s) => s.key);
|
|
162
170
|
if (result.states.length === 1) {
|
|
163
171
|
finalData = result.states[0].data;
|
|
164
172
|
}
|
|
@@ -170,7 +178,7 @@ const extractFrameworkState = defineTabTool({
|
|
|
170
178
|
// Limit to MAX_DISPLAY_ITEMS for display if array
|
|
171
179
|
const totalItems = Array.isArray(finalData)
|
|
172
180
|
? finalData.length
|
|
173
|
-
: typeof finalData ===
|
|
181
|
+
: typeof finalData === "object" && finalData !== null
|
|
174
182
|
? Object.keys(finalData).length
|
|
175
183
|
: 1;
|
|
176
184
|
let displayData = finalData;
|
|
@@ -179,35 +187,38 @@ const extractFrameworkState = defineTabTool({
|
|
|
179
187
|
displayData = finalData.slice(0, MAX_DISPLAY_ITEMS);
|
|
180
188
|
isTruncated = true;
|
|
181
189
|
}
|
|
182
|
-
else if (typeof finalData ===
|
|
190
|
+
else if (typeof finalData === "object" &&
|
|
183
191
|
finalData !== null &&
|
|
184
192
|
!Array.isArray(finalData)) {
|
|
185
193
|
const keys = Object.keys(finalData);
|
|
186
194
|
if (keys.length > MAX_DISPLAY_ITEMS) {
|
|
187
195
|
displayData = {};
|
|
188
|
-
keys.slice(0, MAX_DISPLAY_ITEMS).forEach(key => {
|
|
196
|
+
keys.slice(0, MAX_DISPLAY_ITEMS).forEach((key) => {
|
|
189
197
|
displayData[key] = finalData[key];
|
|
190
198
|
});
|
|
191
199
|
isTruncated = true;
|
|
192
200
|
}
|
|
193
201
|
}
|
|
194
202
|
// Truncate for readability and cap output
|
|
195
|
-
const truncatedDisplay = truncate(displayData, {
|
|
203
|
+
const truncatedDisplay = truncate(displayData, {
|
|
204
|
+
maxStringLength: 1000,
|
|
205
|
+
maxItems: MAX_DISPLAY_ITEMS,
|
|
206
|
+
});
|
|
196
207
|
const outputStr = JSON.stringify(truncatedDisplay, null, 2);
|
|
197
208
|
const summary = [
|
|
198
209
|
`Found ${result.count} framework state(s)`,
|
|
199
210
|
result.states.length > 1
|
|
200
|
-
? `Extracted from: ${extractedKeys.join(
|
|
211
|
+
? `Extracted from: ${extractedKeys.join(", ")}`
|
|
201
212
|
: `Using: ${result.states[0]?.key}`,
|
|
202
213
|
isTruncated
|
|
203
214
|
? `Showing first ${MAX_DISPLAY_ITEMS} of ${totalItems} results`
|
|
204
215
|
: null,
|
|
205
216
|
path ? `Extracted path: ${path}` : null,
|
|
206
|
-
result.errors ? `Errors: ${result.errors.join(
|
|
217
|
+
result.errors ? `Errors: ${result.errors.join("; ")}` : null,
|
|
207
218
|
]
|
|
208
219
|
.filter(Boolean)
|
|
209
|
-
.join(
|
|
210
|
-
response.addResult(summary +
|
|
220
|
+
.join("\n");
|
|
221
|
+
response.addResult(summary + "\n\n" + outputStr);
|
|
211
222
|
response.setIncludeSnapshot();
|
|
212
223
|
},
|
|
213
224
|
});
|
package/lib/tools/files.js
CHANGED
|
@@ -13,24 +13,29 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { z } from
|
|
17
|
-
import { defineTabTool } from
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { defineTabTool } from "./tool.js";
|
|
18
18
|
export const uploadFile = defineTabTool({
|
|
19
|
-
capability:
|
|
19
|
+
capability: "core",
|
|
20
20
|
schema: {
|
|
21
|
-
name:
|
|
22
|
-
title:
|
|
23
|
-
description:
|
|
21
|
+
name: "browser_file_upload",
|
|
22
|
+
title: "Upload files",
|
|
23
|
+
description: "Upload one or multiple files",
|
|
24
24
|
inputSchema: z.object({
|
|
25
|
-
paths: z
|
|
25
|
+
paths: z
|
|
26
|
+
.array(z.string())
|
|
27
|
+
.optional()
|
|
28
|
+
.describe("The absolute paths to the files to upload. Can be single file or multiple files. If omitted, file chooser is cancelled."),
|
|
26
29
|
}),
|
|
27
|
-
type:
|
|
30
|
+
type: "action",
|
|
28
31
|
},
|
|
29
32
|
handle: async (tab, params, response) => {
|
|
30
33
|
response.setIncludeSnapshot();
|
|
31
|
-
const modalState = tab
|
|
34
|
+
const modalState = tab
|
|
35
|
+
.modalStates()
|
|
36
|
+
.find((state) => state.type === "fileChooser");
|
|
32
37
|
if (!modalState)
|
|
33
|
-
throw new Error(
|
|
38
|
+
throw new Error("No file chooser visible");
|
|
34
39
|
response.addCode(`await fileChooser.setFiles(${JSON.stringify(params.paths)})`);
|
|
35
40
|
tab.clearModalState(modalState);
|
|
36
41
|
await tab.waitForCompletion(async () => {
|
|
@@ -38,8 +43,6 @@ export const uploadFile = defineTabTool({
|
|
|
38
43
|
await modalState.fileChooser.setFiles(params.paths);
|
|
39
44
|
});
|
|
40
45
|
},
|
|
41
|
-
clearsModalState:
|
|
46
|
+
clearsModalState: "fileChooser",
|
|
42
47
|
});
|
|
43
|
-
export default [
|
|
44
|
-
uploadFile,
|
|
45
|
-
];
|
|
48
|
+
export default [uploadFile];
|
package/lib/tools/form.js
CHANGED
|
@@ -13,45 +13,54 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { z } from
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import { generateLocator } from
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import * as codegen from "../utils/codegen.js";
|
|
18
|
+
import { defineTabTool } from "./tool.js";
|
|
19
|
+
import { generateLocator } from "./utils.js";
|
|
20
20
|
const fillForm = defineTabTool({
|
|
21
|
-
capability:
|
|
21
|
+
capability: "core",
|
|
22
22
|
schema: {
|
|
23
|
-
name:
|
|
24
|
-
title:
|
|
25
|
-
description:
|
|
23
|
+
name: "browser_fill_form",
|
|
24
|
+
title: "Fill form",
|
|
25
|
+
description: "Fill multiple form fields",
|
|
26
26
|
inputSchema: z.object({
|
|
27
|
-
fields: z
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
fields: z
|
|
28
|
+
.array(z.object({
|
|
29
|
+
name: z.string().describe("Human-readable field name"),
|
|
30
|
+
type: z
|
|
31
|
+
.enum(["textbox", "checkbox", "radio", "combobox", "slider"])
|
|
32
|
+
.describe("Type of the field"),
|
|
33
|
+
ref: z
|
|
34
|
+
.string()
|
|
35
|
+
.describe("Exact target field reference from the page snapshot"),
|
|
36
|
+
value: z
|
|
37
|
+
.string()
|
|
38
|
+
.describe("Value to fill in the field. If the field is a checkbox, the value should be `true` or `false`. If the field is a combobox, the value should be the text of the option."),
|
|
39
|
+
}))
|
|
40
|
+
.describe("Fields to fill in"),
|
|
33
41
|
}),
|
|
34
|
-
type:
|
|
42
|
+
type: "input",
|
|
35
43
|
},
|
|
36
44
|
handle: async (tab, params, response) => {
|
|
37
45
|
for (const field of params.fields) {
|
|
38
|
-
const locator = await tab.refLocator({
|
|
39
|
-
|
|
46
|
+
const locator = await tab.refLocator({
|
|
47
|
+
element: field.name,
|
|
48
|
+
ref: field.ref,
|
|
49
|
+
});
|
|
50
|
+
if (field.type === "textbox" || field.type === "slider") {
|
|
40
51
|
const secret = tab.context.lookupSecret(field.value);
|
|
41
52
|
await locator.fill(secret.value);
|
|
42
53
|
response.addCode(`await page.${await generateLocator(locator)}.fill(${secret.code});`);
|
|
43
54
|
}
|
|
44
|
-
else if (field.type ===
|
|
45
|
-
await locator.setChecked(field.value ===
|
|
55
|
+
else if (field.type === "checkbox" || field.type === "radio") {
|
|
56
|
+
await locator.setChecked(field.value === "true");
|
|
46
57
|
response.addCode(`await page.${await generateLocator(locator)}.setChecked(${field.value});`);
|
|
47
58
|
}
|
|
48
|
-
else if (field.type ===
|
|
59
|
+
else if (field.type === "combobox") {
|
|
49
60
|
await locator.selectOption({ label: field.value });
|
|
50
61
|
response.addCode(`await page.${await generateLocator(locator)}.selectOption(${codegen.quote(field.value)});`);
|
|
51
62
|
}
|
|
52
63
|
}
|
|
53
64
|
},
|
|
54
65
|
});
|
|
55
|
-
export default [
|
|
56
|
-
fillForm,
|
|
57
|
-
];
|
|
66
|
+
export default [fillForm];
|
package/lib/tools/getSnapshot.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { truncate } from "../utils/truncate.js";
|
|
3
|
+
import { defineTool } from "./tool.js";
|
|
4
4
|
const MAX_LENGTH = 100_000;
|
|
5
5
|
const getSnapshotSchema = z.object({});
|
|
6
6
|
const getSnapshot = defineTool({
|
|
7
|
-
capability:
|
|
7
|
+
capability: "core",
|
|
8
8
|
schema: {
|
|
9
|
-
name:
|
|
10
|
-
title:
|
|
11
|
-
description:
|
|
12
|
-
|
|
9
|
+
name: "browser_get_snapshot",
|
|
10
|
+
title: "Get snapshot",
|
|
11
|
+
description: "Get accessibility snapshot of the current page",
|
|
12
|
+
inputSchema: getSnapshotSchema,
|
|
13
|
+
type: "readOnly",
|
|
13
14
|
},
|
|
14
15
|
handle: async (context, _params, response) => {
|
|
15
16
|
const tab = await context.ensureTab();
|
|
@@ -20,18 +21,16 @@ const getSnapshot = defineTool({
|
|
|
20
21
|
lines.push(`- Page URL: ${snapshot.url}`);
|
|
21
22
|
lines.push(`- Page Title: ${snapshot.title}`);
|
|
22
23
|
lines.push(`- Page Snapshot:`);
|
|
23
|
-
lines.push(
|
|
24
|
-
let aria = snapshot.ariaSnapshot ||
|
|
24
|
+
lines.push("```yaml");
|
|
25
|
+
let aria = snapshot.ariaSnapshot || "";
|
|
25
26
|
aria = String(truncate(aria, { maxStringLength: MAX_LENGTH }));
|
|
26
27
|
lines.push(aria);
|
|
27
|
-
lines.push(
|
|
28
|
-
response.addResult(lines.join(
|
|
28
|
+
lines.push("```");
|
|
29
|
+
response.addResult(lines.join("\n"));
|
|
29
30
|
}
|
|
30
31
|
catch (error) {
|
|
31
32
|
response.addResult(`Failed to get snapshot: ${String(error)}`);
|
|
32
33
|
}
|
|
33
34
|
},
|
|
34
35
|
});
|
|
35
|
-
export default [
|
|
36
|
-
getSnapshot,
|
|
37
|
-
];
|
|
36
|
+
export default [getSnapshot];
|
|
@@ -1,20 +1,35 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { sanitizeHtml } from
|
|
3
|
-
import { defineTool } from
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { sanitizeHtml } from "../utils/sanitizeHtml.js";
|
|
3
|
+
import { defineTool } from "./tool.js";
|
|
4
4
|
const getVisibleHtmlSchema = z.object({
|
|
5
|
-
selector: z
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
selector: z
|
|
6
|
+
.string()
|
|
7
|
+
.optional()
|
|
8
|
+
.describe("CSS selector for a specific element (optional)"),
|
|
9
|
+
cleanHtml: z
|
|
10
|
+
.boolean()
|
|
11
|
+
.optional()
|
|
12
|
+
.default(true)
|
|
13
|
+
.describe("Clean HTML (true) or raw HTML (false)"),
|
|
14
|
+
removeScripts: z
|
|
15
|
+
.boolean()
|
|
16
|
+
.optional()
|
|
17
|
+
.default(true)
|
|
18
|
+
.describe("Remove scripts (true) or keep them (false)"),
|
|
19
|
+
removeStyles: z
|
|
20
|
+
.boolean()
|
|
21
|
+
.optional()
|
|
22
|
+
.default(true)
|
|
23
|
+
.describe("Remove styles (true) or keep them (false)"),
|
|
9
24
|
});
|
|
10
25
|
const getVisibleHtml = defineTool({
|
|
11
|
-
capability:
|
|
26
|
+
capability: "core",
|
|
12
27
|
schema: {
|
|
13
|
-
name:
|
|
14
|
-
title:
|
|
15
|
-
description:
|
|
28
|
+
name: "browser_get_visible_html",
|
|
29
|
+
title: "Get visible HTML",
|
|
30
|
+
description: "Get HTML content of the page or a specific element",
|
|
16
31
|
inputSchema: getVisibleHtmlSchema,
|
|
17
|
-
type:
|
|
32
|
+
type: "readOnly",
|
|
18
33
|
},
|
|
19
34
|
handle: async (context, params, response) => {
|
|
20
35
|
const tab = await context.ensureTab();
|
|
@@ -28,7 +43,7 @@ const getVisibleHtml = defineTool({
|
|
|
28
43
|
const element = await page.$(selector);
|
|
29
44
|
if (!element)
|
|
30
45
|
throw new Error(`Element with selector "${selector}" not found`);
|
|
31
|
-
htmlContent = await page.evaluate(el => el.outerHTML, element);
|
|
46
|
+
htmlContent = await page.evaluate((el) => el.outerHTML, element);
|
|
32
47
|
}
|
|
33
48
|
else {
|
|
34
49
|
// Otherwise get the full page HTML
|
|
@@ -39,7 +54,10 @@ const getVisibleHtml = defineTool({
|
|
|
39
54
|
const shouldRemoveStyles = removeStyles || cleanHtml;
|
|
40
55
|
// Apply filters server-side to avoid TrustedHTML/Trusted Types restrictions in page context
|
|
41
56
|
if (shouldRemoveScripts || shouldRemoveStyles)
|
|
42
|
-
htmlContent = sanitizeHtml(htmlContent, {
|
|
57
|
+
htmlContent = sanitizeHtml(htmlContent, {
|
|
58
|
+
shouldRemoveScripts,
|
|
59
|
+
shouldRemoveStyles,
|
|
60
|
+
});
|
|
43
61
|
response.addResult(`HTML content:\n${htmlContent}`);
|
|
44
62
|
}
|
|
45
63
|
catch (error) {
|
|
@@ -47,6 +65,4 @@ const getVisibleHtml = defineTool({
|
|
|
47
65
|
}
|
|
48
66
|
},
|
|
49
67
|
});
|
|
50
|
-
export default [
|
|
51
|
-
getVisibleHtml,
|
|
52
|
-
];
|
|
68
|
+
export default [getVisibleHtml];
|
package/lib/tools/install.js
CHANGED
|
@@ -13,39 +13,39 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { fork } from
|
|
17
|
-
import path from
|
|
18
|
-
import { z } from
|
|
19
|
-
import { defineTool } from
|
|
16
|
+
import { fork } from "child_process";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { defineTool } from "./tool.js";
|
|
20
20
|
const install = defineTool({
|
|
21
|
-
capability:
|
|
21
|
+
capability: "core-install",
|
|
22
22
|
schema: {
|
|
23
|
-
name:
|
|
24
|
-
title:
|
|
25
|
-
description:
|
|
23
|
+
name: "browser_install",
|
|
24
|
+
title: "Install the browser specified in the config",
|
|
25
|
+
description: "Install the browser specified in the config. Call this if you get an error about the browser not being installed.",
|
|
26
26
|
inputSchema: z.object({}),
|
|
27
|
-
type:
|
|
27
|
+
type: "action",
|
|
28
28
|
},
|
|
29
29
|
handle: async (context, params, response) => {
|
|
30
|
-
const channel = context.config.browser?.launchOptions?.channel ??
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const channel = context.config.browser?.launchOptions?.channel ??
|
|
31
|
+
context.config.browser?.browserName ??
|
|
32
|
+
"chrome";
|
|
33
|
+
const cliPath = path.join(require.resolve("playwright/package.json"), "../cli.js");
|
|
34
|
+
const child = fork(cliPath, ["install", channel], {
|
|
35
|
+
stdio: "pipe",
|
|
34
36
|
});
|
|
35
37
|
const output = [];
|
|
36
|
-
child.stdout?.on(
|
|
37
|
-
child.stderr?.on(
|
|
38
|
+
child.stdout?.on("data", (data) => output.push(data.toString()));
|
|
39
|
+
child.stderr?.on("data", (data) => output.push(data.toString()));
|
|
38
40
|
await new Promise((resolve, reject) => {
|
|
39
|
-
child.on(
|
|
41
|
+
child.on("close", (code) => {
|
|
40
42
|
if (code === 0)
|
|
41
43
|
resolve();
|
|
42
44
|
else
|
|
43
|
-
reject(new Error(`Failed to install browser: ${output.join(
|
|
45
|
+
reject(new Error(`Failed to install browser: ${output.join("")}`));
|
|
44
46
|
});
|
|
45
47
|
});
|
|
46
48
|
response.setIncludeTabs();
|
|
47
49
|
},
|
|
48
50
|
});
|
|
49
|
-
export default [
|
|
50
|
-
install,
|
|
51
|
-
];
|
|
51
|
+
export default [install];
|