Skip to content

Commit 3d26f62

Browse files
authored
Merge pull request #13 from operasoftware/snapshot-optimizations
Optimize opera CLI snapshot command for agentic usage and add two verification benchmarks in python.
2 parents 14d735c + 485aa49 commit 3d26f62

44 files changed

Lines changed: 4163 additions & 108 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ on:
77
branches: [main]
88

99
jobs:
10+
lint-benchmark:
11+
runs-on: ubuntu-latest
12+
defaults:
13+
run:
14+
working-directory: benchmarks
15+
steps:
16+
- uses: actions/checkout@v4
17+
- uses: actions/setup-python@v5
18+
with:
19+
python-version: "3.11"
20+
- run: pip install -r requirements-dev.txt
21+
- run: make check
22+
1023
build-and-test:
1124
runs-on: ubuntu-latest
1225
steps:

CLAUDE.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Key files:
1515
| `src/bridge.ts``runBridge()` | Entry point for the bridge process |
1616
| `bin/opera-browser-cli-bridge.js` | Bridge binary entrypoint (calls `runBridge`) |
1717

18+
## Benchmarks
19+
20+
Token-cost and agentic-quality measurements live in `benchmarks/`. See `benchmarks/CLAUDE.md` for file roles and how to run them.
21+
1822
## Specs directory
1923

2024
Planned and in-progress fixes are documented as Markdown specs in `specs/`.
@@ -24,6 +28,34 @@ Always check there before starting implementation work.
2428
|---|---|
2529
| [`specs/fix-parallel-streaming-routing.md`](specs/fix-parallel-streaming-routing.md) | Planned — parallel chunk routing for concurrent Opera AI calls |
2630

31+
## Common issues
32+
33+
### Stale bridge process after update (`BRIDGE_NOT_READY` / "different server")
34+
35+
**Symptom:** `opera-browser-cli` commands fail with:
36+
```
37+
error: Port 9224 is in use by a different server (not opera-devtools-mcp).
38+
code: BRIDGE_NOT_READY
39+
```
40+
even though the bridge is running (`lsof -i :9224` shows a `node` process).
41+
42+
**Cause:** The bridge process was started before `dist/src/bridge.js` was rebuilt. The
43+
running process has old code in memory; its `/health` response is missing the
44+
`server: "opera-browser-cli"` field that `checkPortStatus` (`client.ts`) requires to
45+
recognise the bridge as healthy. Without that field the port is classified as a conflict.
46+
47+
**Fix:** Restart the bridge:
48+
```sh
49+
opera-browser-cli stop
50+
# next command auto-starts a fresh bridge with current code
51+
```
52+
53+
If `stop` does nothing (the bridge was started without a PID file, or the PID file was
54+
deleted), kill it by port instead:
55+
```sh
56+
lsof -ti :9224 | xargs kill
57+
```
58+
2759
## Architecture notes
2860

2961
### Bridge transport model

README.md

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ OPERA_CLI_BROWSER_URL=http://127.0.0.1:9222 opera-browser-cli open https://examp
112112

113113
```
114114
┌───────────────────────┐
115-
│ opera-browser-cli │ CLI — parse args, format output
115+
│ opera-browser-cli │ CLI — parse args, format output
116116
└──────────┬────────────┘
117117
│ HTTP (localhost:9225)
118118
@@ -122,7 +122,7 @@ OPERA_CLI_BROWSER_URL=http://127.0.0.1:9222 opera-browser-cli open https://examp
122122
│ stdio
123123
124124
┌───────────────────────┐
125-
│ opera-devtools-mcp │ Headless Chrome via DevTools Protocol
125+
│ opera-devtools-mcp │ Headless Chrome via DevTools Protocol
126126
└───────────────────────┘
127127
```
128128

@@ -136,7 +136,7 @@ OPERA_CLI_BROWSER_URL=http://127.0.0.1:9222 opera-browser-cli open https://examp
136136
### Navigation
137137

