@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.
Files changed (87) hide show
  1. package/cli-wrapper.js +15 -14
  2. package/cli.js +1 -1
  3. package/config.d.ts +11 -6
  4. package/index.d.ts +7 -5
  5. package/index.js +1 -1
  6. package/lib/browserContextFactory.js +131 -58
  7. package/lib/browserServerBackend.js +14 -12
  8. package/lib/config.js +60 -46
  9. package/lib/context.js +41 -39
  10. package/lib/extension/cdpRelay.js +67 -61
  11. package/lib/extension/extensionContextFactory.js +10 -10
  12. package/lib/frameworkPatterns.js +21 -21
  13. package/lib/hooks/antiBotDetectionHook.js +59 -52
  14. package/lib/hooks/core.js +11 -10
  15. package/lib/hooks/eventConsumer.js +21 -21
  16. package/lib/hooks/events.js +3 -3
  17. package/lib/hooks/formatToolCallEvent.js +3 -7
  18. package/lib/hooks/frameworkStateHook.js +40 -40
  19. package/lib/hooks/grouping.js +3 -3
  20. package/lib/hooks/jsonLdDetectionHook.js +44 -37
  21. package/lib/hooks/networkFilters.js +17 -17
  22. package/lib/hooks/networkSetup.js +9 -7
  23. package/lib/hooks/networkTrackingHook.js +21 -21
  24. package/lib/hooks/pageHeightHook.js +9 -9
  25. package/lib/hooks/registry.js +15 -16
  26. package/lib/hooks/requireTabHook.js +3 -3
  27. package/lib/hooks/schema.js +38 -38
  28. package/lib/hooks/waitHook.js +7 -7
  29. package/lib/index.js +12 -10
  30. package/lib/mcp/inProcessTransport.js +3 -4
  31. package/lib/mcp/proxyBackend.js +43 -28
  32. package/lib/mcp/server.js +24 -19
  33. package/lib/mcp/tool.js +14 -8
  34. package/lib/mcp/transport.js +60 -53
  35. package/lib/playwrightTransformer.js +129 -106
  36. package/lib/program.js +54 -52
  37. package/lib/response.js +36 -30
  38. package/lib/sessionLog.js +19 -17
  39. package/lib/tab.js +41 -39
  40. package/lib/tools/common.js +19 -19
  41. package/lib/tools/console.js +11 -11
  42. package/lib/tools/dialogs.js +18 -15
  43. package/lib/tools/evaluate.js +26 -17
  44. package/lib/tools/extractFrameworkState.js +48 -37
  45. package/lib/tools/files.js +17 -14
  46. package/lib/tools/form.js +32 -23
  47. package/lib/tools/getSnapshot.js +14 -15
  48. package/lib/tools/getVisibleHtml.js +33 -17
  49. package/lib/tools/install.js +20 -20
  50. package/lib/tools/keyboard.js +29 -24
  51. package/lib/tools/mouse.js +29 -31
  52. package/lib/tools/navigate.js +19 -23
  53. package/lib/tools/network.js +12 -14
  54. package/lib/tools/networkDetail.js +58 -49
  55. package/lib/tools/networkSearch/bodySearch.js +46 -32
  56. package/lib/tools/networkSearch/grouping.js +15 -6
  57. package/lib/tools/networkSearch/helpers.js +4 -4
  58. package/lib/tools/networkSearch/searchHtml.js +25 -16
  59. package/lib/tools/networkSearch/urlSearch.js +56 -14
  60. package/lib/tools/networkSearch.js +46 -36
  61. package/lib/tools/pdf.js +13 -12
  62. package/lib/tools/repl.js +66 -54
  63. package/lib/tools/screenshot.js +57 -33
  64. package/lib/tools/scroll.js +29 -24
  65. package/lib/tools/snapshot.js +66 -49
  66. package/lib/tools/tabs.js +22 -19
  67. package/lib/tools/tool.js +5 -3
  68. package/lib/tools/utils.js +17 -13
  69. package/lib/tools/wait.js +24 -19
  70. package/lib/tools.js +21 -20
  71. package/lib/utils/adBlockFilter.js +29 -26
  72. package/lib/utils/codegen.js +20 -16
  73. package/lib/utils/extensionPath.js +4 -4
  74. package/lib/utils/fileUtils.js +17 -13
  75. package/lib/utils/graphql.js +69 -58
  76. package/lib/utils/guid.js +3 -3
  77. package/lib/utils/httpServer.js +9 -9
  78. package/lib/utils/log.js +3 -3
  79. package/lib/utils/manualPromise.js +7 -7
  80. package/lib/utils/networkFormat.js +7 -5
  81. package/lib/utils/package.js +4 -4
  82. package/lib/utils/sanitizeHtml.js +66 -34
  83. package/lib/utils/truncate.js +25 -25
  84. package/lib/utils/withTimeout.js +1 -1
  85. package/package.json +34 -57
  86. package/src/index.ts +27 -17
  87. package/LICENSE +0 -202
