rahmanef63/mso/camoufox-browse

Claude Code and agent skill by rahmanef63/mso

Trust tier 3
  • community
  • ext:[{"type":"plugin","description":"Custom plugin integration"}]
Source check pending

Checking that the SKILL.md file is still in the repository.

Anti-detection browsing with a real Firefox (Camoufox) when Cloudflare, Datadome, or fingerprinting blocks the built-in browser tool. Node + Python entry points, session persistence, residential proxies, human-like input. Use for authorized access to sites that flag standard automation.

Install

npx vskill@latest install rahmanef63/mso/camoufox-browse

Semi-extensibleMentions customization, but needs skill-specific knowledge. Read the extensible skills standard

SKILL.md

Camoufox Browse

Drive stealth / anti-fingerprinting browser sessions through Camoufox when a site's aggressive bot-detection or fingerprinting blocks the built-in browser tool and the user has confirmed automated access is permitted.

Two entry points — pick one:

Node (camoufox-js)Python (camoufox)
Installnpm install onlypip install + python3 -m camoufox fetch
Binaries requirednode, npmpython3
APIPlaywright (sync wrapper)Playwright (async or sync)
Statusexperimental (Apify port, v0.11.x)upstream original
Best forNode-first workspaces, CI, OpenClaw scriptslegacy scripts, Python-first stacks

Both expose the same Playwright API surface, so the rest of this skill applies to either.

Install — Node (recommended)

bash
# In your project directory (or globally with -g)
npm install playwright@1.60.0 camoufox-js
# First run auto-fetches the camoufox Firefox binary into node_modules

Headed mode also needs a display server:

  • Linux desktop: nothing extra — your existing X11/Wayland session works.
  • Headless server: install xvfb (apt install xvfb) and wrap in xvfb-run, or use headless: true.
⚠️ Pin `playwright@1.60.0`. Playwright 1.61+ sends a viewport.isMobile field that camoufox's bundled Firefox rejects on newPage(), breaking every page open.
Required patch — Playwright Firefox pageerror crash

playwright-core@1.60.0 + Firefox + camoufox has a bug: when a page raises an uncaught error without a location (common on heavy JS sites like TikTok, social feeds, ad-laden pages), the PageError dispatcher crashes the entire browser at coreBundle.js:49624 reading pageError.location.url. The browser process dies mid-session and you lose all pages and tabs.

Apply this patch to `node_modules/playwright-core/lib/coreBundle.js` after every `npm install`:

bash
# Idempotent — safe to re-run. Patches both occurrences (browser context + page).
python3 - <<'PY'
import re, pathlib
p = pathlib.Path("node_modules/playwright-core/lib/coreBundle.js")
src = p.read_text()
old = """ location: {
url: pageError.location.url,
line: pageError.location.lineNumber,
column: pageError.location.columnNumber
}"""
new = """ location: pageError.location ? {
url: pageError.location.url,
line: pageError.location.lineNumber,
column: pageError.location.columnNumber
} : { url: "", line: 0, column: 0 }"""
n = src.count(old)
if n != 2:
raise SystemExit(f"expected 2 occurrences, found {n} — playwright version may have changed")
p.write_text(src.replace(old, new))
print(f"patched {n} occurrences OK")
PY

Verify the patch is in place before any non-trivial run:

bash
grep -c "pageError.location ?" node_modules/playwright-core/lib/coreBundle.js # must print 2

Without this patch, multi-page sessions on TikTok / Instagram / ad-heavy sites will silently die after the first page error. The patch is local to node_modules — npm install overwrites it, hence "after every install."

Install — Python (fallback)

bash
# Use --break-system-packages on Debian/Ubuntu, or a venv
python3 -m pip install "camoufox[geoip]" playwright==1.60.0
# Fetch the camoufox browser binary (~700 MB) + uBlock Origin addon
python3 -m camoufox fetch

Same playwright==1.60.0 pin applies here. Headed mode requires DISPLAY or xvfb-run. The Python binding uses Playwright's async API directly and does not exhibit the same pageerror crash (as of camoufox 0.4.x); if the Node path is unstable, fall back to Python.

Headed setup — quick sanity check

Before any headed run, confirm X11 is actually running. A DISPLAY=:0 env var with no X server underneath is a common failure mode on freshly-booted VMs.

bash
echo "DISPLAY=$DISPLAY"
xdpyinfo -display "${DISPLAY:-:0}" 2>&1 | head -3
# Expect: "name of display: :0" + a vendor string
# If "xdpyinfo: unable to open display" → no X server, install xvfb or use headless.

If xdpyinfo reports a display, headed camoufox runs without xvfb. Otherwise start a virtual display (xvfb-run …) or set headless: true.

Quick Start — Node

js
// CommonJS
const { Camoufox } = require('camoufox-js');
(async () => {
const browser = await Camoufox({
headless: false, // false for visible window
humanize: true, // human-like mouse movement
geoip: true, // match timezone/locale to exit IP
locale: 'id-ID', // match target site
os: 'linux', // pin to keep fingerprint stable across runs
});
const page = await browser.newPage(); // Firefox Browser exposes newPage directly
await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
console.log(await page.title());
await browser.close();
})();
First run takes ~10-20s while camoufox-js fetches its bundled Firefox (~700 MB, cached in node_modules/camoufox-js/.cache after). Subsequent runs are fast.
Headed (visible window) — Node
js
const browser = await Camoufox({ headless: false, humanize: true, geoip: true });
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
// Keep window open until user closes it
await new Promise(r => browser.on('disconnected', r));
bash
# Headless server: provide a virtual display
xvfb-run node your_script.js