138138
| Command | Description |
139-
| ----------------- | -------------------------------------------- |
139+
|-------------------|----------------------------------------------|
140140
| `open <url>` | Navigate to URL and snapshot |
141141
| `snapshot` | Capture current page state |
142142
| `screenshot <p>` | Save a screenshot to a file |
@@ -156,7 +156,7 @@ opera-browser-cli eval "(() => { const rows = [...document.querySelectorAll('tr'
156156
### Interaction
157157

158158
| Command | Description |
159-
| -------------------------- | ------------------------------ |
159+
|----------------------------|--------------------------------|
160160
| `click @<uid>` | Click an element by ref |
161161
| `fill @<uid> <text>` | Fill a form field |
162162
| `type <text>` | Type text at current focus |
@@ -170,7 +170,7 @@ opera-browser-cli eval "(() => { const rows = [...document.querySelectorAll('tr'
170170
### Page Management
171171

172172
| Command | Description |
173-
| ----------------- | --------------------------- |
173+
|-------------------|-----------------------------|
174174
| `pages` | List all open tabs |
175175
| `newpage <url>` | Open a new tab |
176176
| `selectpage <id>` | Switch to a tab by ID |
@@ -180,13 +180,13 @@ opera-browser-cli eval "(() => { const rows = [...document.querySelectorAll('tr'
180180
### Emulation
181181

182182
| Command | Description |
183-
| --------- | ------------------------------- |
183+
|-----------|---------------------------------|
184184
| `emulate` | Emulate device/network/viewport |
185185

186186
### DevTools Debugging
187187

188188
| Command | Description |
189-
| ------------------ | ------------------------------ |
189+
|--------------------|--------------------------------|
190190
| `console` | List console messages |
191191
| `console-get <id>` | Get a specific console message |
192192
| `network` | List network requests |
@@ -195,7 +195,7 @@ opera-browser-cli eval "(() => { const rows = [...document.querySelectorAll('tr'
195195
### Performance
196196

197197
| Command | Description |
198-
| --------------------------- | ----------------------------- |
198+
|-----------------------------|-------------------------------|
199199
| `lighthouse` | Run a Lighthouse audit |
200200
| `perf-start` | Start a performance trace |
201201
| `perf-stop` | Stop the performance trace |
@@ -204,27 +204,27 @@ opera-browser-cli eval "(() => { const rows = [...document.querySelectorAll('tr'
204204

205205
### Opera AI
206206

207-
| Command | Description | Requires |
208-
| ------------------- | --------------------------------------------- | -------------- |
209-
| `chat <prompt>` | Send a chat message to Opera's built-in AI | Any Opera |
210-
| `invoke-do <prompt>`| Ask the AI to perform a complex browsing task | Opera Neon |
211-
| `make <prompt>` | Ask the AI to build a webpage or app | Opera Neon |
212-
| `research <prompt>` | Ask the AI to research a topic in depth | Opera Neon |
207+
| Command | Description | Requires |
208+
|----------------------|-----------------------------------------------|------------|
209+
| `chat <prompt>` | Send a chat message to Opera's built-in AI | Any Opera |
210+
| `invoke-do <prompt>` | Ask the AI to perform a complex browsing task | Opera Neon |
211+
| `make <prompt>` | Ask the AI to build a webpage or app | Opera Neon |
212+
| `research <prompt>` | Ask the AI to research a topic in depth | Opera Neon |
213213

214214
`research` accepts `--type local` (default), `--type one-minute`, or `--type deep`.
215215

216216
### Configuration
217217

218218
| Command | Description |
219-
| -------- | ------------------------------------------------ |
219+
|----------|--------------------------------------------------|
220220
| `setup` | Interactive first-time setup (browser path, etc) |
221221
| `doctor` | Check configuration and environment |
222222
| `logs` | Show bridge server logs |
223223

224224
### Bridge
225225

226226
| Command | Description |
227-
| ------- | ----------------------- |
227+
|---------|-------------------------|
228228
| `start` | Start the bridge server |
229229
| `stop` | Stop the bridge server |
230230

@@ -235,7 +235,7 @@ session is active or the no-session status/help block when one is not.
235235
### Flags
236236

