bulu-browser
by Jevin0201
README.md
# 布噜浏览器专家
把 buLu(布噜换芯)的浏览器控制能力抽出来,做成 WorkBuddy 可用的技能 + MCP 工具包。
装完之后,你可以直接说「打开这个网页看看」「帮我填这个表单」「把这个页面的内容抓下来」,
WorkBuddy 会真的打开一个 Chrome 去操作,而不是只抓 HTML 文本。
## 作为公共 MCP 使用(任意客户端,无需克隆仓库)
包已发布到 npm,并注册到官方 MCP Registry(`io.github.jevin0201/bulu-browser`)。
要求:Node.js ≥ 20,本机装有 Chrome / Edge / Brave 之一。
在任意支持 stdio MCP 的客户端(Claude Code、Claude Desktop、Cursor、WorkBuddy 等)配置:
```json
{
"mcpServers": {
"bulu-browser": {
"command": "npx",
"args": ["-y", "bulu-browser-mcp"]
}
}
}
```
可选环境变量:
| 变量 | 作用 |
| --- | --- |
| `BULU_CHROME_PATH` | 指定浏览器可执行文件路径 |
| `BULU_PROFILE_DIR` | 自定义浏览器 profile 目录(登录态保存在这里) |
| `BULU_CDP_URL` | 复用已有 Chrome 的 CDP 地址(如 `http://127.0.0.1:9222`),不自带启动 |
| `BULU_ALLOWED_HOSTS` | 逗号分隔的域名白名单,设置后只允许访问这些站点 |
Windows 用户也可以直接克隆本仓库运行 `install.ps1`,获得 WorkBuddy 深度集成(技能 + 连接器 + 桌面快捷方式)。
## 项目结构
```
bulu-browser/
├── install.ps1 一键安装(复制程序 + 注册 MCP + 自检)
├── uninstall.ps1 卸载(可选保留浏览器数据)
├── create-shortcut.ps1 在桌面创建「WorkBuddy 浏览器」快捷方式
├── package.json
├── src/ ── 核心程序,4 个文件共 42KB
│ ├── index.mjs MCP server(stdio JSON-RPC,15 个工具)
│ ├── driver.mjs 浏览器驾驶层(会话、标签、动作、网络)
│ ├── snapshot.mjs 页面快照与 ref 机制
│ └── chrome.mjs Chrome 启动、CDP 探测、锁清理、端口复用
├── skill/SKILL.md 技能说明(教模型什么时候用、怎么用)
├── connector-plugin/ ── 带图标的连接器插件包(见「启用步骤 · 方式 A」)
│ ├── install.mjs 注册到本地连接器市场 / --uninstall 回滚
│ └── connectors/bulu-browser/ai.workbuddy/assets/icon.png ← 连接器图标
├── assets/
│ ├── icons/
│ │ ├── bulu-browser.ico 多尺寸图标(16~256 共 7 档,54KB)
│ │ └── icon-16…512.png 各尺寸 PNG
│ ├── bulu-browser-icon-1024.png 主图标(白底圆角)
│ ├── bulu-browser-transparent-1024.png 透明底版
│ ├── showcase.png 图标效果总览
│ ├── bulu-mascot-upper.png 布噜半身素材(图标用)
│ ├── icon-preview.html 图标源文件(改设计从这里改)
│ └── showcase.html
├── node_modules/playwright-core/ 唯一运行时依赖(12MB)
└── dev/ 开发辅助脚本(生成图标、跑测试)
```
## 它和普通网页抓取的区别
| | WebFetch | 本工具 |
|---|---|---|
| 真实浏览器渲染 | 否 | 是(真 Chrome) |
| 保留登录态 | 否 | 是(持久 profile) |
| 能点击/填表 | 否 | 是 |
| 能截图 | 否 | 是 |
| 能看接口请求 | 否 | 是 |
## 安装了哪些东西
```
%LOCALAPPDATA%\bulu-browser\
├── app\ ← 程序本体(12MB,含 playwright-core)
│ ├── src\index.mjs ← MCP server
│ ├── src\driver.mjs ← 浏览器驾驶层
│ ├── src\snapshot.mjs ← 页面快照 + ref 机制
│ └── src\chrome.mjs ← Chrome 启动与 CDP 探测
└── profile\ ← 浏览器数据(登录态存在这里)
%USERPROFILE%\.workbuddy\
├── skills\bulu-browser\SKILL.md ← 技能说明(教模型怎么用)
├── mcp.json ← 方式 B:裸 MCP 注册
└── plugins\marketplaces\bulu-local\ ← 方式 A:带图标的连接器插件包(本机当前用这个)
```
两种方式只会启用一种;方式 A 注册时会备份并移除 `mcp.json` 里的同名条目,
备份文件为 `mcp.json.bak-bulu`,`--uninstall` 会还原。
## 启用步骤
有**两种接入方式**,二选一(不要同时用,会注册出两个同名连接器)。
### 方式 A:带图标的连接器插件(推荐,本机已启用)
WorkBuddy 的 `mcp.json` 自定义 MCP 通道**不支持图标字段**,所以裸配置在连接器列表里
只能显示通用占位图。带图标的连接器走的是插件包格式:图标按约定路径
`ai.workbuddy/assets/icon.png` 自动读取,不需要在 JSON 里声明。
`connector-plugin/` 就是一个标准的连接器插件包:
```
connector-plugin/
├── .codebuddy-plugin/marketplace.json 本地市场索引
├── connectors/bulu-browser/
│ ├── .codebuddy-plugin/plugin.json 中文名「布噜浏览器」+ 示例指令
│ ├── mcp.json stdio 服务定义
│ ├── ai.workbuddy/assets/icon.png 512×512 ← 图标在这里
│ └── skills/bulu-browser/SKILL.md
└── install.mjs 注册 / 回滚
```
注册与回滚(脚本会先备份索引和 `mcp.json`):
```powershell
node "D:\network shared\bulu-browser\connector-plugin\install.mjs" # 注册
node "D:\network shared\bulu-browser\connector-plugin\install.mjs" --uninstall # 回滚
```
注册时会自动移除 `mcp.json` 里的裸配置,避免重名;回滚时恢复。
改完后**完全退出并重启 WorkBuddy** → 「专家·技能·连接器」→「连接器」→
找到「布噜浏览器」→ 安装 / 信任。状态灯变绿即生效。
> 如果重启后市场里找不到它(部分版本只校验服务端下发的市场索引),跑 `--uninstall`
> 回滚,会自动恢复成下面的方式 B(裸 MCP 配置),工具不会丢,只是没有图标。
### 方式 B:裸 MCP 配置(最通用,无图标)
`install.ps1` 会把 `bulu-browser` 注册进 `~/.workbuddy/mcp.json`。重启 WorkBuddy →
「连接器」→ 右上角「自定义连接器」→「MCP 服务管理」→ 点「信任」。
配置写好后不点信任、不重启都不会加载。
两种方式最终暴露的工具名一致:`mcp__bulu-browser__browser_*`。之后正常说话即可。
## 它是怎么工作的
和 buLu 桌面端同款方案,只是去掉了 UI 层:
```
WorkBuddy ──stdio JSON-RPC──> index.mjs
│
├─ 启动真实 Chrome:--remote-debugging-port + 独立 --user-data-dir
├─ playwright-core connectOverCDP 接管
└─ 给可交互元素打 data-bulu-ref 标记
模型看到:页面文本 + refs 列表(ref / tag / label / placeholder / value / checked)
模型操作:browser_act { ref: "5:12", interaction: "type", text: "..." }
```
关键设计:
- **ref 带版本号**(`5:12` 里的 5 是页面 epoch)。页面一变化就失效,逼着模型重新快照,
避免拿着陈旧元素引用乱点。
- **操作后自动 diff**:返回 url / title / 文本 / 控件数是否变化,模型能判断操作到底生效没有。
- **输入后校验**:填完读回 value,写不进去直接报错,不会静默失败。
- **代理隔离**:启动时清掉 `HTTP_PROXY` 并补 `NO_PROXY`,否则 Playwright 的 WebSocket
升级会被企业代理拦成 404(这是实测踩到的坑)。
## 15 个工具
`browser_open` `browser_snapshot` `browser_act` `browser_batch` `browser_press_key`
`browser_screenshot` `browser_tabs` `browser_select_tab` `browser_close_tab` `browser_request`
`browser_network` `browser_console` `browser_downloads` `browser_get_content` `browser_close`
三个关键能力:
- **`browser_batch`** —— 一次调用执行多步(填表单从 20 次往返降到 1 次)
- **`expect` 参数** —— 操作后由服务端直接验证 url/text/selector,省掉一次确认快照
- **紧凑 ref 格式** —— 元素清单按行输出,实测比 JSON 体积小 42%
## 环境变量
| 变量 | 作用 |
|---|---|
| `BULU_CHROME_PATH` | 指定浏览器可执行文件 |
| `BULU_PROFILE_DIR` | 指定 profile 目录(默认 `%LOCALAPPDATA%\bulu-browser\profile`) |
| `BULU_CDP_URL` | 复用已有 Chrome 的调试端口,如 `http://127.0.0.1:9222` |
| `BULU_ALLOWED_HOSTS` | 域名白名单,逗号分隔(如 `example.com,corp.internal`)。设置后只允许访问这些域名,用于防注入 |
## 登录态、书签与数据持久化
profile 是独立的、持久的,存在 `%LOCALAPPDATA%\bulu-browser\profile`。所有数据都在里面:
- 各站点的登录态(cookie、localStorage)
- 书签
- 浏览器扩展(如果装了)
- 历史记录、密码
**关闭再启动,用的还是这一个浏览器。** 四种情况都验证过:
| 场景 | 结果 |
|---|---|
| 调 `browser_close` 正常关闭 → 再启动 | cookie / localStorage 全部保留 |
| WorkBuddy 结束会话、MCP 进程被终止 → 再启动 | Chrome 独立存活,下次直接复用同一个实例,数据都在 |
| 你手动点 Chrome 窗口的 ✕ → 再启动 | 正常关闭,数据保留 |
| **重启电脑 → 再启动** | 旧的调试端口失效会自动重新拉起;profile 残留锁会被清理并重试,不会卡死 |
实现上有三个关键点,缺一个都会出问题:
1. **启动时 `detached + unref`** —— 让 Chrome 独立于 MCP 进程。否则 WorkBuddy 杀掉 MCP 进程时会把 Chrome 一起带走,来不及落盘。
2. **关闭时走 CDP `Browser.close`** —— 让 Chrome 自己正常退出并保存数据,等它退出(最多 6 秒)才兜底强杀。早期版本直接 `taskkill /F`,实测 cookie 和 localStorage 会双双丢失。
3. **启动失败自动清锁重试** —— 非正常关机后 Chrome 可能在 profile 里留下 `SingletonLock` 等锁文件,导致下次起不来。现在第一次失败会清锁再试一次,超时也从 20 秒放宽到 45 秒(应付刚开机时的冷启动)。
⚠️ 唯一的例外:**断电或任务管理器强杀**时 Chrome 来不及把 cookie 刷到磁盘,最近几秒内的登录可能丢失。这是 Chrome 自身机制,正常重启不会触发。
第一次遇到要登录的网站,Chrome 窗口会弹出来,你在里面登录一次即可,之后一直有效。
**不要向模型提供账号密码**,让它停在登录页,你自己完成。
想彻底清空的话,删掉 `%LOCALAPPDATA%\bulu-browser\profile` 目录即可。
## 随时打开这个专属浏览器
桌面上的「WorkBuddy 浏览器」快捷方式,双击即可打开(或唤醒)这个浏览器,
不需要开始对话。它和技能用的是**同一个 profile**,登录态、书签都共享。
命令行等价操作:
```powershell
# 打开浏览器(可选带网址)
node "$env:LOCALAPPDATA\bulu-browser\app\src\index.mjs" --open https://www.bing.com
# 查看它是否正在运行
node "$env:LOCALAPPDATA\bulu-browser\app\src\index.mjs" --status
```
如果你删了快捷方式,重新生成:
```powershell
powershell -ExecutionPolicy Bypass -File "$env:LOCALAPPDATA\bulu-browser\app\create-shortcut.ps1"
```
这个浏览器和你日常用的 Chrome 是**两个独立实例**:独立的用户数据目录,书签和登录态不互通。
这样自动化操作不会污染你的日常浏览记录,也不会误用你的账号。
## 手动排查
```powershell
# 自检:启动一次浏览器并访问 example.com
node "$env:LOCALAPPDATA\bulu-browser\app\src\index.mjs" --check
# 指定浏览器
$env:BULU_CHROME_PATH = "D:\Chrome\chrome.exe"
# 指定 profile 目录
$env:BULU_PROFILE_DIR = "D:\my-profile"
```
## 图标
Chrome 官方几何(红/绿/黄三扇各 120°、白环、蓝心)+ 右下角布噜角标。
布噜取自源码包 `packages/app/src/assets/bulu/bulu-mascot-front.png`(全身像),
裁出上半身,底部用渐变淡出避免生硬切断。
| 文件 | 用途 |
|---|---|
| `assets/icons/bulu-browser.ico` | Windows 快捷方式图标,含 16/24/32/48/64/128/256 七档 |
| `assets/icons/icon-*.png` | 各尺寸单文件 |
| `assets/bulu-browser-icon-1024.png` | 主图标源图(白底圆角) |
| `assets/bulu-browser-transparent-1024.png` | 透明底版,适合深色背景 |
| `assets/icon-preview.html` | 图标源文件,改设计从这里改 CSS |
改了图标之后重新生成并同步:
```powershell
node dev/render-final.mjs # 输出两张 1024 主图
node dev/export-icons.mjs # 输出全套 PNG + 打包 ico
powershell -ExecutionPolicy Bypass -File .\install.ps1 # 同步到 AppData
powershell -ExecutionPolicy Bypass -File .\create-shortcut.ps1 # 刷新快捷方式图标
```
## 卸载
```powershell
powershell -ExecutionPolicy Bypass -File .\uninstall.ps1
```
会删掉技能目录、从 mcp.json 移除条目,并询问是否保留浏览器数据。
## 已知限制
- 需要本机装有 Chrome / Edge / Brave 之一(不自带浏览器内核,省 150MB+)
- snapshot 仍基于 DOM 扫描,未改用 Playwright 的 `ariaSnapshot()`;复杂 SPA 上 ref 稳定性还有提升空间
- 多个会话共用同一个 profile 时会接管同一个 Chrome 实例,标签页会互相影响;需要隔离请给不同会话设不同的 `BULU_PROFILE_DIR`
- `browser_network` 的 body 是重新发一次请求取的,和原始请求的 header 可能不完全一致
- 下载文件统一存到 `%TEMP%\bulu-browser\downloads`,会话结束后不会自动清理