jo-inc/camofox-browser

GitHub: jo-inc/camofox-browser

一个基于 Camoufox 引擎的免检测浏览器自动化服务,为 AI 代理提供绕过反爬的稳定 REST API。

Stars: 2132 | Forks: 203

camofox-browser

camofox-browser

Anti-detection browser server for AI agents, powered by Camoufox

Build License Camoufox Docker

Standing on the mighty shoulders of Camoufox - a Firefox fork with fingerprint spoofing at the C++ level.

The same engine behind Jo — an AI assistant that doesn't need you to babysit it. Runs half on your Mac, half on a dedicated cloud machine that only you use. Available on macOS, Telegram, and WhatsApp. Try the beta free →


``` git clone https://github.com/jo-inc/camofox-browser && cd camofox-browser npm install && npm start # → http://localhost:9377 ``` ## 为什么 AI 代理需要浏览真实网络。Playwright 会被屏蔽。无头 Chrome 会被指纹识别。隐身插件本身会成为指纹。 Camoufox 在 **C++ 实现层**修补 Firefox — `navigator.hardwareConcurrency`、WebGL 渲染器、AudioContext、屏幕几何、WebRTC 等 — 在 JavaScript 触及它们之前全部伪造。没有 shims,没有包装器,没有泄露。 该项目将这一引擎封装为面向代理的 REST API:使用可访问性快照替代臃肿的 HTML,用稳定的元素引用实现可靠点击,并为常用网站提供搜索宏。 ## 功能 - **C++ 反检测** — 绕过 Google、Cloudflare 及大多数机器人检测 - **元素引用** — 稳定的 `e1`、`e2`、`e3` 标识符,用于可靠交互 - **令牌高效** — 可访问性快照体积约为原始 HTML 的 10% - **随处运行** — 懒加载浏览器启动 + 空闲自动关闭,闲置时内存约 40MB。设计用于与你的其他栈共享一台机器:树莓派、5 美元 VPS、共享 Railway 基础设施 - **会话隔离** — 每个用户独立的 Cookie 与存储 - **Cookie 导入** — 注入 Netscape 格式的 Cookie 文件以实现免登录浏览 - **代理 + 地理定位** — 通过内置 GeoIP 将流量路由至住宅代理,并自动设置区域/时区 - **结构化日志** — 每行 JSON 日志含请求 ID,便于生产环境观测 - **YouTube 文本** — 无需 API 键即可通过 yt-dlp 提取任意 YouTube 视频字幕 - **搜索宏** — `@google_search`、`@youtube_search`、`@amazon_search`、`@reddit_subreddit` 等 10+ 种 - **快照截图** — 在可访问性快照中附带 base64 PNG 截图 - **大页处理** — 自动截断并支持基于偏移的分页 - **下载捕获** — 捕获浏览器下载并通过 API 获取(可选内联 base64) - **DOM 图像提取** — 列出 `` 的 src/alt,并可选择返回内联 data URL - **任意部署** — 支持 Docker、Fly.io、Railway ## 可选依赖 | 依赖 | 用途 | 安装 | |-----------|---------|---------| | [yt-dlp](https://github.com/yt-dlp/yt-dlp) | YouTube 字幕提取(快速路径) | `pip install yt-dlp` 或 `brew install yt-dlp` | Docker 镜像已包含 yt-dlp。本地开发请安装该依赖以启用 `/youtube/transcript` 端点;未安装时将回退到较慢的浏览器方式。 ## 快速开始 ### OpenClaw 插件 ``` openclaw plugins install @askjo/camofox-browser ``` **工具:** `camofox_create_tab` · `camofox_snapshot` · `camofox_click` · `camofox_type` · `camofox_navigate` · `camofox_scroll` · `camofox_screenshot` · `camofox_close_tab` · `camofox_list_tabs` · `camofox_import_cookies` ### 独立运行 ``` git clone https://github.com/jo-inc/camofox-browser cd camofox-browser npm install npm start # downloads Camoufox on first run (~300MB) ``` 默认端口为 `9377`。请参阅 [环境变量](#environment-variables) 获取全部选项。 ### Docker 包含的 `Makefile` 能自动识别 CPU 架构并在 Docker 构建**之外**预先下载 Camoufox 与 yt-dlp 二进制文件,因此重建速度很快(约 30 秒,而非约 3 分钟)。 ``` # 构建并启动(自动检测架构:在 M1/M2 上为 aarch64,在 Intel 上为 x86_64) make up # 停止并移除容器 make down # 强制干净重建(例如在升级 VERSION/RELEASE 后) make reset # 仅下载二进制文件(不构建) make fetch # 显式覆盖架构或版本 make up ARCH=x86_64 make up VERSION=135.0.1 RELEASE=beta.24 ``` 注意:必须先运行 `make fetch`(或 `make build`)——Dockerfile 期望预下载的二进制文件位于 `dist/` 目录。 ### Fly.io / Railway 已包含 `fly.toml` 与 `railway.toml`。使用 `fly deploy` 部署或连接仓库到 Railway。 ## 用法 ### Cookie 导入 从浏览器导入 Cookie 到 Camoufox,以跳过 LinkedIn、Amazon 等网站的交互式登录。 #### 设置 **1. 生成密钥:** ``` # macOS / Linux openssl rand -hex 32 ``` **2. 启动 OpenClaw 前设置环境变量:** ``` export CAMOFOX_API_KEY="your-generated-key" openclaw start ``` 插件与服务器均使用同一密钥进行身份验证。请只需设置一次。 **3. 从浏览器导出 Cookie:** 安装支持导出 Netscape 格式 Cookie 文件的浏览器扩展(例如 Chrome/Firefox 的 “cookies.txt”)。导出目标网站的 Cookie。 **4. 放置 Cookie 文件:** ``` mkdir -p ~/.camofox/cookies cp ~/Downloads/linkedin_cookies.txt ~/.camofox/cookies/linkedin.txt ``` 默认目录为 `~/.camofox/cookies/`。可通过 `CAMOFOX_COOKIES_DIR` 覆盖。 **5. 请求代理导入:** 代理调用 `camofox_import_cookies` → 读取文件 → 使用 Bearer 令牌 POST 到服务器 → Cookie 注入浏览器会话。后续对 linkedin.com 的 `camofox_create_tab` 调用将已认证。 #### 工作原理 ``` ~/.camofox/cookies/linkedin.txt (Netscape format, on disk) │ ▼ camofox_import_cookies tool (parses file, filters by domain) │ ▼ POST /sessions/:userId/cookies │ Authorization: Bearer │ Body: { cookies: [Playwright cookie objects] } ▼ camofox server (validates, sanitizes, injects) │ ▼ context.addCookies(...) │ Camoufox browser session (authenticated browsing) ``` - `cookiesPath` 相对于 Cookie 目录解析,禁止路径遍历到外部 - 每个请求最多 500 个 Cookie,文件大小限制 5MB - Cookie 对象被清理为 Playwright 字段的允许列表 #### 独立服务器使用 ``` curl -X POST http://localhost:9377/sessions/agent1/cookies \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer YOUR_CAMOFOX_API_KEY' \ -d '{"cookies":[{"name":"foo","value":"bar","domain":"example.com","path":"/","expires":-1,"httpOnly":false,"secure":false}]}' ``` #### Docker / Fly.io ``` docker run -p 9377:9377 \ -e CAMOFOX_API_KEY="your-generated-key" \ -v ~/.camofox/cookies:/home/node/.camofox/cookies:ro \ camofox-browser ``` 对于 Fly.io: ``` fly secrets set CAMOFOX_API_KEY="your-generated-key" ``` ### 代理 + 地理定位 将所有浏览器流量通过代理路由,并自动派生区域、时区与地理位置(基于代理出口 IP 的内置 GeoIP)。 **简单代理(单端点):** ``` export PROXY_HOST=166.88.179.132 export PROXY_PORT=46040 export PROXY_USERNAME=myuser export PROXY_PASSWORD=mypass npm start ``` **回连代理(旋转粘性会话):** 适用于 Decodo、Bright Data 或 Oxylabs 等提供单网关端点并带基于会话的粘性 IP 的服务: ``` export PROXY_STRATEGY=backconnect export PROXY_BACKCONNECT_HOST=gate.provider.com export PROXY_BACKCONNECT_PORT=7000 export PROXY_USERNAME=myuser export PROXY_PASSWORD=mypass npm start ``` 每个浏览器上下文拥有独立的粘性会话,因此不同用户获得不同 IP。会话在代理错误或 Google 屏蔽时自动轮换。 或在 Docker 中: ``` docker run -p 9377:9377 \ -e PROXY_HOST=166.88.179.132 \ -e PROXY_PORT=46040 \ -e PROXY_USERNAME=myuser \ -e PROXY_PASSWORD=mypass \ camofox-browser ``` 配置代理后: - 所有流量经代理路由 - Camoufox 的 GeoIP 自动设置 `locale`、`timezone` 与 `geolocation`,匹配代理出口 IP - 浏览器指纹(语言、时区、坐标)与代理位置一致 - 无代理时默认使用 `en-US`、`America/Los_Angeles` 及旧金山坐标 ### 结构化日志 所有日志输出为 JSON(每行一个对象),便于日志聚合器解析: ``` {"ts":"2026-02-11T23:45:01.234Z","level":"info","msg":"req","reqId":"a1b2c3d4","method":"POST","path":"/tabs","userId":"agent1"} {"ts":"2026-02-11T23:45:01.567Z","level":"info","msg":"res","reqId":"a1b2c3d4","status":200,"ms":333} ``` 健康检查请求(`/health`)不计入请求日志以降低噪音。 ### 基础浏览 ``` # 创建标签页 curl -X POST http://localhost:9377/tabs \ -H 'Content-Type: application/json' \ -d '{"userId": "agent1", "sessionKey": "task1", "url": "https://example.com"}' # 使用元素引用获取可访问性快照 curl "http://localhost:9377/tabs/TAB_ID/snapshot?userId=agent1" # → { "snapshot": "[button e1] Submit [link e2] Learn more", ... } # 通过引用点击 curl -X POST http://localhost:9377/tabs/TAB_ID/click \ -H 'Content-Type: application/json' \ -d '{"userId": "agent1", "ref": "e1"}' # 向元素输入内容 curl -X POST http://localhost:9377/tabs/TAB_ID/type \ -H 'Content-Type: application/json' \ -d '{"userId": "agent1", "ref": "e2", "text": "hello", "pressEnter": true}' # 使用搜索宏导航 curl -X POST http://localhost:9377/tabs/TAB_ID/navigate \ -H 'Content-Type: application/json' \ -d '{"userId": "agent1", "macro": "@google_search", "query": "best coffee beans"}' ``` ## API ### 标签页生命周期 | 方法 | 端点 | 描述 | |--------|----------|-------------| | `POST` | `/tabs` | 创建标签页并设置初始 URL | | `GET` | `/tabs?userId=X` | 列出打开的标签页 | | `GET` | `/tabs/:id/stats` | 标签页统计(工具调用、访问 URL) | | `DELETE` | `/tabs/:id` | 关闭标签页 | | `DELETE` | `/tabs/group/:groupId` | 关闭标签页组 | | `DELETE` | `/sessions/:userId` | 关闭用户所有标签页 | ### 页面交互 | 方法 | 端点 | 描述 | |--------|----------|-------------| | `GET` | `/tabs/:id/snapshot` | 可访问性快照(含元素引用)。查询参数:`includeScreenshot=true`(附加 base64 PNG)、`offset=N`(分页大快照) | | `POST` | `/tabs/:id/click` | 点击元素(引用或 CSS 选择器) | | `POST` | `/tabs/:id/type` | 向元素输入文本 | | `POST` | `/tabs/:id/press` | 按下键盘按键 | | `POST` | `/tabs/:id/scroll` | 滚动页面(上下左右) | | `POST` | `/tabs/:id/navigate` | 导航至 URL 或搜索宏 | | `POST` | `/tabs/:id/wait` |等待选择器或超时 | | `GET` | `/tabs/:id/links` | 提取页面所有链接 | | `GET` | `/tabs/:id/images` | 列出 `` 元素。查询参数:`includeData=true`(返回内联 data URL)、`maxBytes=N`、`limit=N` | | `GET` | `/tabs/:id/downloads` | 列出捕获的下载。查询参数:`includeData=true`(base64 文件数据)、`consume=true`(读取后清除)、`maxBytes=N` | | `GET` | `/tabs/:id/screenshot` | 截取屏幕 | | `POST` | `/tabs/:id/back` | 后退 | | `POST` | `/tabs/:id/forward` | 前进 | | `POST` | `/tabs/:id/refresh` | 刷新 | ### YouTube 文本 | 方法 | 端点 | 描述 | |--------|----------|-------------| | `POST` | `/youtube/transcript` | 从 YouTube 视频提取字幕 | ``` curl -X POST http://localhost:9377/youtube/transcript \ -H 'Content-Type: application/json' \ -d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "languages": ["en"]}' # → { "status": "ok", "transcript": "[00:18] ♪ We're no strangers to love ♪\n...", "video_title": "...", "total_words": 548 } ``` 使用可用的 [yt-dlp](https://github.com/yt-dlp/yt-dlp)(快速路径,无需浏览器)。若未安装则回退到浏览器拦截方式,速度较慢且受 YouTube 广告前贴片影响。 ### 服务器 | 方法 | 端点 | 描述 | |--------|----------|-------------| | `GET` | `/health` | 健康检查 | | `POST` | `/start` | 启动浏览器引擎 | | `POST` | `/stop` | 停止浏览器引擎 | ### 会话 | 方法 | 端点 | 描述 | |--------|----------|-------------| | `POST` | `/sessions/:userId/cookies` | 向用户会话添加 Cookie(Playwright 格式对象) | ## 搜索宏 `@google_search` · `@youtube_search` · `@amazon_search` · `@reddit_search` · `@reddit_subreddit` · `@wikipedia_search` · `@twitter_search` · `@yelp_search` · `@spotify_search` · `@netflix_search` · `@linkedin_search` · `@instagram_search` · `@tiktok_search` · `@twitch_search` Reddit 宏直接返回 JSON(无需 HTML 解析): - `@reddit_search` — 搜索全站 Reddit,返回含 25 条结果的 JSON - `@reddit_subreddit` — 浏览子版块(例:查询 `"programming"` → `/r/programming.json`) ## 环境变量 | 变量 | 描述 | 默认 | |----------|-------------|---------| | `CAMOFOX_PORT` | 服务器端口 | `9377` | | `PORT` | 服务器端口(备用,用于 Fly.io 等平台) | `9377` | | `CAMOFOX_API_KEY` | 启用 Cookie 导入端点(未设置则禁用) | — | | `CAMOFOX_ADMIN_KEY` | 启用 `POST /stop` 所需 | — | | `CAMOFOX_COOKIES_DIR` | Cookie 文件目录 | `~/.camofox/cookies` | | `MAX_SESSIONS` | 最大并发浏览器会话 | `50` | | `MAX_TABS_PER_SESSION` | 每会话最大标签页数 | `10` | | `SESSION_TIMEOUT_MS` | 会话空闲超时 | `1800000`(30 分钟) | | `BROWSER_IDLE_TIMEOUT_MS` | 浏览器空闲后关闭(0 = 永不) | `300000`(5 分钟) | | `HANDLER_TIMEOUT_MS` | 单个请求最大处理时间 | `30000`(30 秒) | | `MAX_CONCURRENT_PER_USER` | 每用户并发请求上限 | `3` | | `MAX_OLD_SPACE_SIZE` | Node.js V8 堆限制(MB) | `128` | | `PROXY_STRATEGY` | 代理模式:`backconnect`(旋转粘性会话)或空(单端点) | — | | `PROXY_PROVIDER` | 会话格式提供者(如 `decodo`) | `decodo` | | `PROXY_HOST` | 代理主机或 IP(简单模式) | — | | `PROXY_PORT` | 代理端口(简单模式) | — | | `PROXY_USERNAME` | 代理认证用户名 | — | | `PROXY_PASSWORD` | 代理认证密码 | — | | `PROXY_BACKCONNECT_HOST` | 回连网关主机名 | — | | `PROXY_BACKCONNECT_PORT` | 回连网关端口 | `7000` | | `PROXY_COUNTRY` | 代理地理目标国家 | — | | `PROXY_STATE` | 代理地理目标州/区域 | — | | `TAB_INACTIVITY_MS` | 超过此时间关闭标签页 | `300000`(5 分钟) | ## 架构 ``` Browser Instance (Camoufox) └── User Session (BrowserContext) - isolated cookies/storage ├── Tab Group (sessionKey: "conv1") │ ├── Tab (google.com) │ └── Tab (github.com) └── Tab Group (sessionKey: "conv2") └── Tab (amazon.com) ``` 会话在 30 分钟无活动后自动过期。浏览器在无活跃会话 5 分钟后自动关闭,并在下次请求时重新启动。 当会话达到标签页上限时,自动回收最旧/最少使用的标签页,而非返回错误 —— 因此长时间运行的代理会话不会遇到死胡同。 ## 测试 ``` npm test # all tests npm run test:e2e # e2e tests only npm run test:live # live site tests (Google, macros) npm run test:debug # with server output ``` ## npm ``` npm install @askjo/camofox-browser ``` ## 加密货币诈骗警告 一些不法分子正在利用本项目的关注度,用名为 “Camofox” 的加密货币代币做手脚。**Camofox 并非加密项目,也永远不会成为加密项目。** 任何使用 Camofox 名称的代币、币或 NFT 都与我们无关。 ## 许可证 MIT
标签:AI代理, AI浏览, AudioContext, Camoufox, Chrome无头指纹, C++级补丁, Docker, Firefox Fork, MITM代理, Playwright绕过, REST API, SEO, Telegram, WebGL渲染器, WebRTC, WhatsApp, 云部署, 反机器人检测, 反检测, 头部浏览器, 安全防御评估, 屏幕几何, 搜索宏, 无头浏览器, 浏览器指纹混淆, 硬件并发, 稳定元素引用, 网站访问, 自动化服务器, 自定义脚本, 访问性快照, 请求拦截, 阻止绕过, 隐身插件