@browserbasehq/orca 3.6.0-gemini-cua → 3.7.1-preview.0
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/dist/cjs/lib/modelUtils.d.ts +10 -0
- package/dist/cjs/lib/modelUtils.js +11 -0
- package/dist/cjs/lib/modelUtils.js.map +1 -1
- package/dist/cjs/lib/v3/agent/AgentClient.d.ts +2 -2
- package/dist/cjs/lib/v3/agent/AgentClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/AgentProvider.js +3 -0
- package/dist/cjs/lib/v3/agent/AgentProvider.js.map +1 -1
- package/dist/cjs/lib/v3/agent/AnthropicCUAClient.d.ts +4 -3
- package/dist/cjs/lib/v3/agent/AnthropicCUAClient.js +11 -9
- package/dist/cjs/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/GoogleCUAClient.d.ts +12 -3
- package/dist/cjs/lib/v3/agent/GoogleCUAClient.js +107 -22
- package/dist/cjs/lib/v3/agent/GoogleCUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/MicrosoftCUAClient.d.ts +2 -2
- package/dist/cjs/lib/v3/agent/MicrosoftCUAClient.js +2 -2
- package/dist/cjs/lib/v3/agent/MicrosoftCUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/OpenAICUAClient.d.ts +6 -3
- package/dist/cjs/lib/v3/agent/OpenAICUAClient.js +13 -7
- package/dist/cjs/lib/v3/agent/OpenAICUAClient.js.map +1 -1
- package/dist/cjs/lib/v3/agent/utils/handleDoneToolCall.d.ts +1 -0
- package/dist/cjs/lib/v3/agent/utils/handleDoneToolCall.js +43 -3
- package/dist/cjs/lib/v3/agent/utils/handleDoneToolCall.js.map +1 -1
- package/dist/cjs/lib/v3/api.js +9 -0
- package/dist/cjs/lib/v3/api.js.map +1 -1
- package/dist/cjs/lib/v3/cache/AgentCache.js +6 -1
- package/dist/cjs/lib/v3/cache/AgentCache.js.map +1 -1
- package/dist/cjs/lib/v3/external_clients/aisdk.js +2 -0
- package/dist/cjs/lib/v3/external_clients/aisdk.js.map +1 -1
- package/dist/cjs/lib/v3/handlers/v3AgentHandler.js +35 -11
- package/dist/cjs/lib/v3/handlers/v3AgentHandler.js.map +1 -1
- package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.js +32 -18
- package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.js.map +1 -1
- package/dist/cjs/lib/v3/index.d.ts +2 -1
- package/dist/cjs/lib/v3/llm/LLMProvider.d.ts +2 -2
- package/dist/cjs/lib/v3/llm/LLMProvider.js +9 -2
- package/dist/cjs/lib/v3/llm/LLMProvider.js.map +1 -1
- package/dist/cjs/lib/v3/llm/aisdk.js +2 -0
- package/dist/cjs/lib/v3/llm/aisdk.js.map +1 -1
- package/dist/cjs/lib/v3/llm/providerOptions.d.ts +7 -0
- package/dist/cjs/lib/v3/llm/providerOptions.js +9 -0
- package/dist/cjs/lib/v3/llm/providerOptions.js.map +1 -0
- package/dist/cjs/lib/v3/types/private/agent.d.ts +1 -1
- package/dist/cjs/lib/v3/types/private/agent.js +1 -0
- package/dist/cjs/lib/v3/types/private/agent.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/agent.d.ts +10 -1
- package/dist/cjs/lib/v3/types/public/agent.js +3 -0
- package/dist/cjs/lib/v3/types/public/agent.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/context.d.ts +7 -0
- package/dist/cjs/lib/v3/types/public/context.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/model.d.ts +7 -0
- package/dist/cjs/lib/v3/types/public/model.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/sdkErrors.d.ts +4 -0
- package/dist/cjs/lib/v3/types/public/sdkErrors.js +9 -1
- package/dist/cjs/lib/v3/types/public/sdkErrors.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/capture.js +4 -3
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/capture.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/cdp.d.ts +2 -0
- package/dist/cjs/lib/v3/understudy/cdp.js +26 -12
- package/dist/cjs/lib/v3/understudy/cdp.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/context.d.ts +13 -1
- package/dist/cjs/lib/v3/understudy/context.js +284 -1
- package/dist/cjs/lib/v3/understudy/context.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/domainPolicy.d.ts +25 -0
- package/dist/cjs/lib/v3/understudy/domainPolicy.js +162 -0
- package/dist/cjs/lib/v3/understudy/domainPolicy.js.map +1 -0
- package/dist/cjs/lib/v3/v3.js +43 -4
- package/dist/cjs/lib/v3/v3.js.map +1 -1
- package/dist/cjs/lib/v3/verifier/types.d.ts +3 -2
- package/dist/cjs/lib/v3/verifier/types.js.map +1 -1
- package/dist/cjs/lib/version.d.ts +1 -1
- package/dist/cjs/lib/version.js +1 -1
- package/dist/cjs/lib/version.js.map +1 -1
- package/dist/esm/lib/modelUtils.d.ts +10 -0
- package/dist/esm/lib/modelUtils.js +9 -0
- package/dist/esm/lib/modelUtils.js.map +1 -1
- package/dist/esm/lib/v3/agent/AgentClient.d.ts +2 -2
- package/dist/esm/lib/v3/agent/AgentClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/AgentProvider.js +3 -0
- package/dist/esm/lib/v3/agent/AgentProvider.js.map +1 -1
- package/dist/esm/lib/v3/agent/AnthropicCUAClient.d.ts +4 -3
- package/dist/esm/lib/v3/agent/AnthropicCUAClient.js +11 -9
- package/dist/esm/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/GoogleCUAClient.d.ts +12 -3
- package/dist/esm/lib/v3/agent/GoogleCUAClient.js +107 -22
- package/dist/esm/lib/v3/agent/GoogleCUAClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/MicrosoftCUAClient.d.ts +2 -2
- package/dist/esm/lib/v3/agent/MicrosoftCUAClient.js +2 -2
- package/dist/esm/lib/v3/agent/MicrosoftCUAClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/OpenAICUAClient.d.ts +6 -3
- package/dist/esm/lib/v3/agent/OpenAICUAClient.js +13 -7
- package/dist/esm/lib/v3/agent/OpenAICUAClient.js.map +1 -1
- package/dist/esm/lib/v3/agent/utils/handleDoneToolCall.d.ts +1 -0
- package/dist/esm/lib/v3/agent/utils/handleDoneToolCall.js +42 -3
- package/dist/esm/lib/v3/agent/utils/handleDoneToolCall.js.map +1 -1
- package/dist/esm/lib/v3/api.js +9 -0
- package/dist/esm/lib/v3/api.js.map +1 -1
- package/dist/esm/lib/v3/cache/AgentCache.js +6 -1
- package/dist/esm/lib/v3/cache/AgentCache.js.map +1 -1
- package/dist/esm/lib/v3/external_clients/aisdk.js +2 -0
- package/dist/esm/lib/v3/external_clients/aisdk.js.map +1 -1
- package/dist/esm/lib/v3/handlers/v3AgentHandler.js +35 -11
- package/dist/esm/lib/v3/handlers/v3AgentHandler.js.map +1 -1
- package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.js +32 -18
- package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.js.map +1 -1
- package/dist/esm/lib/v3/index.d.ts +2 -1
- package/dist/esm/lib/v3/llm/LLMProvider.d.ts +2 -2
- package/dist/esm/lib/v3/llm/LLMProvider.js +9 -2
- package/dist/esm/lib/v3/llm/LLMProvider.js.map +1 -1
- package/dist/esm/lib/v3/llm/aisdk.js +2 -0
- package/dist/esm/lib/v3/llm/aisdk.js.map +1 -1
- package/dist/esm/lib/v3/llm/providerOptions.d.ts +7 -0
- package/dist/esm/lib/v3/llm/providerOptions.js +6 -0
- package/dist/esm/lib/v3/llm/providerOptions.js.map +1 -0
- package/dist/esm/lib/v3/types/private/agent.d.ts +1 -1
- package/dist/esm/lib/v3/types/private/agent.js +1 -0
- package/dist/esm/lib/v3/types/private/agent.js.map +1 -1
- package/dist/esm/lib/v3/types/public/agent.d.ts +10 -1
- package/dist/esm/lib/v3/types/public/agent.js +3 -0
- package/dist/esm/lib/v3/types/public/agent.js.map +1 -1
- package/dist/esm/lib/v3/types/public/context.d.ts +7 -0
- package/dist/esm/lib/v3/types/public/context.js.map +1 -1
- package/dist/esm/lib/v3/types/public/model.d.ts +7 -0
- package/dist/esm/lib/v3/types/public/model.js.map +1 -1
- package/dist/esm/lib/v3/types/public/sdkErrors.d.ts +4 -0
- package/dist/esm/lib/v3/types/public/sdkErrors.js +7 -0
- package/dist/esm/lib/v3/types/public/sdkErrors.js.map +1 -1
- package/dist/esm/lib/v3/understudy/a11y/snapshot/capture.js +4 -3
- package/dist/esm/lib/v3/understudy/a11y/snapshot/capture.js.map +1 -1
- package/dist/esm/lib/v3/understudy/cdp.d.ts +2 -0
- package/dist/esm/lib/v3/understudy/cdp.js +26 -12
- package/dist/esm/lib/v3/understudy/cdp.js.map +1 -1
- package/dist/esm/lib/v3/understudy/context.d.ts +13 -1
- package/dist/esm/lib/v3/understudy/context.js +285 -2
- package/dist/esm/lib/v3/understudy/context.js.map +1 -1
- package/dist/esm/lib/v3/understudy/domainPolicy.d.ts +25 -0
- package/dist/esm/lib/v3/understudy/domainPolicy.js +158 -0
- package/dist/esm/lib/v3/understudy/domainPolicy.js.map +1 -0
- package/dist/esm/lib/v3/v3.js +44 -5
- package/dist/esm/lib/v3/v3.js.map +1 -1
- package/dist/esm/lib/v3/verifier/types.d.ts +3 -2
- package/dist/esm/lib/v3/verifier/types.js.map +1 -1
- package/dist/esm/lib/version.d.ts +1 -1
- package/dist/esm/lib/version.js +1 -1
- package/dist/esm/lib/version.js.map +1 -1
- package/package.json +3 -3
|
@@ -6,9 +6,14 @@ import { v3ScriptContent } from "../dom/build/scriptV3Content.js";
|
|
|
6
6
|
import { executionContexts } from "./executionContextRegistry.js";
|
|
7
7
|
import { normalizeInitScriptSource } from "./initScripts.js";
|
|
8
8
|
import { ContextClipboard } from "./clipboard.js";
|
|
9
|
-
import { TimeoutError, CookieSetError, PageNotFoundError, StagehandSetExtraHTTPHeadersError, } from "../types/public/sdkErrors.js";
|
|
9
|
+
import { TimeoutError, CookieSetError, PageNotFoundError, StagehandSetExtraHTTPHeadersError, StagehandSetDomainPolicyError, } from "../types/public/sdkErrors.js";
|
|
10
10
|
import { getEnvTimeoutMs, withTimeout } from "../timeoutConfig.js";
|
|
11
11
|
import { filterCookies, normalizeCookieParams, cookieMatchesFilter, toCdpCookieParam, } from "./cookies.js";
|
|
12
|
+
import { getDomainPolicyDecision, normalizeDomainPolicy, } from "./domainPolicy.js";
|
|
13
|
+
function isMissingTargetError(error) {
|
|
14
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15
|
+
return /No target with given id found/i.test(message);
|
|
16
|
+
}
|
|
12
17
|
/**
|
|
13
18
|
* Returns true when the target's URL points to a document with a real,
|
|
14
19
|
* pierceable HTML DOM. We allowlist the small set of schemes that carry
|
|
@@ -79,6 +84,7 @@ export class V3Context {
|
|
|
79
84
|
// Timestamp for most recent popup/open signal
|
|
80
85
|
_lastPopupSignalAt = 0;
|
|
81
86
|
_targetSessionListeners = new Set();
|
|
87
|
+
_domainPolicySessionListeners = new Map();
|
|
82
88
|
_sessionInit = new Set();
|
|
83
89
|
pagesByTarget = new Map();
|
|
84
90
|
mainFrameToTarget = new Map();
|
|
@@ -89,8 +95,17 @@ export class V3Context {
|
|
|
89
95
|
typeByTarget = new Map();
|
|
90
96
|
_pageOrder = [];
|
|
91
97
|
pendingCreatedTargetUrl = new Map();
|
|
98
|
+
pageCreationFailures = new Map();
|
|
99
|
+
// Popup close attempts can race targetCreated, targetInfoChanged, and attached.
|
|
100
|
+
// In-flight promises let attach wait for a close result before deciding whether
|
|
101
|
+
// to skip normal setup. Successful closes stay deduped for the context lifetime
|
|
102
|
+
// because Chrome can emit late targetInfoChanged events after targetDestroyed.
|
|
103
|
+
// Failed closes are not retained so attach can continue and future events may retry.
|
|
104
|
+
domainPolicyClosingTargets = new Set();
|
|
105
|
+
domainPolicyClosePromises = new Map();
|
|
92
106
|
initScripts = [];
|
|
93
107
|
extraHttpHeaders = null;
|
|
108
|
+
domainPolicy = null;
|
|
94
109
|
_clipboard;
|
|
95
110
|
installTargetSessionListeners(session) {
|
|
96
111
|
const sessionId = session.id;
|
|
@@ -329,6 +344,120 @@ export class V3Context {
|
|
|
329
344
|
throw new StagehandSetExtraHTTPHeadersError(failures);
|
|
330
345
|
}
|
|
331
346
|
}
|
|
347
|
+
getDomainPolicy() {
|
|
348
|
+
if (!this.domainPolicy)
|
|
349
|
+
return null;
|
|
350
|
+
return {
|
|
351
|
+
...(this.domainPolicy.allowedDomains.length
|
|
352
|
+
? { allowedDomains: [...this.domainPolicy.allowedDomains] }
|
|
353
|
+
: {}),
|
|
354
|
+
...(this.domainPolicy.blockedDomains.length
|
|
355
|
+
? { blockedDomains: [...this.domainPolicy.blockedDomains] }
|
|
356
|
+
: {}),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
async setDomainPolicy(policy) {
|
|
360
|
+
const nextPolicy = normalizeDomainPolicy(policy);
|
|
361
|
+
this.domainPolicy = nextPolicy;
|
|
362
|
+
const sessions = [];
|
|
363
|
+
for (const sessionId of this._sessionInit) {
|
|
364
|
+
const session = this.conn.getSession(sessionId);
|
|
365
|
+
if (session)
|
|
366
|
+
sessions.push(session);
|
|
367
|
+
}
|
|
368
|
+
if (!sessions.length)
|
|
369
|
+
return;
|
|
370
|
+
const results = await Promise.allSettled(sessions.map(async (session) => {
|
|
371
|
+
if (!nextPolicy) {
|
|
372
|
+
try {
|
|
373
|
+
await session.send("Fetch.disable");
|
|
374
|
+
this.uninstallDomainPolicyHandler(session);
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
throw { action: "disable", error };
|
|
378
|
+
}
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
this.installDomainPolicyHandler(session);
|
|
382
|
+
try {
|
|
383
|
+
await session.send("Fetch.enable", {
|
|
384
|
+
patterns: nextPolicy.fetchPatterns,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
throw { action: "enable", error };
|
|
389
|
+
}
|
|
390
|
+
}));
|
|
391
|
+
const failures = results
|
|
392
|
+
.map((result, index) => ({ result, session: sessions[index] }))
|
|
393
|
+
.filter((entry) => entry.result.status === "rejected")
|
|
394
|
+
.map((entry) => {
|
|
395
|
+
const failure = entry.result.reason;
|
|
396
|
+
if (failure?.action === "enable") {
|
|
397
|
+
this.uninstallDomainPolicyHandler(entry.session);
|
|
398
|
+
}
|
|
399
|
+
const reason = failure?.error ?? entry.result.reason;
|
|
400
|
+
const sid = entry.session.id ?? "unknown";
|
|
401
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
402
|
+
return `session=${sid} error=${message}`;
|
|
403
|
+
});
|
|
404
|
+
if (failures.length) {
|
|
405
|
+
throw new StagehandSetDomainPolicyError(failures);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
installDomainPolicyHandler(session) {
|
|
409
|
+
const sessionId = session.id;
|
|
410
|
+
if (!sessionId)
|
|
411
|
+
return;
|
|
412
|
+
if (this._domainPolicySessionListeners.has(sessionId))
|
|
413
|
+
return;
|
|
414
|
+
const handler = (evt) => {
|
|
415
|
+
void this.handleDomainPolicyRequestPaused(session, evt);
|
|
416
|
+
};
|
|
417
|
+
this._domainPolicySessionListeners.set(sessionId, handler);
|
|
418
|
+
session.on("Fetch.requestPaused", handler);
|
|
419
|
+
}
|
|
420
|
+
uninstallDomainPolicyHandler(session) {
|
|
421
|
+
const sessionId = session.id;
|
|
422
|
+
if (!sessionId)
|
|
423
|
+
return;
|
|
424
|
+
const handler = this._domainPolicySessionListeners.get(sessionId);
|
|
425
|
+
if (!handler)
|
|
426
|
+
return;
|
|
427
|
+
session.off("Fetch.requestPaused", handler);
|
|
428
|
+
this._domainPolicySessionListeners.delete(sessionId);
|
|
429
|
+
}
|
|
430
|
+
async handleDomainPolicyRequestPaused(session, evt) {
|
|
431
|
+
const decision = getDomainPolicyDecision(evt.request.url, this.domainPolicy);
|
|
432
|
+
if (decision.action === "continue") {
|
|
433
|
+
await session
|
|
434
|
+
.send("Fetch.continueRequest", { requestId: evt.requestId })
|
|
435
|
+
.catch(() => { });
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
let hostname = "";
|
|
439
|
+
try {
|
|
440
|
+
hostname = new URL(evt.request.url).hostname.toLowerCase();
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
// ignore malformed URLs for logging
|
|
444
|
+
}
|
|
445
|
+
v3Logger({
|
|
446
|
+
category: "network",
|
|
447
|
+
message: "Blocked request by domain policy",
|
|
448
|
+
level: 2,
|
|
449
|
+
auxiliary: {
|
|
450
|
+
hostname: { value: hostname, type: "string" },
|
|
451
|
+
ruleType: { value: decision.reason, type: "string" },
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
await session
|
|
455
|
+
.send("Fetch.failRequest", {
|
|
456
|
+
requestId: evt.requestId,
|
|
457
|
+
errorReason: "BlockedByClient",
|
|
458
|
+
})
|
|
459
|
+
.catch(() => { });
|
|
460
|
+
}
|
|
332
461
|
get clipboard() {
|
|
333
462
|
return (this._clipboard ??= new ContextClipboard({
|
|
334
463
|
context: this,
|
|
@@ -397,6 +526,12 @@ export class V3Context {
|
|
|
397
526
|
await this.conn.send("Target.activateTarget", { targetId }).catch(() => { });
|
|
398
527
|
const deadline = Date.now() + 5000;
|
|
399
528
|
while (Date.now() < deadline) {
|
|
529
|
+
const failure = this.pageCreationFailures.get(targetId);
|
|
530
|
+
if (failure) {
|
|
531
|
+
this.pageCreationFailures.delete(targetId);
|
|
532
|
+
this.pendingCreatedTargetUrl.delete(targetId);
|
|
533
|
+
throw failure;
|
|
534
|
+
}
|
|
400
535
|
const page = this.pagesByTarget.get(targetId);
|
|
401
536
|
if (page) {
|
|
402
537
|
// we created at about:blank; navigate only after attach so init scripts run
|
|
@@ -412,6 +547,7 @@ export class V3Context {
|
|
|
412
547
|
}
|
|
413
548
|
await new Promise((r) => setTimeout(r, 25));
|
|
414
549
|
}
|
|
550
|
+
this.pendingCreatedTargetUrl.delete(targetId);
|
|
415
551
|
throw new TimeoutError(`newPage: target not attached (${targetId})`, 5000);
|
|
416
552
|
}
|
|
417
553
|
/**
|
|
@@ -427,6 +563,9 @@ export class V3Context {
|
|
|
427
563
|
this.createdAtByTarget.clear();
|
|
428
564
|
this.typeByTarget.clear();
|
|
429
565
|
this.pendingCreatedTargetUrl.clear();
|
|
566
|
+
this.pageCreationFailures.clear();
|
|
567
|
+
this.domainPolicyClosingTargets.clear();
|
|
568
|
+
this.domainPolicyClosePromises.clear();
|
|
430
569
|
}
|
|
431
570
|
/**
|
|
432
571
|
* Bootstrap target lifecycle:
|
|
@@ -453,8 +592,12 @@ export class V3Context {
|
|
|
453
592
|
const ti = info;
|
|
454
593
|
if (info.type === "page" && (ti?.openerId || ti?.openerFrameId)) {
|
|
455
594
|
this._notePopupSignal();
|
|
595
|
+
void this.closePopupIfBlockedByDomainPolicy(info, "targetCreated");
|
|
456
596
|
}
|
|
457
597
|
});
|
|
598
|
+
this.conn.on("Target.targetInfoChanged", (evt) => {
|
|
599
|
+
void this.closePopupIfBlockedByDomainPolicy(evt.targetInfo, "targetInfoChanged");
|
|
600
|
+
});
|
|
458
601
|
// Only enable auto-attach after listeners are ready so replayed targets are captured.
|
|
459
602
|
await this.conn.enableAutoAttach();
|
|
460
603
|
const targets = await this.conn.getTargets();
|
|
@@ -482,6 +625,9 @@ export class V3Context {
|
|
|
482
625
|
* - Resume the target only after listeners are wired.
|
|
483
626
|
*/
|
|
484
627
|
async onAttachedToTarget(info, sessionId) {
|
|
628
|
+
if (await this.closePopupIfBlockedByDomainPolicy(info, "attached")) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
485
631
|
// Skip non-web targets (workers, chrome extensions, background pages, etc.).
|
|
486
632
|
// They still need to be resumed so we don't leave them paused by
|
|
487
633
|
// waitForDebuggerOnStart, but injecting the piercer into these targets
|
|
@@ -533,6 +679,24 @@ export class V3Context {
|
|
|
533
679
|
.catch(() => false);
|
|
534
680
|
return { dispatched, response };
|
|
535
681
|
};
|
|
682
|
+
const queueFetchEnablePreResume = (params) => {
|
|
683
|
+
let error;
|
|
684
|
+
const dispatched = this.conn
|
|
685
|
+
.waitForSessionDispatch(sessionId, "Fetch.enable")
|
|
686
|
+
.then(() => true)
|
|
687
|
+
.catch((err) => {
|
|
688
|
+
error = err;
|
|
689
|
+
return false;
|
|
690
|
+
});
|
|
691
|
+
const response = session
|
|
692
|
+
.send("Fetch.enable", params)
|
|
693
|
+
.then(() => true)
|
|
694
|
+
.catch((err) => {
|
|
695
|
+
error = err;
|
|
696
|
+
return false;
|
|
697
|
+
});
|
|
698
|
+
return { dispatched, response, getError: () => error };
|
|
699
|
+
};
|
|
536
700
|
const initScriptOps = [];
|
|
537
701
|
// Pre-resume ordering matters:
|
|
538
702
|
// - enable domains;
|
|
@@ -554,6 +718,13 @@ export class V3Context {
|
|
|
554
718
|
headerPreResumeOps.push(queuePreResume("Network.enable"));
|
|
555
719
|
headerPreResumeOps.push(queuePreResume("Network.setExtraHTTPHeaders", { headers }));
|
|
556
720
|
}
|
|
721
|
+
const fetchPreResumeOps = [];
|
|
722
|
+
if (this.domainPolicy) {
|
|
723
|
+
this.installDomainPolicyHandler(session);
|
|
724
|
+
fetchPreResumeOps.push(queueFetchEnablePreResume({
|
|
725
|
+
patterns: this.domainPolicy.fetchPatterns,
|
|
726
|
+
}));
|
|
727
|
+
}
|
|
557
728
|
// Send init scripts only after auto-attach has been queued.
|
|
558
729
|
if (this.initScripts.length) {
|
|
559
730
|
for (const source of this.initScripts) {
|
|
@@ -572,6 +743,7 @@ export class V3Context {
|
|
|
572
743
|
const preResumeDispatched = (await Promise.all([
|
|
573
744
|
...corePreResumeOps.map((op) => op.dispatched),
|
|
574
745
|
...headerPreResumeOps.map((op) => op.dispatched),
|
|
746
|
+
...fetchPreResumeOps.map((op) => op.dispatched),
|
|
575
747
|
...initScriptOps.map((op) => op.dispatched),
|
|
576
748
|
piercerPreloadOp.dispatched,
|
|
577
749
|
])).every(Boolean);
|
|
@@ -581,9 +753,10 @@ export class V3Context {
|
|
|
581
753
|
resumeOp.dispatched,
|
|
582
754
|
resumeOp.response,
|
|
583
755
|
]);
|
|
584
|
-
const [coreResults, headerResults, initScriptResults, piercerPreRegistered,] = await Promise.all([
|
|
756
|
+
const [coreResults, headerResults, fetchResults, initScriptResults, piercerPreRegistered,] = await Promise.all([
|
|
585
757
|
Promise.all(corePreResumeOps.map((op) => op.response)),
|
|
586
758
|
Promise.all(headerPreResumeOps.map((op) => op.response)),
|
|
759
|
+
Promise.all(fetchPreResumeOps.map((op) => op.response)),
|
|
587
760
|
Promise.all(initScriptOps.map((op) => op.response)),
|
|
588
761
|
piercerPreloadOp.response,
|
|
589
762
|
]);
|
|
@@ -616,6 +789,40 @@ export class V3Context {
|
|
|
616
789
|
return;
|
|
617
790
|
}
|
|
618
791
|
resumed = true;
|
|
792
|
+
if (fetchPreResumeOps.length > 0 && !fetchResults.every(Boolean)) {
|
|
793
|
+
this.uninstallDomainPolicyHandler(session);
|
|
794
|
+
const fetchError = fetchPreResumeOps
|
|
795
|
+
.map((op) => op.getError())
|
|
796
|
+
.find((error) => error !== undefined);
|
|
797
|
+
const fetchErrorMessage = fetchError
|
|
798
|
+
? fetchError instanceof Error
|
|
799
|
+
? fetchError.message
|
|
800
|
+
: String(fetchError)
|
|
801
|
+
: "Fetch.enable failed during target attach";
|
|
802
|
+
const policyFailureMessage = "Fetch.enable failed during target attach; closing target because " +
|
|
803
|
+
"Stagehand cannot guarantee domain policy enforcement";
|
|
804
|
+
if (this.pendingCreatedTargetUrl.has(info.targetId)) {
|
|
805
|
+
this.pageCreationFailures.set(info.targetId, new StagehandSetDomainPolicyError([
|
|
806
|
+
`session=${sessionId} target=${info.targetId} error=${policyFailureMessage} cdpError=${fetchErrorMessage}`,
|
|
807
|
+
]));
|
|
808
|
+
}
|
|
809
|
+
v3Logger({
|
|
810
|
+
category: "ctx",
|
|
811
|
+
message: "Closing target because domain policy could not be guaranteed",
|
|
812
|
+
level: 0,
|
|
813
|
+
auxiliary: {
|
|
814
|
+
targetId: { value: String(info.targetId), type: "string" },
|
|
815
|
+
targetType: { value: String(info.type), type: "string" },
|
|
816
|
+
targetUrl: { value: String(info.url ?? ""), type: "string" },
|
|
817
|
+
sessionId: { value: sessionId, type: "string" },
|
|
818
|
+
cdpError: { value: fetchErrorMessage, type: "string" },
|
|
819
|
+
},
|
|
820
|
+
});
|
|
821
|
+
await this.conn
|
|
822
|
+
.send("Target.closeTarget", { targetId: info.targetId })
|
|
823
|
+
.catch(() => { });
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
619
826
|
const scriptsInstalled = coreResults.every(Boolean) && initScriptResults.every(Boolean);
|
|
620
827
|
try {
|
|
621
828
|
// Best-effort lifecycle events; do not block top-level page registration
|
|
@@ -731,6 +938,74 @@ export class V3Context {
|
|
|
731
938
|
await resume();
|
|
732
939
|
}
|
|
733
940
|
}
|
|
941
|
+
async closePopupIfBlockedByDomainPolicy(info, source) {
|
|
942
|
+
if (!this.domainPolicy || !isTopLevelPage(info))
|
|
943
|
+
return false;
|
|
944
|
+
if (!info.openerId && !info.openerFrameId)
|
|
945
|
+
return false;
|
|
946
|
+
if (this.domainPolicyClosingTargets.has(info.targetId))
|
|
947
|
+
return true;
|
|
948
|
+
const existingClose = this.domainPolicyClosePromises.get(info.targetId);
|
|
949
|
+
if (existingClose) {
|
|
950
|
+
return source === "attached" ? await existingClose : true;
|
|
951
|
+
}
|
|
952
|
+
const decision = getDomainPolicyDecision(info.url ?? "", this.domainPolicy);
|
|
953
|
+
if (decision.action === "continue")
|
|
954
|
+
return false;
|
|
955
|
+
v3Logger({
|
|
956
|
+
category: "network",
|
|
957
|
+
message: "Popup reached a disallowed domain before it could be intercepted; closing it",
|
|
958
|
+
level: 2,
|
|
959
|
+
auxiliary: {
|
|
960
|
+
targetId: { value: String(info.targetId), type: "string" },
|
|
961
|
+
targetUrl: { value: String(info.url ?? ""), type: "string" },
|
|
962
|
+
openerId: { value: String(info.openerId ?? ""), type: "string" },
|
|
963
|
+
openerFrameId: {
|
|
964
|
+
value: String(info.openerFrameId ?? ""),
|
|
965
|
+
type: "string",
|
|
966
|
+
},
|
|
967
|
+
ruleType: { value: decision.reason, type: "string" },
|
|
968
|
+
source: { value: source, type: "string" },
|
|
969
|
+
},
|
|
970
|
+
});
|
|
971
|
+
const closePromise = this.closeTargetAfterDomainPolicyViolation(info, {
|
|
972
|
+
failureMessage: "Failed to close popup after it reached a disallowed domain",
|
|
973
|
+
}).then((closed) => {
|
|
974
|
+
this.domainPolicyClosePromises.delete(info.targetId);
|
|
975
|
+
if (closed) {
|
|
976
|
+
this.domainPolicyClosingTargets.add(info.targetId);
|
|
977
|
+
}
|
|
978
|
+
return closed;
|
|
979
|
+
});
|
|
980
|
+
this.domainPolicyClosePromises.set(info.targetId, closePromise);
|
|
981
|
+
return await closePromise;
|
|
982
|
+
}
|
|
983
|
+
async closeTargetAfterDomainPolicyViolation(info, opts) {
|
|
984
|
+
try {
|
|
985
|
+
await this.conn.send("Target.closeTarget", { targetId: info.targetId });
|
|
986
|
+
return true;
|
|
987
|
+
}
|
|
988
|
+
catch (error) {
|
|
989
|
+
if (isMissingTargetError(error)) {
|
|
990
|
+
return true;
|
|
991
|
+
}
|
|
992
|
+
v3Logger({
|
|
993
|
+
category: "network",
|
|
994
|
+
message: opts.failureMessage,
|
|
995
|
+
level: 0,
|
|
996
|
+
auxiliary: {
|
|
997
|
+
targetId: { value: String(info.targetId), type: "string" },
|
|
998
|
+
targetType: { value: String(info.type), type: "string" },
|
|
999
|
+
targetUrl: { value: String(info.url ?? ""), type: "string" },
|
|
1000
|
+
error: {
|
|
1001
|
+
value: error instanceof Error ? error.message : String(error),
|
|
1002
|
+
type: "string",
|
|
1003
|
+
},
|
|
1004
|
+
},
|
|
1005
|
+
});
|
|
1006
|
+
return false;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
734
1009
|
/**
|
|
735
1010
|
* Detach handler:
|
|
736
1011
|
* - Remove child session ownership and prune its subtree.
|
|
@@ -751,6 +1026,13 @@ export class V3Context {
|
|
|
751
1026
|
this.pendingOopifByMainFrame.delete(fid);
|
|
752
1027
|
}
|
|
753
1028
|
this._targetSessionListeners.delete(sessionId);
|
|
1029
|
+
const session = this.conn.getSession(sessionId);
|
|
1030
|
+
if (session) {
|
|
1031
|
+
this.uninstallDomainPolicyHandler(session);
|
|
1032
|
+
}
|
|
1033
|
+
else {
|
|
1034
|
+
this._domainPolicySessionListeners.delete(sessionId);
|
|
1035
|
+
}
|
|
754
1036
|
this._sessionInit.delete(sessionId);
|
|
755
1037
|
this._piercerInstalled.delete(sessionId);
|
|
756
1038
|
}
|
|
@@ -761,6 +1043,7 @@ export class V3Context {
|
|
|
761
1043
|
const page = this.pagesByTarget.get(targetId);
|
|
762
1044
|
if (!page)
|
|
763
1045
|
return;
|
|
1046
|
+
this.pageCreationFailures.delete(targetId);
|
|
764
1047
|
const mainId = page.mainFrameId();
|
|
765
1048
|
this.mainFrameToTarget.delete(mainId);
|
|
766
1049
|
this.frameOwnerPage.delete(mainId);
|