Metadata-Version: 2.4
Name: nakalbrowser
Version: 0.2.0
Summary: Nakal Browser — light, powerful CDP browser automation for Python
Author: NakalBrowser
License: MIT
Project-URL: Homepage, https://github.com/nakalbrowser/nakalbrowser
Project-URL: Documentation, https://github.com/nakalbrowser/nakalbrowser
Keywords: browser,automation,cdp,chrome,scraping,nakal,mcp
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: websocket-client>=1.6.0
Requires-Dist: curl_cffi>=0.13.0
Provides-Extra: parse
Requires-Dist: selectolax>=0.3.21; extra == "parse"
Provides-Extra: proc
Requires-Dist: psutil>=5.9.0; extra == "proc"
Provides-Extra: cloak
Requires-Dist: cloakbrowser>=0.4.0; extra == "cloak"
Provides-Extra: captcha
Provides-Extra: all
Requires-Dist: nakalbrowser[cloak,parse,proc]; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"

<p align="center">
  <img src="BannerGithubBrowser.png?v=2" alt="Nakal Browser" width="100%">
</p>

<h1 align="center">Nakal Browser</h1>

<p align="center">
  <b>Lightweight browser automation for Python — fast, stealth, production-ready</b>
</p>

<p align="center">
  <img src="https://img.shields.io/badge/python-3.10+-blue?style=flat-square&logo=python&logoColor=white" alt="Python">
  <img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="License">
  <img src="https://img.shields.io/badge/CDP-pure-red?style=flat-square" alt="CDP">
  <img src="https://img.shields.io/badge/version-0.2.0-orange?style=flat-square" alt="Version">
</p>

---

## Features

- **Pure CDP** — no chromedriver, no webdriver, direct Chrome DevTools Protocol
- **Rush** — HTTP requests + JavaScript rendering in one class
- **Byte-perfect TLS fingerprint** — bypass Cloudflare / Akamai / DataDome JA3 checks via `curl_cffi` (libcurl-impersonate)
- **gRPC-Web support** — call Connect-RPC / Envoy APIs directly
- **Playwright-style locators** — `bot.locator("h1").click()` with auto-wait
- **Stealth built-in** — fingerprint, humanize, proxy, multi-account
- **Any Chromium** — Chrome, Edge, CloakBrowser, or custom binary
- **Farm** — run multiple accounts in parallel
- **Network capture** — sniff requests, intercept, mock responses
- **AI Agent ready** — snapshot, extract, a11y for LLM integration
- **MCP server** — exposes browser + HTTP tools to Claude Code / Desktop / Cursor

---

## Install

```bash
pip install nakalbrowser
```

```bash
python -m nakalbrowser doctor   # check environment
```

---

## Quick start

```python
from nakalbrowser import Engine

with Engine(headless=True) as bot:
    bot.open("https://example.com")
    print(bot.title)
    print(bot.find("h1").text)
    bot.find("a").click()
```

---

## Rush — HTTP + JS hybrid

```python
from nakalbrowser import Rush

# Pure HTTP (fast, no browser) — byte-perfect Chrome TLS fingerprint
with Rush(impersonate="chrome") as r:
    resp = r.get("https://api.example.com/data")
    print(resp.json, resp.headers, resp.cookies)

# Need JS? Render with browser
with Rush(headless=True) as r:
    r.render("https://spa.example.com", wait_for=".content", wait_until="networkidle")
    print(r.find("h1").text)
```

**All Rush modes:**

| Mode | What it does |
|---|---|
| `impersonate="chrome"` / `"firefox"` / `"safari"` / `"edge"` | Pick a browser TLS fingerprint. Default is `chrome131`. |
| `http_version="auto"` | Negotiate h2 via ALPN, fall back to h1. **Default.** |
| `http_version="h2"` | Force HTTP/2 (required for gRPC-Web / Connect-RPC). |
| `http_version="h1"` | Force HTTP/1.1 via stdlib urllib (last resort). |
| `proxy="http://user:pass@host:port"` | Proxy with optional auth. |
| `timeout=30` / `retries=2` | Per-request timeout and retry policy. |
| `face=Face(...)` | Sticky fingerprint for User-Agent / Accept-Language. |
| `render=True` (on `get()`) | Fall back to bundled Chromium for JS rendering. |