@@ -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 'zod';
17
- import { defineTabTool, defineTool } from './tool.js';
18
- import * as javascript from '../utils/codegen.js';
19
- import { generateLocator } from './utils.js';
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: 'core',
21
+ capability: "core",
22
22
  schema: {
23
- name: 'browser_snapshot',
24
- title: 'Page snapshot',
25
- description: 'Capture accessibility snapshot of the current page, this is better than screenshot',
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: 'readOnly',
27
+ type: "readOnly",
28
28
  },
29
29
  handle: async (context, params, response) => {
30
30
  await context.ensureTab();
31
- response.setIncludeSnapshot('full');
31
+ response.setIncludeSnapshot("full");
32
32
  },
33
33
  });
34
34
  export const elementSchema = z.object({
35
- element: z.string().describe('Human-readable element description used to obtain permission to interact with the element'),
36
- ref: z.string().describe('Exact target element reference from the page snapshot'),
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.boolean().optional().describe('Whether to perform a double click instead of a single click'),
40
- button: z.enum(['left', 'right', 'middle']).optional().describe('Button to click, defaults to left'),
41
- modifiers: z.array(z.enum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift'])).optional().describe('Modifier keys to press'),
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: 'core',
57
+ capability: "core",
45
58
  schema: {
46
- name: 'browser_click',
47
- title: 'Click',
48
- description: 'Perform click on a web page',
59
+ name: "browser_click",
60
+ title: "Click",
61
+ description: "Perform click on a web page",
49
62
  inputSchema: clickSchema,
50
- type: 'input',
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, ' ', 'oneline');
60
- const optionsAttr = formatted !== '{}' ? 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: 'core',
87
+ capability: "core",
75
88
  schema: {
76
- name: 'browser_drag',
77
- title: 'Drag mouse',
78
- description: 'Perform drag and drop between two elements',
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.string().describe('Human-readable source element description used to obtain the permission to interact with the element'),
81
- startRef: z.string().describe('Exact source element reference from the page snapshot'),
82
- endElement: z.string().describe('Human-readable target element description used to obtain the permission to interact with the element'),
83
- endRef: z.string().describe('Exact target element reference from the page snapshot'),
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: 'input',
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: 'core',
121
+ capability: "core",
101
122
  schema: {
102
- name: 'browser_hover',
103
- title: 'Hover mouse',
104
- description: 'Hover over element on page',
123
+ name: "browser_hover",
124
+ title: "Hover mouse",
125
+ description: "Hover over element on page",
105
126
  inputSchema: elementSchema,
106
- type: 'readOnly',
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.array(z.string()).describe('Array of values to select in the dropdown. This can be a single value or multiple values.'),
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: 'core',
144
+ capability: "core",
122
145
  schema: {
123
- name: 'browser_select_option',
124
- title: 'Select option',
125
- description: 'Select an option in a dropdown',
146
+ name: "browser_select_option",
147
+ title: "Select option",
148
+ description: "Select an option in a dropdown",
126
149
  inputSchema: selectOptionSchema,
127
- type: 'input',
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 'zod';
17
- import { defineTool } from './tool.js';
16
+ import { z } from "zod";
17
+ import { defineTool } from "./tool.js";
18
18
  const browserTabs = defineTool({
19
- capability: 'core-tabs',
19
+ capability: "core-tabs",
20
20
  schema: {
21
- name: 'browser_tabs',
22
- title: 'Manage tabs',
23
- description: 'List, create, close, or select a browser tab.',
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.enum(['list', 'new', 'close', 'select']).describe('Operation to perform'),
26
- index: z.number().optional().describe('Tab index, used for close/select. If omitted for close, current tab is closed.'),
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: 'action',
33
+ type: "action",
29
34
  },
30
35
  handle: async (context, params, response) => {
31
36
  switch (params.action) {
32
- case 'list': {
37
+ case "list": {
33
38
  await context.ensureTab();
34
39
  response.setIncludeTabs();
35
40
  return;
36
41
  }
37
- case 'new': {
42
+ case "new": {
38
43
  await context.newTab();
39
44
  response.setIncludeTabs();
40
45
  return;
41
46
  }
42
- case 'close': {
47
+ case "close": {
43
48
  await context.closeTab(params.index);
44
- response.setIncludeSnapshot('full');
49
+ response.setIncludeSnapshot("full");
45
50
  return;
46
51
  }
47
- case 'select': {
52
+ case "select": {
48
53
  if (params.index === undefined)
49
- throw new Error('Tab index is required');
54
+ throw new Error("Tab index is required");
50
55
  await context.selectTab(params.index);
51
- response.setIncludeSnapshot('full');
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` + tab.modalStatesMarkdown().join('\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` + tab.modalStatesMarkdown().join('\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
  },
@@ -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-ignore
17
- import { asLocator } from 'playwright-core/lib/utils';
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 => { waitCallback = 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('load').then(waitCallback);
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('request', requestListener);
42
- tab.page.on('requestfinished', requestFinishedListener);
43
- tab.page.on('framenavigated', frameNavigateListener);
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('request', requestListener);
47
- tab.page.off('requestfinished', requestFinishedListener);
48
- tab.page.off('framenavigated', frameNavigateListener);
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('javascript', resolvedSelector);
68
+ return asLocator("javascript", resolvedSelector);
67
69
  }
68
70
  catch (e) {
69
- throw new Error('Ref not found, likely because element was removed. Use browser_get_snapshot to see what elements are currently on the page.');
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), { internal: true });
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 'zod';
17
- import { defineTool } from './tool.js';
16
+ import { z } from "zod";
17
+ import { defineTool } from "./tool.js";
18
18
  const wait = defineTool({
19
- capability: 'core',
19
+ capability: "core",
20
20
  schema: {
21
- name: 'browser_wait_for',
22
- title: 'Wait for',
23
- description: 'Wait for text to appear or disappear or a specified time to pass',
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('The time to wait in seconds'),
26
- text: z.string().optional().describe('The text to wait for'),
27
- textGone: z.string().optional().describe('The text to wait for to disappear'),
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: 'readOnly',
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('Either time, text or textGone must be provided');
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 ? tab.page.getByText(params.text).first() : undefined;
40
- const goneLocator = params.textGone ? tab.page.getByText(params.textGone).first() : undefined;
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: 'hidden' });
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: 'visible' });
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 common from './tools/common.js';
17
- import console from './tools/console.js';
18
- // import dialogs from './tools/dialogs.js';
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 './tools/evaluate.js';
33
- import getVisibleHtml from './tools/getVisibleHtml.js';
34
- import getSnapshot from './tools/getSnapshot.js';
35
- import repl from './tools/repl.js';
36
- import extractFrameworkState from './tools/extractFrameworkState.js';
37
- import scroll from './tools/scroll.js';
38
- import { toolNameSchema } from './hooks/schema.js';
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('core') || config.capabilities?.includes(tool.capability)) &&
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 fs from 'fs';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import { dirname } from 'path';
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, 'lib/filters');
14
- const srcFiltersDir = path.join(packageRoot, 'src/filters');
15
- const filtersDir = fs.existsSync(libFiltersDir) ? libFiltersDir : srcFiltersDir;
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.readdirSync(filtersDir).filter(f => f.endsWith('.txt'));
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, 'utf-8');
25
+ const content = fs.readFileSync(filePath, "utf-8");
23
26
  allFilters.push(content);
24
27
  }
25
- const combinedFilters = allFilters.join('\n');
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
- 'document': 'main_frame',
63
- 'stylesheet': 'stylesheet',
64
- 'image': 'image',
65
- 'media': 'media',
66
- 'font': 'font',
67
- 'script': 'script',
68
- 'texttrack': 'other',
69
- 'xhr': 'xmlhttprequest',
70
- 'fetch': 'xmlhttprequest',
71
- 'websocket': 'websocket',
72
- 'manifest': 'other',
73
- 'other': 'other',
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] || 'other';
78
+ return mapping[resourceType] || "other";
76
79
  }
77
80
  }
78
81
  let filterInstance;
@@ -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.substring(1, stringified.length - 1).replace(/\\"/g, '"');
23
- if (char === '\'')
24
- return char + escapedText.replace(/[']/g, '\\\'') + char;
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, '\\`') + char;
29
- throw new Error('Invalid escape char');
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 = ' ', mode = 'multiline') {
35
- if (typeof value === 'string')
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 === 'object') {
40
- const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();
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 === 'multiline')
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 'node:path';
2
- import { fileURLToPath } from 'node:url';
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, 'src/extensions/uBlockOriginLite');
9
+ const packageRoot = resolve(__dirname, "../..");
10
+ export const extensionPath = resolve(packageRoot, "src/extensions/uBlockOriginLite");