@divebell/agent-browser 0.33.1-divebell.1 → 0.33.2-divebell.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1043,7 +1043,7 @@ Create an `agent-browser.json` file to set persistent defaults instead of repeat
1043
1043
 
1044
1044
  **Locations (lowest to highest priority):**
1045
1045
 
1046
- 1. `~/.agent-browser/config.json`: user-level defaults
1046
+ 1. `~/.agent-browser/config.json`: user-level defaults (or `$AGENT_BROWSER_HOME/config.json`)
1047
1047
  2. `./agent-browser.json`: project-level overrides (in working directory)
1048
1048
  3. `AGENT_BROWSER_*` environment variables override config file values
1049
1049
  4. CLI flags override everything
@@ -1075,6 +1075,15 @@ agent-browser --config ./ci-config.json open example.com
1075
1075
  AGENT_BROWSER_CONFIG=./ci-config.json agent-browser open example.com
1076
1076
  ```
1077
1077
 
1078
+ For sandboxed agents whose home directory is read-only, set
1079
+ `AGENT_BROWSER_HOME` to a writable directory such as `/tmp/agent-browser`.
1080
+ This relocates agent-browser-owned configuration, session state, browser
1081
+ downloads, temporary artifacts, and daemon files without changing `HOME` for
1082
+ other tools. When no override is set and `~/.agent-browser` is not writable,
1083
+ agent-browser automatically uses a short per-user directory under `/tmp` on
1084
+ Unix. Sandboxes that prohibit local sockets or browser processes entirely
1085
+ still require a browser running outside the sandbox and a reachable CDP URL.
1086
+
1078
1087
  All options from the table above can be set in the config file using camelCase keys (e.g., `--executable-path` becomes `"executablePath"`, `--proxy-bypass` becomes `"proxyBypass"`). Plugins are configured with the `"plugins"` array shown above. Unknown keys are ignored for forward compatibility.
1079
1088
 
1080
1089
  A [JSON Schema](agent-browser.schema.json) is available for IDE autocomplete and validation. Add a `$schema` key to your config file to enable it:
@@ -1448,6 +1457,24 @@ To bind to a specific port, set `AGENT_BROWSER_STREAM_PORT`:
1448
1457
  AGENT_BROWSER_STREAM_PORT=9223 agent-browser open example.com
1449
1458
  ```
1450
1459
 
1460
+ Frame encoding is daemon-wide:
1461
+
1462
+ | Variable | Default | Description |
1463
+ |---|---|---|
1464
+ | `AGENT_BROWSER_STREAM_QUALITY` | `80` | JPEG quality, 0 to 100 |
1465
+ | `AGENT_BROWSER_STREAM_MAX_WIDTH` | the viewport | Caps frame width in pixels |
1466
+ | `AGENT_BROWSER_STREAM_MAX_HEIGHT` | the viewport | Caps frame height in pixels |
1467
+
1468
+ Width and height cap the encoded frame and leave the page size alone, so a portrait or HiDPI viewport keeps its resolution unless you cap it. The live stream requests jpeg. An explicit `screencast_start` reconfigures the same screencast, so a client can see the format change mid-stream. On a busy page at 1280x720, quality 80 costs about 54 KB per frame, quality 20 about 25 KB, and quality 20 at 640x360 about 9 KB.
1469
+
1470
+ ```bash
1471
+ # Cheaper frames for a constrained link
1472
+ AGENT_BROWSER_STREAM_QUALITY=20 \
1473
+ AGENT_BROWSER_STREAM_MAX_WIDTH=640 \
1474
+ AGENT_BROWSER_STREAM_MAX_HEIGHT=360 \
1475
+ agent-browser open example.com
1476
+ ```
1477
+
1451
1478
  You can also manage streaming at runtime with `stream enable`, `stream disable`, and `stream status`:
1452
1479
 