237237
| Flag | Description |
238-
| --------------------------- | ------------------------------------------- |
238+
|-----------------------------|---------------------------------------------|
239239
| `--help` | Show usage information |
240240
| `-v`, `-V`, `--version` | Show the installed CLI version |
241241
| `--full` | Show complete output without truncation |
@@ -263,21 +263,21 @@ session is active or the no-session status/help block when one is not.
263263

264264
## Configuration
265265

266-
| Variable | Default | Purpose |
267-
| --- | --- | --- |
268-
| `OPERA_CLI_PORT` | `9225` | Bridge server port |
269-
| `OPERA_CLI_MCP_BIN` | _(bundled `opera-devtools-mcp`)_ | Override the MCP server binary |
270-
| `OPERA_CLI_EXECUTABLE_PATH` | _(system Chrome)_ | Custom browser binary |
271-
| `OPERA_CLI_BROWSER_URL` || Connect to an existing browser instance instead of launching one |
272-
| `OPERA_CLI_USER_DATA_DIR` || Persistent Chrome profile directory (skips isolated mode) |
273-
| `OPERA_CLI_HEADED` || Set to `1` to run in headed (visible) mode |
274-
| `OPERA_CLI_CHROME_ARGS` || Extra Chrome flags, space-separated |
275-
| `OPERA_CLI_DISABLE_HOOKS` || Set to `1` to skip auto-installing session hooks |
266+
| Variable | Default | Purpose |
267+
|-----------------------------|----------------------------------|------------------------------------------------------------------|
268+
| `OPERA_CLI_PORT` | `9225` | Bridge server port |
269+
| `OPERA_CLI_MCP_BIN` | _(bundled `opera-devtools-mcp`)_ | Override the MCP server binary |
270+
| `OPERA_CLI_EXECUTABLE_PATH` | _(system Chrome)_ | Custom browser binary |
271+
| `OPERA_CLI_BROWSER_URL` | | Connect to an existing browser instance instead of launching one |
272+
| `OPERA_CLI_USER_DATA_DIR` | | Persistent Chrome profile directory (skips isolated mode) |
273+
| `OPERA_CLI_HEADED` | | Set to `1` to run in headed (visible) mode |
274+
| `OPERA_CLI_CHROME_ARGS` | | Extra Chrome flags, space-separated |
275+
| `OPERA_CLI_DISABLE_HOOKS` | | Set to `1` to skip auto-installing session hooks |
276276

277277
State is stored in `~/.opera-browser-cli/`:
278278

279279
| File | Purpose |
280-
| ------------ | ---------------------------------- |
280+
|--------------|------------------------------------|
281281
| `bridge.pid` | PID and port of the running bridge |
282282

283283
### Session Hooks
@@ -328,6 +328,45 @@ export OPERA_CLI_MCP_BIN=opera-devtools-mcp
328328
export OPERA_CLI_HEADED=1
329329
```
330330

331+
## Benchmarks
332+
333+
### Page Snapshot
334+
335+
Runs snapshot command on 50 static pages (Wikipedia, GitHub, MDN, Python docs, RFC Editor) and counts output tokens via tiktoken. No LLM involved — purely mechanical measurement.
336+
337+
**Results (50 runs each):**
338+
339+
| Condition | Avg tokens | Median tokens | p95 tokens |
340+
|-----------------|------------|---------------|------------|
341+
| `opera-compact` | 60.6k | 24.3k | 256.1k |
342+
| `mcp-raw` | 94.7k | 45.0k | 391.3k |
343+
| `opera-raw` | 94.9k | 45.1k | 381.4k |
344+
| `axi` | 98.5k | 46.6k | 396.9k |
345+
346+
`--full` variants (no char limit) are also measured; see the [detailed README](page-token-benchmark/README.md) and [results report](page-token-benchmark/results/report.md).
347+
348+
---
349+
350+
### Agentic Use
351+
352+
An LLM agent completes 7 browser tasks (adapted from the [axi bench-browser benchmark](https://github.com/kunchenguid/axi/tree/main/bench-browser)) across 4 conditions. Each run is graded pass/fail by an LLM judge. Captures input tokens, snapshot size, wall time, and tool call count.
353+
The agent was selecting each tool with or without `--full` flag, depending on the context.
354+
355+
**Results (35 runs each, 5 repeats × 7 tasks):**
356+
357+
| Condition | Pass [%] | Avg input length [tokens] | Avg snapshot length [chars] | Avg task time [seconds] | Avg tool calls |
358+
|---------------|----------|---------------------------|-----------------------------|-------------------------|----------------|
359+
| opera-compact | 100% | 36.3k | 83.1k | 6.8 | 1.4 |
360+
| opera-raw | 100% | 107.5k | 198.1k | 8.5 | 1.6 |
361+
| axi | 100% | 102.2k | 203.9k | 9.8 | 1.5 |
362+
| mcp-raw | 100% | 179.2k | 218.7k | 9.4 | 2.1 |
363+
364+
> opera-compact saves **80%** total tokens vs mcp-raw baseline.
365+
366+
See the [detailed README](snapshot-agentic-use/README.md) and [results report](snapshot-agentic-use/results/v6/report.md).
367+
368+
---
369+
331370
## Development
332371

333372
```sh

