Metadata-Version: 2.5
Name: browser-tools
Version: 0.7.1
Summary: Browser automation, debugging, and anti-detect browsing CLI
Project-URL: Homepage, https://github.com/dungle-scrubs/browser-tools
Project-URL: Repository, https://github.com/dungle-scrubs/browser-tools
Project-URL: Issues, https://github.com/dungle-scrubs/browser-tools/issues
Project-URL: Changelog, https://github.com/dungle-scrubs/browser-tools/releases
License-Expression: MIT
License-File: LICENSE
License-File: NOTICE
Keywords: anti-detect,browser-automation,camoufox,cdp,chrome-devtools,playwright,web-scraping
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Python: >=3.13
Requires-Dist: websockets>=16.0
Provides-Extra: all
Requires-Dist: aiohttp>=3.14.1; extra == 'all'
Requires-Dist: camoufox[geoip]>=0.4.11; extra == 'all'
Requires-Dist: pillow>=10.0.0; extra == 'all'
Provides-Extra: camoufox
Requires-Dist: aiohttp>=3.14.1; extra == 'camoufox'
Requires-Dist: camoufox[geoip]>=0.4.11; extra == 'camoufox'
Provides-Extra: profiling
Requires-Dist: pillow>=10.0.0; extra == 'profiling'
Description-Content-Type: text/markdown

# Browser Tools

