Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

Termux Browser Pilot is a real browser automation tool for Termux/Android. It controls Firefox via native X11 input (xdotool + clipboard JS execution) or Chromium via CDP WebSocket. Firefox passes Cloudflare natively via TLS fingerprint; Chromium requires stealth patches.

## Development Commands

### Install
```bash
bash setup.sh # One-command installer (Termux)
pip install -e . # Development install
pip install -e ".[chromium,mcp]" # With optional deps
```

### Run
```bash
tbp goto https://example.com # Auto-starts daemon on first use
tbp goto https://example.com --instance hermes-1 # Use isolated instance
tbp status --instance hermes-1 # Check instance status
tbp stop --instance hermes-1 # Shutdown instance
tbp kill --all # Kill all instances
python -m src.daemon start --browser firefox --instance hermes-1
```

### Tests
Tests are standalone asyncio scripts (not pytest). They require a running browser:

```bash
# For CDP tests (test_basic.py, test_webgl.py):
# Start Chromium with remote debugging first, then:
python tests/test_basic.py

# For native Firefox tests (test_native_fp.py, test_nowsecure.py, test_sannysoft.py):
# Start Firefox via tbp daemon first, then:
python tests/test_native_fp.py
```

There is no `pytest` suite. Tests are run individually as scripts.

### Packaging
```bash
python -m build # Build wheel/sdist (uses pyproject.toml)
```

## Architecture

### High-Level Flow
```
CLI (cli.py) ──Unix socket (~/.tbp/instances/{id}/daemon.sock)──→ Daemon (daemon.py)
└── Pilot (src/pilot.py)
├── BrowserPilot Xvfb + openbox + browser lifecycle
├── NativeFirefoxSession xdotool + clipboard JS (Firefox only)
├── CDPSession WebSocket CDP (Chromium only)
├── PageCommands navigate, eval, text, html, links
├── InputCommands click, type, scroll, Bezier mouse
├── ScreenshotCommands PNG, full-page, PDF
├── CookieCommands get/set/save/load/export/import
├── NetworkTracker request capture (Chromium CDP events)
├── Accessibility a11y tree
└── CloudflareHandler Turnstile solver (Chromium only)
```

### Browser Modes

**Firefox (default):** No automation framework. Started as a regular browser process. Controlled entirely through `xdotool` (native X11 input) and clipboard-based JS execution via the Web Console (`F12` → paste → `Enter`). A local HTTP callback server receives JS results. This avoids `navigator.webdriver=true` and other automation flags, so Cloudflare passes instantly via TLS fingerprint.

**Chromium:** Connected via Chrome DevTools Protocol (CDP) over WebSocket. Requires `websockets>=12.0`. Uses `src/stealth.py` to patch `navigator.webdriver`, WebGL, canvas, and other fingerprints. `src/cdp.py` deliberately does NOT enable `Runtime` domain by default (avoids `consoleAPICalled` detection signal used by anti-bot systems).

### Key Module Responsibilities

- `src/browser.py`: Starts/stops Xvfb display, manages browser process (Firefox or Chromium), handles temp profile cleanup on crash.
- `src/native.py`: Firefox-only. Opens/closes Web Console, pastes JS via clipboard, reads results from a local HTTP callback server. Includes toolbar height detection and window ID tracking.
- `src/cdp.py`: Low-level CDP WebSocket client with async request/response matching and event handler dispatch.
- `src/commands.py`: High-level page commands. URL scheme validation. `networkidle` wait uses CDP Network events.
- `src/input.py`: Mouse/keyboard via CDP `Input.dispatchMouseEvent`/`dispatchKeyEvent`. Includes Bezier curve generation for human-like movement. Shadow DOM piercing via recursive `querySelector`.
- `src/daemon.py`: Persistent Unix socket server. Auto-starts browser. Maintains single browser instance. Idle timeout support. JSON command protocol.
- `src/client.py`: Daemon client. `send_command()` auto-starts daemon if not running.
- `src/mcp_server.py`: MCP server exposing browser tools to Claude Code / AI agents.

### Data Directories

- `~/.tbp/`: Main working directory
- `~/.tbp/instances/{id}/`: Per-instance directory (daemon.sock, daemon.pid, daemon.log, .lock)
- `~/.tbp/instances/{id}/firefox_profile/`: Instance-specific Firefox profile
- `~/.tbp/instances/{id}/downloads/`: Instance-specific download directory
- `~/.tbp/sessions/`: Named multi-tab sessions (shared across instances)
- `~/.tbp/profiles/`: Named browser state profiles (cookies + localStorage)
- `~/.tbp/auth/`: Auth sessions (0o600 permissions)

Default instance ID is `"default"`; existing `~/.tbp/daemon.sock` paths are preserved for backward compatibility.

## Important Constraints

- **Target platform:** Termux on Android (aarch64, POSIX Linux). Do not assume systemd, docker, or desktop Linux features.
- **Firefox profile CA fix:** The daemon writes `security.enterprise_roots.enabled=true` to each instance's `firefox_profile/user.js` on startup to fix Termux SSL certificate errors.
- **Chromium single-process:** Only forced via `TBP_SINGLE_PROCESS=1` env var. Default multi-process is required for Cloudflare stealth to work.
- **Window size auto-detection:** Uses `src/device.py` (reads `ro.product.model`, DPI, `/sys/kernel/gpu/gpu_model`, `/proc/meminfo`). Capped at 1920x1080.
- **No pytest/unittest framework:** Tests are standalone asyncio scripts. `tests/test_multi_instance.py` validates path isolation and resource discovery.
- **Optional imports:** `cdp.py`, `cloudflare.py`, `network.py` use `try/except ImportError` guards since `websockets` is optional.

## Multi-Instance Support

Multiple independent browser instances can run concurrently. Each instance gets its own daemon process, Unix socket, Xvfb display, CDP port, Firefox profile, and download directory.

**Resource discovery:** `find_free_display()` scans `$TMPDIR/.X{n}-lock` (or `/tmp/.X{n}-lock`) for display numbers 99-199. `find_free_port()` test-binds TCP ports 9222-9322. Both skip resources already in use.

**CLI:** Add `--instance/-i` to any command. Use `tbp kill --all` to terminate every instance.

**MCP:** Set `TBP_INSTANCE=hermes-1` env var when running the MCP server, or pass `instance` parameter in tool calls.

**Hermes / sub-agents:** Each sub-agent should use a unique instance ID (e.g., `hermes-1`, `hermes-2`). This avoids queuing because Firefox cannot handle simultaneous page navigation.

**Memory warning:** Each Firefox + Xvfb instance consumes significant RAM. On Android, practical concurrency is typically 2-3 instances.

## MCP Integration

The repo includes `.mcp.json` for Claude Code auto-discovery. The MCP server (`tbp-mcp` entry point) exposes browser automation tools. Requires `pip install "mcp[cli]>=1.0"`.
Loading