Skill: redf426/chromium (version 1.1.0)

--- name: chromium description: Launch a persistent headless Chromium with remote debugging (CDP) for browser automation — page navigation, clicks, form filling, screenshots, and cookie import. Use when the user asks to open a website, browse, click, read page content, or work with a browser. --- # Chromium (persistent headless profile) ## What it does - Launches headless Chromium with a **persistent profile** (logins, cookies, localStorage survive restarts). - Exposes Chrome DevTools Protocol (CDP) on `127.0.0.1` for browser tool integration. - Supports cookie import for pre-authenticated sessions. ## Quick start ### 1. Launch Chromium ```bash ~/.openclaw/workspace/skills/chromium/scripts/start_chromium.sh ``` Environment variables (all optional): | Variable | Default | Description | |---|---|---| | `CHROMIUM_PROFILE_DIR` | `$HOME/.openclaw/workspace/chromium-profile` | User data directory | | `CHROMIUM_DEBUG_PORT` | `18801` | CDP remote debugging port | | `CHROMIUM_LOG_FILE` | `$HOME/.openclaw/workspace/logs/chromium.log` | Log file path | | `CHROMIUM_BIN` | auto-detect (`chromium`, `chromium-browser`, `google-chrome`) | Browser binary | ### 2. Verify CDP is ready ```bash curl -s http://127.0.0.1:18801/json/version ``` If you get JSON with `Browser` and `webSocketDebuggerUrl` — it's ready. ### 3. Use browser tools ``` browser navigate url=https://example.com browser wait --load networkidle browser snapshot ``` ## Browser tool cheatsheet | Action | Command | |---|---| | Open page | `browser navigate url=<URL>` | | Wait for load | `browser wait --load networkidle` | | Read page content | `browser snapshot` | | Click element | `browser click ref=<ref>` | | Type text | `browser type ref=<ref> text=<text>` | | Scroll to element | `browser scrollintoview <ref>` | | Take screenshot | `browser screenshot` | | Run JavaScript | `browser evaluate --fn "document.title"` | ## Snapshot format — important **Always use the default snapshot format** (no extra parameters): ``` browser snapshot ``` **Do NOT use:** - `refs=aria` — returns accessibility tree without actionable refs - `depth=2` or other depth limits — truncates DOM and hides content The default AI format returns full page text with refs (e12, e293, etc.) suitable for click/type. ## Working with dynamic pages (SPAs) Single-page apps (React, Next.js, etc.) continuously update the DOM. Refs become stale between snapshots. **After every navigation:** ``` browser wait --load networkidle browser snapshot ``` **Before clicking:** ``` browser scrollintoview <ref> browser click <ref> ``` **If click fails** ("Element not found or not visible"): 1. Take a fresh `browser snapshot` — never reuse old refs 2. Use `browser screenshot` to see the visual state 3. For links, navigate directly by URL instead of clicking 4. As a last resort, use JavaScript: `browser evaluate --fn "document.querySelector('...').click()"` ## Cookie import (pre-authenticated sessions) To use a site that requires login, export cookies from a browser where you're already logged in and import them: **Step 1 — Export cookies** (in your regular browser): - Install [Cookie-Editor](https://cookie-editor.com/) extension - Go to the target site (make sure you're logged in) - Export cookies as JSON, save as `cookies.json` - Copy to server: `scp cookies.json server:/tmp/` **Step 2 — Import:** ```bash python3 ~/.openclaw/workspace/skills/chromium/scripts/import_cookies.py \ /tmp/cookies.json \ --domain example.com ``` **Step 3 — Verify** by navigating to the site and checking if you're logged in. ## Data locations | What | Path | |---|---| | Browser profile | `$CHROMIUM_PROFILE_DIR` (default: `~/.openclaw/workspace/chromium-profile`) | | Launch log | `$CHROMIUM_LOG_FILE` (default: `~/.openclaw/workspace/logs/chromium.log`) | ## Troubleshooting If CDP doesn't respond within 3 seconds after launch, check the log: ```bash cat ~/.openclaw/workspace/logs/chromium.log ``` Common issues: - **"Address already in use"** — another Chromium is running. The script kills previous instances automatically, but you can run `pkill -f "chromium.*remote-debugging"` manually. - **SingletonLock** — stale lock file. The script removes it if Chromium isn't running. - **No `chromium` binary** — set `CHROMIUM_BIN` to the correct path (e.g., `/usr/bin/google-chrome`). ## Requirements - Chromium, Chromium Browser, or Google Chrome installed on the host - Python 3 (for cookie import script) - OpenClaw with browser tool support

