mirror of
https://github.com/CloakHQ/CloakBrowser.git
synced 2026-06-23 11:41:46 +02:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f91700c4a4 | ||
|
|
1380c86847 | ||
|
|
83e3b30117 | ||
|
|
5649620545 |
@@ -107,6 +107,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
VERSION=$(python -c 'import re; print(re.search(r"__version__\s*=\s*[\"'\'']([^\"'\'']+)", open("cloakbrowser/_version.py").read()).group(1))')
|
VERSION=$(python -c 'import re; print(re.search(r"__version__\s*=\s*[\"'\'']([^\"'\'']+)", open("cloakbrowser/_version.py").read()).group(1))')
|
||||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
- uses: docker/setup-qemu-action@v3
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v3
|
||||||
- uses: docker/login-action@v3
|
- uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -117,6 +118,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
cloakhq/cloakbrowser:${{ env.VERSION }}
|
cloakhq/cloakbrowser:${{ env.VERSION }}
|
||||||
|
|||||||
@@ -6,6 +6,17 @@ Changes are tagged: **[wrapper]** for Python/JS wrapper, **[binary]** for Chromi
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [0.3.17] — 2026-03-15
|
||||||
|
|
||||||
|
- **[binary]** Windows x64 build upgraded to 145.0.7632.159.7 — 33 source-level C++ patches, matching Linux
|
||||||
|
- **[wrapper]** Auto-inject GPU blocklist bypass for headed mode and Windows — fixes WebGL/WebGPU on software GPUs in Docker/VNC (fixes #56)
|
||||||
|
- **[wrapper]** Add 8 framework integration examples (Scrapy, Crawlee, BrowserBase, etc.) and README integrations section
|
||||||
|
|
||||||
|
## [0.3.16] — 2026-03-14
|
||||||
|
|
||||||
|
- **[binary]** Linux arm64 build available — Raspberry Pi, AWS Graviton, Oracle Ampere now supported
|
||||||
|
- **[wrapper]** Add donate link to first-launch welcome banner
|
||||||
|
|
||||||
## [0.3.15] — 2026-03-13
|
## [0.3.15] — 2026-03-13
|
||||||
|
|
||||||
- **[binary]** Upgrade Linux build to 145.0.7632.159.7 — 33 source-level C++ patches
|
- **[binary]** Upgrade Linux build to 145.0.7632.159.7 — 33 source-level C++ patches
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ Open [http://localhost:8080](http://localhost:8080). Create a profile. Click **L
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Latest: v0.3.15 (Chromium 145.0.7632.159.7)
|
## Latest: v0.3.17 (Chromium 145.0.7632.159.7)
|
||||||
|
|
||||||
- **`humanize=True`** — one flag makes all mouse, keyboard, and scroll interactions behave like a real user. Bézier curves, per-character typing, realistic scroll patterns. Two presets: `default` and `careful`
|
- **`humanize=True`** — one flag makes all mouse, keyboard, and scroll interactions behave like a real user. Bézier curves, per-character typing, realistic scroll patterns. Two presets: `default` and `careful`
|
||||||
- **CDP input behavior mimicking** — input events sent via CDP now produce the same signals as real user interactions. 5 new source-level patches covering pointer, keyboard, and mouse behavior
|
- **CDP input behavior mimicking** — input events sent via CDP now produce the same signals as real user interactions. 5 new source-level patches covering pointer, keyboard, and mouse behavior
|
||||||
@@ -147,7 +147,7 @@ See the full [CHANGELOG.md](CHANGELOG.md) for details.
|
|||||||
- **CloakBrowser patches Chromium source code** — fingerprints are modified at the C++ level, compiled into the binary. Detection sites see a real browser because it *is* a real browser.
|
- **CloakBrowser patches Chromium source code** — fingerprints are modified at the C++ level, compiled into the binary. Detection sites see a real browser because it *is* a real browser.
|
||||||
- **Source-level stealth** — C++ patches handle fingerprints (GPU, screen, UA, hardware reporting) at the binary level. No JavaScript injection, no config-level hacks. Most stealth tools only patch at the surface.
|
- **Source-level stealth** — C++ patches handle fingerprints (GPU, screen, UA, hardware reporting) at the binary level. No JavaScript injection, no config-level hacks. Most stealth tools only patch at the surface.
|
||||||
- **Same behavior everywhere** — works identically local, in Docker, and on VPS. No environment-specific patches or config needed.
|
- **Same behavior everywhere** — works identically local, in Docker, and on VPS. No environment-specific patches or config needed.
|
||||||
- **Works with AI agents and automation frameworks** — drop-in stealth for browser-use, Crawl4AI, agent-browser, Claude computer use, and OpenAI Operator. Also tested with Playwright, Puppeteer, and Selenium — point any Chromium-based framework at the binary path.
|
- **Works with AI agents and automation frameworks** — drop-in stealth for browser-use, Crawl4AI, Scrapling, Stagehand, LangChain, Selenium, and more. See [integrations](#framework-integrations).
|
||||||
|
|
||||||
CloakBrowser doesn't solve CAPTCHAs — it prevents them from appearing. No CAPTCHA-solving services, no proxy rotation built in — bring your own proxies, use the Playwright API you already know.
|
CloakBrowser doesn't solve CAPTCHAs — it prevents them from appearing. No CAPTCHA-solving services, no proxy rotation built in — bring your own proxies, use the Playwright API you already know.
|
||||||
|
|
||||||
@@ -610,14 +610,43 @@ browser = launch(args=[
|
|||||||
- [`basic-puppeteer.ts`](js/examples/basic-puppeteer.ts) — Puppeteer launch and load
|
- [`basic-puppeteer.ts`](js/examples/basic-puppeteer.ts) — Puppeteer launch and load
|
||||||
- [`stealth-test.ts`](js/examples/stealth-test.ts) — Run against 6 detection sites
|
- [`stealth-test.ts`](js/examples/stealth-test.ts) — Run against 6 detection sites
|
||||||
|
|
||||||
|
### Framework Integrations
|
||||||
|
|
||||||
|
CloakBrowser works with any framework that uses Playwright or Chromium:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Option 1: Framework launches our binary directly (Selenium, Stagehand, UC)
|
||||||
|
from cloakbrowser.download import ensure_binary
|
||||||
|
from cloakbrowser.config import get_default_stealth_args
|
||||||
|
binary_path = ensure_binary() # auto-downloads if needed
|
||||||
|
stealth_args = get_default_stealth_args() # all fingerprint flags
|
||||||
|
|
||||||
|
# Option 2: CloakBrowser launches first, framework connects via CDP (browser-use, Crawl4AI, Scrapling)
|
||||||
|
from cloakbrowser import launch_async
|
||||||
|
browser = await launch_async(args=["--remote-debugging-port=9242"])
|
||||||
|
# Connect your framework to http://127.0.0.1:9242 — all stealth flags are set
|
||||||
|
```
|
||||||
|
|
||||||
|
| Framework | Stars | Language | Example |
|
||||||
|
|-----------|-------|----------|---------|
|
||||||
|
| [browser-use](https://github.com/browser-use/browser-use) | 70K | Python | [`browser_use_example.py`](examples/integrations/browser_use_example.py) |
|
||||||
|
| [Crawl4AI](https://github.com/unclecode/crawl4ai) | 58K | Python | [`crawl4ai_example.py`](examples/integrations/crawl4ai_example.py) |
|
||||||
|
| [Scrapling](https://github.com/D4Vinci/Scrapling) | 21K | Python | [`scrapling_example.py`](examples/integrations/scrapling_example.py) |
|
||||||
|
| [Stagehand](https://github.com/browserbase/stagehand) | 21K | TypeScript | [`stagehand.ts`](js/examples/stagehand.ts) |
|
||||||
|
| [LangChain](https://github.com/langchain-ai/langchain) | 100K+ | Python | [`langchain_loader.py`](examples/integrations/langchain_loader.py) |
|
||||||
|
| [Selenium](https://github.com/SeleniumHQ/selenium) | — | Python | [`selenium_example.py`](examples/integrations/selenium_example.py) |
|
||||||
|
| [undetected-chromedriver](https://github.com/ultrafunkamsterdam/undetected-chromedriver) | 12K | Python | [`undetected_chromedriver.py`](examples/integrations/undetected_chromedriver.py) |
|
||||||
|
| [agent-browser](https://github.com/nichochar/agent-browser) | — | Shell | [`agent_browser.sh`](examples/integrations/agent_browser.sh) |
|
||||||
|
|
||||||
## Platforms
|
## Platforms
|
||||||
|
|
||||||
| Platform | Chromium | Patches | Status |
|
| Platform | Chromium | Patches | Status |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Linux x86_64 | 145 | 31 | ✅ Latest |
|
| Linux x86_64 | 145 | 33 | ✅ Latest |
|
||||||
|
| Linux arm64 (RPi, Graviton) | 145 | 33 | ✅ Latest |
|
||||||
| macOS arm64 (Apple Silicon) | 145 | 26 | ✅ Latest |
|
| macOS arm64 (Apple Silicon) | 145 | 26 | ✅ Latest |
|
||||||
| macOS x86_64 (Intel) | 145 | 26 | ✅ Latest |
|
| macOS x86_64 (Intel) | 145 | 26 | ✅ Latest |
|
||||||
| Windows x86_64 | 145 | 26 | ✅ Latest |
|
| Windows x86_64 | 145 | 33 | ✅ Latest |
|
||||||
|
|
||||||
The wrapper auto-downloads the correct binary for your platform.
|
The wrapper auto-downloads the correct binary for your platform.
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.3.15"
|
__version__ = "0.3.17"
|
||||||
|
|||||||
+15
-4
@@ -102,7 +102,7 @@ def launch(
|
|||||||
|
|
||||||
binary_path = ensure_binary()
|
binary_path = ensure_binary()
|
||||||
timezone, locale = _maybe_resolve_geoip(geoip, proxy, timezone, locale)
|
timezone, locale = _maybe_resolve_geoip(geoip, proxy, timezone, locale)
|
||||||
chrome_args = _build_args(stealth_args, args, timezone=timezone, locale=locale)
|
chrome_args = _build_args(stealth_args, args, timezone=timezone, locale=locale, headless=headless)
|
||||||
|
|
||||||
logger.debug("Launching stealth Chromium (headless=%s, args=%d)", headless, len(chrome_args))
|
logger.debug("Launching stealth Chromium (headless=%s, args=%d)", headless, len(chrome_args))
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ async def launch_async( # noqa: C901
|
|||||||
|
|
||||||
binary_path = ensure_binary()
|
binary_path = ensure_binary()
|
||||||
timezone, locale = _maybe_resolve_geoip(geoip, proxy, timezone, locale)
|
timezone, locale = _maybe_resolve_geoip(geoip, proxy, timezone, locale)
|
||||||
chrome_args = _build_args(stealth_args, args, timezone=timezone, locale=locale)
|
chrome_args = _build_args(stealth_args, args, timezone=timezone, locale=locale, headless=headless)
|
||||||
|
|
||||||
logger.debug("Launching stealth Chromium async (headless=%s, args=%d)", headless, len(chrome_args))
|
logger.debug("Launching stealth Chromium async (headless=%s, args=%d)", headless, len(chrome_args))
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ def launch_persistent_context(
|
|||||||
|
|
||||||
binary_path = ensure_binary()
|
binary_path = ensure_binary()
|
||||||
timezone, locale = _maybe_resolve_geoip(geoip, proxy, timezone, locale)
|
timezone, locale = _maybe_resolve_geoip(geoip, proxy, timezone, locale)
|
||||||
chrome_args = _build_args(stealth_args, args, timezone=timezone, locale=locale)
|
chrome_args = _build_args(stealth_args, args, timezone=timezone, locale=locale, headless=headless)
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Launching persistent stealth Chromium (headless=%s, user_data_dir=%s)",
|
"Launching persistent stealth Chromium (headless=%s, user_data_dir=%s)",
|
||||||
@@ -394,7 +394,7 @@ async def launch_persistent_context_async(
|
|||||||
|
|
||||||
binary_path = ensure_binary()
|
binary_path = ensure_binary()
|
||||||
timezone, locale = _maybe_resolve_geoip(geoip, proxy, timezone, locale)
|
timezone, locale = _maybe_resolve_geoip(geoip, proxy, timezone, locale)
|
||||||
chrome_args = _build_args(stealth_args, args, timezone=timezone, locale=locale)
|
chrome_args = _build_args(stealth_args, args, timezone=timezone, locale=locale, headless=headless)
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Launching persistent stealth Chromium async (headless=%s, user_data_dir=%s)",
|
"Launching persistent stealth Chromium async (headless=%s, user_data_dir=%s)",
|
||||||
@@ -609,6 +609,7 @@ def _build_args(
|
|||||||
extra_args: list[str] | None,
|
extra_args: list[str] | None,
|
||||||
timezone: str | None = None,
|
timezone: str | None = None,
|
||||||
locale: str | None = None,
|
locale: str | None = None,
|
||||||
|
headless: bool = True,
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
"""Combine stealth args with user-provided args and locale flags.
|
"""Combine stealth args with user-provided args and locale flags.
|
||||||
|
|
||||||
@@ -621,6 +622,16 @@ def _build_args(
|
|||||||
for arg in get_default_stealth_args():
|
for arg in get_default_stealth_args():
|
||||||
seen[arg.split("=", 1)[0]] = arg
|
seen[arg.split("=", 1)[0]] = arg
|
||||||
|
|
||||||
|
# GPU blocklist bypass:
|
||||||
|
# - Headed mode (all platforms): Chromium blocks WebGL on software GPUs
|
||||||
|
# in Docker/Xvfb. Flag lets SwiftShader serve WebGL. See issue #56.
|
||||||
|
# - Windows (all modes): Chromium's GPU blocklist blocks WebGPU for the
|
||||||
|
# Microsoft Basic Render Driver. Dawn's adapter_blocklist bypass alone
|
||||||
|
# isn't enough — need this flag too. Linux doesn't need it.
|
||||||
|
import platform as _platform
|
||||||
|
if not headless or _platform.system() == "Windows":
|
||||||
|
seen["--ignore-gpu-blocklist"] = "--ignore-gpu-blocklist"
|
||||||
|
|
||||||
if extra_args:
|
if extra_args:
|
||||||
for arg in extra_args:
|
for arg in extra_args:
|
||||||
key = arg.split("=", 1)[0]
|
key = arg.split("=", 1)[0]
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ CHROMIUM_VERSION = "145.0.7632.159.7"
|
|||||||
|
|
||||||
PLATFORM_CHROMIUM_VERSIONS: dict[str, str] = {
|
PLATFORM_CHROMIUM_VERSIONS: dict[str, str] = {
|
||||||
"linux-x64": "145.0.7632.159.7",
|
"linux-x64": "145.0.7632.159.7",
|
||||||
|
"linux-arm64": "145.0.7632.159.7",
|
||||||
"darwin-arm64": "145.0.7632.109.2",
|
"darwin-arm64": "145.0.7632.109.2",
|
||||||
"darwin-x64": "145.0.7632.109.2",
|
"darwin-x64": "145.0.7632.109.2",
|
||||||
"windows-x64": "145.0.7632.109.2",
|
"windows-x64": "145.0.7632.159.7",
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ def _show_welcome() -> None:
|
|||||||
print(" https://github.com/CloakHQ/CloakBrowser")
|
print(" https://github.com/CloakHQ/CloakBrowser")
|
||||||
print()
|
print()
|
||||||
print(" Issues? https://github.com/CloakHQ/CloakBrowser/issues")
|
print(" Issues? https://github.com/CloakHQ/CloakBrowser/issues")
|
||||||
|
print(" Donate? https://ko-fi.com/cloakhq")
|
||||||
print(" Star us if CloakBrowser helps your project!")
|
print(" Star us if CloakBrowser helps your project!")
|
||||||
print()
|
print()
|
||||||
try:
|
try:
|
||||||
|
|||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# agent-browser + CloakBrowser: AI browser agent with stealth fingerprints.
|
||||||
|
#
|
||||||
|
# agent-browser is a Node.js CLI for browser automation with session management.
|
||||||
|
# CloakBrowser provides the stealth Chromium binary.
|
||||||
|
#
|
||||||
|
# Requires: npm install -g agent-browser
|
||||||
|
# pip install cloakbrowser (to auto-download the binary)
|
||||||
|
#
|
||||||
|
# Note: agent-browser launches Chrome itself via env vars — it can't connect
|
||||||
|
# to an existing browser via CDP. So we pass the binary path and stealth args directly.
|
||||||
|
|
||||||
|
# Get CloakBrowser binary path (auto-downloads if needed)
|
||||||
|
BINARY_PATH=$(python3 -c "from cloakbrowser.download import ensure_binary; print(ensure_binary())")
|
||||||
|
|
||||||
|
# Get stealth args from our wrapper (comma-separated for agent-browser)
|
||||||
|
STEALTH_ARGS=$(python3 -c "from cloakbrowser.config import get_default_stealth_args; print(','.join(get_default_stealth_args()))")
|
||||||
|
|
||||||
|
# Point agent-browser at CloakBrowser
|
||||||
|
export AGENT_BROWSER_EXECUTABLE_PATH="$BINARY_PATH"
|
||||||
|
export AGENT_BROWSER_ARGS="$STEALTH_ARGS"
|
||||||
|
|
||||||
|
# Open a page
|
||||||
|
agent-browser --session stealth-test open "https://example.com"
|
||||||
|
|
||||||
|
# Get page title
|
||||||
|
agent-browser --session stealth-test eval "document.title"
|
||||||
|
|
||||||
|
# Check stealth
|
||||||
|
agent-browser --session stealth-test eval "JSON.stringify({webdriver: navigator.webdriver, plugins: navigator.plugins.length, platform: navigator.platform})"
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
"""browser-use + CloakBrowser: AI agent with stealth fingerprints.
|
||||||
|
|
||||||
|
browser-use handles AI agent logic, CloakBrowser handles bot detection.
|
||||||
|
Your agent can now browse sites behind Cloudflare, reCAPTCHA, DataDome.
|
||||||
|
|
||||||
|
Requires: pip install browser-use cloakbrowser langchain-openai
|
||||||
|
Set OPENAI_API_KEY (or swap for another LLM provider).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from browser_use import Agent, Browser, BrowserConfig
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
|
||||||
|
from cloakbrowser import launch_async
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
# Step 1: Launch CloakBrowser (handles binary, stealth args, fingerprints)
|
||||||
|
cb_browser = await launch_async(
|
||||||
|
headless=True,
|
||||||
|
args=["--remote-debugging-port=9242", "--remote-debugging-address=127.0.0.1"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 2: Connect browser-use to the stealth browser via CDP
|
||||||
|
config = BrowserConfig(cdp_url="http://127.0.0.1:9242")
|
||||||
|
browser = Browser(config=config)
|
||||||
|
|
||||||
|
# Step 3: Run your AI agent — it browses through CloakBrowser
|
||||||
|
agent = Agent(
|
||||||
|
task="Go to https://www.google.com and search for 'browser automation'",
|
||||||
|
llm=ChatOpenAI(model="gpt-4o-mini"),
|
||||||
|
browser=browser,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await agent.run()
|
||||||
|
print(result)
|
||||||
|
|
||||||
|
await cb_browser.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
"""Crawl4AI + CloakBrowser: LLM-ready web crawling with stealth fingerprints.
|
||||||
|
|
||||||
|
Crawl4AI handles extraction and markdown conversion,
|
||||||
|
CloakBrowser handles bot detection.
|
||||||
|
|
||||||
|
Requires: pip install crawl4ai cloakbrowser
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
|
||||||
|
|
||||||
|
from cloakbrowser import launch_async
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
# Step 1: Launch CloakBrowser with remote debugging
|
||||||
|
cb_browser = await launch_async(
|
||||||
|
headless=True,
|
||||||
|
args=["--remote-debugging-port=9243", "--remote-debugging-address=127.0.0.1"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 2: Connect Crawl4AI to the stealth browser via CDP
|
||||||
|
browser_config = BrowserConfig(cdp_url="http://127.0.0.1:9243")
|
||||||
|
run_config = CrawlerRunConfig()
|
||||||
|
|
||||||
|
async with AsyncWebCrawler(config=browser_config) as crawler:
|
||||||
|
result = await crawler.arun(
|
||||||
|
"https://example.com",
|
||||||
|
config=run_config,
|
||||||
|
)
|
||||||
|
print(f"Extracted {len(result.markdown)} chars of markdown")
|
||||||
|
print(result.markdown[:500])
|
||||||
|
|
||||||
|
await cb_browser.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
"""LangChain + CloakBrowser: load web pages behind bot detection into LangChain Documents.
|
||||||
|
|
||||||
|
LangChain's PlaywrightURLLoader hardcodes chromium.launch() with no way to pass
|
||||||
|
a custom binary. This example uses CloakBrowser directly as a stealth document loader
|
||||||
|
that produces LangChain Document objects.
|
||||||
|
|
||||||
|
Requires: pip install langchain-core cloakbrowser
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from langchain_core.documents import Document
|
||||||
|
|
||||||
|
from cloakbrowser import launch_async
|
||||||
|
|
||||||
|
|
||||||
|
async def load_urls_stealth(urls: list[str], **launch_kwargs) -> list[Document]:
|
||||||
|
"""Load URLs using CloakBrowser stealth browser, return LangChain Documents."""
|
||||||
|
browser = await launch_async(headless=True, **launch_kwargs)
|
||||||
|
page = await browser.new_page()
|
||||||
|
docs = []
|
||||||
|
|
||||||
|
for url in urls:
|
||||||
|
await page.goto(url, wait_until="domcontentloaded")
|
||||||
|
text = await page.evaluate("document.body.innerText")
|
||||||
|
title = await page.title()
|
||||||
|
docs.append(Document(
|
||||||
|
page_content=text,
|
||||||
|
metadata={"source": url, "title": title},
|
||||||
|
))
|
||||||
|
|
||||||
|
await browser.close()
|
||||||
|
return docs
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
urls = [
|
||||||
|
"https://example.com",
|
||||||
|
"https://httpbin.org/html",
|
||||||
|
]
|
||||||
|
|
||||||
|
docs = await load_urls_stealth(urls)
|
||||||
|
|
||||||
|
for doc in docs:
|
||||||
|
print(f"--- {doc.metadata['title']} ({doc.metadata['source']}) ---")
|
||||||
|
print(doc.page_content[:300])
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
"""Scrapling + CloakBrowser: adaptive web scraping with stealth fingerprints.
|
||||||
|
|
||||||
|
Scrapling handles parsing and element tracking,
|
||||||
|
CloakBrowser handles bot detection.
|
||||||
|
|
||||||
|
Requires: pip install scrapling[all] cloakbrowser
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
from urllib.request import urlopen
|
||||||
|
|
||||||
|
from scrapling.fetchers import StealthyFetcher
|
||||||
|
|
||||||
|
from cloakbrowser import launch_async
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
# Launch CloakBrowser with remote debugging
|
||||||
|
cb_browser = await launch_async(
|
||||||
|
headless=True,
|
||||||
|
args=["--remote-debugging-port=9245", "--remote-debugging-address=127.0.0.1"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get the WebSocket URL from Chrome (Scrapling requires ws:// scheme)
|
||||||
|
info = json.loads(urlopen("http://127.0.0.1:9245/json/version").read())
|
||||||
|
ws_url = info["webSocketDebuggerUrl"]
|
||||||
|
|
||||||
|
# Connect Scrapling to the stealth browser via CDP
|
||||||
|
page = await StealthyFetcher.async_fetch(
|
||||||
|
"https://example.com",
|
||||||
|
cdp_url=ws_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Title: {page.css('title::text').get()}")
|
||||||
|
print(f"Text: {page.css('p::text').getall()}")
|
||||||
|
|
||||||
|
await cb_browser.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
"""Selenium + CloakBrowser: use stealth Chromium with Selenium WebDriver.
|
||||||
|
|
||||||
|
CloakBrowser provides the binary and stealth args.
|
||||||
|
Selenium drives it via ChromeDriver.
|
||||||
|
|
||||||
|
Requires: pip install selenium cloakbrowser
|
||||||
|
Note: ChromeDriver version must match Chromium 145.
|
||||||
|
pip install chromedriver-autoinstaller or download manually.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from selenium import webdriver
|
||||||
|
from selenium.webdriver.chrome.options import Options
|
||||||
|
|
||||||
|
from cloakbrowser.config import get_default_stealth_args
|
||||||
|
from cloakbrowser.download import ensure_binary
|
||||||
|
|
||||||
|
binary_path = ensure_binary()
|
||||||
|
stealth_args = get_default_stealth_args()
|
||||||
|
|
||||||
|
options = Options()
|
||||||
|
options.binary_location = binary_path
|
||||||
|
options.add_argument("--headless")
|
||||||
|
for arg in stealth_args:
|
||||||
|
options.add_argument(arg)
|
||||||
|
|
||||||
|
driver = webdriver.Chrome(options=options)
|
||||||
|
|
||||||
|
driver.get("https://example.com")
|
||||||
|
print(f"Selenium + CloakBrowser: {driver.title}")
|
||||||
|
|
||||||
|
# Verify stealth
|
||||||
|
result = driver.execute_script("""
|
||||||
|
return {
|
||||||
|
webdriver: navigator.webdriver,
|
||||||
|
plugins: navigator.plugins.length,
|
||||||
|
platform: navigator.platform,
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
print(f"Stealth checks: {result}")
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
"""undetected-chromedriver + CloakBrowser: double stealth layer.
|
||||||
|
|
||||||
|
undetected-chromedriver patches ChromeDriver detection signals,
|
||||||
|
CloakBrowser patches the browser fingerprints at the C++ level.
|
||||||
|
|
||||||
|
Requires: pip install undetected-chromedriver cloakbrowser
|
||||||
|
"""
|
||||||
|
|
||||||
|
import undetected_chromedriver as uc
|
||||||
|
|
||||||
|
from cloakbrowser.config import get_chromium_version, get_default_stealth_args
|
||||||
|
from cloakbrowser.download import ensure_binary
|
||||||
|
|
||||||
|
binary_path = ensure_binary()
|
||||||
|
stealth_args = get_default_stealth_args()
|
||||||
|
chromium_major = int(get_chromium_version().split(".")[0])
|
||||||
|
|
||||||
|
options = uc.ChromeOptions()
|
||||||
|
options.binary_location = binary_path
|
||||||
|
options.add_argument("--headless")
|
||||||
|
for arg in stealth_args:
|
||||||
|
options.add_argument(arg)
|
||||||
|
|
||||||
|
driver = uc.Chrome(options=options, version_main=chromium_major)
|
||||||
|
|
||||||
|
driver.get("https://example.com")
|
||||||
|
print(f"undetected-chromedriver + CloakBrowser: {driver.title}")
|
||||||
|
|
||||||
|
# Verify stealth
|
||||||
|
result = driver.execute_script("""
|
||||||
|
return {
|
||||||
|
webdriver: navigator.webdriver,
|
||||||
|
plugins: navigator.plugins.length,
|
||||||
|
platform: navigator.platform,
|
||||||
|
hardwareConcurrency: navigator.hardwareConcurrency,
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
print(f"Stealth checks: {result}")
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
+5
-4
@@ -16,7 +16,7 @@ Drop-in Playwright/Puppeteer replacement. Same API, same code — just swap the
|
|||||||
- **Passes Cloudflare Turnstile**, FingerprintJS, BrowserScan — tested against 30+ detection sites
|
- **Passes Cloudflare Turnstile**, FingerprintJS, BrowserScan — tested against 30+ detection sites
|
||||||
- **`npm install cloakbrowser`** — binary auto-downloads, auto-updates, zero config
|
- **`npm install cloakbrowser`** — binary auto-downloads, auto-updates, zero config
|
||||||
- **Free and open source** — no subscriptions, no usage limits
|
- **Free and open source** — no subscriptions, no usage limits
|
||||||
- **Works with any framework** — also tested with Selenium, undetected-chromedriver, browser-use, Crawl4AI, and agent-browser
|
- **Works with any framework** — tested with browser-use, Crawl4AI, Scrapling, Stagehand ([example](examples/stagehand.ts)), LangChain, Selenium, and more
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@@ -203,10 +203,11 @@ const page = await browser.newPage();
|
|||||||
|
|
||||||
| Platform | Chromium | Patches | Status |
|
| Platform | Chromium | Patches | Status |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Linux x86_64 | 145 | 26 | ✅ Latest |
|
| Linux x86_64 | 145 | 33 | ✅ Latest |
|
||||||
|
| Linux arm64 (RPi, Graviton) | 145 | 33 | ✅ Latest |
|
||||||
| macOS arm64 (Apple Silicon) | 145 | 26 | ✅ Latest |
|
| macOS arm64 (Apple Silicon) | 145 | 26 | ✅ Latest |
|
||||||
| macOS x86_64 (Intel) | 145 | 26 | ✅ Latest |
|
| macOS x86_64 (Intel) | 145 | 26 | ✅ Latest |
|
||||||
| Windows x86_64 | 145 | 26 | ✅ Latest |
|
| Windows x86_64 | 145 | 33 | ✅ Latest |
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@@ -264,7 +265,7 @@ export CLOAKBROWSER_BINARY_PATH=~/.cloakbrowser/chromium-145.0.7632.159.2/chrome
|
|||||||
export CLOAKBROWSER_BINARY_PATH=~/.cloakbrowser/chromium-145.0.7632.109.2/Chromium.app/Contents/MacOS/Chromium
|
export CLOAKBROWSER_BINARY_PATH=~/.cloakbrowser/chromium-145.0.7632.109.2/Chromium.app/Contents/MacOS/Chromium
|
||||||
|
|
||||||
# Windows
|
# Windows
|
||||||
set CLOAKBROWSER_BINARY_PATH=%USERPROFILE%\.cloakbrowser\chromium-145.0.7632.109.2\chrome.exe
|
set CLOAKBROWSER_BINARY_PATH=%USERPROFILE%\.cloakbrowser\chromium-145.0.7632.159.7\chrome.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Stagehand + CloakBrowser: AI browser automation with stealth fingerprints.
|
||||||
|
*
|
||||||
|
* Stagehand handles AI-powered navigation and actions,
|
||||||
|
* CloakBrowser handles bot detection.
|
||||||
|
*
|
||||||
|
* Requires: npm install @browserbasehq/stagehand cloakbrowser
|
||||||
|
* Set OPENAI_API_KEY for the AI model.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* CLOAKBROWSER_BINARY_PATH=/path/to/chrome npx tsx examples/stagehand.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Stagehand } from "@browserbasehq/stagehand";
|
||||||
|
import { ensureBinary } from "../src/download.js";
|
||||||
|
import { getDefaultStealthArgs } from "../src/config.js";
|
||||||
|
|
||||||
|
const binaryPath = await ensureBinary();
|
||||||
|
const stealthArgs = getDefaultStealthArgs();
|
||||||
|
|
||||||
|
const stagehand = new Stagehand({
|
||||||
|
env: "LOCAL",
|
||||||
|
localBrowserLaunchOptions: {
|
||||||
|
executablePath: binaryPath,
|
||||||
|
args: stealthArgs,
|
||||||
|
headless: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await stagehand.init();
|
||||||
|
|
||||||
|
const page = stagehand.context.pages()[0];
|
||||||
|
await page.goto("https://example.com");
|
||||||
|
console.log(`Stagehand + CloakBrowser: ${await page.title()}`);
|
||||||
|
|
||||||
|
await stagehand.close();
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cloakbrowser",
|
"name": "cloakbrowser",
|
||||||
"version": "0.3.15",
|
"version": "0.3.17",
|
||||||
"description": "Stealth Chromium that passes every bot detection test. Drop-in Playwright/Puppeteer replacement with source-level fingerprint patches.",
|
"description": "Stealth Chromium that passes every bot detection test. Drop-in Playwright/Puppeteer replacement with source-level fingerprint patches.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
@@ -20,6 +20,15 @@ export function buildArgs(options: LaunchOptions): string[] {
|
|||||||
seen.set(arg.split("=")[0], arg);
|
seen.set(arg.split("=")[0], arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// GPU blocklist bypass:
|
||||||
|
// - Headed mode (all platforms): Chromium blocks WebGL on software GPUs
|
||||||
|
// in Docker/Xvfb. Flag lets SwiftShader serve WebGL. See issue #56.
|
||||||
|
// - Windows (all modes): Chromium's GPU blocklist blocks WebGPU for the
|
||||||
|
// Microsoft Basic Render Driver. Dawn's adapter_blocklist bypass alone
|
||||||
|
// isn't enough. Linux doesn't need it.
|
||||||
|
if (options.headless === false || process.platform === "win32") {
|
||||||
|
seen.set("--ignore-gpu-blocklist", "--ignore-gpu-blocklist");
|
||||||
|
}
|
||||||
if (options.args) {
|
if (options.args) {
|
||||||
for (const arg of options.args) {
|
for (const arg of options.args) {
|
||||||
const key = arg.split("=")[0];
|
const key = arg.split("=")[0];
|
||||||
|
|||||||
+2
-1
@@ -31,9 +31,10 @@ export const CHROMIUM_VERSION = "145.0.7632.159.7";
|
|||||||
|
|
||||||
export const PLATFORM_CHROMIUM_VERSIONS: Record<string, string> = {
|
export const PLATFORM_CHROMIUM_VERSIONS: Record<string, string> = {
|
||||||
"linux-x64": "145.0.7632.159.7",
|
"linux-x64": "145.0.7632.159.7",
|
||||||
|
"linux-arm64": "145.0.7632.159.7",
|
||||||
"darwin-arm64": "145.0.7632.109.2",
|
"darwin-arm64": "145.0.7632.109.2",
|
||||||
"darwin-x64": "145.0.7632.109.2",
|
"darwin-x64": "145.0.7632.109.2",
|
||||||
"windows-x64": "145.0.7632.109.2",
|
"windows-x64": "145.0.7632.159.7",
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ function showWelcome(): void {
|
|||||||
console.log(" https://github.com/CloakHQ/CloakBrowser");
|
console.log(" https://github.com/CloakHQ/CloakBrowser");
|
||||||
console.log();
|
console.log();
|
||||||
console.log(" Issues? https://github.com/CloakHQ/CloakBrowser/issues");
|
console.log(" Issues? https://github.com/CloakHQ/CloakBrowser/issues");
|
||||||
|
console.log(" Donate? https://ko-fi.com/cloakhq");
|
||||||
console.log(" Star us if CloakBrowser helps your project!");
|
console.log(" Star us if CloakBrowser helps your project!");
|
||||||
console.log();
|
console.log();
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user