cdpilot 0.4.4 → 0.8.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/README.md +273 -2
- package/bin/cdpilot.js +178 -0
- package/package.json +40 -9
- package/src/cdpilot.py +5702 -215
package/README.md
CHANGED
|
@@ -86,6 +86,39 @@ npx cdpilot launch # Start browser with CDP enabled
|
|
|
86
86
|
npx cdpilot status # Check connection
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
### Upgrading from 0.4.x → 0.5.0 — read this first
|
|
90
|
+
|
|
91
|
+
**One breaking change**, the rest is additive.
|
|
92
|
+
|
|
93
|
+
**Breaking — Visual feedback default flipped to OFF.** The green glow border,
|
|
94
|
+
animated fake cursor, click ripples, and keystroke display made cdpilot feel
|
|
95
|
+
like an amateur typing on every page. They are now opt-in:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
cdpilot show on # restore the old visual feedback layer
|
|
99
|
+
cdpilot show off # default since 0.5.0
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The MCP server's persistent-glow flow (`CDPILOT_MCP_SESSION=1`) is **unchanged**
|
|
103
|
+
— AI agents that rely on visible feedback during a session still see it
|
|
104
|
+
automatically. Only direct-CLI users see the difference.
|
|
105
|
+
|
|
106
|
+
**New in 0.5.0** (no migration needed):
|
|
107
|
+
|
|
108
|
+
- `cdpilot dismiss` — heuristic auto-click for "Stay signed out / No thanks"
|
|
109
|
+
buttons on LLM chat sign-up walls.
|
|
110
|
+
- `cdpilot adaptive on` — auto-escalate to stealth on CAPTCHA-protected
|
|
111
|
+
hosts, with persistent per-host memory.
|
|
112
|
+
- `cdpilot cookies save/load` — export/import cookies as JSON to replay
|
|
113
|
+
CF/DataDome clearance across runs.
|
|
114
|
+
- `cdpilot context create/list/close` + `CDPILOT_TARGET` — isolated browser
|
|
115
|
+
contexts for true parallel automation inside a single browser.
|
|
116
|
+
- `cdpilot fast` / `cdpilot show` — bundled timing + visual toggles.
|
|
117
|
+
- Pure performance: post-load sleep 1500ms → 300ms, `scrollIntoView` instant,
|
|
118
|
+
WebSocket connection pool, `/json` TTL cache.
|
|
119
|
+
|
|
120
|
+
See [CHANGELOG.md](CHANGELOG.md) for the full list with rationale.
|
|
121
|
+
|
|
89
122
|
## Commands
|
|
90
123
|
|
|
91
124
|
### Navigation & Content
|
|
@@ -119,8 +152,50 @@ cdpilot network [url] # Monitor network requests
|
|
|
119
152
|
cdpilot debug [url] # Full diagnostic (console+network+perf+shot)
|
|
120
153
|
cdpilot perf # Performance metrics
|
|
121
154
|
cdpilot eval <js> # Execute JavaScript
|
|
155
|
+
cdpilot eval-batch <json> # Run N JS expressions in 1 roundtrip (5-30x faster)
|
|
122
156
|
```
|
|
123
157
|
|
|
158
|
+
```bash
|
|
159
|
+
# Example: read 4 DOM values in a single CDP roundtrip instead of 4
|
|
160
|
+
cdpilot eval-batch '["document.title","location.href","document.links.length","document.images.length"]'
|
|
161
|
+
# → [{"ok":true,"value":"..."}, {"ok":true,"value":"..."}, ...]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Performance
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
cdpilot block # Show status
|
|
168
|
+
cdpilot block on # Enable (default preset: images+fonts+ads)
|
|
169
|
+
cdpilot block off # Disable
|
|
170
|
+
cdpilot block preset images,fonts,ads,media # Set patterns from named presets
|
|
171
|
+
cdpilot block patterns '*.png' '*.woff2' # Custom URL patterns
|
|
172
|
+
cdpilot block clear # Drop all patterns
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
> **Stealth caveat:** `block` changes the fingerprint surface — real browsers fetch
|
|
176
|
+
> images, fonts, and analytics. Cloudflare-class bot detectors notice missing
|
|
177
|
+
> requests. Keep `block` **off** for stealth/anti-bot targets; turn it **on** for
|
|
178
|
+
> known-safe internal sites where speed matters more than blending in.
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
cdpilot fast # Show status (effective auto-wait ms)
|
|
182
|
+
cdpilot fast on # Auto-wait 5s → 2s, less idle padding
|
|
183
|
+
cdpilot fast off # Back to defaults
|
|
184
|
+
CDPILOT_WAIT_MS=1000 cdpilot click # Per-command override (env wins over fast mode)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
cdpilot show # Show status (visual feedback on/off)
|
|
189
|
+
cdpilot show on # Re-enable glow border + cursor + ripples + keystrokes
|
|
190
|
+
cdpilot show off # Default since 0.4.4 — quiet, professional output
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
> **Visual feedback default changed in 0.4.4** — the old animations (green glow,
|
|
194
|
+
> moving cursor, click ripples) used to make every action look like an amateur
|
|
195
|
+
> driving the screen. They're now opt-in via `cdpilot show on`. The MCP server's
|
|
196
|
+
> persistent-glow flow (`CDPILOT_MCP_SESSION=1`) is unaffected — AI agents that
|
|
197
|
+
> rely on visible feedback during a session still see it automatically.
|
|
198
|
+
|
|
124
199
|
### Tab Management
|
|
125
200
|
|
|
126
201
|
```bash
|
|
@@ -138,10 +213,34 @@ cdpilot throttle slow3g # Simulate slow 3G
|
|
|
138
213
|
cdpilot throttle fast3g # Simulate fast 3G
|
|
139
214
|
cdpilot throttle offline # Go offline
|
|
140
215
|
cdpilot throttle off # Back to normal
|
|
141
|
-
cdpilot proxy <url> # Set proxy
|
|
216
|
+
cdpilot proxy <url> # Set proxy (legacy single-URL form)
|
|
142
217
|
cdpilot proxy off # Remove proxy
|
|
218
|
+
|
|
219
|
+
# v0.7.0: named pools (BrightData, IPRoyal, Anchor, etc.)
|
|
220
|
+
cdpilot proxy add brd "http://USER:PASS@brd.superproxy.io:22225" --geo us
|
|
221
|
+
cdpilot proxy add ipr "http://USER:PASS@geo.iproyal.com:12321" --sticky
|
|
222
|
+
cdpilot proxy use brd # Activate one pool
|
|
223
|
+
cdpilot proxy list # Show pools (credentials redacted)
|
|
224
|
+
cdpilot proxy show [<name>] # Active or named pool URL (redacted)
|
|
225
|
+
cdpilot proxy remove <name> # Drop a pool
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### TLS Fingerprint (v0.8.0)
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
cdpilot tls-check # Probe JA3/JA4/H2 via tls.peet.ws
|
|
232
|
+
cdpilot tls-check --service browserleaks # Alternate echo
|
|
233
|
+
cdpilot tls-check --json # Raw JSON
|
|
143
234
|
```
|
|
144
235
|
|
|
236
|
+
**Known limitation:** v0.8.0 ships the **probe** (`tls-check`) but no in-tree TLS fix.
|
|
237
|
+
There is no Chromium-based TLS-corrected browser that ships as a standalone binary
|
|
238
|
+
exposing `--remote-debugging-port`. Camoufox is Firefox+Juggler (no CDP);
|
|
239
|
+
Patchright / undetected-chromedriver / nodriver are Python/Playwright libraries,
|
|
240
|
+
not standalone browsers. cdpilot's CDP-only architecture is incompatible with all
|
|
241
|
+
of them without a protocol adapter. **Tracking:** v0.9 roadmap (TLS-MITM plugin
|
|
242
|
+
using curl-impersonate semantics, OR BoringSSL-patched Chromium fork).
|
|
243
|
+
|
|
145
244
|
### Request Interception
|
|
146
245
|
|
|
147
246
|
```bash
|
|
@@ -189,7 +288,22 @@ cdpilot session-close [id] # Close session
|
|
|
189
288
|
### Advanced
|
|
190
289
|
|
|
191
290
|
```bash
|
|
192
|
-
cdpilot cookies [domain]
|
|
291
|
+
cdpilot cookies [domain] # List cookies (filter by domain)
|
|
292
|
+
cdpilot cookies save <file> [<domain>] # Export cookies as JSON
|
|
293
|
+
cdpilot cookies load <file> # Import cookies (replay CF clearance)
|
|
294
|
+
cdpilot cookies save --host x.com # Save to per-host cache
|
|
295
|
+
cdpilot cookies load --host x.com # Load from per-host cache
|
|
296
|
+
cdpilot cookies list # Cached hosts + age + CF clearance flag
|
|
297
|
+
cdpilot cookies clear --host x.com # Remove one host
|
|
298
|
+
cdpilot cookies clear --all # Wipe entire cache
|
|
299
|
+
cdpilot cookies clear --older-than 7d # Remove stale entries
|
|
300
|
+
cdpilot cookies auto on # Toggle global auto-save/replay flag (v0.6.1: requires safe-list)
|
|
301
|
+
cdpilot cookies auto add <host> # Opt host into auto save/replay (v0.6.1)
|
|
302
|
+
cdpilot cookies auto remove <host> # Remove host from safe-list
|
|
303
|
+
cdpilot cookies auto list # Enable flag + current safe-list
|
|
304
|
+
cdpilot cookies cf-replay <url> # Inject cached CF clearance before nav
|
|
305
|
+
cdpilot wipe [--cookies|--storage|--tabs|--keep h1,h2]
|
|
306
|
+
# v0.6.2: per-task state hygiene (cross-task contamination)
|
|
193
307
|
cdpilot storage # localStorage contents
|
|
194
308
|
cdpilot upload <sel> <file> # Upload file to input
|
|
195
309
|
cdpilot multi-eval <js> # Execute JS in all tabs
|
|
@@ -199,6 +313,36 @@ cdpilot dialog auto-accept # Auto-accept dialogs
|
|
|
199
313
|
cdpilot permission grant geo # Grant geolocation
|
|
200
314
|
```
|
|
201
315
|
|
|
316
|
+
### Parallel Contexts
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
cdpilot context create [url] # Make fresh browser context + tab (prints JSON)
|
|
320
|
+
cdpilot context list # Tree of contexts and their tabs
|
|
321
|
+
cdpilot context close <ctx> # Destroy a context (refuses 'default')
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Address a specific context's tab in subsequent commands via the env pin:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
ID=$(cdpilot context create https://example.com | jq -r .target_id)
|
|
328
|
+
CDPILOT_TARGET=$ID cdpilot eval 'document.title'
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
> True isolation — each context has its own cookie/storage jar. Designed for
|
|
332
|
+
> running N AI chat queries in parallel without history pollution, or A/B
|
|
333
|
+
> testing logged-in vs logged-out flows without spinning up multiple browsers.
|
|
334
|
+
|
|
335
|
+
### Smart Navigation (LLM-aware)
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
cdpilot dismiss # Click best "Stay signed out / No thanks" button
|
|
339
|
+
cdpilot dismiss aggressive # Handle chained modals (cookie banner → signup)
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
> Built-in English + Turkish pattern library. Explicitly excludes destructive
|
|
343
|
+
> lookalikes (Delete account, Sign out, Subscribe) — safe to chain into a
|
|
344
|
+
> query workflow.
|
|
345
|
+
|
|
202
346
|
### Stealth & CAPTCHA
|
|
203
347
|
|
|
204
348
|
Zero-dependency anti-fingerprint layer — patches `navigator.webdriver`,
|
|
@@ -216,8 +360,55 @@ cdpilot captcha-check # JSON detection of Turnstile/hCaptcha/reCAPTCHA/
|
|
|
216
360
|
# DataDome/PerimeterX/Arkose/GeeTest. Exit 0/3
|
|
217
361
|
cdpilot captcha-wait [sec] # block until user solves (interactive)
|
|
218
362
|
# or poll with JSON stream (non-interactive)
|
|
363
|
+
|
|
364
|
+
cdpilot adaptive [on|off|status]
|
|
365
|
+
# Auto-escalate to stealth on hosts that show
|
|
366
|
+
# CAPTCHA. Persistent per-host memory.
|
|
367
|
+
cdpilot adaptive forget <host>
|
|
368
|
+
# Remove a hostname from the stealth list
|
|
369
|
+
cdpilot adaptive clear # Drop the stealth host memory entirely
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
> **Adaptive mode** is the "run fast, climb walls when seen" automation: cdpilot
|
|
373
|
+
> runs in the open lane by default, detects CAPTCHA after each navigation, and
|
|
374
|
+
> when it sees one — adds the host to a persistent list, retries once with
|
|
375
|
+
> stealth on. Never auto-demotes. Conservative by design.
|
|
376
|
+
|
|
377
|
+
### Captcha Solver Plugins (v0.6+)
|
|
378
|
+
|
|
379
|
+
Optional integration with 2captcha, anti-captcha, and capmonster. Per-solve
|
|
380
|
+
cost ~$0.001–0.003. API keys stored in `~/.cdpilot/captcha-providers.json`
|
|
381
|
+
(chmod 600) — **never committed to git**.
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
# One-time setup
|
|
385
|
+
cdpilot captcha config --provider 2captcha --api-key YOUR_KEY
|
|
386
|
+
cdpilot captcha config --provider anticaptcha --api-key YOUR_KEY # fallback
|
|
387
|
+
|
|
388
|
+
# Enable auto-solve (adaptive layer auto-solves on detect)
|
|
389
|
+
cdpilot captcha auto on
|
|
390
|
+
|
|
391
|
+
# Manual solve (debug / scripting)
|
|
392
|
+
cdpilot captcha solve --type recaptcha-v2 --site-key SK --url https://example.com
|
|
393
|
+
# returns: {"token": "03AGdBq2...", "duration_ms": 12500, "cost": 0.003, "provider": "2captcha"}
|
|
394
|
+
|
|
395
|
+
cdpilot captcha solve --type hcaptcha --site-key SK --url URL
|
|
396
|
+
cdpilot captcha solve --type turnstile --site-key SK --url URL
|
|
397
|
+
cdpilot captcha solve --type funcaptcha --site-key SK --url URL
|
|
398
|
+
|
|
399
|
+
# Status & balance
|
|
400
|
+
cdpilot captcha status # {"configured": [...], "preferred": "2captcha", "auto_enabled": true}
|
|
401
|
+
cdpilot captcha balance # {"2captcha": 1.23, "anticaptcha": 0.50}
|
|
219
402
|
```
|
|
220
403
|
|
|
404
|
+
Supported types: `recaptcha-v2`, `recaptcha-v3`, `hcaptcha`, `turnstile`, `funcaptcha`
|
|
405
|
+
|
|
406
|
+
Tokens are injected via `Runtime.evaluate` CDP — no browser-side libraries required.
|
|
407
|
+
When `captcha auto on` is set, the adaptive layer detects and solves automatically
|
|
408
|
+
after each navigation. Without auto-on, detection still works but solving is manual.
|
|
409
|
+
|
|
410
|
+
> **Expected bench improvement (v0.6):** reCaptcha 2/6 → 5+/6, hCaptcha 2/3 → 3/3
|
|
411
|
+
|
|
221
412
|
Verified against public bot-detection panels:
|
|
222
413
|
- **bot.sannysoft.com:** 24/24 PASS (WebDriver, Chrome obj, Plugins as PluginArray, WebGL, PHANTOM_*, HEADCHR_*, SELENIUM_DRIVER)
|
|
223
414
|
- **bot.incolumitas.com** intoli: 6/6 OK — new-tests: 6/7 OK (one FAIL = pure CDP presence, cannot be JS-patched)
|
|
@@ -302,6 +493,54 @@ print(result.stdout)
|
|
|
302
493
|
|
|
303
494
|
**No Puppeteer. No Playwright. No Selenium.** Just direct CDP communication.
|
|
304
495
|
|
|
496
|
+
## Stealth Bench V1
|
|
497
|
+
|
|
498
|
+
`cdpilot` is benchmarked against a suite of 80 high-friction web tasks to measure success rates against modern anti-bot systems. Gemini 2.5 Flash drives `cdpilot` as the controller; success is defined as full task completion without interception.
|
|
499
|
+
|
|
500
|
+
### v0.5.3 Results (current)
|
|
501
|
+
|
|
502
|
+
| Category | Success / Total | Rate |
|
|
503
|
+
| :--- | :--- | :--- |
|
|
504
|
+
| Custom Antibot | 5 / 5 | 100.0% |
|
|
505
|
+
| Temu Slider | 1 / 1 | 100.0% |
|
|
506
|
+
| hCaptcha | 2 / 3 | 67.0% |
|
|
507
|
+
| Cloudflare | 12 / 22 | 55.0% |
|
|
508
|
+
| DataDome | 5 / 13 | 38.0% |
|
|
509
|
+
| reCaptcha | 2 / 6 | 33.0% |
|
|
510
|
+
| Akamai | 1 / 6 | 17.0% |
|
|
511
|
+
| PerimeterX | 2 / 18 | 11.0% |
|
|
512
|
+
| GeeTest | 0 / 4 | 0.0% |
|
|
513
|
+
| Shape | 0 / 1 | 0.0% |
|
|
514
|
+
| Kasada | 0 / 1 | 0.0% |
|
|
515
|
+
| **Total** | **30 / 80** | **37.5%** |
|
|
516
|
+
|
|
517
|
+
### Version history
|
|
518
|
+
|
|
519
|
+
| Version | Mode | Total | Rate |
|
|
520
|
+
| :--- | :--- | :--- | :--- |
|
|
521
|
+
| v0.5.0 | Baseline (stealth off / adaptive off) | 30 / 80 | 37.5% |
|
|
522
|
+
| v0.5.0 | Stealth only (stealth on / adaptive off) | 32 / 80 | 40.0% |
|
|
523
|
+
| v0.5.0 | Full (stealth on / adaptive on) | 26 / 80 | 32.5% |
|
|
524
|
+
| v0.5.1 | Full — regression fix | 29 / 80 | 36.25% |
|
|
525
|
+
| v0.5.2 | Full — entropy auto-hook | 28 / 80 | 35.0% |
|
|
526
|
+
| v0.5.3 | Full — entropy scope tightened | 30 / 80 | 37.5% |
|
|
527
|
+
| v0.6.0 | + captcha solver + cookies-auto (regression) | 15 / 80 | 18.75% |
|
|
528
|
+
| **v0.8.0** | **Full — cookies safe-host scoped + per-task wipe (no proxy, no TLS fork)** | **29 / 80** | **36.25%** |
|
|
529
|
+
| v0.7.0 (slot) | + named proxy pools | _depends on user proxy_ | — |
|
|
530
|
+
| v0.8.0 (slot) | + TLS-aware launcher (camoufox/undetected-chrome) | _depends on browser choice_ | — |
|
|
531
|
+
|
|
532
|
+
**What cdpilot does not do:** `cdpilot` is an avoidance engine, not a CAPTCHA solver. We prioritize structural stealth (JS fingerprinting, behavioral entropy) to prevent challenges from appearing. When a CAPTCHA blocks progress and cannot be bypassed, the task fails — that is the honest definition of our success rate. PerimeterX (2/18), GeeTest (0/4) and Akamai (1/6) are known weaknesses; v0.7+ (residential proxy) and v0.8+ (TLS fingerprint correction via camoufox) target these directly.
|
|
533
|
+
|
|
534
|
+
## Recommended Configuration
|
|
535
|
+
|
|
536
|
+
Based on Stealth Bench V1 results:
|
|
537
|
+
|
|
538
|
+
- **Speed-first** (most use cases): `cdpilot launch` — default browser behavior, no patches
|
|
539
|
+
- **Stealth-on** (RECOMMENDED, best overall): `cdpilot launch && cdpilot stealth on`
|
|
540
|
+
- **Full adaptive** (specific captcha-heavy workflows): `cdpilot launch && cdpilot stealth on && cdpilot adaptive on`
|
|
541
|
+
|
|
542
|
+
The full adaptive layer is bench-neutral vs baseline (30/80 vs 30/80) for Stealth Bench V1's task mix. Stealth-only (32/80 = 40%) is still the best-performing single variant. For your specific workload, profile both and pick.
|
|
543
|
+
|
|
305
544
|
## Comparison
|
|
306
545
|
|
|
307
546
|
| Feature | cdpilot | Puppeteer | Playwright | Selenium |
|
|
@@ -333,6 +572,29 @@ Future paid offerings:
|
|
|
333
572
|
|
|
334
573
|
Found a vulnerability? Please email the maintainer directly instead of opening a public issue.
|
|
335
574
|
|
|
575
|
+
## Cookie Persistence (v0.6+)
|
|
576
|
+
|
|
577
|
+
Per-host cookie cache with auto-replay before navigation. Particularly useful for
|
|
578
|
+
sites with expensive challenges (Cloudflare, DataDome) — once passed, clearance
|
|
579
|
+
cookies (`cf_clearance`, `__cf_bm`) are cached and replayed on next visit.
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
# Enable auto-mode — cookies saved/replayed on every navigate
|
|
583
|
+
cdpilot cookies auto on
|
|
584
|
+
|
|
585
|
+
# Manual per-host workflow
|
|
586
|
+
cdpilot cookies save --host x.com # cache current session cookies
|
|
587
|
+
cdpilot cookies load --host x.com # inject before navigating
|
|
588
|
+
cdpilot cookies cf-replay https://x.com # explicit CF clearance injection
|
|
589
|
+
|
|
590
|
+
# Inspect and clean
|
|
591
|
+
cdpilot cookies list # all cached hosts + age + CF flag
|
|
592
|
+
cdpilot cookies clear --older-than 7d # prune stale cache
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
Storage: `~/.cdpilot/cookies/<host>/cookies.json` (chmod 600, never committed to git).
|
|
596
|
+
Expired cookies are filtered automatically on load.
|
|
597
|
+
|
|
336
598
|
## Roadmap
|
|
337
599
|
|
|
338
600
|
The only browser MCP with built-in test assertions. Here's what we've shipped and what's next:
|
|
@@ -347,6 +609,15 @@ The only browser MCP with built-in test assertions. Here's what we've shipped an
|
|
|
347
609
|
- [x] **Vision fallback** (`describe`) — a11y + screenshot + text in one call
|
|
348
610
|
- [x] **Annotated screenshots** — @N badge overlays on interactive elements
|
|
349
611
|
- [x] **Auto-wait** — MutationObserver-based, 5s automatic element waiting
|
|
612
|
+
- [x] **`wait-for-text`** — adaptive text-based waiting (subtree + characterData) for streaming AI responses, async toasts, and selector-less synchronization
|
|
613
|
+
- [x] **`eval-batch`** — run N JS expressions in 1 CDP roundtrip (5-30x speedup vs sequential `eval`)
|
|
614
|
+
- [x] **`block`** — request blocking via `Network.setBlockedURLs` with built-in presets (images/fonts/ads/media), 3-10x faster page loads on opt-in
|
|
615
|
+
- [x] **`dismiss`** — heuristic auto-click for LLM chat sign-up walls (EN+TR pattern library, destructive-action guards)
|
|
616
|
+
- [x] **`adaptive`** — auto-escalate to stealth on CAPTCHA-protected hosts, persistent per-host memory ("run fast, climb walls")
|
|
617
|
+
- [x] **`cookies save/load`** — export/import cookies as JSON (replay CF/DataDome clearance across runs)
|
|
618
|
+
- [x] **`context` pool + `CDPILOT_TARGET`** — isolated browser contexts for true parallel automation in a single browser (Playwright's parallel-tabs model)
|
|
619
|
+
- [x] **`fast` / `show`** — bundled timing + visual toggles. Default quiet/fast in 0.5.0
|
|
620
|
+
- [x] **WebSocket pool + `/json` TTL cache** — zero-regression connection reuse for MCP/batch workloads
|
|
350
621
|
- [x] **Batch commands** — pipe JSON arrays via stdin for multi-step automation
|
|
351
622
|
- [x] Visual feedback system (persistent green glow, cursor, ripples, keystroke display)
|
|
352
623
|
- [x] AI control warning toast (red warning when user interacts during automation)
|
package/bin/cdpilot.js
CHANGED
|
@@ -318,16 +318,45 @@ function showHelp() {
|
|
|
318
318
|
network [url] Monitor network requests
|
|
319
319
|
debug [url] Full diagnostic
|
|
320
320
|
eval <js> Execute JavaScript
|
|
321
|
+
eval-batch <json> Run N JS expressions in 1 roundtrip (perf)
|
|
322
|
+
|
|
323
|
+
PERFORMANCE
|
|
324
|
+
block [on|off|preset|patterns|clear]
|
|
325
|
+
Block requests via Network.setBlockedURLs (perf opt-in,
|
|
326
|
+
breaks fingerprint plausibility — not for stealth targets)
|
|
327
|
+
fast [on|off] Fast mode — auto-wait 5s→2s (env CDPILOT_WAIT_MS overrides)
|
|
328
|
+
show [on|off] Visual feedback (glow + cursor + ripples).
|
|
329
|
+
Default OFF since 0.4.4 — opt-in for "see automation" mode.
|
|
330
|
+
|
|
331
|
+
SMART NAVIGATION
|
|
332
|
+
dismiss [N|aggressive]
|
|
333
|
+
Click best "Stay signed out / No thanks / Skip" button.
|
|
334
|
+
English + Turkish patterns; never clicks destructive
|
|
335
|
+
lookalikes (Delete account, Sign out, Subscribe).
|
|
336
|
+
Pass N (1-10) or "aggressive" for chained modals.
|
|
321
337
|
|
|
322
338
|
TABS
|
|
323
339
|
tabs List open tabs
|
|
324
340
|
new-tab [url] Open new tab
|
|
325
341
|
close-tab [id] Close tab
|
|
326
342
|
|
|
343
|
+
PARALLEL CONTEXTS (isolated cookies/storage inside one browser)
|
|
344
|
+
context create [url] Make a fresh browser context + tab; prints JSON
|
|
345
|
+
context list List all browser contexts and their tabs
|
|
346
|
+
context close <ctx-id> Destroy a browser context (closes all its tabs)
|
|
347
|
+
(Address a context's tab in subsequent commands via CDPILOT_TARGET=<tgt-id>)
|
|
348
|
+
|
|
327
349
|
STEALTH & CAPTCHA
|
|
328
350
|
stealth [on|off] Toggle anti-fingerprint patches (opt-in)
|
|
329
351
|
captcha-check Detect CAPTCHA on active page (JSON output)
|
|
330
352
|
captcha-wait [s] Pause until user solves CAPTCHA (default 300s)
|
|
353
|
+
adaptive [on|off] Auto-escalate to stealth on hosts that show CAPTCHA.
|
|
354
|
+
Remembers per-host. Use 'adaptive forget <host>' to reset.
|
|
355
|
+
cookies save <file> [<dom>]
|
|
356
|
+
Export cookies (all or scoped). Replay clearance cookies
|
|
357
|
+
across cdpilot runs to skip Cloudflare walls.
|
|
358
|
+
cookies load <file>
|
|
359
|
+
Import previously-saved cookies into the current jar.
|
|
331
360
|
|
|
332
361
|
RELIABILITY
|
|
333
362
|
browser [name] Show or set preferred browser (chrome|brave|chromium|edge|vivaldi|auto)
|
|
@@ -345,8 +374,157 @@ function showHelp() {
|
|
|
345
374
|
`);
|
|
346
375
|
}
|
|
347
376
|
|
|
377
|
+
// ── Internal Test Runner ──
|
|
378
|
+
|
|
379
|
+
function runInternalTestRunner(testFile, traceDir, traceMode, grepPattern) {
|
|
380
|
+
if (traceDir) {
|
|
381
|
+
fs.mkdirSync(path.join(traceDir, 'screenshots'), { recursive: true });
|
|
382
|
+
fs.mkdirSync(path.join(traceDir, 'a11y'), { recursive: true });
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const metaPath = traceDir ? path.join(traceDir, 'meta.json') : null;
|
|
386
|
+
const stepsPath = traceDir && traceMode !== 'off' ? path.join(traceDir, 'steps.jsonl') : null;
|
|
387
|
+
|
|
388
|
+
const meta = { name: path.basename(testFile), started_at: new Date().toISOString(), status: 'running', tests: [] };
|
|
389
|
+
if (metaPath) fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
390
|
+
|
|
391
|
+
const testQueue = [];
|
|
392
|
+
global.test = (name, fn) => testQueue.push({ name, fn });
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
require(path.resolve(testFile));
|
|
396
|
+
} catch (err) {
|
|
397
|
+
const out = { passed: 0, failed: 1, skipped: 0, tests: [{ name: testFile, status: 'failed', duration_ms: 0, error: err.message }] };
|
|
398
|
+
process.stdout.write(JSON.stringify(out) + '\n');
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const results = { passed: 0, failed: 0, skipped: 0, tests: [] };
|
|
403
|
+
const cdpPort = process.env.CDP_PORT || '9222';
|
|
404
|
+
const SCRIPT = path.join(__dirname, '..', 'src', 'cdpilot.py');
|
|
405
|
+
const python = 'python3';
|
|
406
|
+
let stepIdx = 0;
|
|
407
|
+
|
|
408
|
+
const makeT = () => {
|
|
409
|
+
const runCmd = (cmd, ...cargs) => {
|
|
410
|
+
const padded = String(stepIdx).padStart(3, '0');
|
|
411
|
+
const step = { action: cmd + ' ' + cargs.join(' '), ts_ms: Date.now(), duration_ms: 0, error: null };
|
|
412
|
+
const t0 = Date.now();
|
|
413
|
+
try {
|
|
414
|
+
const quoted = cargs.map(a => JSON.stringify(String(a))).join(' ');
|
|
415
|
+
execSync(`${python} ${SCRIPT} ${cmd} ${quoted}`, {
|
|
416
|
+
stdio: 'pipe',
|
|
417
|
+
env: { ...process.env, CDP_PORT: cdpPort },
|
|
418
|
+
timeout: 30000,
|
|
419
|
+
});
|
|
420
|
+
step.duration_ms = Date.now() - t0;
|
|
421
|
+
if (stepsPath) fs.appendFileSync(stepsPath, JSON.stringify(step) + '\n');
|
|
422
|
+
// Screenshot after each step (best-effort — no browser = skipped)
|
|
423
|
+
if (traceDir && traceMode !== 'off') {
|
|
424
|
+
try {
|
|
425
|
+
const shotPath = path.join(traceDir, 'screenshots', `step-${padded}.png`);
|
|
426
|
+
execSync(`${python} ${SCRIPT} shot ${shotPath}`, { stdio: 'pipe', env: { ...process.env, CDP_PORT: cdpPort }, timeout: 10000 });
|
|
427
|
+
} catch (_) { /* no browser is OK in unit-style tests */ }
|
|
428
|
+
}
|
|
429
|
+
stepIdx++;
|
|
430
|
+
} catch (err) {
|
|
431
|
+
step.duration_ms = Date.now() - t0;
|
|
432
|
+
step.error = err.stderr ? err.stderr.toString().trim() : err.message;
|
|
433
|
+
if (stepsPath) fs.appendFileSync(stepsPath, JSON.stringify(step) + '\n');
|
|
434
|
+
stepIdx++;
|
|
435
|
+
throw new Error(`${cmd} failed: ${step.error}`);
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const t = {
|
|
440
|
+
goto: (url) => runCmd('go', url),
|
|
441
|
+
click: (sel) => runCmd('click', sel),
|
|
442
|
+
fill: (sel, val) => runCmd('fill', sel, val),
|
|
443
|
+
type: (sel, val) => runCmd('type', sel, val),
|
|
444
|
+
hover: (sel) => runCmd('hover', sel),
|
|
445
|
+
screenshot: (p) => runCmd('shot', p),
|
|
446
|
+
eval: (js) => {
|
|
447
|
+
try {
|
|
448
|
+
const out = execSync(`${python} ${SCRIPT} eval ${JSON.stringify(js)}`, { stdio: 'pipe', env: { ...process.env, CDP_PORT: cdpPort }, timeout: 10000 });
|
|
449
|
+
return out.toString().trim();
|
|
450
|
+
} catch (e) { throw new Error('eval failed: ' + e.message); }
|
|
451
|
+
},
|
|
452
|
+
a11y: () => runCmd('a11y-snapshot'),
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
t.expect = (textOrSel) => runCmd('assert', textOrSel);
|
|
456
|
+
t.expect.url = (expected) => runCmd('assert-url', expected);
|
|
457
|
+
t.expect.visible = (sel) => runCmd('assert-visible', sel);
|
|
458
|
+
t.expect.hidden = (sel) => runCmd('assert-hidden', sel);
|
|
459
|
+
|
|
460
|
+
return t;
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
// Run tests sequentially (parallel is managed at the Python level across files)
|
|
464
|
+
const runAll = async () => {
|
|
465
|
+
for (const tst of testQueue) {
|
|
466
|
+
if (grepPattern && !tst.name.match(new RegExp(grepPattern, 'i'))) {
|
|
467
|
+
results.skipped++;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
const t0 = Date.now();
|
|
471
|
+
const rec = { name: tst.name, status: 'passed', duration_ms: 0, error: null };
|
|
472
|
+
try {
|
|
473
|
+
await tst.fn(makeT());
|
|
474
|
+
rec.status = 'passed';
|
|
475
|
+
results.passed++;
|
|
476
|
+
} catch (err) {
|
|
477
|
+
rec.status = 'failed';
|
|
478
|
+
rec.error = err.message;
|
|
479
|
+
results.failed++;
|
|
480
|
+
}
|
|
481
|
+
rec.duration_ms = Date.now() - t0;
|
|
482
|
+
results.tests.push(rec);
|
|
483
|
+
if (metaPath) {
|
|
484
|
+
meta.tests.push(rec);
|
|
485
|
+
meta.status = 'running';
|
|
486
|
+
fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Finalize meta
|
|
491
|
+
if (metaPath) {
|
|
492
|
+
meta.status = results.failed > 0 ? 'failed' : 'passed';
|
|
493
|
+
meta.passed = results.passed;
|
|
494
|
+
meta.failed = results.failed;
|
|
495
|
+
meta.skipped = results.skipped;
|
|
496
|
+
meta.ended_at = new Date().toISOString();
|
|
497
|
+
fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
process.stdout.write(JSON.stringify(results) + '\n');
|
|
501
|
+
process.exit(results.failed > 0 ? 1 : 0);
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
runAll().catch(err => {
|
|
505
|
+
process.stderr.write('Test runner error: ' + err.message + '\n');
|
|
506
|
+
process.exit(1);
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
348
510
|
// ── Main ──
|
|
349
511
|
|
|
512
|
+
// Internal test runner mode — intercept before normal CLI dispatch
|
|
513
|
+
if (process.argv.includes('--internal-test-runner')) {
|
|
514
|
+
const idx = process.argv.indexOf('--internal-test-runner');
|
|
515
|
+
const testFile = process.argv[idx + 1];
|
|
516
|
+
const traceDirArg = process.argv.find(a => a.startsWith('--trace-dir='));
|
|
517
|
+
const traceArg = process.argv.find(a => a.startsWith('--trace='));
|
|
518
|
+
const grepArg = process.argv.find(a => a.startsWith('--grep='));
|
|
519
|
+
runInternalTestRunner(
|
|
520
|
+
testFile,
|
|
521
|
+
traceDirArg ? traceDirArg.split('=').slice(1).join('=') : null,
|
|
522
|
+
traceArg ? traceArg.split('=')[1] : 'default',
|
|
523
|
+
grepArg ? grepArg.split('=').slice(1).join('=') : null,
|
|
524
|
+
);
|
|
525
|
+
return; // runAll() is async, this exits via process.exit
|
|
526
|
+
}
|
|
527
|
+
|
|
350
528
|
const args = process.argv.slice(2);
|
|
351
529
|
const cmd = args[0];
|
|
352
530
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdpilot",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Zero-dependency browser automation
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Zero-dependency browser automation CLI. Raw CDP, 60+ commands, stealth + adaptive escalation, CAPTCHA solvers, proxy pools, TLS probe, MCP server. No Puppeteer/Playwright/Selenium.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cdpilot": "./bin/cdpilot.js",
|
|
7
7
|
"bctl": "./bin/cdpilot.js"
|
|
@@ -11,12 +11,38 @@
|
|
|
11
11
|
"start": "node bin/cdpilot.js"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
14
|
-
"browser-automation",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
14
|
+
"browser-automation",
|
|
15
|
+
"cdp",
|
|
16
|
+
"chrome-devtools-protocol",
|
|
17
|
+
"cli",
|
|
18
|
+
"mcp",
|
|
19
|
+
"ai-agent",
|
|
20
|
+
"web-scraping",
|
|
21
|
+
"zero-dependency",
|
|
22
|
+
"headless-browser",
|
|
23
|
+
"puppeteer-alternative",
|
|
24
|
+
"playwright-alternative",
|
|
25
|
+
"screenshot",
|
|
26
|
+
"pdf",
|
|
27
|
+
"automation",
|
|
28
|
+
"devtools",
|
|
29
|
+
"browser",
|
|
30
|
+
"model-context-protocol",
|
|
31
|
+
"claude",
|
|
32
|
+
"brave",
|
|
33
|
+
"testing",
|
|
34
|
+
"web-testing",
|
|
35
|
+
"accessibility",
|
|
36
|
+
"a11y",
|
|
37
|
+
"stealth",
|
|
38
|
+
"captcha-solver",
|
|
39
|
+
"anti-bot",
|
|
40
|
+
"cloudflare-bypass",
|
|
41
|
+
"datadome",
|
|
42
|
+
"adaptive-escalation",
|
|
43
|
+
"proxy-rotation",
|
|
44
|
+
"tls-fingerprint",
|
|
45
|
+
"cookie-management"
|
|
20
46
|
],
|
|
21
47
|
"author": "Mehmet Nadir",
|
|
22
48
|
"license": "MIT",
|
|
@@ -32,5 +58,10 @@
|
|
|
32
58
|
"src/cdpilot.py",
|
|
33
59
|
"README.md",
|
|
34
60
|
"LICENSE"
|
|
35
|
-
]
|
|
61
|
+
],
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"gray-matter": "^4.0.3",
|
|
64
|
+
"remark": "^15.0.1",
|
|
65
|
+
"remark-html": "^16.0.1"
|
|
66
|
+
}
|
|
36
67
|
}
|