@szymonrybczak/playwright-mcp 0.0.2 → 0.0.5

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 (115) hide show
  1. package/index.d.ts +20 -8
  2. package/lib/cjs/_virtual/_commonjsHelpers.js +9 -0
  3. package/lib/cjs/_virtual/browser.js +11 -0
  4. package/lib/cjs/_virtual/browser2.js +7 -0
  5. package/lib/cjs/cloudflare/package.json.js +10 -0
  6. package/lib/cjs/index.js +27 -0
  7. package/lib/cjs/node_modules/debug/src/browser.js +290 -0
  8. package/lib/cjs/node_modules/debug/src/common.js +307 -0
  9. package/lib/cjs/node_modules/ms/index.js +176 -0
  10. package/lib/cjs/package.js +9 -0
  11. package/lib/cjs/src/browserContextFactory.js +240 -0
  12. package/lib/cjs/src/config.js +77 -0
  13. package/lib/cjs/src/connection.js +74 -0
  14. package/lib/cjs/src/context.js +281 -0
  15. package/lib/cjs/src/fileUtils.js +25 -0
  16. package/lib/cjs/src/index.js +15 -0
  17. package/lib/cjs/src/javascript.js +41 -0
  18. package/lib/cjs/src/manualPromise.js +39 -0
  19. package/lib/cjs/src/pageSnapshot.js +35 -0
  20. package/lib/cjs/src/tab.js +91 -0
  21. package/lib/cjs/src/tools/common.js +60 -0
  22. package/lib/cjs/src/tools/console.js +36 -0
  23. package/lib/cjs/src/tools/dialogs.js +44 -0
  24. package/lib/cjs/src/tools/files.js +43 -0
  25. package/lib/cjs/src/tools/install.js +49 -0
  26. package/lib/cjs/src/tools/keyboard.js +38 -0
  27. package/lib/cjs/src/tools/navigate.js +85 -0
  28. package/lib/cjs/src/tools/network.js +43 -0
  29. package/lib/cjs/src/tools/pdf.js +42 -0
  30. package/lib/cjs/src/tools/screenshot.js +69 -0
  31. package/lib/cjs/src/tools/snapshot.js +195 -0
  32. package/lib/cjs/src/tools/tabs.js +110 -0
  33. package/lib/cjs/src/tools/testing.js +52 -0
  34. package/lib/cjs/src/tools/tool.js +9 -0
  35. package/lib/cjs/src/tools/utils.js +75 -0
  36. package/lib/cjs/src/tools/vision.js +181 -0
  37. package/lib/cjs/src/tools/wait.js +51 -0
  38. package/lib/cjs/src/tools.js +54 -0
  39. package/lib/esm/_virtual/_commonjsHelpers.js +5 -0
  40. package/lib/esm/_virtual/browser.js +7 -0
  41. package/lib/esm/_virtual/browser2.js +3 -0
  42. package/lib/esm/cloudflare/package.json.js +5 -0
  43. package/lib/esm/index.js +23 -0
  44. package/lib/esm/node_modules/debug/src/browser.js +286 -0
  45. package/lib/esm/node_modules/debug/src/common.js +303 -0
  46. package/lib/esm/node_modules/ms/index.js +172 -0
  47. package/lib/esm/package.js +5 -0
  48. package/lib/esm/src/browserContextFactory.js +216 -0
  49. package/lib/esm/src/config.js +72 -0
  50. package/lib/esm/src/connection.js +69 -0
  51. package/lib/esm/src/context.js +277 -0
  52. package/lib/esm/src/fileUtils.js +20 -0
  53. package/lib/esm/src/index.js +11 -0
  54. package/lib/esm/src/javascript.js +35 -0
  55. package/lib/esm/src/manualPromise.js +35 -0
  56. package/lib/esm/src/pageSnapshot.js +31 -0
  57. package/lib/esm/src/tab.js +87 -0
  58. package/lib/esm/src/tools/common.js +56 -0
  59. package/lib/esm/src/tools/console.js +32 -0
  60. package/lib/esm/src/tools/dialogs.js +40 -0
  61. package/lib/esm/src/tools/files.js +39 -0
  62. package/lib/esm/src/tools/install.js +45 -0
  63. package/lib/esm/src/tools/keyboard.js +34 -0
  64. package/lib/esm/src/tools/navigate.js +81 -0
  65. package/lib/esm/src/tools/network.js +39 -0
  66. package/lib/esm/src/tools/pdf.js +38 -0
  67. package/lib/esm/src/tools/screenshot.js +65 -0
  68. package/lib/esm/src/tools/snapshot.js +191 -0
  69. package/lib/esm/src/tools/tabs.js +106 -0
  70. package/lib/esm/src/tools/testing.js +48 -0
  71. package/lib/esm/src/tools/tool.js +5 -0
  72. package/lib/esm/src/tools/utils.js +68 -0
  73. package/lib/esm/src/tools/vision.js +177 -0
  74. package/lib/esm/src/tools/wait.js +47 -0
  75. package/lib/esm/src/tools.js +49 -0
  76. package/package.json +15 -44
  77. package/LICENSE +0 -202
  78. package/README.md +0 -508
  79. package/cli.js +0 -18
  80. package/config.d.ts +0 -128
  81. package/index.js +0 -19
  82. package/lib/browserContextFactory.js +0 -227
  83. package/lib/browserServer.js +0 -151
  84. package/lib/config.js +0 -189
  85. package/lib/connection.js +0 -82
  86. package/lib/context.js +0 -291
  87. package/lib/fileUtils.js +0 -32
  88. package/lib/httpServer.js +0 -201
  89. package/lib/index.js +0 -36
  90. package/lib/javascript.js +0 -49
  91. package/lib/manualPromise.js +0 -111
  92. package/lib/package.js +0 -20
  93. package/lib/pageSnapshot.js +0 -43
  94. package/lib/program.js +0 -72
  95. package/lib/server.js +0 -48
  96. package/lib/tab.js +0 -101
  97. package/lib/tools/common.js +0 -68
  98. package/lib/tools/console.js +0 -44
  99. package/lib/tools/dialogs.js +0 -52
  100. package/lib/tools/files.js +0 -51
  101. package/lib/tools/install.js +0 -57
  102. package/lib/tools/keyboard.js +0 -46
  103. package/lib/tools/navigate.js +0 -93
  104. package/lib/tools/network.js +0 -51
  105. package/lib/tools/pdf.js +0 -49
  106. package/lib/tools/screenshot.js +0 -77
  107. package/lib/tools/snapshot.js +0 -204
  108. package/lib/tools/tabs.js +0 -118
  109. package/lib/tools/testing.js +0 -60
  110. package/lib/tools/tool.js +0 -18
  111. package/lib/tools/utils.js +0 -80
  112. package/lib/tools/vision.js +0 -189
  113. package/lib/tools/wait.js +0 -59
  114. package/lib/tools.js +0 -61
  115. package/lib/transport.js +0 -133
