Skill: jarvis563/stable-browser (version 1.0.0)

--- name: stable-browser description: Set up reliable browser automation using Chrome DevTools Protocol (CDP) instead of the flaky browser extension relay. Use when browser relay keeps disconnecting, throwing WebSocket 403 errors, or when you need stable headless/headed browser control for web scraping, form filling, social media posting, or any browser automation task. Replaces profile="chrome" with a rock-solid CDP connection. --- # Stable Browser Replace the unreliable browser extension relay with a direct Chrome DevTools Protocol connection. ## The Problem The OpenClaw browser extension relay (`profile="chrome"`) frequently breaks: - WebSocket 403 errors - Port confusion (gateway port vs relay port) - Dropped connections mid-automation - "Can't reach browser control service" errors - Badge/tab attachment confusion ## The Fix: Chrome CDP Launch Chrome with a debug port and connect directly. No extension needed. ### Quick Setup Run the setup script to configure everything: ```bash bash scripts/setup-cdp.sh ``` This will: 1. Create a dedicated Chrome profile at `~/.chrome-debug-profile` 2. Add `browser.cdpUrl` to your OpenClaw config 3. Create a LaunchAgent (macOS) so Chrome starts on login 4. Verify the connection works ### Manual Setup If you prefer to set things up manually, see [references/manual-setup.md](references/manual-setup.md). ### Usage After setup, always use `profile="openclaw"` (not `profile="chrome"`): ``` browser(action="snapshot", profile="openclaw") browser(action="navigate", profile="openclaw", targetUrl="https://example.com") browser(action="screenshot", profile="openclaw") ``` ### Key Differences from Extension Relay | Feature | Extension Relay | CDP Direct | |---------|----------------|------------| | Stability | Frequent disconnects | Rock solid | | Setup | Install extension + attach tab | One-time script | | Auth/Cookies | Shares your main Chrome | Dedicated profile | | Speed | Extra hop through extension | Direct protocol | | Headless | No | Optional (`--headless=new`) | ### Dedicated Profile The CDP browser uses `~/.chrome-debug-profile` — a separate Chrome profile. This means: - Log into sites once, stays logged in - Your main Chrome is untouched - No extension conflicts - Survives Chrome updates ### Tips - **First run**: Log into any sites you need (Google, GitHub, X, LinkedIn, etc.) - **Multiple tabs**: CDP manages all tabs — use `targetId` to pin a specific tab - **Headless mode**: Add `--headless=new` to the launch command for invisible operation - **Port conflict**: If port 9222 is taken, change it in both the launch command and config - **Restart Chrome**: `pkill -f 'remote-debugging-port=9222' && sleep 1 && bash scripts/setup-cdp.sh` ### Troubleshooting - **"Can't reach browser"**: Chrome isn't running with debug port. Run `setup-cdp.sh` or launch manually - **Port 9222 in use**: Another Chrome or process grabbed it. Kill it: `lsof -i :9222` - **Stale session**: Chrome crashed. Kill and restart: `pkill -f chrome-debug-profile` - **Profile corruption**: Delete `~/.chrome-debug-profile` and re-run setup

path: references/manual-setup.md
content: # Manual Setup

## 1. Launch Chrome with CDP

```bash
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
    --remote-debugging-port=9222 \
    --user-data-dir=$HOME/.chrome-debug-profile \
    --no-first-run \
    --no-default-browser-check &
```

**Linux:**
```bash
google-chrome \
    --remote-debugging-port=9222 \
    --user-data-dir=$HOME/.chrome-debug-profile \
    --no-first-run &
```

**Windows (PowerShell):**
```powershell
& "C:\Program Files\Google\Chrome\Application\chrome.exe" `
    --remote-debugging-port=9222 `
    --user-data-dir="$env:USERPROFILE\.chrome-debug-profile" `
    --no-first-run
```

## 2. Verify

```bash
curl http://127.0.0.1:9222/json/version
```

Should return JSON with Chrome version info.

## 3. Configure OpenClaw

Add to `~/.openclaw/openclaw.json`:

```json
{
  "browser": {
    "cdpUrl": "http://127.0.0.1:9222"
  }
}
```

Or via CLI:
```bash
openclaw config set browser.cdpUrl http://127.0.0.1:9222
```

## 4. Test

In your OpenClaw session:
```
browser(action="snapshot", profile="openclaw")
```

## Headless Mode

For server/CI environments, add `--headless=new`:

```bash
google-chrome \
    --headless=new \
    --remote-debugging-port=9222 \
    --user-data-dir=$HOME/.chrome-debug-profile \
    --no-sandbox \
    --disable-gpu &
```

## Custom Port

If 9222 is taken, use any free port:

```bash
# Launch on 9333
google-chrome --remote-debugging-port=9333 --user-data-dir=$HOME/.chrome-debug-profile &

# Update config
# browser.cdpUrl: http://127.0.0.1:9333
```

## Auto-Start (Linux systemd)

```ini
# ~/.config/systemd/user/chrome-cdp.service
[Unit]
Description=Chrome CDP for OpenClaw

[Service]
ExecStart=/usr/bin/google-chrome --headless=new --remote-debugging-port=9222 --user-data-dir=%h/.chrome-debug-profile --no-sandbox --disable-gpu
Restart=on-failure