path: README.md
content: # Chromium Skill for OpenClaw

Headless Chromium with a persistent profile and CDP (Chrome DevTools Protocol) remote debugging. Browse websites, click buttons, fill forms, take screenshots, and import cookies — all from your OpenClaw agent.

## Prerequisites

- **OpenClaw** installed and running with gateway + browser tool support
- **Chromium** (or Google Chrome) installed on the host

### Installing Chromium

**Ubuntu/Debian:**
```bash
sudo apt update && sudo apt install -y chromium-browser
```

**Ubuntu (snap, often pre-installed):**
```bash
sudo snap install chromium
```

**Alpine:**
```bash
apk add chromium
```

**macOS:**
```bash
brew install --cask chromium
```

If your binary has a non-standard name or path, set `CHROMIUM_BIN`:
```bash
export CHROMIUM_BIN=/usr/bin/google-chrome
```

## Installation

```bash
clawhub install chromium
```

Or manually: copy the `chromium` folder into your OpenClaw workspace `skills/` directory and restart the session.

## Setup (one-time)

### 1. Launch Chromium

```bash
~/.openclaw/workspace/skills/chromium/scripts/start_chromium.sh
```

### 2. Verify it's running

```bash
curl -s http://127.0.0.1:18801/json/version
```

You should see JSON with `Browser` and `webSocketDebuggerUrl`. If not — check the log:
```bash
cat ~/.openclaw/workspace/logs/chromium.log
```

### 3. Done

Start a new OpenClaw session. The agent will now use headless Chromium for any browser tasks.

## Usage examples

Ask your agent:

- "Open https://example.com and tell me what's on the page"
- "Go to Hacker News and list the top 5 stories"
- "Take a screenshot of https://github.com"
- "Fill in the search box on Google and search for OpenClaw"

## Configuration

All settings are optional environment variables. Set them before running `start_chromium.sh`:

| Variable | Default | Description |
|---|---|---|
| `CHROMIUM_PROFILE_DIR` | `~/.openclaw/workspace/chromium-profile` | Persistent browser profile directory |
| `CHROMIUM_DEBUG_PORT` | `18801` | CDP port (change if 18801 is taken) |
| `CHROMIUM_LOG_FILE` | `~/.openclaw/workspace/logs/chromium.log` | Log file location |
| `CHROMIUM_BIN` | auto-detect | Path to Chromium/Chrome binary |

Example with custom port:
```bash
CHROMIUM_DEBUG_PORT=9222 ~/.openclaw/workspace/skills/chromium/scripts/start_chromium.sh
```

## Cookie import (optional)

To access sites that require login without typing credentials in headless mode:

