opera-browser-cli 0.1.34 → 0.1.36

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.
@@ -1,29 +1,311 @@
1
- /** Count interactive refs (uid=...) in snapshot text. */
1
+ /** Convert a canonical MCP ref ("2_4") to display form ("2.4"). */
2
+ export function refToDisplay(mcpRef) {
3
+ return mcpRef.replace(/_/g, ".");
4
+ }
5
+ /** Convert any ref form — "@2.4", "@2_4", "2.4", "2_4" — to MCP wire form "2_4". */
6
+ export function refToMcp(ref) {
7
+ return ref.replace(/^@/, "").replace(/\./g, "_");
8
+ }
9
+ /** Count interactive refs in snapshot text (accepts both uid= and compact @X.Y form). */
2
10
  export function countRefs(snapshot) {
3
- const matches = snapshot.match(/\buid=\S+/g);
11
+ const matches = snapshot.match(/^\s*(?:uid=\S+|@\d[\d.]*)\b/gm);
4
12
  return matches ? matches.length : 0;
5
13
  }
6
14
  /** Extract ref IDs with labels and types from snapshot text. */
7
15
  export function extractRefs(snapshot) {
8
16
  const refs = [];
9
17
  for (const line of snapshot.split("\n")) {
10
- const m = line.match(/\buid=(\S+)\s+(\w+)\s+"([^"]*)"/);
18
+ // Accept both uid=X_Y (raw MCP) and @X.Y (compact) forms;
19
+ // avoid \b before @ since @ is a non-word character
20
+ const m = line.match(/(?:uid=(\S+)|(?:^|[ \t])@([\d.]+))\s+([\w]+)\s+"([^"]*)"/);
11
21
  if (!m)
12
22
  continue;
13
- refs.push({ ref: m[1], type: m[2], label: m[3] });
23
+ const rawRef = m[1] ?? m[2];
24
+ // Always return in display form so suggestion strings emit @X.Y refs
25
+ const ref = m[1] ? refToDisplay(rawRef) : rawRef;
26
+ refs.push({ ref, type: m[3], label: m[4] });
14
27
  }
15
28
  return refs;
16
29
  }
17
- /** Extract page title from snapshot (RootWebArea or first heading). */
30
+ /** Extract page title from snapshot (RootWebArea/root root node or first heading). */
18
31
  export function extractTitle(snapshot) {
19
- const rootMatch = snapshot.match(/RootWebArea\s+"([^"]+)"/);
32
+ const rootMatch = snapshot.match(/(?:RootWebArea|root)\s+"([^"]+)"/);
20
33
  if (rootMatch)
21
34
  return rootMatch[1];
35
+ // Compact markdown heading after compactSnapshot: `@X.Y ## Title`
36
+ const mdMatch = snapshot.match(/^(?:@\S+\s+)?#{1,6}\s+(.+)$/m);
37
+ if (mdMatch)
38
+ return mdMatch[1].trim();
22
39
  const headingMatch = snapshot.match(/\bheading\s+"([^"]+)"/);
23
40
  if (headingMatch)
24
41
  return headingMatch[1];
25
42
  return "";
26
43
  }