**Rush auto-fallback heuristic:** when `http_version="auto"` and the
response is a Cloudflare bot challenge (403/421 with `no-js` /
`enable javascript` body), Rush retries on h1 — but only for endpoints
that can serve h1. gRPC-Web endpoints are excluded because they
require h2.

### gRPC-Web

```python
from nakalbrowser import Rush

def encode_varint(n):
    out = bytearray()
    while n > 0x7F:
        out.append((n & 0x7F) | 0x80); n >>= 7
    out.append(n & 0x7F)
    return bytes(out)

def encode_string(field_num, value):
    b = value.encode()
    return bytes([(field_num << 3) | 2]) + encode_varint(len(b)) + b

def encode_frame(proto):
    # gRPC-Web: 1-byte flag (0=uncompressed) + 4-byte length + payload
    return b"\x00" + len(proto).to_bytes(4, "big") + proto

fields = encode_string(1, "user@example.com")
frame = encode_frame(fields)

with Rush(impersonate="chrome", http_version="h2") as r:
    resp = r.post(
        "https://accounts.example.com/auth.AuthService/Method",
        data=frame,
        headers={
            "Content-Type": "application/grpc-web+proto",
            "X-Grpc-Web": "1",
            "X-User-Agent": "connect-es/2.1.1",
            "Accept": "application/grpc-web+proto",
        },
    )
print("grpc-status:", resp.headers.get("grpc-status"))
```

### Troubleshooting

- **`resp.status == 0` + `resp.error` non-empty** → transport failure
  (TLS / connection / DNS / timeout). Try a different `proxy`,
  increase `timeout`, or use `Engine` (real browser).
- **`resp.status == 403` + `cf-ray` header** → Cloudflare bot
  challenge. Try a different `impersonate` profile or fall back to
  `Engine`.
- **`resp.status == 200` + `grpc-status: 13`** → gRPC-Web frame is
  malformed. Verify the 5-byte prefix (`\x00` + 4-byte length).
- **`resp.status == 200` + `grpc-status: 8`** → server-side rate
  limit. Wait, slow down, or rotate proxies.

---

## Locators

```python
bot.locator("h1").click()
bot.locator("#email").fill("user@example.com")
bot.get_by_role("button", name="Submit").click()
bot.get_by_text("Welcome").expect().to_have_text("Welcome")
bot.locator("h1").expect().to_be_visible(timeout=5)
bot.locator("form").locator("input[type=text]").fill("x")
```

---

## Multi-account & Proxy

```python
from nakalbrowser import Engine, Face, Profile, Proxy

face = Face.from_seed("acc01", os="windows")
prof = Profile.create("acc01", solid=True, proxy="socks5://user:pass@host:1080")
with Engine(profile=prof, headless=True) as bot:
    bot.open("https://example.com")
```

---

## Farm — parallel execution

```python
from nakalbrowser import Farm, Profile

profiles = [Profile.create(f"acc_{i}", solid=True) for i in range(10)]
farm = Farm(profiles=profiles, concurrency=3, headless=True)
results = farm.run(lambda bot, prof: bot.open("https://target.com") or bot.title)
```

---

## Mouse & Keyboard

```python
bot.mouse.click(100, 200)
bot.mouse.drag(0, 0, 500, 500)
bot.keyboard.type("Hello World")
bot.keyboard.hotkey("Control", "a")
```

---

## Network

```python
# Capture
bot.sniff.start("**/api/**")
bot.open("https://example.com")
for c in bot.sniff.all():
    print(c.method, c.url, c.status)

# Mock
bot.intercept.fulfill("*/api/data", body='{"ok": true}', content_type="application/json")
```

---

## AI Agent

```python
with Engine(headless=True) as bot:
    bot.open("https://example.com")
    state = bot.snapshot(markdown=True)    # compact state for LLM
    data = bot.extract({"title": "h1", "links": "a@href[]"})
    bot.find("text:Learn more").click()
```

