小马的 AI 工具集 给 Agent 学习

隐身浏览

面向 AI Agent 的隐身无头浏览器,绕过 Cloudflare 与反爬,可替代 Puppeteer/Playwright

隐身浏览
类型 6,782 星标 更新 2026-06-03 许可 MIT 原仓库 主页
camofox-browser

camofox-browser

面向 AI 代理的反检测浏览器服务,基于 Camoufox

License: MIT GitHub stars npm version GitHub last commit

站在 Camoufox 这个强大的肩膀上——一个在 C++ 层面进行指纹伪造的 Firefox 分支。


Jo

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 以及大多数机器人检测
  • 元素引用 —— 稳定的 e1e2e3 标识符,用于可靠的交互
  • 令牌高效 —— 可访问性快照比原始 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-dlpYouTube 字幕提取(快速路径)pip install yt-dlpbrew 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_PATHCAMOFOX_EXECUTABLE_PATH。这对于 NixOS 路径(例如 /nix/store/.../camoufox-bin)很有用;可执行文件必须来自包含 properties.jsonversion.jsonfontconfig/ 的 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-scriptsCAMOUFOX_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 foundset: 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 导入 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 自动将 localetimezonegeolocation 设置为与代理出口 IP 匹配
  • 浏览器指纹(语言、时区、坐标)与代理位置保持一致
  • 如果没有代理,则默认使用 en-USAmerica/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

  1. 创建一个 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}
  2. 部署端点 —— 克隆本仓库并部署 Worker:

    cd workers/crash-reporter
    # 编辑 wrangler.toml:将 account_id 设置为您的 Cloudflare 账户 ID
    npx wrangler deploy

    该 Worker 是一个单一的 TypeScript 文件,零 npm 依赖。它也可以在 Deno、Bun 或任何支持 Web Crypto API 的运行时上运行。

  3. 设置 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
  4. 将 camofox-browser 指向你的端点:

    export CAMOFOX_CRASH_REPORT_URL=https://your-worker.your-subdomain.workers.dev/report
  5. 验证:

    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=Nlimit=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_KEYPOST /stop 需要此值-
CAMOFOX_ACCESS_KEY如果设置,所有路由(除了 /health、cookie 导入和 /stop)都需要 Authorization: Bearer <key>。允许你在回环地址之外安全地暴露服务。-
CAMOUFOX_EXECUTABLE使用外部 Camoufox 可执行文件,而非下载/启动内置缓存。必须指向包含同级资源的 Camoufox 包。-
CAMOUFOX_EXECUTABLE_PATHCAMOUFOX_EXECUTABLE 的兼容别名-
CAMOFOX_EXECUTABLE_PATHCAMOUFOX_EXECUTABLE 的兼容别名-
CAMOFOX_COOKIES_DIRcookie 文件存储目录~/.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_SIZENode.js V8 堆内存限制(MB)128
PROXY_STRATEGY代理模式:backconnect(轮换粘性会话)或空白(单个端点)-
PROXY_PROVIDER会话格式的提供商名称(例如 decododecodo
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_PASSWORDVNC 访问密码(建议生产环境使用)-
NOVNC_PORTnoVNC 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_KEYCAMOFOX_ACCESS_KEY)提供,或者是 Cloudflare Worker 环境机密(遥测端点 GitHub App key)。

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

致谢

加密货币诈骗警告

随着该项目受到关注,有可疑人员正在使用名为 “Camofox” 的加密货币代币进行可疑活动。Camofox 不是加密项目,也永远不会成为加密项目。 任何使用 Camofox 名称的代币、币种或 NFT 均与我们无关。

许可证

MIT

在 GitHub 查看完整项目