44
+ // Query-string keys issued by external ad/analytics platforms that carry no functional
45
+ // meaning for the destination page — safe to drop on any site.
46
+ const NOISE_PARAM_EXACT = new Set([
47
+ // Google Ads click IDs
48
+ "gclid", "gbraid", "wbraid", "dclid", "gad_source",
49
+ // Social / messaging platform click IDs
50
+ "fbclid", // Meta/Facebook
51
+ "msclkid", // Microsoft Ads
52
+ "yclid", // Yandex
53
+ "igshid", // Instagram
54
+ "ttclid", // TikTok
55
+ "twclid", // Twitter/X
56
+ "li_fat_id", // LinkedIn
57
+ "srsltid", // Google Shopping
58
+ "_ke", // Klaviyo
59
+ ]);
60
+ // Prefix-matched families (all members are tracking-only)
61
+ const NOISE_PARAM_PREFIXES = [
62
+ "utm_", // Google Analytics UTM parameters
63
+ "mc_", // Mailchimp
64
+ ];
65
+ function isNoiseParam(key) {
66
+ if (NOISE_PARAM_EXACT.has(key))
67
+ return true;
68
+ return NOISE_PARAM_PREFIXES.some((p) => key.startsWith(p));
69
+ }
70
+ /**
71
+ * Clean a URL value to reduce token bloat without losing addressability:
72
+ * - returns null for javascript: and data: URLs so the caller drops the attribute entirely
73
+ * - strips a matching page origin → relative path
74
+ * - removes cross-site tracking query params (utm_*, gclid, fbclid, etc.)
75
+ *
76
+ * Preserves fragment, parameter order, and percent-encoding of remaining values.
77
+ */
78
+ export function cleanUrl(url, origin) {
79
+ if (url.startsWith("javascript:") || url.startsWith("data:"))
80
+ return null;
81
+ let working = url;
82
+ if (origin && working.startsWith(origin)) {
83
+ working = working.slice(origin.length) || "/";
84
+ }
85
+ // Pull the fragment off first so query-param parsing can't accidentally consume it
86
+ let fragment = "";
87
+ const hashIdx = working.indexOf("#");
88
+ if (hashIdx >= 0) {
89
+ fragment = working.slice(hashIdx);
90
+ working = working.slice(0, hashIdx);
91
+ }
92
+ const qIdx = working.indexOf("?");
93
+ if (qIdx < 0)
94
+ return working + fragment;
95
+ const path = working.slice(0, qIdx);
96
+ const query = working.slice(qIdx + 1);
97
+ if (!query)
98
+ return path + fragment;
99
+ const kept = query.split("&").filter((part) => {
100
+ if (!part)
101
+ return false;
102
+ const eq = part.indexOf("=");
103
+ const key = eq < 0 ? part : part.slice(0, eq);
104
+ return !isNoiseParam(key);
105
+ });
106
+ if (kept.length === 0)
107
+ return path + fragment;
108
+ return `${path}?${kept.join("&")}${fragment}`;
109
+ }
110
+ /** Extract scheme://host from the root node's url= attribute, if present. */
111
+ export function extractPageOrigin(tree) {
112
+ const m = tree.match(/^\s*(?:uid=\S+|@\S+)\s+(?:RootWebArea|root)\b[^\n]*\burl="([^"]+)"/m);
113
+ if (!m)
114
+ return null;
115
+ try {
116
+ const u = new URL(m[1]);
117
+ return `${u.protocol}//${u.host}`;
118
+ }
119
+ catch {
120
+ return null;
121
+ }
122
+ }
123
+ // Repeat a description value this many times before we treat it as boilerplate worth deduping.
124
+ // Below this, the bytes saved by dropping repeats don't beat the risk of hiding meaningful copy.
125
+ const DESCRIPTION_DEDUP_THRESHOLD = 3;
126
+ // Chrome a11y tree uses PascalCase for some internal role names; map them to compact lowercase.
127
+ const ROLE_RENAMES = {
128
+ RootWebArea: "root",
129
+ StaticText: "text",
130
+ DisclosureTriangle: "disclosure",
131
+ ColorWell: "color",
132
+ InputTime: "time",
133
+ Date: "date",
134
+ };
135
+ /**
136
+ * Compact an accessibility snapshot tree to reduce token usage (~30% fewer tokens).
137
+ * Removes noise nodes, strips ARIA default attributes, normalises role names,
138
+ * de-quotes numeric attributes, converts headings to markdown, and rewrites
139
+ * refs to the @PAGE.ELEM display format.
140
+ *
141
+ * Operates on the raw tree text (after MCP preamble has been stripped).
142
+ */
143
+ export function compactSnapshot(tree) {
144
+ const lines = tree.split("\n");
145
+ const out = [];
146
+ let dropDanglingQuote = false;
147
+ // Pre-pass: find page origin (for relative-URL rewriting) and count description values
148
+ // so we know which ones cross the dedup threshold.
149
+ const origin = extractPageOrigin(tree);
150
+ const descriptionCounts = new Map();
151
+ for (const line of lines) {
152
+ const re = / description="([^"]*)"/g;
153
+ let m;
154
+ while ((m = re.exec(line)) !== null) {
155
+ descriptionCounts.set(m[1], (descriptionCounts.get(m[1]) ?? 0) + 1);
156
+ }
157
+ }
158
+ const seenDescription = new Set();
159
+ for (const raw of lines) {
160
+ let line = raw;
161
+ // <br> elements appear as LineBreak nodes; they're never useful in the a11y tree.
162
+ // Their label is a literal newline, so splitting on \n leaves a dangling `"` on the
163
+ // next line — skip that too.
164
+ if (/^\s*uid=\S+ LineBreak "/.test(line)) {
165
+ dropDanglingQuote = true;
166
+ continue;
167
+ }
168
+ if (dropDanglingQuote) {
169
+ dropDanglingQuote = false;
170
+ if (/^\s*"\s*$/.test(line))
171
+ continue;
172
+ }
173
+ // Whitespace-only text nodes between elements are structural artifacts, not content
174
+ if (/^\s*uid=\S+ StaticText "\s*"\s*$/.test(line))
175
+ continue;
176
+ // StaticText children that just echo the parent's accessible name are redundant —
177
+ // links, headings, buttons etc. already carry the label on their own line
178
+ {
179
+ const m = line.match(/^(\s*)uid=\S+ StaticText "([^"]+)"\s*$/);
180
+ if (m) {
181
+ const childIndent = m[1].length;
182
+ const label = m[2];
183
+ let drop = false;
184
+ for (let i = out.length - 1; i >= 0; i--) {
185
+ if (!out[i].trim())
186
+ continue;
187
+ // Previous lines may already be in compact @X.Y form (B1 runs per-line before push)
188
+ const pm = out[i].match(/^(\s*)(?:uid=\S+|@\S+) \w+ "([^"]+)"/);
189
+ if (pm && pm[1].length === childIndent - 2 && pm[2] === label)
190
+ drop = true;
191
+ break;
192
+ }
193
+ if (drop)
194
+ continue;
195
+ }
196
+ }
197
+ // Empty valuetext is the same as having no valuetext
198
+ line = line.replace(/ valuetext=""/g, "");
199
+ // `disableable` is redundant when `disabled` is already present
200
+ if (/ disabled\b/.test(line))
201
+ line = line.replace(/ disableable\b/g, "");
202
+ // Every option and tab is selectable by definition; the attribute adds nothing
203
+ if (/ (?:option|tab) "/.test(line))
204
+ line = line.replace(/ selectable\b/g, "");
205
+ // `relevant="additions text"` is the ARIA default for live regions; omit it
206
+ line = line.replace(/ relevant="additions text"/g, "");
207
+ // `atomic` is implicit for alert and status by the ARIA spec
208
+ if (/ (?:alert|status) /.test(line))
209
+ line = line.replace(/ atomic\b/g, "");
210
+ // `live=` defaults are mandated by ARIA for these roles; no need to repeat them
211
+ if (/ status /.test(line))
212
+ line = line.replace(/ live="polite"/g, "");
213
+ if (/ alert /.test(line))
214
+ line = line.replace(/ live="assertive"/g, "");
215
+ // combobox is always expandable with a popup; both attributes are implied by the role
216
+ if (/ combobox /.test(line)) {
217
+ line = line.replace(/ haspopup="(?:menu|listbox)"/g, "");
218
+ line = line.replace(/ expandable\b/g, "");
219
+ }
220
+ // Horizontal is the default orientation for sliders and listboxes
221
+ line = line.replace(/ orientation="horizontal"/g, "");
222
+ // Autocomplete mode is an implementation detail rarely useful for navigation
223
+ line = line.replace(/ autocomplete="(?:both|list)"/g, "");
224
+ // Drop javascript: URLs entirely (no agent-actionable info), strip the page origin
225
+ // from same-site links, and remove tracking/encoding query params
226
+ line = line.replace(/ url="([^"]+)"/g, (_full, rawUrl) => {
227
+ const cleaned = cleanUrl(rawUrl, origin);
228
+ return cleaned == null ? "" : ` url="${cleaned}"`;
229
+ });
230
+ // Boilerplate descriptions (e.g. "use arrow keys to navigate" repeated on every link)
231
+ // are recoverable from the first occurrence; drop the rest
232
+ line = line.replace(/ description="([^"]*)"/g, (full, value) => {
233
+ if ((descriptionCounts.get(value) ?? 0) < DESCRIPTION_DEDUP_THRESHOLD)
234
+ return full;
235
+ if (seenDescription.has(value))
236
+ return "";
237
+ seenDescription.add(value);
238
+ return full;
239
+ });
240
+ // Normalise known PascalCase Chrome-internal role names to short lowercase forms.
241
+ // The uid= or @X.Y prefix is optional to handle simplified test fixtures.
242
+ line = line.replace(/^(\s*(?:(?:uid=|@)\S+\s+)?)([A-Za-z][a-zA-Z]*)( )/, (_, pre, role, post) => pre + (ROLE_RENAMES[role] ?? role) + post);
243
+ // Numeric attribute values don't need quotes — saves two tokens per attribute
244
+ line = line.replace(/(\w+)="(-?\d+)"/g, "$1=$2");
245
+ // `heading "Label" level=N` → `## Label` — markdown is shorter and familiar to models
246
+ {
247
+ const m = line.match(/^(\s*uid=\S+) heading "([^"]+)" level=(\d+)(.*)/);
248
+ if (m) {
249
+ const hashes = "#".repeat(parseInt(m[3], 10));
250
+ const extra = m[4].trim();
251
+ line = `${m[1]} ${hashes} ${m[2]}${extra ? " " + extra : ""}`;
252
+ }
253
+ }
254
+ // Rewrite refs last so all earlier transforms still match the uid= form;
255
+ // dot separator tokenises better than underscore in BPE encodings
256
+ line = line.replace(/\buid=(\d+)_(\d+)\b/g, (_, page, elem) => `@${page}.${elem}`);
257
+ out.push(line);
258
+ }
259
+ return collapseTextRuns(out).join("\n");
260
+ }
261
+ /**
262
+ * Merge consecutive text nodes at the same indent into one, then re-apply
263
+ * the echo-dedup: if the merged label exactly matches the parent's label,
264
+ * the collapsed line is dropped entirely (parent already carries the content).
265
+ *
266
+ * Only runs when 2+ text nodes were actually merged; single text nodes that
267
+ * already survived the per-line echo-dedup are passed through unchanged.
268
+ */
269
+ function collapseTextRuns(lines) {
270
+ const result = [];
271
+ for (let i = 0; i < lines.length; i++) {
272
+ const m = lines[i].match(/^(\s*)(@\S+) text "([^"]*)"\s*$/);
273
+ if (!m) {
274
+ result.push(lines[i]);
275
+ continue;
276
+ }
277
+ const [, indent, ref, firstLabel] = m;
278
+ let j = i + 1;
279
+ let merged = firstLabel;
280
+ while (j < lines.length) {
281
+ const next = lines[j].match(/^(\s*)@\S+ text "([^"]*)"\s*$/);
282
+ if (!next || next[1] !== indent)
283
+ break;
284
+ merged += next[2];
285
+ j++;
286
+ }
287
+ if (j === i + 1) {
288
+ // Only one text node — pass through (already echo-deduped in main loop)
289
+ result.push(lines[i]);
290
+ continue;
291
+ }
292
+ // Multiple nodes merged — advance past consumed lines and echo-dedup the result
293
+ i = j - 1;
294
+ const childIndent = indent.length;
295
+ let drop = false;
296
+ for (let k = result.length - 1; k >= 0; k--) {
297
+ if (!result[k].trim())
298
+ continue;
299
+ const pm = result[k].match(/^(\s*)(?:uid=\S+|@\S+) \w+ "([^"]+)"/);
300
+ if (pm && pm[1].length === childIndent - 2 && pm[2] === merged)
301
+ drop = true;
302
+ break;
303
+ }
304
+ if (!drop)
305
+ result.push(`${indent}${ref} text "${merged}"`);
306
+ }
307
+ return result;
308
+ }
27
309
  export function truncateSnapshot(snapshot, full, limit = 16000) {
28
310
  const totalLength = snapshot.length;
29
311
  if (full || totalLength <= limit) {
@@ -66,4 +348,91 @@ const INPUT_TYPES = ["textbox", "searchbox", "input", "combobox", "textarea"];
66
348
  export function isInputType(type) {
67
349
  return INPUT_TYPES.includes(type);
68
350
  }
351
+ // --- URL LUT (Layer 2) ---
352
+ const MIN_DEDUP_LEN = 15;
353
+ const WHALE_THRESHOLD = 200;
354
+ const WHALE_PREVIEW_CAP = 60;
355
+ // Produce a short human-readable hint for a whale URL (no full value echoed).
356
+ // Relative paths are already concise; absolute URLs strip the scheme first.
357
+ function whalePreview(url) {
358
+ const target = url.startsWith("/") ? url : url.replace(/^https?:\/\//, "");
359
+ return target.length <= WHALE_PREVIEW_CAP
360
+ ? target
361
+ : target.slice(0, WHALE_PREVIEW_CAP - 1) + "…";
362
+ }
363
+ /**
364
+ * Apply a URL lookup table to a compacted, already-truncated snapshot.
365
+ *
366
+ * Two classes of URL are replaced with short $uN tokens:
367
+ * dedup — appears ≥2× and length ≥ MIN_DEDUP_LEN → full URL printed in trailer
368
+ * whale — length ≥ WHALE_THRESHOLD and not already a dedup URL
369
+ * → hidden in trailer with byte-size + path-stem preview only
370
+ *
371
+ * Must run AFTER truncation so the trailer only references URLs the agent can
372
+ * actually see in the body. Token IDs are assigned in tree-walk (top-down)
373
+ * order and are therefore deterministic for identical input.
374
+ */
375
+ export function applyUrlLut(text) {
376
+ // Count occurrences of each URL value (Layer 1 has already cleaned them)
377
+ const urlCounts = new Map();
378
+ const scanRe = / url="([^"]+)"/g;
379
+ let m;
380
+ while ((m = scanRe.exec(text)) !== null) {
381
+ urlCounts.set(m[1], (urlCounts.get(m[1]) ?? 0) + 1);
382
+ }
383
+ const isDedup = (u) => (urlCounts.get(u) ?? 0) >= 2 && u.length >= MIN_DEDUP_LEN;
384
+ // Dedup wins when both conditions hold — URL gets full entry in trailer, not hidden.
385
+ const isWhale = (u) => u.length >= WHALE_THRESHOLD && !isDedup(u);
386
+ const urlToToken = new Map();
387
+ const urlMap = new Map();
388
+ let counter = 0;
389
+ const body = text.replace(/ url="([^"]+)"/g, (_full, url) => {
390
+ if (!isDedup(url) && !isWhale(url))
391
+ return _full;
392
+ if (!urlToToken.has(url)) {
393
+ const token = `$u${++counter}`;
394
+ urlToToken.set(url, token);
395
+ urlMap.set(token, url);
396
+ }
397
+ return ` url=${urlToToken.get(url)}`;
398
+ });
399
+ if (urlMap.size === 0)
400
+ return { body, trailer: "", urlMap };
401
+ const trailerLines = ["urls:"];
402
+ for (const [token, url] of urlMap) {
403
+ if (isWhale(url)) {
404
+ trailerLines.push(` ${token} [hidden ${url.length}b → ${whalePreview(url)}]`);
405
+ }
406
+ else {
407
+ trailerLines.push(` ${token} ${url}`);
408
+ }
409
+ }
410
+ return { body, trailer: trailerLines.join("\n"), urlMap };
411
+ }
412
+ /**
413
+ * Resolve a URL from a LUT-applied snapshot body.
414
+ *
415
+ * target is either "$u3" (a LUT token) or "11.57" / "@11.57" (an element ref).
416
+ * For ref resolution the body is searched for the element's url= attribute;
417
+ * if it was tokenised, the token is further resolved via urlMap.
418
+ *
419
+ * Returns the full URL string, or null if not found.
420
+ */
421
+ export function resolveUrl(body, urlMap, target) {
422
+ const normalised = target.replace(/^@/, "");
423
+ if (normalised.startsWith("$u")) {
424
+ return urlMap.get(normalised) ?? null;
425
+ }
426
+ // ref → find line and extract url= (quoted plain value or unquoted token)
427
+ const escaped = normalised.replace(/\./g, "\\.");
428
+ const re = new RegExp(`@${escaped}\\b[^\\n]*? url=(?:"([^"]+)"|(\\$u\\d+))`);
429
+ const hit = body.match(re);
430
+ if (!hit)
431
+ return null;
432
+ if (hit[1] !== undefined)
433
+ return hit[1];
434
+ if (hit[2] !== undefined)
435
+ return urlMap.get(hit[2]) ?? null;
436
+ return null;
437
+ }
69
438
  //# sourceMappingURL=snapshot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/snapshot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/snapshot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ # Copy to .env and fill in your values.
