Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ dist/
*.tar.gz
*.png
scripts/
.env
docker/.env
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,22 @@ Set `OPERA_CLI_DISABLE_HOOKS=1` to skip that auto-install behavior.

Development entrypoints such as `npm run dev` and `bin/opera-browser-cli.ts` do not modify those hook files.

## Docker / OpenClaw

A ready-made Docker setup runs `opera-browser-cli` as a tool inside an
[OpenClaw](https://openclaw.ai) agent gateway, with a headless Chrome sidecar handling
the browser. No local browser or Node.js install is required on the host — Docker is
the only prerequisite. The skill is registered automatically so OpenClaw agents can
invoke `opera-browser-cli` commands directly.

The `docker/` directory ships with the npm package:

```sh
cd "$(npm root -g)/opera-browser-cli/openclaw"
```

See [`openclaw/README.md`](openclaw/README.md) for the full setup guide.

## Local Setup (Full Stack)

Both `opera-devtools-mcp` and `opera-browser-cli` need to be built and linked so they're available in PATH.
Expand Down
5 changes: 5 additions & 0 deletions SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: opera-browser-cli
description: Browser automation and web interaction using the opera-browser-cli tool. Use for navigating pages, clicking elements, filling forms, taking screenshots, inspecting console/network, running performance audits, and Opera AI features (chat available on any Opera browser; invoke-do, make, research require Opera Neon).
metadata: {"openclaw": {"requires": {"bins": ["opera-browser-cli"]}}}
---

# Skill: opera-browser-cli Browser Automation
Expand Down Expand Up @@ -41,6 +42,10 @@ Use `opera-browser-cli url $uN` or `opera-browser-cli url @ref` to resolve a tok

Commands that accept these flags: `open`, `snapshot`, `click`, `fill`, `type`, `press`, `scroll`, `back`, `hover`, `drag`, `fillform`, `upload`, `newpage`, `selectpage`.

## Running inside an OpenClaw container

To wire this CLI into a Docker-based OpenClaw setup (Chromium sidecar, shared netns, config bootstrap), see [`openclaw/README.md`](openclaw/README.md).

## Sign-in errors

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.
14 changes: 14 additions & 0 deletions openclaw/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copy to .env and fill in your values.
# docker-compose reads this file automatically when run from the openclaw/ directory.

# --- AI provider keys (pass whichever providers OpenClaw is configured to use) ---
# ANTHROPIC_API_KEY=sk-ant-...
# OPENAI_API_KEY=sk-...

# --- opera-browser-cli notes ---
# The compose file wires OPERA_CLI_BROWSER_URL=http://localhost:9222 automatically.
# In connect-to-existing-browser mode use `newpage <url>` instead of `open <url>`:
# docker compose exec openclaw opera-browser-cli newpage https://example.com
#
# Additional Chrome flags (space-separated):
# OPERA_CLI_CHROME_ARGS=--some-flag
14 changes: 14 additions & 0 deletions openclaw/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM ghcr.io/openclaw/openclaw:latest

USER root

# Install opera-browser-cli globally so it is on PATH for OpenClaw agents.
# Browser is provided by the chromedp/headless-shell sidecar in docker-compose.yml.
# Also registers SKILL.md into ~/.agents/skills/ — a path OpenClaw scans for skills
# that is not covered by any named volume, so the file lives in the image layer.
RUN npm install -g opera-browser-cli && \
mkdir -p /home/node/.agents/skills/opera-browser-cli && \
cp "$(npm root -g)/opera-browser-cli/SKILL.md" /home/node/.agents/skills/opera-browser-cli/SKILL.md && \
chown -R node:node /home/node/.agents

USER node
173 changes: 173 additions & 0 deletions openclaw/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# Adding opera-browser-cli to OpenClaw (Docker)

`opera-browser-cli` can be added to any Docker-based OpenClaw setup. Two approaches —
pick whichever suits your workflow:

- **[Option A: Runtime install](#option-a-runtime-install)** — install into the running
container, no image rebuild required
- **[Option B: Dockerfile](#option-b-dockerfile)** — extend the OpenClaw image so the
install is baked in and fully persistent

> **Browser is generic headless Chromium, not Opera.** The sidecar uses
> `chromedp/headless-shell` (upstream Chromium). Standard automation commands
> (`open`, `newpage`, `snapshot`, `click`, `fill`, `type`, `screenshot`, `eval`,
> `pages`, `network`, `console`, `lighthouse`, …) work fine. Opera-specific
> commands — `chat`, `invoke-do`, `make`, `research` — **will not work**; they
> need an Opera browser with a signed-in user session.
>
> Do **not** run `opera-browser-cli setup` or `doctor` inside the container.
> Both are interactive Opera-Neon detectors and will fail. All required env vars
> are injected by the compose file — no further config is needed.

## Prerequisites

- Docker Desktop (Mac/Windows) or Docker Engine + Compose plugin (Linux)

## Compose changes (required for both options)

Add a headless Chrome sidecar and wire OpenClaw's network namespace to it:

```yaml
services:
chrome:
image: chromedp/headless-shell:latest
ports:
- "18789:18789" # expose OpenClaw's gateway here — openclaw shares this netns
restart: unless-stopped

openclaw:
# ... your existing config, with these additions:
network_mode: "service:chrome"
environment:
OPERA_CLI_BROWSER_URL: http://localhost:9222
```

**Note:** `network_mode: "service:chrome"` is incompatible with `networks:` and `ports:`
on the `openclaw` service. Move any ports you were exposing on `openclaw` to the `chrome`
service instead (as shown above).

Then start the stack:

```bash
docker compose up -d
```

## Option A: Runtime install

No Dockerfile or image rebuild needed. Once the stack is up, run:

```bash
docker compose exec openclaw sh -c '
npm install -g opera-browser-cli &&
mkdir -p ~/.openclaw/skills/opera-browser-cli &&
cp $(npm root -g)/opera-browser-cli/SKILL.md ~/.openclaw/skills/opera-browser-cli/SKILL.md
'
```

This installs the binary and registers the skill in one step. The SKILL.md is written
to the `openclaw-config` named volume and persists across restarts and `docker compose
down`. The binary lives in the container filesystem and is lost when the container is
recreated — re-run `npm install -g opera-browser-cli` after each `docker compose down`.

## Option B: Dockerfile

Extend the official OpenClaw image so the install is baked in and survives `docker
compose down`. Use the `Dockerfile` in this directory (the reference implementation
below), or create your own using it as a starting point.

Point your compose service at it:

```yaml
openclaw:
build: . # path to the directory containing the Dockerfile
# ... rest of your existing config
```

Build once, then start normally:

```bash
docker compose build
docker compose up -d
```

Re-run `docker compose build` when a new version of `opera-browser-cli` is released.

## Reference implementation

This directory contains a complete working example for a standalone fresh install:

- `Dockerfile` — the Option B extension above
- `docker-compose.yml` — a full compose file including the Chrome sidecar
- `.env.example` — template for environment variables

For OpenClaw gateway configuration (mode, auth token, AI provider keys), see the
[OpenClaw docs](https://docs.openclaw.ai).

## Using opera-browser-cli

For the full command reference see [SKILL.md](../SKILL.md).

**From the host** (for manual testing or debugging):

```bash
docker compose exec openclaw opera-browser-cli <command>
```

### Opening pages

Because `OPERA_CLI_BROWSER_URL` is set, the CLI connects to the existing Chrome rather
than launching one. Use `newpage` to open a fresh tab:

```bash
opera-browser-cli newpage https://example.com
```

`open <url>` also works, but only after a `stop` first — which resets the bridge so it
reconnects cleanly:

```bash
opera-browser-cli stop
opera-browser-cli open https://example.com
```

Without `stop` first, `open` returns `No page selected` because no tab is pre-selected
when connecting to an already-running Chrome instance.

## Environment variables

`OPERA_CLI_BROWSER_URL` is the only variable required. Set it in your compose file as
shown above.

| Variable | Set to | Purpose |
|---|---|---|
| `OPERA_CLI_BROWSER_URL` | `http://localhost:9222` | Connect to the headless-shell sidecar instead of launching a browser |

Other `opera-browser-cli` variables (not needed unless customising):

| Variable | Purpose |
|---|---|
| `OPERA_CLI_EXECUTABLE_PATH` | Path to a browser binary (launch mode only — not relevant here) |
| `OPERA_CLI_CHROME_ARGS` | Extra Chrome flags (launch mode only) |
| `OPERA_CLI_HEADED` | `1` for headed mode — not useful in a headless container |
| `OPERA_CLI_USER_DATA_DIR` | Persistent Chrome profile path (launch mode only) |
| `OPERA_CLI_PORT` | Bridge HTTP port inside the container (default `9225`) |

## Troubleshooting

**`opera-browser-cli open` returns "No page selected"**

You are in connect mode. Use `newpage <url>` instead, or run `opera-browser-cli stop`
first and then `open`.

**Chrome connection errors (`Failed to fetch browser webSocket URL`)**

The reference `docker-compose.yml` uses a healthcheck on the `chrome` service so
OpenClaw only starts after Chrome's CDP port is confirmed open. If you see this error
with a custom compose file, add the healthcheck and `condition: service_healthy` to
your `depends_on` — see the reference `docker-compose.yml` for the exact config.

**Chromium SIGTRAP / crash if using a different base image or architecture**

Debian's system Chromium (`apt install chromium`) crashes on ARM64 Docker Desktop
with `Trace/breakpoint trap (core dumped)`. Do not substitute it for the
`chromedp/headless-shell` sidecar.
49 changes: 49 additions & 0 deletions openclaw/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
services:
# Dedicated headless Chrome sidecar — ARM64-compatible, pre-configured for Docker.
# opera-browser-cli connects to it via CDP at localhost:9222 (shared network namespace).
chrome:
image: chromedp/headless-shell:latest
# Chrome's CDP HTTP server rejects non-IP/non-localhost Host headers.
# The openclaw service uses network_mode: service:chrome so it sees Chrome
# as localhost, bypassing that check without needing --remote-allow-origins tricks.
ports:
# Expose OpenClaw's gateway through the chrome container (openclaw shares this netns)
- "18789:18789"
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "bash -c 'exec 3<>/dev/tcp/127.0.0.1/9222' 2>/dev/null"]
interval: 3s
timeout: 3s
retries: 10
start_period: 5s

openclaw:
build:
context: .
dockerfile: Dockerfile
depends_on:
chrome:
condition: service_healthy
# Share chrome's network namespace so localhost:9222 IS the Chrome CDP port
network_mode: "service:chrome"
environment:
# localhost:9222 resolves inside chrome's netns
OPERA_CLI_BROWSER_URL: http://localhost:9222

# OpenClaw: skip the interactive first-run wizard
OPENCLAW_SKIP_ONBOARDING: "1"
# Disable mDNS advertising (not useful inside Docker)
OPENCLAW_DISABLE_BONJOUR: "1"

volumes:
# Persist OpenClaw config and workspace across container restarts
- openclaw-config:/home/node/.openclaw
- openclaw-auth:/home/node/.config/openclaw

shm_size: 256m

restart: unless-stopped

volumes:
openclaw-config:
openclaw-auth:
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"files": [
"dist",
"openclaw",
"LICENSE",
"README.md",
"SKILL.md"
Expand Down
2 changes: 1 addition & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2466,7 +2466,7 @@ function warnIfUnconfigured(argv: string[]): void {
const cmd = argv[0];
if (cmd !== undefined && SETUP_SKIP_COMMANDS.has(cmd)) return;
const configFile = join(homedir(), ".opera-browser-cli", "config");
if (!existsSync(configFile) && !process.env.OPERA_CLI_EXECUTABLE_PATH) {
if (!existsSync(configFile) && !process.env.OPERA_CLI_EXECUTABLE_PATH && !process.env.OPERA_CLI_BROWSER_URL) {
process.stderr.write(
"hint: run `opera-browser-cli setup` to configure (first-time setup)\n",
);
Expand Down
Loading