@achamm/veilbrowser 0.3.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/LICENSE +21 -0
- package/README.md +272 -0
- package/dist/browser.d.ts +16 -0
- package/dist/browser.js +43 -0
- package/dist/cdp.d.ts +39 -0
- package/dist/cdp.js +128 -0
- package/dist/human.d.ts +33 -0
- package/dist/human.js +73 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/launcher.d.ts +67 -0
- package/dist/launcher.js +272 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +156 -0
- package/dist/page.d.ts +194 -0
- package/dist/page.js +527 -0
- package/dist/stealth.d.ts +28 -0
- package/dist/stealth.js +74 -0
- package/package.json +54 -0
- package/src/browser.ts +43 -0
- package/src/cdp.ts +133 -0
- package/src/human.ts +82 -0
- package/src/index.ts +6 -0
- package/src/launcher.ts +325 -0
- package/src/mcp.ts +155 -0
- package/src/page.ts +571 -0
- package/src/stealth.ts +103 -0
package/dist/page.d.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent-facing page API.
|
|
3
|
+
*
|
|
4
|
+
* Design goal: an LLM agent should never write a CSS/XPath selector. Selectors
|
|
5
|
+
* are the #1 source of automation breakage. Instead, `snapshot()` returns the
|
|
6
|
+
* page as a flat, numbered list of meaningful elements pulled from Chrome's
|
|
7
|
+
* accessibility tree — the same semantic layer a screen reader sees. The agent
|
|
8
|
+
* acts on a stable integer `ref`; we resolve the geometry and drive real input.
|
|
9
|
+
*/
|
|
10
|
+
import type { CDP } from "./cdp.js";
|
|
11
|
+
import { type Point } from "./human.js";
|
|
12
|
+
export interface Element {
|
|
13
|
+
ref: number;
|
|
14
|
+
role: string;
|
|
15
|
+
name: string;
|
|
16
|
+
value?: string;
|
|
17
|
+
center: Point;
|
|
18
|
+
}
|
|
19
|
+
export interface Snapshot {
|
|
20
|
+
url: string;
|
|
21
|
+
title: string;
|
|
22
|
+
/** Human/agent-readable index, e.g. `[3] button "Sign in"`. */
|
|
23
|
+
text: string;
|
|
24
|
+
elements: Element[];
|
|
25
|
+
}
|
|
26
|
+
/** One account offered in a FedCM account chooser. */
|
|
27
|
+
export interface FedCmAccount {
|
|
28
|
+
accountId: string;
|
|
29
|
+
email?: string;
|
|
30
|
+
name?: string;
|
|
31
|
+
givenName?: string;
|
|
32
|
+
idpConfigUrl?: string;
|
|
33
|
+
}
|
|
34
|
+
/** A FedCM dialog Chrome would normally render as native browser UI. */
|
|
35
|
+
export interface FedCmDialog {
|
|
36
|
+
dialogId: string;
|
|
37
|
+
/** "AccountChooser" | "AutoReauthn" | "ConfirmIdpLogin" | "SelectAccount" ... */
|
|
38
|
+
type: string;
|
|
39
|
+
title?: string;
|
|
40
|
+
subtitle?: string;
|
|
41
|
+
accounts: FedCmAccount[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* True if `url` targets a loopback / private-network host. Fingerprinters
|
|
45
|
+
* (iphey, pixelscan, …) port-scan these from page JS to profile the machine's
|
|
46
|
+
* OTHER software — VNC on :5900, a local automation API on :3001, etc. — which
|
|
47
|
+
* also leaks your LAN to every site you visit. Exotic IP encodings (decimal,
|
|
48
|
+
* hex) are a known gap; real-world scanners use the canonical forms below.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isPrivateHost(url: string): boolean;
|
|
51
|
+
export declare class Page {
|
|
52
|
+
private cdp;
|
|
53
|
+
readonly sessionId: string;
|
|
54
|
+
private targetId?;
|
|
55
|
+
private rng;
|
|
56
|
+
private mouse;
|
|
57
|
+
private refs;
|
|
58
|
+
private closed;
|
|
59
|
+
private fedcmOff?;
|
|
60
|
+
private fedcmQueue;
|
|
61
|
+
private fedcmWaiters;
|
|
62
|
+
private lastFedcmDialogId?;
|
|
63
|
+
private blockPrivateOff?;
|
|
64
|
+
private mainFrameId?;
|
|
65
|
+
private topPrivate;
|
|
66
|
+
constructor(cdp: CDP, sessionId: string, targetId?: string | undefined);
|
|
67
|
+
/** Enable the domains we use and arm stealth injection on every document. */
|
|
68
|
+
init(opts?: {
|
|
69
|
+
maskWebgl?: boolean;
|
|
70
|
+
blockPrivateNetwork?: boolean;
|
|
71
|
+
}): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Inject cookies before navigating — e.g. a logged-in session transferred
|
|
74
|
+
* from another browser. Each cookie is a CDP CookieParam ({name, value,
|
|
75
|
+
* domain, path, secure, httpOnly, expires?, sameSite?}). Lets the browser
|
|
76
|
+
* ride an existing session instead of re-doing a bot-walled login.
|
|
77
|
+
*/
|
|
78
|
+
setCookies(cookies: Array<Record<string, any>>): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Scrub the "HeadlessChrome" token from the UA and the matching client-hint
|
|
81
|
+
* brands. headless=new leaks it in both navigator.userAgent AND the Sec-CH-UA
|
|
82
|
+
* request headers; setUserAgentOverride with metadata fixes both at once. A
|
|
83
|
+
* no-op for headful Chrome, whose UA is already clean.
|
|
84
|
+
*/
|
|
85
|
+
private normalizeUserAgent;
|
|
86
|
+
private send;
|
|
87
|
+
/** Navigate and wait for the load event. */
|
|
88
|
+
goto(url: string, opts?: {
|
|
89
|
+
timeout?: number;
|
|
90
|
+
}): Promise<void>;
|
|
91
|
+
/** Evaluate JS in the page WITHOUT Runtime.enable (avoids the CDP tell). */
|
|
92
|
+
evaluate<T = any>(expression: string): Promise<T>;
|
|
93
|
+
url(): Promise<string>;
|
|
94
|
+
/** Build the numbered element index from the accessibility tree. */
|
|
95
|
+
snapshot(): Promise<Snapshot>;
|
|
96
|
+
private boxCenter;
|
|
97
|
+
/** Move the cursor along a human curve to a target point. */
|
|
98
|
+
private moveTo;
|
|
99
|
+
/** Click an element by its snapshot ref. */
|
|
100
|
+
click(ref: number): Promise<void>;
|
|
101
|
+
/** Bring this page's target to the foreground — CDP Input only routes to the active target. */
|
|
102
|
+
bringToFront(): Promise<void>;
|
|
103
|
+
/** Trusted click at absolute viewport coords (when you can't resolve a snapshot ref). */
|
|
104
|
+
clickAt(x: number, y: number): Promise<void>;
|
|
105
|
+
/** Type text into the focused element with human cadence. */
|
|
106
|
+
type(text: string): Promise<void>;
|
|
107
|
+
/** Click a field then type into it. */
|
|
108
|
+
fill(ref: number, text: string): Promise<void>;
|
|
109
|
+
/** Capture a PNG screenshot (Buffer) — feed to a vision model. */
|
|
110
|
+
screenshot(opts?: {
|
|
111
|
+
fullPage?: boolean;
|
|
112
|
+
}): Promise<Buffer>;
|
|
113
|
+
/** Poll an expression until truthy (replaces flaky fixed sleeps). */
|
|
114
|
+
waitFor(expression: string, opts?: {
|
|
115
|
+
timeout?: number;
|
|
116
|
+
poll?: number;
|
|
117
|
+
}): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Attach local files to a file `<input>` — even a hidden one — without an OS
|
|
120
|
+
* file picker. Uses CDP DOM.setFileInputFiles (the same primitive Playwright
|
|
121
|
+
* uses under the hood), which sets `input.files` and fires `change` directly.
|
|
122
|
+
* `selector` defaults to the first file input; pass a more specific one if the
|
|
123
|
+
* page has several. Paths must be absolute.
|
|
124
|
+
*/
|
|
125
|
+
uploadFile(paths: string[], selector?: string): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Attach files through a control that opens a file picker (e.g. an "Upload
|
|
128
|
+
* files" menu item) WITHOUT an OS dialog. Intercepts the chooser via CDP,
|
|
129
|
+
* clicks the trigger, then feeds the paths to the input it opened for. This is
|
|
130
|
+
* the path for SPAs (like Gemini) that create the `<input>` lazily on click.
|
|
131
|
+
* Paths must be absolute.
|
|
132
|
+
*/
|
|
133
|
+
uploadViaPicker(triggerRef: number, paths: string[], opts?: {
|
|
134
|
+
timeout?: number;
|
|
135
|
+
}): Promise<void>;
|
|
136
|
+
/** Read the page's visible text (for scraping a model response, etc.). */
|
|
137
|
+
innerText(): Promise<string>;
|
|
138
|
+
/** Press a single named key on the focused element (Enter, Tab, Escape, arrows...). */
|
|
139
|
+
press(key: string): Promise<void>;
|
|
140
|
+
/**
|
|
141
|
+
* Start intercepting FedCM on this page. Call it ON DEMAND, right before the
|
|
142
|
+
* sign-in you're driving — never as blanket startup setup. Any page that
|
|
143
|
+
* silently probes FedCM at load (GoHighLevel, many SaaS logins) will HANG if
|
|
144
|
+
* interception is on and nothing resolves the probe, so keep it off until you
|
|
145
|
+
* need it and disableFedCm() afterwards.
|
|
146
|
+
*
|
|
147
|
+
* With {autoSelectFirst:true} (default) veil selects account 0 on every dialog
|
|
148
|
+
* automatically — the one-liner for "just sign me in". Pass false to inspect
|
|
149
|
+
* accounts via waitForFedCmDialog() and choose with selectFedCmAccount().
|
|
150
|
+
*/
|
|
151
|
+
enableFedCm(opts?: {
|
|
152
|
+
autoSelectFirst?: boolean;
|
|
153
|
+
}): Promise<void>;
|
|
154
|
+
/** Resolve with the next FedCM dialog (or one already queued since enable). */
|
|
155
|
+
waitForFedCmDialog(opts?: {
|
|
156
|
+
timeout?: number;
|
|
157
|
+
}): Promise<FedCmDialog>;
|
|
158
|
+
/** Pick an account in the current FedCM dialog (index into dialog.accounts). */
|
|
159
|
+
selectFedCmAccount(accountIndex?: number, dialogId?: string | undefined): Promise<void>;
|
|
160
|
+
/** Dismiss the current FedCM dialog (decline the sign-in). */
|
|
161
|
+
dismissFedCm(dialogId?: string | undefined): Promise<void>;
|
|
162
|
+
/** Stop intercepting FedCM. Call after a sign-in so a later navigation that
|
|
163
|
+
* probes FedCM isn't left hanging on us. */
|
|
164
|
+
disableFedCm(): Promise<void>;
|
|
165
|
+
/**
|
|
166
|
+
* One call to complete an active federated sign-in: enables FedCM, clicks the
|
|
167
|
+
* "Sign in with Google" button (a snapshot ref), waits for the account
|
|
168
|
+
* chooser, selects an account, and returns it. For passive/one-tap flows that
|
|
169
|
+
* fire on page load, enableFedCm() BEFORE navigating, then
|
|
170
|
+
* waitForFedCmDialog() — the default autoSelectFirst signs you straight in.
|
|
171
|
+
*/
|
|
172
|
+
signInWithFedCm(opts?: {
|
|
173
|
+
triggerRef?: number;
|
|
174
|
+
accountIndex?: number;
|
|
175
|
+
timeout?: number;
|
|
176
|
+
}): Promise<FedCmAccount>;
|
|
177
|
+
/**
|
|
178
|
+
* Stop the page — and any site it loads — from reaching loopback / private
|
|
179
|
+
* hosts. Detectors port-scan 127.0.0.1 from JS to fingerprint the machine's
|
|
180
|
+
* other software (and it leaks your LAN to every site). With this on, each
|
|
181
|
+
* such request is failed UNIFORMLY (same instant error, open port or closed),
|
|
182
|
+
* so the scan can't tell them apart and comes back empty. Only private-host
|
|
183
|
+
* requests are intercepted, so normal browsing keeps its exact timing.
|
|
184
|
+
*
|
|
185
|
+
* Still allowed: the agent's own top-level navigation to a private host
|
|
186
|
+
* (page.goto("http://localhost:3000")), and a localhost page loading its own
|
|
187
|
+
* localhost resources — only a PUBLIC page reaching a private host is blocked.
|
|
188
|
+
*/
|
|
189
|
+
blockPrivateNetwork(): Promise<void>;
|
|
190
|
+
/** Lift the private-network block (re-allows localhost/LAN requests). */
|
|
191
|
+
unblockPrivateNetwork(): Promise<void>;
|
|
192
|
+
/** Close this page and detach its target from the browser. Idempotent. */
|
|
193
|
+
close(): Promise<void>;
|
|
194
|
+
}
|