2
+ # docker-compose reads this file automatically when run from the openclaw/ directory.
3
+
4
+ # --- AI provider keys (pass whichever providers OpenClaw is configured to use) ---
5
+ # ANTHROPIC_API_KEY=sk-ant-...
6
+ # OPENAI_API_KEY=sk-...
7
+
8
+ # --- opera-browser-cli notes ---
9
+ # The compose file wires OPERA_CLI_BROWSER_URL=http://localhost:9222 automatically.
10
+ # In connect-to-existing-browser mode use `newpage <url>` instead of `open <url>`:
11
+ # docker compose exec openclaw opera-browser-cli newpage https://example.com
12
+ #
13
+ # Additional Chrome flags (space-separated):
14
+ # OPERA_CLI_CHROME_ARGS=--some-flag
@@ -0,0 +1,14 @@
1
+ FROM ghcr.io/openclaw/openclaw:latest
2
+
3
+ USER root
4
+
5
+ # Install opera-browser-cli globally so it is on PATH for OpenClaw agents.
6
+ # Browser is provided by the chromedp/headless-shell sidecar in docker-compose.yml.
7
+ # Also registers SKILL.md into ~/.agents/skills/ — a path OpenClaw scans for skills
8
+ # that is not covered by any named volume, so the file lives in the image layer.
9
+ RUN npm install -g opera-browser-cli && \
10
+ mkdir -p /home/node/.agents/skills/opera-browser-cli && \
11
+ cp "$(npm root -g)/opera-browser-cli/SKILL.md" /home/node/.agents/skills/opera-browser-cli/SKILL.md && \
12
+ chown -R node:node /home/node/.agents
13
+
14
+ USER node
@@ -0,0 +1,173 @@
1
+ # Adding opera-browser-cli to OpenClaw (Docker)
2
+
3
+ `opera-browser-cli` can be added to any Docker-based OpenClaw setup. Two approaches —
4
+ pick whichever suits your workflow:
5
+
6
+ - **[Option A: Runtime install](#option-a-runtime-install)** — install into the running
7
+ container, no image rebuild required
8
+ - **[Option B: Dockerfile](#option-b-dockerfile)** — extend the OpenClaw image so the
9
+ install is baked in and fully persistent
10
+
11
+ > **Browser is generic headless Chromium, not Opera.** The sidecar uses
12
+ > `chromedp/headless-shell` (upstream Chromium). Standard automation commands
13
+ > (`open`, `newpage`, `snapshot`, `click`, `fill`, `type`, `screenshot`, `eval`,
14
+ > `pages`, `network`, `console`, `lighthouse`, …) work fine. Opera-specific
15
+ > commands — `chat`, `invoke-do`, `make`, `research` — **will not work**; they
16
+ > need an Opera browser with a signed-in user session.
17
+ >
18
+ > Do **not** run `opera-browser-cli setup` or `doctor` inside the container.
19
+ > Both are interactive Opera-Neon detectors and will fail. All required env vars
20
+ > are injected by the compose file — no further config is needed.
21
+
22
+ ## Prerequisites
23
+
24
+ - Docker Desktop (Mac/Windows) or Docker Engine + Compose plugin (Linux)
25
+
26
+ ## Compose changes (required for both options)
27
+
28
+ Add a headless Chrome sidecar and wire OpenClaw's network namespace to it:
29
+
30
+ ```yaml
31
+ services:
32
+ chrome:
33
+ image: chromedp/headless-shell:latest
34
+ ports:
35
+ - "18789:18789" # expose OpenClaw's gateway here — openclaw shares this netns
36
+ restart: unless-stopped
37
+
38
+ openclaw:
39
+ # ... your existing config, with these additions:
40
+ network_mode: "service:chrome"
41
+ environment:
42
+ OPERA_CLI_BROWSER_URL: http://localhost:9222
43
+ ```
44
+
45
+ **Note:** `network_mode: "service:chrome"` is incompatible with `networks:` and `ports:`
46
+ on the `openclaw` service. Move any ports you were exposing on `openclaw` to the `chrome`
47
+ service instead (as shown above).
48
+
49
+ Then start the stack:
50
+
51
+ ```bash
52
+ docker compose up -d
53
+ ```
54
+
55
+ ## Option A: Runtime install
56
+
57
+ No Dockerfile or image rebuild needed. Once the stack is up, run:
58
+
59
+ ```bash
60
+ docker compose exec openclaw sh -c '
61
+ npm install -g opera-browser-cli &&
62
+ mkdir -p ~/.openclaw/skills/opera-browser-cli &&
63
+ cp $(npm root -g)/opera-browser-cli/SKILL.md ~/.openclaw/skills/opera-browser-cli/SKILL.md
64
+ '
65
+ ```
66
+
67
+ This installs the binary and registers the skill in one step. The SKILL.md is written
68
+ to the `openclaw-config` named volume and persists across restarts and `docker compose
69
+ down`. The binary lives in the container filesystem and is lost when the container is
70
+ recreated — re-run `npm install -g opera-browser-cli` after each `docker compose down`.
71
+
72
+ ## Option B: Dockerfile
73
+
74
+ Extend the official OpenClaw image so the install is baked in and survives `docker
75
+ compose down`. Use the `Dockerfile` in this directory (the reference implementation
76
+ below), or create your own using it as a starting point.
77
+
78
+ Point your compose service at it:
79
+
80
+ ```yaml
81
+ openclaw:
82
+ build: . # path to the directory containing the Dockerfile
83
+ # ... rest of your existing config
84
+ ```
85
+
86
+ Build once, then start normally:
87
+
88
+ ```bash
89
+ docker compose build
90
+ docker compose up -d
91
+ ```
92
+
93
+ Re-run `docker compose build` when a new version of `opera-browser-cli` is released.
94
+
95
+ ## Reference implementation
96
+
97
+ This directory contains a complete working example for a standalone fresh install:
98
+
99
+ - `Dockerfile` — the Option B extension above
100
+ - `docker-compose.yml` — a full compose file including the Chrome sidecar
101
+ - `.env.example` — template for environment variables
102
+
103
+ For OpenClaw gateway configuration (mode, auth token, AI provider keys), see the
104
+ [OpenClaw docs](https://docs.openclaw.ai).
105
+
106
+ ## Using opera-browser-cli
107
+
108
+ For the full command reference see [SKILL.md](../SKILL.md).
109
+
110
+ **From the host** (for manual testing or debugging):
111
+
112
+ ```bash
113
+ docker compose exec openclaw opera-browser-cli <command>
114
+ ```
115
+
116
+ ### Opening pages
117
+
118
+ Because `OPERA_CLI_BROWSER_URL` is set, the CLI connects to the existing Chrome rather
119
+ than launching one. Use `newpage` to open a fresh tab:
120
+
121
+ ```bash
122
+ opera-browser-cli newpage https://example.com
123
+ ```
124
+
125
+ `open <url>` also works, but only after a `stop` first — which resets the bridge so it
126
+ reconnects cleanly:
127
+
128
+ ```bash
129
+ opera-browser-cli stop
130
+ opera-browser-cli open https://example.com
131
+ ```
132
+
133
+ Without `stop` first, `open` returns `No page selected` because no tab is pre-selected
134
+ when connecting to an already-running Chrome instance.
135
+
136
+ ## Environment variables
137
+
138
+ `OPERA_CLI_BROWSER_URL` is the only variable required. Set it in your compose file as
139
+ shown above.
140
+
141
+ | Variable | Set to | Purpose |
142
+ |---|---|---|
143
+ | `OPERA_CLI_BROWSER_URL` | `http://localhost:9222` | Connect to the headless-shell sidecar instead of launching a browser |
144
+
145
+ Other `opera-browser-cli` variables (not needed unless customising):
146
+
147
+ | Variable | Purpose |
148
+ |---|---|
149
+ | `OPERA_CLI_EXECUTABLE_PATH` | Path to a browser binary (launch mode only — not relevant here) |
150
+ | `OPERA_CLI_CHROME_ARGS` | Extra Chrome flags (launch mode only) |
151
+ | `OPERA_CLI_HEADED` | `1` for headed mode — not useful in a headless container |
152
+ | `OPERA_CLI_USER_DATA_DIR` | Persistent Chrome profile path (launch mode only) |
153
+ | `OPERA_CLI_PORT` | Bridge HTTP port inside the container (default `9225`) |
154
+
155
+ ## Troubleshooting
156
+
157
+ **`opera-browser-cli open` returns "No page selected"**
158
+
159
+ You are in connect mode. Use `newpage <url>` instead, or run `opera-browser-cli stop`
160
+ first and then `open`.
161
+
162
+ **Chrome connection errors (`Failed to fetch browser webSocket URL`)**
163
+
164
+ The reference `docker-compose.yml` uses a healthcheck on the `chrome` service so
165
+ OpenClaw only starts after Chrome's CDP port is confirmed open. If you see this error
166
+ with a custom compose file, add the healthcheck and `condition: service_healthy` to
167
+ your `depends_on` — see the reference `docker-compose.yml` for the exact config.
168
+
169
+ **Chromium SIGTRAP / crash if using a different base image or architecture**
170
+
171
+ Debian's system Chromium (`apt install chromium`) crashes on ARM64 Docker Desktop
172
+ with `Trace/breakpoint trap (core dumped)`. Do not substitute it for the
173
+ `chromedp/headless-shell` sidecar.