SKILL.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Browser automation and web interaction using the opera-browser-cli
55

66
# Skill: opera-browser-cli Browser Automation
77

8-
`opera-browser-cli` controls a Opera browser browser session.
8+
`opera-browser-cli` controls an Opera browser session.
99

1010
- **Standard commands** (`open`, `click`, `fill`, `screenshot`, etc.) — work with any Opera browser session.
1111
- **`chat`** — available on any Opera browser.
@@ -17,4 +17,30 @@ Run `opera-browser-cli --help` for the full command list, or `opera-browser-cli
1717
opera-browser-cli open https://example.com # start here — navigate and snapshot the page
1818
```
1919

20-
If a user hits `Opera: user is not signed in` on an AI command, suggest they sign in to their Opera account. `invoke-do`, `make`, and `research` require Opera Neon with an active sign-in. Run `opera-browser-cli setup` or `opera-browser-cli doctor` to configure or diagnose.
20+
## Snapshot format
21+
22+
By default snapshots are **compact**: role names are shortened, refs use `@PAGE.ELEM` form (e.g. `@2.4`), headings become markdown, and redundant ARIA attributes are stripped. Pass `--raw` to any snapshot-returning command to get the unprocessed MCP output instead.
23+
24+
Repeated or very long URLs in compact output are replaced with `$uN` tokens. A `urls:` trailer at the end of the snapshot lists what each token resolves to.
25+
26+
```
27+
@2.4 link "Download" url=$u1
28+
...
29+
urls:
30+
$u1 /downloads/installer-v3.2.1-x86_64.tar.gz
31+
```
32+
33+
Use `opera-browser-cli url $uN` or `opera-browser-cli url @ref` to resolve a token or element ref to its full URL without taking a new snapshot.
34+
35+
## Flags available on snapshot-returning commands
36+
37+
| Flag | Effect |
38+
|----------|--------------------------------------------------------------|
39+
| `--full` | Show complete snapshot without truncation |
40+
| `--raw` | Unprocessed MCP output (disables compact format and URL LUT) |
41+
42+
Commands that accept these flags: `open`, `snapshot`, `click`, `fill`, `type`, `press`, `scroll`, `back`, `hover`, `drag`, `fillform`, `upload`, `newpage`, `selectpage`.
43+
44+
## Sign-in errors
45+
46+
If you hit `Opera: user is not signed in` on an AI command, suggest signing in to their Opera account. Run `opera-browser-cli setup` or `opera-browser-cli doctor` to configure or diagnose.

benchmarks/.flake8

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[flake8]
2+
max-line-length = 120
3+
# E203: whitespace before ':' — conflicts with black's slice formatting
4+
# W503: line break before binary operator — conflicts with black
5+
extend-ignore = E203, W503

0 commit comments

Comments
 (0)