openchrome-mcp 1.8.11 → 1.8.13
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/README.md +106 -10
- package/dist/cdp/client.d.ts +1 -1
- package/dist/cdp/client.d.ts.map +1 -1
- package/dist/cdp/client.js +68 -35
- package/dist/cdp/client.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +15 -3
- package/dist/mcp-server.js.map +1 -1
- package/dist/session-manager.d.ts +6 -0
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +12 -0
- package/dist/session-manager.js.map +1 -1
- package/dist/stealth/fingerprint-defense.d.ts +20 -0
- package/dist/stealth/fingerprint-defense.d.ts.map +1 -0
- package/dist/stealth/fingerprint-defense.js +261 -0
- package/dist/stealth/fingerprint-defense.js.map +1 -0
- package/dist/stealth/human-behavior.d.ts +40 -0
- package/dist/stealth/human-behavior.d.ts.map +1 -0
- package/dist/stealth/human-behavior.js +194 -0
- package/dist/stealth/human-behavior.js.map +1 -0
- package/dist/stealth/index.d.ts +9 -0
- package/dist/stealth/index.d.ts.map +1 -0
- package/dist/stealth/index.js +19 -0
- package/dist/stealth/index.js.map +1 -0
- package/dist/tools/fill-form.d.ts.map +1 -1
- package/dist/tools/fill-form.js +29 -6
- package/dist/tools/fill-form.js.map +1 -1
- package/dist/tools/find.d.ts.map +1 -1
- package/dist/tools/find.js +7 -0
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/interact.d.ts.map +1 -1
- package/dist/tools/interact.js +15 -3
- package/dist/tools/interact.js.map +1 -1
- package/dist/tools/lightweight-scroll.d.ts.map +1 -1
- package/dist/tools/lightweight-scroll.js +19 -0
- package/dist/tools/lightweight-scroll.js.map +1 -1
- package/dist/tools/navigate.d.ts.map +1 -1
- package/dist/tools/navigate.js +6 -0
- package/dist/tools/navigate.js.map +1 -1
- package/dist/tools/read-page.js +1 -1
- package/dist/tools/read-page.js.map +1 -1
- package/dist/tools/wait-and-click.js +1 -1
- package/dist/tools/wait-and-click.js.map +1 -1
- package/dist/types/mcp.d.ts +16 -1
- package/dist/types/mcp.d.ts.map +1 -1
- package/dist/types/mcp.js +10 -0
- package/dist/types/mcp.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<h1 align="center">OpenChrome</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<b>
|
|
9
|
-
|
|
8
|
+
<b>Harness-Engineered Browser Automation</b><br>
|
|
9
|
+
The MCP server that guides AI agents instead of just exposing APIs.
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
@@ -32,12 +32,17 @@
|
|
|
32
32
|
| **RAM (20 parallel)** | **~300 MB** | ~5 GB+ | impractical | impractical |
|
|
33
33
|
| **Bot detection** | **invisible** (real Chrome) | detected (TLS fingerprint) | detected (CDP signals) | detected (local) / cloud only |
|
|
34
34
|
| **Chrome login reuse** | **built-in** | extension mode only | manual | manual state files |
|
|
35
|
-
| **LLM hang prevention** | **hint engine** (
|
|
36
|
-
| **
|
|
35
|
+
| **LLM hang prevention** | **hint engine** (30+ rules) | none | none | error rewrite (5 patterns) |
|
|
36
|
+
| **Reliability mechanisms** | **49** (8-layer defense) | ~3 | ~3 | ~5 |
|
|
37
|
+
| **Token compression** | **15x** (DOM serializer) | none | none | none |
|
|
38
|
+
| **Outcome classification** | **yes** (DOM delta) | none | none | none |
|
|
39
|
+
| **Cross-session learning** | **yes** (domain memory) | none | none | none |
|
|
40
|
+
| **Circuit breaker** | **3-level** | none | none | none |
|
|
41
|
+
| **Shadow DOM** | **all types** (open + closed) | open only | invisible | invisible |
|
|
37
42
|
| **MCP native** | **yes** | yes | yes | no (CLI only) |
|
|
38
43
|
| **Parallel sessions** | **1 Chrome, N tabs** | N browsers | manual tabs | N daemons |
|
|
39
44
|
|
|
40
|
-
> **tl;dr** — OpenChrome talks directly to Chrome via CDP with zero middleware, reuses your real login sessions
|
|
45
|
+
> **tl;dr** — OpenChrome talks directly to Chrome via CDP with zero middleware, reuses your real login sessions, and is the only browser MCP server with **harness engineering** — 27 intelligent subsystems that guide, protect, and optimize the AI agent at every step.
|
|
41
46
|
|
|
42
47
|
---
|
|
43
48
|
|
|
@@ -68,7 +73,9 @@ AI: [8 parallel workers, all sites simultaneously]
|
|
|
68
73
|
|
|
69
74
|
---
|
|
70
75
|
|
|
71
|
-
##
|
|
76
|
+
## Harness-Engineered, Not Just Automated
|
|
77
|
+
|
|
78
|
+
Traditional browser automation exposes raw APIs. When the AI agent fails, it's on its own — burning tokens guessing, retrying, and wandering. **Harness engineering** means the tool itself wraps intelligence around those APIs: preventing mistakes, recovering from errors, and guiding the agent toward efficient behavior.
|
|
72
79
|
|
|
73
80
|
The bottleneck in browser automation isn't the browser — it's the **LLM thinking between each step**. Every tool call costs 5–15 seconds of inference time. When an AI agent guesses wrong, it doesn't just fail — it spends another 10 seconds thinking about why, then another 10 seconds trying something else.
|
|
74
81
|
|
|
@@ -105,7 +112,7 @@ OpenChrome agent checking prices on 5 sites:
|
|
|
105
112
|
= ~20 LLM calls, ~15 seconds, ~$0.40
|
|
106
113
|
```
|
|
107
114
|
|
|
108
|
-
The hint engine watches every tool call across
|
|
115
|
+
The hint engine watches every tool call across 9 categories — error recovery, blocking page detection, composite suggestions, repetition detection, sequence detection, pagination detection, learned patterns, success guidance, and setup hints. When it sees the same error→recovery pattern 3+ times, it promotes it to a permanent rule across sessions via the Pattern Learner.
|
|
109
116
|
|
|
110
117
|
| | Playwright | OpenChrome | Savings |
|
|
111
118
|
|---|---|---|---|
|
|
@@ -114,6 +121,35 @@ The hint engine watches every tool call across 6 layers — error recovery, comp
|
|
|
114
121
|
| **Token cost** | ~$2.00 | ~$0.40 | **5x cheaper** |
|
|
115
122
|
| **Wasted calls** | ~95% | ~0% | |
|
|
116
123
|
|
|
124
|
+
### 27 Harness Features Across 7 Categories
|
|
125
|
+
|
|
126
|
+
OpenChrome isn't just a browser API — it's an intelligent harness with 27 subsystems that work together:
|
|
127
|
+
|
|
128
|
+
| Category | Key Features | What It Does |
|
|
129
|
+
|----------|-------------|--------------|
|
|
130
|
+
| **Guidance** | Hint Engine (30+ rules, 9 types), Progress Tracker, Usage Guide | Prevents mistakes before they cascade |
|
|
131
|
+
| **Resilience** | Ralph Engine (7-strategy waterfall), Auto-Reconnect, Ref Self-Healing | Recovers from failures automatically |
|
|
132
|
+
| **Protection** | 3-Level Circuit Breaker, Rate Limiter, Domain Guard | Stops runaway token waste |
|
|
133
|
+
| **Feedback** | Outcome Classifier, DOM Delta, Visual Summary, Hit Detection | Reports what *actually* happened |
|
|
134
|
+
| **Learning** | Pattern Learner, Strategy Learner, Domain Memory | Gets smarter across sessions |
|
|
135
|
+
| **Optimization** | DOM Mode (15x compression), Adaptive Screenshot, Snapshot Delta | Minimizes token consumption |
|
|
136
|
+
| **Detection** | Auth Redirect Detection, Blocking Page, Pagination Detector | Identifies situations early |
|
|
137
|
+
|
|
138
|
+
<details>
|
|
139
|
+
<summary>Feature highlights</summary>
|
|
140
|
+
|
|
141
|
+
**Hint Engine** — 30+ rules across 9 categories (error recovery, blocking page detection, repetition loops, pagination, composite suggestions, sequence optimization, learned patterns, success guidance, setup hints). Escalates from `info` → `warning` → `critical` as patterns repeat. The Progress Tracker detects stuck agents within 3-5 tool calls.
|
|
142
|
+
|
|
143
|
+
**Ralph Engine** — When an interaction fails, Ralph automatically tries 7 strategies in sequence: AX tree click → CSS discovery → CDP coordinate dispatch → JS injection → Keyboard navigation → Raw CDP mouse events → Human-in-the-loop escalation. Each attempt is classified by the Outcome Classifier (SUCCESS / SILENT_CLICK / WRONG_ELEMENT).
|
|
144
|
+
|
|
145
|
+
**3-Level Circuit Breaker** — Element level (3 failures → skip, 2min reset), Page level (5 distinct failures → suggest reload), Global level (10 failures in 5min → pause all). Prevents agents from burning tokens on permanently broken elements.
|
|
146
|
+
|
|
147
|
+
**Pattern Learner** — When a hint rule misses, the learner observes the next 3 tool calls. If a different tool succeeds, it records the error→recovery correlation. After 3 occurrences at 60%+ confidence, it promotes the pattern to a permanent rule that fires in future sessions.
|
|
148
|
+
|
|
149
|
+
**DOM Mode** — Serializes the full DOM into a compact text format: strips SCRIPT/STYLE/SVG, keeps only 18 actionable attributes, deduplicates repetitive siblings, collapses nested wrapper chains. **Benchmarked: ~12K tokens vs ~180K tokens** for the same page (15x compression).
|
|
150
|
+
|
|
151
|
+
</details>
|
|
152
|
+
|
|
117
153
|
---
|
|
118
154
|
|
|
119
155
|
## Quick Start
|
|
@@ -184,7 +220,7 @@ oc compare prices for "AirPods Pro" across Amazon, eBay, Walmart, Best Buy
|
|
|
184
220
|
|
|
185
221
|
---
|
|
186
222
|
|
|
187
|
-
##
|
|
223
|
+
## 46 Tools
|
|
188
224
|
|
|
189
225
|
| Category | Tools |
|
|
190
226
|
|----------|-------|
|
|
@@ -196,7 +232,7 @@ oc compare prices for "AirPods Pro" across Amazon, eBay, Walmart, Best Buy
|
|
|
196
232
|
| **Memory** | `memory_record`, `memory_query`, `memory_validate` |
|
|
197
233
|
|
|
198
234
|
<details>
|
|
199
|
-
<summary>Full tool list (
|
|
235
|
+
<summary>Full tool list (46)</summary>
|
|
200
236
|
|
|
201
237
|
`navigate` `interact` `computer` `read_page` `find` `form_input` `fill_form` `javascript_tool` `page_reload` `page_content` `page_pdf` `wait_for` `user_agent` `geolocation` `emulate_device` `network` `selector_query` `xpath_query` `cookies` `storage` `console_capture` `performance_metrics` `request_intercept` `drag_drop` `file_upload` `http_auth` `worker_create` `worker_list` `worker_update` `worker_complete` `worker_delete` `tabs_create` `tabs_context` `tabs_close` `workflow_init` `workflow_status` `workflow_collect` `workflow_collect_partial` `workflow_cleanup` `execute_plan` `batch_execute` `lightweight_scroll` `memory_record` `memory_query` `memory_validate` `oc_stop`
|
|
202
238
|
|
|
@@ -316,7 +352,7 @@ By default, benchmarks run in **stub mode** — measuring protocol correctness a
|
|
|
316
352
|
|
|
317
353
|
## Server / Headless Deployment
|
|
318
354
|
|
|
319
|
-
OpenChrome works on servers and in CI/CD pipelines without Chrome login. All
|
|
355
|
+
OpenChrome works on servers and in CI/CD pipelines without Chrome login. All 46 tools function with unauthenticated Chrome — navigation, scraping, screenshots, form filling, and parallel workflows all work in clean sessions.
|
|
320
356
|
|
|
321
357
|
### Quick start
|
|
322
358
|
|
|
@@ -389,6 +425,66 @@ openchrome serve \
|
|
|
389
425
|
|
|
390
426
|
---
|
|
391
427
|
|
|
428
|
+
## Under the Hood: 8-Layer Reliability
|
|
429
|
+
|
|
430
|
+
OpenChrome has **49 distinct reliability mechanisms** across 8 defense layers — ensuring no single failure can hang the MCP server.
|
|
431
|
+
|
|
432
|
+
```
|
|
433
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
434
|
+
│ Layer 7: MCP Gateway │
|
|
435
|
+
│ Rate limiter · Tool timeout (120s) · Error recovery hints │
|
|
436
|
+
├─────────────────────────────────────────────────────────────┤
|
|
437
|
+
│ Layer 6: Session Management │
|
|
438
|
+
│ TTL cleanup · Memory pressure · Target reconciliation │
|
|
439
|
+
├─────────────────────────────────────────────────────────────┤
|
|
440
|
+
│ Layer 5: Request Queue │
|
|
441
|
+
│ Per-session FIFO · Per-item timeout (120s) │
|
|
442
|
+
├─────────────────────────────────────────────────────────────┤
|
|
443
|
+
│ Layer 4: Circuit Breaker │
|
|
444
|
+
│ Element (3 fails) · Page (5 fails) · Global (10/5min) │
|
|
445
|
+
├─────────────────────────────────────────────────────────────┤
|
|
446
|
+
│ Layer 3: CDP Client │
|
|
447
|
+
│ Adaptive heartbeat · Stale target guard · Page defenses │
|
|
448
|
+
├─────────────────────────────────────────────────────────────┤
|
|
449
|
+
│ Layer 2: Reconnection Engine │
|
|
450
|
+
│ Auto-reconnect (5 retries) · Exponential backoff · Cookie │
|
|
451
|
+
│ restore · Sleep/wake detection │
|
|
452
|
+
├─────────────────────────────────────────────────────────────┤
|
|
453
|
+
│ Layer 1: Self-Healing │
|
|
454
|
+
│ Chrome watchdog · Tab health monitor · Event loop monitor │
|
|
455
|
+
│ Disk monitor · Health endpoint (/health, /metrics) │
|
|
456
|
+
├─────────────────────────────────────────────────────────────┤
|
|
457
|
+
│ Layer 0: Process Lifecycle │
|
|
458
|
+
│ Graceful shutdown · Orphan cleanup · Atomic file writes │
|
|
459
|
+
└─────────────────────────────────────────────────────────────┘
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**32 configurable timeouts** cover every operation from CDP commands (15s) to tool execution (120s) to Chrome launch (60s). Every timeout is independently tunable via `src/config/defaults.ts`.
|
|
463
|
+
|
|
464
|
+
## Element Intelligence
|
|
465
|
+
|
|
466
|
+
Finding elements by natural language instead of CSS selectors:
|
|
467
|
+
|
|
468
|
+
```
|
|
469
|
+
"Submit button" → normalizeQuery → parseQueryForAX → AX Tree Resolution
|
|
470
|
+
│
|
|
471
|
+
match found?
|
|
472
|
+
/ \
|
|
473
|
+
yes no
|
|
474
|
+
│ │
|
|
475
|
+
[AX result] CSS Fallback
|
|
476
|
+
+ Shadow DOM
|
|
477
|
+
+ Scoring
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
- **AX-first**: Uses Chrome's accessibility tree — framework-agnostic across React, Angular, Vue, Web Components
|
|
481
|
+
- **Cascading filter**: 4-level deterministic priority (exact role+name → role+contains → exact name → partial)
|
|
482
|
+
- **3-tier Shadow DOM**: Open roots (JS) + closed roots (CDP) + user-agent roots
|
|
483
|
+
- **Hit detection**: After clicking, reports what was actually hit + nearest interactive element
|
|
484
|
+
- **i18n**: Korean role keywords built-in (`"버튼"` → button, `"링크"` → link, `"드롭다운"` → combobox)
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
392
488
|
## Development
|
|
393
489
|
|
|
394
490
|
```bash
|
package/dist/cdp/client.d.ts
CHANGED
|
@@ -238,7 +238,7 @@ export declare class CDPClient {
|
|
|
238
238
|
* CDP observer attached. CDP is attached only after the settle window expires.
|
|
239
239
|
*
|
|
240
240
|
* @param url URL to open in the new tab
|
|
241
|
-
* @param settleMs
|
|
241
|
+
* @param settleMs Total settle time in ms — used for navigation timeout and post-nav wait (default 8000)
|
|
242
242
|
* @returns The Puppeteer Page and its targetId
|
|
243
243
|
*/
|
|
244
244
|
createTargetStealth(url: string, settleMs?: number): Promise<{
|
package/dist/cdp/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/cdp/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAkB,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/cdp/client.ts"],"names":[],"mappings":"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"}
|
package/dist/cdp/client.js
CHANGED
|
@@ -52,6 +52,7 @@ const defaults_1 = require("../config/defaults");
|
|
|
52
52
|
const with_timeout_1 = require("../utils/with-timeout");
|
|
53
53
|
const collector_1 = require("../metrics/collector");
|
|
54
54
|
const connection_1 = require("../errors/connection");
|
|
55
|
+
const fingerprint_defense_1 = require("../stealth/fingerprint-defense");
|
|
55
56
|
function parseEnvInt(name, fallback) {
|
|
56
57
|
const raw = process.env[name];
|
|
57
58
|
if (raw === undefined)
|
|
@@ -1214,28 +1215,31 @@ class CDPClient {
|
|
|
1214
1215
|
* CDP observer attached. CDP is attached only after the settle window expires.
|
|
1215
1216
|
*
|
|
1216
1217
|
* @param url URL to open in the new tab
|
|
1217
|
-
* @param settleMs
|
|
1218
|
+
* @param settleMs Total settle time in ms — used for navigation timeout and post-nav wait (default 8000)
|
|
1218
1219
|
* @returns The Puppeteer Page and its targetId
|
|
1219
1220
|
*/
|
|
1220
1221
|
async createTargetStealth(url, settleMs = 8000) {
|
|
1221
1222
|
const browser = this.getBrowser();
|
|
1222
|
-
//
|
|
1223
|
-
//
|
|
1224
|
-
//
|
|
1225
|
-
//
|
|
1226
|
-
//
|
|
1223
|
+
// Stealth v3 architecture (#450):
|
|
1224
|
+
// Instead of navigating directly to the target URL (which lets anti-bot scripts
|
|
1225
|
+
// fingerprint the raw browser before defenses are applied), we:
|
|
1226
|
+
// 1. Create tab with about:blank (no anti-bot scripts, no network request)
|
|
1227
|
+
// 2. Attach CDP immediately and register all defenses via evaluateOnNewDocument
|
|
1228
|
+
// 3. Navigate to the real URL — defenses fire at document_start, BEFORE any page JS
|
|
1229
|
+
// This closes the fingerprint timing gap that caused Access Denied on Coupang/Reddit.
|
|
1230
|
+
// Step 1: Create target with about:blank — invisible to anti-bot systems
|
|
1227
1231
|
const cdp = await browser.target().createCDPSession();
|
|
1228
1232
|
let targetId;
|
|
1229
1233
|
try {
|
|
1230
|
-
const result = await cdp.send('Target.createTarget', { url });
|
|
1234
|
+
const result = await cdp.send('Target.createTarget', { url: 'about:blank' });
|
|
1231
1235
|
targetId = result.targetId;
|
|
1232
1236
|
}
|
|
1233
1237
|
catch (createErr) {
|
|
1234
1238
|
await cdp.detach().catch(() => { });
|
|
1235
1239
|
throw new Error(`Stealth navigation: failed to create target: ${createErr instanceof Error ? createErr.message : String(createErr)}`);
|
|
1236
1240
|
}
|
|
1237
|
-
console.error(`[CDPClient] Stealth tab created: ${targetId},
|
|
1238
|
-
// Warn if headless —
|
|
1241
|
+
console.error(`[CDPClient] Stealth tab created: ${targetId} (about:blank), will navigate to ${url}`);
|
|
1242
|
+
// Warn if headless — anti-bot detection is nearly guaranteed in headless mode
|
|
1239
1243
|
{
|
|
1240
1244
|
const { headless } = (0, global_1.getGlobalConfig)();
|
|
1241
1245
|
let isHeadless = !!headless;
|
|
@@ -1249,31 +1253,23 @@ class CDPClient {
|
|
|
1249
1253
|
}
|
|
1250
1254
|
}
|
|
1251
1255
|
if (isHeadless) {
|
|
1252
|
-
console.error('[CDPClient] WARNING: Stealth mode in headless Chrome is unlikely to bypass
|
|
1256
|
+
console.error('[CDPClient] WARNING: Stealth mode in headless Chrome is unlikely to bypass anti-bot systems. Use headed Chrome (--visible) for anti-bot pages.');
|
|
1253
1257
|
}
|
|
1254
1258
|
}
|
|
1255
|
-
// Step 2:
|
|
1256
|
-
await new Promise(resolve => setTimeout(resolve, settleMs));
|
|
1257
|
-
// Step 3: Attach to the target via CDP to bring it into Puppeteer's attached set
|
|
1259
|
+
// Step 2: Attach CDP immediately (about:blank has no anti-bot to detect it)
|
|
1258
1260
|
try {
|
|
1259
1261
|
await cdp.send('Target.attachToTarget', { targetId, flatten: true });
|
|
1260
1262
|
}
|
|
1261
1263
|
catch (attachErr) {
|
|
1262
|
-
// Ghost tab cleanup: close the orphaned Chrome tab before throwing
|
|
1263
1264
|
await cdp.send('Target.closeTarget', { targetId }).catch(() => { });
|
|
1264
1265
|
await cdp.detach().catch(() => { });
|
|
1265
1266
|
throw new Error(`Stealth navigation: failed to attach to target ${targetId}: ${attachErr instanceof Error ? attachErr.message : String(attachErr)}`);
|
|
1266
1267
|
}
|
|
1267
|
-
// Step 4: Use browser.waitForTarget() to reliably find the target.
|
|
1268
|
-
// After attachToTarget, Puppeteer's internal ChromeTargetManager processes the
|
|
1269
|
-
// attachment asynchronously. browser.targets().find() may miss it due to a race
|
|
1270
|
-
// condition. waitForTarget() listens for the target event and handles timing.
|
|
1271
1268
|
let target;
|
|
1272
1269
|
try {
|
|
1273
1270
|
target = await browser.waitForTarget(t => (0, puppeteer_helpers_1.getTargetId)(t) === targetId, { timeout: 5000 });
|
|
1274
1271
|
}
|
|
1275
1272
|
catch {
|
|
1276
|
-
// Ghost tab cleanup: close the orphaned Chrome tab before throwing
|
|
1277
1273
|
await cdp.send('Target.closeTarget', { targetId }).catch(() => { });
|
|
1278
1274
|
await cdp.detach().catch(() => { });
|
|
1279
1275
|
throw new Error(`Stealth navigation: target ${targetId} not found after attach (waitForTarget timed out)`);
|
|
@@ -1286,22 +1282,51 @@ class CDPClient {
|
|
|
1286
1282
|
page = null;
|
|
1287
1283
|
}
|
|
1288
1284
|
if (!page) {
|
|
1289
|
-
// Ghost tab cleanup: close the orphaned Chrome tab before throwing
|
|
1290
1285
|
await cdp.send('Target.closeTarget', { targetId }).catch(() => { });
|
|
1291
1286
|
await cdp.detach().catch(() => { });
|
|
1292
1287
|
throw new Error(`Stealth navigation: could not get page for target ${targetId}`);
|
|
1293
1288
|
}
|
|
1294
|
-
// Clean up the helper session — the page now has its own CDP session managed by Puppeteer
|
|
1295
1289
|
await cdp.detach().catch(() => { });
|
|
1296
|
-
// Step
|
|
1290
|
+
// Step 3: Register ALL defense scripts BEFORE navigation.
|
|
1291
|
+
// evaluateOnNewDocument scripts persist on the CDP session and execute at
|
|
1292
|
+
// document_start of every new document — before any <script> tag on the page.
|
|
1297
1293
|
this.targetIdIndex.set(targetId, page);
|
|
1298
1294
|
this.configurePageDefenses(page);
|
|
1299
|
-
//
|
|
1300
|
-
|
|
1301
|
-
|
|
1295
|
+
// Stealth-only fingerprint defenses (WebGL, Canvas, Audio, hardware, screen, webdriver)
|
|
1296
|
+
const fpScript = (0, fingerprint_defense_1.getStealthFingerprintDefenseScript)();
|
|
1297
|
+
const stackScript = (0, fingerprint_defense_1.getStealthStackSanitizationScript)();
|
|
1298
|
+
await page.evaluateOnNewDocument(fpScript).catch(() => { });
|
|
1299
|
+
await page.evaluateOnNewDocument(stackScript).catch(() => { });
|
|
1300
|
+
// Step 4: Navigate to the real URL — all defenses now fire at document_start
|
|
1301
|
+
console.error(`[CDPClient] Stealth tab ${targetId}: navigating to ${url} with defenses pre-registered`);
|
|
1302
|
+
try {
|
|
1303
|
+
await page.goto(url, {
|
|
1304
|
+
waitUntil: 'domcontentloaded',
|
|
1305
|
+
timeout: Math.max(settleMs, 30000),
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
catch (navErr) {
|
|
1309
|
+
// Navigation timeout is not fatal — the page may still be usable
|
|
1310
|
+
// (e.g., Turnstile challenge pages load slowly but are interactive)
|
|
1311
|
+
console.error(`[CDPClient] Stealth navigation warning: ${navErr instanceof Error ? navErr.message : String(navErr)}`);
|
|
1312
|
+
}
|
|
1313
|
+
// Step 5: Post-navigation settle period for challenge completion (Turnstile, etc.)
|
|
1314
|
+
// The page has loaded with defenses active; this extra wait lets async challenges finish.
|
|
1315
|
+
const postNavSettleMs = Math.max(settleMs - 5000, 2000); // At least 2s, reduced from total settle
|
|
1316
|
+
await new Promise(resolve => setTimeout(resolve, postNavSettleMs));
|
|
1317
|
+
// Step 6: Defense-in-depth — apply patches directly to the current page.
|
|
1318
|
+
// evaluateOnNewDocument should have handled this at document_start, but we
|
|
1319
|
+
// re-apply as fallback in case of edge cases (e.g., SPA soft-navigation).
|
|
1320
|
+
await page.evaluate(fpScript).catch(() => { });
|
|
1321
|
+
await page.evaluate(stackScript).catch(() => { });
|
|
1302
1322
|
await page.evaluate(() => {
|
|
1303
|
-
// 1. navigator.webdriver
|
|
1304
|
-
|
|
1323
|
+
// 1. navigator.webdriver — prototype-level deletion (less detectable than defineProperty)
|
|
1324
|
+
try {
|
|
1325
|
+
delete Object.getPrototypeOf(navigator).webdriver;
|
|
1326
|
+
}
|
|
1327
|
+
catch {
|
|
1328
|
+
Object.defineProperty(navigator, 'webdriver', { get: () => undefined, configurable: true });
|
|
1329
|
+
}
|
|
1305
1330
|
// 2. chrome.runtime shape
|
|
1306
1331
|
if (window.chrome) {
|
|
1307
1332
|
const originalChrome = window.chrome;
|
|
@@ -1387,7 +1412,7 @@ class CDPClient {
|
|
|
1387
1412
|
}
|
|
1388
1413
|
}
|
|
1389
1414
|
}).catch(() => { });
|
|
1390
|
-
console.error(`[CDPClient] Stealth tab ${targetId}
|
|
1415
|
+
console.error(`[CDPClient] Stealth tab ${targetId} ready (defenses pre-injected, page loaded)`);
|
|
1391
1416
|
return { page, targetId };
|
|
1392
1417
|
}
|
|
1393
1418
|
/**
|
|
@@ -1428,14 +1453,22 @@ class CDPClient {
|
|
|
1428
1453
|
window.print = () => { console.warn('[OpenChrome] window.print() suppressed'); };
|
|
1429
1454
|
}).catch(() => { });
|
|
1430
1455
|
// Remove navigator.webdriver flag that CDP sets automatically.
|
|
1431
|
-
//
|
|
1432
|
-
//
|
|
1433
|
-
//
|
|
1456
|
+
// The --disable-blink-features=AutomationControlled launch flag prevents Blink from
|
|
1457
|
+
// setting this flag in headed mode. For headless mode (where the flag may not work),
|
|
1458
|
+
// we delete the property from the prototype rather than using Object.defineProperty
|
|
1459
|
+
// on the instance — the defineProperty approach is detectable via
|
|
1460
|
+
// Object.getOwnPropertyDescriptor(navigator, 'webdriver'). (#247, #446)
|
|
1434
1461
|
page.evaluateOnNewDocument(() => {
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1462
|
+
try {
|
|
1463
|
+
delete Object.getPrototypeOf(navigator).webdriver;
|
|
1464
|
+
}
|
|
1465
|
+
catch {
|
|
1466
|
+
// Fallback: defineProperty if prototype deletion fails
|
|
1467
|
+
Object.defineProperty(navigator, 'webdriver', {
|
|
1468
|
+
get: () => undefined,
|
|
1469
|
+
configurable: true,
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1439
1472
|
}).catch(() => { });
|
|
1440
1473
|
// Mask Chrome automation artifacts that anti-bot systems scan for.
|
|
1441
1474
|
// These cover the most common fingerprinting vectors after navigator.webdriver.
|