@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/snapshot.js
CHANGED
|
@@ -13,41 +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 javascript from "../utils/codegen.js";
|
|
18
|
+
import { defineTabTool, defineTool } from "./tool.js";
|
|
19
|
+
import { generateLocator } from "./utils.js";
|
|
20
20
|
const snapshot = defineTool({
|
|
21
|
-
capability:
|
|
21
|
+
capability: "core",
|
|
22
22
|
schema: {
|
|
23
|
-
name:
|
|
24
|
-
title:
|
|
25
|
-
description:
|
|
23
|
+
name: "browser_snapshot",
|
|
24
|
+
title: "Page snapshot",
|
|
25
|
+
description: "Capture accessibility snapshot of the current page, this is better than screenshot",
|
|
26
26
|
inputSchema: z.object({}),
|
|
27
|
-
type:
|
|
27
|
+
type: "readOnly",
|
|
28
28
|
},
|
|
29
29
|
handle: async (context, params, response) => {
|
|
30
30
|
await context.ensureTab();
|
|
31
|
-
response.setIncludeSnapshot(
|
|
31
|
+
response.setIncludeSnapshot("full");
|
|
32
32
|
},
|
|
33
33
|
});
|
|
34
34
|
export const elementSchema = z.object({
|
|
35
|
-
element: z
|
|
36
|
-
|
|
35
|
+
element: z
|
|
36
|
+
.string()
|
|
37
|
+
.describe("Human-readable element description used to obtain permission to interact with the element"),
|
|
38
|
+
ref: z
|
|
39
|
+
.string()
|
|
40
|
+
.describe("Exact target element reference from the page snapshot"),
|
|
37
41
|
});
|
|
38
42
|
const clickSchema = elementSchema.extend({
|
|
39
|
-
doubleClick: z
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
doubleClick: z
|
|
44
|
+
.boolean()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Whether to perform a double click instead of a single click"),
|
|
47
|
+
button: z
|
|
48
|
+
.enum(["left", "right", "middle"])
|
|
49
|
+
.optional()
|
|
50
|
+
.describe("Button to click, defaults to left"),
|
|
51
|
+
modifiers: z
|
|
52
|
+
.array(z.enum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"]))
|
|
53
|
+
.optional()
|
|
54
|
+
.describe("Modifier keys to press"),
|
|
42
55
|
});
|
|
43
56
|
const click = defineTabTool({
|
|
44
|
-
capability:
|
|
57
|
+
capability: "core",
|
|
45
58
|
schema: {
|
|
46
|
-
name:
|
|
47
|
-
title:
|
|
48
|
-
description:
|
|
59
|
+
name: "browser_click",
|
|
60
|
+
title: "Click",
|
|
61
|
+
description: "Perform click on a web page",
|
|
49
62
|
inputSchema: clickSchema,
|
|
50
|
-
type:
|
|
63
|
+
type: "input",
|
|
51
64
|
},
|
|
52
65
|
handle: async (tab, params, response) => {
|
|
53
66
|
response.setIncludeSnapshot();
|
|
@@ -56,8 +69,8 @@ const click = defineTabTool({
|
|
|
56
69
|
button: params.button,
|
|
57
70
|
modifiers: params.modifiers,
|
|
58
71
|
};
|
|
59
|
-
const formatted = javascript.formatObject(options,
|
|
60
|
-
const optionsAttr = formatted !==
|
|
72
|
+
const formatted = javascript.formatObject(options, " ", "oneline");
|
|
73
|
+
const optionsAttr = formatted !== "{}" ? formatted : "";
|
|
61
74
|
if (params.doubleClick)
|
|
62
75
|
response.addCode(`await page.${await generateLocator(locator)}.dblclick(${optionsAttr});`);
|
|
63
76
|
else
|
|
@@ -71,18 +84,26 @@ const click = defineTabTool({
|
|
|
71
84
|
},
|
|
72
85
|
});
|
|
73
86
|
const drag = defineTabTool({
|
|
74
|
-
capability:
|
|
87
|
+
capability: "core",
|
|
75
88
|
schema: {
|
|
76
|
-
name:
|
|
77
|
-
title:
|
|
78
|
-
description:
|
|
89
|
+
name: "browser_drag",
|
|
90
|
+
title: "Drag mouse",
|
|
91
|
+
description: "Perform drag and drop between two elements",
|
|
79
92
|
inputSchema: z.object({
|
|
80
|
-
startElement: z
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
startElement: z
|
|
94
|
+
.string()
|
|
95
|
+
.describe("Human-readable source element description used to obtain the permission to interact with the element"),
|
|
96
|
+
startRef: z
|
|
97
|
+
.string()
|
|
98
|
+
.describe("Exact source element reference from the page snapshot"),
|
|
99
|
+
endElement: z
|
|
100
|
+
.string()
|
|
101
|
+
.describe("Human-readable target element description used to obtain the permission to interact with the element"),
|
|
102
|
+
endRef: z
|
|
103
|
+
.string()
|
|
104
|
+
.describe("Exact target element reference from the page snapshot"),
|
|
84
105
|
}),
|
|
85
|
-
type:
|
|
106
|
+
type: "input",
|
|
86
107
|
},
|
|
87
108
|
handle: async (tab, params, response) => {
|
|
88
109
|
response.setIncludeSnapshot();
|
|
@@ -97,13 +118,13 @@ const drag = defineTabTool({
|
|
|
97
118
|
},
|
|
98
119
|
});
|
|
99
120
|
const hover = defineTabTool({
|
|
100
|
-
capability:
|
|
121
|
+
capability: "core",
|
|
101
122
|
schema: {
|
|
102
|
-
name:
|
|
103
|
-
title:
|
|
104
|
-
description:
|
|
123
|
+
name: "browser_hover",
|
|
124
|
+
title: "Hover mouse",
|
|
125
|
+
description: "Hover over element on page",
|
|
105
126
|
inputSchema: elementSchema,
|
|
106
|
-
type:
|
|
127
|
+
type: "readOnly",
|
|
107
128
|
},
|
|
108
129
|
handle: async (tab, params, response) => {
|
|
109
130
|
response.setIncludeSnapshot();
|
|
@@ -115,16 +136,18 @@ const hover = defineTabTool({
|
|
|
115
136
|
},
|
|
116
137
|
});
|
|
117
138
|
const selectOptionSchema = elementSchema.extend({
|
|
118
|
-
values: z
|
|
139
|
+
values: z
|
|
140
|
+
.array(z.string())
|
|
141
|
+
.describe("Array of values to select in the dropdown. This can be a single value or multiple values."),
|
|
119
142
|
});
|
|
120
143
|
const selectOption = defineTabTool({
|
|
121
|
-
capability:
|
|
144
|
+
capability: "core",
|
|
122
145
|
schema: {
|
|
123
|
-
name:
|
|
124
|
-
title:
|
|
125
|
-
description:
|
|
146
|
+
name: "browser_select_option",
|
|
147
|
+
title: "Select option",
|
|
148
|
+
description: "Select an option in a dropdown",
|
|
126
149
|
inputSchema: selectOptionSchema,
|
|
127
|
-
type:
|
|
150
|
+
type: "input",
|
|
128
151
|
},
|
|
129
152
|
handle: async (tab, params, response) => {
|
|
130
153
|
response.setIncludeSnapshot();
|
|
@@ -135,10 +158,4 @@ const selectOption = defineTabTool({
|
|
|
135
158
|
});
|
|
136
159
|
},
|
|
137
160
|
});
|
|
138
|
-
export default [
|
|
139
|
-
snapshot,
|
|
140
|
-
click,
|
|
141
|
-
drag,
|
|
142
|
-
hover,
|
|
143
|
-
selectOption,
|
|
144
|
-
];
|
|
161
|
+
export default [snapshot, click, drag, hover, selectOption];
|
package/lib/tools/tabs.js
CHANGED
|
@@ -13,47 +13,50 @@
|
|
|
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 { defineTool } from
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { defineTool } from "./tool.js";
|
|
18
18
|
const browserTabs = defineTool({
|
|
19
|
-
capability:
|
|
19
|
+
capability: "core-tabs",
|
|
20
20
|
schema: {
|
|
21
|
-
name:
|
|
22
|
-
title:
|
|
23
|
-
description:
|
|
21
|
+
name: "browser_tabs",
|
|
22
|
+
title: "Manage tabs",
|
|
23
|
+
description: "List, create, close, or select a browser tab.",
|
|
24
24
|
inputSchema: z.object({
|
|
25
|
-
action: z
|
|
26
|
-
|
|
25
|
+
action: z
|
|
26
|
+
.enum(["list", "new", "close", "select"])
|
|
27
|
+
.describe("Operation to perform"),
|
|
28
|
+
index: z
|
|
29
|
+
.number()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("Tab index, used for close/select. If omitted for close, current tab is closed."),
|
|
27
32
|
}),
|
|
28
|
-
type:
|
|
33
|
+
type: "action",
|
|
29
34
|
},
|
|
30
35
|
handle: async (context, params, response) => {
|
|
31
36
|
switch (params.action) {
|
|
32
|
-
case
|
|
37
|
+
case "list": {
|
|
33
38
|
await context.ensureTab();
|
|
34
39
|
response.setIncludeTabs();
|
|
35
40
|
return;
|
|
36
41
|
}
|
|
37
|
-
case
|
|
42
|
+
case "new": {
|
|
38
43
|
await context.newTab();
|
|
39
44
|
response.setIncludeTabs();
|
|
40
45
|
return;
|
|
41
46
|
}
|
|
42
|
-
case
|
|
47
|
+
case "close": {
|
|
43
48
|
await context.closeTab(params.index);
|
|
44
|
-
response.setIncludeSnapshot(
|
|
49
|
+
response.setIncludeSnapshot("full");
|
|
45
50
|
return;
|
|
46
51
|
}
|
|
47
|
-
case
|
|
52
|
+
case "select": {
|
|
48
53
|
if (params.index === undefined)
|
|
49
|
-
throw new Error(
|
|
54
|
+
throw new Error("Tab index is required");
|
|
50
55
|
await context.selectTab(params.index);
|
|
51
|
-
response.setIncludeSnapshot(
|
|
56
|
+
response.setIncludeSnapshot("full");
|
|
52
57
|
return;
|
|
53
58
|
}
|
|
54
59
|
}
|
|
55
60
|
},
|
|
56
61
|
});
|
|
57
|
-
export default [
|
|
58
|
-
browserTabs,
|
|
59
|
-
];
|
|
62
|
+
export default [browserTabs];
|
package/lib/tools/tool.js
CHANGED
|
@@ -21,11 +21,13 @@ export function defineTabTool(tool) {
|
|
|
21
21
|
...tool,
|
|
22
22
|
handle: async (context, params, response) => {
|
|
23
23
|
const tab = await context.ensureTab();
|
|
24
|
-
const modalStates = tab.modalStates().map(state => state.type);
|
|
24
|
+
const modalStates = tab.modalStates().map((state) => state.type);
|
|
25
25
|
if (tool.clearsModalState && !modalStates.includes(tool.clearsModalState))
|
|
26
|
-
response.addError(`Error: The tool "${tool.schema.name}" can only be used when there is related modal state present.\n` +
|
|
26
|
+
response.addError(`Error: The tool "${tool.schema.name}" can only be used when there is related modal state present.\n` +
|
|
27
|
+
tab.modalStatesMarkdown().join("\n"));
|
|
27
28
|
else if (!tool.clearsModalState && modalStates.length)
|
|
28
|
-
response.addError(`Error: Tool "${tool.schema.name}" does not handle the modal state.\n` +
|
|
29
|
+
response.addError(`Error: Tool "${tool.schema.name}" does not handle the modal state.\n` +
|
|
30
|
+
tab.modalStatesMarkdown().join("\n"));
|
|
29
31
|
else
|
|
30
32
|
return tool.handle(tab, params, response);
|
|
31
33
|
},
|
package/lib/tools/utils.js
CHANGED
|
@@ -13,13 +13,15 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
// @ts-
|
|
17
|
-
import { asLocator } from
|
|
16
|
+
// @ts-expect-error
|
|
17
|
+
import { asLocator } from "playwright-core/lib/utils";
|
|
18
18
|
export async function waitForCompletion(tab, callback) {
|
|
19
19
|
const requests = new Set();
|
|
20
20
|
let frameNavigated = false;
|
|
21
21
|
let waitCallback = () => { };
|
|
22
|
-
const waitBarrier = new Promise(f => {
|
|
22
|
+
const waitBarrier = new Promise((f) => {
|
|
23
|
+
waitCallback = f;
|
|
24
|
+
});
|
|
23
25
|
const requestListener = (request) => requests.add(request);
|
|
24
26
|
const requestFinishedListener = (request) => {
|
|
25
27
|
requests.delete(request);
|
|
@@ -32,20 +34,20 @@ export async function waitForCompletion(tab, callback) {
|
|
|
32
34
|
frameNavigated = true;
|
|
33
35
|
dispose();
|
|
34
36
|
clearTimeout(timeout);
|
|
35
|
-
void tab.waitForLoadState(
|
|
37
|
+
void tab.waitForLoadState("load").then(waitCallback);
|
|
36
38
|
};
|
|
37
39
|
const onTimeout = () => {
|
|
38
40
|
dispose();
|
|
39
41
|
waitCallback();
|
|
40
42
|
};
|
|
41
|
-
tab.page.on(
|
|
42
|
-
tab.page.on(
|
|
43
|
-
tab.page.on(
|
|
43
|
+
tab.page.on("request", requestListener);
|
|
44
|
+
tab.page.on("requestfinished", requestFinishedListener);
|
|
45
|
+
tab.page.on("framenavigated", frameNavigateListener);
|
|
44
46
|
const timeout = setTimeout(onTimeout, 10000);
|
|
45
47
|
const dispose = () => {
|
|
46
|
-
tab.page.off(
|
|
47
|
-
tab.page.off(
|
|
48
|
-
tab.page.off(
|
|
48
|
+
tab.page.off("request", requestListener);
|
|
49
|
+
tab.page.off("requestfinished", requestFinishedListener);
|
|
50
|
+
tab.page.off("framenavigated", frameNavigateListener);
|
|
49
51
|
clearTimeout(timeout);
|
|
50
52
|
};
|
|
51
53
|
try {
|
|
@@ -63,12 +65,14 @@ export async function waitForCompletion(tab, callback) {
|
|
|
63
65
|
export async function generateLocator(locator) {
|
|
64
66
|
try {
|
|
65
67
|
const { resolvedSelector } = await locator._resolveSelector();
|
|
66
|
-
return asLocator(
|
|
68
|
+
return asLocator("javascript", resolvedSelector);
|
|
67
69
|
}
|
|
68
70
|
catch (e) {
|
|
69
|
-
throw new Error(
|
|
71
|
+
throw new Error("Ref not found, likely because element was removed. Use browser_get_snapshot to see what elements are currently on the page.");
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
export async function callOnPageNoTrace(page, callback) {
|
|
73
|
-
return await page._wrapApiCall(() => callback(page), {
|
|
75
|
+
return await page._wrapApiCall(() => callback(page), {
|
|
76
|
+
internal: true,
|
|
77
|
+
});
|
|
74
78
|
}
|
package/lib/tools/wait.js
CHANGED
|
@@ -13,43 +13,48 @@
|
|
|
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 { defineTool } from
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { defineTool } from "./tool.js";
|
|
18
18
|
const wait = defineTool({
|
|
19
|
-
capability:
|
|
19
|
+
capability: "core",
|
|
20
20
|
schema: {
|
|
21
|
-
name:
|
|
22
|
-
title:
|
|
23
|
-
description:
|
|
21
|
+
name: "browser_wait_for",
|
|
22
|
+
title: "Wait for",
|
|
23
|
+
description: "Wait for text to appear or disappear or a specified time to pass",
|
|
24
24
|
inputSchema: z.object({
|
|
25
|
-
time: z.number().optional().describe(
|
|
26
|
-
text: z.string().optional().describe(
|
|
27
|
-
textGone: z
|
|
25
|
+
time: z.number().optional().describe("The time to wait in seconds"),
|
|
26
|
+
text: z.string().optional().describe("The text to wait for"),
|
|
27
|
+
textGone: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("The text to wait for to disappear"),
|
|
28
31
|
}),
|
|
29
|
-
type:
|
|
32
|
+
type: "readOnly",
|
|
30
33
|
},
|
|
31
34
|
handle: async (context, params, response) => {
|
|
32
35
|
if (!params.text && !params.textGone && !params.time)
|
|
33
|
-
throw new Error(
|
|
36
|
+
throw new Error("Either time, text or textGone must be provided");
|
|
34
37
|
if (params.time) {
|
|
35
38
|
response.addCode(`await new Promise(f => setTimeout(f, ${params.time} * 1000));`);
|
|
36
|
-
await new Promise(f => setTimeout(f, Math.min(30000, params.time * 1000)));
|
|
39
|
+
await new Promise((f) => setTimeout(f, Math.min(30000, params.time * 1000)));
|
|
37
40
|
}
|
|
38
41
|
const tab = context.currentTabOrDie();
|
|
39
|
-
const locator = params.text
|
|
40
|
-
|
|
42
|
+
const locator = params.text
|
|
43
|
+
? tab.page.getByText(params.text).first()
|
|
44
|
+
: undefined;
|
|
45
|
+
const goneLocator = params.textGone
|
|
46
|
+
? tab.page.getByText(params.textGone).first()
|
|
47
|
+
: undefined;
|
|
41
48
|
if (goneLocator) {
|
|
42
49
|
response.addCode(`await page.getByText(${JSON.stringify(params.textGone)}).first().waitFor({ state: 'hidden' });`);
|
|
43
|
-
await goneLocator.waitFor({ state:
|
|
50
|
+
await goneLocator.waitFor({ state: "hidden" });
|
|
44
51
|
}
|
|
45
52
|
if (locator) {
|
|
46
53
|
response.addCode(`await page.getByText(${JSON.stringify(params.text)}).first().waitFor({ state: 'visible' });`);
|
|
47
|
-
await locator.waitFor({ state:
|
|
54
|
+
await locator.waitFor({ state: "visible" });
|
|
48
55
|
}
|
|
49
56
|
response.addResult(`Waited for ${params.text || params.textGone || params.time}`);
|
|
50
57
|
response.setIncludeSnapshot();
|
|
51
58
|
},
|
|
52
59
|
});
|
|
53
|
-
export default [
|
|
54
|
-
wait,
|
|
55
|
-
];
|
|
60
|
+
export default [wait];
|
package/lib/tools.js
CHANGED
|
@@ -13,29 +13,29 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
// import files from './tools/files.js';
|
|
20
|
-
// import install from './tools/install.js';
|
|
21
|
-
import keyboard from './tools/keyboard.js';
|
|
22
|
-
import navigate from './tools/navigate.js';
|
|
23
|
-
import network from './tools/network.js';
|
|
24
|
-
import networkSearch from './tools/networkSearch.js';
|
|
25
|
-
import networkDetail from './tools/networkDetail.js';
|
|
26
|
-
// import pdf from './tools/pdf.js';
|
|
27
|
-
import snapshot from './tools/snapshot.js';
|
|
16
|
+
import { toolNameSchema } from "./hooks/schema.js";
|
|
17
|
+
import common from "./tools/common.js";
|
|
18
|
+
import console from "./tools/console.js";
|
|
28
19
|
// import tabs from './tools/tabs.js';
|
|
29
20
|
// import screenshot from './tools/screenshot.js';
|
|
30
21
|
// import wait from './tools/wait.js';
|
|
31
22
|
// import javascript from './tools/javascript.js';
|
|
32
|
-
import evaluate from
|
|
33
|
-
import
|
|
34
|
-
import getSnapshot from
|
|
35
|
-
import
|
|
36
|
-
import
|
|
37
|
-
import
|
|
38
|
-
import
|
|
23
|
+
import evaluate from "./tools/evaluate.js";
|
|
24
|
+
import extractFrameworkState from "./tools/extractFrameworkState.js";
|
|
25
|
+
import getSnapshot from "./tools/getSnapshot.js";
|
|
26
|
+
import getVisibleHtml from "./tools/getVisibleHtml.js";
|
|
27
|
+
// import dialogs from './tools/dialogs.js';
|
|
28
|
+
// import files from './tools/files.js';
|
|
29
|
+
// import install from './tools/install.js';
|
|
30
|
+
import keyboard from "./tools/keyboard.js";
|
|
31
|
+
import navigate from "./tools/navigate.js";
|
|
32
|
+
import network from "./tools/network.js";
|
|
33
|
+
import networkDetail from "./tools/networkDetail.js";
|
|
34
|
+
import networkSearch from "./tools/networkSearch.js";
|
|
35
|
+
import repl from "./tools/repl.js";
|
|
36
|
+
import scroll from "./tools/scroll.js";
|
|
37
|
+
// import pdf from './tools/pdf.js';
|
|
38
|
+
import snapshot from "./tools/snapshot.js";
|
|
39
39
|
export const allTools = [
|
|
40
40
|
...common,
|
|
41
41
|
...console,
|
|
@@ -62,6 +62,7 @@ export const allTools = [
|
|
|
62
62
|
...scroll,
|
|
63
63
|
];
|
|
64
64
|
export function filteredTools(config) {
|
|
65
|
-
return allTools.filter(tool => (tool.capability.startsWith(
|
|
65
|
+
return allTools.filter((tool) => (tool.capability.startsWith("core") ||
|
|
66
|
+
config.capabilities?.includes(tool.capability)) &&
|
|
66
67
|
toolNameSchema.safeParse(tool.schema.name).success);
|
|
67
68
|
}
|
|
@@ -1,28 +1,31 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { FiltersEngine, Request } from '@ghostery/adblocker';
|
|
1
|
+
import { FiltersEngine, Request } from "@ghostery/adblocker";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path, { dirname } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
6
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
6
|
const __dirname = dirname(__filename);
|
|
8
7
|
class AdBlockFilter {
|
|
9
8
|
_engine;
|
|
10
9
|
_initialized = false;
|
|
11
10
|
_loadFilters() {
|
|
12
|
-
const packageRoot = path.resolve(__dirname,
|
|
13
|
-
const libFiltersDir = path.join(packageRoot,
|
|
14
|
-
const srcFiltersDir = path.join(packageRoot,
|
|
15
|
-
const filtersDir = fs.existsSync(libFiltersDir)
|
|
11
|
+
const packageRoot = path.resolve(__dirname, "../..");
|
|
12
|
+
const libFiltersDir = path.join(packageRoot, "lib/filters");
|
|
13
|
+
const srcFiltersDir = path.join(packageRoot, "src/filters");
|
|
14
|
+
const filtersDir = fs.existsSync(libFiltersDir)
|
|
15
|
+
? libFiltersDir
|
|
16
|
+
: srcFiltersDir;
|
|
16
17
|
if (!fs.existsSync(filtersDir))
|
|
17
18
|
return FiltersEngine.empty();
|
|
18
|
-
const filterFiles = fs
|
|
19
|
+
const filterFiles = fs
|
|
20
|
+
.readdirSync(filtersDir)
|
|
21
|
+
.filter((f) => f.endsWith(".txt"));
|
|
19
22
|
const allFilters = [];
|
|
20
23
|
for (const file of filterFiles) {
|
|
21
24
|
const filePath = path.join(filtersDir, file);
|
|
22
|
-
const content = fs.readFileSync(filePath,
|
|
25
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
23
26
|
allFilters.push(content);
|
|
24
27
|
}
|
|
25
|
-
const combinedFilters = allFilters.join(
|
|
28
|
+
const combinedFilters = allFilters.join("\n");
|
|
26
29
|
return FiltersEngine.parse(combinedFilters);
|
|
27
30
|
}
|
|
28
31
|
initialize() {
|
|
@@ -47,7 +50,7 @@ class AdBlockFilter {
|
|
|
47
50
|
sourceHostname: hostname,
|
|
48
51
|
sourceDomain: domain,
|
|
49
52
|
type: this._mapResourceType(resourceType),
|
|
50
|
-
requestId:
|
|
53
|
+
requestId: "",
|
|
51
54
|
tabId: 0,
|
|
52
55
|
});
|
|
53
56
|
const result = this._engine.match(request);
|
|
@@ -59,20 +62,20 @@ class AdBlockFilter {
|
|
|
59
62
|
}
|
|
60
63
|
_mapResourceType(resourceType) {
|
|
61
64
|
const mapping = {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
document: "main_frame",
|
|
66
|
+
stylesheet: "stylesheet",
|
|
67
|
+
image: "image",
|
|
68
|
+
media: "media",
|
|
69
|
+
font: "font",
|
|
70
|
+
script: "script",
|
|
71
|
+
texttrack: "other",
|
|
72
|
+
xhr: "xmlhttprequest",
|
|
73
|
+
fetch: "xmlhttprequest",
|
|
74
|
+
websocket: "websocket",
|
|
75
|
+
manifest: "other",
|
|
76
|
+
other: "other",
|
|
74
77
|
};
|
|
75
|
-
return mapping[resourceType] ||
|
|
78
|
+
return mapping[resourceType] || "other";
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
let filterInstance;
|
package/lib/utils/codegen.js
CHANGED
|
@@ -17,35 +17,39 @@
|
|
|
17
17
|
// - https://github.com/microsoft/playwright/blob/76ee48dc9d4034536e3ec5b2c7ce8be3b79418a8/packages/playwright-core/src/utils/isomorphic/stringUtils.ts
|
|
18
18
|
// - https://github.com/microsoft/playwright/blob/76ee48dc9d4034536e3ec5b2c7ce8be3b79418a8/packages/playwright-core/src/server/codegen/javascript.ts
|
|
19
19
|
// NOTE: this function should not be used to escape any selectors.
|
|
20
|
-
export function escapeWithQuotes(text, char = '
|
|
20
|
+
export function escapeWithQuotes(text, char = "'") {
|
|
21
21
|
const stringified = JSON.stringify(text);
|
|
22
|
-
const escapedText = stringified
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const escapedText = stringified
|
|
23
|
+
.substring(1, stringified.length - 1)
|
|
24
|
+
.replace(/\\"/g, '"');
|
|
25
|
+
if (char === "'")
|
|
26
|
+
return char + escapedText.replace(/[']/g, "\\'") + char;
|
|
25
27
|
if (char === '"')
|
|
26
28
|
return char + escapedText.replace(/["]/g, '\\"') + char;
|
|
27
|
-
if (char ===
|
|
28
|
-
return char + escapedText.replace(/[`]/g,
|
|
29
|
-
throw new Error(
|
|
29
|
+
if (char === "`")
|
|
30
|
+
return char + escapedText.replace(/[`]/g, "\\`") + char;
|
|
31
|
+
throw new Error("Invalid escape char");
|
|
30
32
|
}
|
|
31
33
|
export function quote(text) {
|
|
32
|
-
return escapeWithQuotes(text, '
|
|
34
|
+
return escapeWithQuotes(text, "'");
|
|
33
35
|
}
|
|
34
|
-
export function formatObject(value, indent =
|
|
35
|
-
if (typeof value ===
|
|
36
|
+
export function formatObject(value, indent = " ", mode = "multiline") {
|
|
37
|
+
if (typeof value === "string")
|
|
36
38
|
return quote(value);
|
|
37
39
|
if (Array.isArray(value))
|
|
38
|
-
return `[${value.map(o => formatObject(o)).join(
|
|
39
|
-
if (typeof value ===
|
|
40
|
-
const keys = Object.keys(value)
|
|
40
|
+
return `[${value.map((o) => formatObject(o)).join(", ")}]`;
|
|
41
|
+
if (typeof value === "object") {
|
|
42
|
+
const keys = Object.keys(value)
|
|
43
|
+
.filter((key) => value[key] !== undefined)
|
|
44
|
+
.sort();
|
|
41
45
|
if (!keys.length)
|
|
42
|
-
return
|
|
46
|
+
return "{}";
|
|
43
47
|
const tokens = [];
|
|
44
48
|
for (const key of keys)
|
|
45
49
|
tokens.push(`${key}: ${formatObject(value[key])}`);
|
|
46
|
-
if (mode ===
|
|
50
|
+
if (mode === "multiline")
|
|
47
51
|
return `{\n${tokens.join(`,\n${indent}`)}\n}`;
|
|
48
|
-
return `{ ${tokens.join(
|
|
52
|
+
return `{ ${tokens.join(", ")} }`;
|
|
49
53
|
}
|
|
50
54
|
return String(value);
|
|
51
55
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { dirname, resolve } from
|
|
2
|
-
import { fileURLToPath } from
|
|
1
|
+
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
3
|
// Resolve package root from this module's location
|
|
4
4
|
// In published package: node_modules/@wordbricks/playwright-mcp/lib/utils/extensionPath.js
|
|
5
5
|
// In development: src/utils/extensionPath.ts
|
|
6
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = dirname(__filename);
|
|
8
8
|
// Go up from lib/utils/ or src/utils/ to package root
|
|
9
|
-
const packageRoot = resolve(__dirname,
|
|
10
|
-
export const extensionPath = resolve(packageRoot,
|
|
9
|
+
const packageRoot = resolve(__dirname, "../..");
|
|
10
|
+
export const extensionPath = resolve(packageRoot, "src/extensions/uBlockOriginLite");
|