[![CI](https://github.com/dungle-scrubs/browser-tools/actions/workflows/ci.yml/badge.svg)](https://github.com/dungle-scrubs/browser-tools/actions/workflows/ci.yml)
[![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://docs.astral.sh/ruff/)

Browser automation, debugging, and anti-detect browsing CLI. Provides:

- **Raw CDP passthrough** - Any `Domain.method` goes straight to the running
  browser, with protocol help read live from it
- **Snapshot-based page automation** - Accessibility-tree snapshots and UID
  interaction (`snapshot`, `click --uid`, `fill --uid`), in Python over CDP
- **Named browser instances** - Long-lived Chrome with a registry and named
  profiles that keep a login across restarts
- **External browsers** - `--endpoint http://127.0.0.1:9222` drives a browser
  you already have open and logged in, writing nothing to the registry
- **Frame-aware tools** - Iframe/CDP frame tree management, execution context
  resolution, and storage inspection
- **Interstitial detection** - Multi-signal heuristic detection for Cloudflare,
  DataDome, Akamai, PerimeterX, Imperva, AWS WAF, and other challenge pages
- **Camoufox anti-detect browsing** - Fingerprint-injected Firefox-based
  browsing for bot-protected sites
- **Anything else CDP can do** - File upload, viewport emulation, cookies,
  PDF export and page management have no curated verb and need none; `bt
  guide` has a worked example of each
- **CPU profiling** - `browser-tools-profiler`, a second command installed
  alongside `bt`, with timed capture and threshold-triggered capture

## Quick Start

### Installation

```bash
# Using uv (recommended)
uv tool install browser-tools

# Or with pip
pip install browser-tools
```

### Usage

```bash
# Run the CLI (`bt` is an alias for `browser-tools`)
bt --help

# Launch a browser. It is registered by name and outlives this command, so
# later verbs drive the same Chrome and share its page and login state.
bt launch

# Navigate through the raw CDP passthrough, then read the page.
bt Page.navigate '{"url": "https://example.com"}'
bt snapshot

# Interact by UID from that snapshot.
bt click --uid 1-13

# When the page is blocking, ask directly.
bt detect

bt status          # registered instances and their page targets
bt stop            # close the browser and retire its registry entry
```

Omit `INSTANCE` while exactly one instance is running; with several, every verb
names the candidates rather than guessing. `bt help Domain.method` reads the
protocol schema from the running browser.

**`bt guide` is the manual.** It is the complete CLI surface -- every verb,
every refusal with its exit code, the login walkthrough, the UID rule, and the
`--endpoint` rules -- and reading it is enough to drive a browser with this
tool. A test fails the build when a verb has no entry in it.

To drive a browser you started yourself, pass its debugging port instead of an
instance name:

```bash
# Chrome started with --remote-debugging-port=9222, already logged in.
bt snapshot --endpoint http://127.0.0.1:9222
bt click --uid 0BDAEF756714-14 --endpoint http://127.0.0.1:9222
```

Nothing is written to the registry, so `status` does not list it and `stop` and
`cleanup` cannot reach it -- which is what keeps them away from your real Chrome
profile directory. Only `127.0.0.1` and `::1` are accepted; forward a remote
browser with `ssh -L 9222:127.0.0.1:9222 <host>`. `Browser.close` and
`Browser.crash` are refused over `--endpoint`.

### Development setup

```bash
# Clone and install in editable mode
git clone https://github.com/dungle-scrubs/browser-tools.git
cd browser-tools
uv sync
```

Runtime requirements:

- **Chrome Canary** by default (the default channel is `canary`). Use another
  installed channel with `--channel stable|beta|dev`, e.g. `--channel stable`
  for regular Google Chrome.
- **Camoufox** (`camoufox fetch`) for anti-detect Firefox workflows, with the
  `camoufox` extra

The default install depends on `websockets` only. There is no Node.js
dependency: the Node `chrome-devtools-mcp` path was removed in RFC-01.

## Architecture

Five layers. Each upper layer consumes only the layer below it, and every
curated tool calls the same CDP `send` path the raw passthrough uses -- so any
method the installed browser supports works through `bt Domain.method` whether
or not a verb exists for it.

| Layer | Name | Contents |
|---|---|---|
| 4 | Front | CLI verbs (the only surface), `bt guide`, the agent skill that points at it |
| 3 | Policy | Named profiles and the profile root, fingerprint profiles, engine routing |
| 2 | Native toolset | Curated tools as plain CDP consumers; profiling; window marking |
| 1 | Core (vendored) | Registry, liveness, launcher, supervisor, attach, passthrough, live-schema help |
| 0 | Browser | Chrome/Chromium over CDP; Camoufox for anti-detect paths |

```
cli.py                       CLI entry point (argparse, verb dispatch, exit codes)
        |
        +-- lifecycle.py             launch / status / stop / cleanup over the registry
        |       +-- camoufox_runner.py   Camoufox host process (`launch --engine camoufox`)
        |       +-- process_utils.py     Chrome process and port utilities
        |
        +-- endpoint.py              `--endpoint URL`: loopback check, refusals
        +-- passthrough.py           Raw `Domain.method` send + the focus guard
        +-- curated.py               The curated verbs over one short-lived CDP handler
        +-- one_shot.py              Connect, resolve a page target, attach, detach
        +-- events.py                attach / wait / console-list / network-list
        +-- list_verbs.py            frames / storage
        +-- user_settings.py         The persistent settings file (window border)
        |
        +-- core/                    Vendored chrome-agent core (RFC-01; see NOTICE)
        |       +-- registry.py          Named instances, liveness, retirement
        |       +-- launcher.py          Chrome launch (windowless, background first window)
        |       +-- supervisor.py        Per-instance window marking and retirement
        |       +-- cdp_client.py        CDP WebSocket client and target discovery
        |       +-- attach.py            Isolated event subscriptions
        |       +-- protocol.py          Live protocol schema for `help`
        |       +-- fingerprint.py       Launch-flag fingerprint profiles
        |
        +-- cdp_handler.py           CDP tool implementations (snapshot, click, fill, ...)
        |       +-- native_snapshot.py   Accessibility-tree snapshot and UID scheme
        |       +-- native_interaction.py UID click / fill
        |       +-- frame_manager.py     Frame tree management
        |       +-- interstitial.py      Challenge detection and the retry policy
        |       +-- screencast.py        Screencast capture state machine
        |       +-- screenshot_utils.py  Blank-frame detection
        |       +-- tool_registry.py     Single source of truth for CDP tool routing
        |       +-- mcp_response.py      Response envelope builders
        |
        +-- profiler.py              Standalone CPU profiler (`browser-tools-profiler`)
```

The CLI is the only surface. The optional MCP front and the persistent-session
stack behind it were deleted in RFC-01 Phase 5; there is no daemon, no broker,
and no listening socket.

## Keeping login state across calls

Login state lives in a browser profile directory and survives only while the
same directory is reused. To keep a session logged in:

- **Use a named profile**: `bt launch --profile <name>`. Named profiles persist
  across restarts and are unaffected by headed/headless switches, viewport, or
  which directory you invoke from.
- A profile is held by at most one live instance. Launching into a profile
  another instance holds fails naming the holder rather than opening a second
  browser on the same directory.
- Without `--profile`, a launch gets a fresh ephemeral directory and starts
  logged out.
- `bt profile list` shows every profile with its path and its live holder, and
  `bt profile delete NAME` removes one. Deleting a profile deletes its login
  state; `cleanup` never removes a profile on age.

Profiles live in durable storage: `$BROWSER_TOOLS_PROFILES_DIR`, else
`$XDG_DATA_HOME/browser-tools/profiles`, else
`~/.local/share/browser-tools/profiles`. They used to live under `/tmp`, where
the operating system deleted every signed-in session at boot, silently. A
profile still there is listed with `"legacy": true`; `bt profile migrate`
moves them all across (`--dry-run` first, `--back` to reverse it), and
`launch --profile NAME` brings that one forward by itself.
- **Camoufox** persists login state only when you pass `--profile`; without it,
  every launch starts logged out.

## Development

```bash
uv sync
uv run ruff check src/ tests/
uv run pytest
```

## License

MIT - see [LICENSE](LICENSE).

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
All contributors are expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md).

## Security

Found a vulnerability? See [SECURITY.md](SECURITY.md) for responsible disclosure.