1453
1480
  ```bash
@@ -1466,6 +1493,7 @@ Connect to `ws://localhost:9223` to receive frames and send input:
1466
1493
  ```json
1467
1494
  {
1468
1495
  "type": "frame",
1496
+ "seq": 41,
1469
1497
  "data": "<base64-encoded-jpeg>",
1470
1498
  "metadata": {
1471
1499
  "deviceWidth": 1280,
@@ -1473,11 +1501,14 @@ Connect to `ws://localhost:9223` to receive frames and send input:
1473
1501
  "pageScaleFactor": 1,
1474
1502
  "offsetTop": 0,
1475
1503
  "scrollOffsetX": 0,
1476
- "scrollOffsetY": 0
1504
+ "scrollOffsetY": 0,
1505
+ "timestamp": 1785038682238
1477
1506
  }
1478
1507
  }
1479
1508
  ```
1480
1509
 
1510
+ `seq` is a monotonic frame id, echoed back in an `ack` message under ack pacing. `metadata.timestamp` is the capture time in epoch milliseconds, so a client can tell how old a frame is by the time it draws it.
1511
+
1481
1512
  **Send mouse events:**
1482
1513
 
1483
1514
  ```json
@@ -1512,6 +1543,17 @@ Connect to `ws://localhost:9223` to receive frames and send input:
1512
1543
  }
1513
1544
  ```
1514
1545
 
1546
+ **Cap the frame rate (per client):**
1547
+
1548
+ ```json
1549
+ {
1550
+ "type": "config",
1551
+ "maxFps": 10
1552
+ }
1553
+ ```
1554
+
1555
+ Frames are delivered latest-first: the server picks the newest frame at send time, so frames produced while an earlier one is still being written are skipped rather than queued. `maxFps` (1 to 120, `0` = uncapped) limits delivery for that client only. A client that sends `{"type":"config","pacing":"ack"}` receives one frame at a time and acknowledges it with `{"type":"ack","seq":N}`, so nothing stale reaches the socket even if that client stalls; in the default push pacing, frames already handed to the transport are still delivered in order. Both settings can also be declared on the URL (`ws://127.0.0.1:<port>/?pacing=ack&maxFps=10`), which is the only way to cover the connection's opening frame. Input events are read on a dedicated task per connection, so clicks and keystrokes dispatch immediately even while frames are mid-write to a slow client. They are sent to the browser without waiting for its reply, so a click stays responsive behind a burst of mouse moves, and ordering is preserved.
1556
+
1515
1557
  ## Architecture
1516
1558
 
1517
1559
  agent-browser uses a client-daemon architecture:
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divebell/agent-browser",
3
- "version": "0.33.1-divebell.1",
3
+ "version": "0.33.2-divebell.1",
4
4
  "description": "Browser automation CLI for AI agents with Divebell memory diagnostics",
5
5
  "type": "module",
