英文原文正文为项目原始 README(英文),本站后续会翻译为中文,当前仅剔除图片与无关章节并统一排版。
由jo,一款个人AI助手背后的团队打造,该助手一半运行在你的Mac上,一半运行在专属于你的云端机器上——无需任何维护。在macOS、Telegram、WhatsApp和电子邮件上可用。免费试用测试版 ->
Pradeep Elankumaran (@pradeep24) — Jo的联合创始人兼技术首席执行官。
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 看到之前全部伪装。没有 shim,没有包装器,没有痕迹。
这个项目将该引擎封装在为代理构建的 REST API 中:使用可访问性快照代替臃肿的 HTML,点击用稳定的元素引用,以及用于常用网站的搜索宏。
功能
- C 反检测 - 绕过 Google、Cloudflare 和大多数机器人检测
- 元素引用 - 稳定的
e1、e2、e3标识符,用于可靠交互 - 节省 Token - 可访问性快照比原始 HTML 小约 90%
- 适用于任何环境 - 延迟浏览器启动,空闲关闭,使内存空闲时保持约 40MB。设计用于与栈中其他服务共享同一台机器——Raspberry Pi、5 美元 VPS、共享基础设施。
- 会话隔离 - 每个用户独立的 Cookie/存储
- Cookie 导入 - 注入 Netscape 格式的 Cookie 文件,实现已认证的浏览
- 文件上传 - 从配置的上传目录附加文件,无需本地操作系统对话框
- 代理与 GeoIP - 通过住宅代理路由流量,自动设置区域/时区
- 结构化日志 - JSON 日志行,带请求 ID,用于生产环境可观测性
- YouTube 字幕 - 通过 yt-dlp 提取任何 YouTube 视频的字幕,无需 API key
- 搜索宏 -
@google_search、@youtube_search、@amazon_search、@reddit_subreddit等 10 个以上 - 快照截图 - 在可访问性快照旁包含 base64 PNG 截图
- 大页面处理 - 自动快照截断并基于偏移量分页
- 下载捕获 - 捕获浏览器下载并通过 API 获取(可选内联 base64)
- DOM 图像提取 - 列出
<img>的 src/alt,并可选择返回内联数据 URL - 随处部署 - Docker, Fly.io, Railway
- VNC 交互登录 - 通过 noVNC 可视化登录网站,导出存储状态供代理重用
- OpenAPI 文档 - 在
/openapi.json上自动生成规格说明,在/docs查看交互式文档 - 结构化提取 - 使用 JSON Schema 的
POST /tabs/:tabId/extract将属性映射到通过x-ref的快照引用 - 会话跟踪 - 每个会话可选择启用 Playwright 跟踪捕获(截图、DOM 快照、网络),提供 API 端点列出、获取和删除跟踪压缩包
- 遥测 - 通过 GitHub Issues 自动收集匿名崩溃/挂起遥测。可识别哪些网站导致失败及常见失败模式。私有域名经过 HMAC 哈希处理,路径/参数删除,令牌/IP 模糊化。可通过
CAMOFOX_CRASH_REPORT_ENABLED=false选择退出。
可选依赖
| 依赖 | 目的 | 安装 |
|---|---|---|
| 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
独立使用
从 npm 运行:
npx @askjo/camofox-browser
或者来自来源:
git clone https://github.com/jo-inc/camofox-browser
cd camofox-browser
npm install
npm start # downloads Camoufox on first run (~300MB)
默认端口是 9377。有关所有选项,请参阅环境变量。
注意: 安装后脚本会在获取 Camoufox 二进制文件之前为自身取消设置
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD。如果没有这个覆盖,导出的PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1(在 Playwright 配置为使用系统 Chrome 时很常见)会默默跳过二进制文件下载,并在运行时导致服务器崩溃。外部 Camoufox 可执行文件: 在运行
npm install之前以及启动服务器时设置CAMOUFOX_EXECUTABLE=/path/to/camoufox-bin,以跳过内置下载并启动该可执行文件。兼容别名为CAMOUFOX_EXECUTABLE_PATH和CAMOFOX_EXECUTABLE_PATH。这对于 NixOS 路径(例如/nix/store/.../camoufox-bin)非常有用;该可执行文件必须来自包含properties.json、version.json和fontconfig/的 Camoufox 包。隔离环境或自定义二进制管理: 当你已有 Camoufox 包时,优先使用
CAMOUFOX_EXECUTABLE。否则,通过npm install --ignore-scripts禁用自动获取(对每个依赖项跳过生命周期脚本——最粗暴的方法),或者更精细地使用npm install --omit=optional,然后手动执行npx camoufox-js fetch来从你的镜像获取。请注意,PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install现在不再跳过 Camoufox 下载(postinstall 会在本地清理环境);对此请使用--ignore-scripts或CAMOUFOX_EXECUTABLE。
Docker
包含的 Makefile 会自动检测你的 CPU 架构,并在 Docker 构建之外预下载 Camoufox 和 yt-dlp 二进制文件,因此重建速度很快(约 30 秒 vs 约 3 分钟)。
# Build and start (auto-detects arch: aarch64 on M1/M2, x86_64 on Intel)
make up
# Stop and remove the container
make down
# Force a clean rebuild (e.g. after upgrading VERSION/RELEASE)
make reset
# Just download binaries (without building)
make fetch
# Override arch or version explicitly
make up ARCH=x86_64
make up VERSION=135.0.1 RELEASE=beta.24
窗口
在 Windows 上,make 不可用。请改用随附的 build.ps1 PowerShell 脚本:
# Build and start
.\build.ps1 up
# Stop and remove the container
.\build.ps1 down
# Build image only
.\build.ps1 build
# Force a clean rebuild
.\build.ps1 reset
# Download binaries only (without building)
.\build.ps1 fetch
# Override architecture
.\build.ps1 up -Arch x86_64
.\build.ps1 up -Arch aarch64
注意: 建议使用 PowerShell 7 (
pwsh),但powershell.exe(Windows PowerShell 5.1)也可以。该脚本需要安装带有 WSL2 后端的 Windows 版 Docker Desktop。行结束符: 该项目包含一个
.gitattributes文件,用于强制.sh文件使用 Unix (LF) 行结束符。如果你已经克隆了仓库,并在docker build期间遇到sh: not found或set: Illegal option -错误,请运行:Get-ChildItem -Recurse *.sh | ForEach-Object { (Get-Content $_) -join "`n" + "`n" | Set-Content $_ -NoNewline }这会将 shell 脚本转换为 LF 行结尾。未来的克隆将自动处理此问题,感谢
.gitattributes。警告:不要直接运行
docker build。 该 Dockerfile 使用绑定挂载从dist/拉取预下载的二进制文件。请始终使用make up(或先make fetch然后make build)——它会先下载二进制文件。
Fly.io
对于 Fly.io 或其他远程 CI,你需要一个在构建时下载二进制文件而不是使用绑定挂载的 Dockerfile。
铁路
包含了一个 railway.toml。它使用 Dockerfile.ci(在构建时下载二进制文件)并自动将 Railway 的 PORT 环境变量映射到 CAMOFOX_PORT。
# Install Railway CLI, then:
railway link
railway up
通过 Railway 仪表板或 CLI 设置秘密:
railway variables set CAMOFOX_API_KEY="your-generated-key"
使用
Cookie 导入
将浏览器中的 Cookie 导入 Camoufox,以跳过例如 LinkedIn、Amazon 等网站的互动登录。
设置
1. 生成一个密钥:
# macOS / Linux
openssl rand -hex 32
2. 在启动 OpenClaw 之前设置环境变量:
export CAMOFOX_API_KEY="your-generated-key"
openclaw start
相同的密钥同时被插件(用于验证请求)和服务器(用于验证请求)使用。两者都运行在相同的环境中——只需设置一次。
为什么使用环境变量? 密钥是机密信息。
openclaw.json中的插件配置以明文存储,因此秘密信息不应该放在那里。在你的 shell 配置文件、systemd 单元、Docker 环境变量或 Fly.io secrets 中设置CAMOFOX_API_KEY。默认情况下禁用 cookie 导入。 如果未设置
CAMOFOX_API_KEY,服务器会以 403 拒绝所有 cookie 请求。
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. 让你的代理导入它们:
从 linkedin.txt 导入我的 LinkedIn cookies
代理调用 camofox_import_cookies -> 读取文件 -> 使用 Bearer 令牌 POST 到服务器 -> cookies 被注入到浏览器会话中。随后对 linkedin.com 的 camofox_create_tab 调用将是已认证的。
它的工作原理
~/.camofox/cookies/linkedin.txt (Netscape format, on disk)
|
v
camofox_import_cookies tool (parses file, filters by domain)
|
v POST /sessions/:userId/cookies
| Authorization: Bearer <CAMOFOX_API_KEY>
| Body: { cookies: [Playwright cookie objects] }
v
camofox server (validates, sanitizes, injects)
|
v context.addCookies(...)
|
Camoufox browser session (authenticated browsing)
cookiesPath是相对于 cookies 目录解析的——阻止了该目录之外的路径遍历- 每个请求最多 500 个 cookie,文件大小限制为 5MB
- Cookie 对象会清理为 Playwright 字段的允许列表
会话持久化
默认情况下,camofox 会将每个用户的 cookies 和 localStorage 持久化到 ~/.camofox/profiles/。会话在浏览器重启后依然有效——只需登录一次(通过 cookies 或 VNC),随后会话会自动恢复已认证状态。
~/.camofox/
|-- cookies/ # Bootstrap cookie files (Netscape format)
\-- profiles/ # Persisted session state (auto-managed)
\-- <hashed-userId>/
\-- storage_state.json
通过 CAMOFOX_PROFILE_DIR 覆盖目录,或在持久化插件配置中设置 "profileDir"。要禁用持久化,请在 camofox.config.json 中设置 "persistence": { "enabled": false }。
默认情况下,存储状态只包含 cookies 和 localStorage。要同时持久化 IndexedDB,请在持久化插件配置中设置 "indexedDB": true。这将捕获所有可序列化的 IndexedDB 记录——不仅仅是身份验证数据——并可能使快照显著增大,检查点速度变慢。
会话追踪
捕获会话中每个操作的 Playwright 追踪:页面截图、DOM 快照、网络请求和控制台输出。输出为单个 .zip 文件,可在 Playwright 内置的 Trace Viewer 中打开。
在打开第一个标签页时,通过传递 trace: true 选择每个会话:
curl -X POST http://localhost:9377/tabs \
-H 'Content-Type: application/json' \
-d '{"userId":"agent1","sessionKey":"task1","url":"https://example.com","trace":true}'
跟踪在会话关闭时写入。关闭会话以刷新它,然后列出、获取并查看:
# Close the session to flush the trace
curl -X DELETE http://localhost:9377/sessions/agent1
# List trace files
curl http://localhost:9377/sessions/agent1/traces
# {"traces":[{"filename":"trace-2026-04-18T04-05-00-...zip","sizeBytes":42810,"createdAt":...}]}
# Download (Content-Type: application/zip)
curl http://localhost:9377/sessions/agent1/traces/trace-2026-04-18T04-05-00-abc.zip > session.zip
# View it in Playwright's Trace Viewer
npx playwright show-trace session.zip
# Delete
curl -X DELETE http://localhost:9377/sessions/agent1/traces/trace-2026-04-18T04-05-00-abc.zip
为什么使用追踪而不是视频:Camoufox 基于 Firefox,而 Playwright 的 recordVideo 仅支持 Chromium。追踪可以在 Firefox 上工作,并且提供比视频更多的信息(网络、DOM、控制台、截图)。
追踪无法在现有会话中切换。如果需要更改标志,先执行 DELETE /sessions/:userId。
存储默认在 ~/.camofox/traces/<hashed-userId>/,服务器启动时会清理:
CAMOFOX_TRACES_DIR- 基目录(默认:~/.camofox/traces)CAMOFOX_TRACES_MAX_BYTES- 每个追踪的最大大小,如果超出将在下次启动时删除(默认:50MB)CAMOFOX_TRACES_TTL_HOURS- 超过此时间的追踪将在下次启动时删除(默认:24 小时)
独立服务器使用
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 / Railway
docker run -p 9377:9377 \
-e CAMOFOX_API_KEY="your-generated-key" \
-v ~/.camofox/cookies:/home/node/.camofox/cookies:ro \
camofox-browser
Applicable to Fly.io:
fly secrets set CAMOFOX_API_KEY="your-generated-key"
铁路专用:
railway variables set CAMOFOX_API_KEY="your-generated-key"
代理 GeoIP
通过代理路由所有浏览器流量,并通过Camoufox内置的GeoIP根据代理的IP地址自动获取区域、时区和地理位置。
简单代理(单一端点):
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 - 浏览器指纹(语言、时区、坐标)与代理位置一致
没有代理时,Camofox 不会声明地理位置,也不会从主机 IP 推断地理位置。要使用固定的直接会话身份,请同时设置 CAMOFOX_LOCALE 和 CAMOFOX_TIMEZONE;否则 Camoufox 会保留其自身的默认身份。
遥测
浏览器自动化可能会以难以预测的方式失败——Cloudflare 挑战、网站重新设计导致选择器失效、重定向循环、对话框风暴、渲染器崩溃。影响范围广,失败模式多样。没有遥测的情况下,唯一的信号是“它没有工作”。
遥测为我们提供有关 哪些网站失败、如何失败 以及 发生频率 的结构化数据,以便我们优先修复真正影响用户的模式。当出现以下情况时,它会自动创建 GitHub Issues:
- 未捕获的异常 导致进程崩溃
- 事件循环停滞 超过 5 秒(看门狗检测)
- 挫败模式 —— 同一标签页连续 3 次失败(超时、上下文失效、导航中止)
每份报告包括失败类型、堆栈跟踪、标签页健康计数器(HTTP 状态直方图、控制台错误、请求失败、重定向深度)以及目标 URL——所有信息均已匿名化。
工作原理
遥测数据发送到轻量级的 Cloudflare Worker 端点 https://camofox-telemetry.askjo.workers.dev。该端点将 GitHub App 凭证作为环境密钥存储——本软件包中不包含任何密钥。
lib/reporter.js (client, no secrets)
| anonymize -> POST https://camofox-telemetry.askjo.workers.dev/report
v
Cloudflare Worker (holds GitHub App key)
| validate -> rate-limit -> dedup -> create GitHub Issue
v
GitHub Issue created
终端源码位于该仓库的 workers/crash-reporter/index.ts。
验证
你不必信任我们 —— 验证实时端点正在运行的内容:
# 1. Ask the endpoint what code it's running
curl https://camofox-telemetry.askjo.workers.dev/source
# -> { "commit": "abc1234", "sha256": "e3b0c44...", "source": "https://github.com/..." }
# 2. Compare the sha256 against the source in this repo
sha256sum workers/crash-reporter/index.ts
# 3. Check the commit matches what CI deployed
# https://github.com/jo-inc/camofox-browser/actions/workflows/telemetry-deploy.yml
git log --oneline workers/crash-reporter/index.ts | head -1
如果哈希不匹配,则端点运行的代码与仓库中的不同。部署工作流程 (.github/workflows/telemetry-deploy.yml) 会在部署时注入提交和源哈希 —— 每次部署都可以在 GitHub Actions 中审计。
或者完全跳过验证:CAMOFOX_CRASH_REPORT_ENABLED=false 禁用所有遥测,或者使用 CAMOFOX_CRASH_REPORT_URL 指向 您自己的端点。
隐私
所有报告的数据在离开进程之前都会经过严格匿名化 (lib/reporter.js L28-290):
- URL —— 知名公共域名(Google、Amazon、Reddit、Cloudflare 等)会如实显示,以便我们识别哪些网站会导致问题。私有/未知域名会被替换为稳定的 HMAC 哈希(
site-a1b2c3d4) —— 不同报告中使用相同哈希以便关联,但不能反向还原为原始域名。路径段会变为*/*/*(仅保留深度)。查询参数会变为?[3](仅保留数量)。绝不包含密钥、值或路径内容。 - 文件路径 -> 只保留文件名 (
<path>/server.js) - 令牌、密钥、API 密钥 ->
<token> - IP、邮件、环境变量 -> 已编辑
- Docker/Fly 机器 ID ->
<id> - 标签健康 —— 纯计数器(崩溃次数、错误次数、状态码直方图)。不包含页面内容、不包含 URL、不包含用户数据。
重复问题通过堆栈签名检测,并获得一个 +1 注释,而不是新问题。
# Disable telemetry
export CAMOFOX_CRASH_REPORT_ENABLED=false
# Point to your own endpoint (see below)
export CAMOFOX_CRASH_REPORT_URL=https://your-endpoint.example.com/report
# Adjust rate limit (default: 10 per hour)
export CAMOFOX_CRASH_REPORT_RATE_LIMIT=5
自托管遥测端点
在您自己的 GitHub 仓库中归档遥测报告,而不是 jo-inc/camofox-browser:
-
创建一个 GitHub 应用 -- 设置 -> 开发者设置 -> GitHub 应用 -> 新建
- 权限:仓库 -> 问题 -> 读与写
- 取消勾选 Webhook -> 激活(不需要)
- 点击 生成密钥 -- 下载
.pem文件 - 在目标仓库中安装应用(安装应用 -> 选择仓库)
- 记下您的 应用 ID(应用通用页面上的数字)和 安装 ID(安装后 URL 中的
github.com/settings/installations/{id})
-
部署端点 -- 克隆此仓库并部署 worker:
cd workers/crash-reporter # Edit wrangler.toml: set account_id to your Cloudflare account ID npx wrangler deploy该 worker 是一个单独的 TypeScript 文件,没有任何 npm 依赖。它也可以在 Deno、Bun 或任何支持 Web Crypto API 的运行时环境中运行。
-
设置 worker 密钥:
cd workers/crash-reporter echo "YOUR_APP_ID" | npx wrangler secret put GH_APP_ID echo "YOUR_INSTALL_ID" | npx wrangler secret put GH_INSTALL_ID # Key must be PKCS#8 DER base64 (not raw PEM) openssl pkcs8 -topk8 -inform PEM -outform DER -nocrypt -in your-app.pem | \ base64 | tr -d '\n' | npx wrangler secret put GH_PRIVATE_KEY # File issues in your repo echo "your-org/your-repo" | npx wrangler secret put GH_REPO -
将 camofox-browser 指向您的端点:
export CAMOFOX_CRASH_REPORT_URL=https://your-worker.your-subdomain.workers.dev/report -
验证:
curl https://your-worker.your-subdomain.workers.dev/health # -> {"status":"ok"}
结构化日志
所有日志输出都是 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)被排除在请求日志之外,以减少噪音。
基础浏览
# Create a tab
curl -X POST http://localhost:9377/tabs \
-H 'Content-Type: application/json' \
-d '{"userId": "agent1", "sessionKey": "task1", "url": "https://example.com"}'
# Get accessibility snapshot with element refs
curl "http://localhost:9377/tabs/TAB_ID/snapshot?userId=agent1"
# -> { "snapshot": "[button e1] Submit [link e2] Learn more", ... }
# Click by ref
curl -X POST http://localhost:9377/tabs/TAB_ID/click \
-H 'Content-Type: application/json' \
-d '{"userId": "agent1", "ref": "e1"}'
# Type into an element
curl -X POST http://localhost:9377/tabs/TAB_ID/type \
-H 'Content-Type: application/json' \
-d '{"userId": "agent1", "ref": "e2", "text": "hello", "pressEnter": true}'
# Navigate with a search macro
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"}'
应用程序接口
标签生命周期
| 方法 | 端点 | 描述 |
|---|---|---|
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 |
列出 <img> 元素。查询参数:includeData=true(返回内联数据 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] [music] We're no strangers to love [music]\n...", "video_title": "...", "total_words": 548 }
在可用时使用 yt-dlp(快速,无需浏览器)。如果未安装 yt-dlp,则回退到基于浏览器的拦截方法——由于 YouTube 广告前置,这种方法更慢且不太可靠。
服务器
| 方法 | 端点 | 描述 |
|---|---|---|
GET |
/health |
健康检查 |
POST |
/start |
启动浏览器引擎 |
POST |
/stop |
停止浏览器引擎 |
会话
| 方法 | 端点 | 描述 |
|---|---|---|
GET |
/tabs/:tabId/downloads |
列出已捕获的浏览器下载和保存的当前资源 PDF |
POST |
/tabs/:tabId/fetch-current-resource |
使用浏览器会话认证将当前内联 PDF 保存为下载工件 |
GET |
/sessions/:userId/storage_state |
导出已持久化的浏览器存储(VNC 插件) |
DELETE |
/sessions/:userId/storage_state |
重置实时会话并删除其持久化的浏览器存储(持久化插件) |
搜索宏
@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.config.json 中进行调整:
{
"newPageTimeoutMs": 10000
}
newPageTimeoutMs 控制标签创建等待 Firefox 创建页面的时间。如果上下文没有响应,Camofox 仅替换该用户的上下文并重试一次。默认值为 10 秒。
环境变量
| 变量 | 描述 | 默认 |
|---|---|---|
CAMOFOX_PORT |
服务器端口 | 9377 |
PORT |
服务器端口(回退,用于像 Fly.io、Railway 这样的平台) | 9377 |
CAMOFOX_BIND_HOST |
可选的服务器绑定主机。设置为 127.0.0.1 以仅回环访问,或设置为 0.0.0.0 以在所有接口上启用 IPv4。当未设置时,Node 使用其默认的所有接口绑定。 |
- |
CAMOFOX_API_KEY |
启用 Cookie 导入端点(如果未设置则禁用) | - |
CAMOFOX_ADMIN_KEY |
需要用于 POST /stop |
- |
CAMOFOX_ACCESS_KEY |
如果设置,所有路由(除了 /health、cookie 导入和 /stop)都需要 Authorization: Bearer <key>。允许您安全地在回环之外公开服务器。 |
- |
CAMOFOX_EVALUATE_MAX_BODY_SIZE |
POST /tabs/:tabId/evaluate 的最大 JSON 请求体大小;其他 JSON 路由仍限制为 100kb。 |
1mb |
CAMOFOX_LOCALE |
明确配置的直接会话身份的本地化语言。必须与 CAMOFOX_TIMEZONE 一起设置。 |
- |
CAMOFOX_TIMEZONE |
明确配置的直接会话身份的 IANA 时区。必须与 CAMOFOX_LOCALE 一起设置。 |
- |
CAMOUFOX_EXECUTABLE |
使用的外部 Camoufox 可执行文件,代替下载/启动捆绑缓存。必须指向包含同级资源的 Camoufox 包。 | - |
CAMOUFOX_EXECUTABLE_PATH |
CAMOUFOX_EXECUTABLE 的兼容别名 |
- |
CAMOFOX_EXECUTABLE_PATH |
CAMOUFOX_EXECUTABLE 的兼容别名 |
- |
CAMOFOX_DISABLE_DEFAULT_ADDONS |
设置为 1/true 跳过下载和启动默认的 uBlock Origin (UBO) 插件。在 addons.mozilla.org 下载不可靠或不需要的部署中很有用(否则下载失败会留下损坏的插件缓存,阻止启动)。 |
0 |
CAMOFOX_COOKIES_DIR |
cookie 文件目录 | ~/.camofox/cookies |
CAMOFOX_UPLOADS_DIR |
允许 POST /tabs/:tabId/upload 文件附件的目录。目录之外的路径,包括符号链接逃逸,将被拒绝。 |
~/.camofox/uploads |
CAMOFOX_PROFILE_DIR |
持久化会话配置文件目录 | ~/.camofox/profiles |
CAMOFOX_TRACES_DIR |
会话跟踪压缩文件目录 | ~/.camofox/traces |
CAMOFOX_TRACES_MAX_BYTES |
每个跟踪的最大大小,超过将在下次启动时被删除 | 52428800 (50MB) |
CAMOFOX_TRACES_TTL_HOURS |
启动时清理早于此时间的跟踪 | 24 |
MAX_SESSIONS |
最大并发浏览器会话数 | 50 |
MAX_TABS_PER_SESSION |
每个会话的最大标签页数 | 10 |
SESSION_TIMEOUT_MS |
会话无活动超时(0 = 永不) | 600000 (10分钟) |
BROWSER_IDLE_TIMEOUT_MS |
空闲时关闭浏览器(0 = 永不) | 300000 (5分钟) |
CAMOFOX_INTERACTIVE |
交互式浏览器模式:desktop 打开本地真实 Camoufox 窗口;off 保持正常无头行为 |
off |
HANDLER_TIMEOUT_MS |
任何处理程序的最大时间 | 30000 (30秒) |
MAX_CONCURRENT_PER_USER |
每个用户的并发请求上限 | 3 |
MAX_OLD_SPACE_SIZE |
Node.js V8 堆内存限制 (MB) | 128 |
PROXY_STRATEGY |
代理模式:backconnect(轮换粘性会话)或空白(单一端点) |
- |
PROXY_PROTOCOL |
代理协议:http、https、socks4 或 socks5 |
http |
PROXY_PROVIDER |
会话格式提供商名称(例如 decodo) |
decodo |
PROXY_HOST |
代理主机名或 IP(简单模式) | - |
PROXY_PORT |
代理端口(简单模式) | - |
PROXY_USERNAME |
代理认证用户名 | - |
PROXY_PASSWORD |
代理认证密码 | - |
PROXY_BACKCONNECT_HOST |
Backconnect 网关主机名 | - |
PROXY_BACKCONNECT_PORT |
Backconnect 网关端口 | 7000 |
PROXY_COUNTRY |
代理地理定位的目标国家 | - |
PROXY_STATE |
代理地理定位的目标州/地区 | - |
TAB_INACTIVITY_MS |
关闭闲置时间超过此值的标签页 | 300000 (5分钟) |
CAMOFOX_CRASH_REPORT_ENABLED |
启用匿名崩溃/挂起遥测(设为 false 以禁用) |
true |
CAMOFOX_CRASH_REPORT_URL |
遥测端点(自托管端点) | https://camofox-telemetry.askjo.workers.dev/report |
CAMOFOX_CRASH_REPORT_REPO |
遥测问题的 GitHub 仓库 | jo-inc/camofox-browser |
CAMOFOX_CRASH_REPORT_RATE_LIMIT |
每小时最大遥测报告数 | 10 |
ENABLE_VNC |
启用 VNC 插件以获取交互式浏览器访问 (1) |
- |
VNC_PASSWORD |
VNC 访问密码(建议在生产环境中使用) | - |
VNC_BIND |
noVNC 监听地址;设置为 0.0.0.0 可通过公开的容器端口访问 noVNC |
127.0.0.1 |
VNC_RFB_BIND |
原生 VNC 监听地址;非回环地址需要 VNC_PASSWORD,并且不会随 VNC_BIND 变化 |
127.0.0.1 |
NOVNC_PORT |
noVNC Web 界面端口 | 6080 |
交互式桌面浏览器
Camofox 默认是无头的。在有本地图形桌面的机器上,可选择显示 Camoufox 窗口:
CAMOFOX_INTERACTIVE=desktop npm start
或者在 camofox.config.json 中设置相同的偏好并重启服务器:
{
"interactive": { "mode": "desktop" }
}
环境变量会覆盖文件设置。设置 CAMOFOX_INTERACTIVE=off 可恢复正常的无头行为。此模式适用于使用同一台机器的人;它不会暴露远程浏览器控制服务。
架构
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分钟关闭,并在下一个请求时重新启动。
当会话的标签限制达到时,最旧/最少使用的标签页会自动回收,而不是返回错误——这样长期运行的代理会话就不会遇到死胡同。
安全模型
本节记录了 Camofox-browser 为用户和自动安全审查者所面临的安全边界。
代码隔离
所有 process.env 的读取都集中在 lib/config.js 中。所有 child_process 的使用都在 lib/launcher.js(服务器子进程)和 plugins/youtube/youtube.js(yt-dlp)中。主 server.js 有路由处理程序,但没有任何 process.env 的读取,也没有任何 child_process 的导入。没有单个文件同时包含环境/凭据访问和网络发送。
没有嵌入的秘密
此软件包中不包含任何凭证、私钥、API 令牌或签名密钥。所有秘密信息均在运行时通过环境变量提供(CAMOFOX_API_KEY、CAMOFOX_ACCESS_KEY),或者作为 Cloudflare Worker 环境秘密提供(遥测端点 GitHub 应用密钥)。
Cookie导入默认是禁用的
Cookie 导入端点(POST /sessions/:userId/cookies)需要 CAMOFOX_API_KEY 才能访问。如果未设置该环境变量,服务器会以 HTTP 403 拒绝所有 cookie 导入请求。Cookie 文件从一个受限目录(~/.camofox/cookies/)中读取,该目录具有路径遍历保护——尝试逃出目录的操作会被阻止。每次请求最多允许 500 个 cookie,文件大小限制为 5MB。
访问控制
CAMOFOX_ACCESS_KEY 为所有路由(除了 /health)提供全局承载令牌认证。设置后,每个请求都必须包含 Authorization: Bearer <key>。建议在任何超出本地主机的部署中使用。
二进制下载
Camoufox 浏览器引擎(约 300MB)在运行 npm install 时由 camoufox-js 下载,该 npm 包由 Camoufox 项目 维护。它从 官方 GitHub 发布 下载,完整性验证由 camoufox-js 处理。没有自定义下载 URL,没有 URL 缩短器,没有直接 IP 地址。
遥测
匿名崩溃/卡顿遥测数据会发送到 Cloudflare Worker 端点。端点源码位于 此仓库 并且可审计。验证方法:在端点执行 GET /source 会返回已部署的提交哈希和 sha256,你可以与仓库进行比对。报告器 (lib/reporter.js L28-290) 采用严格匿名化:私有域名会进行 HMAC 哈希(不可逆),路径会被移除,令牌/IP/邮箱会被遮蔽。不会发送任何页面内容、Cookie 或用户数据。可通过 CAMOFOX_CRASH_REPORT_ENABLED=false 禁用,或通过 CAMOFOX_CRASH_REPORT_URL 指向你自己的端点。
会话持久性
持久化插件将 cookies 和 localStorage 保存到 ~/.camofox/profiles/<hashed-userId>/,以便经过认证的会话在浏览器重启后仍然有效。UserId 会被哈希用于目录名称。可以通过在 camofox.config.json 中从插件数组中移除 persistence 来禁用。
网络访问
外部连接包括: (1) 代理访问的 URL(核心功能),(2) 遥测端点(匿名,可选择退出)。内部连接:REST API 监听 9377 端口,默认绑定到所有接口,或在配置 CAMOFOX_BIND_HOST 时绑定到指定接口,可选地使用 CAMOFOX_ACCESS_KEY 保护。
子进程使用
可能会启动两个子进程: (1) Camoufox 浏览器引擎(核心功能,lib/launcher.js),(2) yt-dlp 用于 YouTube 字幕提取(可选,plugins/youtube/youtube.js)。两者都在独立文件中隔离,独立于路由处理器。
测试
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
致谢
- Camoufox - 基于 Firefox 的浏览器,带有 C 反检测功能
- 捐赠给 Camoufox 的原始创作者 daijro
- OpenClaw - 开源 AI 代理框架
加密货币诈骗警告
现在,一些可疑的人正在用名为“Camofox”的加密代币做可疑的事情,因为这个项目开始受到关注。Camofox 不是加密项目,也永远不会成为加密项目。任何使用 Camofox 名称的代币、硬币或 NFT 都与我们无关。
许可证
麻省理工学院
- 本文标题:camofox-browser - 用于 AI 代理的隐身无头浏览器——绕过 Cloud
- 本文链接:https://www.cn121.com/agent/jo-inc-camofox-browser.html
- 原项目:jo-inc/camofox-browser 版权归原作者 jo-inc 及贡献者所有
- 收录信息:本站于 2026-10-28 收录本项目,本页所列协议与仓库指标均为收录当时的状态;该日期之后原项目的版本更新与协议变更,本页不作同步。
- 开源协议:收录时本项目采用 MIT(查看 LICENSE 原文),本站转载其原始文档(未改动文字,仅剔除图片与无关章节);使用、修改、分发请以该仓库 LICENSE 原文为准。
- 站点出处:本文首发于 OneTwoOne,收录自 GitHub 开源项目 jo-inc/camofox-browser。
- 内容说明:本页正文为原项目 README 原文(英文),本站后续会翻译为中文(当前尚未译出),仅剔除了图片与赞助等无关章节、并把相对链接改为绝对地址;页首简介为机器翻译自仓库描述。
- 引用声明:商业转载、第三方聚合或 AI 检索训练引用时,请务必保留以上来源出处、本文永久链接,以及原项目的版权声明与许可信息。
- 下架通道:若原项目此后变更或收紧了许可协议、或作者/权利人认为本站的收录方式(译文、排版适配、简介翻译等)超出其授权范围,请通过 xyd3302001@163.com 发送下架通知,并附上项目地址与本页链接。本站核实后将第一时间删除本页内容,或改为不复制原文的目录性收录;署名更正等其他要求可一并提出。