cdpilot 0.9.0 → 0.9.2

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 CHANGED
@@ -76,7 +76,7 @@ npx cdpilot <command>
76
76
  npm i -g cdpilot
77
77
  ```
78
78
 
79
- **Requirements:** Node.js 18+ and one of: Brave Browser, Google Chrome, or Chromium.
79
+ **Requirements:** Node.js 18+, Python 3.10+, and one of: Brave Browser, Google Chrome, or Chromium. (The Python `websockets` module is auto-installed by the pre-flight wizard on first launch.)
80
80
 
81
81
  ### First-time setup
82
82
 
@@ -124,7 +124,7 @@ See [CHANGELOG.md](CHANGELOG.md) for the full list with rationale.
124
124
  ### Navigation & Content
125
125
 
126
126
  ```bash
127
- cdpilot go <url> # Navigate to URL
127
+ cdpilot go <url> # Navigate to URL (alias: open <url>)
128
128
  cdpilot content # Get page text content
129
129
  cdpilot html # Get page HTML
130
130
  cdpilot shot [file] # Take screenshot (PNG)
@@ -537,11 +537,26 @@ cdpilot profile warm # age the profile for reCAPTCHA v3 score
537
537
  which nudges reCAPTCHA v3's behavioral score upward over time. Slow by
538
538
  design — run it ahead of a session, not inline.
539
539
 
540
- Verified against public bot-detection panels:
541
- - **bot.sannysoft.com:** 24/24 PASS (WebDriver, Chrome obj, Plugins as PluginArray, WebGL, PHANTOM_*, HEADCHR_*, SELENIUM_DRIVER)
542
- - **bot.incolumitas.com** intoli: 6/6 OK — new-tests: 6/7 OK (one FAIL = pure CDP presence, cannot be JS-patched)
543
- - **nowsecure.nl** (Cloudflare full challenge): passed
544
- - **arh.antoinevastel.com/areyouheadless:** "You are not Chrome headless"
540
+ Public bot-detection panels, measured 2026-09-27, **headless** Brave (Chrome 154)
541
+ ([method and raw results](.claude/docs/stealth-panel-olcumu-2026-09-27.md)):
542
+
543
+ | `mode` | bot.sannysoft.com (31 rows) | incolumitas intoli | incolumitas fpscanner | incolumitas new-tests |
544
+ |---|---|---|---|---|
545
+ | `regular` (no patches, default) | 28 pass / 3 fail | 5/6 | 17 ok / 3 fail / 1 warn | all ok |
546
+ | `stealth` | 31 pass | 6/6 | 19 ok / 1 fail / 1 warn | all ok |
547
+ | `undetected` | 31 pass | 6/6 | 19 ok / 1 fail / 1 warn | all ok |
548
+
549
+ - The remaining fpscanner fail is `'webdriver' in navigator`, which is true in every modern
550
+ Chrome; faking it away would make cdpilot the odd one out.
551
+ - `regular`'s fails all come from the `HeadlessChrome` user agent. `stealth`/`undetected`
552
+ rewrite it for the page load that `go` starts; a later, separate command in the same page
553
+ can still read `HeadlessChrome`.
554
+ - new-tests: every key is ok; `connectionRTT` is reported as unknown in all tiers.
555
+ - nowsecure.nl and areyouheadless could not be measured (a fixed always-interactive
556
+ Turnstile test key; HTTP 502).
557
+
558
+ Earlier figures on this page (sannysoft 24/24, intoli 6/6) were measured on v0.4.x in April
559
+ 2026; they are replaced by the table above.
545
560
 
546
561
  ### Reliability
547
562
 
@@ -559,23 +574,45 @@ until cdpilot health >/dev/null; do cdpilot launch; sleep 2; done
559
574
  Surfaces today's Brave crash count from `~/Library/Logs/DiagnosticReports/`
560
575
  on macOS — spot degradation before your automation silently stalls.
561
576
 
577
+ **Auto-launch.** A page command (`go`, `content`, `click`, `shot`, …) that finds
578
+ the browser not running starts it the same way `cdpilot launch` does (same
579
+ profile, same headless/visible setting), then carries on, printing one line to
580
+ stderr: `cdpilot: browser was not running — launched it (CDPILOT_NO_AUTOLAUNCH=1 to disable)`.
581
+ Lifecycle, status and configuration commands (`launch`, `stop`, `close`,
582
+ `close-tab`, `stop-all`, `project-stop`, `session-close`, `status`, `health`,
583
+ `tabs`, `sessions`, `projects`, `headless`, `proxy`, `browser`, `extensions`,
584
+ `mcp`, `serve`, …) never launch. `CDPILOT_NO_AUTOLAUNCH=1` restores the old
585
+ "CDP connection error. Is the browser running?" error and exit code 1.
586
+
587
+ **Timeouts.** Any command takes `--timeout <seconds>`, before or after the
588
+ command name, or a default from `CDPILOT_TIMEOUT` (the flag wins; `0`
589
+ disables). It bounds the whole command's wall-clock: on expiry cdpilot prints
590
+ `cdpilot: timed out after <N>s (<command>)` to stderr, kills the child processes
591
+ it started and exits with code **124** (like GNU `timeout`). A browser that was
592
+ already up and registered is left running.
593
+
594
+ ```bash
595
+ cdpilot --timeout 10 click "#submit" # before the command...
596
+ cdpilot shot page.png --timeout 30 # ...or after it
597
+ CDPILOT_TIMEOUT=60 cdpilot run flow.cdp # default for every command (and each script line)
598
+ ```
599
+
600
+ For `mcp` and `serve` the value is not applied to the long-running server
601
+ itself; it is passed on to every tool call / request it runs.
602
+
562
603
  ### Scaling & Workstation Use
563
604
 
564
605
  ```bash
565
- CDPILOT_POOL_SIZE=4 cdpilot launch # 4 separate browser processes
566
606
  CDPILOT_OFFSCREEN=1 cdpilot launch # headed, but no window on your screen
567
607
  ```
568
608
 
569
- - **Multi-instance pool** (`CDPILOT_POOL_SIZE=N`) starts N independent browser
570
- processes and dispatches work to the least-loaded one, for `N × per-instance`
571
- parallelism. Default is `1` — single instance, no change for existing users.
572
609
  - **Off-screen mode** (`CDPILOT_OFFSCREEN=1`) keeps the browser headed (real
573
610
  rendering, no headless fingerprint) but positions the window where it can't
574
611
  steal focus — meant for automating on a workstation you're also using.