6
6
  "engines": {
@@ -513,6 +513,7 @@ That pulls in:
513
513
  - `references/session-management.md` — persistence, multi-session workflows
514
514
  - `references/profiling.md` — Chrome DevTools tracing and profiling
515
515
  - `references/video-recording.md` — video capture options
516
+ - `references/streaming.md` covers live viewport streaming, remote input, per-client frame rate, and the encoding vars that set bandwidth cost
516
517
  - `references/proxy-support.md` — proxy configuration
517
518
  - `references/webgpu.md` — screenshots/video of WebGPU pages (three.js, Babylon.js), Linux/CI setup
518
519
  - `templates/*` — starter shell scripts for auth, capture, form automation
@@ -328,6 +328,17 @@ agent-browser state save auth.json # Save cookies, storage, auth state
328
328
  agent-browser state load auth.json # Restore saved state
329
329
  ```
330
330
 
331
+ ## Live Streaming
332
+
333
+ ```bash
334
+ agent-browser stream status --json # Enabled state, port, client count
335
+ agent-browser stream enable # Start the WebSocket stream server
336
+ agent-browser stream enable --port 9223
337
+ agent-browser stream disable # Stop it
338
+ ```
339
+
340
+ Clients connect to `ws://127.0.0.1:<port>` and receive `frame`, `status`, `tabs`, `url`, and `console` messages. They send `input_mouse`, `input_keyboard`, and `input_touch` to drive the page, `{"type":"config","maxFps":N}` (1 to 120, `0` = uncapped) to cap their own frame rate, and `{"type":"config","pacing":"ack"}` to receive one frame at a time, acknowledged with `{"type":"ack","seq":N}`. Both settings can be declared on the URL instead (`ws://127.0.0.1:<port>/?pacing=ack&maxFps=10`). See [streaming.md](streaming.md).
341
+
331
342
  ## MCP Server
332
343
 
333
344
  ```bash
@@ -0,0 +1,104 @@
1
+ # Live Streaming
2
+
3
+ Stream a session's viewport over WebSocket and drive it with remote input. This is what a remote preview or embedded dashboard connects to: the browser runs wherever the daemon runs (a sandbox, a container, a CI box), and the client renders frames and sends clicks back.
4
+
5
+ **Related**: [commands.md](commands.md) for full command reference, [SKILL.md](../SKILL.md) for quick start.
6
+
7
+ ## Contents
8
+
9
+ - [Enabling the stream](#enabling-the-stream)
10
+ - [Connecting](#connecting)
11
+ - [Messages from the server](#messages-from-the-server)
12
+ - [Messages from the client](#messages-from-the-client)
13
+ - [Frame rate and staleness](#frame-rate-and-staleness)
14
+ - [Limitations](#limitations)
15
+
16
+ ## Enabling the stream
17
+
18
+ Streaming is always available; the server binds an OS-assigned localhost port unless told otherwise.
19
+
20
+ ```bash
21
+ agent-browser stream status --json # Report enabled state, port, client count
22
+ agent-browser stream enable # Create the server (--port to pin one)
23
+ agent-browser stream disable # Tear it down
24
+ ```
25
+
26
+ `AGENT_BROWSER_STREAM_PORT` pins the port for the whole daemon instead of passing `--port`.
27
+
28
+ Frame encoding is daemon-wide, read once at startup:
29
+
30
+ | Variable | Default | Notes |
31
+ |---|---|---|
32
+ | `AGENT_BROWSER_STREAM_QUALITY` | `80` | 0 to 100, clamped |
33
+ | `AGENT_BROWSER_STREAM_MAX_WIDTH` | the viewport | caps the frame, does not resize the page |
34
+ | `AGENT_BROWSER_STREAM_MAX_HEIGHT` | the viewport | same |
35
+
36
+ The live stream requests jpeg, since a `frame` message carries no format field. An explicit `screencast_start` reconfigures the same underlying screencast, so a client can still see the format change mid-stream; sniff the bytes rather than assuming. Measured on a busy page at 1280x720: quality 80 gives ~54 KB per frame, quality 20 gives ~25 KB, and quality 20 at 640x360 gives ~9 KB. An unusable value leaves the default.
37
+
38
+ Read the port from `stream status --json` rather than assuming one; the OS-assigned default changes per daemon.
39
+
40
+ ## Connecting
41
+
42
+ Connect a WebSocket client to `ws://127.0.0.1:<port>`. Frame delivery starts automatically once a client attaches, so there is no subscribe message. Browser clients must load from `localhost`, `127.0.0.1`, `::1` or `file://`. Any other origin gets a 403 on the upgrade and needs a proxy.
43
+
44
+ ## Messages from the server
45
+
46
+ Every message is JSON text with a `type` field.
47
+
48
+ - `frame`: a viewport image plus its metadata. Delivered latest-first (see below).
49
+
50
+ ```json
51
+ {
52
+ "type": "frame",
53
+ "seq": 41,
54
+ "data": "<base64-encoded-jpeg>",
55
+ "metadata": {
56
+ "deviceWidth": 1280, "deviceHeight": 720, "pageScaleFactor": 1,
57
+ "offsetTop": 0, "scrollOffsetX": 0, "scrollOffsetY": 0,
58
+ "timestamp": 1785038682238
59
+ }
60
+ }
61
+ ```
62
+
63
+ `seq` is a monotonic frame id, echoed back under ack pacing and stable across browser relaunches. `metadata.timestamp` is the capture time in epoch milliseconds, so `Date.now() - timestamp` is the age of the frame being drawn. The other message types:
64
+
65
+ - `status`: connection state, screencasting flag, viewport size, engine, recording flag. Sent once on connect and again on change.
66
+ - `tabs`: the current tab list, sent on connect when tabs are known and on change.
67
+ - `url`, `console`: navigation and console events.
68
+
69
+ Status, tabs, url, and console travel on an ordered channel: they are delivered in order and are never replaced by a newer message the way frames are. They are not unconditionally durable. A client that falls far enough behind can lag out of that channel and lose messages it never saw, so treat console output as a live feed, not an audit log.
70
+
71
+ ## Messages from the client
72
+
73
+ ```json
74
+ {"type": "input_mouse", "eventType": "mousePressed", "x": 40, "y": 40, "button": "left", "clickCount": 1}
75
+ {"type": "input_keyboard", "eventType": "keyDown", "key": "a", "text": "a"}
76
+ {"type": "input_touch", "eventType": "touchStart", "touchPoints": []}
77
+ {"type": "config", "maxFps": 10}
78
+ {"type": "config", "pacing": "ack"}
79
+ {"type": "ack", "seq": 41}
80
+ ```
81
+
82
+ Input dispatches to the browser on a task of its own, separate from frame delivery, so a click is not queued behind a frame write. Events are sent to the browser without waiting for its reply, so a click stays responsive behind a burst of mouse moves. Ordering is preserved: press never overtakes move. Mouse, keyboard, and touch input also reset the daemon idle timer, so an actively driven preview is not shut down by the idle timeout.
83
+
84
+ `config` sets a per-client frame cap: 1 to 120, or `0` for uncapped (the default). It takes effect immediately, including when it loosens the cap. Each client's cap is its own; other connected clients are unaffected. A value above 120 is clamped to 120; a negative or non-numeric value is ignored, leaving the current cap in place. Neither rejects the connection.
85
+
86
+ Both settings can also be declared on the URL, which is the only way to have them cover the connection's opening frame: `ws://127.0.0.1:<port>/?pacing=ack&maxFps=10`. A `config` message sent after connecting still wins.
87
+
88
+ ## Frame rate and staleness
89
+
90
+ The server holds only the newest frame per client and reads it at send time. A frame produced while an earlier one is still being written is skipped, not queued, so the application never builds a backlog.
91
+
92
+ Push pacing (the default) stops there, and the transport underneath is still ordered: frames already accepted by the socket are delivered in order, so a client that stalls drains whatever the kernel buffered before the writer blocked.
93
+
94
+ Ack pacing closes that gap. Send `{"type":"config","pacing":"ack"}` and the server keeps at most one frame in flight, waiting for `{"type":"ack","seq":N}` before sending the next. Every frame carries a monotonic `seq`; echo the one you finished rendering. Frames produced while an ack is outstanding replace each other and never reach the socket, so a client that stalls for ten seconds and resumes gets the current page, not ten seconds of history.
95
+
96
+ Under ack pacing one frame is in flight at a time, so the rate is one frame per transfer plus one acknowledgement round trip. Both the link's bandwidth and its latency bound it, and a link whose bandwidth-delay product exceeds a single frame goes underused. Ack pacing bounds one hop. With a proxy in the path, forward the renderer's acks; acks generated on receipt leave frames queued on the far side. Acks are cumulative, so acknowledging a newer id covers any older one. A client that opts in and then stops acking simply stops receiving frames; status, tabs, url, and console keep flowing.
97
+
98
+ The two settings compose: `pacing` bounds how much is in flight, `maxFps` bounds the rate. A constrained preview usually wants both.
99
+
100
+ ## Limitations
101
+
102
+ - Localhost only. Exposing the stream beyond the machine is the embedder's job (tunnel, proxy, or port forward), and the origin allowlist applies to browser clients.
103
+ - Frames are images, not a video codec. Bandwidth scales with viewport size and page activity; cap the rate for constrained links.
104
+ - In push pacing the server cannot tell a slow renderer from a fast one beyond transport backpressure. Use ack pacing when that distinction matters.