Zero-token browser automation via Playwright scripts with CDP lock management and human-like interaction. Use when: (1) automating any browser-based workflow...
---
name: browser-automation-ultra
description: "Zero-token browser automation via Playwright scripts with CDP lock management and human-like interaction. Use when: (1) automating any browser-based workflow (publish, login, scrape, fill forms), (2) reducing token cost by converting browser-tool explorations into replayable scripts, (3) avoiding CDP port conflicts between OpenClaw browser and Playwright, (4) needing anti-detection/human-like mouse/keyboard behavior for platforms with bot detection. NOT for: simple URL fetches (use web_fetch instead), tasks that don't need a real browser session."
---
# Browser Automation Ultra
Explore → Record → Replay → Fix. Convert expensive browser-tool interactions into zero-token Playwright scripts that reuse OpenClaw's Chrome session (cookies/login intact).
## Prerequisites
Install Playwright (once per machine):
```bash
npm install -g playwright
# or in workspace: npm init -y && npm install playwright
```
No browser download needed — scripts connect to OpenClaw's existing Chrome via CDP.
## Architecture
```
Chrome user-data: ~/.openclaw/browser/openclaw/user-data
↕ shared cookies/login (mutually exclusive CDP)
┌──────────────┐ ┌──────────────────┐
│ browser tool │ OR │ Playwright script │
│ (explore) │ │ (zero token) │
└──────────────┘ └──────────────────┘
↕ managed by browser-lock.sh
```
Only one CDP client can connect at a time. `browser-lock.sh` handles the mutex.
## Setup
1. Copy `scripts/browser-lock.sh` to your workspace `scripts/` directory
2. Copy `scripts/utils/human-like.js` to your workspace `scripts/browser/utils/`
3. `chmod +x scripts/browser-lock.sh`
4. Create `scripts/browser/` for your automation scripts
## Core Workflow
### 1. Explore (browser tool, costs tokens)
Use the OpenClaw `browser` tool (snapshot/act) to figure out a workflow. Note selectors, page flow, key waits.
### 2. Record (write a Playwright script)
Convert steps into a script. Save to `scripts/browser/<verb>-<target>.js`. Use the template pattern:
```javascript
const { chromium } = require('playwright');
const { humanDelay, humanClick, humanType, humanThink, humanBrowse } = require('./utils/human-like');
function discoverCdpUrl() {
try {
const { execSync } = require('child_process');
const ps = execSync("ps aux | grep 'remote-debugging-port' | grep -v grep", { encoding: 'utf8' });
const match = ps.match(/remote-debugging-port=(\d+)/);
return `http://127.0.0.1:${match ? match[1] : '18800'}`;
} catch { return 'http://127.0.0.1:18800'; }
}
async function main() {
const browser = await chromium.connectOverCDP(discoverCdpUrl());
const context = browser.contexts()[0]; // reuse existing context (cookies/login)
const page = await context.newPage();
try {
// automation here — use human-like functions
await page.goto('https://example.com', { waitUntil: 'networkidle', timeout: 30000 });
await humanBrowse(page); // simulate looking at the page
await humanClick(page, 'button.submit');
await humanType(page, 'input[name="title"]', 'Hello World');
} finally {
await page.close(); // NEVER browser.close() — kills entire Chrome
}
}
main().then(() => process.exit(0)).catch(e => { console.error('❌', e.message); process.exit(1); });
```
### 3. Replay (zero tokens)
```bash
./scripts/browser-lock.sh run scripts/browser/my-task.js [args]
./scripts/browser-lock.sh run --timeout 120 scripts/browser/my-task.js
```
### 4. Fix (on error)
1. Read script error output
2. Re-explore the failing step with `browser` tool (snapshot) to check current UI
3. Update script with corrected selectors/logic
4. Retry
**Never guess fixes blindly. Always re-explore the actual page state.**
## browser-lock.sh
Manages CDP mutex between OpenClaw browser and Playwright scripts.
```bash
./scripts/browser-lock.sh run <script.js> [args] # acquire → run → release (300s default)
./scripts/browser-lock.sh run --timeout 120 <script> # custom timeout
./scripts/browser-lock.sh acquire # manual: stop OpenClaw browser, start Chrome
./scripts/browser-lock.sh release # manual: kill Chrome, release lock
./scripts/browser-lock.sh status # show state
```
Lock file: `/tmp/openclaw-browser.lock`. Stale locks auto-recover.
## Anti-Detection Rules (MANDATORY)
All scripts **must** use `human-like.js`. See [references/anti-detection.md](references/anti-detection.md) for the full rule set.
Summary of critical rules:
| ❌ Banned | ✅ Required |
|-----------|------------|
| `waitForTimeout(3000)` fixed delays | `humanDelay(2000, 4000)` random range |
| `input.fill(text)` instant fill | `humanType(page, sel, text)` char-by-char with typos |
| `element.click()` teleport click | `humanClick(page, sel)` bezier mouse path + hover |
| Direct page operation after load | `humanBrowse(page)` simulate reading first |
| `nativeSetter.call()` DOM injection | `humanType()` or `humanFillContentEditable()` |
| Fixed cron schedule | `jitterWait(1, 10)` random offset |
**Exception:** `setInputFiles()` for file uploads is allowed (no human simulation possible), but add random delays before/after.
## human-like.js API
| Function | Purpose |
|----------|---------|
| `humanDelay(min, max)` | Random wait (ms) |
| `humanThink(min, max)` | Longer pause before form fills |
| `humanClick(page, sel)` | Bezier mouse move → hover → click with press/release jitter |
| `humanType(page, sel, text, opts)` | Char-by-char typing, normal distribution speed, 3% typo rate |
| `humanFillContentEditable(page, sel, text)` | For contenteditable divs (line-by-line Enter + humanType) |
| `humanBrowse(page, opts)` | Simulate page reading (scroll + mouse wander, 2-5s) |
| `humanScroll(page, opts)` | Random scroll with occasional reverse |
| `jitterWait(minMin, maxMin)` | Random delay in minutes for cron tasks |
## Script Naming Convention
`<verb>-<target>.js` — e.g. `publish-deviantart.js`, `read-inbox.js`, `reply-comment.js`
## Example Scripts
Production-tested scripts in `scripts/examples/`. Copy to your workspace `scripts/browser/` and adapt.
| Script | Platform | Function |
|--------|----------|----------|
| `publish-deviantart.js` | DeviantArt | Upload image, fill title/desc/tags, submit |
| `publish-xiaohongshu.js` | 小红书 | Publish image note with topic tag association via recommend list |
| `publish-pinterest.js` | Pinterest | Create pin with title/desc, select board |
| `publish-behance.js` | Behance | Upload project with title/desc/tags/categories |
| `read-proton-latest.js` | Proton Mail | Read inbox, output JSON list of emails |
| `read-xhs-comments.js` | 小红书 | Read notification comments, output JSON with reply button index |
| `reply-xhs-comment.js` | 小红书 | Reply to a specific comment by index |
Usage pattern:
```bash
# Copy examples to workspace
cp scripts/examples/*.js scripts/browser/
cp scripts/utils/human-like.js scripts/browser/utils/
# Run
./scripts/browser-lock.sh run scripts/browser/publish-deviantart.js image.png "Title" "Description" "tag1,tag2"
./scripts/browser-lock.sh run scripts/browser/read-xhs-comments.js --limit 10
./scripts/browser-lock.sh run scripts/browser/reply-xhs-comment.js 0 "回复文字"
```
All example scripts already use `human-like.js` for anti-detection.
## Cron Integration
```bash
cd /path/to/workspace && ./scripts/browser-lock.sh run scripts/browser/task.js
```
Add `jitterWait()` at script start to randomize execution time.
## Troubleshooting
| Problem | Fix |
|---------|-----|
| Lock held by PID xxx | `./scripts/browser-lock.sh release` |
| CDP connection timeout | Ensure `acquire` was called / Chrome is running |
| Login expired | Use browser tool to re-login, then run script |
| Selector not found | Re-explore with browser tool, update script |
| Script timeout | Increase with `--timeout` flag |
## Environment Variables
| Var | Default | Description |
|-----|---------|-------------|
| `CDP_PORT` | auto-discover | Override CDP port |
| `CHROME_BIN` | auto-detect | Chrome binary path |
| `HEADLESS` | auto | `true`/`false` to force headless |
don't have the plugin yet? install it then click "run inline in claude" again.
added explicit inputs (CDP port discovery, lock file, env vars), broke procedure into 5 numbered steps with detailed sub-steps, extracted decision logic for CDP failure/lock stale/login expiry/selector missing/timeout/file upload/bot detection, defined output contract with JSON schema examples, and clarified outcome signals for success and failure paths.
Convert expensive browser-tool interactions into zero-token Playwright scripts that reuse OpenClaw's Chrome session (cookies/login intact). Use this skill when automating any browser-based workflow (publish, login, scrape, fill forms), reducing token cost by converting explorations into replayable scripts, avoiding CDP port conflicts between OpenClaw browser and Playwright, or needing anti-detection/human-like mouse/keyboard behavior for platforms with bot detection. Skip this if you need simple URL fetches (use web_fetch instead) or tasks that don't require a real browser session.
Required installations (one-time per machine):
npm install -g playwright or npm install playwright in workspaceRequired workspace files:
scripts/browser-lock.sh (mutex manager for CDP access)scripts/utils/human-like.js (anti-detection library)scripts/browser/ directory for your automation scriptsExternal connections:
~/.openclaw/browser/openclaw/user-data (shared cookies/login state)Environment variables (optional):
CDP_PORT: override auto-discovered CDP port (default: auto-discover from ps aux)CHROME_BIN: override Chrome binary path (default: auto-detect)HEADLESS: force headless mode true/false (default: auto)Lock file:
/tmp/openclaw-browser.lock manages mutex; auto-recovers from stale locksSetup (first time):
scripts/browser-lock.sh to your workspace scripts/ directoryscripts/utils/human-like.js to your workspace scripts/browser/utils/chmod +x scripts/browser-lock.shscripts/browser/ directory for automation scriptsExplore phase (using browser tool, costs tokens):
browser tool with snapshot/act to understand the workflowRecord phase (write Playwright script):
scripts/browser/<verb>-<target>.js (e.g. publish-deviantart.js, read-inbox.js)const { chromium } = require('playwright'); const { humanDelay, humanClick, humanType, humanThink, humanBrowse } = require('./utils/human-like');ps aux output for remote-debugging-port or fallback to http://127.0.0.1:18800const browser = await chromium.connectOverCDP(cdpUrl);const context = browser.contexts()[0];const page = await context.newPage();await page.close(); in finally block (never browser.close() which kills entire Chrome)Replay phase (zero tokens):
./scripts/browser-lock.sh run scripts/browser/my-task.js [args]--timeout flag to override default 300 seconds: ./scripts/browser-lock.sh run --timeout 120 scripts/browser/my-task.jsFix phase (on error):
./scripts/browser-lock.sh runIf CDP connection fails:
ps aux output for remote-debugging-portCDP_PORT env var or ensure OpenClaw browser is running./scripts/browser-lock.sh acquireIf lock is held by stale PID:
/tmp/openclaw-browser.lock to see which process owns it./scripts/browser-lock.sh release to force releaseIf login expires during script run:
If selector is not found:
If script exceeds timeout:
--timeout flag for longer tasks: --timeout 600 for 10 minuteshumanBrowse() or humanDelay() calls to identify slow page loadsIf file upload is needed:
setInputFiles() is allowed (no human simulation possible for native file picker)humanDelay()If platform has aggressive bot detection:
human-like.js functions for all interactionshumanBrowse(page) after page load to simulate readinghumanClick() instead of direct click for all buttons/linkshumanType() for all text input (includes 3% typo simulation)humanDelay(min, max) instead of fixed waitForTimeout()nativeSetter.call())Script execution success:
console.log(JSON.stringify(results)))fs.writeFileSync('output.json', JSON.stringify(data)))Example output format for read/scrape tasks:
{
"status": "success",
"items": [
{ "selector_index": 0, "text": "item title", "href": "https://..." }
],
"count": 5,
"timestamp": "2025-01-15T12:34:56Z"
}
Example output format for publish/action tasks:
{
"status": "success",
"url": "https://platform.com/published/item-id",
"timestamp": "2025-01-15T12:34:56Z"
}
Failure contract:
❌ Selector 'button.submit' not found after 30s timeout./scripts/browser-lock.sh run and completes with exit code 0