[Install]
WantedBy=default.target
```

```bash
systemctl --user enable chrome-cdp
systemctl --user start chrome-cdp
```


path: scripts/setup-cdp.sh
content: #!/bin/bash
# stable-browser: Set up Chrome CDP for reliable OpenClaw browser automation
# Replaces the flaky extension relay with a direct DevTools Protocol connection

set -e

CHROME_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
PROFILE_DIR="$HOME/.chrome-debug-profile"
CDP_PORT=9222
OPENCLAW_CONFIG="$HOME/.openclaw/openclaw.json"
PLIST_PATH="$HOME/Library/LaunchAgents/com.openclaw.chrome-cdp.plist"

echo "🔧 Setting up Chrome CDP for OpenClaw..."
echo ""

# 1. Check Chrome exists
if [ ! -f "$CHROME_PATH" ]; then
    echo "❌ Google Chrome not found at $CHROME_PATH"
    echo "   Install Chrome first: https://www.google.com/chrome/"
    exit 1
fi
echo "✅ Chrome found"

# 2. Create profile directory
mkdir -p "$PROFILE_DIR"
echo "✅ Profile directory: $PROFILE_DIR"

# 3. Kill any existing debug Chrome
if lsof -i :$CDP_PORT >/dev/null 2>&1; then
    echo "⚠️  Port $CDP_PORT in use — killing existing process..."
    pkill -f "remote-debugging-port=$CDP_PORT" 2>/dev/null || true
    sleep 2
fi

# 4. Launch Chrome with CDP
echo "🚀 Launching Chrome with CDP on port $CDP_PORT..."
"$CHROME_PATH" \
    --remote-debugging-port=$CDP_PORT \
    --user-data-dir="$PROFILE_DIR" \
    --no-first-run \
    --no-default-browser-check \
    --disable-background-timer-throttling \
    --disable-backgrounding-occluded-windows \
    --disable-renderer-backgrounding \
    &>/dev/null &

CHROME_PID=$!
sleep 3

# 5. Verify CDP is responding
if curl -s "http://127.0.0.1:$CDP_PORT/json/version" >/dev/null 2>&1; then
    echo "✅ CDP responding on port $CDP_PORT"
else
    echo "❌ CDP not responding. Chrome may have failed to start."
    echo "   Try launching manually:"
    echo "   \"$CHROME_PATH\" --remote-debugging-port=$CDP_PORT --user-data-dir=\"$PROFILE_DIR\""
    exit 1
fi

# 6. Update OpenClaw config
if [ -f "$OPENCLAW_CONFIG" ]; then
    if python3 -c "
import json, sys
with open('$OPENCLAW_CONFIG') as f:
    config = json.load(f)
if config.get('browser', {}).get('cdpUrl') == 'http://127.0.0.1:$CDP_PORT':
    sys.exit(0)
config.setdefault('browser', {})['cdpUrl'] = 'http://127.0.0.1:$CDP_PORT'
with open('$OPENCLAW_CONFIG', 'w') as f:
    json.dump(config, f, indent=2)
print('updated')
" 2>/dev/null | grep -q "updated"; then
        echo "✅ OpenClaw config updated (browser.cdpUrl)"
    else
        echo "✅ OpenClaw config already set"
    fi
else
    echo "⚠️  OpenClaw config not found at $OPENCLAW_CONFIG"
    echo "   Add manually: \"browser\": { \"cdpUrl\": \"http://127.0.0.1:$CDP_PORT\" }"
fi

# 7. Create LaunchAgent for auto-start (macOS only)
if [ "$(uname)" = "Darwin" ]; then
    mkdir -p "$(dirname "$PLIST_PATH")"
    cat > "$PLIST_PATH" << PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.openclaw.chrome-cdp</string>
    <key>ProgramArguments</key>
    <array>
        <string>$CHROME_PATH</string>
        <string>--remote-debugging-port=$CDP_PORT</string>
        <string>--user-data-dir=$PROFILE_DIR</string>
        <string>--no-first-run</string>
        <string>--no-default-browser-check</string>
        <string>--disable-background-timer-throttling</string>
        <string>--disable-backgrounding-occluded-windows</string>
        <string>--disable-renderer-backgrounding</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <dict>
        <key>Crashed</key>
        <true/>
    </dict>
    <key>StandardOutPath</key>
    <string>/tmp/chrome-cdp.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/chrome-cdp.err</string>
</dict>
</plist>
PLIST
    launchctl unload "$PLIST_PATH" 2>/dev/null || true
    launchctl load "$PLIST_PATH" 2>/dev/null || true
    echo "✅ LaunchAgent created (auto-starts on login, restarts on crash)"
fi

# 8. Print summary
VERSION=$(curl -s "http://127.0.0.1:$CDP_PORT/json/version" | python3 -c "import sys,json; print(json.load(sys.stdin).get('Browser','unknown'))" 2>/dev/null)
echo ""
echo "═══════════════════════════════════════════════════"
echo "  ✅ Chrome CDP ready!"
echo "  Browser: $VERSION"
echo "  CDP URL: http://127.0.0.1:$CDP_PORT"
echo "  Profile: $PROFILE_DIR"
echo ""
echo "  Use in OpenClaw: profile=\"openclaw\""
echo "  NOT: profile=\"chrome\" (that's the old relay)"
echo "═══════════════════════════════════════════════════"
echo ""
echo "💡 First time? Log into your accounts in the CDP"
echo "   Chrome window (it's the one without your extensions)."


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