隐身浏览
面向 AI Agent 的隐身无头浏览器,绕过 Cloudflare 与反爬,可替代 Puppeteer/Playwright
由 jo 团队开发——jo 是一个个人 AI 代理,一半运行在你的 Mac 上,一半运行在专属于你的云机器上,无需任何维护。可在 macOS、Telegram、WhatsApp 和电子邮件上使用。免费试用 beta 版 ->
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 看到之前就被伪造了。没有垫片、没有包装器、没有任何暴露迹象。
本项目将该引擎封装成一个专为代理构建的 REST API:可访问性快照替代臃肿的 HTML、用于点击的稳定元素引用、以及针对常见网站的搜索宏。
特性
- C++ 反检测 —— 绕过 Google、Cloudflare 以及大多数机器人检测
- 元素引用 —— 稳定的
e1、e2、e3标识符,用于可靠的交互 - 令牌高效 —— 可访问性快照比原始 HTML 大约小 90%
- 可在任何地方运行 —— 懒加载浏览器启动 + 空闲时关闭,空闲时内存约 40MB。设计用于与你技术栈的其他部分共享同一台机器——树莓派、5 美元 VPS、共享基础设施。
- 会话隔离 —— 每个用户独立的 cookie/存储
- Cookie 导入 —— 注入 Netscape 格式的 cookie 文件,用于已认证浏览
- 代理 + GeoIP —— 通过住宅代理路由流量,自动设置地区/时区
- 结构化日志 —— 包含请求 ID 的 JSON 日志行,适用于生产环境可观测性
- YouTube 字幕 —— 通过 yt-dlp 从任何 YouTube 视频提取字幕,无需 API 密钥
- 搜索宏 ——
@google_search、@youtube_search、@amazon_search、@reddit_subreddit等 10 余种 - 快照截图 —— 在可访问性快照旁附带一个 base64 PNG 截图
- 大页面处理 —— 自动截断快照,并支持基于偏移量的分页
- 下载捕获 —— 捕获浏览器的下载并通过 API 获取(可选的 inline base64)
- DOM 图片提取 —— 列出
<img>的 src/alt,并可选择返回 inline data URL - 随处部署 —— Docker、Fly.io、Railway
- VNC 交互式登录 —— 通过 noVNC 可视登录网站,导出存储状态供代理复用
- OpenAPI 文档 —— 在
/openapi.json自动生成规范,交互式文档位于/docs - 结构化提取 ——
POST /tabs/:tabId/extract使用 JSON Schema,通过x-ref将属性映射到快照引用 - 会话追踪 —— 可选基于会话的 Playwright 追踪捕获(截图 + DOM 快照 + 网络),提供 API 端点用于列出、获取和删除追踪 zip
- 遥测 —— 通过 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 # 首次运行时会下载 Camoufox(约 300MB)
默认端口为 9377。参见 环境变量 了解所有选项。
注意: postinstall 脚本会为自己取消设置
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD,然后获取 Camoufox 二进制文件。如果没有这个覆盖,已导出的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 约 300 秒)。
# 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
在 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 后端的 Docker Desktop for Windows。行尾: 本项目包含一个
.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
项目包含一个 railway.toml。它使用 Dockerfile.ci(在构建时下载二进制文件),并自动将 Railway 的 PORT 环境变量映射到 CAMOFOX_PORT。
# 安装 Railway CLI,然后:
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 密钥中设置CAMOFOX_API_KEY。
Cookie 导入默认禁用。 如果未设置
CAMOFOX_API_KEY,服务端会拒绝所有 Cookie 请求并返回 403。
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 的 Cookie
代理会调用 camofox_import_cookies -> 读取文件 -> 使用 Bearer token 向服务端发送 POST 请求 -> Cookie 被注入到浏览器会话中。后续对 linkedin.com 的 camofox_create_tab 调用将携带认证信息。
工作原理
~/.camofox/cookies/linkedin.txt (磁盘上的 Netscape 格式文件)
|
v
camofox_import_cookies tool (解析文件,按域名过滤)
|
v POST /sessions/:userId/cookies
| Authorization: Bearer <CAMOFOX_API_KEY>
| Body: { cookies: [Playwright cookie objects] }
v
camofox server (验证、清理、注入)
|
v context.addCookies(...)
|
Camoufox browser session (已认证的浏览)
cookiesPath相对于 Cookie 目录解析——超出该目录的路径穿越会被阻止- 每个请求最多 500 个 Cookie,文件大小上限为 5MB
- Cookie 对象会被清理,仅保留 Playwright 允许的字段
会话持久化
默认情况下,camofox 将每个用户的 Cookie 和 localStorage 持久化到 ~/.camofox/profiles/。会话在浏览器重启后依然存在——仅需登录一次(通过 Cookie 或 VNC),后续会话会自动恢复已认证状态。
~/.camofox/
|-- cookies/ # 引导 Cookie 文件(Netscape 格式)
\-- profiles/ # 持久化的会话状态(自动管理)
\-- <hashed-userId>/
\-- storage_state.json
通过 CAMOFOX_PROFILE_DIR 覆盖目录,或在持久化插件配置中设置 "profileDir"。若要禁用持久化,请在 camofox.config.json 中设置 "persistence": { "enabled": false }。
会话追踪
捕获会话中每个操作的 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}'
追踪会在会话关闭时写入。先关闭会话以刷新追踪,然后列出、获取和查看:
# 关闭会话以刷新追踪
curl -X DELETE http://localhost:9377/sessions/agent1
# 列出追踪文件
curl http://localhost:9377/sessions/agent1/traces
# {"traces":[{"filename":"trace-2026-04-18T04-05-00-...zip","sizeBytes":42810,"createdAt":...}]}
# 下载(Content-Type: application/zip)
curl http://localhost:9377/sessions/agent1/traces/trace-2026-04-18T04-05-00-abc.zip > session.zip
# 在 Playwright 的 Trace Viewer 中查看
npx playwright show-trace session.zip
# 删除
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
对于 Fly.io:
fly secrets set CAMOFOX_API_KEY="your-generated-key"
对于 Railway:
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 匹配 - 浏览器指纹(语言、时区、坐标)与代理位置保持一致
- 如果没有代理,则默认使用
en-US、America/Los_Angeles、旧金山坐标
遥测
浏览器自动化失败的方式难以预测——Cloudflare 挑战、站点改版导致选择器失效、重定向循环、对话框风暴、渲染器崩溃。涉及范围广,失败模式多样。如果没有遥测,唯一的信号就是“没成功”。
遥测为我们提供结构化的数据,包括哪些站点失败、如何失败以及失败频率,这样我们可以优先修复那些实际影响用户的模式。它会在以下情况自动创建 GitHub Issue:
- 未捕获的异常导致进程崩溃
- 事件循环停滞超过 5 秒(看门狗检测)
- 挫败模式——同一标签页连续 3 次失败(超时、上下文已死、导航中断)
每个报告包含失败类型、堆栈跟踪、标签页健康计数器(HTTP 状态码直方图、控制台错误、请求失败、重定向深度)以及目标 URL——全部经过匿名化处理。
工作原理
遥测数据发送到位于 https://camofox-telemetry.askjo.workers.dev 的轻量级 Cloudflare Worker 端点。该端点将 GitHub App 凭据作为环境机密保存——本包中不包含任何机密。
lib/reporter.js(客户端,无机密)
| 匿名化 -> POST https://camofox-telemetry.askjo.workers.dev/report
v
Cloudflare Worker(持有 GitHub App 密钥)
| 验证 -> 限速 -> 去重 -> 创建 GitHub Issue
v
GitHub Issue 已创建
端点源代码位于本仓库的 workers/crash-reporter/index.ts。
验证
您无需信任我们——可以验证运行中的端点正在执行的代码:
# 1. 询问端点它正在运行的代码是什么
curl https://camofox-telemetry.askjo.workers.dev/source
# -> { "commit": "abc1234", "sha256": "e3b0c44...", "source": "https://github.com/..." }
# 2. 将 sha256 与本仓库中的源代码进行比较
sha256sum workers/crash-reporter/index.ts
# 3. 检查提交是否与 CI 部署的匹配
# 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](仅保留数量)。从不包含任何键、值或路径内容。 - 文件路径 -> 仅保留文件名(
<路径>/server.js) - 令牌、机密、API 密钥 ->
<token> - IP、电子邮件、环境变量 -> 已编辑
- Docker/Fly 机器 ID ->
<id> - 标签页健康 —— 纯计数器(崩溃次数、错误次数、状态码直方图)。不包含页面内容、URL 或用户数据。
重复的 Issue 通过堆栈签名检测,并添加 +1 评论而不是创建新 Issue。
# 禁用遥测
export CAMOFOX_CRASH_REPORT_ENABLED=false
# 指向您自己的端点(见下方)
export CAMOFOX_CRASH_REPORT_URL=https://your-endpoint.example.com/report
# 调整速率限制(默认:每小时 10 次)
export CAMOFOX_CRASH_REPORT_RATE_LIMIT=5
自托管遥测端点
要将遥测报告归档到您自己的 GitHub 仓库中,而不是 jo-inc/camofox-browser:
-
创建一个 GitHub App —— Settings -> Developer settings -> GitHub Apps -> New
- 权限:Repository -> Issues -> Read & Write
- 取消勾选 Webhook -> Active(不需要)
- 点击 Generate a key —— 下载一个
.pem文件 - 在您的目标仓库上安装该应用(Install App -> 选择仓库)
- 记下您的 App ID(应用常规页面上的数字)和 Installation ID(安装后 URL 中的数字:
github.com/settings/installations/{id})
-
部署端点 —— 克隆本仓库并部署 Worker:
cd workers/crash-reporter # 编辑 wrangler.toml:将 account_id 设置为您的 Cloudflare 账户 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 # 密钥必须是 PKCS#8 DER base64 格式(不是原始 PEM) openssl pkcs8 -topk8 -inform PEM -outform DER -nocrypt -in your-app.pem | \ base64 | tr -d '\n' | npx wrangler secret put GH_PRIVATE_KEY # 在你的仓库中提交 issues 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)不会记录到请求日志中,以减少噪音。
基本浏览操作
# 创建一个标签页
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 | 列出 <img> 元素。查询参数: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] [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 | 停止浏览器引擎 |
会话管理
| 方法 | 端点 | 描述 |
|---|---|---|
POST | /sessions/:userId/cookies | 向用户会话添加 cookies(Playwright cookie 对象) |
GET | /sessions/:userId/storage_state | 导出 cookies + localStorage(VNC 插件) |
搜索宏
@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、Railway 等平台) | 9377 |
CAMOFOX_API_KEY | 启用 cookie 导入端点(未设置时禁用) | - |
CAMOFOX_ADMIN_KEY | POST /stop 需要此值 | - |
CAMOFOX_ACCESS_KEY | 如果设置,所有路由(除了 /health、cookie 导入和 /stop)都需要 Authorization: Bearer <key>。允许你在回环地址之外安全地暴露服务。 | - |
CAMOUFOX_EXECUTABLE | 使用外部 Camoufox 可执行文件,而非下载/启动内置缓存。必须指向包含同级资源的 Camoufox 包。 | - |
CAMOUFOX_EXECUTABLE_PATH | CAMOUFOX_EXECUTABLE 的兼容别名 | - |
CAMOFOX_EXECUTABLE_PATH | CAMOUFOX_EXECUTABLE 的兼容别名 | - |
CAMOFOX_COOKIES_DIR | cookie 文件存储目录 | ~/.camofox/cookies |
CAMOFOX_PROFILE_DIR | 持久化会话配置文件存储目录 | ~/.camofox/profiles |
CAMOFOX_TRACES_DIR | 会话跟踪 zip 文件存储目录 | ~/.camofox/traces |
CAMOFOX_TRACES_MAX_BYTES | 每个跟踪文件的最大大小,下次启动时若超出将被删除 | 52428800(50MB) |
CAMOFOX_TRACES_TTL_HOURS | 超过此时间的跟踪文件在启动时会被清理 | 24 |
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分钟) |
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 访问密码(建议生产环境使用) | - |
NOVNC_PORT | noVNC Web UI 端口 | 6080 |
架构
Browser Instance (Camoufox)
\-- User Session (BrowserContext) - 隔离的 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 App key)。
Cookie 导入默认禁用
Cookie 导入端点(POST /sessions/:userId/cookies)受 CAMOFOX_API_KEY 保护。如果未设置此环境变量,服务器将拒绝所有 cookie 导入请求并返回 HTTP 403。Cookie 文件从沙盒目录(~/.camofox/cookies/)读取,并带有路径遍历保护——试图逃逸该目录将被阻止。每个请求最多 500 个 cookie,文件大小限制 5MB。
访问控制
CAMOFOX_ACCESS_KEY 为所有路由(/health 除外)提供全局 Bearer 令牌认证。设置后,每个请求必须包含 Authorization: Bearer <key>。建议在任何非 localhost 的部署中使用。
二进制文件下载
Camoufox 浏览器引擎(约 300MB)在 npm install 时由 camoufox-js 下载,这是一个由 Camoufox 项目 维护的 npm 包。它从 官方 GitHub 发布页 下载,完整性校验由 camoufox-js 负责。无自定义下载 URL、无 URL 缩短器、无原始 IP 地址。
遥测
匿名化的崩溃/挂起遥测发送至 Cloudflare Worker 端点。端点源代码位于本仓库中,可供审计。验证方法:在端点上执行 GET /source 将返回已部署的提交哈希和 sha256,以便与仓库对比。报告器(lib/reporter.js 第28-290行)应用了偏执级别的匿名化:私有域名经过 HMAC 哈希(不可逆)、路径被剥离、令牌/IP/电子邮件被过滤。从不发送页面内容、cookie 或用户数据。可通过 CAMOFOX_CRASH_REPORT_ENABLED=false 禁用,或通过 CAMOFOX_CRASH_REPORT_URL 指向自己的端点。
会话持久化
持久化插件将 cookies 和 localStorage 保存至 ~/.camofox/profiles/<hashed-userId>/,这样经过身份验证的会话可在浏览器重启后存活。用户 ID 经过哈希处理用作目录名。可通过在 camofox.config.json 的 plugins 数组中移除 persistence 来禁用。
网络访问
出站连接: (1) 代理导航到的 URL(核心功能),(2) 遥测端点(匿名化,可选择退出)。入站:REST API 监听 localhost:9377(默认),可选地由 CAMOFOX_ACCESS_KEY 保护。
子进程使用
可能生成两个子进程: (1) Camoufox 浏览器引擎(核心功能,lib/launcher.js),(2) 用于 YouTube 字幕提取的 yt-dlp(可选,plugins/youtube/youtube.js)。两者都在与路由处理器分离的专用文件中隔离。
测试
npm test # 所有测试
npm run test:e2e # 仅 e2e 测试
npm run test:live # 实时站点测试(Google, macros)
npm run test:debug # 带服务器输出的测试
npm
npm install @askjo/camofox-browser
致谢
- Camoufox - 基于 Firefox 的浏览器,带有 C++ 反检测功能
- 向 Camoufox 原创者 daijro 捐赠
- OpenClaw - 开源 AI 代理框架
加密货币诈骗警告
随着该项目受到关注,有可疑人员正在使用名为 “Camofox” 的加密货币代币进行可疑活动。Camofox 不是加密项目,也永远不会成为加密项目。 任何使用 Camofox 名称的代币、币种或 NFT 均与我们无关。
许可证
MIT
