把“网页上的 AI 服务”包装成 OpenAI 兼容接口。
如果你已经有:
- 代理
- 站点登录态(例如 Claude 的
sessionKey) - 想让
OpenAI SDK、Cursor、或者任何兼容/v1/chat/completions的客户端直接调用
那这个项目就是干这个的。
当前仓库默认内置的是 claude 插件,也就是说你可以像调 OpenAI 一样去调 Claude 网页端。
简单说,它在本地帮你做了这几件事:
- 启动一个真实浏览器
- 用你的代理和登录态打开网页
- 帮你维持网页会话
- 对外暴露一个 OpenAI 风格的 HTTP API
你调用的是:
POST /claude/v1/chat/completions
项目内部实际做的是:
代理组 -> 浏览器 -> Claude tab -> 网页会话
适合下面这类场景:
- 你已经有现成的 OpenAI 客户端,但想换成网页端 Claude
- 你想让 Cursor 之类的工具接一个“看起来像 OpenAI”的后端
- 你不想手写浏览器自动化细节,只想配好账号就能用
如果你只是想“体验 Claude 网页”,这个项目不适合你;它更像一个给开发者用的桥接服务。
代理组一组代理配置,对应一个浏览器进程。type某种站点能力。当前仓库默认是claude。账号某个type的登录态。Claude 一般就是sessionKey。会话一次聊天上下文。项目会尽量复用,重启后如果复用不了,就会自动新建并把历史对话重新发过去。
你需要先准备好:
- Python
3.12+ uv- 指纹浏览器
fingerprint-chromium - 一个可用代理
- 一个可用的 Claude
sessionKey
uv sync如果你是在 Linux 服务器或 Docker 容器里常驻运行,建议配合 Xvfb,这样浏览器不会占用桌面,同时仍然是“有界面浏览器”。
先安装:
sudo apt update
sudo apt install -y xvfb启动时这样跑:
xvfb-run -a -s "-screen 0 1920x1080x24" ./start.sh这样做比直接改成 headless 更稳,原因是:
- 站点兼容性通常更好
- 扩展和登录态行为更接近真实浏览器
- 更不容易因为无头模式差异触发风控
如果你是放在 Docker 里运行,也建议沿用这个思路:
- 容器内跑
Chromium + Xvfb - 持久化
config.yaml、db.sqlite3、浏览器数据目录 - 给 Chromium 足够的共享内存,例如更大的
/dev/shm
项目根目录有一个 config.yaml,它主要控制:
- 服务端口
- 浏览器可执行文件路径
- 调度与回收参数
- mock 调试端口
你至少要确认这一项是对的:
browser:
chromium_bin: '/Applications/Chromium.app/Contents/MacOS/Chromium'当前仓库示例端口是:
server:
port: 9000./start.sh如果启动成功,你会看到类似日志:
服务已就绪,已注册 type: claude
Uvicorn running on http://127.0.0.1:9000
浏览器访问:
http://127.0.0.1:9000/config
在里面填:
proxy_hostproxy_userproxy_passfingerprint_id- 账号
name - 账号
type=claude - 账号
auth.sessionKey
保存后立即生效。
配置时建议注意一件事:
- 不要在同一个
代理组下面堆很多同类型账号
例如同一个 IP 下面挂大量claude账号。这样虽然看起来方便调度,但更容易让站点把这些账号关联到同一出口 IP,增加风控和封号风险。
curl -s "http://127.0.0.1:9000/claude/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "s4",
"stream": false,
"messages": [
{"role":"user","content":"你好,简单介绍一下你自己。"}
]
}'这是正常的。
项目会优先复用旧会话;但如果是下面这些情况,就会新建一个远端会话:
- 这是第一次聊天
- 服务刚刚重启
- 本地缓存已经失效
- 旧会话对应的 tab/账号不可用了
新建会话之后,项目会把你已有的聊天历史重新发给 Claude,所以你在网页上看到“新会话里带着旧记录”,这通常就是预期行为。
这对流式接口是正常的。
因为服务先把 HTTP 响应头发给客户端,然后才一边和 Claude 网页通信、一边流式吐内容。
这里很多新人会搞混。
这是“运行参数”,例如:
- 端口
- 浏览器路径
- 调度并发
- 回收周期
这是“业务配置”,例如:
- 代理组
- 账号
- auth
- 账号冻结时间
也就是说:
config.yaml管程序怎么跑db.sqlite3管你有哪些代理和账号
项目会把会话 ID 以不可见字符的形式附在 assistant 回复末尾。
这意味着:
- 如果你直接用 OpenAI SDK / Cursor,通常不用管
- 如果你自己保存聊天记录,不要把 assistant 文本里的零宽字符清洗掉
否则下一轮请求时,服务端可能没法继续复用会话。
因为这个项目优先追求的是稳定复用网页侧真实能力,不是做一个“看起来更轻”的抓包转发器。
直接封装网络包当然更省资源,但长期使用时通常有这些问题:
-
登录态不只是一个固定 Cookie
很多站点除了sessionKey,还依赖浏览器里的本地存储、页面初始化状态、动态 token 等运行时上下文。 -
前端协议不是静态不变的
有些请求字段是前端 JS 在运行时组装的,站点一改前端逻辑,纯抓包方案就容易失效。 -
更容易碰到风控
真实浏览器天然更接近站点预期的访问行为;纯脚本直连接口更容易因为指纹、请求时序、上下文缺失而被拦。 -
会话复用更自然
这个项目需要长期维持网页会话、支持断点续聊、支持账号切换后的调度。浏览器方案更容易和网页端保持一致。 -
调试成本更低
出问题时可以直接看真实页面、真实登录态、真实请求环境,而不是只盯着抓包日志猜协议哪里变了。
一句话说,这个项目是用更高的资源成本,换更强的稳定性、兼容性和可维护性。
当然,浏览器方案也有代价:
- 更吃内存
- 冷启动更慢
- 调度逻辑更复杂
所以这不是最轻的方案,而是更适合“长期跑、尽量少因为站点变动而失效”的方案。
如果你是在本机直接运行,这个项目会启动真实浏览器,所以看到窗口弹出是正常的。
针对 Linux 用户,推荐直接看上面的“快速开始 -> Linux 用户:推荐用虚拟屏幕启动”。
如果你是在 macOS 本机上运行,更现实的做法通常不是强行隐藏本机窗口,而是把项目放到 Docker 或远程 Linux 环境里运行。
如果你接入的是检测不严格的平台,也可以考虑增加或开启 headless 模式,来减少窗口干扰和图形环境依赖。
对应配置在 config.yaml:
browser:
headless: true但要注意:
headless更适合风控弱、页面行为简单的平台- 对依赖真实浏览器环境的平台,兼容性通常不如“有界面浏览器 + 虚拟屏幕”
- 对
claude这类检测更严格的平台,不推荐优先走headless
所以默认建议仍然是:
claude:优先真实浏览器,Linux下配合Xvfb- 其它检测不严格的平台:可以再评估是否使用
headless
curl "http://127.0.0.1:9000/claude/v1/models"curl -s "http://127.0.0.1:9000/claude/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "s4",
"stream": false,
"messages": [
{"role":"user","content":"给我 3 条学习建议。"}
]
}'curl -N "http://127.0.0.1:9000/claude/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "s4",
"stream": true,
"messages": [
{"role":"user","content":"用三点总结今天的计划。"}
]
}'如果你暂时不想连真实 Claude,可以先启动 mock:
uv run python main_mock.py然后把 config.yaml 里的这两项改成 mock 地址:
claude:
start_url: 'http://127.0.0.1:8002/mock'
api_base: 'http://127.0.0.1:8002/mock'这样主服务就会把请求打到本地 mock,而不是打到真实 Claude。
当前仓库默认支持:
claude
项目本身是插件化的,后续可以继续扩展别的 type,但如果你刚开始使用,先把 Claude 跑通就行。
你不需要一开始就看懂全部代码,先知道这些入口就够了:
- main.py 主服务入口
- main_mock.py mock 服务入口
- core/app.py 应用组装
- core/api/ OpenAI 兼容接口
- core/plugin/ 各种站点插件
- core/runtime/ 浏览器、tab、会话调度
如果你想看更底层的设计,再去读:
uv run ruff check .请不要把这些内容提交到公开仓库:
db.sqlite3- 代理账号密码
sessionKey- 抓包数据
- 任何真实用户对话
另外也不建议这样使用:
- 在同一个代理组下集中堆很多同类型账号
更稳妥的做法是把同类型账号分散到不同 IP / 不同代理组,降低账号关联和风控风险。
如果你是第一次接触这个项目,最推荐的路径是:
- 先把
config.yaml里的端口和浏览器路径改对 - 启动服务
- 去
/config配一个 Claude 账号 - 用上面的
curl发第一条消息 - 成功之后再去看架构文档
先跑通,再读源码,会轻松很多。