Metadata-Version: 2.5
Name: hitl-cdp-browser-provider
Version: 1.3.1
Summary: Bridge any CDP-capable browser session (agent-browser, Playwright, Puppeteer, plain Chrome) into an HTTP+WebSocket endpoint for human-in-the-loop remote viewing/control, e.g. HITL Broker's Browser Gateway.
Project-URL: Homepage, https://github.com/aqiu9/HITL-Broker
Project-URL: Repository, https://github.com/aqiu9/HITL-Broker
License: MIT
Keywords: agent-browser,browser-automation,cdp,chrome-devtools-protocol,hitl,human-in-the-loop,playwright,puppeteer
Requires-Python: >=3.9
Requires-Dist: websockets<14,>=13
Description-Content-Type: text/markdown

# hitl-cdp-browser-provider

Bridges **any** Chrome DevTools Protocol (CDP) browser session into a plain
HTTP page + WebSocket endpoint — the shape [HITL Broker](https://github.com/aqiu9/HITL-Broker)'s
Browser Gateway (and similar systems) expect a task's `internal_url` to
serve.

Works with anything that exposes a CDP remote-debugging endpoint:
[agent-browser](https://github.com/keithamus/agent-browser), Playwright,
Puppeteer, or a plain `chrome --remote-debugging-port=...`. It talks CDP's
own standard primitives (`Page.startScreencast`, `Input.dispatchMouseEvent`,
`Input.dispatchKeyEvent`) — not any tool-specific protocol — so one bridge
covers all of them. Functionally identical to the
[Node package of the same name](https://github.com/aqiu9/HITL-Broker/tree/main/packages/node/hitl-cdp-browser-provider);
pick whichever fits your automation stack (e.g. Playwright-Python/Selenium-Python
users won't need Node at all).

## Install

```bash
pip install hitl-cdp-browser-provider
```

## Use

```bash
# Point it at a CDP HTTP base -- it auto-picks the first "page" target
hitl-cdp-browser-provider --cdp-http http://127.0.0.1:9222 --bind 0.0.0.0:18234 --token "$(openssl rand -hex 16)"

# Or an exact page WebSocket URL if you already have one
hitl-cdp-browser-provider --cdp-url ws://127.0.0.1:9222/devtools/page/ABC123 --bind 0.0.0.0:18234 --token "$(openssl rand -hex 16)"
```

Then point your HITL Source's `browser_endpoint` (or any equivalent
"internal_url for browser tasks" field) at `<bind-host>:<bind-port>?token=<same-secret>`.

**⚠️ This bridge has no authentication of its own beyond `--token`.**
Without it, anyone who can reach `--bind`'s address and port can view and
remotely control the browser session -- no HITL token, no login, nothing.
`127.0.0.1` (the default) is safe on its own since only local processes
can reach it; the moment you bind wider (`0.0.0.0`, a LAN IP -- which the
typical deployment needs, since the bridge and HITL Broker usually run on
different hosts), **always pass `--token`**. The viewer page forwards its
own query string (including `?token=...`) into the WebSocket URL it opens,
so setting it once on the page's URL covers both the HTTP GET and the WS
connection.

### Finding a CDP endpoint

- **agent-browser**: `agent-browser get cdp-url --session <name>` gives a
  `ws://127.0.0.1:<port>/devtools/browser/<id>` browser-level URL — pass its
  `http://127.0.0.1:<port>` base as `--cdp-http` (screencast needs a page
  target, which `--cdp-http` auto-discovers via `/json/list`).
- **Chrome/Chromium directly**: launch with `--remote-debugging-port=9222`,
  use `--cdp-http http://127.0.0.1:9222`.
- **Playwright (Python)**: launch Chromium with
  `args=["--remote-debugging-port=9222"]`, then point `--cdp-http` at that
  port. (`browser.new_cdp_session(page)` also works if you'd rather wire
  the CDP session up yourself instead of using this CLI.)
- **Selenium (Python)**: Chrome launched with `--remote-debugging-port`
  works the same way; Selenium 4's own `driver.execute_cdp_cmd` isn't
  needed here since this bridge connects directly.

## Library use

```python
import asyncio
from hitl_cdp_browser_provider import serve

asyncio.run(serve({
    "cdp_http": "http://127.0.0.1:9222",
    "cdp_url": None,
    "bind": "0.0.0.0:18234",
    "quality": 60,
    "max_width": 1280,
    "max_height": 800,
    "token": "replace-with-a-real-secret",
}))
```

## Protocol

The viewer page renders binary WebSocket messages (raw JPEG screencast
frames) onto a `<canvas>` and sends back `{"t":"mouse",...}` /
`{"t":"key",...}` JSON messages (touch is mapped to the left mouse button),
which this bridge translates into `Input.dispatchMouseEvent` /
`Input.dispatchKeyEvent` CDP calls. Other text messages are control only:
`{"t":"status",...}` and a `{"t":"ping"}` keepalive every 20s (so reverse
proxies don't drop an idle session while the page isn't repainting).

Tuned for responsiveness over picture quality: a frame is only acknowledged
to Chrome (so the next one is produced) once the previous one has drained
from the socket, so latency can't pile up on a slow link.

**Stream profiles** (`profiles.json`): `outline` (shapes only, smoothest),
`smooth` (default), `balanced`, `sharp`. `--profile` sets the bridge default
(`--quality/--max-width/--max-height` override it); **each task picks its own**
with `?profile=<name>` in its internal_url, and can pin a page with
`?target=<page id>`. Adaptive presets stream a light profile while the human
operates and send a sharper still once input is idle: `adaptive` (smooth +
sharp) and `adaptive-lite` (outline + balanced, most fluid for sliders in
phone tests); override per task with `?motion=` / `?still=` / `?idle_ms=`.
See the human-checkpoint skill for when to use which.

**`GET /status?token=...`** returns the browser's `cdp_http`, its pages, the
default page and the profile table -- so automation can drive exactly the
page being streamed. The CDP target is re-resolved on every viewer
connection; a `{"t":"ping"}` keepalive is sent every 20s. The easiest way to
use all of this is `hitl_client.browser.BrowserHandoff`
(`pip install "hitl-client[browser]"`).

## Not CDP? Use noVNC instead

If your browser session isn't driven by anything CDP-capable (or you want a
screen-level rather than browser-API-level handoff — e.g. an arbitrary GUI
app in a container, not just a browser), see the
[noVNC recipe](https://github.com/aqiu9/HITL-Broker/blob/main/docs/browser-provider-novnc.md)
in the HITL-Broker repo instead — noVNC + websockify already implements the
same "HTTP page + WS" contract with zero custom *code*, at the cost of
needing an X server (Xvfb) + VNC server in the container (see that doc for
path-prefix caveats).

## License

MIT
