Skip to content

Commit 86af600

Browse files
mabry1985claude
andauthored
feat(panel): fully interactive CDP-screencast viewport — full switchover (v0.6.0) (#14)
* feat(panel): fully interactive CDP-screencast viewport — full switchover (v0.6.0) Replace the entire dashboard-embed / screenshot panel approach with a single fully drivable browser viewport. A live CDP screencast (event-driven JPEG frames) is painted on a <canvas> and operator mouse/keyboard/scroll are forwarded back via Input.dispatch*, all over a gated same-origin WebSocket — so it works on the host AND a remote fleet member, and you can drive the page alongside the agent. New: - browser_stream.py — a second-CDP-client bridge (Page.startScreencast out, input in) + single-use ticket auth. resolve_page_target/pick_page_target/input_to_cdp are pure + host-free-tested. - browser_panel.py — the interactive canvas page + gated POST /stream-ticket and the self-gated WS /stream. WS auth: the host's operator-bearer middleware is HTTP-only and skips WS handshakes, so the panel mints a ticket over the gated HTTP route and presents it on the WS URL; the handler validates + burns it. Full switchover (no backward compatibility): - Removed panel_mode (interactive is the only panel), the screenshot `minimal` mode and /shot, and the `full` dashboard-embed page. - Removed the browser_dashboard tool (16 tools now), lifecycle.py (dashboard daemon boot/shutdown surface), and the /dashboard control routes. - Removed config: panel_mode, dashboard_port, manage_dashboard. Validated end-to-end against the real agent-browser binary through the production routes (bad ticket refused, ticket minted, real frame streamed, keys + click land). 34 host-free tests pass; ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(panel): Start button + configurable homepage (home_url) The empty viewport was a dead end. Now, when no page is open, the panel shows a Start button, and a new `home_url` config sets the page it opens to: - home_url set → the panel auto-opens it once; the button reads "Open <host>" - home_url blank → the button opens about:blank (no auto-open) home_url is injected as a JS string literal via json.dumps + `<`→< so a value containing `</script>` can't break out of the inline script. Editable in Settings ▸ Plugins. 35 tests pass; ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 1bfd7f9 commit 86af600

13 files changed

Lines changed: 731 additions & 479 deletions

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,31 @@
11
# Changelog
22

3+
## v0.6.0
4+
- **The Browser panel is now a fully interactive, drivable viewport — and it is the ONLY mode.**
5+
A live **CDP screencast** — event-driven JPEG frames (not a screenshot poll) painted on a
6+
`<canvas>` — forwards your **mouse, keyboard, and scroll** back into the page via
7+
`Input.dispatch*`. You can click, type, and scroll the real browser from the console, right
8+
alongside the agent. Because every byte is a **gated same-origin WebSocket**, it works on the
9+
host **and** a remote fleet member.
10+
- **How it works.** A second CDP client attaches to the same Chrome agent-browser drives (via
11+
`agent-browser get cdp-url`); `browser_stream.py` bridges `Page.startScreencast` ⇆ the panel.
12+
The nav toolbar (url / back / forward / reload) reuses the gated HTTP `/nav` route.
13+
- **Start button + configurable homepage.** When no page is open, the panel shows a **Start
14+
button** instead of a dead end. A new `home_url` config sets the page it opens to: when set,
15+
the panel **auto-opens** it (and the button reads “Open <host>”); blank → the button opens
16+
`about:blank`. Editable in Settings ▸ Plugins.
17+
- **WebSocket auth.** The host's operator-bearer gate is HTTP-only and does **not** cover WS
18+
handshakes, so the stream self-gates: the panel mints a **single-use ticket** from the gated
19+
`POST /stream-ticket` (bearer-checked) and presents it on the WS URL; the handler validates and
20+
burns it. Safe in gated deployments, transparent in open ones.
21+
- **Full switchover — the old dashboard-embed approach is removed** (no backward compatibility):
22+
- `panel_mode` is gone (there is one panel now); so is the screenshot `minimal` mode and its
23+
`/shot` route, and the `full` dashboard-embed page.
24+
- The **`browser_dashboard` tool is removed** (16 tools now) along with the boot/shutdown
25+
**dashboard lifecycle** (`lifecycle.py`) and the panel's `/dashboard` control routes.
26+
- Removed config: `panel_mode`, `dashboard_port`, `manage_dashboard`. The interactive panel
27+
talks CDP directly and never needs agent-browser's separate dashboard daemon.
28+
329
## v0.5.1
430
- **Full mode: an "Open ↗" button** to pop the dashboard out into a full browser tab, alongside the
531
inline embed. Shown whenever the dashboard is on this machine (loopback host) — including over an

README.md

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,24 @@ point.
3333
`browser_snapshot`, `browser_click`, `browser_fill`, `browser_type`,
3434
`browser_get_text`/`get_html`/`get_value`, `browser_press`, `browser_hover`,
3535
`browser_eval`, `browser_screenshot`, `browser_back`/`forward`/`reload`,
36-
`browser_close`, `browser_dashboard`.
36+
`browser_close`.
3737
- **Skill** — a discovery skill that defers to the CLI's always-current workflow
3838
content (`agent-browser skills get core`), so instructions never go stale.
3939
- **Workflows** — declarative browser recipes (browse-and-extract, fill-a-form, …).
40-
- **Browser panel** — a console view (ADR 0026) for watching/driving the browser. Two
41-
modes, set by `panel_mode`:
42-
- **`full` (default)****embeds agent-browser's dashboard inline** (viewport + activity/
43-
console/network feeds) at its own **local origin** (`http://<host>:<port>/`). Best for a
44-
**local setup** (console + agent-browser on one machine). Because the dashboard is a
45-
Next.js app with root-absolute assets (no base-path), it only loads at its own origin —
46-
so when the console is opened **remotely** (a fleet member, a non-loopback host, or over
47-
https) its `localhost` isn't reachable from your browser, and the panel shows a **clear
48-
error** (pointing you at `minimal`) rather than a blank frame.
49-
- **`minimal`** — a **live screenshot** of the viewport + a nav toolbar, all through the
50-
**gated same-origin routes**. Works everywhere (host and member), no dashboard daemon
51-
needed — use it for a remote/member agent.
52-
53-
Either mode can **start the dashboard from the panel** (no terminal) — the Start/Stop
54-
control hits the gated `POST /api/plugins/agent_browser/dashboard`.
40+
- **Browser panel** — a console view (ADR 0026) that is a **fully drivable viewport**. A live
41+
**CDP screencast** (event-driven JPEG frames, not a screenshot poll) is painted on a
42+
`<canvas>`, and your **mouse / keyboard / scroll** are forwarded back into the page via
43+
`Input.dispatch*` — so you can click, type, and scroll the real browser from the console,
44+
alongside the agent. Everything rides a **gated same-origin WebSocket**, so it works on the
45+
**host and a remote fleet member** alike. A second CDP client attaches to the same Chrome
46+
agent-browser drives (`agent-browser get cdp-url`); `browser_stream.py` does the bridging.
47+
48+
When no page is open the panel shows a **Start button** (not a dead end). Set `home_url` to a
49+
page and the panel **auto-opens** it — a homepage — otherwise Start opens `about:blank`.
50+
51+
**WebSocket auth:** the host's operator-bearer gate is HTTP-only and doesn't cover WS
52+
handshakes, so the stream self-gates — the panel mints a **single-use ticket** from the gated
53+
`POST /api/plugins/agent_browser/stream-ticket` and presents it on the WS URL.
5554

