Skip to content

feat: upgrade browser skill to Puppeteer + Stealth for WAF bypass#11

Merged
guiramos merged 12 commits intoworkfrom
feat/browser-navigation-skill
Mar 23, 2026
Merged

feat: upgrade browser skill to Puppeteer + Stealth for WAF bypass#11
guiramos merged 12 commits intoworkfrom
feat/browser-navigation-skill

Conversation

@guiramos
Copy link
Copy Markdown

Replaces Playwright browser skill (PR #9) with Puppeteer + stealth plugin for WAF bypass.

Why

The original Playwright approach fails on sites with WAF protection (Imperva, Cloudflare, DataDome).

Test results on F1 Fantasy (Imperva WAF):

Changes

  • Dockerfile: New OPENCLAW_INSTALL_STEALTH_BROWSER build arg. Downloads Chrome for Testing + installs puppeteer-core, puppeteer-extra, puppeteer-extra-plugin-stealth
  • Skill: Rewritten from Playwright to Puppeteer + Stealth with ES module syntax

Build: docker build --build-arg OPENCLAW_INSTALL_STEALTH_BROWSER=1 .

guiramos added 12 commits March 23, 2026 14:16
Replace Playwright-based browser-navigation skill with Puppeteer +
puppeteer-extra-plugin-stealth. The stealth plugin applies ~12 evasion
techniques that pass WAFs (Imperva, Cloudflare, DataDome) which block
vanilla headless Chrome and Playwright.

Dockerfile changes:
- Add OPENCLAW_INSTALL_STEALTH_BROWSER build arg
- Installs Chrome for Testing at /opt/chrome/
- Installs puppeteer-core, puppeteer-extra, puppeteer-extra-plugin-stealth
- Installs required system libs for headless Chrome

Skill changes:
- Rewrite all examples from Playwright to Puppeteer + Stealth
- Document why stealth is needed (WAF evasion techniques)
- Update troubleshooting for common container issues
- Use ES module syntax (.mjs) throughout

Tested on F1 Fantasy (Imperva WAF):
- Playwright: blocked (CORS/network error on all XHR)
- agent-browser: blocked (same WAF)
- Puppeteer + Stealth: login + full navigation successful
- Copy docker/agent/ build files into fork (Option A: single build context)
- Source of truth: backend/docker/agent/Dockerfile, now lives in fork
- Remove Playwright/Chromium/Xvfb browser block from root Dockerfile
- Add Chrome for Testing + puppeteer-extra-plugin-stealth to agent Dockerfile (always installed)
- Agent Dockerfile verified against running container d6b0d06f35de:
  - butley-api extension: confirmed at dist/extensions/butley-api/
  - QMD symlink: /usr/local/lib/node_modules/@tobilu/qmd/bin/qmd
  - Workspace dir: /root/workspace
- Passes Imperva/Cloudflare WAFs that block vanilla headless Chrome
npm install -g puts packages in /usr/local/lib/node_modules/ which
Node can't resolve without NODE_PATH. Install in /opt/openclaw/ instead
so they're found via the existing node_modules resolution.
- Use cp -rn to merge ALL packages (including transitive deps like debug)
- Add NODE_PATH=/opt/openclaw/node_modules so scripts run from /root/ can resolve
- Add references/sample-stealth-navigation.mjs as working template
- Document createRequire() + absolute path pattern for ESM compatibility
- Update all code examples to use the correct import pattern
- Agents can copy the sample and adapt for their use case
- Add references/sample-browserbase-navigation.mjs template
- Document when to use local Chrome+Stealth vs Browserbase
- Browserbase uses residential IPs — passes banking sites (Chase, etc.)
- No stealth plugin needed — real browser, not detectable

Tested: Chase.com login succeeds via Browserbase (blocked via datacenter)
- Add @browserbasehq/sdk to Dockerfile (official SDK)
- Update sample to use SDK (session.create, session.debug)
- Document live debug URL and session replay
- Cleaner than raw fetch API calls
- Remove Chrome for Testing + system libs from Dockerfile (~250MB savings)
- Remove puppeteer-extra + stealth plugin (not needed with Browserbase)
- Keep only puppeteer-core + @browserbasehq/sdk
- BROWSERBASE_API_KEY passed as env var from orchestrator
- Skill rewritten for Browserbase-only workflow
- Cloud browser = residential IPs, no anti-detection needed
- Remove OPENCLAW_INSTALL_STEALTH_BROWSER block from root Dockerfile
- Remove Chrome for Testing, puppeteer-extra, stealth plugin (~250MB savings)
- Install only puppeteer-core + @browserbasehq/sdk in agent Dockerfile
- Rewrite skill for Browserbase-only workflow
- API key injected via BROWSERBASE_API_KEY env var (set by orchestrator)
@guiramos guiramos merged commit b7e1098 into work Mar 23, 2026
2 of 9 checks passed
guiramos added a commit that referenced this pull request Mar 29, 2026
- #1: Fix capabilities query param — use append() for repeated params instead of join()
- #4: Fix isConfigured — require hostname instead of registryUrl (default localhost OK)
- #5: Add deregister on stopAccount — store registryClient+agentId per account
- #7: Heartbeat re-registration — after 3 failures, deregister + re-register
- #10: Read pilotPort from config in resolveAccount
- #11: Remove unused execFile import from send.ts
- #12: Fix ENOENT race condition — use spawn event instead of setTimeout(500ms)
- #13: heartbeat checkReady — only require Pilot daemon if pilotPort configured
- #17: Verified daemon.ts imports — both spawn and execFile are used (no change)
- openclaw#18: Client reuse — store registryClient per account, reuse in gateway methods
- Also: Add API key support (registryApiKey config + X-Registry-Key header)
- Also: Add ok/error fields to SearchResult type
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant