🧪 Skills

Browser Playwright Bridge

Run Playwright scripts that reuse OpenClaw browser's login state via CDP with automatic lock-based conflict prevention.

v1.1.0
❤️ 0
⬇️ 260
👁 1
Share

Description


name: browser-playwright-bridge description: Run Playwright scripts that share OpenClaw browser's login state via CDP, with automatic conflict avoidance. Use when: (1) recording browser tool operations as reusable Playwright scripts, (2) running headless automation that needs existing cookies/sessions, (3) scheduling browser tasks in cron without CDP conflicts, (4) converting exploratory browser tool workflows into zero-token repeatable scripts.

Browser ↔ Playwright Bridge

OpenClaw's browser tool and external Playwright scripts cannot share the same CDP connection simultaneously. This skill provides a lock-based bridge: stop OpenClaw browser → run Playwright with the same Chrome profile (cookies/login intact) → release for OpenClaw to reconnect.

Architecture

Chrome (CDP port)  ←  shared user-data-dir (~/.openclaw/browser/openclaw/user-data)
       ↕ mutually exclusive
┌──────────────┐    ┌──────────────────┐
│ OpenClaw     │ OR │ Playwright script │
│ browser tool │    │ (zero token cost) │
└──────────────┘    └──────────────────┘
       ↕ managed by browser-lock.sh

Setup

  1. Install Playwright in the workspace (once):
cd <workspace> && npm install playwright

Note: npx playwright install is NOT needed. Playwright connects to the existing Chrome via CDP — no local browser download required.

  1. Copy scripts/browser-lock.sh to your workspace scripts/ directory:
chmod +x scripts/browser-lock.sh

Discovering the CDP Port

The CDP port is dynamically assigned. Never hardcode it. Use discoverCdpUrl() (see below) or the shell equivalent in browser-lock.sh.

Shell one-liner:

ps aux | grep 'remote-debugging-port=' | grep -v grep | grep -o 'remote-debugging-port=[0-9]*' | head -1 | cut -d= -f2

Verify CDP is responding:

curl -s --max-time 1 http://127.0.0.1:<port>/json/version

Usage

Run a Playwright script (recommended)

./scripts/browser-lock.sh run scripts/my-task.js [args...]
./scripts/browser-lock.sh run --timeout 120 scripts/my-task.js  # custom timeout

Default timeout: 300s. If the script exceeds it, the watchdog kills it and releases the lock.

This automatically: checks lock → stops OpenClaw browser → starts Chrome with CDP → runs script → cleans up → releases lock.

Manual acquire/release

./scripts/browser-lock.sh acquire    # stop OpenClaw browser, start Chrome
node scripts/my-task.js              # run script(s)
./scripts/browser-lock.sh release    # kill Chrome, release lock

Check status

./scripts/browser-lock.sh status

Writing Playwright Scripts

Use scripts/playwright-template.js as starting point.

CDP Discovery Helper

All scripts should use discoverCdpUrl() instead of hardcoding a port:

const { execSync } = require('child_process');

/**
 * Discover the CDP URL by inspecting Chrome process args.
 * Falls back to CDP_PORT env var, then probes common ports.
 */
function discoverCdpUrl() {
  // Method 1: extract from running Chrome process
  try {
    const ps = execSync(
      "ps aux | grep 'remote-debugging-port=' | grep -v grep",
      { encoding: 'utf8', timeout: 3000 }
    );
    const match = ps.match(/remote-debugging-port=(\d+)/);
    if (match) return `http://127.0.0.1:${match[1]}`;
  } catch {}

  // Method 2: CDP_PORT env var
  if (process.env.CDP_PORT) {
    return `http://127.0.0.1:${process.env.CDP_PORT}`;
  }

  // Method 3: probe common ports
  // 18800 is the typical OpenClaw default; others are common CDP conventions
  const { execSync: probe } = require('child_process');
  for (const port of [18800, 9222, 9229]) {
    try {
      probe(`curl -s --max-time 1 http://127.0.0.1:${port}/json/version`, {
        encoding: 'utf8', timeout: 2000
      });
      return `http://127.0.0.1:${port}`;
    } catch {}
  }

  throw new Error('CDP port not found. Is Chrome running with --remote-debugging-port?');
}

Script Pattern

const { chromium } = require('playwright');

async function main() {
  let browser;
  try {
    browser = await chromium.connectOverCDP(discoverCdpUrl());
  } catch (e) {
    console.error('❌ Cannot connect to Chrome CDP:', e.message);
    console.error('   Ensure browser-lock.sh acquire was called, or Chrome is running with --remote-debugging-port');
    process.exit(1);
  }

  const context = browser.contexts()[0]; // reuse existing context (cookies!)
  const page = await context.newPage();

  try {
    // ====== Your automation here ======
    await page.goto('https://example.com');
    console.log('Title:', await page.title());
    // ==================================
  } catch (e) {
    console.error('❌ Script error:', e.message);
    throw e;
  } finally {
    await page.close();     // close only your tab
    // NEVER call browser.close() — it kills the entire Chrome
  }
}

main().then(() => process.exit(0)).catch(e => {
  console.error('❌', e.message);
  process.exit(1);
});

Critical rules:

  • browser.contexts()[0] — reuse the existing context to inherit cookies/login
  • page.close() only — never browser.close()
  • Always process.exit(0) on success — Playwright keeps event loops alive otherwise
  • Wrap connectOverCDP in try-catch — fail fast with a clear message

Workflow: Explore → Record → Replay

  1. Explore — Use OpenClaw browser tool (snapshot/act) to figure out a new workflow
  2. Record — Ask the agent to convert the steps into a Playwright script
  3. Replay — Run via browser-lock.sh run — zero token cost, deterministic

Cron / Scheduled Tasks

In cron tasks, call browser-lock.sh directly:

cd /path/to/workspace && ./scripts/browser-lock.sh run scripts/publish-task.js

The lock file (/tmp/openclaw-browser.lock) prevents concurrent browser access. If a lock is stale (owner process dead), it auto-recovers.

Troubleshooting

Problem Fix
Lock held by PID xxx ./scripts/browser-lock.sh release to force-release
Playwright connectOverCDP timeout Ensure OpenClaw browser is stopped first (acquire does this)
CDP port not found Chrome isn't running; call browser-lock.sh acquire first
openclaw browser stop doesn't kill Chrome Known issue; browser-lock.sh kills the process directly
Script hangs after completion Add process.exit(0) at the end
Login expired Use OpenClaw browser tool to re-login, then run scripts again

Environment Variables

Var Default Description
CDP_PORT auto-discover Override CDP port (skips process detection)
CHROME_BIN auto-detect Path to Chrome/Chromium binary
HEADLESS auto Set true/1 to force headless; false/0 to force headed. Auto-detects on Linux without DISPLAY

Reviews (0)

Sign in to write a review.

No reviews yet. Be the first to review!

Comments (0)

Sign in to join the discussion.

No comments yet. Be the first to share your thoughts!

Compatible Platforms

Pricing

Free

Related Configs