1. In your regular browser, install the [Cookie-Editor](https://cookie-editor.com/) extension
2. Go to the target site and make sure you're logged in
3. Export cookies as JSON → save as `cookies.json`
4. Copy to your server: `scp cookies.json yourserver:/tmp/`
5. Import:
   ```bash
   python3 ~/.openclaw/workspace/skills/chromium/scripts/import_cookies.py \
     /tmp/cookies.json --domain example.com
   ```
6. Ask your agent to navigate to the site — it should be logged in

## Auto-start on boot (optional)

To launch Chromium automatically when the system starts, add a cron entry:

```bash
crontab -e
```

Add:
```
@reboot ~/.openclaw/workspace/skills/chromium/scripts/start_chromium.sh
```

## Troubleshooting

| Problem | Solution |
|---|---|
| `Address already in use` | Another Chromium is running. Run `pkill -f "chromium.*remote-debugging"` and try again |
| CDP doesn't respond | Check the log file for errors. Common cause: missing dependencies (`libnss3`, `libatk-bridge2.0-0`) |
| `No Chromium/Chrome binary found` | Install Chromium or set `CHROMIUM_BIN` to the correct path |
| Stale `SingletonLock` | The script handles this automatically. If it persists: `rm ~/.openclaw/workspace/chromium-profile/SingletonLock` |
| Cookie import fails | Make sure the OpenClaw gateway is running (`openclaw gateway run`) |

### Missing system libraries (headless Linux)

If Chromium crashes on a minimal server, you may need:
```bash
sudo apt install -y libnss3 libatk-bridge2.0-0 libdrm2 libxkbcommon0 libgbm1 libasound2
```

## Files

```
chromium/
├── SKILL.md                      # Skill instructions for the agent
├── README.md                     # This file
└── scripts/
    ├── start_chromium.sh          # Launch script
    └── import_cookies.py          # Cookie import utility
```

## License

Public domain. Use however you like.


path: scripts/import_cookies.py
content: #!/usr/bin/env python3
"""
Import browser cookies into the OpenClaw headless Chromium profile.

Usage:
  python3 import_cookies.py cookies.json [--domain x.com]

Cookie JSON format (from Cookie-Editor, EditThisCookie, etc.):
  [
    {"name": "auth_token", "value": "...", "domain": ".x.com", "path": "/", ...},
    ...
  ]

After import, navigate to the target site in the browser to verify.
"""

import json
import sys
import os
import argparse
import urllib.request
import urllib.error

# OpenClaw gateway browser control port = gateway port + 2
GATEWAY_PORT = int(os.environ.get("OPENCLAW_GATEWAY_PORT", 18792))
BROWSER_CONTROL_PORT = GATEWAY_PORT + 2


def get_gateway_token():
    """Read gateway auth token from OpenClaw config."""
    config_path = os.path.expanduser("~/.openclaw/openclaw.json")
    if not os.path.exists(config_path):
        return ""
    with open(config_path) as f:
        cfg = json.load(f)
    return cfg.get("gateway", {}).get("auth", {}).get("token", "")


def set_cookie(token: str, cookie: dict, profile: str) -> bool:
    url = f"http://127.0.0.1:{BROWSER_CONTROL_PORT}/cookies/set?profile={profile}"
    payload = json.dumps({"cookie": cookie}).encode()
    req = urllib.request.Request(
        url,
        data=payload,
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
        },
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=5) as resp:
            result = json.loads(resp.read())
            return result.get("ok", False)
    except Exception as e:
        print(f"  ERROR: {e}", file=sys.stderr)
        return False


def normalize_cookie(raw: dict) -> dict:
    """Normalize cookie from various export formats to CDP format."""
    cookie = {}

    cookie["name"] = raw.get("name", raw.get("Name", ""))
    cookie["value"] = raw.get("value", raw.get("Value", ""))

    domain = raw.get("domain", raw.get("Domain", raw.get("host", "")))
    if domain and not domain.startswith(".") and not domain.startswith("http"):
        domain = "." + domain
    cookie["domain"] = domain

    cookie["path"] = raw.get("path", raw.get("Path", "/"))

    expires = raw.get("expirationDate", raw.get("expires", raw.get("Expires")))
    if expires and isinstance(expires, (int, float)) and expires > 0:
        cookie["expires"] = int(expires)

    cookie["httpOnly"] = bool(raw.get("httpOnly", raw.get("HttpOnly", False)))
    cookie["secure"] = bool(raw.get("secure", raw.get("Secure", False)))

    samesite = raw.get("sameSite", raw.get("SameSite", ""))
    if samesite:
        cookie["sameSite"] = samesite.capitalize()

    return cookie