5655
## Requirements
5756

@@ -75,20 +74,19 @@ plugins:
7574

7675
agent_browser:
7776
binary: agent-browser
78-
dashboard_port: 4848
7977
```
8078
8179
## Layout
8280
8381
| File | What |
8482
|---|---|
8583
| `tools.py` | the browser tools — subprocess wrappers over the `agent-browser` CLI |
86-
| `browser_panel.py` | the Browser panel — `minimal` (live viewport + nav + dashboard control) and `full` (a launcher for the dashboard) |
87-
| `lifecycle.py` | the dashboard daemon surface — start on boot, stop on shutdown (ADR 0018) |
84+
| `browser_panel.py` | the Browser panel page + routes — the interactive canvas + the gated nav / stream-ticket / WS-stream routes |
85+
| `browser_stream.py` | the CDP bridge — screencast frames out, input in; the WS ticket auth |
8886
| `skills/` | the discovery skill (defers to `agent-browser skills get core`) |
8987
| `workflows/` | declarative browser recipes |
9088
| `tests/` | the host-free pytest suite (subprocess mocked — no binary needed) |
91-
| `__init__.py` | `register()` — wires tools + panel + lifecycle; skills/workflows auto-discovered |
89+
| `__init__.py` | `register()` — wires tools + the interactive panel; skills/workflows auto-discovered |
9290

9391
The operator knobs (panel mode, headed, allowed domains, profile, device, …) are editable in
9492
**Settings ▸ Plugins ▸ Agent Browser**, or under `agent_browser:` in `langgraph-config.yaml`.

__init__.py

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
33
Composition over construction: this plugin is a thin shell over the `agent-browser`
44
CLI/daemon. It contributes the browser **tools** (subprocess wrappers), a discovery
5-
**skill** + browser **workflows** (auto-discovered from skills/ and workflows/), and a
6-
**Browser panel** console view that embeds agent-browser's own live dashboard — it
7-
does NOT reimplement browser automation or a renderer.
5+
**skill** + browser **workflows** (auto-discovered from skills/ and workflows/), and an
6+
interactive **Browser panel** console view — a live, drivable CDP-screencast viewport
7+
(browser_stream bridges Chrome's CDP to a canvas over a gated WebSocket). It does NOT
8+
reimplement browser automation or a renderer.
89
910
Ships DISABLED. Enable with `plugins: { enabled: [agent_browser] }` and put the
1011
`agent-browser` binary on PATH (`npm i -g agent-browser && agent-browser install`).
@@ -28,12 +29,13 @@ def register(registry) -> None:
2829
except Exception: # noqa: BLE001 — tools are the foundation; log loudly if they fail
2930
log.exception("[agent_browser] registering browser tools failed")
3031

31-
# Browser panel console view (embeds agent-browser's dashboard). Built out by the
32-
# board; register it best-effort so the foundation works before the view lands.
33-
# TWO routers at DISTINCT prefixes: the PAGE (+ the iframe-loaded /panel/dash
34-
# proxy, which can't carry a bearer) stays on the public /plugins/agent_browser;
35-
# the shot/nav DATA routes mount under /api/plugins/agent_browser so they
36-
# inherit the operator bearer gate (plugin-view rule 2).
32+
# Interactive Browser panel console view. Register it best-effort so the tools still
33+
# serve if the panel can't import. TWO routers at DISTINCT prefixes: the PAGE stays on
34+
# the public /plugins/agent_browser (an iframe page-load can't carry a bearer); the
35+
# DATA routes (the nav toolbar, the stream ticket + the /stream WS) mount under
36+
# /api/plugins/agent_browser so the HTTP ones inherit the operator bearer gate
37+
# (plugin-view rule 2). The WS gates itself with a single-use ticket — the host's auth
38+
# middleware doesn't cover WS handshakes.
3739
try:
3840
from .browser_panel import build_panel_data_router, build_panel_router
3941
registry.register_router(build_panel_router(cfg))
@@ -43,16 +45,5 @@ def register(registry) -> None:
4345
except Exception: # noqa: BLE001
4446
log.exception("[agent_browser] mounting browser panel failed")
4547

46-
# Lifecycle (ADR 0018): on shutdown, stop the dashboard daemon we manage so it
47-
# doesn't outlive the server (dashboard-only; the session is left alone).
48-
try:
49-
from .lifecycle import make_dashboard_surface
50-
start, stop = make_dashboard_surface(cfg)
51-
registry.register_surface(start, stop=stop, name="agent-browser-dashboard")
52-
except Exception: # noqa: BLE001 — lifecycle is best-effort; tools/panel still serve
53-
log.exception("[agent_browser] registering lifecycle surface failed")
54-
5548
# skills/ and workflows/ are auto-discovered (ADR 0027) — no register call.
56-
log.info("[agent_browser] registered browser tools (binary=%s, dashboard:%s, manage=%s)",
57-
cfg.get("binary", "agent-browser"), cfg.get("dashboard_port", 4848),
58-
cfg.get("manage_dashboard", True))
49+
log.info("[agent_browser] registered browser tools (binary=%s)", cfg.get("binary", "agent-browser"))

0 commit comments

Comments
 (0)