@szymonrybczak/playwright-mcp 0.0.10 → 0.0.12
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 +67 -8
- package/lib/cjs/src/context.js +47 -5
- package/lib/cjs/src/tab.js +18 -2
- package/lib/cjs/src/tools/navigate.js +28 -3
- package/lib/esm/cloudflare/package.json.js +1 -1
- package/lib/esm/src/browserContextFactory.js +67 -8
- package/lib/esm/src/context.js +47 -5
- package/lib/esm/src/tab.js +18 -2
- package/lib/esm/src/tools/navigate.js +28 -3
- package/package.json +1 -1
|
@@ -31,14 +31,25 @@ const playwright__namespace = /*#__PURE__*/_interopNamespaceDefault(playwright);
|
|
|
31
31
|
|
|
32
32
|
const testDebug = debugStub.default("pw:mcp:test");
|
|
33
33
|
function contextFactory(browserConfig) {
|
|
34
|
-
|
|
34
|
+
console.log("[contextFactory] START");
|
|
35
|
+
console.log("[contextFactory] browserConfig:", JSON.stringify(browserConfig, null, 2));
|
|
36
|
+
if (browserConfig.remoteEndpoint) {
|
|
37
|
+
console.log("[contextFactory] Using RemoteContextFactory");
|
|
35
38
|
return new RemoteContextFactory(browserConfig);
|
|
36
|
-
|
|
39
|
+
}
|
|
40
|
+
if (browserConfig.cdpEndpoint) {
|
|
41
|
+
console.log("[contextFactory] Using CdpContextFactory");
|
|
37
42
|
return new CdpContextFactory(browserConfig);
|
|
38
|
-
|
|
43
|
+
}
|
|
44
|
+
if (browserConfig.isolated) {
|
|
45
|
+
console.log("[contextFactory] Using IsolatedContextFactory");
|
|
39
46
|
return new IsolatedContextFactory(browserConfig);
|
|
40
|
-
|
|
47
|
+
}
|
|
48
|
+
if (browserConfig.browserAgent) {
|
|
49
|
+
console.log("[contextFactory] Using BrowserServerContextFactory");
|
|
41
50
|
return new BrowserServerContextFactory(browserConfig);
|
|
51
|
+
}
|
|
52
|
+
console.log("[contextFactory] Using PersistentContextFactory");
|
|
42
53
|
return new PersistentContextFactory(browserConfig);
|
|
43
54
|
}
|
|
44
55
|
class BaseContextFactory {
|
|
@@ -48,17 +59,26 @@ class BaseContextFactory {
|
|
|
48
59
|
constructor(name, browserConfig) {
|
|
49
60
|
this.name = name;
|
|
50
61
|
this.browserConfig = browserConfig;
|
|
62
|
+
console.log(`[BaseContextFactory] Created factory: ${name}`);
|
|
51
63
|
}
|
|
52
64
|
async _obtainBrowser() {
|
|
53
|
-
|
|
65
|
+
console.log(`[BaseContextFactory._obtainBrowser] START (${this.name})`);
|
|
66
|
+
console.log(`[BaseContextFactory._obtainBrowser] _browserPromise exists:`, !!this._browserPromise);
|
|
67
|
+
if (this._browserPromise) {
|
|
68
|
+
console.log(`[BaseContextFactory._obtainBrowser] Returning existing promise`);
|
|
54
69
|
return this._browserPromise;
|
|
70
|
+
}
|
|
55
71
|
testDebug(`obtain browser (${this.name})`);
|
|
72
|
+
console.log(`[BaseContextFactory._obtainBrowser] Calling _doObtainBrowser...`);
|
|
56
73
|
this._browserPromise = this._doObtainBrowser();
|
|
57
74
|
void this._browserPromise.then((browser) => {
|
|
75
|
+
console.log(`[BaseContextFactory._obtainBrowser] Browser obtained successfully`);
|
|
58
76
|
browser.on("disconnected", () => {
|
|
77
|
+
console.log(`[BaseContextFactory._obtainBrowser] Browser disconnected`);
|
|
59
78
|
this._browserPromise = void 0;
|
|
60
79
|
});
|
|
61
|
-
}).catch(() => {
|
|
80
|
+
}).catch((err) => {
|
|
81
|
+
console.log(`[BaseContextFactory._obtainBrowser] Browser promise rejected:`, err.message);
|
|
62
82
|
this._browserPromise = void 0;
|
|
63
83
|
});
|
|
64
84
|
return this._browserPromise;
|
|
@@ -67,15 +87,22 @@ class BaseContextFactory {
|
|
|
67
87
|
throw new Error("Not implemented");
|
|
68
88
|
}
|
|
69
89
|
async createContext() {
|
|
90
|
+
console.log(`[BaseContextFactory.createContext] START (${this.name})`);
|
|
70
91
|
testDebug(`create browser context (${this.name})`);
|
|
92
|
+
console.log(`[BaseContextFactory.createContext] Calling _obtainBrowser...`);
|
|
71
93
|
const browser = await this._obtainBrowser();
|
|
94
|
+
console.log(`[BaseContextFactory.createContext] Got browser`);
|
|
95
|
+
console.log(`[BaseContextFactory.createContext] Calling _doCreateContext...`);
|
|
72
96
|
const browserContext = await this._doCreateContext(browser);
|
|
97
|
+
console.log(`[BaseContextFactory.createContext] Got browserContext`);
|
|
98
|
+
console.log(`[BaseContextFactory.createContext] END`);
|
|
73
99
|
return { browserContext, close: () => this._closeBrowserContext(browserContext, browser) };
|
|
74
100
|
}
|
|
75
101
|
async _doCreateContext(browser) {
|
|
76
102
|
throw new Error("Not implemented");
|
|
77
103
|
}
|
|
78
104
|
async _closeBrowserContext(browserContext, browser) {
|
|
105
|
+
console.log(`[BaseContextFactory._closeBrowserContext] START (${this.name})`);
|
|
79
106
|
testDebug(`close browser context (${this.name})`);
|
|
80
107
|
if (browser.contexts().length === 1)
|
|
81
108
|
this._browserPromise = void 0;
|
|
@@ -86,6 +113,7 @@ class BaseContextFactory {
|
|
|
86
113
|
await browser.close().catch(() => {
|
|
87
114
|
});
|
|
88
115
|
}
|
|
116
|
+
console.log(`[BaseContextFactory._closeBrowserContext] END`);
|
|
89
117
|
}
|
|
90
118
|
}
|
|
91
119
|
class IsolatedContextFactory extends BaseContextFactory {
|
|
@@ -114,10 +142,41 @@ class CdpContextFactory extends BaseContextFactory {
|
|
|
114
142
|
super("cdp", browserConfig);
|
|
115
143
|
}
|
|
116
144
|
async _doObtainBrowser() {
|
|
117
|
-
|
|
145
|
+
console.log("[CdpContextFactory._doObtainBrowser] START");
|
|
146
|
+
console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint:", this.browserConfig.cdpEndpoint);
|
|
147
|
+
console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint type:", typeof this.browserConfig.cdpEndpoint);
|
|
148
|
+
console.log("[CdpContextFactory._doObtainBrowser] playwright object keys:", Object.keys(playwright__namespace));
|
|
149
|
+
console.log("[CdpContextFactory._doObtainBrowser] playwright.chromium:", typeof playwright__namespace.chromium);
|
|
150
|
+
const endpoint = this.browserConfig.cdpEndpoint;
|
|
151
|
+
const isCloudfareFetcher = typeof endpoint !== "string";
|
|
152
|
+
console.log("[CdpContextFactory._doObtainBrowser] isCloudfareFetcher:", isCloudfareFetcher);
|
|
153
|
+
try {
|
|
154
|
+
let browser;
|
|
155
|
+
if (isCloudfareFetcher) {
|
|
156
|
+
console.log("[CdpContextFactory._doObtainBrowser] Using chromium.connect() for Cloudflare Fetcher...");
|
|
157
|
+
browser = await playwright__namespace.chromium.connect(endpoint);
|
|
158
|
+
} else {
|
|
159
|
+
console.log("[CdpContextFactory._doObtainBrowser] Using chromium.connectOverCDP() for CDP URL...");
|
|
160
|
+
browser = await playwright__namespace.chromium.connectOverCDP(endpoint);
|
|
161
|
+
}
|
|
162
|
+
console.log("[CdpContextFactory._doObtainBrowser] Connection completed");
|
|
163
|
+
return browser;
|
|
164
|
+
} catch (err) {
|
|
165
|
+
console.log("[CdpContextFactory._doObtainBrowser] ERROR:", err.message, err.stack);
|
|
166
|
+
throw err;
|
|
167
|
+
}
|
|
118
168
|
}
|
|
119
169
|
async _doCreateContext(browser) {
|
|
120
|
-
|
|
170
|
+
console.log("[CdpContextFactory._doCreateContext] START");
|
|
171
|
+
console.log("[CdpContextFactory._doCreateContext] isolated:", this.browserConfig.isolated);
|
|
172
|
+
try {
|
|
173
|
+
const ctx = this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
|
|
174
|
+
console.log("[CdpContextFactory._doCreateContext] Got context");
|
|
175
|
+
return ctx;
|
|
176
|
+
} catch (err) {
|
|
177
|
+
console.log("[CdpContextFactory._doCreateContext] ERROR:", err.message, err.stack);
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
121
180
|
}
|
|
122
181
|
}
|
|
123
182
|
class RemoteContextFactory extends BaseContextFactory {
|
package/lib/cjs/src/context.js
CHANGED
|
@@ -70,9 +70,29 @@ class Context {
|
|
|
70
70
|
await this._currentTab.page.bringToFront();
|
|
71
71
|
}
|
|
72
72
|
async ensureTab() {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
console.log("[Context.ensureTab] START");
|
|
74
|
+
console.log("[Context.ensureTab] _currentTab exists:", !!this._currentTab);
|
|
75
|
+
console.log("[Context.ensureTab] Calling _ensureBrowserContext()...");
|
|
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");
|
|
76
96
|
return this._currentTab;
|
|
77
97
|
}
|
|
78
98
|
async listTabsMarkdown() {
|
|
@@ -249,29 +269,51 @@ ${code.join("\n")}
|
|
|
249
269
|
}
|
|
250
270
|
}
|
|
251
271
|
_ensureBrowserContext() {
|
|
272
|
+
console.log("[Context._ensureBrowserContext] START");
|
|
273
|
+
console.log("[Context._ensureBrowserContext] _browserContextPromise exists:", !!this._browserContextPromise);
|
|
252
274
|
if (!this._browserContextPromise) {
|
|
275
|
+
console.log("[Context._ensureBrowserContext] Creating new browser context promise...");
|
|
253
276
|
this._browserContextPromise = this._setupBrowserContext();
|
|
254
|
-
this._browserContextPromise.catch(() => {
|
|
277
|
+
this._browserContextPromise.catch((err) => {
|
|
278
|
+
console.log("[Context._ensureBrowserContext] Promise rejected:", err.message);
|
|
255
279
|
this._browserContextPromise = void 0;
|
|
256
280
|
});
|
|
257
281
|
}
|
|
282
|
+
console.log("[Context._ensureBrowserContext] END, returning promise");
|
|
258
283
|
return this._browserContextPromise;
|
|
259
284
|
}
|
|
260
285
|
async _setupBrowserContext() {
|
|
261
|
-
|
|
286
|
+
console.log("[Context._setupBrowserContext] START");
|
|
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
|
+
}
|
|
262
296
|
const { browserContext } = result;
|
|
297
|
+
console.log("[Context._setupBrowserContext] Got browserContext");
|
|
298
|
+
console.log("[Context._setupBrowserContext] Setting up request interception...");
|
|
263
299
|
await this._setupRequestInterception(browserContext);
|
|
300
|
+
console.log("[Context._setupBrowserContext] Request interception done");
|
|
301
|
+
console.log("[Context._setupBrowserContext] Processing existing pages...");
|
|
264
302
|
for (const page of browserContext.pages())
|
|
265
303
|
this._onPageCreated(page);
|
|
266
304
|
browserContext.on("page", (page) => this._onPageCreated(page));
|
|
305
|
+
console.log("[Context._setupBrowserContext] Pages processed");
|
|
267
306
|
if (this.config.saveTrace) {
|
|
307
|
+
console.log("[Context._setupBrowserContext] Starting tracing...");
|
|
268
308
|
await browserContext.tracing.start({
|
|
269
309
|
name: "trace",
|
|
270
310
|
screenshots: false,
|
|
271
311
|
snapshots: true,
|
|
272
312
|
sources: false
|
|
273
313
|
});
|
|
314
|
+
console.log("[Context._setupBrowserContext] Tracing started");
|
|
274
315
|
}
|
|
316
|
+
console.log("[Context._setupBrowserContext] END");
|
|
275
317
|
return result;
|
|
276
318
|
}
|
|
277
319
|
}
|
package/lib/cjs/src/tab.js
CHANGED
|
@@ -50,24 +50,40 @@ class Tab {
|
|
|
50
50
|
}));
|
|
51
51
|
}
|
|
52
52
|
async navigate(url) {
|
|
53
|
+
console.log("[Tab.navigate] START, url:", url);
|
|
54
|
+
console.log("[Tab.navigate] Clearing collected artifacts...");
|
|
53
55
|
this._clearCollectedArtifacts();
|
|
56
|
+
console.log("[Tab.navigate] Artifacts cleared");
|
|
57
|
+
console.log("[Tab.navigate] Setting up download event listener...");
|
|
54
58
|
const downloadEvent = utils.callOnPageNoTrace(this.page, (page) => page.waitForEvent("download").catch(() => {
|
|
55
59
|
}));
|
|
60
|
+
console.log("[Tab.navigate] Download event listener ready");
|
|
56
61
|
try {
|
|
62
|
+
console.log("[Tab.navigate] Calling page.goto()...");
|
|
57
63
|
await this.page.goto(url, { waitUntil: "domcontentloaded" });
|
|
64
|
+
console.log("[Tab.navigate] page.goto() completed successfully");
|
|
58
65
|
} catch (_e) {
|
|
59
66
|
const e = _e;
|
|
67
|
+
console.log("[Tab.navigate] page.goto() threw error:", e.message);
|
|
60
68
|
const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
|
|
61
|
-
if (!mightBeDownload)
|
|
69
|
+
if (!mightBeDownload) {
|
|
70
|
+
console.log("[Tab.navigate] Not a download, re-throwing error");
|
|
62
71
|
throw e;
|
|
72
|
+
}
|
|
73
|
+
console.log("[Tab.navigate] Might be download, waiting for download event...");
|
|
63
74
|
const download = await Promise.race([
|
|
64
75
|
downloadEvent,
|
|
65
76
|
new Promise((resolve) => setTimeout(resolve, 1e3))
|
|
66
77
|
]);
|
|
67
|
-
if (!download)
|
|
78
|
+
if (!download) {
|
|
79
|
+
console.log("[Tab.navigate] No download received, re-throwing error");
|
|
68
80
|
throw e;
|
|
81
|
+
}
|
|
82
|
+
console.log("[Tab.navigate] Download detected");
|
|
69
83
|
}
|
|
84
|
+
console.log("[Tab.navigate] Waiting for load state...");
|
|
70
85
|
await this.waitForLoadState("load", { timeout: 5e3 });
|
|
86
|
+
console.log("[Tab.navigate] END");
|
|
71
87
|
}
|
|
72
88
|
hasSnapshot() {
|
|
73
89
|
return !!this._snapshot;
|
|
@@ -5,6 +5,9 @@ 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
|
+
};
|
|
8
11
|
const navigate = (captureSnapshot) => tool.defineTool({
|
|
9
12
|
capability: "core",
|
|
10
13
|
schema: {
|
|
@@ -17,17 +20,39 @@ const navigate = (captureSnapshot) => tool.defineTool({
|
|
|
17
20
|
type: "destructive"
|
|
18
21
|
},
|
|
19
22
|
handle: async (context, params) => {
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
log("=== NAVIGATE HANDLER START ===");
|
|
24
|
+
log("Params received:", params);
|
|
25
|
+
log("captureSnapshot:", captureSnapshot);
|
|
26
|
+
log("About to call context.ensureTab()...");
|
|
27
|
+
let tab;
|
|
28
|
+
try {
|
|
29
|
+
tab = await context.ensureTab();
|
|
30
|
+
log("context.ensureTab() completed successfully");
|
|
31
|
+
log("Tab page URL:", tab.page?.url?.());
|
|
32
|
+
} catch (err) {
|
|
33
|
+
log("ERROR in context.ensureTab():", { message: err.message, stack: err.stack });
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
log(`About to call tab.navigate("${params.url}")...`);
|
|
37
|
+
try {
|
|
38
|
+
await tab.navigate(params.url);
|
|
39
|
+
log("tab.navigate() completed successfully");
|
|
40
|
+
} catch (err) {
|
|
41
|
+
log("ERROR in tab.navigate():", { message: err.message, stack: err.stack });
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
22
44
|
const code = [
|
|
23
45
|
`// Navigate to ${params.url}`,
|
|
24
46
|
`await page.goto('${params.url}');`
|
|
25
47
|
];
|
|
26
|
-
return
|
|
48
|
+
log("About to return result...");
|
|
49
|
+
const result = {
|
|
27
50
|
code,
|
|
28
51
|
captureSnapshot,
|
|
29
52
|
waitForNetwork: false
|
|
30
53
|
};
|
|
54
|
+
log("=== NAVIGATE HANDLER END ===");
|
|
55
|
+
return result;
|
|
31
56
|
}
|
|
32
57
|
});
|
|
33
58
|
const goBack = (captureSnapshot) => tool.defineTool({
|
|
@@ -8,14 +8,25 @@ import { userDataDir } from './fileUtils.js';
|
|
|
8
8
|
|
|
9
9
|
const testDebug = createDebug("pw:mcp:test");
|
|
10
10
|
function contextFactory(browserConfig) {
|
|
11
|
-
|
|
11
|
+
console.log("[contextFactory] START");
|
|
12
|
+
console.log("[contextFactory] browserConfig:", JSON.stringify(browserConfig, null, 2));
|
|
13
|
+
if (browserConfig.remoteEndpoint) {
|
|
14
|
+
console.log("[contextFactory] Using RemoteContextFactory");
|
|
12
15
|
return new RemoteContextFactory(browserConfig);
|
|
13
|
-
|
|
16
|
+
}
|
|
17
|
+
if (browserConfig.cdpEndpoint) {
|
|
18
|
+
console.log("[contextFactory] Using CdpContextFactory");
|
|
14
19
|
return new CdpContextFactory(browserConfig);
|
|
15
|
-
|
|
20
|
+
}
|
|
21
|
+
if (browserConfig.isolated) {
|
|
22
|
+
console.log("[contextFactory] Using IsolatedContextFactory");
|
|
16
23
|
return new IsolatedContextFactory(browserConfig);
|
|
17
|
-
|
|
24
|
+
}
|
|
25
|
+
if (browserConfig.browserAgent) {
|
|
26
|
+
console.log("[contextFactory] Using BrowserServerContextFactory");
|
|
18
27
|
return new BrowserServerContextFactory(browserConfig);
|
|
28
|
+
}
|
|
29
|
+
console.log("[contextFactory] Using PersistentContextFactory");
|
|
19
30
|
return new PersistentContextFactory(browserConfig);
|
|
20
31
|
}
|
|
21
32
|
class BaseContextFactory {
|
|
@@ -25,17 +36,26 @@ class BaseContextFactory {
|
|
|
25
36
|
constructor(name, browserConfig) {
|
|
26
37
|
this.name = name;
|
|
27
38
|
this.browserConfig = browserConfig;
|
|
39
|
+
console.log(`[BaseContextFactory] Created factory: ${name}`);
|
|
28
40
|
}
|
|
29
41
|
async _obtainBrowser() {
|
|
30
|
-
|
|
42
|
+
console.log(`[BaseContextFactory._obtainBrowser] START (${this.name})`);
|
|
43
|
+
console.log(`[BaseContextFactory._obtainBrowser] _browserPromise exists:`, !!this._browserPromise);
|
|
44
|
+
if (this._browserPromise) {
|
|
45
|
+
console.log(`[BaseContextFactory._obtainBrowser] Returning existing promise`);
|
|
31
46
|
return this._browserPromise;
|
|
47
|
+
}
|
|
32
48
|
testDebug(`obtain browser (${this.name})`);
|
|
49
|
+
console.log(`[BaseContextFactory._obtainBrowser] Calling _doObtainBrowser...`);
|
|
33
50
|
this._browserPromise = this._doObtainBrowser();
|
|
34
51
|
void this._browserPromise.then((browser) => {
|
|
52
|
+
console.log(`[BaseContextFactory._obtainBrowser] Browser obtained successfully`);
|
|
35
53
|
browser.on("disconnected", () => {
|
|
54
|
+
console.log(`[BaseContextFactory._obtainBrowser] Browser disconnected`);
|
|
36
55
|
this._browserPromise = void 0;
|
|
37
56
|
});
|
|
38
|
-
}).catch(() => {
|
|
57
|
+
}).catch((err) => {
|
|
58
|
+
console.log(`[BaseContextFactory._obtainBrowser] Browser promise rejected:`, err.message);
|
|
39
59
|
this._browserPromise = void 0;
|
|
40
60
|
});
|
|
41
61
|
return this._browserPromise;
|
|
@@ -44,15 +64,22 @@ class BaseContextFactory {
|
|
|
44
64
|
throw new Error("Not implemented");
|
|
45
65
|
}
|
|
46
66
|
async createContext() {
|
|
67
|
+
console.log(`[BaseContextFactory.createContext] START (${this.name})`);
|
|
47
68
|
testDebug(`create browser context (${this.name})`);
|
|
69
|
+
console.log(`[BaseContextFactory.createContext] Calling _obtainBrowser...`);
|
|
48
70
|
const browser = await this._obtainBrowser();
|
|
71
|
+
console.log(`[BaseContextFactory.createContext] Got browser`);
|
|
72
|
+
console.log(`[BaseContextFactory.createContext] Calling _doCreateContext...`);
|
|
49
73
|
const browserContext = await this._doCreateContext(browser);
|
|
74
|
+
console.log(`[BaseContextFactory.createContext] Got browserContext`);
|
|
75
|
+
console.log(`[BaseContextFactory.createContext] END`);
|
|
50
76
|
return { browserContext, close: () => this._closeBrowserContext(browserContext, browser) };
|
|
51
77
|
}
|
|
52
78
|
async _doCreateContext(browser) {
|
|
53
79
|
throw new Error("Not implemented");
|
|
54
80
|
}
|
|
55
81
|
async _closeBrowserContext(browserContext, browser) {
|
|
82
|
+
console.log(`[BaseContextFactory._closeBrowserContext] START (${this.name})`);
|
|
56
83
|
testDebug(`close browser context (${this.name})`);
|
|
57
84
|
if (browser.contexts().length === 1)
|
|
58
85
|
this._browserPromise = void 0;
|
|
@@ -63,6 +90,7 @@ class BaseContextFactory {
|
|
|
63
90
|
await browser.close().catch(() => {
|
|
64
91
|
});
|
|
65
92
|
}
|
|
93
|
+
console.log(`[BaseContextFactory._closeBrowserContext] END`);
|
|
66
94
|
}
|
|
67
95
|
}
|
|
68
96
|
class IsolatedContextFactory extends BaseContextFactory {
|
|
@@ -91,10 +119,41 @@ class CdpContextFactory extends BaseContextFactory {
|
|
|
91
119
|
super("cdp", browserConfig);
|
|
92
120
|
}
|
|
93
121
|
async _doObtainBrowser() {
|
|
94
|
-
|
|
122
|
+
console.log("[CdpContextFactory._doObtainBrowser] START");
|
|
123
|
+
console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint:", this.browserConfig.cdpEndpoint);
|
|
124
|
+
console.log("[CdpContextFactory._doObtainBrowser] cdpEndpoint type:", typeof this.browserConfig.cdpEndpoint);
|
|
125
|
+
console.log("[CdpContextFactory._doObtainBrowser] playwright object keys:", Object.keys(playwright));
|
|
126
|
+
console.log("[CdpContextFactory._doObtainBrowser] playwright.chromium:", typeof playwright.chromium);
|
|
127
|
+
const endpoint = this.browserConfig.cdpEndpoint;
|
|
128
|
+
const isCloudfareFetcher = typeof endpoint !== "string";
|
|
129
|
+
console.log("[CdpContextFactory._doObtainBrowser] isCloudfareFetcher:", isCloudfareFetcher);
|
|
130
|
+
try {
|
|
131
|
+
let browser;
|
|
132
|
+
if (isCloudfareFetcher) {
|
|
133
|
+
console.log("[CdpContextFactory._doObtainBrowser] Using chromium.connect() for Cloudflare Fetcher...");
|
|
134
|
+
browser = await playwright.chromium.connect(endpoint);
|
|
135
|
+
} else {
|
|
136
|
+
console.log("[CdpContextFactory._doObtainBrowser] Using chromium.connectOverCDP() for CDP URL...");
|
|
137
|
+
browser = await playwright.chromium.connectOverCDP(endpoint);
|
|
138
|
+
}
|
|
139
|
+
console.log("[CdpContextFactory._doObtainBrowser] Connection completed");
|
|
140
|
+
return browser;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.log("[CdpContextFactory._doObtainBrowser] ERROR:", err.message, err.stack);
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
95
145
|
}
|
|
96
146
|
async _doCreateContext(browser) {
|
|
97
|
-
|
|
147
|
+
console.log("[CdpContextFactory._doCreateContext] START");
|
|
148
|
+
console.log("[CdpContextFactory._doCreateContext] isolated:", this.browserConfig.isolated);
|
|
149
|
+
try {
|
|
150
|
+
const ctx = this.browserConfig.isolated ? await browser.newContext() : browser.contexts()[0];
|
|
151
|
+
console.log("[CdpContextFactory._doCreateContext] Got context");
|
|
152
|
+
return ctx;
|
|
153
|
+
} catch (err) {
|
|
154
|
+
console.log("[CdpContextFactory._doCreateContext] ERROR:", err.message, err.stack);
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
98
157
|
}
|
|
99
158
|
}
|
|
100
159
|
class RemoteContextFactory extends BaseContextFactory {
|
package/lib/esm/src/context.js
CHANGED
|
@@ -66,9 +66,29 @@ class Context {
|
|
|
66
66
|
await this._currentTab.page.bringToFront();
|
|
67
67
|
}
|
|
68
68
|
async ensureTab() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
console.log("[Context.ensureTab] START");
|
|
70
|
+
console.log("[Context.ensureTab] _currentTab exists:", !!this._currentTab);
|
|
71
|
+
console.log("[Context.ensureTab] Calling _ensureBrowserContext()...");
|
|
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");
|
|
72
92
|
return this._currentTab;
|
|
73
93
|
}
|
|
74
94
|
async listTabsMarkdown() {
|
|
@@ -245,29 +265,51 @@ ${code.join("\n")}
|
|
|
245
265
|
}
|
|
246
266
|
}
|
|
247
267
|
_ensureBrowserContext() {
|
|
268
|
+
console.log("[Context._ensureBrowserContext] START");
|
|
269
|
+
console.log("[Context._ensureBrowserContext] _browserContextPromise exists:", !!this._browserContextPromise);
|
|
248
270
|
if (!this._browserContextPromise) {
|
|
271
|
+
console.log("[Context._ensureBrowserContext] Creating new browser context promise...");
|
|
249
272
|
this._browserContextPromise = this._setupBrowserContext();
|
|
250
|
-
this._browserContextPromise.catch(() => {
|
|
273
|
+
this._browserContextPromise.catch((err) => {
|
|
274
|
+
console.log("[Context._ensureBrowserContext] Promise rejected:", err.message);
|
|
251
275
|
this._browserContextPromise = void 0;
|
|
252
276
|
});
|
|
253
277
|
}
|
|
278
|
+
console.log("[Context._ensureBrowserContext] END, returning promise");
|
|
254
279
|
return this._browserContextPromise;
|
|
255
280
|
}
|
|
256
281
|
async _setupBrowserContext() {
|
|
257
|
-
|
|
282
|
+
console.log("[Context._setupBrowserContext] START");
|
|
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
|
+
}
|
|
258
292
|
const { browserContext } = result;
|
|
293
|
+
console.log("[Context._setupBrowserContext] Got browserContext");
|
|
294
|
+
console.log("[Context._setupBrowserContext] Setting up request interception...");
|
|
259
295
|
await this._setupRequestInterception(browserContext);
|
|
296
|
+
console.log("[Context._setupBrowserContext] Request interception done");
|
|
297
|
+
console.log("[Context._setupBrowserContext] Processing existing pages...");
|
|
260
298
|
for (const page of browserContext.pages())
|
|
261
299
|
this._onPageCreated(page);
|
|
262
300
|
browserContext.on("page", (page) => this._onPageCreated(page));
|
|
301
|
+
console.log("[Context._setupBrowserContext] Pages processed");
|
|
263
302
|
if (this.config.saveTrace) {
|
|
303
|
+
console.log("[Context._setupBrowserContext] Starting tracing...");
|
|
264
304
|
await browserContext.tracing.start({
|
|
265
305
|
name: "trace",
|
|
266
306
|
screenshots: false,
|
|
267
307
|
snapshots: true,
|
|
268
308
|
sources: false
|
|
269
309
|
});
|
|
310
|
+
console.log("[Context._setupBrowserContext] Tracing started");
|
|
270
311
|
}
|
|
312
|
+
console.log("[Context._setupBrowserContext] END");
|
|
271
313
|
return result;
|
|
272
314
|
}
|
|
273
315
|
}
|
package/lib/esm/src/tab.js
CHANGED
|
@@ -46,24 +46,40 @@ class Tab {
|
|
|
46
46
|
}));
|
|
47
47
|
}
|
|
48
48
|
async navigate(url) {
|
|
49
|
+
console.log("[Tab.navigate] START, url:", url);
|
|
50
|
+
console.log("[Tab.navigate] Clearing collected artifacts...");
|
|
49
51
|
this._clearCollectedArtifacts();
|
|
52
|
+
console.log("[Tab.navigate] Artifacts cleared");
|
|
53
|
+
console.log("[Tab.navigate] Setting up download event listener...");
|
|
50
54
|
const downloadEvent = callOnPageNoTrace(this.page, (page) => page.waitForEvent("download").catch(() => {
|
|
51
55
|
}));
|
|
56
|
+
console.log("[Tab.navigate] Download event listener ready");
|
|
52
57
|
try {
|
|
58
|
+
console.log("[Tab.navigate] Calling page.goto()...");
|
|
53
59
|
await this.page.goto(url, { waitUntil: "domcontentloaded" });
|
|
60
|
+
console.log("[Tab.navigate] page.goto() completed successfully");
|
|
54
61
|
} catch (_e) {
|
|
55
62
|
const e = _e;
|
|
63
|
+
console.log("[Tab.navigate] page.goto() threw error:", e.message);
|
|
56
64
|
const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
|
|
57
|
-
if (!mightBeDownload)
|
|
65
|
+
if (!mightBeDownload) {
|
|
66
|
+
console.log("[Tab.navigate] Not a download, re-throwing error");
|
|
58
67
|
throw e;
|
|
68
|
+
}
|
|
69
|
+
console.log("[Tab.navigate] Might be download, waiting for download event...");
|
|
59
70
|
const download = await Promise.race([
|
|
60
71
|
downloadEvent,
|
|
61
72
|
new Promise((resolve) => setTimeout(resolve, 1e3))
|
|
62
73
|
]);
|
|
63
|
-
if (!download)
|
|
74
|
+
if (!download) {
|
|
75
|
+
console.log("[Tab.navigate] No download received, re-throwing error");
|
|
64
76
|
throw e;
|
|
77
|
+
}
|
|
78
|
+
console.log("[Tab.navigate] Download detected");
|
|
65
79
|
}
|
|
80
|
+
console.log("[Tab.navigate] Waiting for load state...");
|
|
66
81
|
await this.waitForLoadState("load", { timeout: 5e3 });
|
|
82
|
+
console.log("[Tab.navigate] END");
|
|
67
83
|
}
|
|
68
84
|
hasSnapshot() {
|
|
69
85
|
return !!this._snapshot;
|
|
@@ -1,6 +1,9 @@
|
|
|
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
|
+
};
|
|
4
7
|
const navigate = (captureSnapshot) => defineTool({
|
|
5
8
|
capability: "core",
|
|
6
9
|
schema: {
|
|
@@ -13,17 +16,39 @@ const navigate = (captureSnapshot) => defineTool({
|
|
|
13
16
|
type: "destructive"
|
|
14
17
|
},
|
|
15
18
|
handle: async (context, params) => {
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
log("=== NAVIGATE HANDLER START ===");
|
|
20
|
+
log("Params received:", params);
|
|
21
|
+
log("captureSnapshot:", captureSnapshot);
|
|
22
|
+
log("About to call context.ensureTab()...");
|
|
23
|
+
let tab;
|
|
24
|
+
try {
|
|
25
|
+
tab = await context.ensureTab();
|
|
26
|
+
log("context.ensureTab() completed successfully");
|
|
27
|
+
log("Tab page URL:", tab.page?.url?.());
|
|
28
|
+
} catch (err) {
|
|
29
|
+
log("ERROR in context.ensureTab():", { message: err.message, stack: err.stack });
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
log(`About to call tab.navigate("${params.url}")...`);
|
|
33
|
+
try {
|
|
34
|
+
await tab.navigate(params.url);
|
|
35
|
+
log("tab.navigate() completed successfully");
|
|
36
|
+
} catch (err) {
|
|
37
|
+
log("ERROR in tab.navigate():", { message: err.message, stack: err.stack });
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
18
40
|
const code = [
|
|
19
41
|
`// Navigate to ${params.url}`,
|
|
20
42
|
`await page.goto('${params.url}');`
|
|
21
43
|
];
|
|
22
|
-
return
|
|
44
|
+
log("About to return result...");
|
|
45
|
+
const result = {
|
|
23
46
|
code,
|
|
24
47
|
captureSnapshot,
|
|
25
48
|
waitForNetwork: false
|
|
26
49
|
};
|
|
50
|
+
log("=== NAVIGATE HANDLER END ===");
|
|
51
|
+
return result;
|
|
27
52
|
}
|
|
28
53
|
});
|
|
29
54
|
const goBack = (captureSnapshot) => defineTool({
|