@@ -0,0 +1,277 @@
1
+ import debug from '../_virtual/browser.js';
2
+ import { waitForCompletion, callOnPageNoTrace } from './tools/utils.js';
3
+ import { ManualPromise } from './manualPromise.js';
4
+ import { Tab } from './tab.js';
5
+ import { outputFile } from './config.js';
6
+
7
+ const testDebug = debug("pw:mcp:test");
8
+ class Context {
9
+ tools;
10
+ config;
11
+ _browserContextPromise;
12
+ _browserContextFactory;
13
+ _tabs = [];
14
+ _currentTab;
15
+ _modalStates = [];
16
+ _pendingAction;
17
+ _downloads = [];
18
+ clientVersion;
19
+ constructor(tools, config, browserContextFactory) {
20
+ this.tools = tools;
21
+ this.config = config;
22
+ this._browserContextFactory = browserContextFactory;
23
+ testDebug("create context");
24
+ }
25
+ clientSupportsImages() {
26
+ if (this.config.imageResponses === "allow")
27
+ return true;
28
+ if (this.config.imageResponses === "omit")
29
+ return false;
30
+ return !this.clientVersion?.name.includes("cursor");
31
+ }
32
+ modalStates() {
33
+ return this._modalStates;
34
+ }
35
+ setModalState(modalState, inTab) {
36
+ this._modalStates.push({ ...modalState, tab: inTab });
37
+ }
38
+ clearModalState(modalState) {
39
+ this._modalStates = this._modalStates.filter((state) => state !== modalState);
40
+ }
41
+ modalStatesMarkdown() {
42
+ const result = ["### Modal state"];
43
+ if (this._modalStates.length === 0)
44
+ result.push("- There is no modal state present");
45
+ for (const state of this._modalStates) {
46
+ const tool = this.tools.find((tool2) => tool2.clearsModalState === state.type);
47
+ result.push(`- [${state.description}]: can be handled by the "${tool?.schema.name}" tool`);
48
+ }
49
+ return result;
50
+ }
51
+ tabs() {
52
+ return this._tabs;
53
+ }
54
+ currentTabOrDie() {
55
+ if (!this._currentTab)
56
+ throw new Error("No current snapshot available. Capture a snapshot or navigate to a new location first.");
57
+ return this._currentTab;
58
+ }
59
+ async newTab() {
60
+ const { browserContext } = await this._ensureBrowserContext();
61
+ const page = await browserContext.newPage();
62
+ this._currentTab = this._tabs.find((t) => t.page === page);
63
+ return this._currentTab;
64
+ }
65
+ async selectTab(index) {
66
+ this._currentTab = this._tabs[index - 1];
67
+ await this._currentTab.page.bringToFront();
68
+ }
69
+ async ensureTab() {
70
+ const { browserContext } = await this._ensureBrowserContext();
71
+ if (!this._currentTab)
72
+ await browserContext.newPage();
73
+ return this._currentTab;
74
+ }
75
+ async listTabsMarkdown() {
76
+ if (!this._tabs.length)
77
+ return "### No tabs open";
78
+ const lines = ["### Open tabs"];
79
+ for (let i = 0; i < this._tabs.length; i++) {
80
+ const tab = this._tabs[i];
81
+ const title = await tab.title();
82
+ const url = tab.page.url();
83
+ const current = tab === this._currentTab ? " (current)" : "";
84
+ lines.push(`- ${i + 1}:${current} [${title}] (${url})`);
85
+ }
86
+ return lines.join("\n");
87
+ }
88
+ async closeTab(index) {
89
+ const tab = index === void 0 ? this._currentTab : this._tabs[index - 1];
90
+ await tab?.page.close();
91
+ return await this.listTabsMarkdown();
92
+ }
93
+ async run(tool, params) {
94
+ const toolResult = await tool.handle(this, tool.schema.inputSchema.parse(params || {}));
95
+ const { code, action, waitForNetwork, captureSnapshot, resultOverride } = toolResult;
96
+ const racingAction = action ? () => this._raceAgainstModalDialogs(action) : void 0;
97
+ if (resultOverride)
98
+ return resultOverride;
99
+ if (!this._currentTab) {
100
+ return {
101
+ content: [{
102
+ type: "text",
103
+ text: 'No open pages available. Use the "browser_navigate" tool to navigate to a page first.'
104
+ }]
105
+ };
106
+ }
107
+ const tab = this.currentTabOrDie();
108
+ let actionResult;
109
+ try {
110
+ if (waitForNetwork)
111
+ actionResult = await waitForCompletion(this, tab, async () => racingAction?.()) ?? void 0;
112
+ else
113
+ actionResult = await racingAction?.() ?? void 0;
114
+ } finally {
115
+ if (captureSnapshot && !this._javaScriptBlocked())
116
+ await tab.captureSnapshot();
117
+ }
118
+ const result = [];
119
+ result.push(`- Ran Playwright code:
120
+ \`\`\`js
121
+ ${code.join("\n")}
122
+ \`\`\`
123
+ `);
124
+ if (this.modalStates().length) {
125
+ result.push(...this.modalStatesMarkdown());
126
+ return {
127
+ content: [{
128
+ type: "text",
129
+ text: result.join("\n")
130
+ }]
131
+ };
132
+ }
133
+ if (this._downloads.length) {
134
+ result.push("", "### Downloads");
135
+ for (const entry of this._downloads) {
136
+ if (entry.finished)
137
+ result.push(`- Downloaded file ${entry.download.suggestedFilename()} to ${entry.outputFile}`);
138
+ else
139
+ result.push(`- Downloading file ${entry.download.suggestedFilename()} ...`);
140
+ }
141
+ result.push("");
142
+ }
143
+ if (this.tabs().length > 1)
144
+ result.push(await this.listTabsMarkdown(), "");
145
+ if (this.tabs().length > 1)
146
+ result.push("### Current tab");
147
+ result.push(
148
+ `- Page URL: ${tab.page.url()}`,
149
+ `- Page Title: ${await tab.title()}`
150
+ );
151
+ if (captureSnapshot && tab.hasSnapshot())
152
+ result.push(tab.snapshotOrDie().text());
153
+ const content = actionResult?.content ?? [];
154
+ return {
155
+ content: [
156
+ ...content,
157
+ {
158
+ type: "text",
159
+ text: result.join("\n")
160
+ }
161
+ ]
162
+ };
163
+ }
164
+ async waitForTimeout(time) {
165
+ if (!this._currentTab || this._javaScriptBlocked()) {
166
+ await new Promise((f) => setTimeout(f, time));
167
+ return;
168
+ }
169
+ await callOnPageNoTrace(this._currentTab.page, (page) => {
170
+ return page.evaluate(() => new Promise((f) => setTimeout(f, 1e3)));
171
+ });
172
+ }
173
+ async _raceAgainstModalDialogs(action) {
174
+ this._pendingAction = {
175
+ dialogShown: new ManualPromise()
176
+ };
177
+ let result;
178
+ try {
179
+ await Promise.race([
180
+ action().then((r) => result = r),
181
+ this._pendingAction.dialogShown
182
+ ]);
183
+ } finally {
184
+ this._pendingAction = void 0;
185
+ }
186
+ return result;
187
+ }
188
+ _javaScriptBlocked() {
189
+ return this._modalStates.some((state) => state.type === "dialog");
190
+ }
191
+ dialogShown(tab, dialog) {
192
+ this.setModalState({
193
+ type: "dialog",
194
+ description: `"${dialog.type()}" dialog with message "${dialog.message()}"`,
195
+ dialog
196
+ }, tab);
197
+ this._pendingAction?.dialogShown.resolve();
198
+ }
199
+ async downloadStarted(tab, download) {
200
+ const entry = {
201
+ download,
202
+ finished: false,
203
+ outputFile: await outputFile(this.config, download.suggestedFilename())
204
+ };
205
+ this._downloads.push(entry);
206
+ await download.saveAs(entry.outputFile);
207
+ entry.finished = true;
208
+ }
209
+ _onPageCreated(page) {
210
+ const tab = new Tab(this, page, (tab2) => this._onPageClosed(tab2));
211
+ this._tabs.push(tab);
212
+ if (!this._currentTab)
213
+ this._currentTab = tab;
214
+ }
215
+ _onPageClosed(tab) {
216
+ this._modalStates = this._modalStates.filter((state) => state.tab !== tab);
217
+ const index = this._tabs.indexOf(tab);
218
+ if (index === -1)
219
+ return;
220
+ this._tabs.splice(index, 1);
221
+ if (this._currentTab === tab)
222
+ this._currentTab = this._tabs[Math.min(index, this._tabs.length - 1)];
223
+ if (!this._tabs.length)
224
+ void this.close();
225
+ }
226
+ async close() {
227
+ if (!this._browserContextPromise)
228
+ return;
229
+ testDebug("close context");
230
+ const promise = this._browserContextPromise;
231
+ this._browserContextPromise = void 0;
232
+ await promise.then(async ({ browserContext, close }) => {
233
+ if (this.config.saveTrace)
234
+ await browserContext.tracing.stop();
235
+ await close();
236
+ });
237
+ }
238
+ async _setupRequestInterception(context) {
239
+ if (this.config.network?.allowedOrigins?.length) {
240
+ await context.route("**", (route) => route.abort("blockedbyclient"));
241
+ for (const origin of this.config.network.allowedOrigins)
242
+ await context.route(`*://${origin}/**`, (route) => route.continue());
243
+ }
244
+ if (this.config.network?.blockedOrigins?.length) {
245
+ for (const origin of this.config.network.blockedOrigins)
246
+ await context.route(`*://${origin}/**`, (route) => route.abort("blockedbyclient"));
247
+ }
248
+ }
249
+ _ensureBrowserContext() {
250
+ if (!this._browserContextPromise) {
251
+ this._browserContextPromise = this._setupBrowserContext();
252
+ this._browserContextPromise.catch(() => {
253
+ this._browserContextPromise = void 0;
254
+ });
255
+ }
256
+ return this._browserContextPromise;
257
+ }
258
+ async _setupBrowserContext() {
259
+ const result = await this._browserContextFactory.createContext();
260
+ const { browserContext } = result;
261
+ await this._setupRequestInterception(browserContext);
262
+ for (const page of browserContext.pages())
263
+ this._onPageCreated(page);
264
+ browserContext.on("page", (page) => this._onPageCreated(page));
265
+ if (this.config.saveTrace) {
266
+ await browserContext.tracing.start({
267
+ name: "trace",
268
+ screenshots: false,
269
+ snapshots: true,
270
+ sources: false
271
+ });
272
+ }
273
+ return result;
274
+ }
275
+ }
276
+
277
+ export { Context };
@@ -0,0 +1,20 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+
4
+ function cacheDir() {
5
+ let cacheDirectory;
6
+ if (process.platform === "linux")
7
+ cacheDirectory = process.env.XDG_CACHE_HOME || path.join(os.homedir(), ".cache");
8
+ else if (process.platform === "darwin")
9
+ cacheDirectory = path.join(os.homedir(), "Library", "Caches");
10
+ else if (process.platform === "win32")
11
+ cacheDirectory = process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local");
12
+ else
13
+ throw new Error("Unsupported platform: " + process.platform);
14
+ return path.join(cacheDirectory, "ms-playwright");
15
+ }
16
+ async function userDataDir(browserConfig) {
17
+ return path.join(cacheDir(), "ms-playwright", `mcp-${browserConfig.launchOptions?.channel ?? browserConfig?.browserName}-profile`);
18
+ }
19
+
20
+ export { cacheDir, userDataDir };
@@ -0,0 +1,11 @@
1
+ import { createConnection as createConnection$1 } from './connection.js';
2
+ import { resolveConfig } from './config.js';
3
+ import { contextFactory } from './browserContextFactory.js';
4
+
5
+ async function createConnection(userConfig = {}, contextGetter) {
6
+ const config = await resolveConfig(userConfig);
7
+ const factory = contextFactory(config.browser);
8
+ return createConnection$1(config, factory);
9
+ }
10
+
11
+ export { createConnection };
@@ -0,0 +1,35 @@
1
+ function escapeWithQuotes(text, char = "'") {
2
+ const stringified = JSON.stringify(text);
3
+ const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\"/g, '"');
4
+ if (char === "'")
5
+ return char + escapedText.replace(/[']/g, "\\'") + char;
6
+ if (char === '"')
7
+ return char + escapedText.replace(/["]/g, '\\"') + char;
8
+ if (char === "`")
9
+ return char + escapedText.replace(/[`]/g, "`") + char;
10
+ throw new Error("Invalid escape char");
11
+ }
12
+ function quote(text) {
13
+ return escapeWithQuotes(text, "'");
14
+ }
15
+ function formatObject(value, indent = " ") {
16
+ if (typeof value === "string")
17
+ return quote(value);
18
+ if (Array.isArray(value))
19
+ return `[${value.map((o) => formatObject(o)).join(", ")}]`;
20
+ if (typeof value === "object") {
21
+ const keys = Object.keys(value).filter((key) => value[key] !== void 0).sort();
22
+ if (!keys.length)
23
+ return "{}";
24
+ const tokens = [];
25
+ for (const key of keys)
26
+ tokens.push(`${key}: ${formatObject(value[key])}`);
27
+ return `{
28
+ ${indent}${tokens.join(`,
29
+ ${indent}`)}
30
+ }`;
31
+ }
32
+ return String(value);
33
+ }
34
+
35
+ export { escapeWithQuotes, formatObject, quote };
@@ -0,0 +1,35 @@
1
+ class ManualPromise extends Promise {
2
+ _resolve;
3
+ _reject;
4
+ _isDone;
5
+ constructor() {
6
+ let resolve;
7
+ let reject;
8
+ super((f, r) => {
9
+ resolve = f;
10
+ reject = r;
11
+ });
12
+ this._isDone = false;
13
+ this._resolve = resolve;
14
+ this._reject = reject;
15
+ }
16
+ isDone() {
17
+ return this._isDone;
18
+ }
19
+ resolve(t) {
20
+ this._isDone = true;
21
+ this._resolve(t);
22
+ }
23
+ reject(e) {
24
+ this._isDone = true;
25
+ this._reject(e);
26
+ }
27
+ static get [Symbol.species]() {
28
+ return Promise;
29
+ }
30
+ get [Symbol.toStringTag]() {
31
+ return "ManualPromise";
32
+ }
33
+ }
34
+
35
+ export { ManualPromise };
@@ -0,0 +1,31 @@
1
+ import { callOnPageNoTrace } from './tools/utils.js';
2
+
3
+ class PageSnapshot {
4
+ _page;
5
+ _text;
6
+ constructor(page) {
7
+ this._page = page;
8
+ }
9
+ static async create(page) {
10
+ const snapshot = new PageSnapshot(page);
11
+ await snapshot._build();
12
+ return snapshot;
13
+ }
14
+ text() {
15
+ return this._text;
16
+ }
17
+ async _build() {
18
+ const snapshot = await callOnPageNoTrace(this._page, (page) => page._snapshotForAI());
19
+ this._text = [
20
+ `- Page Snapshot`,
21
+ "```yaml",
22
+ snapshot,
23
+ "```"
24
+ ].join("\n");
25
+ }
26
+ refLocator(params) {
27
+ return this._page.locator(`aria-ref=${params.ref}`).describe(params.element);
28
+ }
29
+ }
30
+
31
+ export { PageSnapshot };
@@ -0,0 +1,87 @@
1
+ import { PageSnapshot } from './pageSnapshot.js';
2
+ import { callOnPageNoTrace } from './tools/utils.js';
3
+
4
+ class Tab {
5
+ context;
6
+ page;
7
+ _consoleMessages = [];
8
+ _requests = /* @__PURE__ */ new Map();
9
+ _snapshot;
10
+ _onPageClose;
11
+ constructor(context, page, onPageClose) {
12
+ this.context = context;
13
+ this.page = page;
14
+ this._onPageClose = onPageClose;
15
+ page.on("console", (event) => this._consoleMessages.push(event));
16
+ page.on("request", (request) => this._requests.set(request, null));
17
+ page.on("response", (response) => this._requests.set(response.request(), response));
18
+ page.on("close", () => this._onClose());
19
+ page.on("filechooser", (chooser) => {
20
+ this.context.setModalState({
21
+ type: "fileChooser",
22
+ description: "File chooser",
23
+ fileChooser: chooser
24
+ }, this);
25
+ });
26
+ page.on("dialog", (dialog) => this.context.dialogShown(this, dialog));
27
+ page.on("download", (download) => {
28
+ void this.context.downloadStarted(this, download);
29
+ });
30
+ page.setDefaultNavigationTimeout(6e4);
31
+ page.setDefaultTimeout(5e3);
32
+ }
33
+ _clearCollectedArtifacts() {
34
+ this._consoleMessages.length = 0;
35
+ this._requests.clear();
36
+ }
37
+ _onClose() {
38
+ this._clearCollectedArtifacts();
39
+ this._onPageClose(this);
40
+ }
41
+ async title() {
42
+ return await callOnPageNoTrace(this.page, (page) => page.title());
43
+ }
44
+ async waitForLoadState(state, options) {
45
+ await callOnPageNoTrace(this.page, (page) => page.waitForLoadState(state, options).catch(() => {
46
+ }));
47
+ }
48
+ async navigate(url) {
49
+ this._clearCollectedArtifacts();
50
+ const downloadEvent = callOnPageNoTrace(this.page, (page) => page.waitForEvent("download").catch(() => {
51
+ }));
52
+ try {
53
+ await this.page.goto(url, { waitUntil: "domcontentloaded" });
54
+ } catch (_e) {
55
+ const e = _e;
56
+ const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
57
+ if (!mightBeDownload)
58
+ throw e;
59
+ const download = await Promise.race([
60
+ downloadEvent,
61
+ new Promise((resolve) => setTimeout(resolve, 1e3))
62
+ ]);
63
+ if (!download)
64
+ throw e;
65
+ }
66
+ await this.waitForLoadState("load", { timeout: 5e3 });
67
+ }
68
+ hasSnapshot() {
69
+ return !!this._snapshot;
70
+ }
71
+ snapshotOrDie() {
72
+ if (!this._snapshot)
73
+ throw new Error("No snapshot available");
74
+ return this._snapshot;
75
+ }
76
+ consoleMessages() {
77
+ return this._consoleMessages;
78
+ }
79
+ requests() {
80
+ return this._requests;
81
+ }
82
+ async captureSnapshot() {
83
+ this._snapshot = await PageSnapshot.create(this.page);
84
+ }
85
+ }
86
+
87
+ export { Tab };
@@ -0,0 +1,56 @@
1
+ import { z } from 'zod';
2
+ import { defineTool } from './tool.js';
3
+
4
+ const close = defineTool({
5
+ capability: "core",
6
+ schema: {
7
+ name: "browser_close",
8
+ title: "Close browser",
9
+ description: "Close the page",
10
+ inputSchema: z.object({}),
11
+ type: "readOnly"
12
+ },
13
+ handle: async (context) => {
14
+ await context.close();
15
+ return {
16
+ code: [`await page.close()`],
17
+ captureSnapshot: false,
18
+ waitForNetwork: false
19
+ };
20
+ }
21
+ });
22
+ const resize = (captureSnapshot) => defineTool({
23
+ capability: "core",
24
+ schema: {
25
+ name: "browser_resize",
26
+ title: "Resize browser window",
27
+ description: "Resize the browser window",
28
+ inputSchema: z.object({
29
+ width: z.coerce.number().describe("Width of the browser window"),
30
+ height: z.coerce.number().describe("Height of the browser window")
31
+ }),
32
+ type: "readOnly"
33
+ },
34
+ handle: async (context, params) => {
35
+ const tab = context.currentTabOrDie();
36
+ const code = [
37
+ `// Resize browser window to ${params.width}x${params.height}`,
38
+ `await page.setViewportSize({ width: ${params.width}, height: ${params.height} });`
39
+ ];
40
+ const action = async () => {
41
+ await tab.page.setViewportSize({ width: params.width, height: params.height });
42
+ };
43
+ return {
44
+ code,
45
+ action,
46
+ captureSnapshot,
47
+ waitForNetwork: true
48
+ };
49
+ }
50
+ });
51
+ const common = (captureSnapshot) => [
52
+ close,
53
+ resize(captureSnapshot)
54
+ ];
55
+
56
+ export { common as default };
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+ import { defineTool } from './tool.js';
3
+
4
+ const console = defineTool({
5
+ capability: "core",
6
+ schema: {
7
+ name: "browser_console_messages",
8
+ title: "Get console messages",
9
+ description: "Returns all console messages",
10
+ inputSchema: z.object({}),
11
+ type: "readOnly"
12
+ },
13
+ handle: async (context) => {
14
+ const messages = context.currentTabOrDie().consoleMessages();
15
+ const log = messages.map((message) => `[${message.type().toUpperCase()}] ${message.text()}`).join("\n");
16
+ return {
17
+ code: [`// <internal code to get console messages>`],
18
+ action: async () => {
19
+ return {
20
+ content: [{ type: "text", text: log }]
21
+ };
22
+ },
23
+ captureSnapshot: false,
24
+ waitForNetwork: false
25
+ };
26
+ }
27
+ });
28
+ const console$1 = [
29
+ console
30
+ ];
31
+
32
+ export { console$1 as default };
@@ -0,0 +1,40 @@
1
+ import { z } from 'zod';
2
+ import { defineTool } from './tool.js';
3
+
4
+ const handleDialog = (captureSnapshot) => defineTool({
5
+ capability: "core",
6
+ schema: {
7
+ name: "browser_handle_dialog",
8
+ title: "Handle a dialog",
9
+ description: "Handle a dialog",
10
+ inputSchema: z.object({
11
+ accept: z.coerce.boolean().describe("Whether to accept the dialog."),
12
+ promptText: z.string().optional().describe("The text of the prompt in case of a prompt dialog.")
13
+ }),
14
+ type: "destructive"
15
+ },
16
+ handle: async (context, params) => {
17
+ const dialogState = context.modalStates().find((state) => state.type === "dialog");
18
+ if (!dialogState)
19
+ throw new Error("No dialog visible");
20
+ if (params.accept)
21
+ await dialogState.dialog.accept(params.promptText);
22
+ else
23
+ await dialogState.dialog.dismiss();
24
+ context.clearModalState(dialogState);
25
+ const code = [
26
+ `// <internal code to handle "${dialogState.dialog.type()}" dialog>`
27
+ ];
28
+ return {
29
+ code,
30
+ captureSnapshot,
31
+ waitForNetwork: false
32
+ };
33
+ },
34
+ clearsModalState: "dialog"
35
+ });
36
+ const dialogs = (captureSnapshot) => [
37
+ handleDialog(captureSnapshot)
38
+ ];
39
+
40
+ export { dialogs as default };
@@ -0,0 +1,39 @@
1
+ import { z } from 'zod';
2
+ import { defineTool } from './tool.js';
3
+
4
+ const uploadFile = (captureSnapshot) => defineTool({
5
+ capability: "files",
6
+ schema: {
7
+ name: "browser_file_upload",
8
+ title: "Upload files",
9
+ description: "Upload one or multiple files",
10
+ inputSchema: z.object({
11
+ paths: z.array(z.string()).describe("The absolute paths to the files to upload. Can be a single file or multiple files.")
12
+ }),
13
+ type: "destructive"
14
+ },
15
+ handle: async (context, params) => {
16
+ const modalState = context.modalStates().find((state) => state.type === "fileChooser");
17
+ if (!modalState)
18
+ throw new Error("No file chooser visible");
19
+ const code = [
20
+ `// <internal code to chose files ${params.paths.join(", ")}`
21
+ ];
22
+ const action = async () => {
23
+ await modalState.fileChooser.setFiles(params.paths);
24
+ context.clearModalState(modalState);
25
+ };
26
+ return {
27
+ code,
28
+ action,
29
+ captureSnapshot,
30
+ waitForNetwork: true
31
+ };
32
+ },
33
+ clearsModalState: "fileChooser"
34
+ });
35
+ const files = (captureSnapshot) => [
36
+ uploadFile(captureSnapshot)
37
+ ];
38
+
39
+ export { files as default };