Quick Start — Python (fallback)

python
import asyncio
from camoufox import AsyncCamoufox
async def browse(url: str):
async with AsyncCamoufox(
headless=True, # set False for a visible window
humanize=True, # human-like mouse movement
geoip=True, # match timezone/locale to exit IP
) as browser:
page = await browser.new_page()
await page.goto(url, wait_until="domcontentloaded", timeout=30000)
return await page.title()
print(asyncio.run(browse("https://example.com")))

For synchronous scripts, from camoufox import Camoufox provides the same API without async/await.

Headed (visible window) — Python

Set headless=False to watch the browser drive itself. On a desktop it just works; on a headless server, wrap the script in xvfb-run.

python
import asyncio
from camoufox import AsyncCamoufox
async def browse_headed(url: str):
async with AsyncCamoufox(
headless=False, # visible window
humanize=True,
geoip=True,
) as browser:
page = await browser.new_page()
await page.goto(url, wait_until="domcontentloaded", timeout=30000)
input("Press Enter to close…") # keep the window open
asyncio.run(browse_headed("https://example.com"))
bash
# Headless server: provide a virtual display
xvfb-run python your_script.py

When to Use Camoufox vs the Built-in Browser Tool

Use this skillUse the built-in openclaw browser
Sites with aggressive bot detectionNormal page reads, documentation lookups
Tasks where fingerprint consistency matters (logged-in sessions across runs)Quick fetches, snapshots, form interactions
Anything where being identified as a bot would break the taskBulk page content extraction
User explicitly requests a real / anti-fingerprinting browser for an authorized taskWhen speed matters more than stealth

Rule of thumb: Only reach for Camoufox after the built-in tool has actually been blocked or detected, or when the user explicitly requests it for an authorized task. Default to the built-in tool otherwise.

Common Operations

Playwright API is identical in both languages; only syntax differs.

Read content
js
// Node
const content = await page.content(); // raw HTML
const text = await page.innerText('body'); // visible text
const title = await page.title();
await page.screenshot({ path: '/tmp/shot.png', fullPage: true });
python
# Python
content = await page.content()
text = await page.inner_text("body")
title = await page.title()
await page.screenshot(path="/tmp/shot.png", full_page=True)
Forms
js
// Node
await page.fill('input[name="email"]', 'user@example.com');
await page.fill('input[name="password"]', '...');
await page.click('button[type="submit"]');
await page.waitForLoadState('networkidle');
python
# Python
await page.fill('input[name="email"]', "user@example.com")
await page.fill('input[name="password"]', "...")
await page.click('button[type="submit"]')
await page.wait_for_load_state("networkidle")
Workflow patterns

Common multi-step operations. Python shown below; Node mirrors it with camelCase method names (waitForLoadState, querySelector, etc.).

Fill a whole form, then submit:

python
fields = {
'input[name="first"]': "Ada",
'input[name="last"]': "Lovelace",
'input[name="email"]': "ada@example.com",
}
for selector, value in fields.items():
await page.fill(selector, value)
await page.click('button[type="submit"]')
await page.wait_for_load_state("networkidle")

Navigate, wait, and snapshot in one go:

python
async def navigate_and_read(page, url):
await page.goto(url, wait_until="domcontentloaded", timeout=30000)
await page.wait_for_load_state("networkidle")
return await page.inner_text("body")

Scroll to reveal below-the-fold / lazy-loaded content:

python
async def scroll_to_bottom(page, steps=10, pause=0.5):
for _ in range(steps):
await page.mouse.wheel(0, 2000)
await page.wait_for_timeout(int(pause * 1000))
return await page.content() # now includes lazy-loaded items

Click through many elements sequentially:

python
selectors = ["#accept", "button.load-more", "a.next"]
for sel in selectors:
el = await page.query_selector(sel)
if el:
await el.click()
await page.wait_for_load_state("networkidle")
Persist a session
js
// Node — camoufox-js returns a Browser; use its default context, not browser.contexts()[0]
const fs = require('fs');
const browser = await Camoufox({ headless: false });
const context = browser.contexts()[0]; // or `await browser.newContext()` for a fresh one
const page = await context.newPage();
// ... do stuff ...
const state = await context.storageState();
fs.writeFileSync('/path/to/state.json', JSON.stringify(state));
await browser.close();
Note on the Node API shape: camoufox-js returns a Firefox Browser object. Browser.newPage() is available directly; for storage state, use browser.contexts()[0] (one default context is created at launch). The Python binding's browser.new_context() has no direct Node equivalent — use the default context or browser.newContext().
python
# Python
async with AsyncCamoufox(headless=True) as browser:
context = await browser.new_context(storage_state="/path/to/state.json")
# ...
state = await context.storage_state() # save for next run
Residential proxy
js
// Node
const browser = await Camoufox({
headless: true,
proxy: { server: 'http://resi.example.com:8000', username: 'u', password: '***' },
});
python
# Python
async with AsyncCamoufox(
headless=True,
proxy={"server": "http://resi.example.com:8000", "username": "u", "password": "***"},
) as browser:
...

Useful when a target rate-limits by IP. Only for sites you're authorized to access at volume.

Multi-page sessions — required error isolation

For any run that opens more than one page (scanning event listings, scraping search results, batch-checking URLs), you must isolate errors. Without this, one bad page will tear down the whole browser session.

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
95
Repository forks
15
7-day trend score
0

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Extension points

  • pluginCustom plugin integration

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/rahmanef63/mso/camoufox-browse/badge)](https://verified-skill.com/skills/rahmanef63/mso/camoufox-browse)

Something wrong with this skill or its page? Tell us.