playwright-cdp-probe 0.2.0__tar.gz
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.
- playwright_cdp_probe-0.2.0/.coverage +0 -0
- playwright_cdp_probe-0.2.0/.coverage 2 +0 -0
- playwright_cdp_probe-0.2.0/.github/workflows/ci.yml +31 -0
- playwright_cdp_probe-0.2.0/.github/workflows/release.yml +54 -0
- playwright_cdp_probe-0.2.0/.gitignore +12 -0
- playwright_cdp_probe-0.2.0/CHANGELOG.md +35 -0
- playwright_cdp_probe-0.2.0/LICENSE +21 -0
- playwright_cdp_probe-0.2.0/PKG-INFO +237 -0
- playwright_cdp_probe-0.2.0/README.md +201 -0
- playwright_cdp_probe-0.2.0/docs/AFFILIATE.md +52 -0
- playwright_cdp_probe-0.2.0/docs/FAQ.md +21 -0
- playwright_cdp_probe-0.2.0/docs/MLX_INTEGRATION.md +80 -0
- playwright_cdp_probe-0.2.0/docs/SCORING.md +59 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/__init__.py +7 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/cli.py +247 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/deal.py +15 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/exit_codes.py +34 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/integrations/__init__.py +1 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/integrations/mlx.py +164 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/mlx.py +23 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/presets.py +71 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/probe.py +92 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/report.py +157 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/scoring.py +148 -0
- playwright_cdp_probe-0.2.0/playwright_cdp_probe/signals.py +160 -0
- playwright_cdp_probe-0.2.0/presets/cloudflare.json +16 -0
- playwright_cdp_probe-0.2.0/presets/social-login.json +16 -0
- playwright_cdp_probe-0.2.0/presets/stripe-dashboard.json +16 -0
- playwright_cdp_probe-0.2.0/pyproject.toml +94 -0
- playwright_cdp_probe-0.2.0/tests/conftest.py +11 -0
- playwright_cdp_probe-0.2.0/tests/test_cli.py +155 -0
- playwright_cdp_probe-0.2.0/tests/test_exit_codes.py +27 -0
- playwright_cdp_probe-0.2.0/tests/test_integrations_mlx.py +100 -0
- playwright_cdp_probe-0.2.0/tests/test_mlx.py +61 -0
- playwright_cdp_probe-0.2.0/tests/test_mlx_cli.py +66 -0
- playwright_cdp_probe-0.2.0/tests/test_presets.py +100 -0
- playwright_cdp_probe-0.2.0/tests/test_probe.py +45 -0
- playwright_cdp_probe-0.2.0/tests/test_probe_extended.py +140 -0
- playwright_cdp_probe-0.2.0/tests/test_report_formats.py +54 -0
- playwright_cdp_probe-0.2.0/tests/test_report_io.py +33 -0
- playwright_cdp_probe-0.2.0/tests/test_scoring.py +64 -0
- playwright_cdp_probe-0.2.0/tests/test_signals.py +65 -0
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
strategy:
|
|
10
|
+
fail-fast: false
|
|
11
|
+
matrix:
|
|
12
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
|
|
21
|
+
- name: Install package and dev deps
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
pip install -e ".[dev]"
|
|
25
|
+
playwright install chromium
|
|
26
|
+
|
|
27
|
+
- name: Ruff
|
|
28
|
+
run: ruff check .
|
|
29
|
+
|
|
30
|
+
- name: Pytest
|
|
31
|
+
run: pytest
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Set up Python 3.12
|
|
15
|
+
uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
|
|
19
|
+
- name: Install package and dev deps
|
|
20
|
+
run: |
|
|
21
|
+
python -m pip install --upgrade pip
|
|
22
|
+
pip install -e ".[dev]"
|
|
23
|
+
playwright install chromium
|
|
24
|
+
|
|
25
|
+
- name: Ruff
|
|
26
|
+
run: ruff check .
|
|
27
|
+
|
|
28
|
+
- name: Pytest
|
|
29
|
+
run: pytest
|
|
30
|
+
|
|
31
|
+
publish:
|
|
32
|
+
needs: test
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
|
|
37
|
+
- name: Set up Python 3.12
|
|
38
|
+
uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: "3.12"
|
|
41
|
+
|
|
42
|
+
- name: Install build tools
|
|
43
|
+
run: python -m pip install --upgrade pip build twine
|
|
44
|
+
|
|
45
|
+
- name: Build
|
|
46
|
+
run: python -m build
|
|
47
|
+
|
|
48
|
+
- name: Twine check
|
|
49
|
+
run: twine check dist/*
|
|
50
|
+
|
|
51
|
+
- name: Publish to PyPI
|
|
52
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
53
|
+
with:
|
|
54
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v0.2.0 — 2026-06-11
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `playwright_cdp_probe.integrations.mlx` — `mlx_launch_and_probe(profile_id)` (Launcher start → CDP probe → stop)
|
|
8
|
+
- `cdp-probe mlx --profile-id UUID` (`[mlx]` extra); `mlx-launch` kept as hidden alias
|
|
9
|
+
- `docs/MLX_INTEGRATION.md` — Launcher flow, CLI, and Python API
|
|
10
|
+
- MLX integration tests with `httpx.MockTransport` (no real MLX account)
|
|
11
|
+
|
|
12
|
+
- `--format json|table|github-actions` on `run`, `check-local`, `mlx`, and `report`
|
|
13
|
+
- GitHub Actions workflow annotations (`::error`, `::warning`, `::notice`) for CI
|
|
14
|
+
- Score-band exit codes: `0` pass (≤30), `1` warn (31–60), `2` fail (>60), `3` runtime error
|
|
15
|
+
- `cdp-probe --version` via Click (package metadata)
|
|
16
|
+
- `docs/SCORING.md` — transparent signal weight rubric
|
|
17
|
+
- URL validation for `run` / `mlx-launch` navigation targets
|
|
18
|
+
- `headless_launch` recorded on collected signals
|
|
19
|
+
- GitHub Actions CI matrix (Python 3.10 / 3.11 / 3.12)
|
|
20
|
+
- Expanded test suite (timeout, invalid URL, headed vs headless, CDP path, formats)
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Removed `--strict` / `--threshold`; exit code now follows fixed score bands
|
|
25
|
+
- Report JSON includes `exit_code` field
|
|
26
|
+
- Table output shows exit band legend and `headless_launch`
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- MLX launcher tests tolerate query-string URL matching
|
|
31
|
+
|
|
32
|
+
## v0.1.0 — 2026-06-01
|
|
33
|
+
|
|
34
|
+
- Initial release: `run`, `check-local`, `report`, optional `mlx-launch`
|
|
35
|
+
- Weighted exposure scoring (0–100) with JSON report cache
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 playwright-cdp-probe contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: playwright-cdp-probe
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Catch automation leaks before production — score CDP sessions for webdriver and headless fingerprints.
|
|
5
|
+
Project-URL: Homepage, https://github.com/playwright-cdp-probe/playwright-cdp-probe
|
|
6
|
+
Project-URL: Documentation, https://github.com/playwright-cdp-probe/playwright-cdp-probe#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/playwright-cdp-probe/playwright-cdp-probe
|
|
8
|
+
Project-URL: Issues, https://github.com/playwright-cdp-probe/playwright-cdp-probe/issues
|
|
9
|
+
Author: playwright-cdp-probe contributors
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: anti-bot-testing,automation-exposure,bot-detection,bot-score,cdp-probe,exposure-score,headless-fingerprint,navigator-webdriver,puppeteer-audit,selenium-audit,stealth-audit,webdriver-leak
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: click>=8.1
|
|
25
|
+
Requires-Dist: playwright>=1.40
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-httpx>=0.34; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
33
|
+
Provides-Extra: mlx
|
|
34
|
+
Requires-Dist: httpx>=0.27; extra == 'mlx'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# playwright-cdp-probe
|
|
38
|
+
|
|
39
|
+
Python 3.10+ | MLX optional · [Compatibility](../packages/COMPATIBILITY.md)
|
|
40
|
+
|
|
41
|
+
Audit Playwright and CDP browser sessions for **automation exposure signals** — `navigator.webdriver`, `chrome.runtime`, plugin counts, permissions API, headless UA hints, and window/WebGL fingerprints.
|
|
42
|
+
|
|
43
|
+
Returns a **0–100 exposure score** (higher = more detectable automation artifacts).
|
|
44
|
+
|
|
45
|
+
## Problem
|
|
46
|
+
|
|
47
|
+
Bot-detection stacks probe dozens of browser signals beyond IP and cookies. Before shipping automation to production, teams need a quick way to answer:
|
|
48
|
+
|
|
49
|
+
- Does this browser context leak `navigator.webdriver`?
|
|
50
|
+
- Are headless or SwiftShader fingerprints visible?
|
|
51
|
+
- How does a CDP-attached session compare to a local Playwright launch?
|
|
52
|
+
|
|
53
|
+
`cdp-probe` launches Playwright (or connects over CDP), collects signals in-page, and scores automation exposure with a reproducible report.
|
|
54
|
+
|
|
55
|
+
## pip install
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install playwright-cdp-probe
|
|
59
|
+
playwright install chromium # first-time browser binaries
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Optional Multilogin X launcher integration:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install playwright-cdp-probe[mlx]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Development:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install playwright-cdp-probe[dev]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Quick start
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Probe a local headless Chromium session
|
|
78
|
+
cdp-probe check-local --browser chromium
|
|
79
|
+
|
|
80
|
+
# Navigate and probe a live URL
|
|
81
|
+
cdp-probe run --url https://example.com
|
|
82
|
+
|
|
83
|
+
# Site-class presets (different signal weights — see presets/)
|
|
84
|
+
cdp-probe run --preset cloudflare --url https://example.com
|
|
85
|
+
cdp-probe run --preset stripe-dashboard --url https://dashboard.stripe.com
|
|
86
|
+
cdp-probe run --preset social-login --url https://example.com/login
|
|
87
|
+
|
|
88
|
+
# Re-print the last report
|
|
89
|
+
cdp-probe report --format table
|
|
90
|
+
cdp-probe report --format json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from playwright_cdp_probe import ProbeRunner
|
|
95
|
+
from playwright_cdp_probe.probe import ProbeOptions
|
|
96
|
+
|
|
97
|
+
report = ProbeRunner(ProbeOptions(url="https://example.com")).run()
|
|
98
|
+
print(report.exposure.score, report.exposure.grade)
|
|
99
|
+
for finding in report.exposure.findings:
|
|
100
|
+
print(finding.signal, finding.detail)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## CLI
|
|
104
|
+
|
|
105
|
+
| Command | Description |
|
|
106
|
+
|---------|-------------|
|
|
107
|
+
| `cdp-probe run --url URL` | Launch browser, navigate, score exposure |
|
|
108
|
+
| `cdp-probe run --preset NAME --url URL` | Score with site-class weights (`presets/`) |
|
|
109
|
+
| `cdp-probe check-local --browser chromium\|firefox` | Probe `about:blank` locally |
|
|
110
|
+
| `cdp-probe report --format json\|table\|github-actions` | Format last saved report |
|
|
111
|
+
| `cdp-probe mlx --profile-id UUID` | MLX Launcher → CDP probe → stop (`[mlx]` extra) |
|
|
112
|
+
|
|
113
|
+
### Exit codes
|
|
114
|
+
|
|
115
|
+
| Code | Meaning |
|
|
116
|
+
|------|---------|
|
|
117
|
+
| `0` | **pass** — exposure score ≤ 30 |
|
|
118
|
+
| `1` | **warn** — exposure score 31–60 |
|
|
119
|
+
| `2` | **fail** — exposure score > 60 |
|
|
120
|
+
| `3` | Runtime / validation error |
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
cdp-probe run --url https://example.com --format github-actions
|
|
124
|
+
cdp-probe --version
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Signal weights and bands are documented in [docs/SCORING.md](docs/SCORING.md).
|
|
128
|
+
|
|
129
|
+
### Presets
|
|
130
|
+
|
|
131
|
+
`presets/*.json` define alternate weight maps for common gate pages. Default rubric in [docs/SCORING.md](docs/SCORING.md) is generic; presets emphasize signals each surface tends to punish.
|
|
132
|
+
|
|
133
|
+
| Preset | CLI | What vanilla Chrome / Playwright usually fails |
|
|
134
|
+
|--------|-----|-----------------------------------------------|
|
|
135
|
+
| `cloudflare` | `cdp-probe run --preset cloudflare --url URL` | Turnstile / JS challenge: `HeadlessChrome` UA, `navigator.webdriver`, SwiftShader WebGL, empty `navigator.plugins` |
|
|
136
|
+
| `stripe-dashboard` | `cdp-probe run --preset stripe-dashboard --url URL` | Radar-style fingerprint drift: thin plugin lists, missing `chrome.runtime`, empty `navigator.languages` |
|
|
137
|
+
| `social-login` | `cdp-probe run --preset social-login --url URL` | OAuth popups: `webdriver`, zero outer window size in headless, inner/outer dimension mismatch |
|
|
138
|
+
|
|
139
|
+
Reports are saved to `~/.cache/cdp-probe/last-report.json` (override with `--output`). Preset name is recorded in report `mode` (e.g. `run:cloudflare`).
|
|
140
|
+
|
|
141
|
+
## API
|
|
142
|
+
|
|
143
|
+
| Symbol | Description |
|
|
144
|
+
|--------|-------------|
|
|
145
|
+
| `ProbeRunner` | Launch Playwright or `connect_over_cdp`, collect signals |
|
|
146
|
+
| `ProbeOptions` | `url`, `browser`, `headless`, `cdp_endpoint` |
|
|
147
|
+
| `compute_exposure_score(signals)` | Score 0–100 with weighted findings |
|
|
148
|
+
| `ProbeReport` | Signals + exposure + timestamp |
|
|
149
|
+
| `format_report(report, "json"\|"table")` | CLI-compatible formatting |
|
|
150
|
+
|
|
151
|
+
### Signals collected
|
|
152
|
+
|
|
153
|
+
- `navigator.webdriver`
|
|
154
|
+
- `window.chrome.runtime` (Chromium)
|
|
155
|
+
- `navigator.plugins` / `mimeTypes` counts
|
|
156
|
+
- `navigator.permissions` API presence
|
|
157
|
+
- `Notification.permission`
|
|
158
|
+
- HeadlessChrome user-agent hint
|
|
159
|
+
- Window outer/inner dimension mismatch
|
|
160
|
+
- WebGL vendor/renderer (SwiftShader detection)
|
|
161
|
+
|
|
162
|
+
## Limitations
|
|
163
|
+
|
|
164
|
+
- **Not a bypass tool** — this package measures exposure; it does not patch, spoof, or hide fingerprints.
|
|
165
|
+
- **Vanilla Chrome/Playwright leaks at scale** — default Playwright Chromium launches routinely expose `webdriver`, thin plugin lists, and headless WebGL renderers. A low score locally is unusual; a high score is expected.
|
|
166
|
+
- **Page-level only** — TLS/JA3, IP reputation, behavioral biometrics, and server-side ML models are out of scope.
|
|
167
|
+
- **Single-page snapshot** — results reflect one navigation moment, not long-session drift.
|
|
168
|
+
- **Firefox signal set differs** — `chrome.runtime` checks apply to Chromium only.
|
|
169
|
+
- **MLX requires desktop agent** — `mlx` needs Multilogin running locally; this is not a cloud-only probe. See [docs/MLX_INTEGRATION.md](docs/MLX_INTEGRATION.md).
|
|
170
|
+
|
|
171
|
+
## Production
|
|
172
|
+
|
|
173
|
+
Partner offers, eligibility, and disclosure: [docs/AFFILIATE.md](docs/AFFILIATE.md).
|
|
174
|
+
|
|
175
|
+
For production workflows that need **isolated antidetect profiles**, connect Playwright over a Launcher API CDP endpoint instead of launching vanilla Chromium:
|
|
176
|
+
|
|
177
|
+
1. Start an antidetect profile via the MLX Launcher API (`automation_type=playwright`).
|
|
178
|
+
2. Read the returned local port (`http://127.0.0.1:{port}`).
|
|
179
|
+
3. `connect_over_cdp` and re-run the probe to compare exposure.
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
export MLX_TOKEN="your-bearer-token"
|
|
183
|
+
export MLX_FOLDER_ID="your-folder-uuid"
|
|
184
|
+
cdp-probe mlx --profile-id PROFILE_UUID --url https://example.com
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Or programmatically:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from playwright_cdp_probe.integrations.mlx import mlx_launch_and_probe
|
|
191
|
+
|
|
192
|
+
report = mlx_launch_and_probe(
|
|
193
|
+
"profile-id",
|
|
194
|
+
folder_id="folder-id",
|
|
195
|
+
token="...",
|
|
196
|
+
url="https://example.com",
|
|
197
|
+
)
|
|
198
|
+
print(report.exposure.score)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Compare `check-local` (vanilla Playwright) vs `mlx` (isolated profile) scores to quantify exposure reduction — not elimination.
|
|
202
|
+
|
|
203
|
+
## Guides
|
|
204
|
+
|
|
205
|
+
Monorepo playbooks (copy-paste commands, sample output, diagrams):
|
|
206
|
+
|
|
207
|
+
| Guide | Flow |
|
|
208
|
+
|-------|------|
|
|
209
|
+
| [Detection fail → MLX farm](../packages/docs/workflows/WORKFLOW_DETECTED.md) | `cdp-probe` → `cdp-connect` → `farm-runner mlx-pool` |
|
|
210
|
+
| [Competitor migration](../packages/docs/workflows/WORKFLOW_MIGRATION.md) | `antidetect-import` → `profile-factory mlx-create` |
|
|
211
|
+
| [Proxy lane → profile pool](../packages/docs/workflows/WORKFLOW_FARM.md) | `proxy-lane` → `profile-factory` → `farm-runner mlx-pool` |
|
|
212
|
+
|
|
213
|
+
**FAQ:** [docs/FAQ.md](docs/FAQ.md) — Playwright detection, CDP exposure, headless Chrome.
|
|
214
|
+
|
|
215
|
+
## Related tools
|
|
216
|
+
|
|
217
|
+
| Tool | Use with |
|
|
218
|
+
|------|----------|
|
|
219
|
+
| [playwright-cdp-probe](../playwright-cdp-probe/) — Score CDP/WebDriver exposure and fingerprint leaks | → [cdp-connect-kit](../cdp-connect-kit/) when probe fails |
|
|
220
|
+
| [cookie-jar-bridge](../cookie-jar-bridge/) — Convert, validate, and merge cookies across formats | → [session-bundle-kit](../session-bundle-kit/) for full session |
|
|
221
|
+
| [proxy-lane-checker](../proxy-lane-checker/) — Batch-check proxies for connectivity, geo, and DNSBL | → [profile-yaml-factory](../profile-yaml-factory/) |
|
|
222
|
+
| [fingerprint-coherence](../fingerprint-coherence/) — Lint fingerprints for cross-signal contradictions | → [profile-yaml-factory](../profile-yaml-factory/) before create |
|
|
223
|
+
| [human-input-kit](../human-input-kit/) — Human-like mouse, scroll, and warmup replay | → [automation-farm-runner](../automation-farm-runner/) |
|
|
224
|
+
| [cdp-connect-kit](../cdp-connect-kit/) — Playwright CDP connect; MLX start/stop helpers | → all packages needing CDP |
|
|
225
|
+
| [session-bundle-kit](../session-bundle-kit/) — Export/import cookies and storage as portable bundles | → [cookie-jar-bridge](../cookie-jar-bridge/) |
|
|
226
|
+
| [profile-yaml-factory](../profile-yaml-factory/) — YAML blueprints → MLX `profile/create` payloads | → [antidetect-importer](../antidetect-importer/) after migration |
|
|
227
|
+
| [antidetect-importer](../antidetect-importer/) — Parse competitor exports and push to MLX | → [profile-yaml-factory](../profile-yaml-factory/) |
|
|
228
|
+
| [automation-farm-runner](../automation-farm-runner/) — YAML job queue and MLX profile-pool runner | → [cdp-connect-kit](../cdp-connect-kit/) `[mlx]` |
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
**Production antidetect:** [Multilogin X](https://multilogin.com/?ref=SAAS50) · Code `SAAS50` (-50% browser) · `MIN50` (-50% cloud phone)
|
|
237
|
+
More scripts: [@Multilogin_Scripts_Bot](https://t.me/Multilogin_Scripts_Bot) · [Coupons](https://anti-detect.github.io/)
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# playwright-cdp-probe
|
|
2
|
+
|
|
3
|
+
Python 3.10+ | MLX optional · [Compatibility](../packages/COMPATIBILITY.md)
|
|
4
|
+
|
|
5
|
+
Audit Playwright and CDP browser sessions for **automation exposure signals** — `navigator.webdriver`, `chrome.runtime`, plugin counts, permissions API, headless UA hints, and window/WebGL fingerprints.
|
|
6
|
+
|
|
7
|
+
Returns a **0–100 exposure score** (higher = more detectable automation artifacts).
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Bot-detection stacks probe dozens of browser signals beyond IP and cookies. Before shipping automation to production, teams need a quick way to answer:
|
|
12
|
+
|
|
13
|
+
- Does this browser context leak `navigator.webdriver`?
|
|
14
|
+
- Are headless or SwiftShader fingerprints visible?
|
|
15
|
+
- How does a CDP-attached session compare to a local Playwright launch?
|
|
16
|
+
|
|
17
|
+
`cdp-probe` launches Playwright (or connects over CDP), collects signals in-page, and scores automation exposure with a reproducible report.
|
|
18
|
+
|
|
19
|
+
## pip install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install playwright-cdp-probe
|
|
23
|
+
playwright install chromium # first-time browser binaries
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Optional Multilogin X launcher integration:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install playwright-cdp-probe[mlx]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Development:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install playwright-cdp-probe[dev]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Probe a local headless Chromium session
|
|
42
|
+
cdp-probe check-local --browser chromium
|
|
43
|
+
|
|
44
|
+
# Navigate and probe a live URL
|
|
45
|
+
cdp-probe run --url https://example.com
|
|
46
|
+
|
|
47
|
+
# Site-class presets (different signal weights — see presets/)
|
|
48
|
+
cdp-probe run --preset cloudflare --url https://example.com
|
|
49
|
+
cdp-probe run --preset stripe-dashboard --url https://dashboard.stripe.com
|
|
50
|
+
cdp-probe run --preset social-login --url https://example.com/login
|
|
51
|
+
|
|
52
|
+
# Re-print the last report
|
|
53
|
+
cdp-probe report --format table
|
|
54
|
+
cdp-probe report --format json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from playwright_cdp_probe import ProbeRunner
|
|
59
|
+
from playwright_cdp_probe.probe import ProbeOptions
|
|
60
|
+
|
|
61
|
+
report = ProbeRunner(ProbeOptions(url="https://example.com")).run()
|
|
62
|
+
print(report.exposure.score, report.exposure.grade)
|
|
63
|
+
for finding in report.exposure.findings:
|
|
64
|
+
print(finding.signal, finding.detail)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## CLI
|
|
68
|
+
|
|
69
|
+
| Command | Description |
|
|
70
|
+
|---------|-------------|
|
|
71
|
+
| `cdp-probe run --url URL` | Launch browser, navigate, score exposure |
|
|
72
|
+
| `cdp-probe run --preset NAME --url URL` | Score with site-class weights (`presets/`) |
|
|
73
|
+
| `cdp-probe check-local --browser chromium\|firefox` | Probe `about:blank` locally |
|
|
74
|
+
| `cdp-probe report --format json\|table\|github-actions` | Format last saved report |
|
|
75
|
+
| `cdp-probe mlx --profile-id UUID` | MLX Launcher → CDP probe → stop (`[mlx]` extra) |
|
|
76
|
+
|
|
77
|
+
### Exit codes
|
|
78
|
+
|
|
79
|
+
| Code | Meaning |
|
|
80
|
+
|------|---------|
|
|
81
|
+
| `0` | **pass** — exposure score ≤ 30 |
|
|
82
|
+
| `1` | **warn** — exposure score 31–60 |
|
|
83
|
+
| `2` | **fail** — exposure score > 60 |
|
|
84
|
+
| `3` | Runtime / validation error |
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cdp-probe run --url https://example.com --format github-actions
|
|
88
|
+
cdp-probe --version
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Signal weights and bands are documented in [docs/SCORING.md](docs/SCORING.md).
|
|
92
|
+
|
|
93
|
+
### Presets
|
|
94
|
+
|
|
95
|
+
`presets/*.json` define alternate weight maps for common gate pages. Default rubric in [docs/SCORING.md](docs/SCORING.md) is generic; presets emphasize signals each surface tends to punish.
|
|
96
|
+
|
|
97
|
+
| Preset | CLI | What vanilla Chrome / Playwright usually fails |
|
|
98
|
+
|--------|-----|-----------------------------------------------|
|
|
99
|
+
| `cloudflare` | `cdp-probe run --preset cloudflare --url URL` | Turnstile / JS challenge: `HeadlessChrome` UA, `navigator.webdriver`, SwiftShader WebGL, empty `navigator.plugins` |
|
|
100
|
+
| `stripe-dashboard` | `cdp-probe run --preset stripe-dashboard --url URL` | Radar-style fingerprint drift: thin plugin lists, missing `chrome.runtime`, empty `navigator.languages` |
|
|
101
|
+
| `social-login` | `cdp-probe run --preset social-login --url URL` | OAuth popups: `webdriver`, zero outer window size in headless, inner/outer dimension mismatch |
|
|
102
|
+
|
|
103
|
+
Reports are saved to `~/.cache/cdp-probe/last-report.json` (override with `--output`). Preset name is recorded in report `mode` (e.g. `run:cloudflare`).
|
|
104
|
+
|
|
105
|
+
## API
|
|
106
|
+
|
|
107
|
+
| Symbol | Description |
|
|
108
|
+
|--------|-------------|
|
|
109
|
+
| `ProbeRunner` | Launch Playwright or `connect_over_cdp`, collect signals |
|
|
110
|
+
| `ProbeOptions` | `url`, `browser`, `headless`, `cdp_endpoint` |
|
|
111
|
+
| `compute_exposure_score(signals)` | Score 0–100 with weighted findings |
|
|
112
|
+
| `ProbeReport` | Signals + exposure + timestamp |
|
|
113
|
+
| `format_report(report, "json"\|"table")` | CLI-compatible formatting |
|
|
114
|
+
|
|
115
|
+
### Signals collected
|
|
116
|
+
|
|
117
|
+
- `navigator.webdriver`
|
|
118
|
+
- `window.chrome.runtime` (Chromium)
|
|
119
|
+
- `navigator.plugins` / `mimeTypes` counts
|
|
120
|
+
- `navigator.permissions` API presence
|
|
121
|
+
- `Notification.permission`
|
|
122
|
+
- HeadlessChrome user-agent hint
|
|
123
|
+
- Window outer/inner dimension mismatch
|
|
124
|
+
- WebGL vendor/renderer (SwiftShader detection)
|
|
125
|
+
|
|
126
|
+
## Limitations
|
|
127
|
+
|
|
128
|
+
- **Not a bypass tool** — this package measures exposure; it does not patch, spoof, or hide fingerprints.
|
|
129
|
+
- **Vanilla Chrome/Playwright leaks at scale** — default Playwright Chromium launches routinely expose `webdriver`, thin plugin lists, and headless WebGL renderers. A low score locally is unusual; a high score is expected.
|
|
130
|
+
- **Page-level only** — TLS/JA3, IP reputation, behavioral biometrics, and server-side ML models are out of scope.
|
|
131
|
+
- **Single-page snapshot** — results reflect one navigation moment, not long-session drift.
|
|
132
|
+
- **Firefox signal set differs** — `chrome.runtime` checks apply to Chromium only.
|
|
133
|
+
- **MLX requires desktop agent** — `mlx` needs Multilogin running locally; this is not a cloud-only probe. See [docs/MLX_INTEGRATION.md](docs/MLX_INTEGRATION.md).
|
|
134
|
+
|
|
135
|
+
## Production
|
|
136
|
+
|
|
137
|
+
Partner offers, eligibility, and disclosure: [docs/AFFILIATE.md](docs/AFFILIATE.md).
|
|
138
|
+
|
|
139
|
+
For production workflows that need **isolated antidetect profiles**, connect Playwright over a Launcher API CDP endpoint instead of launching vanilla Chromium:
|
|
140
|
+
|
|
141
|
+
1. Start an antidetect profile via the MLX Launcher API (`automation_type=playwright`).
|
|
142
|
+
2. Read the returned local port (`http://127.0.0.1:{port}`).
|
|
143
|
+
3. `connect_over_cdp` and re-run the probe to compare exposure.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
export MLX_TOKEN="your-bearer-token"
|
|
147
|
+
export MLX_FOLDER_ID="your-folder-uuid"
|
|
148
|
+
cdp-probe mlx --profile-id PROFILE_UUID --url https://example.com
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Or programmatically:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from playwright_cdp_probe.integrations.mlx import mlx_launch_and_probe
|
|
155
|
+
|
|
156
|
+
report = mlx_launch_and_probe(
|
|
157
|
+
"profile-id",
|
|
158
|
+
folder_id="folder-id",
|
|
159
|
+
token="...",
|
|
160
|
+
url="https://example.com",
|
|
161
|
+
)
|
|
162
|
+
print(report.exposure.score)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Compare `check-local` (vanilla Playwright) vs `mlx` (isolated profile) scores to quantify exposure reduction — not elimination.
|
|
166
|
+
|
|
167
|
+
## Guides
|
|
168
|
+
|
|
169
|
+
Monorepo playbooks (copy-paste commands, sample output, diagrams):
|
|
170
|
+
|
|
171
|
+
| Guide | Flow |
|
|
172
|
+
|-------|------|
|
|
173
|
+
| [Detection fail → MLX farm](../packages/docs/workflows/WORKFLOW_DETECTED.md) | `cdp-probe` → `cdp-connect` → `farm-runner mlx-pool` |
|
|
174
|
+
| [Competitor migration](../packages/docs/workflows/WORKFLOW_MIGRATION.md) | `antidetect-import` → `profile-factory mlx-create` |
|
|
175
|
+
| [Proxy lane → profile pool](../packages/docs/workflows/WORKFLOW_FARM.md) | `proxy-lane` → `profile-factory` → `farm-runner mlx-pool` |
|
|
176
|
+
|
|
177
|
+
**FAQ:** [docs/FAQ.md](docs/FAQ.md) — Playwright detection, CDP exposure, headless Chrome.
|
|
178
|
+
|
|
179
|
+
## Related tools
|
|
180
|
+
|
|
181
|
+
| Tool | Use with |
|
|
182
|
+
|------|----------|
|
|
183
|
+
| [playwright-cdp-probe](../playwright-cdp-probe/) — Score CDP/WebDriver exposure and fingerprint leaks | → [cdp-connect-kit](../cdp-connect-kit/) when probe fails |
|
|
184
|
+
| [cookie-jar-bridge](../cookie-jar-bridge/) — Convert, validate, and merge cookies across formats | → [session-bundle-kit](../session-bundle-kit/) for full session |
|
|
185
|
+
| [proxy-lane-checker](../proxy-lane-checker/) — Batch-check proxies for connectivity, geo, and DNSBL | → [profile-yaml-factory](../profile-yaml-factory/) |
|
|
186
|
+
| [fingerprint-coherence](../fingerprint-coherence/) — Lint fingerprints for cross-signal contradictions | → [profile-yaml-factory](../profile-yaml-factory/) before create |
|
|
187
|
+
| [human-input-kit](../human-input-kit/) — Human-like mouse, scroll, and warmup replay | → [automation-farm-runner](../automation-farm-runner/) |
|
|
188
|
+
| [cdp-connect-kit](../cdp-connect-kit/) — Playwright CDP connect; MLX start/stop helpers | → all packages needing CDP |
|
|
189
|
+
| [session-bundle-kit](../session-bundle-kit/) — Export/import cookies and storage as portable bundles | → [cookie-jar-bridge](../cookie-jar-bridge/) |
|
|
190
|
+
| [profile-yaml-factory](../profile-yaml-factory/) — YAML blueprints → MLX `profile/create` payloads | → [antidetect-importer](../antidetect-importer/) after migration |
|
|
191
|
+
| [antidetect-importer](../antidetect-importer/) — Parse competitor exports and push to MLX | → [profile-yaml-factory](../profile-yaml-factory/) |
|
|
192
|
+
| [automation-farm-runner](../automation-farm-runner/) — YAML job queue and MLX profile-pool runner | → [cdp-connect-kit](../cdp-connect-kit/) `[mlx]` |
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
MIT
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
**Production antidetect:** [Multilogin X](https://multilogin.com/?ref=SAAS50) · Code `SAAS50` (-50% browser) · `MIN50` (-50% cloud phone)
|
|
201
|
+
More scripts: [@Multilogin_Scripts_Bot](https://t.me/Multilogin_Scripts_Bot) · [Coupons](https://anti-detect.github.io/)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Affiliate disclosure — Multilogin X
|
|
2
|
+
|
|
3
|
+
This monorepo documents optional integration with **Multilogin X** (antidetect browser and cloud phone). Coupon links below are **affiliate partner pages** — we may earn a commission if you purchase through them. Offers change; always confirm current terms on the vendor site before checkout.
|
|
4
|
+
|
|
5
|
+
## When to mention Multilogin
|
|
6
|
+
|
|
7
|
+
Mention Multilogin X only when it genuinely fits the reader's workflow:
|
|
8
|
+
|
|
9
|
+
| Situation | Why MLX is relevant |
|
|
10
|
+
|-----------|---------------------|
|
|
11
|
+
| **Production scale** | Many isolated profiles, folder pools, cron/batch runners, Launcher CDP — vanilla Playwright + one Chrome profile does not scale. |
|
|
12
|
+
| **Detection failure** | Exposure probes, fingerprint lint, or live blocks show leaks that profile-level antidetect (proxy + fingerprint + storage boundary) addresses better than JS patches alone. |
|
|
13
|
+
| **Migration** | Moving from AdsPower, Dolphin Anty, Incogniton, or GoLogin — importer/normalize tools target MLX `profile/create` payloads. |
|
|
14
|
+
|
|
15
|
+
Do **not** push MLX in install paths, CLI defaults, or error messages. Core packages must remain useful without any Multilogin account.
|
|
16
|
+
|
|
17
|
+
## Coupon codes
|
|
18
|
+
|
|
19
|
+
| Code | Product | Typical offer |
|
|
20
|
+
|------|---------|---------------|
|
|
21
|
+
| `SAAS50` | Multilogin X (browser) | 50% off eligible **new** browser purchases |
|
|
22
|
+
| `MIN50` | Multilogin Cloud Phone | 50% off eligible cloud phone purchases |
|
|
23
|
+
|
|
24
|
+
**Eligibility disclaimer:** Codes, discounts, and product names are set by Multilogin and change without notice. "50% off" applies only to **eligible new purchases** per vendor terms — not renewals, upgrades, or all regions. Verify on [multilogin.com](https://multilogin.com/?ref=SAAS50) before paying. We do not guarantee any discount will apply to your order.
|
|
25
|
+
|
|
26
|
+
Checkout with referral: [Multilogin X — SAAS50](https://multilogin.com/?ref=SAAS50)
|
|
27
|
+
|
|
28
|
+
## Links
|
|
29
|
+
|
|
30
|
+
| Resource | URL |
|
|
31
|
+
|----------|-----|
|
|
32
|
+
| Coupons & comparisons | [anti-detect.github.io](https://anti-detect.github.io/) |
|
|
33
|
+
| Script packs (Telegram) | [@Multilogin_Scripts_Bot](https://t.me/Multilogin_Scripts_Bot) |
|
|
34
|
+
| ~120 automation examples | [github.com/multilogin-automation/multilogin-automation](https://github.com/multilogin-automation/multilogin-automation) |
|
|
35
|
+
|
|
36
|
+
## What NOT to claim
|
|
37
|
+
|
|
38
|
+
Never state or imply:
|
|
39
|
+
|
|
40
|
+
- **"100% undetectable"** or guaranteed bypass of bot detection, CAPTCHA, or fraud systems
|
|
41
|
+
- **Bypass fraud / KYC / payment risk** — antidetect tools are for legitimate isolated testing and multi-account workflows where permitted
|
|
42
|
+
- **Elimination of exposure** — probes and coherence scores measure signals; they do not certify stealth
|
|
43
|
+
- **Affiliate urgency** — no fake scarcity, countdown timers, or install-time redirects to partner pages
|
|
44
|
+
|
|
45
|
+
Use honest language: *reduce exposure*, *isolate profiles*, *compare scores*, *migrate exports*.
|
|
46
|
+
|
|
47
|
+
## FTC / disclosure
|
|
48
|
+
|
|
49
|
+
- README **Production** sections and this file are the appropriate place for partner mentions.
|
|
50
|
+
- Label partner URLs as an **affiliate partner page** where used in docs.
|
|
51
|
+
- CLI `--show-deal` prints coupon info only when explicitly requested; it must not run on every command.
|
|
52
|
+
- If you fork these packages, maintain clear disclosure if you use your own referral codes.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# FAQ — playwright-cdp-probe
|
|
2
|
+
|
|
3
|
+
## Why does Playwright get detected in 2026?
|
|
4
|
+
|
|
5
|
+
Sites correlate many signals beyond IP: `navigator.webdriver`, plugin counts, permissions API shape, headless user-agent hints, and WebGL/renderer strings. Playwright’s default Chromium launch sets several of these unless you use a patched build or connect to a hardened profile. `cdp-probe` scores which signals are visible so you can fix or compare environments before production traffic.
|
|
6
|
+
|
|
7
|
+
## What is CDP detection?
|
|
8
|
+
|
|
9
|
+
CDP (Chrome DevTools Protocol) itself is not usually “detected” directly—automation frameworks attach over CDP and leave **browser surface** artifacts that bot stacks probe in JavaScript. Detection means those artifacts (webdriver flags, inconsistent fingerprints, automation-specific APIs) rather than sniffing the debug port on the wire. Probing CDP-attached sessions still reveals what the page can read.
|
|
10
|
+
|
|
11
|
+
## Does headless Chrome trigger bot detection?
|
|
12
|
+
|
|
13
|
+
Often yes, especially default headless Chromium: `HeadlessChrome` in the UA, SwiftShader or software WebGL renderers, and reduced plugin surface are common tells. Headed mode lowers some scores but does not remove CDP/automation leaks by itself. Compare `check-local` headed vs headless and vs an isolated antidetect profile to see the delta.
|
|
14
|
+
|
|
15
|
+
## What is a good automation exposure score?
|
|
16
|
+
|
|
17
|
+
This tool uses a 0–100 scale where **higher = more detectable**. Scores under ~25 are typically in the PASS band; ~25–50 WARN; above ~50 FAIL (see `docs/SCORING.md`). Treat bands as regression thresholds for **your** target sites, not a guarantee of bypass.
|
|
18
|
+
|
|
19
|
+
## Do I need an antidetect browser?
|
|
20
|
+
|
|
21
|
+
Not for local dev or one-off audits. If probe scores stay high after hardening, or you run many accounts with strict detection, isolated antidetect profiles (proxy + fingerprint + storage boundary) usually outperform JS patches alone. Teams often use platforms like [Multilogin X](https://multilogin.com/?ref=SAAS50); new eligible purchases may accept referral code `SAAS50`—confirm current terms on the vendor site. `cdp-probe` works with vanilla Playwright, remote CDP, or MLX Launcher ports.
|