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.
Files changed (48) hide show
  1. package/README.md +106 -10
  2. package/dist/cdp/client.d.ts +1 -1
  3. package/dist/cdp/client.d.ts.map +1 -1
  4. package/dist/cdp/client.js +68 -35
  5. package/dist/cdp/client.js.map +1 -1
  6. package/dist/mcp-server.d.ts.map +1 -1
  7. package/dist/mcp-server.js +15 -3
  8. package/dist/mcp-server.js.map +1 -1
  9. package/dist/session-manager.d.ts +6 -0
  10. package/dist/session-manager.d.ts.map +1 -1
  11. package/dist/session-manager.js +12 -0
  12. package/dist/session-manager.js.map +1 -1
  13. package/dist/stealth/fingerprint-defense.d.ts +20 -0
  14. package/dist/stealth/fingerprint-defense.d.ts.map +1 -0
  15. package/dist/stealth/fingerprint-defense.js +261 -0
  16. package/dist/stealth/fingerprint-defense.js.map +1 -0
  17. package/dist/stealth/human-behavior.d.ts +40 -0
  18. package/dist/stealth/human-behavior.d.ts.map +1 -0
  19. package/dist/stealth/human-behavior.js +194 -0
  20. package/dist/stealth/human-behavior.js.map +1 -0
  21. package/dist/stealth/index.d.ts +9 -0
  22. package/dist/stealth/index.d.ts.map +1 -0
  23. package/dist/stealth/index.js +19 -0
  24. package/dist/stealth/index.js.map +1 -0
  25. package/dist/tools/fill-form.d.ts.map +1 -1
  26. package/dist/tools/fill-form.js +29 -6
  27. package/dist/tools/fill-form.js.map +1 -1
  28. package/dist/tools/find.d.ts.map +1 -1
  29. package/dist/tools/find.js +7 -0
  30. package/dist/tools/find.js.map +1 -1
  31. package/dist/tools/interact.d.ts.map +1 -1
  32. package/dist/tools/interact.js +15 -3
  33. package/dist/tools/interact.js.map +1 -1
  34. package/dist/tools/lightweight-scroll.d.ts.map +1 -1
  35. package/dist/tools/lightweight-scroll.js +19 -0
  36. package/dist/tools/lightweight-scroll.js.map +1 -1
  37. package/dist/tools/navigate.d.ts.map +1 -1
  38. package/dist/tools/navigate.js +6 -0
  39. package/dist/tools/navigate.js.map +1 -1
  40. package/dist/tools/read-page.js +1 -1
  41. package/dist/tools/read-page.js.map +1 -1
  42. package/dist/tools/wait-and-click.js +1 -1
  43. package/dist/tools/wait-and-click.js.map +1 -1
  44. package/dist/types/mcp.d.ts +16 -1
  45. package/dist/types/mcp.d.ts.map +1 -1
  46. package/dist/types/mcp.js +10 -0
  47. package/dist/types/mcp.js.map +1 -1
  48. 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>Smart. Fast. Parallel.</b><br>
9
- Browser automation MCP server that uses your real Chrome.
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** (17 rules) | none | none | error rewrite (5 patterns) |
36
- | **Shadow DOM** | **supported** | invisible | invisible | invisible |
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 (making bot detection irrelevant), and is the only tool with a hint engine that stops LLM agents from wandering.
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
- ## Guided, Not Guessing
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 6 layers — error recovery, composite suggestions, repetition detection, sequence detection, learned patterns, and success guidance. When it sees the same error→recovery pattern 3+ times, it promotes it to a permanent rule across sessions.
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
- ## 45 Tools
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 (45)</summary>
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 45 tools function with unauthenticated Chrome — navigation, scraping, screenshots, form filling, and parallel workflows all work in clean sessions.
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
@@ -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 Milliseconds to wait before attaching CDP (default 5000, range 1000-30000)
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<{
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/cdp/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/cdp/client.ts"],"names":[],"mappings":"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"}
@@ -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 Milliseconds to wait before attaching CDP (default 5000, range 1000-30000)
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
- // Step 1: Create target via CDP Target.createTarget.
1223
- // Puppeteer's ChromeTargetManager uses Target.setAutoAttach with { exclude: true }
1224
- // for page targets, so the new tab is added to #discoveredTargetsByTargetId but NOT
1225
- // #attachedTargetsByTargetId. This means browser.pages() will never find it.
1226
- // We use a CDP session on the browser target to issue the command directly.
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}, settling for ${settleMs}ms`);
1238
- // Warn if headless — Turnstile detection is nearly guaranteed in headless mode
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 Turnstile. Use headed Chrome (--visible) for anti-bot pages.');
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: Wait for the page to load without CDP observation (Turnstile runs here)
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 5: Index the page and configure defenses (CDP commands flow from here)
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
- // Step 6: Apply all stealth defenses immediately to the already-loaded page.
1300
- // configurePageDefenses() registers evaluateOnNewDocument scripts that only run on
1301
- // future navigations. The current page loaded without CDP, so we must patch it now.
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
- Object.defineProperty(navigator, 'webdriver', { get: () => undefined, configurable: true });
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} attached after settle period`);
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
- // Anti-automation systems (e.g., Cloudflare Turnstile) check this flag and refuse
1432
- // to function even for manual human interaction. Defense-in-depth alongside the
1433
- // --disable-blink-features=AutomationControlled launch flag. (#247)
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
- Object.defineProperty(navigator, 'webdriver', {
1436
- get: () => undefined,
1437
- configurable: true,
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.