---

## API

| Class | What it does |
|---|---|
| `Engine` | Main browser controller |
| `Rush` | HTTP + JS render |
| `Face` | Fingerprint (save/load) |
| `Profile` | Multi-account bundle |
| `Proxy` | Proxy parse + auth bridge |
| `Farm` | Concurrent multi-profile |
| `Locator` | Playwright-style finder |
| `Mouse` / `Keyboard` | Precise input |
| `Page` / `Node` / `Ghost` | Tab / element / missing |
| `Pane` / `Shell` | iframe / shadow DOM |

Plus: `sniff`, `intercept`, `until`, `download`, `save_state` / `load_state`

---

## CLI

```bash
python -m nakalbrowser doctor
python -m nakalbrowser open https://example.com --headless
```

---

## Browser

```python
Engine()                       # auto-detect
Engine(browser="chrome")       # force Chrome
Engine(browser="edge")         # force Edge
Engine(browser_path="...")     # custom path
```

---

## MCP Server

One command to install MCP for Claude Code, Claude Desktop, Cursor:

```bash
nakalbrowser mcp
```

That's it. Restart your AI assistant and the tools are available.

**Tools:**

| Tool | Purpose |
|---|---|
| `browser_open` / `browser_click` / `browser_fill` / `browser_text` / `browser_screenshot` / `browser_extract` / `browser_js` / `browser_close` | Real-browser automation via CDP |
| `http_get` / `http_post` / `http_request` | curl_cffi-backed HTTP (all Rush knobs) |
| `http_diagnose` | Auto-probe 5 impersonate profiles, return which one works |
| `scrape_links` | Render URL, return anchors |

Every error response includes a `hint` field telling the AI agent
what to try next. `http_diagnose` is the recommended starting point
for unknown endpoints.

---

## Docs

| File | Content |
|---|---|
| [docs/USAGE.md](docs/USAGE.md) | Full guide |
| [docs/AGENTS.md](docs/AGENTS.md) | Project structure for AI agents |
| [.claude/skills/nakalbrowser/SKILL.md](.claude/skills/nakalbrowser/SKILL.md) | Packaged Claude Code skill |

## Claude Code Skill

NakalBrowser ships a packaged skill for the Claude Code `Skill` tool.
Two ways to install it:

### Option A — local install (recommended for development)

The skill lives at `.claude/skills/nakalbrowser/SKILL.md` inside this
repo. Claude Code picks it up automatically when you run from inside
the project directory. Verify with `/skills` in Claude Code — you should
see `nakalbrowser` listed.

### Option B — install into your home directory

To use the skill from any directory (not just inside the NakalBrowser
repo), copy the skill into `~/.claude/skills/`:

```bash
# Linux / macOS / WSL
mkdir -p ~/.claude/skills
cp -r .claude/skills/nakalbrowser ~/.claude/skills/

# Windows (PowerShell)
$dest = Join-Path $env:USERPROFILE '.claude\skills\nakalbrowser'
New-Item -ItemType Directory -Force -Path $dest
Copy-Item -Recurse -Force .\.claude\skills\nakalbrowser\* $dest
```

Then verify with `/skills` in Claude Code.

### Option C — distribute as a Claude Code marketplace plugin

For distribution to other users, package as a marketplace plugin. See
the [Claude Code plugin docs](https://docs.claude.com/en/docs/claude-code/plugins)
for `.claude-plugin/marketplace.json` + `.claude-plugin/plugin.json` setup.
The skill manifest goes into `plugins/nakalbrowser/skills/nakalbrowser/SKILL.md`.

### What the skill does

Once installed, an AI agent can `/skill nakalbrowser` (or auto-load it
when the task is browser automation / HTTP / scraping). The skill
loads:

- All Rush mode catalog (constructor options, request options,
  response shape)
- 4 quick recipes (scrape, HTTP, gRPC-Web, diagnose)
- 7-step failure recovery tree
- Common pitfalls (Content-Length, header case, TLS errors)
- Multi-account setup example

---

## License

MIT
