@szymonrybczak/playwright-mcp 0.0.16 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/cloudflare/package.json.js +1 -1
- package/lib/cjs/src/browserContextFactory.js +8 -132
- package/lib/cjs/src/context.js +5 -47
- package/lib/cjs/src/tab.js +41 -15
- package/lib/cjs/src/tools/navigate.js +28 -21
- package/lib/esm/cloudflare/package.json.js +1 -1
- package/lib/esm/src/browserContextFactory.js +8 -132
- package/lib/esm/src/context.js +5 -47
- package/lib/esm/src/tab.js +41 -15
- package/lib/esm/src/tools/navigate.js +28 -21
- package/package.json +1 -1
|
@@ -31,25 +31,14 @@ const playwright__namespace = /*#__PURE__*/_interopNamespaceDefault(playwright);
|
|
|
31
31
|
|
|
32
32
|
const testDebug = debugStub.default("pw:mcp:test");
|
|
33
33
|
function contextFactory(browserConfig) {
|
|
34
|
-
|
|
35
|
-
console.log("[contextFactory] browserConfig:", JSON.stringify(browserConfig, null, 2));
|
|
36
|
-
if (browserConfig.remoteEndpoint) {
|
|
37
|
-
console.log("[contextFactory] Using RemoteContextFactory");
|
|
34
|
+
if (browserConfig.remoteEndpoint)
|
|
38
35
|
return new RemoteContextFactory(browserConfig);
|
|
39
|
-
|
|
40
|
-
if (browserConfig.cdpEndpoint) {
|
|
41
|
-
console.log("[contextFactory] Using CdpContextFactory");
|
|
36
|
+
if (browserConfig.cdpEndpoint)
|
|
42
37
|
return new CdpContextFactory(browserConfig);
|
|
43
|
-
|
|
44
|
-
if (browserConfig.isolated) {
|
|
45
|
-
console.log("[contextFactory] Using IsolatedContextFactory");
|
|
38
|
+
if (browserConfig.isolated)
|
|
46
39
|
return new IsolatedContextFactory(browserConfig);
|
|
47
|
-
|
|
48
|
-
if (browserConfig.browserAgent) {
|
|
49
|
-
console.log("[contextFactory] Using BrowserServerContextFactory");
|
|
40
|
+
if (browserConfig.browserAgent)
|
|
50
41
|
return new BrowserServerContextFactory(browserConfig);
|
|
51
|
-
}
|
|
52
|
-
console.log("[contextFactory] Using PersistentContextFactory");
|
|
53
42
|
return new PersistentContextFactory(browserConfig);
|
|
54
43
|
}
|
|
55
44
|
class BaseContextFactory {
|
|
@@ -59,26 +48,17 @@ class BaseContextFactory {
|
|
|
59
48
|
constructor(name, browserConfig) {
|
|
60
49
|
this.name = name;
|
|
61
50
|
this.browserConfig = browserConfig;
|
|
62
|
-
console.log(`[BaseContextFactory] Created factory: ${name}`);
|
|
63
51
|
}
|
|
64
52
|
async _obtainBrowser() {
|
|
65
|
-
|
|
66
|
-
console.log(`[BaseContextFactory._obtainBrowser] _browserPromise exists:`, !!this._browserPromise);
|
|
67
|
-
if (this._browserPromise) {
|
|
68
|
-
console.log(`[BaseContextFactory._obtainBrowser] Returning existing promise`);
|
|
53
|
+
if (this._browserPromise)
|
|
69
54
|
return this._browserPromise;
|
|
70
|
-
}
|
|
71
55
|
testDebug(`obtain browser (${this.name})`);
|
|
72
|
-
console.log(`[BaseContextFactory._obtainBrowser] Calling _doObtainBrowser...`);
|
|
73
56
|
this._browserPromise = this._doObtainBrowser();
|
|
74
57
|
void this._browserPromise.then((browser) => {
|
|
75
|
-
console.log(`[BaseContextFactory._obtainBrowser] Browser obtained successfully`);
|
|
76
58
|
browser.on("disconnected", () => {
|
|
77
|
-
console.log(`[BaseContextFactory._obtainBrowser] Browser disconnected`);
|
|
78
59
|
this._browserPromise = void 0;
|
|
79
60
|
});
|
|
80
|
-
}).catch((
|
|
81
|
-
console.log(`[BaseContextFactory._obtainBrowser] Browser promise rejected:`, err.message);
|
|
61
|
+
}).catch(() => {
|
|
82
62
|
this._browserPromise = void 0;
|
|
83
63
|
});
|
|
84
64
|
return this._browserPromise;
|
|
@@ -87,22 +67,15 @@ class BaseContextFactory {
|
|
|
87
67
|
throw new Error("Not implemented");
|
|
88
68
|
}
|
|
89
69
|
async createContext() {
|
|
90
|
-
console.log(`[BaseContextFactory.createContext] START (${this.name})`);
|
|
91
70
|
testDebug(`create browser context (${this.name})`);
|
|
92
|
-
console.log(`[BaseContextFactory.createContext] Calling _obtainBrowser...`);
|
|
93
71
|
const browser = await this._obtainBrowser();
|
|
94
|
-
console.log(`[BaseContextFactory.createContext] Got browser`);
|
|
95
|
-
console.log(`[BaseContextFactory.createContext] Calling _doCreateContext...`);
|
|
96
72
|
const browserContext = await this._doCreateContext(browser);
|
|
97
|
-
console.log(`[BaseContextFactory.createContext] Got browserContext`);
|
|
98
|
-
console.log(`[BaseContextFactory.createContext] END`);
|
|
99
73
|
return { browserContext, close: () => this._closeBrowserContext(browserContext, browser) };
|
|
100
74
|
}
|
|
101
75
|
async _doCreateContext(browser) {
|
|
102
76
|
throw new Error("Not implemented");
|
|
103
77
|
}
|
|
104
78
|
async _closeBrowserContext(browserContext, browser) {
|
|
105
|
-
console.log(`[BaseContextFactory._closeBrowserContext] START (${this.name})`);
|
|
106
79
|
testDebug(`close browser context (${this.name})`);
|
|
107
80
|
if (browser.contexts().length === 1)
|
|
108
81
|
this._browserPromise = void 0;
|
|
@@ -113,7 +86,6 @@ class BaseContextFactory {
|
|
|
113
86
|
await browser.close().catch(() => {
|
|
114
87
|
});
|
|
115
88
|
}
|
|
116
|
-
console.log(`[BaseContextFactory._closeBrowserContext] END`);
|
|
117
89
|
}
|
|
118
90
|
}
|
|
119
91
|
class IsolatedContextFactory extends BaseContextFactory {
|
|
@@ -142,106 +114,10 @@ class CdpContextFactory extends BaseContextFactory {
|
|
|
142
114
|
super("cdp", browserConfig);
|
|
143
115
|
}
|
|
144
116
|
async _doObtainBrowser() {
|
|
145
|
-
|
|
146
|
-
console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint:", this.browserConfig.cdpEndpoint);
|
|
147
|
-
console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint type:", typeof this.browserConfig.cdpEndpoint);
|
|
148
|
-
const endpoint = this.browserConfig.cdpEndpoint;
|
|
149
|
-
const isCloudfareFetcher = typeof endpoint !== "string";
|
|
150
|
-
console.log("[CdpContextFactory._doObtainBrowser] isCloudfareFetcher:", isCloudfareFetcher);
|
|
151
|
-
try {
|
|
152
|
-
let browser;
|
|
153
|
-
if (isCloudfareFetcher) {
|
|
154
|
-
const fetcher = endpoint;
|
|
155
|
-
console.log("[CdpContextFactory._doObtainBrowser] Fetcher detected, using connect() method...");
|
|
156
|
-
const connectUrls = [
|
|
157
|
-
"https://cloudflare",
|
|
158
|
-
"https://cloudflare/",
|
|
159
|
-
"wss://cloudflare"
|
|
160
|
-
];
|
|
161
|
-
let socket = null;
|
|
162
|
-
let successUrl = "";
|
|
163
|
-
for (const url of connectUrls) {
|
|
164
|
-
try {
|
|
165
|
-
console.log(`[CdpContextFactory._doObtainBrowser] Trying fetcher.connect("${url}")...`);
|
|
166
|
-
const connection = await fetcher.connect(url);
|
|
167
|
-
console.log("[CdpContextFactory._doObtainBrowser] connect() returned:", typeof connection);
|
|
168
|
-
console.log("[CdpContextFactory._doObtainBrowser] connection keys:", Object.keys(connection || {}));
|
|
169
|
-
console.log("[CdpContextFactory._doObtainBrowser] connection.socket:", typeof connection?.socket);
|
|
170
|
-
if (connection && typeof connection === "object") {
|
|
171
|
-
if ("accept" in connection && typeof connection.accept === "function") {
|
|
172
|
-
console.log("[CdpContextFactory._doObtainBrowser] Connection has accept(), calling it...");
|
|
173
|
-
connection.accept();
|
|
174
|
-
}
|
|
175
|
-
if ("send" in connection && "close" in connection) {
|
|
176
|
-
socket = connection;
|
|
177
|
-
successUrl = url;
|
|
178
|
-
console.log("[CdpContextFactory._doObtainBrowser] Got WebSocket-like object directly");
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
if ("socket" in connection) {
|
|
182
|
-
socket = connection.socket;
|
|
183
|
-
successUrl = url;
|
|
184
|
-
console.log("[CdpContextFactory._doObtainBrowser] Got socket from connection.socket");
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
if ("webSocket" in connection) {
|
|
188
|
-
socket = connection.webSocket;
|
|
189
|
-
if (socket && "accept" in socket) {
|
|
190
|
-
socket.accept();
|
|
191
|
-
}
|
|
192
|
-
successUrl = url;
|
|
193
|
-
console.log("[CdpContextFactory._doObtainBrowser] Got socket from connection.webSocket");
|
|
194
|
-
break;
|
|
195
|
-
}
|
|
196
|
-
console.log("[CdpContextFactory._doObtainBrowser] Connection structure:", JSON.stringify(Object.getOwnPropertyNames(connection)));
|
|
197
|
-
const proto = Object.getPrototypeOf(connection);
|
|
198
|
-
if (proto) {
|
|
199
|
-
console.log("[CdpContextFactory._doObtainBrowser] Connection prototype:", Object.getOwnPropertyNames(proto));
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
} catch (e) {
|
|
203
|
-
console.log(`[CdpContextFactory._doObtainBrowser] connect("${url}") error:`, e.message);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (!socket) {
|
|
207
|
-
throw new Error("Could not establish WebSocket via Fetcher.connect(). Check logs above.");
|
|
208
|
-
}
|
|
209
|
-
console.log(`[CdpContextFactory._doObtainBrowser] WebSocket obtained from ${successUrl}`);
|
|
210
|
-
console.log("[CdpContextFactory._doObtainBrowser] Socket type:", typeof socket);
|
|
211
|
-
console.log("[CdpContextFactory._doObtainBrowser] Socket readyState:", socket.readyState);
|
|
212
|
-
console.log("[CdpContextFactory._doObtainBrowser] Socket methods:", Object.getOwnPropertyNames(Object.getPrototypeOf(socket)));
|
|
213
|
-
console.log("[CdpContextFactory._doObtainBrowser] playwright.chromium methods:", Object.keys(playwright__namespace.chromium));
|
|
214
|
-
if (typeof playwright__namespace.chromium.connectOverCDPWithSocket === "function") {
|
|
215
|
-
console.log("[CdpContextFactory._doObtainBrowser] Using connectOverCDPWithSocket...");
|
|
216
|
-
browser = await playwright__namespace.chromium.connectOverCDPWithSocket(socket);
|
|
217
|
-
} else if (typeof playwright__namespace.chromium.connectWithWebSocket === "function") {
|
|
218
|
-
console.log("[CdpContextFactory._doObtainBrowser] Using connectWithWebSocket...");
|
|
219
|
-
browser = await playwright__namespace.chromium.connectWithWebSocket(socket);
|
|
220
|
-
} else {
|
|
221
|
-
throw new Error(`WebSocket connection established but patchright needs a method to accept raw WebSocket for CDP. Available methods: ${Object.keys(playwright__namespace.chromium).join(", ")}`);
|
|
222
|
-
}
|
|
223
|
-
} else {
|
|
224
|
-
console.log("[CdpContextFactory._doObtainBrowser] Using chromium.connectOverCDP() for CDP URL...");
|
|
225
|
-
browser = await playwright__namespace.chromium.connectOverCDP(endpoint);
|
|
226
|
-
}
|
|
227
|
-
console.log("[CdpContextFactory._doObtainBrowser] Connection completed");
|
|
228
|
-
return browser;
|
|
229
|
-
} catch (err) {
|
|
230
|
-
console.log("[CdpContextFactory._doObtainBrowser] ERROR:", err.message, err.stack);
|
|
231
|
-
throw err;
|
|
232
|
-
}
|
|
117
|
+
return playwright__namespace.chromium.connectOverCDP(this.browserConfig.cdpEndpoint);
|
|
233
118
|
}
|
|
234
119
|
async _doCreateContext(browser) {
|
|
235
|
-
|
|
236
|
-
console.log("[CdpContextFactory._doCreateContext] isolated:", this.browserConfig.isolated);
|
|
237
|
-
try {
|
|
238
|
-
const ctx = this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
|
|
239
|
-
console.log("[CdpContextFactory._doCreateContext] Got context");
|
|
240
|
-
return ctx;
|
|
241
|
-
} catch (err) {
|
|
242
|
-
console.log("[CdpContextFactory._doCreateContext] ERROR:", err.message, err.stack);
|
|
243
|
-
throw err;
|
|
244
|
-
}
|
|
120
|
+
return this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
|
|
245
121
|
}
|
|
246
122
|
}
|
|
247
123
|
class RemoteContextFactory extends BaseContextFactory {
|
package/lib/cjs/src/context.js
CHANGED
|
@@ -70,29 +70,9 @@ class Context {
|
|
|
70
70
|
await this._currentTab.page.bringToFront();
|
|
71
71
|
}
|
|
72
72
|
async ensureTab() {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let browserContext;
|
|
77
|
-
try {
|
|
78
|
-
const result = await this._ensureBrowserContext();
|
|
79
|
-
browserContext = result.browserContext;
|
|
80
|
-
console.log("[Context.ensureTab] _ensureBrowserContext() completed");
|
|
81
|
-
} catch (err) {
|
|
82
|
-
console.log("[Context.ensureTab] ERROR in _ensureBrowserContext():", err.message, err.stack);
|
|
83
|
-
throw err;
|
|
84
|
-
}
|
|
85
|
-
if (!this._currentTab) {
|
|
86
|
-
console.log("[Context.ensureTab] No current tab, creating new page...");
|
|
87
|
-
try {
|
|
88
|
-
await browserContext.newPage();
|
|
89
|
-
console.log("[Context.ensureTab] New page created");
|
|
90
|
-
} catch (err) {
|
|
91
|
-
console.log("[Context.ensureTab] ERROR creating new page:", err.message, err.stack);
|
|
92
|
-
throw err;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
console.log("[Context.ensureTab] END, returning tab");
|
|
73
|
+
const { browserContext } = await this._ensureBrowserContext();
|
|
74
|
+
if (!this._currentTab)
|
|
75
|
+
await browserContext.newPage();
|
|
96
76
|
return this._currentTab;
|
|
97
77
|
}
|
|
98
78
|
async listTabsMarkdown() {
|
|
@@ -269,51 +249,29 @@ ${code.join("\n")}
|
|
|
269
249
|
}
|
|
270
250
|
}
|
|
271
251
|
_ensureBrowserContext() {
|
|
272
|
-
console.log("[Context._ensureBrowserContext] START");
|
|
273
|
-
console.log("[Context._ensureBrowserContext] _browserContextPromise exists:", !!this._browserContextPromise);
|
|
274
252
|
if (!this._browserContextPromise) {
|
|
275
|
-
console.log("[Context._ensureBrowserContext] Creating new browser context promise...");
|
|
276
253
|
this._browserContextPromise = this._setupBrowserContext();
|
|
277
|
-
this._browserContextPromise.catch((
|
|
278
|
-
console.log("[Context._ensureBrowserContext] Promise rejected:", err.message);
|
|
254
|
+
this._browserContextPromise.catch(() => {
|
|
279
255
|
this._browserContextPromise = void 0;
|
|
280
256
|
});
|
|
281
257
|
}
|
|
282
|
-
console.log("[Context._ensureBrowserContext] END, returning promise");
|
|
283
258
|
return this._browserContextPromise;
|
|
284
259
|
}
|
|
285
260
|
async _setupBrowserContext() {
|
|
286
|
-
|
|
287
|
-
console.log("[Context._setupBrowserContext] Calling _browserContextFactory.createContext()...");
|
|
288
|
-
let result;
|
|
289
|
-
try {
|
|
290
|
-
result = await this._browserContextFactory.createContext();
|
|
291
|
-
console.log("[Context._setupBrowserContext] createContext() completed");
|
|
292
|
-
} catch (err) {
|
|
293
|
-
console.log("[Context._setupBrowserContext] ERROR in createContext():", err.message, err.stack);
|
|
294
|
-
throw err;
|
|
295
|
-
}
|
|
261
|
+
const result = await this._browserContextFactory.createContext();
|
|
296
262
|
const { browserContext } = result;
|
|
297
|
-
console.log("[Context._setupBrowserContext] Got browserContext");
|
|
298
|
-
console.log("[Context._setupBrowserContext] Setting up request interception...");
|
|
299
263
|
await this._setupRequestInterception(browserContext);
|
|
300
|
-
console.log("[Context._setupBrowserContext] Request interception done");
|
|
301
|
-
console.log("[Context._setupBrowserContext] Processing existing pages...");
|
|
302
264
|
for (const page of browserContext.pages())
|
|
303
265
|
this._onPageCreated(page);
|
|
304
266
|
browserContext.on("page", (page) => this._onPageCreated(page));
|
|
305
|
-
console.log("[Context._setupBrowserContext] Pages processed");
|
|
306
267
|
if (this.config.saveTrace) {
|
|
307
|
-
console.log("[Context._setupBrowserContext] Starting tracing...");
|
|
308
268
|
await browserContext.tracing.start({
|
|
309
269
|
name: "trace",
|
|
310
270
|
screenshots: false,
|
|
311
271
|
snapshots: true,
|
|
312
272
|
sources: false
|
|
313
273
|
});
|
|
314
|
-
console.log("[Context._setupBrowserContext] Tracing started");
|
|
315
274
|
}
|
|
316
|
-
console.log("[Context._setupBrowserContext] END");
|
|
317
275
|
return result;
|
|
318
276
|
}
|
|
319
277
|
}
|
package/lib/cjs/src/tab.js
CHANGED
|
@@ -50,40 +50,66 @@ class Tab {
|
|
|
50
50
|
}));
|
|
51
51
|
}
|
|
52
52
|
async navigate(url) {
|
|
53
|
-
|
|
54
|
-
console.log(
|
|
53
|
+
const navStartTime = Date.now();
|
|
54
|
+
console.log(`[Tab.navigate] --------- TAB NAVIGATION START ---------`);
|
|
55
|
+
console.log(`[Tab.navigate] URL: ${url}`);
|
|
56
|
+
console.log(`[Tab.navigate] Current page URL: ${this.page.url()}`);
|
|
57
|
+
console.log(`[Tab.navigate] Page is closed: ${this.page.isClosed()}`);
|
|
58
|
+
console.log(`[Tab.navigate] Clearing collected artifacts...`);
|
|
55
59
|
this._clearCollectedArtifacts();
|
|
56
|
-
console.log(
|
|
57
|
-
console.log(
|
|
60
|
+
console.log(`[Tab.navigate] Artifacts cleared. Console messages: ${this._consoleMessages.length}, Requests: ${this._requests.size}`);
|
|
61
|
+
console.log(`[Tab.navigate] Setting up download event listener...`);
|
|
58
62
|
const downloadEvent = utils.callOnPageNoTrace(this.page, (page) => page.waitForEvent("download").catch(() => {
|
|
59
63
|
}));
|
|
60
|
-
console.log(
|
|
64
|
+
console.log(`[Tab.navigate] Download event listener set up`);
|
|
65
|
+
console.log(`[Tab.navigate] Calling page.goto() with waitUntil: 'domcontentloaded'`);
|
|
66
|
+
const gotoStartTime = Date.now();
|
|
61
67
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
console.log(
|
|
68
|
+
const response = await this.page.goto(url, { waitUntil: "domcontentloaded" });
|
|
69
|
+
const gotoTime = Date.now() - gotoStartTime;
|
|
70
|
+
console.log(`[Tab.navigate] page.goto() completed in ${gotoTime}ms`);
|
|
71
|
+
if (response) {
|
|
72
|
+
console.log(`[Tab.navigate] Response status: ${response.status()}`);
|
|
73
|
+
console.log(`[Tab.navigate] Response URL: ${response.url()}`);
|
|
74
|
+
console.log(`[Tab.navigate] Response headers:`, JSON.stringify(response.headers(), null, 2));
|
|
75
|
+
} else {
|
|
76
|
+
console.log(`[Tab.navigate] Response is null (might be same-document navigation or cached)`);
|
|
77
|
+
}
|
|
65
78
|
} catch (_e) {
|
|
66
79
|
const e = _e;
|
|
67
|
-
|
|
80
|
+
const gotoTime = Date.now() - gotoStartTime;
|
|
81
|
+
console.log(`[Tab.navigate] page.goto() threw after ${gotoTime}ms`);
|
|
82
|
+
console.log(`[Tab.navigate] Error name: ${e.name}`);
|
|
83
|
+
console.log(`[Tab.navigate] Error message: ${e.message}`);
|
|
68
84
|
const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
|
|
85
|
+
console.log(`[Tab.navigate] Might be download: ${mightBeDownload}`);
|
|
69
86
|
if (!mightBeDownload) {
|
|
70
|
-
console.
|
|
87
|
+
console.error(`[Tab.navigate] ERROR: Not a download, rethrowing error`);
|
|
88
|
+
console.error(`[Tab.navigate] Error stack:`, e.stack);
|
|
71
89
|
throw e;
|
|
72
90
|
}
|
|
73
|
-
console.log(
|
|
91
|
+
console.log(`[Tab.navigate] Waiting for potential download event (up to 1000ms)...`);
|
|
92
|
+
const downloadWaitStart = Date.now();
|
|
74
93
|
const download = await Promise.race([
|
|
75
94
|
downloadEvent,
|
|
76
95
|
new Promise((resolve) => setTimeout(resolve, 1e3))
|
|
77
96
|
]);
|
|
97
|
+
console.log(`[Tab.navigate] Download wait completed in ${Date.now() - downloadWaitStart}ms`);
|
|
98
|
+
console.log(`[Tab.navigate] Download detected: ${!!download}`);
|
|
78
99
|
if (!download) {
|
|
79
|
-
console.
|
|
100
|
+
console.error(`[Tab.navigate] ERROR: No download detected, rethrowing original error`);
|
|
80
101
|
throw e;
|
|
81
102
|
}
|
|
82
|
-
console.log(
|
|
103
|
+
console.log(`[Tab.navigate] Download detected, continuing without error`);
|
|
83
104
|
}
|
|
84
|
-
console.log(
|
|
105
|
+
console.log(`[Tab.navigate] Waiting for 'load' state (timeout: 5000ms)...`);
|
|
106
|
+
const loadWaitStart = Date.now();
|
|
85
107
|
await this.waitForLoadState("load", { timeout: 5e3 });
|
|
86
|
-
console.log(
|
|
108
|
+
console.log(`[Tab.navigate] 'load' state wait completed in ${Date.now() - loadWaitStart}ms`);
|
|
109
|
+
console.log(`[Tab.navigate] Final page URL: ${this.page.url()}`);
|
|
110
|
+
console.log(`[Tab.navigate] Final page title: ${await this.page.title().catch(() => "N/A")}`);
|
|
111
|
+
console.log(`[Tab.navigate] Total navigation time: ${Date.now() - navStartTime}ms`);
|
|
112
|
+
console.log(`[Tab.navigate] --------- TAB NAVIGATION END ---------`);
|
|
87
113
|
}
|
|
88
114
|
hasSnapshot() {
|
|
89
115
|
return !!this._snapshot;
|
|
@@ -5,9 +5,6 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
|
|
|
5
5
|
const zod = require('zod');
|
|
6
6
|
const tool = require('./tool.js');
|
|
7
7
|
|
|
8
|
-
const log = (msg, data) => {
|
|
9
|
-
console.log(`[browser_navigate] ${msg}`, data !== void 0 ? JSON.stringify(data, null, 2) : "");
|
|
10
|
-
};
|
|
11
8
|
const navigate = (captureSnapshot) => tool.defineTool({
|
|
12
9
|
capability: "core",
|
|
13
10
|
schema: {
|
|
@@ -20,39 +17,49 @@ const navigate = (captureSnapshot) => tool.defineTool({
|
|
|
20
17
|
type: "destructive"
|
|
21
18
|
},
|
|
22
19
|
handle: async (context, params) => {
|
|
23
|
-
|
|
24
|
-
log(
|
|
25
|
-
log(
|
|
26
|
-
log(
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
console.log(`[browser_navigate] ========== NAVIGATION START ==========`);
|
|
22
|
+
console.log(`[browser_navigate] Target URL: ${params.url}`);
|
|
23
|
+
console.log(`[browser_navigate] Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
24
|
+
console.log(`[browser_navigate] captureSnapshot setting: ${captureSnapshot}`);
|
|
25
|
+
console.log(`[browser_navigate] Ensuring tab is available...`);
|
|
26
|
+
const ensureTabStart = Date.now();
|
|
27
27
|
let tab;
|
|
28
28
|
try {
|
|
29
29
|
tab = await context.ensureTab();
|
|
30
|
-
log(
|
|
31
|
-
log(
|
|
32
|
-
} catch (
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
console.log(`[browser_navigate] Tab ensured successfully in ${Date.now() - ensureTabStart}ms`);
|
|
31
|
+
console.log(`[browser_navigate] Current page URL before navigation: ${tab.page.url()}`);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(`[browser_navigate] ERROR: Failed to ensure tab after ${Date.now() - ensureTabStart}ms`);
|
|
34
|
+
console.error(`[browser_navigate] Error details:`, error);
|
|
35
|
+
throw error;
|
|
35
36
|
}
|
|
36
|
-
log(`
|
|
37
|
+
console.log(`[browser_navigate] Starting navigation to: ${params.url}`);
|
|
38
|
+
const navigationStart = Date.now();
|
|
37
39
|
try {
|
|
38
40
|
await tab.navigate(params.url);
|
|
39
|
-
log(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
console.log(`[browser_navigate] Navigation completed successfully in ${Date.now() - navigationStart}ms`);
|
|
42
|
+
console.log(`[browser_navigate] Current page URL after navigation: ${tab.page.url()}`);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error(`[browser_navigate] ERROR: Navigation failed after ${Date.now() - navigationStart}ms`);
|
|
45
|
+
console.error(`[browser_navigate] Error type: ${error.constructor.name}`);
|
|
46
|
+
console.error(`[browser_navigate] Error message: ${error.message}`);
|
|
47
|
+
console.error(`[browser_navigate] Error stack:`, error.stack);
|
|
48
|
+
throw error;
|
|
43
49
|
}
|
|
44
50
|
const code = [
|
|
45
51
|
`// Navigate to ${params.url}`,
|
|
46
52
|
`await page.goto('${params.url}');`
|
|
47
53
|
];
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
const totalTime = Date.now() - startTime;
|
|
55
|
+
console.log(`[browser_navigate] Total execution time: ${totalTime}ms`);
|
|
56
|
+
console.log(`[browser_navigate] Returning with captureSnapshot: ${captureSnapshot}, waitForNetwork: false`);
|
|
57
|
+
console.log(`[browser_navigate] ========== NAVIGATION END ==========`);
|
|
58
|
+
return {
|
|
50
59
|
code,
|
|
51
60
|
captureSnapshot,
|
|
52
61
|
waitForNetwork: false
|
|
53
62
|
};
|
|
54
|
-
log("=== NAVIGATE HANDLER END ===");
|
|
55
|
-
return result;
|
|
56
63
|
}
|
|
57
64
|
});
|
|
58
65
|
const goBack = (captureSnapshot) => tool.defineTool({
|
|
@@ -8,25 +8,14 @@ import { userDataDir } from './fileUtils.js';
|
|
|
8
8
|
|
|
9
9
|
const testDebug = createDebug("pw:mcp:test");
|
|
10
10
|
function contextFactory(browserConfig) {
|
|
11
|
-
|
|
12
|
-
console.log("[contextFactory] browserConfig:", JSON.stringify(browserConfig, null, 2));
|
|
13
|
-
if (browserConfig.remoteEndpoint) {
|
|
14
|
-
console.log("[contextFactory] Using RemoteContextFactory");
|
|
11
|
+
if (browserConfig.remoteEndpoint)
|
|
15
12
|
return new RemoteContextFactory(browserConfig);
|
|
16
|
-
|
|
17
|
-
if (browserConfig.cdpEndpoint) {
|
|
18
|
-
console.log("[contextFactory] Using CdpContextFactory");
|
|
13
|
+
if (browserConfig.cdpEndpoint)
|
|
19
14
|
return new CdpContextFactory(browserConfig);
|
|
20
|
-
|
|
21
|
-
if (browserConfig.isolated) {
|
|
22
|
-
console.log("[contextFactory] Using IsolatedContextFactory");
|
|
15
|
+
if (browserConfig.isolated)
|
|
23
16
|
return new IsolatedContextFactory(browserConfig);
|
|
24
|
-
|
|
25
|
-
if (browserConfig.browserAgent) {
|
|
26
|
-
console.log("[contextFactory] Using BrowserServerContextFactory");
|
|
17
|
+
if (browserConfig.browserAgent)
|
|
27
18
|
return new BrowserServerContextFactory(browserConfig);
|
|
28
|
-
}
|
|
29
|
-
console.log("[contextFactory] Using PersistentContextFactory");
|
|
30
19
|
return new PersistentContextFactory(browserConfig);
|
|
31
20
|
}
|
|
32
21
|
class BaseContextFactory {
|
|
@@ -36,26 +25,17 @@ class BaseContextFactory {
|
|
|
36
25
|
constructor(name, browserConfig) {
|
|
37
26
|
this.name = name;
|
|
38
27
|
this.browserConfig = browserConfig;
|
|
39
|
-
console.log(`[BaseContextFactory] Created factory: ${name}`);
|
|
40
28
|
}
|
|
41
29
|
async _obtainBrowser() {
|
|
42
|
-
|
|
43
|
-
console.log(`[BaseContextFactory._obtainBrowser] _browserPromise exists:`, !!this._browserPromise);
|
|
44
|
-
if (this._browserPromise) {
|
|
45
|
-
console.log(`[BaseContextFactory._obtainBrowser] Returning existing promise`);
|
|
30
|
+
if (this._browserPromise)
|
|
46
31
|
return this._browserPromise;
|
|
47
|
-
}
|
|
48
32
|
testDebug(`obtain browser (${this.name})`);
|
|
49
|
-
console.log(`[BaseContextFactory._obtainBrowser] Calling _doObtainBrowser...`);
|
|
50
33
|
this._browserPromise = this._doObtainBrowser();
|
|
51
34
|
void this._browserPromise.then((browser) => {
|
|
52
|
-
console.log(`[BaseContextFactory._obtainBrowser] Browser obtained successfully`);
|
|
53
35
|
browser.on("disconnected", () => {
|
|
54
|
-
console.log(`[BaseContextFactory._obtainBrowser] Browser disconnected`);
|
|
55
36
|
this._browserPromise = void 0;
|
|
56
37
|
});
|
|
57
|
-
}).catch((
|
|
58
|
-
console.log(`[BaseContextFactory._obtainBrowser] Browser promise rejected:`, err.message);
|
|
38
|
+
}).catch(() => {
|
|
59
39
|
this._browserPromise = void 0;
|
|
60
40
|
});
|
|
61
41
|
return this._browserPromise;
|
|
@@ -64,22 +44,15 @@ class BaseContextFactory {
|
|
|
64
44
|
throw new Error("Not implemented");
|
|
65
45
|
}
|
|
66
46
|
async createContext() {
|
|
67
|
-
console.log(`[BaseContextFactory.createContext] START (${this.name})`);
|
|
68
47
|
testDebug(`create browser context (${this.name})`);
|
|
69
|
-
console.log(`[BaseContextFactory.createContext] Calling _obtainBrowser...`);
|
|
70
48
|
const browser = await this._obtainBrowser();
|
|
71
|
-
console.log(`[BaseContextFactory.createContext] Got browser`);
|
|
72
|
-
console.log(`[BaseContextFactory.createContext] Calling _doCreateContext...`);
|
|
73
49
|
const browserContext = await this._doCreateContext(browser);
|
|
74
|
-
console.log(`[BaseContextFactory.createContext] Got browserContext`);
|
|
75
|
-
console.log(`[BaseContextFactory.createContext] END`);
|
|
76
50
|
return { browserContext, close: () => this._closeBrowserContext(browserContext, browser) };
|
|
77
51
|
}
|
|
78
52
|
async _doCreateContext(browser) {
|
|
79
53
|
throw new Error("Not implemented");
|
|
80
54
|
}
|
|
81
55
|
async _closeBrowserContext(browserContext, browser) {
|
|
82
|
-
console.log(`[BaseContextFactory._closeBrowserContext] START (${this.name})`);
|
|
83
56
|
testDebug(`close browser context (${this.name})`);
|
|
84
57
|
if (browser.contexts().length === 1)
|
|
85
58
|
this._browserPromise = void 0;
|
|
@@ -90,7 +63,6 @@ class BaseContextFactory {
|
|
|
90
63
|
await browser.close().catch(() => {
|
|
91
64
|
});
|
|
92
65
|
}
|
|
93
|
-
console.log(`[BaseContextFactory._closeBrowserContext] END`);
|
|
94
66
|
}
|
|
95
67
|
}
|
|
96
68
|
class IsolatedContextFactory extends BaseContextFactory {
|
|
@@ -119,106 +91,10 @@ class CdpContextFactory extends BaseContextFactory {
|
|
|
119
91
|
super("cdp", browserConfig);
|
|
120
92
|
}
|
|
121
93
|
async _doObtainBrowser() {
|
|
122
|
-
|
|
123
|
-
console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint:", this.browserConfig.cdpEndpoint);
|
|
124
|
-
console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint type:", typeof this.browserConfig.cdpEndpoint);
|
|
125
|
-
const endpoint = this.browserConfig.cdpEndpoint;
|
|
126
|
-
const isCloudfareFetcher = typeof endpoint !== "string";
|
|
127
|
-
console.log("[CdpContextFactory._doObtainBrowser] isCloudfareFetcher:", isCloudfareFetcher);
|
|
128
|
-
try {
|
|
129
|
-
let browser;
|
|
130
|
-
if (isCloudfareFetcher) {
|
|
131
|
-
const fetcher = endpoint;
|
|
132
|
-
console.log("[CdpContextFactory._doObtainBrowser] Fetcher detected, using connect() method...");
|
|
133
|
-
const connectUrls = [
|
|
134
|
-
"https://cloudflare",
|
|
135
|
-
"https://cloudflare/",
|
|
136
|
-
"wss://cloudflare"
|
|
137
|
-
];
|
|
138
|
-
let socket = null;
|
|
139
|
-
let successUrl = "";
|
|
140
|
-
for (const url of connectUrls) {
|
|
141
|
-
try {
|
|
142
|
-
console.log(`[CdpContextFactory._doObtainBrowser] Trying fetcher.connect("${url}")...`);
|
|
143
|
-
const connection = await fetcher.connect(url);
|
|
144
|
-
console.log("[CdpContextFactory._doObtainBrowser] connect() returned:", typeof connection);
|
|
145
|
-
console.log("[CdpContextFactory._doObtainBrowser] connection keys:", Object.keys(connection || {}));
|
|
146
|
-
console.log("[CdpContextFactory._doObtainBrowser] connection.socket:", typeof connection?.socket);
|
|
147
|
-
if (connection && typeof connection === "object") {
|
|
148
|
-
if ("accept" in connection && typeof connection.accept === "function") {
|
|
149
|
-
console.log("[CdpContextFactory._doObtainBrowser] Connection has accept(), calling it...");
|
|
150
|
-
connection.accept();
|
|
151
|
-
}
|
|
152
|
-
if ("send" in connection && "close" in connection) {
|
|
153
|
-
socket = connection;
|
|
154
|
-
successUrl = url;
|
|
155
|
-
console.log("[CdpContextFactory._doObtainBrowser] Got WebSocket-like object directly");
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
if ("socket" in connection) {
|
|
159
|
-
socket = connection.socket;
|
|
160
|
-
successUrl = url;
|
|
161
|
-
console.log("[CdpContextFactory._doObtainBrowser] Got socket from connection.socket");
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
if ("webSocket" in connection) {
|
|
165
|
-
socket = connection.webSocket;
|
|
166
|
-
if (socket && "accept" in socket) {
|
|
167
|
-
socket.accept();
|
|
168
|
-
}
|
|
169
|
-
successUrl = url;
|
|
170
|
-
console.log("[CdpContextFactory._doObtainBrowser] Got socket from connection.webSocket");
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
console.log("[CdpContextFactory._doObtainBrowser] Connection structure:", JSON.stringify(Object.getOwnPropertyNames(connection)));
|
|
174
|
-
const proto = Object.getPrototypeOf(connection);
|
|
175
|
-
if (proto) {
|
|
176
|
-
console.log("[CdpContextFactory._doObtainBrowser] Connection prototype:", Object.getOwnPropertyNames(proto));
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
} catch (e) {
|
|
180
|
-
console.log(`[CdpContextFactory._doObtainBrowser] connect("${url}") error:`, e.message);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
if (!socket) {
|
|
184
|
-
throw new Error("Could not establish WebSocket via Fetcher.connect(). Check logs above.");
|
|
185
|
-
}
|
|
186
|
-
console.log(`[CdpContextFactory._doObtainBrowser] WebSocket obtained from ${successUrl}`);
|
|
187
|
-
console.log("[CdpContextFactory._doObtainBrowser] Socket type:", typeof socket);
|
|
188
|
-
console.log("[CdpContextFactory._doObtainBrowser] Socket readyState:", socket.readyState);
|
|
189
|
-
console.log("[CdpContextFactory._doObtainBrowser] Socket methods:", Object.getOwnPropertyNames(Object.getPrototypeOf(socket)));
|
|
190
|
-
console.log("[CdpContextFactory._doObtainBrowser] playwright.chromium methods:", Object.keys(playwright.chromium));
|
|
191
|
-
if (typeof playwright.chromium.connectOverCDPWithSocket === "function") {
|
|
192
|
-
console.log("[CdpContextFactory._doObtainBrowser] Using connectOverCDPWithSocket...");
|
|
193
|
-
browser = await playwright.chromium.connectOverCDPWithSocket(socket);
|
|
194
|
-
} else if (typeof playwright.chromium.connectWithWebSocket === "function") {
|
|
195
|
-
console.log("[CdpContextFactory._doObtainBrowser] Using connectWithWebSocket...");
|
|
196
|
-
browser = await playwright.chromium.connectWithWebSocket(socket);
|
|
197
|
-
} else {
|
|
198
|
-
throw new Error(`WebSocket connection established but patchright needs a method to accept raw WebSocket for CDP. Available methods: ${Object.keys(playwright.chromium).join(", ")}`);
|
|
199
|
-
}
|
|
200
|
-
} else {
|
|
201
|
-
console.log("[CdpContextFactory._doObtainBrowser] Using chromium.connectOverCDP() for CDP URL...");
|
|
202
|
-
browser = await playwright.chromium.connectOverCDP(endpoint);
|
|
203
|
-
}
|
|
204
|
-
console.log("[CdpContextFactory._doObtainBrowser] Connection completed");
|
|
205
|
-
return browser;
|
|
206
|
-
} catch (err) {
|
|
207
|
-
console.log("[CdpContextFactory._doObtainBrowser] ERROR:", err.message, err.stack);
|
|
208
|
-
throw err;
|
|
209
|
-
}
|
|
94
|
+
return playwright.chromium.connectOverCDP(this.browserConfig.cdpEndpoint);
|
|
210
95
|
}
|
|
211
96
|
async _doCreateContext(browser) {
|
|
212
|
-
|
|
213
|
-
console.log("[CdpContextFactory._doCreateContext] isolated:", this.browserConfig.isolated);
|
|
214
|
-
try {
|
|
215
|
-
const ctx = this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
|
|
216
|
-
console.log("[CdpContextFactory._doCreateContext] Got context");
|
|
217
|
-
return ctx;
|
|
218
|
-
} catch (err) {
|
|
219
|
-
console.log("[CdpContextFactory._doCreateContext] ERROR:", err.message, err.stack);
|
|
220
|
-
throw err;
|
|
221
|
-
}
|
|
97
|
+
return this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
|
|
222
98
|
}
|
|
223
99
|
}
|
|
224
100
|
class RemoteContextFactory extends BaseContextFactory {
|
package/lib/esm/src/context.js
CHANGED
|
@@ -66,29 +66,9 @@ class Context {
|
|
|
66
66
|
await this._currentTab.page.bringToFront();
|
|
67
67
|
}
|
|
68
68
|
async ensureTab() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
let browserContext;
|
|
73
|
-
try {
|
|
74
|
-
const result = await this._ensureBrowserContext();
|
|
75
|
-
browserContext = result.browserContext;
|
|
76
|
-
console.log("[Context.ensureTab] _ensureBrowserContext() completed");
|
|
77
|
-
} catch (err) {
|
|
78
|
-
console.log("[Context.ensureTab] ERROR in _ensureBrowserContext():", err.message, err.stack);
|
|
79
|
-
throw err;
|
|
80
|
-
}
|
|
81
|
-
if (!this._currentTab) {
|
|
82
|
-
console.log("[Context.ensureTab] No current tab, creating new page...");
|
|
83
|
-
try {
|
|
84
|
-
await browserContext.newPage();
|
|
85
|
-
console.log("[Context.ensureTab] New page created");
|
|
86
|
-
} catch (err) {
|
|
87
|
-
console.log("[Context.ensureTab] ERROR creating new page:", err.message, err.stack);
|
|
88
|
-
throw err;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
console.log("[Context.ensureTab] END, returning tab");
|
|
69
|
+
const { browserContext } = await this._ensureBrowserContext();
|
|
70
|
+
if (!this._currentTab)
|
|
71
|
+
await browserContext.newPage();
|
|
92
72
|
return this._currentTab;
|
|
93
73
|
}
|
|
94
74
|
async listTabsMarkdown() {
|
|
@@ -265,51 +245,29 @@ ${code.join("\n")}
|
|
|
265
245
|
}
|
|
266
246
|
}
|
|
267
247
|
_ensureBrowserContext() {
|
|
268
|
-
console.log("[Context._ensureBrowserContext] START");
|
|
269
|
-
console.log("[Context._ensureBrowserContext] _browserContextPromise exists:", !!this._browserContextPromise);
|
|
270
248
|
if (!this._browserContextPromise) {
|
|
271
|
-
console.log("[Context._ensureBrowserContext] Creating new browser context promise...");
|
|
272
249
|
this._browserContextPromise = this._setupBrowserContext();
|
|
273
|
-
this._browserContextPromise.catch((
|
|
274
|
-
console.log("[Context._ensureBrowserContext] Promise rejected:", err.message);
|
|
250
|
+
this._browserContextPromise.catch(() => {
|
|
275
251
|
this._browserContextPromise = void 0;
|
|
276
252
|
});
|
|
277
253
|
}
|
|
278
|
-
console.log("[Context._ensureBrowserContext] END, returning promise");
|
|
279
254
|
return this._browserContextPromise;
|
|
280
255
|
}
|
|
281
256
|
async _setupBrowserContext() {
|
|
282
|
-
|
|
283
|
-
console.log("[Context._setupBrowserContext] Calling _browserContextFactory.createContext()...");
|
|
284
|
-
let result;
|
|
285
|
-
try {
|
|
286
|
-
result = await this._browserContextFactory.createContext();
|
|
287
|
-
console.log("[Context._setupBrowserContext] createContext() completed");
|
|
288
|
-
} catch (err) {
|
|
289
|
-
console.log("[Context._setupBrowserContext] ERROR in createContext():", err.message, err.stack);
|
|
290
|
-
throw err;
|
|
291
|
-
}
|
|
257
|
+
const result = await this._browserContextFactory.createContext();
|
|
292
258
|
const { browserContext } = result;
|
|
293
|
-
console.log("[Context._setupBrowserContext] Got browserContext");
|
|
294
|
-
console.log("[Context._setupBrowserContext] Setting up request interception...");
|
|
295
259
|
await this._setupRequestInterception(browserContext);
|
|
296
|
-
console.log("[Context._setupBrowserContext] Request interception done");
|
|
297
|
-
console.log("[Context._setupBrowserContext] Processing existing pages...");
|
|
298
260
|
for (const page of browserContext.pages())
|
|
299
261
|
this._onPageCreated(page);
|
|
300
262
|
browserContext.on("page", (page) => this._onPageCreated(page));
|
|
301
|
-
console.log("[Context._setupBrowserContext] Pages processed");
|
|
302
263
|
if (this.config.saveTrace) {
|
|
303
|
-
console.log("[Context._setupBrowserContext] Starting tracing...");
|
|
304
264
|
await browserContext.tracing.start({
|
|
305
265
|
name: "trace",
|
|
306
266
|
screenshots: false,
|
|
307
267
|
snapshots: true,
|
|
308
268
|
sources: false
|
|
309
269
|
});
|
|
310
|
-
console.log("[Context._setupBrowserContext] Tracing started");
|
|
311
270
|
}
|
|
312
|
-
console.log("[Context._setupBrowserContext] END");
|
|
313
271
|
return result;
|
|
314
272
|
}
|
|
315
273
|
}
|
package/lib/esm/src/tab.js
CHANGED
|
@@ -46,40 +46,66 @@ class Tab {
|
|
|
46
46
|
}));
|
|
47
47
|
}
|
|
48
48
|
async navigate(url) {
|
|
49
|
-
|
|
50
|
-
console.log(
|
|
49
|
+
const navStartTime = Date.now();
|
|
50
|
+
console.log(`[Tab.navigate] --------- TAB NAVIGATION START ---------`);
|
|
51
|
+
console.log(`[Tab.navigate] URL: ${url}`);
|
|
52
|
+
console.log(`[Tab.navigate] Current page URL: ${this.page.url()}`);
|
|
53
|
+
console.log(`[Tab.navigate] Page is closed: ${this.page.isClosed()}`);
|
|
54
|
+
console.log(`[Tab.navigate] Clearing collected artifacts...`);
|
|
51
55
|
this._clearCollectedArtifacts();
|
|
52
|
-
console.log(
|
|
53
|
-
console.log(
|
|
56
|
+
console.log(`[Tab.navigate] Artifacts cleared. Console messages: ${this._consoleMessages.length}, Requests: ${this._requests.size}`);
|
|
57
|
+
console.log(`[Tab.navigate] Setting up download event listener...`);
|
|
54
58
|
const downloadEvent = callOnPageNoTrace(this.page, (page) => page.waitForEvent("download").catch(() => {
|
|
55
59
|
}));
|
|
56
|
-
console.log(
|
|
60
|
+
console.log(`[Tab.navigate] Download event listener set up`);
|
|
61
|
+
console.log(`[Tab.navigate] Calling page.goto() with waitUntil: 'domcontentloaded'`);
|
|
62
|
+
const gotoStartTime = Date.now();
|
|
57
63
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
console.log(
|
|
64
|
+
const response = await this.page.goto(url, { waitUntil: "domcontentloaded" });
|
|
65
|
+
const gotoTime = Date.now() - gotoStartTime;
|
|
66
|
+
console.log(`[Tab.navigate] page.goto() completed in ${gotoTime}ms`);
|
|
67
|
+
if (response) {
|
|
68
|
+
console.log(`[Tab.navigate] Response status: ${response.status()}`);
|
|
69
|
+
console.log(`[Tab.navigate] Response URL: ${response.url()}`);
|
|
70
|
+
console.log(`[Tab.navigate] Response headers:`, JSON.stringify(response.headers(), null, 2));
|
|
71
|
+
} else {
|
|
72
|
+
console.log(`[Tab.navigate] Response is null (might be same-document navigation or cached)`);
|
|
73
|
+
}
|
|
61
74
|
} catch (_e) {
|
|
62
75
|
const e = _e;
|
|
63
|
-
|
|
76
|
+
const gotoTime = Date.now() - gotoStartTime;
|
|
77
|
+
console.log(`[Tab.navigate] page.goto() threw after ${gotoTime}ms`);
|
|
78
|
+
console.log(`[Tab.navigate] Error name: ${e.name}`);
|
|
79
|
+
console.log(`[Tab.navigate] Error message: ${e.message}`);
|
|
64
80
|
const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
|
|
81
|
+
console.log(`[Tab.navigate] Might be download: ${mightBeDownload}`);
|
|
65
82
|
if (!mightBeDownload) {
|
|
66
|
-
console.
|
|
83
|
+
console.error(`[Tab.navigate] ERROR: Not a download, rethrowing error`);
|
|
84
|
+
console.error(`[Tab.navigate] Error stack:`, e.stack);
|
|
67
85
|
throw e;
|
|
68
86
|
}
|
|
69
|
-
console.log(
|
|
87
|
+
console.log(`[Tab.navigate] Waiting for potential download event (up to 1000ms)...`);
|
|
88
|
+
const downloadWaitStart = Date.now();
|
|
70
89
|
const download = await Promise.race([
|
|
71
90
|
downloadEvent,
|
|
72
91
|
new Promise((resolve) => setTimeout(resolve, 1e3))
|
|
73
92
|
]);
|
|
93
|
+
console.log(`[Tab.navigate] Download wait completed in ${Date.now() - downloadWaitStart}ms`);
|
|
94
|
+
console.log(`[Tab.navigate] Download detected: ${!!download}`);
|
|
74
95
|
if (!download) {
|
|
75
|
-
console.
|
|
96
|
+
console.error(`[Tab.navigate] ERROR: No download detected, rethrowing original error`);
|
|
76
97
|
throw e;
|
|
77
98
|
}
|
|
78
|
-
console.log(
|
|
99
|
+
console.log(`[Tab.navigate] Download detected, continuing without error`);
|
|
79
100
|
}
|
|
80
|
-
console.log(
|
|
101
|
+
console.log(`[Tab.navigate] Waiting for 'load' state (timeout: 5000ms)...`);
|
|
102
|
+
const loadWaitStart = Date.now();
|
|
81
103
|
await this.waitForLoadState("load", { timeout: 5e3 });
|
|
82
|
-
console.log(
|
|
104
|
+
console.log(`[Tab.navigate] 'load' state wait completed in ${Date.now() - loadWaitStart}ms`);
|
|
105
|
+
console.log(`[Tab.navigate] Final page URL: ${this.page.url()}`);
|
|
106
|
+
console.log(`[Tab.navigate] Final page title: ${await this.page.title().catch(() => "N/A")}`);
|
|
107
|
+
console.log(`[Tab.navigate] Total navigation time: ${Date.now() - navStartTime}ms`);
|
|
108
|
+
console.log(`[Tab.navigate] --------- TAB NAVIGATION END ---------`);
|
|
83
109
|
}
|
|
84
110
|
hasSnapshot() {
|
|
85
111
|
return !!this._snapshot;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { defineTool } from './tool.js';
|
|
3
3
|
|
|
4
|
-
const log = (msg, data) => {
|
|
5
|
-
console.log(`[browser_navigate] ${msg}`, data !== void 0 ? JSON.stringify(data, null, 2) : "");
|
|
6
|
-
};
|
|
7
4
|
const navigate = (captureSnapshot) => defineTool({
|
|
8
5
|
capability: "core",
|
|
9
6
|
schema: {
|
|
@@ -16,39 +13,49 @@ const navigate = (captureSnapshot) => defineTool({
|
|
|
16
13
|
type: "destructive"
|
|
17
14
|
},
|
|
18
15
|
handle: async (context, params) => {
|
|
19
|
-
|
|
20
|
-
log(
|
|
21
|
-
log(
|
|
22
|
-
log(
|
|
16
|
+
const startTime = Date.now();
|
|
17
|
+
console.log(`[browser_navigate] ========== NAVIGATION START ==========`);
|
|
18
|
+
console.log(`[browser_navigate] Target URL: ${params.url}`);
|
|
19
|
+
console.log(`[browser_navigate] Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
20
|
+
console.log(`[browser_navigate] captureSnapshot setting: ${captureSnapshot}`);
|
|
21
|
+
console.log(`[browser_navigate] Ensuring tab is available...`);
|
|
22
|
+
const ensureTabStart = Date.now();
|
|
23
23
|
let tab;
|
|
24
24
|
try {
|
|
25
25
|
tab = await context.ensureTab();
|
|
26
|
-
log(
|
|
27
|
-
log(
|
|
28
|
-
} catch (
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
console.log(`[browser_navigate] Tab ensured successfully in ${Date.now() - ensureTabStart}ms`);
|
|
27
|
+
console.log(`[browser_navigate] Current page URL before navigation: ${tab.page.url()}`);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(`[browser_navigate] ERROR: Failed to ensure tab after ${Date.now() - ensureTabStart}ms`);
|
|
30
|
+
console.error(`[browser_navigate] Error details:`, error);
|
|
31
|
+
throw error;
|
|
31
32
|
}
|
|
32
|
-
log(`
|
|
33
|
+
console.log(`[browser_navigate] Starting navigation to: ${params.url}`);
|
|
34
|
+
const navigationStart = Date.now();
|
|
33
35
|
try {
|
|
34
36
|
await tab.navigate(params.url);
|
|
35
|
-
log(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
console.log(`[browser_navigate] Navigation completed successfully in ${Date.now() - navigationStart}ms`);
|
|
38
|
+
console.log(`[browser_navigate] Current page URL after navigation: ${tab.page.url()}`);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(`[browser_navigate] ERROR: Navigation failed after ${Date.now() - navigationStart}ms`);
|
|
41
|
+
console.error(`[browser_navigate] Error type: ${error.constructor.name}`);
|
|
42
|
+
console.error(`[browser_navigate] Error message: ${error.message}`);
|
|
43
|
+
console.error(`[browser_navigate] Error stack:`, error.stack);
|
|
44
|
+
throw error;
|
|
39
45
|
}
|
|
40
46
|
const code = [
|
|
41
47
|
`// Navigate to ${params.url}`,
|
|
42
48
|
`await page.goto('${params.url}');`
|
|
43
49
|
];
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
const totalTime = Date.now() - startTime;
|
|
51
|
+
console.log(`[browser_navigate] Total execution time: ${totalTime}ms`);
|
|
52
|
+
console.log(`[browser_navigate] Returning with captureSnapshot: ${captureSnapshot}, waitForNetwork: false`);
|
|
53
|
+
console.log(`[browser_navigate] ========== NAVIGATION END ==========`);
|
|
54
|
+
return {
|
|
46
55
|
code,
|
|
47
56
|
captureSnapshot,
|
|
48
57
|
waitForNetwork: false
|
|
49
58
|
};
|
|
50
|
-
log("=== NAVIGATE HANDLER END ===");
|
|
51
|
-
return result;
|
|
52
59
|
}
|
|
53
60
|
});
|
|
54
61
|
const goBack = (captureSnapshot) => defineTool({
|