def main():
    parser = argparse.ArgumentParser(description="Import cookies into OpenClaw Chromium")
    parser.add_argument("cookies_file", help="Path to cookies JSON file")
    parser.add_argument("--profile", default="default", help="Browser profile name (default: default)")
    parser.add_argument("--domain", default=None, help="Filter cookies by domain (e.g. x.com)")
    parser.add_argument("--dry-run", action="store_true", help="Show what would be imported")
    args = parser.parse_args()

    with open(args.cookies_file) as f:
        raw_cookies = json.load(f)

    if not isinstance(raw_cookies, list):
        print("ERROR: Cookie file must be a JSON array", file=sys.stderr)
        sys.exit(1)

    if args.domain:
        filter_domain = args.domain.lstrip(".")
        raw_cookies = [
            c for c in raw_cookies
            if filter_domain in c.get("domain", c.get("Domain", c.get("host", "")))
        ]
        print(f"Filtered to {len(raw_cookies)} cookies for domain: {args.domain}")

    [REDACTED_SECRET])
    if not token and not args.dry_run:
        print("ERROR: Could not read gateway token from ~/.openclaw/openclaw.json", file=sys.stderr)
        print("Make sure OpenClaw gateway is configured and running.", file=sys.stderr)
        sys.exit(1)

    print(f"Importing {len(raw_cookies)} cookies into profile '{args.profile}'...")
    print()

    ok_count = 0
    fail_count = 0

    for raw in raw_cookies:
        cookie = normalize_cookie(raw)
        name = cookie.get("name", "")
        domain = cookie.get("domain", "")
        label = f"{name} ({domain})"

        if args.dry_run:
            print(f"  DRY-RUN: {label}")
            ok_count += 1
            continue

        ok = set_cookie(token, cookie, args.profile)
        if ok:
            print(f"  OK:   {label}")
            ok_count += 1
        else:
            print(f"  FAIL: {label}")
            fail_count += 1

    print()
    print(f"Done: {ok_count} imported, {fail_count} failed")


if __name__ == "__main__":
    main()


path: scripts/start_chromium.sh
content: #!/usr/bin/env bash
set -euo pipefail

# --- Configuration (override via environment) ---
PROFILE_DIR="${CHROMIUM_PROFILE_DIR:-${HOME}/.openclaw/workspace/chromium-profile}"
DEBUG_PORT="${CHROMIUM_DEBUG_PORT:-18801}"
LOG_FILE="${CHROMIUM_LOG_FILE:-${HOME}/.openclaw/workspace/logs/chromium.log}"

# Auto-detect browser binary
if [ -n "${CHROMIUM_BIN:-}" ]; then
  BROWSER="$CHROMIUM_BIN"
elif command -v chromium >/dev/null 2>&1; then
  BROWSER="chromium"
elif command -v chromium-browser >/dev/null 2>&1; then
  BROWSER="chromium-browser"
elif command -v google-chrome >/dev/null 2>&1; then
  BROWSER="google-chrome"
else
  echo "ERROR: No Chromium/Chrome binary found. Set CHROMIUM_BIN." >&2
  exit 1
fi

mkdir -p "$PROFILE_DIR"
mkdir -p "$(dirname "$LOG_FILE")"

# Stop previous Chromium launched with same profile
pkill -f "${BROWSER}.*--user-data-dir=.*${PROFILE_DIR##*/}" >/dev/null 2>&1 || true
sleep 0.5

# Remove stale SingletonLock if Chromium is not running
if ! pgrep -f "${BROWSER}.*--user-data-dir=.*${PROFILE_DIR##*/}" >/dev/null 2>&1; then
  rm -f "$PROFILE_DIR/SingletonLock"
fi

nohup "$BROWSER" \
  --remote-debugging-port="$DEBUG_PORT" \
  --user-data-dir="$PROFILE_DIR" \
  --no-first-run \
  --no-default-browser-check \
  --disable-sync \
  --disable-background-networking \
  --disable-component-update \
  --disable-features=Translate,MediaRouter \
  --disable-session-crashed-bubble \
  --hide-crash-restore-bubble \
  --password-store=basic \
  --headless=new \
  --disable-gpu \
  --no-sandbox \
  --disable-setuid-sandbox \
  --disable-dev-shm-usage \
  --disable-blink-features=AutomationControlled \
  --noerrdialogs \
  --ozone-platform=headless \
  --ozone-override-screen-size=800,600 \
  --use-angle=swiftshader-webgl \
  about:blank \
  >"$LOG_FILE" 2>&1 &

echo "Chromium started ($BROWSER)"
echo "Profile: $PROFILE_DIR"
echo "Debug URL: http://127.0.0.1:${DEBUG_PORT}/json/version"


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)