-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathsession.ts
More file actions
108 lines (99 loc) · 4.41 KB
/
Copy pathsession.ts
File metadata and controls
108 lines (99 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
* Shared helpers for resolving a Playwright Page from a browser interceptor target ID.
* Used by humanizer and browser tools so they don't each re-walk the interceptor map.
*
* Resolves both cloakbrowser ("browser_*") and camoufox ("camoufox_*") targets.
* Camoufox entries don't carry a Page eagerly — the interceptor returns a WS
* endpoint and stops there. We connect lazily on first use via
* `firefox.connect(wsUrl)` and cache the Browser/Context/Page on the entry, so
* every `interceptor_browser_*` and `humanizer_*` tool call works identically
* across both engines.
*/
import { firefox, type Browser, type BrowserContext, type Page } from "playwright-core";
import { interceptorManager } from "../interceptors/manager.js";
import type { BrowserInterceptor, BrowserTargetEntry } from "../interceptors/browser.js";
import type { CamoufoxInterceptor, CamoufoxTargetEntry } from "../interceptors/camoufox.js";
interface CamoufoxDriverHandle {
browser?: Browser;
context?: BrowserContext;
page?: Page;
}
type CamoufoxEntryWithDriver = CamoufoxTargetEntry & CamoufoxDriverHandle;
function getBrowserInterceptor(): BrowserInterceptor {
const it = interceptorManager.get("browser") as BrowserInterceptor | undefined;
if (!it) throw new Error("Browser interceptor not registered.");
return it;
}
function getCamoufoxInterceptor(): CamoufoxInterceptor | undefined {
return interceptorManager.get("camoufox") as CamoufoxInterceptor | undefined;
}
function isCamoufoxTargetId(targetId: string): boolean {
return typeof targetId === "string" && targetId.startsWith("camoufox_");
}
async function ensureCamoufoxPage(entry: CamoufoxEntryWithDriver): Promise<Page> {
if (entry.page && !entry.page.isClosed()) return entry.page;
if (!entry.browser) {
entry.browser = await firefox.connect(entry.wsUrl);
}
let ctx = entry.browser.contexts()[0];
if (!ctx) {
// BrowserServer + persistent_context: the persistent context lives
// server-side and `Browser.contexts()` from a fresh `firefox.connect()`
// returns empty. New contexts created here do NOT inherit the
// launch-level proxy, so we have to wire the MITM proxy explicitly
// or the firefox process reaches the internet directly and bypasses
// capture. Pull the port back out of the entry details (set at
// activate() time).
const proxyPort = (entry.target.details as { proxyPort?: number } | undefined)?.proxyPort;
ctx = await entry.browser.newContext({
ignoreHTTPSErrors: true,
...(proxyPort ? { proxy: { server: `http://127.0.0.1:${proxyPort}` } } : {}),
});
}
let page = ctx.pages()[0];
if (!page) {
page = await ctx.newPage();
}
entry.context = ctx;
entry.page = page;
return page;
}
export function getEntry(targetId: string): BrowserTargetEntry | CamoufoxEntryWithDriver {
if (isCamoufoxTargetId(targetId)) {
const cam = getCamoufoxInterceptor();
const entry = cam?.getEntry(targetId) as CamoufoxEntryWithDriver | undefined;
if (!entry) throw new Error(`Browser target '${targetId}' not found. Is it still running?`);
return entry;
}
const entry = getBrowserInterceptor().getEntry(targetId);
if (!entry) throw new Error(`Browser target '${targetId}' not found. Is it still running?`);
return entry;
}
/**
* Cloakbrowser-only entry getter. Use when the caller needs cloakbrowser
* features that camoufox doesn't implement yet — `consoleBuffer` (event
* recording) or pre-warmed `context` (synchronous cookie access). Camoufox
* targets get a clear error instead of a deep type-mismatch.
*/
export function getBrowserEntry(targetId: string): BrowserTargetEntry {
if (isCamoufoxTargetId(targetId)) {
throw new Error(
`Tool not yet supported on camoufox targets ('${targetId}'). Use cloakbrowser ` +
`(interceptor_browser_launch) for console / cookie inspection until camoufox parity lands.`,
);
}
const entry = getBrowserInterceptor().getEntry(targetId);
if (!entry) throw new Error(`Browser target '${targetId}' not found. Is it still running?`);
return entry;
}
export async function getPageForTarget(targetId: string): Promise<Page> {
const entry = getEntry(targetId);
if (isCamoufoxTargetId(targetId)) {
return ensureCamoufoxPage(entry as CamoufoxEntryWithDriver);
}
const browserEntry = entry as BrowserTargetEntry;
if (browserEntry.page.isClosed()) {
throw new Error(`Page for browser target '${targetId}' is closed.`);
}
return browserEntry.page;
}