575
- - **Docker + Xvfb harness** (`cdpilot-bench/docker/`) runs headed-in-Xvfb so
576
- bench/automation runs never pop a window on the host display. CI-ready. Note
577
- that software rendering (no GPU) lowers anti-bot scores versus native — it's
578
- an isolated environment for reproducibility, not the headline configuration.
612
+ - **Multi-instance pool** (`CDPILOT_POOL_SIZE`) — planned, not shipped yet.
613
+ See [Roadmap](#coming-soon).
614
+ - **Docker + Xvfb harness** — used in a separate, private benchmark
615
+ repo (`cdpilot-bench`), not part of this package.
579
616
 
580
617
  ## Use with AI Agents
581
618
 
@@ -624,7 +661,8 @@ print(result.stdout)
624
661
  | `BROWSER_SESSION` | Auto | Session identifier |
625
662
  | `CDPILOT_MODE` | `regular` | Stealth tier override (`regular`/`stealth`/`undetected`) |
626
663
  | `CDPILOT_OFFSCREEN` | `0` | Headed but render off-screen — no window steals focus |
627
- | `CDPILOT_POOL_SIZE` | `1` | N separate browser processes, least-loaded dispatch |
664
+ | `CDPILOT_TIMEOUT` | unset | Default `--timeout` in seconds for every command (flag wins, `0` disables); expiry exits 124 |
665
+ | `CDPILOT_NO_AUTOLAUNCH` | `0` | `1` = page commands fail with the old "Is the browser running?" error instead of launching the browser |
628
666
 
629
667
  ## How It Works
630
668
 
@@ -636,7 +674,7 @@ print(result.stdout)
636
674
  │ │
637
675
  │ Zero dependencies │ Isolated profile
638
676
  │ Pure HTTP + WebSocket │ Separate from your
639
- │ ~2500 lines, single file │ personal browser
677
+ │ single file, stdlib + websockets │ personal browser
640
678
  └────────────────────────────────────────┘
641
679
  ```
642
680
 
@@ -694,8 +732,8 @@ The full adaptive layer is bench-neutral vs baseline (30/80 vs 30/80) for Stealt
694
732
 
695
733
  | Feature | cdpilot | Puppeteer | Playwright | Selenium |
696
734
  |---------|-----------|-----------|------------|----------|
697
- | Install size | **~50KB** | 400MB+ | 200MB+ | 100MB+ |
698
- | Dependencies | **0** | 50+ | 30+ | Java + drivers |
735
+ | Install size | **one Python file + a small Node launcher, no node_modules** | 400MB+ | 200MB+ | 100MB+ |
736
+ | Dependencies | **0 npm** (Python: `websockets`, auto-installed) | 50+ | 30+ | Java + drivers |
699
737
  | Setup time | **instant** | minutes | minutes | painful |
700
738
  | AI-agent ready | **yes** | manual | manual | manual |
701
739
  | Browser download | **no** | yes (Chromium) | yes (3 browsers) | no |
@@ -753,7 +791,8 @@ The only browser MCP with built-in test assertions. Here's what we've shipped an
753
791
  - [x] 70+ CLI commands (navigate, click, fill, screenshot, PDF, console, network, video understanding, friction ladder...)
754
792
  - [x] MCP server for AI agent integration (Claude Code, Cursor, etc.)
755
793
  - [x] **10 built-in test assertions** — assert, assert-url, assert-title, assert-count, assert-value, assert-attr, assert-visible/hidden, wait-for, check (batch), screenshot-diff
756
- - [x] **Accessibility tree snapshot** (`a11y-snapshot`) — structured data with @ref references, 500x fewer tokens than screenshots
794
+ - [x] **`test` runner + `trace` viewer** — `cdpilot test [--watch]` runs `*.cdpt.js` files; `cdpilot trace open <run>` opens a time-travel trace viewer for a run
795
+ - [x] **Accessibility tree snapshot** (`a11y-snapshot`) — structured text with @ref handles the agent can act on directly, no vision model needed; 1.4–42× smaller than the page's raw HTML on the four pages we measured (2026-09-27). Not always cheaper than a screenshot: link-heavy pages produce more text than a small screenshot costs
757
796
  - [x] **Token-efficient screenshots** — element-level crop (13x smaller), JPEG quality control, format selection
758
797
  - [x] **Vision fallback** (`describe`) — a11y + screenshot + text in one call
759
798
  - [x] **Annotated screenshots** — @N badge overlays on interactive elements
@@ -780,7 +819,7 @@ The only browser MCP with built-in test assertions. Here's what we've shipped an
780
819
  - [x] **Three-tier stealth mode** (`mode regular|stealth|undetected`) — single switch over fingerprint surface, with per-host adaptive tier learning
781
820
  - [x] **Press-and-hold solver** (`press-hold`) — humanized press → hold → release gesture (Gaussian 3–7s + micro-jitter) for PerimeterX/HUMAN behavioral challenges; `captcha-solve` auto-routes here
782
821
  - [x] **Image CAPTCHA + profile warming** — offline Amazon image OCR (optional `amazoncaptcha` lib) + BYOK image-to-text (capsolver/2captcha); `profile warm` ages the profile for reCAPTCHA v3 score
783
- - [x] **Multi-instance pool + off-screen mode** (`CDPILOT_POOL_SIZE`, `CDPILOT_OFFSCREEN`) — N parallel browser processes; headed rendering without stealing window focus
822
+ - [x] **Off-screen mode** (`CDPILOT_OFFSCREEN`) — headed rendering without stealing window focus
784
823
  - [x] **Data extraction** (`extract`) — structured DOM data in text, JSON, or list format
785
824
  - [x] **Page observation** (`observe`) — list all interactive elements with available actions
786
825
  - [x] **Script runner** (`run`) — execute `.cdp` script files with pass/fail reporting
@@ -788,6 +827,7 @@ The only browser MCP with built-in test assertions. Here's what we've shipped an
788
827
  ### Coming Soon
789
828
 
790
829
  - [ ] **iframe** support — interact with elements inside iframes (Shadow DOM traversal already shipped in smart commands)
830
+ - [ ] **Multi-instance pool** (`CDPILOT_POOL_SIZE`) — N independent browser processes with least-loaded dispatch
791
831
  - [ ] **Session recording & replay** — record browser sessions and replay them deterministically
792
832
  - [ ] **Stealth mode** *(Pro)* — human-like mouse/typing, anti-fingerprint, CAPTCHA solving
793
833
  - [ ] **cdpilot Cloud** — hosted browser sessions API, REST + WebSocket MCP endpoint
package/bin/cdpilot.js CHANGED
@@ -321,7 +321,14 @@ function showHelp() {
321
321
  cdpilot v${VERSION} — Zero-dependency browser automation
322
322
 
323
323
  USAGE
324
- cdpilot <command> [args]
324
+ cdpilot [--timeout <s>] <command> [args]
325
+
326
+ GLOBAL OPTIONS
327
+ --timeout <s> Abort the command after <s> seconds (exit 124). Before or
328
+ after the command; env CDPILOT_TIMEOUT sets a default
329
+ (the flag wins, 0 disables).
330
+ Page commands start the browser if it is not running
331
+ (set CDPILOT_NO_AUTOLAUNCH=1 to get an error instead).
325
332
 
326
333
  SETUP
327
334
  setup Auto-detect browser, create isolated profile
@@ -332,7 +339,7 @@ function showHelp() {
332
339
  if no user tabs remain (--force quits anyway, --keep never quits)
333
340
 
334
341
  NAVIGATION
335
- go <url> Navigate to URL
342
+ go <url> Navigate to URL (alias: open)
336
343
  content Get page text content
337
344
  html Get page HTML
338
345
  shot [file] Take screenshot
@@ -590,8 +597,23 @@ if (process.argv.includes('--internal-test-runner')) {
590
597
  return; // runAll() is async, this exits via process.exit
591
598
  }
592
599
 
600
+ // Global flags may precede the command name (`cdpilot --timeout 10 click "#x"`).
601
+ // src/cdpilot.py parses and validates them (it is also run directly, without
602
+ // this launcher); here they only have to be skipped to find the command that
603
+ // the launcher answers itself (help / --version / setup / status). args are
604
+ // passed to Python unchanged.
605
+ function commandIndex(argv) {
606
+ let i = 0;
607
+ while (i < argv.length) {
608
+ if (argv[i] === '--timeout') { i += 2; continue; }
609
+ if (argv[i].startsWith('--timeout=')) { i += 1; continue; }
610
+ break;
611
+ }
612
+ return i;
613
+ }
614
+
593
615
  const args = process.argv.slice(2);
594
- const cmd = args[0];
616
+ const cmd = args[commandIndex(args)];
595
617
 
596
618
  if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
597
619
  showHelp();
@@ -620,7 +642,7 @@ if (cmd === 'status') {
620
642
  // Delegate to Python
621
643
  const python = findPython();
622
644
  if (!python) {
623
- console.error('Error: Python 3.8+ required. Install: https://www.python.org/downloads/');
645
+ console.error('Error: Python 3.10+ required. Install: https://www.python.org/downloads/');
624
646
  process.exit(1);
625
647
  }
626
648
 
@@ -650,7 +672,14 @@ if (cmd === 'status') {
650
672
  env,
651
673
  });
652
674
 
653
- child.on('close', (code) => {
654
- process.exit(code || 0);
675
+ // A child killed by a signal reports code === null. `code || 0` turned that
676
+ // into success, so an OOM-killed or SIGKILLed run looked fine to scripts
677
+ // and agents. Use the shell convention 128 + signal number instead.
678
+ child.on('close', (code, signal) => {
679
+ if (code === null && signal) {
680
+ console.error(`cdpilot: python was terminated by ${signal}`);
681
+ process.exit(128 + (os.constants.signals[signal] || 0));
682
+ }
683
+ process.exit(code ?? 0);
655
684
  });
656
685
  }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "cdpilot",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
+ "mcpName": "io.github.mehmetnadir/cdpilot",
4
5
  "description": "Zero-dependency browser automation CLI. Raw CDP, 70+ commands, friction ladder, 3-tier stealth, CAPTCHA + press-and-hold solvers, proxy pools, MCP server. No Puppeteer/Playwright/Selenium.",
5
6
  "bin": {
6
7
  "cdpilot": "./bin/cdpilot.js",
@@ -48,15 +49,18 @@
48
49
  "shadow-dom",
49
50
  "perimeterx",
50
51
  "press-and-hold",
51
- "progressive-resilience",
52
- "multi-instance"
52
+ "progressive-resilience"
53
53
  ],
54
54
  "author": "Mehmet Nadir",
55
55
  "license": "MIT",
56
+ "homepage": "https://cdpilot.ndr.ist",
56
57
  "repository": {
57
58
  "type": "git",
58
59
  "url": "https://github.com/mehmetnadir/cdpilot"
59
60
  },
61
+ "bugs": {
62
+ "url": "https://github.com/mehmetnadir/cdpilot/issues"
63
+ },
60
64
  "engines": {
61
65
  "node": ">=18.0.0"
62
66
  },