{"owner":"epiral","repo":"bb-browser","hasSkills":true,"totalSkillsCount":7,"totalTokensCount":16002,"categories":["root-instruction","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","skills/bb-browser-openclaw/SKILL.md","skills/bb-browser/SKILL.md","skills/bb-browser/references/adapter-development.md","skills/bb-browser/references/fetch-and-network.md","skills/bb-browser/references/site-system.md","skills/bb-browser/references/snapshot-refs.md"],"skills":{"AGENTS.md":"# bb-browser Agent 开发规范\n\n## 架构\n\n```\nCLI ──HTTP──▶ Daemon ──controlConn CDP──▶ Chrome\n                │\n                │ --hub 模式 (可选)\n                ├── Hub ProviderStream ──▶ Pinix Hub\n                │\n                │ Protocol 2\n                └── Streamer (bb-viewer) ──captureConn + inputConn CDP──▶ Chrome\n                         │\n                         └── WebRTC video + DataChannel ──▶ Clip Web UI\n```\n\n**三根 CDP 连接到同一个 Chrome：**\n\n| 连接 | Owner | 职责 |\n|------|-------|------|\n| controlConn | daemon | Agent 操作：tab/nav/site/DOM/click-by-ref |\n| inputConn | streamer | Human 实时输入：鼠标/键盘/IME (DataChannel → CDP) |\n| captureConn | streamer | 视频流：screencast → VP8 → WebRTC |\n\n**组件：**\n\n| 组件 | 位置 | 职责 |\n|------|------|------|\n| CLI | `packages/cli/` | 命令行入口，HTTP 调 daemon |\n| Daemon | `packages/daemon/` | 控制中心：CDP controlConn、HTTP API、Hub 连接、site 执行、streamer 管理 |\n| Shared | `packages/shared/` | 统一命令定义 (`commands.ts`)、协议类型 (`protocol.ts`) |\n| Streamer | `bb-viewer` repo | 纯视频 + 实时输入（Go binary，daemon 子进程） |\n\n## Daemon 两种模式\n\n```bash\n# 本地模式：Agent 用 CLI 操作浏览器\nbb-browser daemon start\n\n# Hub 模式：注册到 Pinix Hub，远程可用\nbb-browser daemon start --hub https://hub.pinixai.com --hub-token xxx\n```\n\n## 协议\n\n### Protocol 1: CLI ↔ daemon (`POST /command`)\n\n请求：`{\"method\": \"snap\", \"params\": {\"tab\": \"3ef9\"}}`\n成功：`{\"result\": {\"tab\": \"3ef9\", \"title\": \"Google\", \"snapshot\": \"...\"}}`\n失败：`{\"error\": {\"message\": \"Missing --tab\", \"hint\": \"Run 'bb-browser tab list'\"}}`\n\n### Protocol 2: daemon ↔ streamer (`POST /command`)\n\n| Method | Params | 说明 |\n|--------|--------|------|\n| connect | {cdpUrl, ice?} | 连接 CDP，创建 WebRTC peer |\n| answer | {answer_sdp, candidates} | 完成 WebRTC 信令 |\n| switch | {cdpUrl} | 切到新 tab（peer 不变） |\n| stop | {} | 停止 |\n\n## 统一命令定义\n\n`packages/shared/src/commands.ts` 是所有命令的单一定义源。每个命令包含 `method`、`group`、`description`、`requiresTab`、`params`。CLI 解析、daemon dispatch、Hub 注册都从这里读取。\n\n添加新命令：\n1. `commands.ts` — 添加 CommandDef\n2. `protocol.ts` — 添加 ActionType\n3. `command-dispatch.ts` — 添加处理分支\n4. `packages/cli/src/commands/<name>.ts` + `index.ts` — CLI 命令\n\n## CLI 命令\n\n`--tab` 必填（除 `open`、`site` 组、`tab list/new`、`daemon`）。\n\n| 组 | 命令 |\n|----|------|\n| 导航 | `open <url> [--tab]`, `back --tab`, `forward --tab`, `reload --tab`, `close --tab` |\n| 观察 | `snap --tab`, `screenshot --tab`, `get <attr> --tab`, `eval <js> --tab` |\n| 交互 | `click/hover/fill/type/press/scroll/check/uncheck/select --tab` |\n| Tab | `tab list`, `tab new [url]` |\n| Site | `site list`, `site info <name>`, `site run <name>` |\n| 调试 | `network/console/errors/trace/cookies/source --tab` |\n| 进程 | `daemon start [--hub]`, `daemon stop`, `daemon status` |\n\n### Debug 命令参数约定\n\nDebug 命令（`network`, `console`, `errors`, `trace`, `source`）有子命令的统一用 `--action` 参数：\n\n```bash\n# CLI 用法（位置参数）\nbb-browser source grep \"api.example\" --tab <id>\nbb-browser trace start --tab <id>\n\n# Hub invoke 用法（命名参数）\npinix invoke browser source --action grep --pattern \"api.example\" --tab <id>\npinix invoke browser trace --action start --tab <id>\npinix invoke browser network --action requests --excludeStatic true --tab <id>\n```\n\n旧参数名（`sourceCommand`、`traceCommand` 等）仍可用但已 deprecated。\n\n## 设计不变量\n\n1. **Daemon 是唯一操作 API**。CLI 和 Hub invoke 都通过 daemon。\n2. **Streamer 不做业务逻辑**。不管 tab、不做导航。只做帧编码和输入转发。\n3. **Tab ID 统一用 daemon 分配的短 ID**（如 `3ef9`）。Streamer 不知道 tab ID，只接收 CDP WebSocket URL。\n4. **Site 执行在 daemon 内**。不 shell-out CLI。\n5. **所有操作响应包含 `tab`**（短 ID）。观察类响应包含 `cursor`。\n6. **Per-tab 事件隔离**。tab 关闭时释放短 ID 和事件缓冲。\n7. **`seq` 全局单调递增**，不可回退。\n8. **Daemon 启动时清理旧进程**。`cleanupStaleDaemon()` 确保不残留。\n\n## Hub Clip 注册\n\nHub 模式下 daemon 注册：\n\n| Clip | 命令 |\n|------|------|\n| browser | 所有标准命令 + `stream.start/answer/close/switch` |\n| \\<platform\\> | 每个 site adapter 一个命令（如 `google/search`） |\n\nClip Web UI 通过 Hub invoke 调用标准命令（tab_list、open、reload 等）和 stream 命令。\n\n## Stream 生命周期 (view.html)\n\nClip Web UI (`web/view.html`) 通过心跳 + visibility 检测管理 stream 生命周期：\n\n```\nACTIVE (visible) → Ping 每 5s via DataChannel (opcode 0x08)\nIDLE   (hidden)  → Ping 每 10s, 30s 后自动 disconnect\nCLOSED           → overlay 显示断开原因, 可点 Connect 重连\n```\n\n**前端 30s idle disconnect 是第一道防线，后端 45s watchdog 是安全网。**\n\n`cleanup(reason)` 根据原因显示不同 overlay：\n- `user` → \"Disconnected\"\n- `idle` → \"Stream closed — tab was inactive\"\n- `network` → \"Connection lost\"\n\n关键实现约束：\n- 不用 `setTimeout`（hidden tab 被浏览器节流），用 `setInterval` + 时间戳\n- 不用 `BigInt`（兼容性），用 `Math.floor` + `>>>`\n- `disconnect` 里 `invoke(\"stream.close\")` 是 fire-and-forget，不 await\n\n## Docker 运行模式\n\nDocker 中 Chrome 必须以**有头模式**运行在 Xvfb 虚拟帧缓冲上。\n\n```\nXvfb (:99) ← Chrome (headed, DISPLAY=:99) ← CDP screencast → bb-viewer → WebRTC\n```\n\n**为什么不能用 `--headless=new`：** Linux 上 `--headless=new` 跳过整个 X11/Ozone 显示层，导致 WebGL、`navigator.plugins`、屏幕信息等 API 返回异常值。Google 等反自动化系统检测这些底层渲染差异来识别 headless 浏览器。macOS 上 headless 没问题，因为 Chrome 仍链接完整的 Cocoa/CoreGraphics 框架，只是不显示窗口。\n\nChrome 始终以有头模式启动，不使用 `--headless=new`。macOS 上没有聚焦的用户 session 时 Chrome 自动离屏渲染（无可见窗口），效果等同 headless 但保留完整 GUI API。\n\n**Docker 关键环境变量：**\n- `DISPLAY=:99` — Xvfb 虚拟显示（Dockerfile 已设置）\n\n**Stealth 注入 = 有害：** Google 的反自动化不检测 CDP 本身，而是检测 `Emulation.setUserAgentOverride`、`Page.addScriptToEvaluateOnNewDocument` 等 CDP domain 调用。不注入任何 stealth，用裸 CDP 即可。\n\n### Docker 构建\n\n```bash\n# 构建（单阶段，下载预编译 bb-viewer + Chrome，阿里云 apt/npm 镜像）\ndocker build --platform linux/amd64 -t bb-browser:amd64 .\n\n# 运行（TURN 凭证自动从 Hub API 获取，不需要配置）\ndocker run -d --platform linux/amd64 --name bb-browser \\\n  -v bb-browser-data:/data -p 19825:19824 --shm-size=2g \\\n  -e CHROME_WINDOW_SIZE=1280,720 \\\n  bb-browser:amd64 --host 0.0.0.0 \\\n  --hub https://hub.pinixai.com --hub-token <token>\n```\n\n镜像内预装了 Chrome for Testing + bb-viewer（静态链接），运行时无需下载。\n`--shm-size=2g` 防止 Chrome 因共享内存不足 crash。\nTURN 环境变量（`TURN_URL`/`TURN_SECRET`）仍可用于覆盖默认值。\n\n**推荐：使用 Pinix 全家桶镜像 `lueco/pinix`，包含 pinixd + bb-browser + Chrome，一个 token 启动一切。**\n\n### bb-viewer (streamer)\n\n- 预编译 binary 在 COS 上，daemon 首次 stream 时自动下载（`~/.bb-browser/bin/bb-viewer`）\n- 静态链接 libvpx + libturbojpeg，不依赖用户系统库版本\n- JPEG → I420 解码使用 `tjDecompress2` (RGB) 再手动转 I420，而非 `tjDecompressToYUVPlanes`（后者在 libturbojpeg 2.1.x 对奇数高度帧有 bug）\n- Decoder 自动 round down 到偶数尺寸\n\n### TURN relay\n\nTURN 凭证通过 Hub API `GET /turn/credentials` 动态获取（24h TTL），secret 只在服务端。\ndaemon 启动 streamer 时自动请求，不需要用户配置。\n环境变量 `TURN_URL` / `TURN_SECRET` 可覆盖（standalone 模式）。\n\n## 代码规范\n\n- Commit：`<type>(<scope>): <summary>`，英文\n- 类型：`fix` / `feat` / `refactor` / `chore` / `docs`\n- 构建：`pnpm build`\n- 测试：`pnpm test`\n- lint：`pnpm lint`\n\n## 参考\n\n- [bb-viewer issue #5](https://github.com/epiral/bb-viewer/issues/5) — Stream 生命周期设计\n- [issue #224](https://github.com/epiral/bb-browser/issues/224) — daemon 生命周期（已修复）\n","skills/bb-browser-openclaw/SKILL.md":"---\nname: bb-browser-openclaw\ndescription: Turn any website into a CLI command. 36 platforms, 103 commands — Twitter, Reddit, GitHub, YouTube, Zhihu, Bilibili, Weibo, and more. Uses OpenClaw's browser directly, no extra extension needed.\nrequires:\n  bins: bb-browser\nallowed-tools: Bash(bb-browser:*)\n---\n\n# bb-browser sites — The web as CLI\n\n36 platforms, 103 commands. One-liner structured data from any website using your login state.\n\n**All commands use `--openclaw` to run through OpenClaw's browser. No Chrome extension or daemon needed.**\n\n## Quick Start\n\n```bash\n# First time: pull community adapters\nbb-browser site update\n\n# See what's available\nbb-browser site list\n\n# See which adapters match your browsing habits\nbb-browser site recommend\n\n# Run any adapter via OpenClaw's browser\nbb-browser site reddit/hot --openclaw\nbb-browser site hackernews/top 5 --openclaw\nbb-browser site v2ex/hot --openclaw\n```\n\n## IMPORTANT: Always use --openclaw\n\nEvery `bb-browser site` command MUST include `--openclaw` to use OpenClaw's browser:\n\n```bash\n# Correct\nbb-browser site twitter/search \"AI agent\" --openclaw\nbb-browser site zhihu/hot 10 --openclaw --json\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[] | {name, changePercent}'\n\n# Wrong (requires separate Chrome extension)\nbb-browser site twitter/search \"AI agent\"\n```\n\n## Data Extraction (most common use)\n\n```bash\n# Social media\nbb-browser site twitter/search \"OpenClaw\" --openclaw\nbb-browser site twitter/thread <tweet-url> --openclaw\nbb-browser site reddit/thread <post-url> --openclaw\nbb-browser site weibo/hot --openclaw\nbb-browser site xiaohongshu/search \"query\" --openclaw\n\n# Developer\nbb-browser site github/repo owner/repo --openclaw\nbb-browser site github/issues owner/repo --openclaw\nbb-browser site hackernews/top 10 --openclaw\nbb-browser site stackoverflow/search \"async await\" --openclaw\nbb-browser site arxiv/search \"transformer\" --openclaw\n\n# Finance\nbb-browser site xueqiu/stock SH600519 --openclaw\nbb-browser site xueqiu/hot-stock 5 --openclaw\nbb-browser site eastmoney/stock \"茅台\" --openclaw\n\n# News & Knowledge\nbb-browser site zhihu/hot --openclaw\nbb-browser site 36kr/newsflash --openclaw\nbb-browser site wikipedia/summary \"Python\" --openclaw\n\n# Video\nbb-browser site youtube/transcript VIDEO_ID --openclaw\nbb-browser site bilibili/search \"query\" --openclaw\n```\n\n## Filtering with --jq\n\nUse `--jq` to extract specific fields (no need for `--json`, it's implied):\n\n```bash\n# Just stock names\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[].name'\n\n# Specific fields as objects\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[] | {name, changePercent, heat}'\n\n# Filter results\nbb-browser site reddit/hot --openclaw --jq '.posts[] | {title, score}'\n```\n\n## View adapter details\n\n```bash\n# Check what args an adapter takes\nbb-browser site info xueqiu/stock\n\n# Search adapters by keyword\nbb-browser site search reddit\n```\n\n## Login State\n\nAdapters run inside OpenClaw's browser tabs. If a site requires login:\n\n1. The adapter will return an error like `{\"error\": \"HTTP 401\", \"hint\": \"Not logged in?\"}`\n2. Log in to the site in OpenClaw's browser:\n   ```bash\n   openclaw browser open https://twitter.com\n   ```\n3. Complete login manually in the browser window\n4. Retry the command\n\n## Creating New Adapters\n\nTurn any website into a CLI command:\n\n```bash\n# Read the guide\nbb-browser guide\n\n# Or just tell me: \"turn notion.so into a bb-browser adapter\"\n# I'll reverse-engineer the API, write the adapter, test it, and submit a PR.\n```\n\n## All 36 Platforms\n\n| Category | Platforms |\n|----------|-----------|\n| Search | Google, Baidu, Bing, DuckDuckGo, Sogou WeChat |\n| Social | Twitter/X, Reddit, Weibo, Xiaohongshu, Jike, LinkedIn, Hupu |\n| News | BBC, Reuters, 36kr, Toutiao, Eastmoney |\n| Dev | GitHub, StackOverflow, HackerNews, CSDN, cnblogs, V2EX, Dev.to, npm, PyPI, arXiv |\n| Video | YouTube, Bilibili |\n| Entertainment | Douban, IMDb, Genius, Qidian |\n| Finance | Xueqiu, Eastmoney, Yahoo Finance |\n| Jobs | BOSS Zhipin, LinkedIn |\n| Knowledge | Wikipedia, Zhihu, Open Library |\n| Shopping | SMZDM |\n| Tools | Youdao, GSMArena, Product Hunt, Ctrip |\n","skills/bb-browser/SKILL.md":"---\nname: bb-browser\ndescription: 强大的信息获取与浏览器自动化工具。通过浏览器 + 用户登录态，获取公域和私域信息。可访问任意网页、内部系统、登录后页面，执行表单填写、信息提取、页面操作。支持 site 系统（36 平台 103 命令一键调用）、带登录态的 fetch、网络请求拦截与 mock、操作录制等高级功能。\nallowed-tools: Bash(bb-browser:*)\n---\n\n# bb-browser - 信息获取与浏览器自动化\n\n## 核心价值\n\n通过浏览器 + 用户登录态，可以获取：\n- 公域信息：任意公开网页、搜索结果、新闻资讯\n- 私域信息：内部系统、企业应用、登录后页面、个人账户数据\n\n还可以代替用户执行浏览器操作：表单填写、按钮点击、数据提取、截图保存、批量操作。\n\n运行在用户真实浏览器中，复用已登录的账号，不触发反爬检测。\n\n## 快速开始\n\n```bash\nbb-browser open <url>        # 打开页面（新 tab）\nbb-browser snapshot -i       # 获取可交互元素\nbb-browser click @5          # 点击元素\nbb-browser fill @3 \"text\"    # 填写输入框\nbb-browser close             # 完成后关闭 tab\n```\n\n## Site 系统 — 把任何网站变成命令行 API\n\nsite 系统是 bb-browser 的核心特性，通过 adapter 将网站功能 CLI 化，覆盖 36+ 平台。\n\n```bash\n# 常用命令\nbb-browser site list                          # 列出所有 adapter\nbb-browser site search <query>                # 搜索 adapter\nbb-browser site <name> [args...]              # 运行 adapter\nbb-browser site update                        # 更新社区 adapter 库\n\n# 使用示例\nbb-browser site twitter/search \"Claude Code\"  # 搜索推文\nbb-browser site zhihu/hot                     # 知乎热榜\nbb-browser site github/repo owner/repo        # 仓库信息\nbb-browser site youtube/transcript <video_id> # 获取字幕\nbb-browser site reddit/thread <url>           # 帖子详情\nbb-browser site eastmoney/stock \"茅台\"         # 股票查询\nbb-browser site weibo/hot                     # 微博热搜\nbb-browser site arxiv/search \"transformer\"    # 论文搜索\n```\n\nadapter 自动处理 tab 管理（查找匹配域名的 tab 或新建），自动检测登录错误并提示。\n\n详细用法和 36 平台完整列表：参见 [references/site-system.md](references/site-system.md)\n创建自定义 adapter：参见 [references/adapter-development.md](references/adapter-development.md)\n\n## fetch — 带登录态的 curl\n\n在浏览器上下文中执行 fetch，自动携带 Cookie 和登录态。\n\n```bash\nbb-browser fetch <url>                                    # GET 请求\nbb-browser fetch <url> --method POST --body '{\"k\":\"v\"}'   # POST 请求\nbb-browser fetch <url> --headers '{\"Auth\":\"Bearer xxx\"}'  # 自定义请求头\nbb-browser fetch <url> --output data.json                 # 保存到文件\nbb-browser fetch /api/me.json                             # 相对路径（用当前 tab 的 origin）\n```\n\n自动域名路由：绝对路径自动查找匹配 tab 或新建；相对路径使用当前 tab。\n\n详细用法：参见 [references/fetch-and-network.md](references/fetch-and-network.md)\n\n## Tab 管理规范\n\n操作完成后必须关闭自己打开的 tab。\n\n```bash\n# 单 tab 场景\nbb-browser open https://example.com    # 打开新 tab\nbb-browser snapshot -i\nbb-browser click @5\nbb-browser close                        # 完成后关闭\n\n# 多 tab 场景\nbb-browser open https://site-a.com     # tabId: 123\nbb-browser open https://site-b.com     # tabId: 456\n# ... 操作 ...\nbb-browser tab close                    # 关闭当前 tab\nbb-browser tab close                    # 关闭剩余 tab\n\n# 指定 tab 操作\nbb-browser open https://example.com --tab current  # 在当前 tab 打开（不新建）\nbb-browser open https://example.com --tab 123      # 在指定 tabId 打开\n```\n\n## 核心工作流\n\n1. `open` 打开页面\n2. `snapshot -i` 查看可操作元素（返回 @ref）\n3. 用 `@ref` 执行操作（click, fill, etc.）\n4. 页面变化后重新 `snapshot -i`\n5. 任务完成后 `close` 关闭 tab\n\n## 命令速查\n\n### 导航\n\n```bash\nbb-browser open <url>                # 打开 URL（新 tab）\nbb-browser open <url> --tab current  # 在当前 tab 打开\nbb-browser back                      # 后退\nbb-browser forward                   # 前进\nbb-browser refresh                   # 刷新\nbb-browser close                     # 关闭当前 tab\n```\n\n### 快照\n\n```bash\nbb-browser snapshot             # 完整页面结构\nbb-browser snapshot -i          # 只显示可交互元素（推荐）\nbb-browser snapshot -c          # 移除空结构节点\nbb-browser snapshot -d 3        # 限制树深度为 3 层\nbb-browser snapshot -s \".main\"  # 限定 CSS 选择器范围\nbb-browser snapshot --json      # JSON 格式输出\n# 选项可组合：bb-browser snapshot -i -c -d 5\n```\n\n### 元素交互\n\n```bash\nbb-browser click @5             # 点击\nbb-browser hover @5             # 悬停\nbb-browser fill @3 \"text\"       # 清空并填写\nbb-browser type @3 \"text\"       # 追加输入（不清空）\nbb-browser check @7             # 勾选复选框\nbb-browser uncheck @7           # 取消勾选\nbb-browser select @4 \"option\"   # 下拉选择\nbb-browser press Enter          # 按键\nbb-browser press Control+a      # 组合键\nbb-browser scroll down          # 向下滚动（默认 300px）\nbb-browser scroll up 500        # 向上滚动 500px\n```\n\n### 获取信息\n\n```bash\nbb-browser get text @5          # 获取元素文本\nbb-browser get url              # 获取当前 URL\nbb-browser get title            # 获取页面标题\n```\n\n### Tab 管理\n\n```bash\nbb-browser tab                  # 列出所有 tab\nbb-browser tab new [url]        # 新建 tab\nbb-browser tab 2                # 切换到第 2 个 tab（按 index）\nbb-browser tab select --id 123  # 切换到指定 tabId 的 tab\nbb-browser tab close            # 关闭当前 tab\nbb-browser tab close 3          # 关闭第 3 个 tab（按 index）\nbb-browser tab close --id 123   # 关闭指定 tabId 的 tab\n```\n\n### 截图\n\n```bash\nbb-browser screenshot           # 截图（自动保存）\nbb-browser screenshot path.png  # 截图到指定路径\n```\n\n### 等待\n\n```bash\nbb-browser wait 2000            # 等待 2 秒\nbb-browser wait @5              # 等待元素出现\n```\n\n### JavaScript\n\n```bash\nbb-browser eval \"document.title\"              # 执行 JS\nbb-browser eval \"window.scrollTo(0, 1000)\"    # 滚动到指定位置\n```\n\n### Frame 切换\n\n```bash\nbb-browser frame \"#iframe-id\"   # 切换到 iframe\nbb-browser frame main           # 返回主 frame\n```\n\n### 对话框处理\n\n```bash\nbb-browser dialog accept        # 确认对话框\nbb-browser dialog dismiss       # 取消对话框\nbb-browser dialog accept \"text\" # 确认并输入（prompt）\n```\n\n### 网络与调试\n\n```bash\nbb-browser network requests                        # 查看网络请求\nbb-browser network requests \"api\" --with-body       # 过滤 + 完整请求/响应体\nbb-browser network route \"*analytics*\" --abort      # 拦截并阻止请求\nbb-browser network route \"*/api/user\" --body '{}'   # 拦截并 mock 响应\nbb-browser network unroute                          # 移除所有拦截规则\nbb-browser network clear                            # 清空请求记录\nbb-browser console                                  # 查看控制台消息\nbb-browser console --clear                          # 清空控制台\nbb-browser errors                                   # 查看 JS 错误\nbb-browser errors --clear                           # 清空错误记录\nbb-browser trace start                              # 开始录制用户操作\nbb-browser trace stop                               # 停止录制，输出事件列表\nbb-browser trace status                             # 查看录制状态\n```\n\n详细的 network 高级用法：参见 [references/fetch-and-network.md](references/fetch-and-network.md)\n\n## 全局选项\n\n```bash\n--json               # 以 JSON 格式输出（所有命令通用）\n--tab <tabId>        # 指定操作的标签页 ID（几乎所有命令通用）\n--mcp                # 启动 MCP server（用于 Claude Code / Cursor 等 AI 工具）\n```\n\n## Ref 使用说明\n\nsnapshot 返回的 `@ref` 是元素的临时标识：\n\n```\n@1 [button] \"提交\"\n@2 [input type=\"text\"] placeholder=\"请输入姓名\"\n@3 [a] \"查看详情\"\n```\n\n注意：\n- 页面导航后 ref 失效，需重新 snapshot\n- 动态内容加载后需重新 snapshot\n- ref 格式：`@1`, `@2`, `@3`...\n\n详细说明：参见 [references/snapshot-refs.md](references/snapshot-refs.md)\n\n## 并发操作\n\n```bash\n# 并发打开多个页面（各自独立 tab）\nbb-browser open https://site-a.com &\nbb-browser open https://site-b.com &\nbb-browser open https://site-c.com &\nwait\n# 每个返回独立的 tabId，互不干扰\n```\n\n## 信息提取 vs 页面操作\n\n根据目的选择不同的方法：\n\n### 提取页面内容（用 eval）\n\n当需要提取文章、正文等长文本时，用 `eval` 直接获取：\n\n```bash\n# 微信公众号文章\nbb-browser eval \"document.querySelector('#js_content').innerText\"\n\n# 知乎回答\nbb-browser eval \"document.querySelector('.RichContent-inner').innerText\"\n\n# 通用：获取页面主体文本\nbb-browser eval \"document.body.innerText.substring(0, 5000)\"\n\n# 获取所有链接\nbb-browser eval \"[...document.querySelectorAll('a')].map(a => a.href).join('\\n')\"\n```\n\n有些网站 DOM 嵌套很深，snapshot 输出冗长，`eval` 直接提取文本更高效。\n\n### 操作页面元素（用 snapshot -i）\n\n当需要点击、填写、选择时，用 `snapshot -i` 获取可交互元素：\n\n```bash\nbb-browser snapshot -i\n# @1 [button] \"登录\"\n# @2 [input] placeholder=\"用户名\"\n# @3 [input type=\"password\"]\n\nbb-browser fill @2 \"username\"\nbb-browser fill @3 \"password\"\nbb-browser click @1\n```\n\n`-i` 只显示可交互元素，过滤掉大量无关内容。\n\n## MCP 集成\n\nbb-browser 提供 MCP server，可与 Claude Code / Cursor 等 AI 工具集成：\n\n```bash\n# 启动 MCP server\nbb-browser --mcp\n```\n\n配置示例（Claude Code / Cursor）：\n```json\n{\n  \"mcpServers\": {\n    \"bb-browser\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"bb-browser\", \"--mcp\"]\n    }\n  }\n}\n```\n\n## 常见任务示例\n\n### 表单填写\n\n```bash\nbb-browser open https://example.com/form\nbb-browser snapshot -i\nbb-browser fill @1 \"张三\"\nbb-browser fill @2 \"zhangsan@example.com\"\nbb-browser click @3\nbb-browser wait 2000\nbb-browser close\n```\n\n### 信息提取\n\n```bash\nbb-browser open https://example.com/dashboard\nbb-browser snapshot -i\nbb-browser get text @5\nbb-browser screenshot report.png\nbb-browser close\n```\n\n### 批量操作\n\n```bash\nfor url in \"url1\" \"url2\" \"url3\"; do\n  bb-browser open \"$url\"\n  bb-browser snapshot -i --json\n  bb-browser close\ndone\n```\n\n## 深入文档\n\n| 文档 | 说明 |\n|------|------|\n| [references/site-system.md](references/site-system.md) | Site 系统完整指南：35 平台列表、命令用法、自动 tab 管理 |\n| [references/adapter-development.md](references/adapter-development.md) | Adapter 开发指南：API 逆向、三层复杂度、元数据格式 |\n| [references/fetch-and-network.md](references/fetch-and-network.md) | Fetch 与 Network 高级功能：带登录态请求、请求拦截与 mock |\n| [references/snapshot-refs.md](references/snapshot-refs.md) | Ref 生命周期、最佳实践、常见问题 |\n","skills/bb-browser/references/adapter-development.md":"# Adapter 开发指南\n\n## 开发流程概览\n\n1. 用 `network` 命令逆向 API\n2. 用 `eval` 测试 fetch 是否可行\n3. 编写 adapter JS 文件\n4. 保存到 `~/.bb-browser/sites/` 测试\n5. 提交 PR 到社区仓库\n\n<!-- 证据来源：packages/cli/src/index.ts:601-658 guide 命令输出 -->\n\n## Step 1：逆向 API\n\n```bash\n# 清空旧记录\nbb-browser network clear --tab <tabId>\n\n# 刷新页面触发请求\nbb-browser refresh --tab <tabId>\n\n# 查看 API 请求（filter 是位置参数，不是 --filter）\nbb-browser network requests \"api\" --with-body --json --tab <tabId>\n```\n\n重点关注：\n- 请求 URL 和参数格式\n- 认证方式（Cookie / Bearer token / CSRF token）\n- 响应数据结构\n\n## Step 2：测试 fetch\n\n```bash\n# 直接在浏览器中测试 fetch（Tier 1 验证）\nbb-browser eval \"fetch('/api/endpoint',{credentials:'include'}).then(r=>r.json())\" --tab <tabId>\n```\n\n根据结果判断复杂度：\n- **能直接拿到数据** → Tier 1（Cookie 认证，如 Reddit/GitHub/V2EX）\n- **需要额外请求头** → Tier 2（如 Twitter：Bearer + CSRF token）\n- **需要请求签名或注入** → Tier 3（如小红书：Pinia store / Webpack 模块）\n\n## Step 3：编写 Adapter\n\n### 元数据格式（`/* @meta */` 块）\n\n<!-- 证据来源：site.ts:56-118 parseSiteMeta() -->\n\n```javascript\n/* @meta\n{\n  \"name\": \"platform/command\",\n  \"description\": \"功能描述\",\n  \"domain\": \"www.example.com\",\n  \"args\": {\n    \"query\": {\"required\": true, \"description\": \"搜索关键词\"},\n    \"count\": {\"required\": false, \"description\": \"返回数量\"}\n  },\n  \"capabilities\": [\"search\"],\n  \"readOnly\": true,\n  \"example\": \"bb-browser site platform/command value\"\n}\n*/\nasync function(args) {\n  // adapter 实现\n}\n```\n\n### 元数据字段说明\n\n| 字段 | 必需 | 说明 |\n|------|------|------|\n| `name` | 是 | 唯一标识，格式 `platform/command` |\n| `description` | 是 | 功能描述 |\n| `domain` | 是 | 目标网站域名（用于自动 tab 匹配） |\n| `args` | 是 | 参数定义，每个参数含 `required` 和 `description` |\n| `capabilities` | 否 | 能力标签数组 |\n| `readOnly` | 否 | 是否只读操作 |\n| `example` | 否 | 使用示例 |\n\n### 旧格式兼容\n\n也支持 `// @tag value` 注释格式（向后兼容）：\n\n```javascript\n// @name platform/command\n// @description 功能描述\n// @domain www.example.com\n// @args query,filter\n// @example bb-browser site platform/command value\n```\n\n<!-- 证据来源：site.ts:100-117 旧格式解析 -->\n\n## 三层复杂度示例\n\n### Tier 1：Cookie 认证（~1 分钟）\n\n直接 fetch，`credentials: 'include'` 自动带 Cookie。\n\n```javascript\n/* @meta\n{\n  \"name\": \"reddit/search\",\n  \"description\": \"Search Reddit posts\",\n  \"domain\": \"www.reddit.com\",\n  \"args\": {\"query\": {\"required\": true, \"description\": \"Search query\"}},\n  \"readOnly\": true,\n  \"example\": \"bb-browser site reddit/search 'local LLM'\"\n}\n*/\nasync function(args) {\n  if (!args.query) return {error: 'Missing argument: query'};\n  const resp = await fetch(\n    '/search.json?q=' + encodeURIComponent(args.query) + '&limit=10',\n    {credentials: 'include'}\n  );\n  if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};\n  const data = await resp.json();\n  return data.data.children.map(c => ({\n    title: c.data.title,\n    url: 'https://www.reddit.com' + c.data.permalink,\n    score: c.data.score,\n    subreddit: c.data.subreddit\n  }));\n}\n```\n\n### Tier 2：Bearer + CSRF token（~3 分钟）\n\n需要从页面提取 token 构造请求头。\n\n```javascript\n/* @meta\n{\n  \"name\": \"twitter/search\",\n  \"description\": \"Search tweets\",\n  \"domain\": \"twitter.com\",\n  \"args\": {\"query\": {\"required\": true, \"description\": \"Search query\"}},\n  \"readOnly\": true\n}\n*/\nasync function(args) {\n  // 从 cookie 提取 CSRF token\n  const csrf = document.cookie.match(/ct0=([^;]+)/)?.[1];\n  if (!csrf) return {error: 'CSRF token not found', hint: 'Not logged in?'};\n\n  // 构造带认证的请求\n  const resp = await fetch('/i/api/2/search/adaptive.json?q=' + encodeURIComponent(args.query), {\n    credentials: 'include',\n    headers: {\n      'authorization': 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs...',\n      'x-csrf-token': csrf\n    }\n  });\n  if (!resp.ok) return {error: 'HTTP ' + resp.status};\n  return await resp.json();\n}\n```\n\n### Tier 3：Webpack 注入 / Pinia store（~10 分钟）\n\n需要访问页面内部状态或调用内部模块。\n\n## 抗变更模式\n\n网站频繁更新前端代码（CSS class、webpack module ID、GraphQL queryId 等）。以下是经过验证的抗变更模式。\n\n### 模式 1：结构化 DOM 提取（替代 CSS class 选择器）\n\n**问题**：网站经常修改 CSS class 名（如 Google 从 `div.g` 改为 `div.MjjYud`），导致 adapter 失效。\n\n**方案**：用语义化 HTML 元素（h3、a、article）定位内容，不依赖任何 class name。\n\n```javascript\n// ❌ 脆弱：依赖 CSS class（Google 已多次更改）\nconst items = doc.querySelectorAll('div.g');\n\n// ✅ 稳健：用语义元素定位\nconst h3s = doc.querySelectorAll('h3');\nfor (const h3 of h3s) {\n  const a = h3.closest('a');\n  if (!a) continue;\n  const link = a.getAttribute('href');\n  if (!link || !link.startsWith('http')) continue;\n  const title = h3.textContent.trim();\n\n  // 向上查找结果容器（找到有多个 h3 兄弟的层级停止）\n  let container = a;\n  while (container.parentElement && container.parentElement.tagName !== 'BODY') {\n    const sibs = [...container.parentElement.children];\n    if (sibs.filter(s => s.querySelector('h3')).length > 1) break;\n    container = container.parentElement;\n  }\n\n  // 在容器内、链接外查找摘要\n  const linkBlock = a.closest('div') || a;\n  let snippet = '';\n  for (const sp of container.querySelectorAll('span')) {\n    if (linkBlock.contains(sp)) continue;\n    const t = sp.textContent.trim();\n    if (t.length > 30 && t !== title) { snippet = t; break; }\n  }\n  results.push({ title, url: link, snippet });\n}\n```\n\n**适用于**：Google、Bing、DuckDuckGo、HackerNews 等搜索/列表页面。\n\n### 模式 2：Webpack 模块动态发现（替代硬编码 module ID）\n\n**问题**：SPA 网站（如 Twitter/X）的 webpack module ID 在每次部署时都会变化，硬编码 ID 很快失效。\n\n**方案**：通过搜索模块源码中的稳定签名来动态查找模块。\n\n```javascript\n// 第一步：获取 webpack require 函数\nlet __webpack_require__;\nconst chunkId = '__bb_' + Date.now();\nwindow.webpackChunk_twitter_responsive_web.push(\n  [[chunkId], {}, (req) => { __webpack_require__ = req; }]\n);\n\n// 第二步：按源码签名查找模块（不依赖 module ID）\n// 示例：查找 Transaction ID 生成器\nlet genTxId;\nfor (const id of Object.keys(__webpack_require__.m)) {\n  const src = __webpack_require__.m[id].toString();\n  // 用模块源码中稳定的字符串特征来匹配\n  if (src.includes('jf.x.com') && src.includes('jJ:')) {\n    genTxId = __webpack_require__(id).jJ;\n    break;\n  }\n}\nif (!genTxId) return {\n  error: 'Cannot find transaction ID generator',\n  hint: 'Twitter webpack structure may have changed.'\n};\n\n// 第三步：按 operationName 查找 GraphQL queryId\nlet queryId;\nfor (const id of Object.keys(__webpack_require__.m)) {\n  const src = __webpack_require__.m[id].toString();\n  const m = src.match(/queryId:\"([^\"]+)\",operationName:\"CreateTweet\"/);\n  if (m) { queryId = m[1]; break; }\n}\nif (!queryId) return {\n  error: 'Cannot find CreateTweet queryId',\n  hint: 'Twitter GraphQL schema may have changed.'\n};\n```\n\n**选择签名的原则**：\n- 选择**业务语义**字符串而非技术细节（`jf.x.com` 比变量名稳定）\n- 选择**多个特征**组合匹配（`includes('A') && includes('B')`）\n- 选择**export 名**匹配（`jJ:` 是 minified 的 export key，比函数体稳定）\n- 对于 GraphQL：`operationName` 几乎不变，`queryId` 会变 → 用前者查后者\n\n**适用于**：Twitter/X、小红书、抖音等 SPA 应用。\n\n### 模式 3：Vue/React 内部状态访问\n\n```javascript\n// Vue 3 + Pinia（如小红号）\nconst app = document.querySelector('#app').__vue_app__;\nconst store = app.config.globalProperties.$pinia._s.get('user');\n\n// React（如 Reddit new）\nconst fiber = document.querySelector('#App')._reactRootContainer?._internalRoot?.current;\n```\n\n**注意**：内部状态访问比 API 调用更脆弱，优先使用 API 逆向。\n\n## Step 4：测试\n\n```bash\n# 保存到私有目录\n# 文件路径：~/.bb-browser/sites/platform/command.js\n\n# 测试运行\nbb-browser site platform/command \"test query\" --json\n\n# 验证输出格式\nbb-browser site platform/command \"test query\"\n```\n\n## Step 5：贡献社区\n\n```bash\n# 方式 A：使用 gh CLI\ngit clone https://github.com/epiral/bb-sites && cd bb-sites\ngit checkout -b feat-platform\n# 添加 adapter 文件\ngit push -u origin feat-platform\ngh pr create --repo epiral/bb-sites\n\n# 方式 B：使用 bb-browser 自身\nbb-browser site github/fork epiral/bb-sites\ngit clone https://github.com/YOUR_USER/bb-sites && cd bb-sites\ngit checkout -b feat-platform\n# 添加 adapter 文件\ngit push -u origin feat-platform\nbb-browser site github/pr-create epiral/bb-sites --title \"feat(platform): add adapters\" --head \"YOUR_USER:feat-platform\"\n```\n\n## 错误处理规范\n\nadapter 返回错误时，使用统一格式：\n\n```javascript\n// 参数缺失\nreturn {error: 'Missing argument: query'};\n\n// HTTP 错误 + 登录提示\nreturn {error: 'HTTP 401', hint: 'Not logged in?'};\n\n// 自定义错误\nreturn {error: 'Rate limited', hint: 'Try again in 60 seconds'};\n```\n\n系统会自动检测 401/403/unauthorized/login 等关键词，生成登录提示。\n\n<!-- 证据来源：site.ts:406-427 错误检测逻辑 -->\n\n## 报告 Adapter Bug\n\n```bash\n# 通过 gh CLI\ngh issue create --repo epiral/bb-sites --title \"[adapter-name] 描述\"\n\n# 通过 bb-browser\nbb-browser site github/issue-create epiral/bb-sites --title \"[adapter-name] 描述\"\n```\n","skills/bb-browser/references/fetch-and-network.md":"# Fetch 与 Network 高级功能\n\n## fetch 命令 — 带登录态的 curl\n\n本质：在浏览器上下文中执行 `fetch()`，自动携带 Cookie 和登录态。\n\n<!-- 证据来源：packages/cli/src/commands/fetch.ts 注释 \"curl，但带浏览器登录态\" -->\n\n### 基本用法\n\n```bash\n# GET 请求（绝对路径）\nbb-browser fetch https://www.reddit.com/api/me.json\n\n# GET 请求（相对路径，使用当前 tab 的 origin）\nbb-browser fetch /api/me.json\n\n# POST 请求\nbb-browser fetch https://api.example.com/data --method POST --body '{\"key\":\"value\"}'\n\n# 自定义请求头\nbb-browser fetch https://api.example.com/data --headers '{\"Authorization\":\"Bearer token\"}'\n\n# 保存到文件\nbb-browser fetch https://api.example.com/data --output response.json\n\n# JSON 格式输出\nbb-browser fetch https://www.reddit.com/api/me.json --json\n```\n\n### 完整选项\n\n| 选项 | 说明 |\n|------|------|\n| `--method <GET\\|POST\\|...>` | HTTP 方法，默认 GET |\n| `--body <json>` | 请求体（仅 POST/PUT 等） |\n| `--headers <json>` | 自定义请求头（必须是合法 JSON） |\n| `--output <file>` | 保存响应到文件 |\n| `--json` | JSON 格式输出 |\n| `--tab <tabId>` | 指定操作的 tab（全局选项，非 fetch 专属） |\n\n<!-- 证据来源：fetch.ts:18-25 FetchOptions 接口，--tab 由 index.ts:215-218 全局解析 -->\n\n### 自动域名路由机制\n\nfetch 命令会自动处理 tab 匹配：\n\n1. **相对路径**（如 `/api/me.json`）：使用当前活动 tab 的 origin\n2. **绝对路径**（如 `https://www.reddit.com/...`）：\n   - 先查找已打开的匹配域名 tab\n   - 没有则自动打开新 tab 并等待 3 秒\n   - 在匹配的 tab 上下文中执行 fetch\n\n<!-- 证据来源：fetch.ts:42-62 ensureTabForOrigin()，fetch.ts:123-139 路由逻辑 -->\n\n### 响应处理\n\n- JSON 响应自动解析为对象\n- 非 JSON 响应返回文本\n- `--output` 时自动格式化写入文件\n\n<!-- 证据来源：fetch.ts:84-106 buildFetchScript() 中的 content-type 判断 -->\n\n### 典型场景\n\n```bash\n# 检查登录状态\nbb-browser fetch https://www.reddit.com/api/me.json\n\n# 调用内部 API\nbb-browser fetch https://internal.company.com/api/dashboard --json\n\n# 提交表单数据\nbb-browser fetch https://api.example.com/submit \\\n  --method POST \\\n  --body '{\"name\":\"test\",\"value\":123}' \\\n  --headers '{\"Content-Type\":\"application/json\"}'\n\n# 下载数据到文件\nbb-browser fetch https://api.example.com/export.csv --output data.csv\n```\n\n---\n\n## network 命令 — 网络监控与拦截\n\n<!-- 证据来源：packages/cli/src/commands/network.ts -->\n\n### 子命令一览\n\n```bash\n# 查看网络请求\nbb-browser network requests [filter] [--with-body] [--json]\n\n# 拦截请求（阻止）\nbb-browser network route <url> --abort\n\n# 拦截请求（mock 响应）\nbb-browser network route <url> --body '{\"mock\":\"data\"}'\n\n# 移除指定拦截规则\nbb-browser network unroute <url>\n\n# 移除所有拦截规则\nbb-browser network unroute\n\n# 清空请求记录\nbb-browser network clear\n```\n\n### network requests 详解\n\n```bash\n# 查看所有请求\nbb-browser network requests\n\n# 按关键词过滤（匹配 URL）\nbb-browser network requests \"api\"\n\n# 包含完整请求/响应体\nbb-browser network requests --with-body\n\n# 组合使用\nbb-browser network requests \"api\" --with-body --json\n```\n\n输出格式：\n```\nGET https://api.example.com/data\n  类型: fetch, 状态: 200 OK\n  请求头: 5, 响应头: 8        # --with-body 时显示\n  请求体: {\"query\":\"test\"}    # --with-body 时显示\n  响应体: {\"result\":[...]}    # --with-body 时显示\n```\n\n<!-- 证据来源：network.ts:46-77 requests 输出逻辑 -->\n\n### network route 详解\n\n拦截匹配 URL 的请求：\n\n```bash\n# 阻止广告/追踪请求\nbb-browser network route \"*analytics*\" --abort\n\n# Mock API 响应（用于测试）\nbb-browser network route \"*/api/user\" --body '{\"name\":\"test\",\"role\":\"admin\"}'\n\n# 添加多条规则\nbb-browser network route \"*tracker*\" --abort\nbb-browser network route \"*/api/config\" --body '{\"feature_flag\":true}'\n```\n\n<!-- 证据来源：network.ts:79-92 route 输出逻辑 -->\n\n### network unroute\n\n```bash\n# 移除指定规则\nbb-browser network unroute \"*analytics*\"\n\n# 移除所有规则\nbb-browser network unroute\n```\n\n<!-- 证据来源：network.ts:94-101 unroute 输出逻辑 -->\n\n### network clear\n\n```bash\n# 清空请求记录（重新开始监控）\nbb-browser network clear\n```\n\n<!-- 证据来源：network.ts:103-106 clear 输出逻辑 -->\n\n### API 逆向工程工作流\n\n这是 fetch + network 最强大的组合用法，用于发现网站内部 API：\n\n```bash\n# 1. 清空旧记录\nbb-browser network clear\n\n# 2. 刷新页面触发请求\nbb-browser refresh\n\n# 3. 查看 API 请求（过滤 + 完整体）\nbb-browser network requests \"api\" --with-body --json\n\n# 4. 找到目标 API 后，用 fetch 测试\nbb-browser fetch /api/discovered-endpoint --json\n\n# 5. 确认可行后，编写 site adapter\n# 参见 adapter-development.md\n```\n\n### 全局 --tab 选项\n\n所有 network 子命令都支持 `--tab <tabId>`，指定监控哪个 tab 的网络活动：\n\n```bash\nbb-browser network requests --tab 123\nbb-browser network route \"*api*\" --abort --tab 456\nbb-browser network clear --tab 123\n```\n","skills/bb-browser/references/site-system.md":"# Site 系统 — 把任何网站变成命令行 API\n\n## 核心概念\n\nSite 系统通过 adapter（适配器）将网站功能 CLI 化。每个 adapter 是一个 JS 文件，在用户真实浏览器中执行，复用登录态，返回结构化 JSON。\n\n<!-- 证据来源：packages/cli/src/commands/site.ts -->\n\n## 命令速查\n\n```bash\n# 列出所有可用 adapter（按平台分组）\nbb-browser site list\n\n# 搜索 adapter（模糊匹配名称、描述、域名）\nbb-browser site search <query>\n\n# 运行 adapter（简写，推荐）\nbb-browser site <name> [args...]\n\n# 运行 adapter（完整写法）\nbb-browser site run <name> [args...]\n\n# 更新社区 adapter 库（从 github.com/epiral/bb-sites 拉取）\nbb-browser site update\n\n# 查看 adapter 开发指南\nbb-browser guide\n```\n\n## 参数传递\n\n支持两种格式混合使用：\n\n```bash\n# 位置参数（按 adapter 定义的参数顺序）\nbb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/...\n\n# 命名参数（--flag value 格式）\nbb-browser site github/pr-create epiral/bb-sites --title \"feat: ...\" --head \"user:branch\"\n\n# 混合使用\nbb-browser site twitter/search \"AI agent\" --count 20\n```\n\n<!-- 证据来源：site.ts:290-314 参数解析逻辑 -->\n\n## Adapter 目录与优先级\n\n```\n~/.bb-browser/\n├── sites/              # 私有 adapter（优先级高，覆盖同名社区 adapter）\n│   └── platform/\n│       └── command.js\n└── bb-sites/           # 社区 adapter（通过 bb-browser site update 拉取）\n    └── platform/\n        └── command.js\n```\n\n私有 adapter 优先于社区同名 adapter。\n\n<!-- 证据来源：site.ts:24-26 目录常量，site.ts:148-157 getAllSites() 合并逻辑 -->\n\n## 自动 Tab 管理\n\n运行 adapter 时，系统自动处理 tab：\n\n1. 如果指定了 `--tab <tabId>`，直接使用该 tab\n2. 否则，根据 adapter 的 `domain` 字段查找已打开的匹配 tab\n3. 如果没有匹配的 tab，自动打开新 tab 并等待 3 秒加载\n\n域名匹配规则：精确匹配或子域名匹配（如 `x.com` 匹配 `api.x.com`）。\n\n<!-- 证据来源：site.ts:162-169 matchTabOrigin()，site.ts:342-368 tab 查找逻辑 -->\n\n## 错误处理与登录提示\n\nadapter 返回 `{error: \"...\", hint: \"...\"}` 时，系统自动检测登录相关错误（匹配 `401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth`），并提示用户先在浏览器中登录。\n\n```bash\n# 错误示例\n[error] site twitter/search: HTTP 401\n  Hint: Please log in to https://twitter.com in your browser first, then retry.\n```\n\n<!-- 证据来源：site.ts:406-427 错误处理逻辑 -->\n\n## 36 平台完整列表\n\n<!-- 证据来源：README.md \"36 platforms, 103 commands\"，具体 adapter 数量以 bb-browser site list 实际输出为准 -->\n\n### 搜索引擎\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Google | `site google/search \"query\"` | 搜索 |\n| Baidu | `site baidu/search \"query\"` | 百度搜索 |\n| Bing | `site bing/search \"query\"` | 必应搜索 |\n| DuckDuckGo | `site duckduckgo/search \"query\"` | DuckDuckGo 搜索 |\n| Sogou WeChat | `site sogou/wechat \"query\"` | 搜狗微信文章搜索 |\n\n### 社交媒体\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Twitter/X | `site twitter/search \"query\"` | 搜索推文 |\n| Twitter/X | `site twitter/user <handle>` | 用户信息 |\n| Reddit | `site reddit/thread <url>` | 帖子详情 |\n| Reddit | `site reddit/search \"query\"` | 搜索 |\n| Weibo | `site weibo/search \"query\"` | 微博搜索 |\n| Weibo | `site weibo/hot` | 微博热搜 |\n| Xiaohongshu | `site xiaohongshu/search \"query\"` | 小红书搜索 |\n| Jike | `site jike/feed` | 即刻动态 |\n| LinkedIn | `site linkedin/search \"query\"` | 搜索 |\n| LinkedIn | `site linkedin/profile <url>` | 个人资料 |\n| Hupu | `site hupu/hot` | 虎扑热帖 |\n\n### 新闻资讯\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| BBC | `site bbc/headlines` | BBC 头条 |\n| Reuters | `site reuters/search \"query\"` | 路透社搜索 |\n| 36kr | `site 36kr/newsflash` | 36氪快讯 |\n| Toutiao | `site toutiao/hot` | 今日头条热榜 |\n| Eastmoney | `site eastmoney/news \"query\"` | 东方财富新闻 |\n\n### 技术开发\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| GitHub | `site github/repo <owner/repo>` | 仓库信息 |\n| GitHub | `site github/issues <owner/repo>` | Issue 列表 |\n| GitHub | `site github/pr-create <repo> --title \"...\"` | 创建 PR |\n| StackOverflow | `site stackoverflow/search \"query\"` | 搜索 |\n| HackerNews | `site hackernews/top` | 热门帖子 |\n| CSDN | `site csdn/search \"query\"` | CSDN 搜索 |\n| cnblogs | `site cnblogs/search \"query\"` | 博客园搜索 |\n| V2EX | `site v2ex/hot` | V2EX 热帖 |\n| Dev.to | `site devto/search \"query\"` | Dev.to 搜索 |\n| npm | `site npm/package <name>` | npm 包信息 |\n| PyPI | `site pypi/package <name>` | PyPI 包信息 |\n| arXiv | `site arxiv/search \"query\"` | 论文搜索 |\n\n### 视频平台\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| YouTube | `site youtube/search \"query\"` | 搜索视频 |\n| YouTube | `site youtube/transcript <video_id>` | 获取字幕 |\n| Bilibili | `site bilibili/search \"query\"` | B站搜索 |\n| Bilibili | `site bilibili/popular` | B站热门 |\n\n### 影音娱乐\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Douban | `site douban/movie <name>` | 豆瓣电影 |\n| Douban | `site douban/top250` | 豆瓣 Top250 |\n| IMDb | `site imdb/search \"query\"` | IMDb 搜索 |\n| Genius | `site genius/search \"query\"` | 歌词搜索 |\n| Qidian | `site qidian/search \"query\"` | 起点小说搜索 |\n\n### 财经股票\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Eastmoney | `site eastmoney/stock \"茅台\"` | 股票查询 |\n| Yahoo Finance | `site yahoo-finance/stock <ticker>` | 股票行情 |\n\n### 求职招聘\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| BOSS Zhipin | `site boss/search \"query\"` | BOSS 直聘搜索 |\n| BOSS Zhipin | `site boss/detail <url>` | 职位详情 |\n\n### 知识百科\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Wikipedia | `site wikipedia/search \"query\"` | 维基百科搜索 |\n| Wikipedia | `site wikipedia/summary \"topic\"` | 摘要 |\n| Zhihu | `site zhihu/hot` | 知乎热榜 |\n| Zhihu | `site zhihu/question <id>` | 问题详情 |\n| Open Library | `site openlibrary/search \"query\"` | 图书搜索 |\n\n### 消费购物\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| SMZDM | `site smzdm/search \"query\"` | 什么值得买搜索 |\n\n### 实用工具\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Youdao | `site youdao/translate \"text\"` | 有道翻译 |\n| GSMArena | `site gsmarena/search \"phone\"` | 手机参数 |\n| Product Hunt | `site producthunt/trending` | 热门产品 |\n| Ctrip | `site ctrip/search \"destination\"` | 携程搜索 |\n\n## 常用场景示例\n\n### 信息检索\n\n```bash\n# 搜索技术话题\nbb-browser site twitter/search \"Claude Code\"\nbb-browser site reddit/search \"local LLM\"\nbb-browser site hackernews/top\n\n# 查看热榜\nbb-browser site zhihu/hot\nbb-browser site weibo/hot\nbb-browser site v2ex/hot\n\n# 查询股票\nbb-browser site eastmoney/stock \"茅台\"\n```\n\n### 开发辅助\n\n```bash\n# GitHub 操作\nbb-browser site github/repo anthropics/claude-code\nbb-browser site github/issues owner/repo\n\n# 查包信息\nbb-browser site npm/package bb-browser\nbb-browser site pypi/package requests\n\n# 搜索技术问题\nbb-browser site stackoverflow/search \"async await error handling\"\n```\n\n### 内容获取\n\n```bash\n# 获取 YouTube 字幕\nbb-browser site youtube/transcript dQw4w9WgXcQ\n\n# 获取 Reddit 帖子完整内容\nbb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/...\n\n# 论文搜索\nbb-browser site arxiv/search \"transformer attention mechanism\"\n```\n\n## 与 --json 配合\n\n所有 site 命令都支持 `--json` 输出结构化数据：\n\n```bash\nbb-browser site zhihu/hot --json\nbb-browser site twitter/search \"AI\" --json\n```\n\n## 更多信息\n\n- 创建自定义 adapter：参见 [adapter-development.md](adapter-development.md)\n- 社区 adapter 仓库：https://github.com/epiral/bb-sites\n","skills/bb-browser/references/snapshot-refs.md":"# Snapshot + Refs 工作流\n\nbb-browser 使用紧凑的元素引用（ref），大幅减少 AI Agent 的上下文消耗。\n\n## 工作原理\n\n### 传统方式的问题\n\n```\n完整 DOM/HTML 发送给 AI → AI 解析 → 生成 CSS 选择器 → 执行操作\n每次交互 ~3000-5000 tokens\n```\n\n### bb-browser 的方案\n\n```\n紧凑快照 → 分配 @ref → 直接用 ref 操作\n每次交互 ~200-400 tokens\n```\n\n## Snapshot 命令\n\n```bash\n# 完整快照（显示页面结构）\nbb-browser snapshot\n\n# 只显示可交互元素（推荐）\nbb-browser snapshot -i\n\n# JSON 格式输出\nbb-browser snapshot -i --json\n```\n\n## 快照输出格式\n\n```\n页面: Example Site - 首页\nURL: https://example.com\n\n@1 [header]\n  @2 [nav]\n    @3 [a] \"首页\"\n    @4 [a] \"产品\"\n    @5 [a] \"关于\"\n  @6 [button] \"登录\"\n\n@7 [main]\n  @8 [h1] \"欢迎\"\n  @9 [form]\n    @10 [input type=\"email\"] placeholder=\"邮箱\"\n    @11 [input type=\"password\"] placeholder=\"密码\"\n    @12 [button type=\"submit\"] \"登录\"\n\n@13 [footer]\n  @14 [a] \"隐私政策\"\n```\n\n## 使用 Ref\n\n有了 ref 后直接操作：\n\n```bash\n# 点击登录按钮\nbb-browser click @6\n\n# 填写邮箱\nbb-browser fill @10 \"user@example.com\"\n\n# 填写密码\nbb-browser fill @11 \"password123\"\n\n# 提交表单\nbb-browser click @12\n```\n\n## Ref 生命周期\n\n**重要**：页面变化后 ref 会失效！\n\n```bash\n# 获取初始快照\nbb-browser snapshot -i\n# @1 [button] \"下一页\"\n\n# 点击触发页面变化\nbb-browser click @1\n\n# 必须重新获取快照！\nbb-browser snapshot -i\n# @1 [h1] \"第二页\"  ← 现在是不同的元素\n```\n\n### Ref 失效的场景\n\n1. **页面导航**：点击链接、表单提交后跳转\n2. **动态加载**：AJAX 加载新内容\n3. **DOM 变化**：JavaScript 修改页面结构\n4. **弹窗/下拉**：打开模态框、展开菜单\n\n## 最佳实践\n\n### 1. 操作前先 Snapshot\n\n```bash\n# 正确\nbb-browser open https://example.com\nbb-browser snapshot -i          # 先获取 ref\nbb-browser click @1             # 再使用\n\n# 错误\nbb-browser open https://example.com\nbb-browser click @1             # ref 不存在！\n```\n\n### 2. 导航后重新 Snapshot\n\n```bash\nbb-browser click @5             # 导航到新页面\nbb-browser snapshot -i          # 获取新页面的 ref\nbb-browser click @1             # 使用新的 ref\n```\n\n### 3. 动态变化后重新 Snapshot\n\n```bash\nbb-browser click @1             # 打开下拉菜单\nbb-browser snapshot -i          # 获取菜单项的 ref\nbb-browser click @7             # 选择菜单项\n```\n\n### 4. 等待加载完成\n\n```bash\nbb-browser click @3             # 触发 AJAX 加载\nbb-browser wait 1000            # 等待加载\nbb-browser snapshot -i          # 获取新内容的 ref\n```\n\n## Ref 格式说明\n\n```\n@1 [tag type=\"value\"] \"文本内容\" placeholder=\"提示\"\n│   │   │             │          │\n│   │   │             │          └─ 其他属性\n│   │   │             └─ 可见文本\n│   │   └─ 关键属性\n│   └─ HTML 标签\n└─ 唯一 ref ID\n```\n\n### 常见模式\n\n```\n@1 [button] \"提交\"                    # 按钮\n@2 [input type=\"email\"]               # 邮箱输入框\n@3 [input type=\"password\"]            # 密码输入框\n@4 [a href=\"/page\"] \"链接文字\"         # 链接\n@5 [select]                           # 下拉框\n@6 [textarea] placeholder=\"请输入\"     # 文本域\n@7 [checkbox] checked                 # 已勾选的复选框\n@8 [radio] selected                   # 已选中的单选框\n```\n\n## 常见问题\n\n### \"Ref not found\" 错误\n\n页面已变化，ref 失效了：\n\n```bash\n# 重新获取快照\nbb-browser snapshot -i\n```\n\n### 元素不在快照中\n\n可能需要滚动或等待：\n\n```bash\n# 滚动到底部\nbb-browser scroll down\nbb-browser snapshot -i\n\n# 或等待动态内容\nbb-browser wait 2000\nbb-browser snapshot -i\n```\n\n### 快照太长\n\n使用 `-i` 只显示可交互元素：\n\n```bash\n# 完整快照可能很长\nbb-browser snapshot\n\n# 只显示可交互元素（推荐）\nbb-browser snapshot -i\n```\n\n### 需要操作不可交互元素\n\n使用 `eval` 直接执行 JavaScript：\n\n```bash\nbb-browser eval \"document.querySelector('.hidden-element').click()\"\n```\n\n## 调试技巧\n\n```bash\n# 查看完整页面结构\nbb-browser snapshot\n\n# 只看可交互元素\nbb-browser snapshot -i\n\n# JSON 格式便于解析\nbb-browser snapshot -i --json\n\n# 获取特定元素的文本\nbb-browser get text @5\n```\n"},"files":{"AGENTS.md":"# bb-browser Agent 开发规范\n\n## 架构\n\n```\nCLI ──HTTP──▶ Daemon ──controlConn CDP──▶ Chrome\n                │\n                │ --hub 模式 (可选)\n                ├── Hub ProviderStream ──▶ Pinix Hub\n                │\n                │ Protocol 2\n                └── Streamer (bb-viewer) ──captureConn + inputConn CDP──▶ Chrome\n                         │\n                         └── WebRTC video + DataChannel ──▶ Clip Web UI\n```\n\n**三根 CDP 连接到同一个 Chrome：**\n\n| 连接 | Owner | 职责 |\n|------|-------|------|\n| controlConn | daemon | Agent 操作：tab/nav/site/DOM/click-by-ref |\n| inputConn | streamer | Human 实时输入：鼠标/键盘/IME (DataChannel → CDP) |\n| captureConn | streamer | 视频流：screencast → VP8 → WebRTC |\n\n**组件：**\n\n| 组件 | 位置 | 职责 |\n|------|------|------|\n| CLI | `packages/cli/` | 命令行入口，HTTP 调 daemon |\n| Daemon | `packages/daemon/` | 控制中心：CDP controlConn、HTTP API、Hub 连接、site 执行、streamer 管理 |\n| Shared | `packages/shared/` | 统一命令定义 (`commands.ts`)、协议类型 (`protocol.ts`) |\n| Streamer | `bb-viewer` repo | 纯视频 + 实时输入（Go binary，daemon 子进程） |\n\n## Daemon 两种模式\n\n```bash\n# 本地模式：Agent 用 CLI 操作浏览器\nbb-browser daemon start\n\n# Hub 模式：注册到 Pinix Hub，远程可用\nbb-browser daemon start --hub https://hub.pinixai.com --hub-token xxx\n```\n\n## 协议\n\n### Protocol 1: CLI ↔ daemon (`POST /command`)\n\n请求：`{\"method\": \"snap\", \"params\": {\"tab\": \"3ef9\"}}`\n成功：`{\"result\": {\"tab\": \"3ef9\", \"title\": \"Google\", \"snapshot\": \"...\"}}`\n失败：`{\"error\": {\"message\": \"Missing --tab\", \"hint\": \"Run 'bb-browser tab list'\"}}`\n\n### Protocol 2: daemon ↔ streamer (`POST /command`)\n\n| Method | Params | 说明 |\n|--------|--------|------|\n| connect | {cdpUrl, ice?} | 连接 CDP，创建 WebRTC peer |\n| answer | {answer_sdp, candidates} | 完成 WebRTC 信令 |\n| switch | {cdpUrl} | 切到新 tab（peer 不变） |\n| stop | {} | 停止 |\n\n## 统一命令定义\n\n`packages/shared/src/commands.ts` 是所有命令的单一定义源。每个命令包含 `method`、`group`、`description`、`requiresTab`、`params`。CLI 解析、daemon dispatch、Hub 注册都从这里读取。\n\n添加新命令：\n1. `commands.ts` — 添加 CommandDef\n2. `protocol.ts` — 添加 ActionType\n3. `command-dispatch.ts` — 添加处理分支\n4. `packages/cli/src/commands/<name>.ts` + `index.ts` — CLI 命令\n\n## CLI 命令\n\n`--tab` 必填（除 `open`、`site` 组、`tab list/new`、`daemon`）。\n\n| 组 | 命令 |\n|----|------|\n| 导航 | `open <url> [--tab]`, `back --tab`, `forward --tab`, `reload --tab`, `close --tab` |\n| 观察 | `snap --tab`, `screenshot --tab`, `get <attr> --tab`, `eval <js> --tab` |\n| 交互 | `click/hover/fill/type/press/scroll/check/uncheck/select --tab` |\n| Tab | `tab list`, `tab new [url]` |\n| Site | `site list`, `site info <name>`, `site run <name>` |\n| 调试 | `network/console/errors/trace/cookies/source --tab` |\n| 进程 | `daemon start [--hub]`, `daemon stop`, `daemon status` |\n\n### Debug 命令参数约定\n\nDebug 命令（`network`, `console`, `errors`, `trace`, `source`）有子命令的统一用 `--action` 参数：\n\n```bash\n# CLI 用法（位置参数）\nbb-browser source grep \"api.example\" --tab <id>\nbb-browser trace start --tab <id>\n\n# Hub invoke 用法（命名参数）\npinix invoke browser source --action grep --pattern \"api.example\" --tab <id>\npinix invoke browser trace --action start --tab <id>\npinix invoke browser network --action requests --excludeStatic true --tab <id>\n```\n\n旧参数名（`sourceCommand`、`traceCommand` 等）仍可用但已 deprecated。\n\n## 设计不变量\n\n1. **Daemon 是唯一操作 API**。CLI 和 Hub invoke 都通过 daemon。\n2. **Streamer 不做业务逻辑**。不管 tab、不做导航。只做帧编码和输入转发。\n3. **Tab ID 统一用 daemon 分配的短 ID**（如 `3ef9`）。Streamer 不知道 tab ID，只接收 CDP WebSocket URL。\n4. **Site 执行在 daemon 内**。不 shell-out CLI。\n5. **所有操作响应包含 `tab`**（短 ID）。观察类响应包含 `cursor`。\n6. **Per-tab 事件隔离**。tab 关闭时释放短 ID 和事件缓冲。\n7. **`seq` 全局单调递增**，不可回退。\n8. **Daemon 启动时清理旧进程**。`cleanupStaleDaemon()` 确保不残留。\n\n## Hub Clip 注册\n\nHub 模式下 daemon 注册：\n\n| Clip | 命令 |\n|------|------|\n| browser | 所有标准命令 + `stream.start/answer/close/switch` |\n| \\<platform\\> | 每个 site adapter 一个命令（如 `google/search`） |\n\nClip Web UI 通过 Hub invoke 调用标准命令（tab_list、open、reload 等）和 stream 命令。\n\n## Stream 生命周期 (view.html)\n\nClip Web UI (`web/view.html`) 通过心跳 + visibility 检测管理 stream 生命周期：\n\n```\nACTIVE (visible) → Ping 每 5s via DataChannel (opcode 0x08)\nIDLE   (hidden)  → Ping 每 10s, 30s 后自动 disconnect\nCLOSED           → overlay 显示断开原因, 可点 Connect 重连\n```\n\n**前端 30s idle disconnect 是第一道防线，后端 45s watchdog 是安全网。**\n\n`cleanup(reason)` 根据原因显示不同 overlay：\n- `user` → \"Disconnected\"\n- `idle` → \"Stream closed — tab was inactive\"\n- `network` → \"Connection lost\"\n\n关键实现约束：\n- 不用 `setTimeout`（hidden tab 被浏览器节流），用 `setInterval` + 时间戳\n- 不用 `BigInt`（兼容性），用 `Math.floor` + `>>>`\n- `disconnect` 里 `invoke(\"stream.close\")` 是 fire-and-forget，不 await\n\n## Docker 运行模式\n\nDocker 中 Chrome 必须以**有头模式**运行在 Xvfb 虚拟帧缓冲上。\n\n```\nXvfb (:99) ← Chrome (headed, DISPLAY=:99) ← CDP screencast → bb-viewer → WebRTC\n```\n\n**为什么不能用 `--headless=new`：** Linux 上 `--headless=new` 跳过整个 X11/Ozone 显示层，导致 WebGL、`navigator.plugins`、屏幕信息等 API 返回异常值。Google 等反自动化系统检测这些底层渲染差异来识别 headless 浏览器。macOS 上 headless 没问题，因为 Chrome 仍链接完整的 Cocoa/CoreGraphics 框架，只是不显示窗口。\n\nChrome 始终以有头模式启动，不使用 `--headless=new`。macOS 上没有聚焦的用户 session 时 Chrome 自动离屏渲染（无可见窗口），效果等同 headless 但保留完整 GUI API。\n\n**Docker 关键环境变量：**\n- `DISPLAY=:99` — Xvfb 虚拟显示（Dockerfile 已设置）\n\n**Stealth 注入 = 有害：** Google 的反自动化不检测 CDP 本身，而是检测 `Emulation.setUserAgentOverride`、`Page.addScriptToEvaluateOnNewDocument` 等 CDP domain 调用。不注入任何 stealth，用裸 CDP 即可。\n\n### Docker 构建\n\n```bash\n# 构建（单阶段，下载预编译 bb-viewer + Chrome，阿里云 apt/npm 镜像）\ndocker build --platform linux/amd64 -t bb-browser:amd64 .\n\n# 运行（TURN 凭证自动从 Hub API 获取，不需要配置）\ndocker run -d --platform linux/amd64 --name bb-browser \\\n  -v bb-browser-data:/data -p 19825:19824 --shm-size=2g \\\n  -e CHROME_WINDOW_SIZE=1280,720 \\\n  bb-browser:amd64 --host 0.0.0.0 \\\n  --hub https://hub.pinixai.com --hub-token <token>\n```\n\n镜像内预装了 Chrome for Testing + bb-viewer（静态链接），运行时无需下载。\n`--shm-size=2g` 防止 Chrome 因共享内存不足 crash。\nTURN 环境变量（`TURN_URL`/`TURN_SECRET`）仍可用于覆盖默认值。\n\n**推荐：使用 Pinix 全家桶镜像 `lueco/pinix`，包含 pinixd + bb-browser + Chrome，一个 token 启动一切。**\n\n### bb-viewer (streamer)\n\n- 预编译 binary 在 COS 上，daemon 首次 stream 时自动下载（`~/.bb-browser/bin/bb-viewer`）\n- 静态链接 libvpx + libturbojpeg，不依赖用户系统库版本\n- JPEG → I420 解码使用 `tjDecompress2` (RGB) 再手动转 I420，而非 `tjDecompressToYUVPlanes`（后者在 libturbojpeg 2.1.x 对奇数高度帧有 bug）\n- Decoder 自动 round down 到偶数尺寸\n\n### TURN relay\n\nTURN 凭证通过 Hub API `GET /turn/credentials` 动态获取（24h TTL），secret 只在服务端。\ndaemon 启动 streamer 时自动请求，不需要用户配置。\n环境变量 `TURN_URL` / `TURN_SECRET` 可覆盖（standalone 模式）。\n\n## 代码规范\n\n- Commit：`<type>(<scope>): <summary>`，英文\n- 类型：`fix` / `feat` / `refactor` / `chore` / `docs`\n- 构建：`pnpm build`\n- 测试：`pnpm test`\n- lint：`pnpm lint`\n\n## 参考\n\n- [bb-viewer issue #5](https://github.com/epiral/bb-viewer/issues/5) — Stream 生命周期设计\n- [issue #224](https://github.com/epiral/bb-browser/issues/224) — daemon 生命周期（已修复）\n","skills/bb-browser-openclaw/SKILL.md":"---\nname: bb-browser-openclaw\ndescription: Turn any website into a CLI command. 36 platforms, 103 commands — Twitter, Reddit, GitHub, YouTube, Zhihu, Bilibili, Weibo, and more. Uses OpenClaw's browser directly, no extra extension needed.\nrequires:\n  bins: bb-browser\nallowed-tools: Bash(bb-browser:*)\n---\n\n# bb-browser sites — The web as CLI\n\n36 platforms, 103 commands. One-liner structured data from any website using your login state.\n\n**All commands use `--openclaw` to run through OpenClaw's browser. No Chrome extension or daemon needed.**\n\n## Quick Start\n\n```bash\n# First time: pull community adapters\nbb-browser site update\n\n# See what's available\nbb-browser site list\n\n# See which adapters match your browsing habits\nbb-browser site recommend\n\n# Run any adapter via OpenClaw's browser\nbb-browser site reddit/hot --openclaw\nbb-browser site hackernews/top 5 --openclaw\nbb-browser site v2ex/hot --openclaw\n```\n\n## IMPORTANT: Always use --openclaw\n\nEvery `bb-browser site` command MUST include `--openclaw` to use OpenClaw's browser:\n\n```bash\n# Correct\nbb-browser site twitter/search \"AI agent\" --openclaw\nbb-browser site zhihu/hot 10 --openclaw --json\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[] | {name, changePercent}'\n\n# Wrong (requires separate Chrome extension)\nbb-browser site twitter/search \"AI agent\"\n```\n\n## Data Extraction (most common use)\n\n```bash\n# Social media\nbb-browser site twitter/search \"OpenClaw\" --openclaw\nbb-browser site twitter/thread <tweet-url> --openclaw\nbb-browser site reddit/thread <post-url> --openclaw\nbb-browser site weibo/hot --openclaw\nbb-browser site xiaohongshu/search \"query\" --openclaw\n\n# Developer\nbb-browser site github/repo owner/repo --openclaw\nbb-browser site github/issues owner/repo --openclaw\nbb-browser site hackernews/top 10 --openclaw\nbb-browser site stackoverflow/search \"async await\" --openclaw\nbb-browser site arxiv/search \"transformer\" --openclaw\n\n# Finance\nbb-browser site xueqiu/stock SH600519 --openclaw\nbb-browser site xueqiu/hot-stock 5 --openclaw\nbb-browser site eastmoney/stock \"茅台\" --openclaw\n\n# News & Knowledge\nbb-browser site zhihu/hot --openclaw\nbb-browser site 36kr/newsflash --openclaw\nbb-browser site wikipedia/summary \"Python\" --openclaw\n\n# Video\nbb-browser site youtube/transcript VIDEO_ID --openclaw\nbb-browser site bilibili/search \"query\" --openclaw\n```\n\n## Filtering with --jq\n\nUse `--jq` to extract specific fields (no need for `--json`, it's implied):\n\n```bash\n# Just stock names\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[].name'\n\n# Specific fields as objects\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[] | {name, changePercent, heat}'\n\n# Filter results\nbb-browser site reddit/hot --openclaw --jq '.posts[] | {title, score}'\n```\n\n## View adapter details\n\n```bash\n# Check what args an adapter takes\nbb-browser site info xueqiu/stock\n\n# Search adapters by keyword\nbb-browser site search reddit\n```\n\n## Login State\n\nAdapters run inside OpenClaw's browser tabs. If a site requires login:\n\n1. The adapter will return an error like `{\"error\": \"HTTP 401\", \"hint\": \"Not logged in?\"}`\n2. Log in to the site in OpenClaw's browser:\n   ```bash\n   openclaw browser open https://twitter.com\n   ```\n3. Complete login manually in the browser window\n4. Retry the command\n\n## Creating New Adapters\n\nTurn any website into a CLI command:\n\n```bash\n# Read the guide\nbb-browser guide\n\n# Or just tell me: \"turn notion.so into a bb-browser adapter\"\n# I'll reverse-engineer the API, write the adapter, test it, and submit a PR.\n```\n\n## All 36 Platforms\n\n| Category | Platforms |\n|----------|-----------|\n| Search | Google, Baidu, Bing, DuckDuckGo, Sogou WeChat |\n| Social | Twitter/X, Reddit, Weibo, Xiaohongshu, Jike, LinkedIn, Hupu |\n| News | BBC, Reuters, 36kr, Toutiao, Eastmoney |\n| Dev | GitHub, StackOverflow, HackerNews, CSDN, cnblogs, V2EX, Dev.to, npm, PyPI, arXiv |\n| Video | YouTube, Bilibili |\n| Entertainment | Douban, IMDb, Genius, Qidian |\n| Finance | Xueqiu, Eastmoney, Yahoo Finance |\n| Jobs | BOSS Zhipin, LinkedIn |\n| Knowledge | Wikipedia, Zhihu, Open Library |\n| Shopping | SMZDM |\n| Tools | Youdao, GSMArena, Product Hunt, Ctrip |\n","skills/bb-browser/SKILL.md":"---\nname: bb-browser\ndescription: 强大的信息获取与浏览器自动化工具。通过浏览器 + 用户登录态，获取公域和私域信息。可访问任意网页、内部系统、登录后页面，执行表单填写、信息提取、页面操作。支持 site 系统（36 平台 103 命令一键调用）、带登录态的 fetch、网络请求拦截与 mock、操作录制等高级功能。\nallowed-tools: Bash(bb-browser:*)\n---\n\n# bb-browser - 信息获取与浏览器自动化\n\n## 核心价值\n\n通过浏览器 + 用户登录态，可以获取：\n- 公域信息：任意公开网页、搜索结果、新闻资讯\n- 私域信息：内部系统、企业应用、登录后页面、个人账户数据\n\n还可以代替用户执行浏览器操作：表单填写、按钮点击、数据提取、截图保存、批量操作。\n\n运行在用户真实浏览器中，复用已登录的账号，不触发反爬检测。\n\n## 快速开始\n\n```bash\nbb-browser open <url>        # 打开页面（新 tab）\nbb-browser snapshot -i       # 获取可交互元素\nbb-browser click @5          # 点击元素\nbb-browser fill @3 \"text\"    # 填写输入框\nbb-browser close             # 完成后关闭 tab\n```\n\n## Site 系统 — 把任何网站变成命令行 API\n\nsite 系统是 bb-browser 的核心特性，通过 adapter 将网站功能 CLI 化，覆盖 36+ 平台。\n\n```bash\n# 常用命令\nbb-browser site list                          # 列出所有 adapter\nbb-browser site search <query>                # 搜索 adapter\nbb-browser site <name> [args...]              # 运行 adapter\nbb-browser site update                        # 更新社区 adapter 库\n\n# 使用示例\nbb-browser site twitter/search \"Claude Code\"  # 搜索推文\nbb-browser site zhihu/hot                     # 知乎热榜\nbb-browser site github/repo owner/repo        # 仓库信息\nbb-browser site youtube/transcript <video_id> # 获取字幕\nbb-browser site reddit/thread <url>           # 帖子详情\nbb-browser site eastmoney/stock \"茅台\"         # 股票查询\nbb-browser site weibo/hot                     # 微博热搜\nbb-browser site arxiv/search \"transformer\"    # 论文搜索\n```\n\nadapter 自动处理 tab 管理（查找匹配域名的 tab 或新建），自动检测登录错误并提示。\n\n详细用法和 36 平台完整列表：参见 [references/site-system.md](references/site-system.md)\n创建自定义 adapter：参见 [references/adapter-development.md](references/adapter-development.md)\n\n## fetch — 带登录态的 curl\n\n在浏览器上下文中执行 fetch，自动携带 Cookie 和登录态。\n\n```bash\nbb-browser fetch <url>                                    # GET 请求\nbb-browser fetch <url> --method POST --body '{\"k\":\"v\"}'   # POST 请求\nbb-browser fetch <url> --headers '{\"Auth\":\"Bearer xxx\"}'  # 自定义请求头\nbb-browser fetch <url> --output data.json                 # 保存到文件\nbb-browser fetch /api/me.json                             # 相对路径（用当前 tab 的 origin）\n```\n\n自动域名路由：绝对路径自动查找匹配 tab 或新建；相对路径使用当前 tab。\n\n详细用法：参见 [references/fetch-and-network.md](references/fetch-and-network.md)\n\n## Tab 管理规范\n\n操作完成后必须关闭自己打开的 tab。\n\n```bash\n# 单 tab 场景\nbb-browser open https://example.com    # 打开新 tab\nbb-browser snapshot -i\nbb-browser click @5\nbb-browser close                        # 完成后关闭\n\n# 多 tab 场景\nbb-browser open https://site-a.com     # tabId: 123\nbb-browser open https://site-b.com     # tabId: 456\n# ... 操作 ...\nbb-browser tab close                    # 关闭当前 tab\nbb-browser tab close                    # 关闭剩余 tab\n\n# 指定 tab 操作\nbb-browser open https://example.com --tab current  # 在当前 tab 打开（不新建）\nbb-browser open https://example.com --tab 123      # 在指定 tabId 打开\n```\n\n## 核心工作流\n\n1. `open` 打开页面\n2. `snapshot -i` 查看可操作元素（返回 @ref）\n3. 用 `@ref` 执行操作（click, fill, etc.）\n4. 页面变化后重新 `snapshot -i`\n5. 任务完成后 `close` 关闭 tab\n\n## 命令速查\n\n### 导航\n\n```bash\nbb-browser open <url>                # 打开 URL（新 tab）\nbb-browser open <url> --tab current  # 在当前 tab 打开\nbb-browser back                      # 后退\nbb-browser forward                   # 前进\nbb-browser refresh                   # 刷新\nbb-browser close                     # 关闭当前 tab\n```\n\n### 快照\n\n```bash\nbb-browser snapshot             # 完整页面结构\nbb-browser snapshot -i          # 只显示可交互元素（推荐）\nbb-browser snapshot -c          # 移除空结构节点\nbb-browser snapshot -d 3        # 限制树深度为 3 层\nbb-browser snapshot -s \".main\"  # 限定 CSS 选择器范围\nbb-browser snapshot --json      # JSON 格式输出\n# 选项可组合：bb-browser snapshot -i -c -d 5\n```\n\n### 元素交互\n\n```bash\nbb-browser click @5             # 点击\nbb-browser hover @5             # 悬停\nbb-browser fill @3 \"text\"       # 清空并填写\nbb-browser type @3 \"text\"       # 追加输入（不清空）\nbb-browser check @7             # 勾选复选框\nbb-browser uncheck @7           # 取消勾选\nbb-browser select @4 \"option\"   # 下拉选择\nbb-browser press Enter          # 按键\nbb-browser press Control+a      # 组合键\nbb-browser scroll down          # 向下滚动（默认 300px）\nbb-browser scroll up 500        # 向上滚动 500px\n```\n\n### 获取信息\n\n```bash\nbb-browser get text @5          # 获取元素文本\nbb-browser get url              # 获取当前 URL\nbb-browser get title            # 获取页面标题\n```\n\n### Tab 管理\n\n```bash\nbb-browser tab                  # 列出所有 tab\nbb-browser tab new [url]        # 新建 tab\nbb-browser tab 2                # 切换到第 2 个 tab（按 index）\nbb-browser tab select --id 123  # 切换到指定 tabId 的 tab\nbb-browser tab close            # 关闭当前 tab\nbb-browser tab close 3          # 关闭第 3 个 tab（按 index）\nbb-browser tab close --id 123   # 关闭指定 tabId 的 tab\n```\n\n### 截图\n\n```bash\nbb-browser screenshot           # 截图（自动保存）\nbb-browser screenshot path.png  # 截图到指定路径\n```\n\n### 等待\n\n```bash\nbb-browser wait 2000            # 等待 2 秒\nbb-browser wait @5              # 等待元素出现\n```\n\n### JavaScript\n\n```bash\nbb-browser eval \"document.title\"              # 执行 JS\nbb-browser eval \"window.scrollTo(0, 1000)\"    # 滚动到指定位置\n```\n\n### Frame 切换\n\n```bash\nbb-browser frame \"#iframe-id\"   # 切换到 iframe\nbb-browser frame main           # 返回主 frame\n```\n\n### 对话框处理\n\n```bash\nbb-browser dialog accept        # 确认对话框\nbb-browser dialog dismiss       # 取消对话框\nbb-browser dialog accept \"text\" # 确认并输入（prompt）\n```\n\n### 网络与调试\n\n```bash\nbb-browser network requests                        # 查看网络请求\nbb-browser network requests \"api\" --with-body       # 过滤 + 完整请求/响应体\nbb-browser network route \"*analytics*\" --abort      # 拦截并阻止请求\nbb-browser network route \"*/api/user\" --body '{}'   # 拦截并 mock 响应\nbb-browser network unroute                          # 移除所有拦截规则\nbb-browser network clear                            # 清空请求记录\nbb-browser console                                  # 查看控制台消息\nbb-browser console --clear                          # 清空控制台\nbb-browser errors                                   # 查看 JS 错误\nbb-browser errors --clear                           # 清空错误记录\nbb-browser trace start                              # 开始录制用户操作\nbb-browser trace stop                               # 停止录制，输出事件列表\nbb-browser trace status                             # 查看录制状态\n```\n\n详细的 network 高级用法：参见 [references/fetch-and-network.md](references/fetch-and-network.md)\n\n## 全局选项\n\n```bash\n--json               # 以 JSON 格式输出（所有命令通用）\n--tab <tabId>        # 指定操作的标签页 ID（几乎所有命令通用）\n--mcp                # 启动 MCP server（用于 Claude Code / Cursor 等 AI 工具）\n```\n\n## Ref 使用说明\n\nsnapshot 返回的 `@ref` 是元素的临时标识：\n\n```\n@1 [button] \"提交\"\n@2 [input type=\"text\"] placeholder=\"请输入姓名\"\n@3 [a] \"查看详情\"\n```\n\n注意：\n- 页面导航后 ref 失效，需重新 snapshot\n- 动态内容加载后需重新 snapshot\n- ref 格式：`@1`, `@2`, `@3`...\n\n详细说明：参见 [references/snapshot-refs.md](references/snapshot-refs.md)\n\n## 并发操作\n\n```bash\n# 并发打开多个页面（各自独立 tab）\nbb-browser open https://site-a.com &\nbb-browser open https://site-b.com &\nbb-browser open https://site-c.com &\nwait\n# 每个返回独立的 tabId，互不干扰\n```\n\n## 信息提取 vs 页面操作\n\n根据目的选择不同的方法：\n\n### 提取页面内容（用 eval）\n\n当需要提取文章、正文等长文本时，用 `eval` 直接获取：\n\n```bash\n# 微信公众号文章\nbb-browser eval \"document.querySelector('#js_content').innerText\"\n\n# 知乎回答\nbb-browser eval \"document.querySelector('.RichContent-inner').innerText\"\n\n# 通用：获取页面主体文本\nbb-browser eval \"document.body.innerText.substring(0, 5000)\"\n\n# 获取所有链接\nbb-browser eval \"[...document.querySelectorAll('a')].map(a => a.href).join('\\n')\"\n```\n\n有些网站 DOM 嵌套很深，snapshot 输出冗长，`eval` 直接提取文本更高效。\n\n### 操作页面元素（用 snapshot -i）\n\n当需要点击、填写、选择时，用 `snapshot -i` 获取可交互元素：\n\n```bash\nbb-browser snapshot -i\n# @1 [button] \"登录\"\n# @2 [input] placeholder=\"用户名\"\n# @3 [input type=\"password\"]\n\nbb-browser fill @2 \"username\"\nbb-browser fill @3 \"password\"\nbb-browser click @1\n```\n\n`-i` 只显示可交互元素，过滤掉大量无关内容。\n\n## MCP 集成\n\nbb-browser 提供 MCP server，可与 Claude Code / Cursor 等 AI 工具集成：\n\n```bash\n# 启动 MCP server\nbb-browser --mcp\n```\n\n配置示例（Claude Code / Cursor）：\n```json\n{\n  \"mcpServers\": {\n    \"bb-browser\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"bb-browser\", \"--mcp\"]\n    }\n  }\n}\n```\n\n## 常见任务示例\n\n### 表单填写\n\n```bash\nbb-browser open https://example.com/form\nbb-browser snapshot -i\nbb-browser fill @1 \"张三\"\nbb-browser fill @2 \"zhangsan@example.com\"\nbb-browser click @3\nbb-browser wait 2000\nbb-browser close\n```\n\n### 信息提取\n\n```bash\nbb-browser open https://example.com/dashboard\nbb-browser snapshot -i\nbb-browser get text @5\nbb-browser screenshot report.png\nbb-browser close\n```\n\n### 批量操作\n\n```bash\nfor url in \"url1\" \"url2\" \"url3\"; do\n  bb-browser open \"$url\"\n  bb-browser snapshot -i --json\n  bb-browser close\ndone\n```\n\n## 深入文档\n\n| 文档 | 说明 |\n|------|------|\n| [references/site-system.md](references/site-system.md) | Site 系统完整指南：35 平台列表、命令用法、自动 tab 管理 |\n| [references/adapter-development.md](references/adapter-development.md) | Adapter 开发指南：API 逆向、三层复杂度、元数据格式 |\n| [references/fetch-and-network.md](references/fetch-and-network.md) | Fetch 与 Network 高级功能：带登录态请求、请求拦截与 mock |\n| [references/snapshot-refs.md](references/snapshot-refs.md) | Ref 生命周期、最佳实践、常见问题 |\n","skills/bb-browser/references/adapter-development.md":"# Adapter 开发指南\n\n## 开发流程概览\n\n1. 用 `network` 命令逆向 API\n2. 用 `eval` 测试 fetch 是否可行\n3. 编写 adapter JS 文件\n4. 保存到 `~/.bb-browser/sites/` 测试\n5. 提交 PR 到社区仓库\n\n<!-- 证据来源：packages/cli/src/index.ts:601-658 guide 命令输出 -->\n\n## Step 1：逆向 API\n\n```bash\n# 清空旧记录\nbb-browser network clear --tab <tabId>\n\n# 刷新页面触发请求\nbb-browser refresh --tab <tabId>\n\n# 查看 API 请求（filter 是位置参数，不是 --filter）\nbb-browser network requests \"api\" --with-body --json --tab <tabId>\n```\n\n重点关注：\n- 请求 URL 和参数格式\n- 认证方式（Cookie / Bearer token / CSRF token）\n- 响应数据结构\n\n## Step 2：测试 fetch\n\n```bash\n# 直接在浏览器中测试 fetch（Tier 1 验证）\nbb-browser eval \"fetch('/api/endpoint',{credentials:'include'}).then(r=>r.json())\" --tab <tabId>\n```\n\n根据结果判断复杂度：\n- **能直接拿到数据** → Tier 1（Cookie 认证，如 Reddit/GitHub/V2EX）\n- **需要额外请求头** → Tier 2（如 Twitter：Bearer + CSRF token）\n- **需要请求签名或注入** → Tier 3（如小红书：Pinia store / Webpack 模块）\n\n## Step 3：编写 Adapter\n\n### 元数据格式（`/* @meta */` 块）\n\n<!-- 证据来源：site.ts:56-118 parseSiteMeta() -->\n\n```javascript\n/* @meta\n{\n  \"name\": \"platform/command\",\n  \"description\": \"功能描述\",\n  \"domain\": \"www.example.com\",\n  \"args\": {\n    \"query\": {\"required\": true, \"description\": \"搜索关键词\"},\n    \"count\": {\"required\": false, \"description\": \"返回数量\"}\n  },\n  \"capabilities\": [\"search\"],\n  \"readOnly\": true,\n  \"example\": \"bb-browser site platform/command value\"\n}\n*/\nasync function(args) {\n  // adapter 实现\n}\n```\n\n### 元数据字段说明\n\n| 字段 | 必需 | 说明 |\n|------|------|------|\n| `name` | 是 | 唯一标识，格式 `platform/command` |\n| `description` | 是 | 功能描述 |\n| `domain` | 是 | 目标网站域名（用于自动 tab 匹配） |\n| `args` | 是 | 参数定义，每个参数含 `required` 和 `description` |\n| `capabilities` | 否 | 能力标签数组 |\n| `readOnly` | 否 | 是否只读操作 |\n| `example` | 否 | 使用示例 |\n\n### 旧格式兼容\n\n也支持 `// @tag value` 注释格式（向后兼容）：\n\n```javascript\n// @name platform/command\n// @description 功能描述\n// @domain www.example.com\n// @args query,filter\n// @example bb-browser site platform/command value\n```\n\n<!-- 证据来源：site.ts:100-117 旧格式解析 -->\n\n## 三层复杂度示例\n\n### Tier 1：Cookie 认证（~1 分钟）\n\n直接 fetch，`credentials: 'include'` 自动带 Cookie。\n\n```javascript\n/* @meta\n{\n  \"name\": \"reddit/search\",\n  \"description\": \"Search Reddit posts\",\n  \"domain\": \"www.reddit.com\",\n  \"args\": {\"query\": {\"required\": true, \"description\": \"Search query\"}},\n  \"readOnly\": true,\n  \"example\": \"bb-browser site reddit/search 'local LLM'\"\n}\n*/\nasync function(args) {\n  if (!args.query) return {error: 'Missing argument: query'};\n  const resp = await fetch(\n    '/search.json?q=' + encodeURIComponent(args.query) + '&limit=10',\n    {credentials: 'include'}\n  );\n  if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};\n  const data = await resp.json();\n  return data.data.children.map(c => ({\n    title: c.data.title,\n    url: 'https://www.reddit.com' + c.data.permalink,\n    score: c.data.score,\n    subreddit: c.data.subreddit\n  }));\n}\n```\n\n### Tier 2：Bearer + CSRF token（~3 分钟）\n\n需要从页面提取 token 构造请求头。\n\n```javascript\n/* @meta\n{\n  \"name\": \"twitter/search\",\n  \"description\": \"Search tweets\",\n  \"domain\": \"twitter.com\",\n  \"args\": {\"query\": {\"required\": true, \"description\": \"Search query\"}},\n  \"readOnly\": true\n}\n*/\nasync function(args) {\n  // 从 cookie 提取 CSRF token\n  const csrf = document.cookie.match(/ct0=([^;]+)/)?.[1];\n  if (!csrf) return {error: 'CSRF token not found', hint: 'Not logged in?'};\n\n  // 构造带认证的请求\n  const resp = await fetch('/i/api/2/search/adaptive.json?q=' + encodeURIComponent(args.query), {\n    credentials: 'include',\n    headers: {\n      'authorization': 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs...',\n      'x-csrf-token': csrf\n    }\n  });\n  if (!resp.ok) return {error: 'HTTP ' + resp.status};\n  return await resp.json();\n}\n```\n\n### Tier 3：Webpack 注入 / Pinia store（~10 分钟）\n\n需要访问页面内部状态或调用内部模块。\n\n## 抗变更模式\n\n网站频繁更新前端代码（CSS class、webpack module ID、GraphQL queryId 等）。以下是经过验证的抗变更模式。\n\n### 模式 1：结构化 DOM 提取（替代 CSS class 选择器）\n\n**问题**：网站经常修改 CSS class 名（如 Google 从 `div.g` 改为 `div.MjjYud`），导致 adapter 失效。\n\n**方案**：用语义化 HTML 元素（h3、a、article）定位内容，不依赖任何 class name。\n\n```javascript\n// ❌ 脆弱：依赖 CSS class（Google 已多次更改）\nconst items = doc.querySelectorAll('div.g');\n\n// ✅ 稳健：用语义元素定位\nconst h3s = doc.querySelectorAll('h3');\nfor (const h3 of h3s) {\n  const a = h3.closest('a');\n  if (!a) continue;\n  const link = a.getAttribute('href');\n  if (!link || !link.startsWith('http')) continue;\n  const title = h3.textContent.trim();\n\n  // 向上查找结果容器（找到有多个 h3 兄弟的层级停止）\n  let container = a;\n  while (container.parentElement && container.parentElement.tagName !== 'BODY') {\n    const sibs = [...container.parentElement.children];\n    if (sibs.filter(s => s.querySelector('h3')).length > 1) break;\n    container = container.parentElement;\n  }\n\n  // 在容器内、链接外查找摘要\n  const linkBlock = a.closest('div') || a;\n  let snippet = '';\n  for (const sp of container.querySelectorAll('span')) {\n    if (linkBlock.contains(sp)) continue;\n    const t = sp.textContent.trim();\n    if (t.length > 30 && t !== title) { snippet = t; break; }\n  }\n  results.push({ title, url: link, snippet });\n}\n```\n\n**适用于**：Google、Bing、DuckDuckGo、HackerNews 等搜索/列表页面。\n\n### 模式 2：Webpack 模块动态发现（替代硬编码 module ID）\n\n**问题**：SPA 网站（如 Twitter/X）的 webpack module ID 在每次部署时都会变化，硬编码 ID 很快失效。\n\n**方案**：通过搜索模块源码中的稳定签名来动态查找模块。\n\n```javascript\n// 第一步：获取 webpack require 函数\nlet __webpack_require__;\nconst chunkId = '__bb_' + Date.now();\nwindow.webpackChunk_twitter_responsive_web.push(\n  [[chunkId], {}, (req) => { __webpack_require__ = req; }]\n);\n\n// 第二步：按源码签名查找模块（不依赖 module ID）\n// 示例：查找 Transaction ID 生成器\nlet genTxId;\nfor (const id of Object.keys(__webpack_require__.m)) {\n  const src = __webpack_require__.m[id].toString();\n  // 用模块源码中稳定的字符串特征来匹配\n  if (src.includes('jf.x.com') && src.includes('jJ:')) {\n    genTxId = __webpack_require__(id).jJ;\n    break;\n  }\n}\nif (!genTxId) return {\n  error: 'Cannot find transaction ID generator',\n  hint: 'Twitter webpack structure may have changed.'\n};\n\n// 第三步：按 operationName 查找 GraphQL queryId\nlet queryId;\nfor (const id of Object.keys(__webpack_require__.m)) {\n  const src = __webpack_require__.m[id].toString();\n  const m = src.match(/queryId:\"([^\"]+)\",operationName:\"CreateTweet\"/);\n  if (m) { queryId = m[1]; break; }\n}\nif (!queryId) return {\n  error: 'Cannot find CreateTweet queryId',\n  hint: 'Twitter GraphQL schema may have changed.'\n};\n```\n\n**选择签名的原则**：\n- 选择**业务语义**字符串而非技术细节（`jf.x.com` 比变量名稳定）\n- 选择**多个特征**组合匹配（`includes('A') && includes('B')`）\n- 选择**export 名**匹配（`jJ:` 是 minified 的 export key，比函数体稳定）\n- 对于 GraphQL：`operationName` 几乎不变，`queryId` 会变 → 用前者查后者\n\n**适用于**：Twitter/X、小红书、抖音等 SPA 应用。\n\n### 模式 3：Vue/React 内部状态访问\n\n```javascript\n// Vue 3 + Pinia（如小红号）\nconst app = document.querySelector('#app').__vue_app__;\nconst store = app.config.globalProperties.$pinia._s.get('user');\n\n// React（如 Reddit new）\nconst fiber = document.querySelector('#App')._reactRootContainer?._internalRoot?.current;\n```\n\n**注意**：内部状态访问比 API 调用更脆弱，优先使用 API 逆向。\n\n## Step 4：测试\n\n```bash\n# 保存到私有目录\n# 文件路径：~/.bb-browser/sites/platform/command.js\n\n# 测试运行\nbb-browser site platform/command \"test query\" --json\n\n# 验证输出格式\nbb-browser site platform/command \"test query\"\n```\n\n## Step 5：贡献社区\n\n```bash\n# 方式 A：使用 gh CLI\ngit clone https://github.com/epiral/bb-sites && cd bb-sites\ngit checkout -b feat-platform\n# 添加 adapter 文件\ngit push -u origin feat-platform\ngh pr create --repo epiral/bb-sites\n\n# 方式 B：使用 bb-browser 自身\nbb-browser site github/fork epiral/bb-sites\ngit clone https://github.com/YOUR_USER/bb-sites && cd bb-sites\ngit checkout -b feat-platform\n# 添加 adapter 文件\ngit push -u origin feat-platform\nbb-browser site github/pr-create epiral/bb-sites --title \"feat(platform): add adapters\" --head \"YOUR_USER:feat-platform\"\n```\n\n## 错误处理规范\n\nadapter 返回错误时，使用统一格式：\n\n```javascript\n// 参数缺失\nreturn {error: 'Missing argument: query'};\n\n// HTTP 错误 + 登录提示\nreturn {error: 'HTTP 401', hint: 'Not logged in?'};\n\n// 自定义错误\nreturn {error: 'Rate limited', hint: 'Try again in 60 seconds'};\n```\n\n系统会自动检测 401/403/unauthorized/login 等关键词，生成登录提示。\n\n<!-- 证据来源：site.ts:406-427 错误检测逻辑 -->\n\n## 报告 Adapter Bug\n\n```bash\n# 通过 gh CLI\ngh issue create --repo epiral/bb-sites --title \"[adapter-name] 描述\"\n\n# 通过 bb-browser\nbb-browser site github/issue-create epiral/bb-sites --title \"[adapter-name] 描述\"\n```\n","skills/bb-browser/references/fetch-and-network.md":"# Fetch 与 Network 高级功能\n\n## fetch 命令 — 带登录态的 curl\n\n本质：在浏览器上下文中执行 `fetch()`，自动携带 Cookie 和登录态。\n\n<!-- 证据来源：packages/cli/src/commands/fetch.ts 注释 \"curl，但带浏览器登录态\" -->\n\n### 基本用法\n\n```bash\n# GET 请求（绝对路径）\nbb-browser fetch https://www.reddit.com/api/me.json\n\n# GET 请求（相对路径，使用当前 tab 的 origin）\nbb-browser fetch /api/me.json\n\n# POST 请求\nbb-browser fetch https://api.example.com/data --method POST --body '{\"key\":\"value\"}'\n\n# 自定义请求头\nbb-browser fetch https://api.example.com/data --headers '{\"Authorization\":\"Bearer token\"}'\n\n# 保存到文件\nbb-browser fetch https://api.example.com/data --output response.json\n\n# JSON 格式输出\nbb-browser fetch https://www.reddit.com/api/me.json --json\n```\n\n### 完整选项\n\n| 选项 | 说明 |\n|------|------|\n| `--method <GET\\|POST\\|...>` | HTTP 方法，默认 GET |\n| `--body <json>` | 请求体（仅 POST/PUT 等） |\n| `--headers <json>` | 自定义请求头（必须是合法 JSON） |\n| `--output <file>` | 保存响应到文件 |\n| `--json` | JSON 格式输出 |\n| `--tab <tabId>` | 指定操作的 tab（全局选项，非 fetch 专属） |\n\n<!-- 证据来源：fetch.ts:18-25 FetchOptions 接口，--tab 由 index.ts:215-218 全局解析 -->\n\n### 自动域名路由机制\n\nfetch 命令会自动处理 tab 匹配：\n\n1. **相对路径**（如 `/api/me.json`）：使用当前活动 tab 的 origin\n2. **绝对路径**（如 `https://www.reddit.com/...`）：\n   - 先查找已打开的匹配域名 tab\n   - 没有则自动打开新 tab 并等待 3 秒\n   - 在匹配的 tab 上下文中执行 fetch\n\n<!-- 证据来源：fetch.ts:42-62 ensureTabForOrigin()，fetch.ts:123-139 路由逻辑 -->\n\n### 响应处理\n\n- JSON 响应自动解析为对象\n- 非 JSON 响应返回文本\n- `--output` 时自动格式化写入文件\n\n<!-- 证据来源：fetch.ts:84-106 buildFetchScript() 中的 content-type 判断 -->\n\n### 典型场景\n\n```bash\n# 检查登录状态\nbb-browser fetch https://www.reddit.com/api/me.json\n\n# 调用内部 API\nbb-browser fetch https://internal.company.com/api/dashboard --json\n\n# 提交表单数据\nbb-browser fetch https://api.example.com/submit \\\n  --method POST \\\n  --body '{\"name\":\"test\",\"value\":123}' \\\n  --headers '{\"Content-Type\":\"application/json\"}'\n\n# 下载数据到文件\nbb-browser fetch https://api.example.com/export.csv --output data.csv\n```\n\n---\n\n## network 命令 — 网络监控与拦截\n\n<!-- 证据来源：packages/cli/src/commands/network.ts -->\n\n### 子命令一览\n\n```bash\n# 查看网络请求\nbb-browser network requests [filter] [--with-body] [--json]\n\n# 拦截请求（阻止）\nbb-browser network route <url> --abort\n\n# 拦截请求（mock 响应）\nbb-browser network route <url> --body '{\"mock\":\"data\"}'\n\n# 移除指定拦截规则\nbb-browser network unroute <url>\n\n# 移除所有拦截规则\nbb-browser network unroute\n\n# 清空请求记录\nbb-browser network clear\n```\n\n### network requests 详解\n\n```bash\n# 查看所有请求\nbb-browser network requests\n\n# 按关键词过滤（匹配 URL）\nbb-browser network requests \"api\"\n\n# 包含完整请求/响应体\nbb-browser network requests --with-body\n\n# 组合使用\nbb-browser network requests \"api\" --with-body --json\n```\n\n输出格式：\n```\nGET https://api.example.com/data\n  类型: fetch, 状态: 200 OK\n  请求头: 5, 响应头: 8        # --with-body 时显示\n  请求体: {\"query\":\"test\"}    # --with-body 时显示\n  响应体: {\"result\":[...]}    # --with-body 时显示\n```\n\n<!-- 证据来源：network.ts:46-77 requests 输出逻辑 -->\n\n### network route 详解\n\n拦截匹配 URL 的请求：\n\n```bash\n# 阻止广告/追踪请求\nbb-browser network route \"*analytics*\" --abort\n\n# Mock API 响应（用于测试）\nbb-browser network route \"*/api/user\" --body '{\"name\":\"test\",\"role\":\"admin\"}'\n\n# 添加多条规则\nbb-browser network route \"*tracker*\" --abort\nbb-browser network route \"*/api/config\" --body '{\"feature_flag\":true}'\n```\n\n<!-- 证据来源：network.ts:79-92 route 输出逻辑 -->\n\n### network unroute\n\n```bash\n# 移除指定规则\nbb-browser network unroute \"*analytics*\"\n\n# 移除所有规则\nbb-browser network unroute\n```\n\n<!-- 证据来源：network.ts:94-101 unroute 输出逻辑 -->\n\n### network clear\n\n```bash\n# 清空请求记录（重新开始监控）\nbb-browser network clear\n```\n\n<!-- 证据来源：network.ts:103-106 clear 输出逻辑 -->\n\n### API 逆向工程工作流\n\n这是 fetch + network 最强大的组合用法，用于发现网站内部 API：\n\n```bash\n# 1. 清空旧记录\nbb-browser network clear\n\n# 2. 刷新页面触发请求\nbb-browser refresh\n\n# 3. 查看 API 请求（过滤 + 完整体）\nbb-browser network requests \"api\" --with-body --json\n\n# 4. 找到目标 API 后，用 fetch 测试\nbb-browser fetch /api/discovered-endpoint --json\n\n# 5. 确认可行后，编写 site adapter\n# 参见 adapter-development.md\n```\n\n### 全局 --tab 选项\n\n所有 network 子命令都支持 `--tab <tabId>`，指定监控哪个 tab 的网络活动：\n\n```bash\nbb-browser network requests --tab 123\nbb-browser network route \"*api*\" --abort --tab 456\nbb-browser network clear --tab 123\n```\n","skills/bb-browser/references/site-system.md":"# Site 系统 — 把任何网站变成命令行 API\n\n## 核心概念\n\nSite 系统通过 adapter（适配器）将网站功能 CLI 化。每个 adapter 是一个 JS 文件，在用户真实浏览器中执行，复用登录态，返回结构化 JSON。\n\n<!-- 证据来源：packages/cli/src/commands/site.ts -->\n\n## 命令速查\n\n```bash\n# 列出所有可用 adapter（按平台分组）\nbb-browser site list\n\n# 搜索 adapter（模糊匹配名称、描述、域名）\nbb-browser site search <query>\n\n# 运行 adapter（简写，推荐）\nbb-browser site <name> [args...]\n\n# 运行 adapter（完整写法）\nbb-browser site run <name> [args...]\n\n# 更新社区 adapter 库（从 github.com/epiral/bb-sites 拉取）\nbb-browser site update\n\n# 查看 adapter 开发指南\nbb-browser guide\n```\n\n## 参数传递\n\n支持两种格式混合使用：\n\n```bash\n# 位置参数（按 adapter 定义的参数顺序）\nbb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/...\n\n# 命名参数（--flag value 格式）\nbb-browser site github/pr-create epiral/bb-sites --title \"feat: ...\" --head \"user:branch\"\n\n# 混合使用\nbb-browser site twitter/search \"AI agent\" --count 20\n```\n\n<!-- 证据来源：site.ts:290-314 参数解析逻辑 -->\n\n## Adapter 目录与优先级\n\n```\n~/.bb-browser/\n├── sites/              # 私有 adapter（优先级高，覆盖同名社区 adapter）\n│   └── platform/\n│       └── command.js\n└── bb-sites/           # 社区 adapter（通过 bb-browser site update 拉取）\n    └── platform/\n        └── command.js\n```\n\n私有 adapter 优先于社区同名 adapter。\n\n<!-- 证据来源：site.ts:24-26 目录常量，site.ts:148-157 getAllSites() 合并逻辑 -->\n\n## 自动 Tab 管理\n\n运行 adapter 时，系统自动处理 tab：\n\n1. 如果指定了 `--tab <tabId>`，直接使用该 tab\n2. 否则，根据 adapter 的 `domain` 字段查找已打开的匹配 tab\n3. 如果没有匹配的 tab，自动打开新 tab 并等待 3 秒加载\n\n域名匹配规则：精确匹配或子域名匹配（如 `x.com` 匹配 `api.x.com`）。\n\n<!-- 证据来源：site.ts:162-169 matchTabOrigin()，site.ts:342-368 tab 查找逻辑 -->\n\n## 错误处理与登录提示\n\nadapter 返回 `{error: \"...\", hint: \"...\"}` 时，系统自动检测登录相关错误（匹配 `401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth`），并提示用户先在浏览器中登录。\n\n```bash\n# 错误示例\n[error] site twitter/search: HTTP 401\n  Hint: Please log in to https://twitter.com in your browser first, then retry.\n```\n\n<!-- 证据来源：site.ts:406-427 错误处理逻辑 -->\n\n## 36 平台完整列表\n\n<!-- 证据来源：README.md \"36 platforms, 103 commands\"，具体 adapter 数量以 bb-browser site list 实际输出为准 -->\n\n### 搜索引擎\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Google | `site google/search \"query\"` | 搜索 |\n| Baidu | `site baidu/search \"query\"` | 百度搜索 |\n| Bing | `site bing/search \"query\"` | 必应搜索 |\n| DuckDuckGo | `site duckduckgo/search \"query\"` | DuckDuckGo 搜索 |\n| Sogou WeChat | `site sogou/wechat \"query\"` | 搜狗微信文章搜索 |\n\n### 社交媒体\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Twitter/X | `site twitter/search \"query\"` | 搜索推文 |\n| Twitter/X | `site twitter/user <handle>` | 用户信息 |\n| Reddit | `site reddit/thread <url>` | 帖子详情 |\n| Reddit | `site reddit/search \"query\"` | 搜索 |\n| Weibo | `site weibo/search \"query\"` | 微博搜索 |\n| Weibo | `site weibo/hot` | 微博热搜 |\n| Xiaohongshu | `site xiaohongshu/search \"query\"` | 小红书搜索 |\n| Jike | `site jike/feed` | 即刻动态 |\n| LinkedIn | `site linkedin/search \"query\"` | 搜索 |\n| LinkedIn | `site linkedin/profile <url>` | 个人资料 |\n| Hupu | `site hupu/hot` | 虎扑热帖 |\n\n### 新闻资讯\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| BBC | `site bbc/headlines` | BBC 头条 |\n| Reuters | `site reuters/search \"query\"` | 路透社搜索 |\n| 36kr | `site 36kr/newsflash` | 36氪快讯 |\n| Toutiao | `site toutiao/hot` | 今日头条热榜 |\n| Eastmoney | `site eastmoney/news \"query\"` | 东方财富新闻 |\n\n### 技术开发\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| GitHub | `site github/repo <owner/repo>` | 仓库信息 |\n| GitHub | `site github/issues <owner/repo>` | Issue 列表 |\n| GitHub | `site github/pr-create <repo> --title \"...\"` | 创建 PR |\n| StackOverflow | `site stackoverflow/search \"query\"` | 搜索 |\n| HackerNews | `site hackernews/top` | 热门帖子 |\n| CSDN | `site csdn/search \"query\"` | CSDN 搜索 |\n| cnblogs | `site cnblogs/search \"query\"` | 博客园搜索 |\n| V2EX | `site v2ex/hot` | V2EX 热帖 |\n| Dev.to | `site devto/search \"query\"` | Dev.to 搜索 |\n| npm | `site npm/package <name>` | npm 包信息 |\n| PyPI | `site pypi/package <name>` | PyPI 包信息 |\n| arXiv | `site arxiv/search \"query\"` | 论文搜索 |\n\n### 视频平台\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| YouTube | `site youtube/search \"query\"` | 搜索视频 |\n| YouTube | `site youtube/transcript <video_id>` | 获取字幕 |\n| Bilibili | `site bilibili/search \"query\"` | B站搜索 |\n| Bilibili | `site bilibili/popular` | B站热门 |\n\n### 影音娱乐\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Douban | `site douban/movie <name>` | 豆瓣电影 |\n| Douban | `site douban/top250` | 豆瓣 Top250 |\n| IMDb | `site imdb/search \"query\"` | IMDb 搜索 |\n| Genius | `site genius/search \"query\"` | 歌词搜索 |\n| Qidian | `site qidian/search \"query\"` | 起点小说搜索 |\n\n### 财经股票\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Eastmoney | `site eastmoney/stock \"茅台\"` | 股票查询 |\n| Yahoo Finance | `site yahoo-finance/stock <ticker>` | 股票行情 |\n\n### 求职招聘\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| BOSS Zhipin | `site boss/search \"query\"` | BOSS 直聘搜索 |\n| BOSS Zhipin | `site boss/detail <url>` | 职位详情 |\n\n### 知识百科\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Wikipedia | `site wikipedia/search \"query\"` | 维基百科搜索 |\n| Wikipedia | `site wikipedia/summary \"topic\"` | 摘要 |\n| Zhihu | `site zhihu/hot` | 知乎热榜 |\n| Zhihu | `site zhihu/question <id>` | 问题详情 |\n| Open Library | `site openlibrary/search \"query\"` | 图书搜索 |\n\n### 消费购物\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| SMZDM | `site smzdm/search \"query\"` | 什么值得买搜索 |\n\n### 实用工具\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Youdao | `site youdao/translate \"text\"` | 有道翻译 |\n| GSMArena | `site gsmarena/search \"phone\"` | 手机参数 |\n| Product Hunt | `site producthunt/trending` | 热门产品 |\n| Ctrip | `site ctrip/search \"destination\"` | 携程搜索 |\n\n## 常用场景示例\n\n### 信息检索\n\n```bash\n# 搜索技术话题\nbb-browser site twitter/search \"Claude Code\"\nbb-browser site reddit/search \"local LLM\"\nbb-browser site hackernews/top\n\n# 查看热榜\nbb-browser site zhihu/hot\nbb-browser site weibo/hot\nbb-browser site v2ex/hot\n\n# 查询股票\nbb-browser site eastmoney/stock \"茅台\"\n```\n\n### 开发辅助\n\n```bash\n# GitHub 操作\nbb-browser site github/repo anthropics/claude-code\nbb-browser site github/issues owner/repo\n\n# 查包信息\nbb-browser site npm/package bb-browser\nbb-browser site pypi/package requests\n\n# 搜索技术问题\nbb-browser site stackoverflow/search \"async await error handling\"\n```\n\n### 内容获取\n\n```bash\n# 获取 YouTube 字幕\nbb-browser site youtube/transcript dQw4w9WgXcQ\n\n# 获取 Reddit 帖子完整内容\nbb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/...\n\n# 论文搜索\nbb-browser site arxiv/search \"transformer attention mechanism\"\n```\n\n## 与 --json 配合\n\n所有 site 命令都支持 `--json` 输出结构化数据：\n\n```bash\nbb-browser site zhihu/hot --json\nbb-browser site twitter/search \"AI\" --json\n```\n\n## 更多信息\n\n- 创建自定义 adapter：参见 [adapter-development.md](adapter-development.md)\n- 社区 adapter 仓库：https://github.com/epiral/bb-sites\n","skills/bb-browser/references/snapshot-refs.md":"# Snapshot + Refs 工作流\n\nbb-browser 使用紧凑的元素引用（ref），大幅减少 AI Agent 的上下文消耗。\n\n## 工作原理\n\n### 传统方式的问题\n\n```\n完整 DOM/HTML 发送给 AI → AI 解析 → 生成 CSS 选择器 → 执行操作\n每次交互 ~3000-5000 tokens\n```\n\n### bb-browser 的方案\n\n```\n紧凑快照 → 分配 @ref → 直接用 ref 操作\n每次交互 ~200-400 tokens\n```\n\n## Snapshot 命令\n\n```bash\n# 完整快照（显示页面结构）\nbb-browser snapshot\n\n# 只显示可交互元素（推荐）\nbb-browser snapshot -i\n\n# JSON 格式输出\nbb-browser snapshot -i --json\n```\n\n## 快照输出格式\n\n```\n页面: Example Site - 首页\nURL: https://example.com\n\n@1 [header]\n  @2 [nav]\n    @3 [a] \"首页\"\n    @4 [a] \"产品\"\n    @5 [a] \"关于\"\n  @6 [button] \"登录\"\n\n@7 [main]\n  @8 [h1] \"欢迎\"\n  @9 [form]\n    @10 [input type=\"email\"] placeholder=\"邮箱\"\n    @11 [input type=\"password\"] placeholder=\"密码\"\n    @12 [button type=\"submit\"] \"登录\"\n\n@13 [footer]\n  @14 [a] \"隐私政策\"\n```\n\n## 使用 Ref\n\n有了 ref 后直接操作：\n\n```bash\n# 点击登录按钮\nbb-browser click @6\n\n# 填写邮箱\nbb-browser fill @10 \"user@example.com\"\n\n# 填写密码\nbb-browser fill @11 \"password123\"\n\n# 提交表单\nbb-browser click @12\n```\n\n## Ref 生命周期\n\n**重要**：页面变化后 ref 会失效！\n\n```bash\n# 获取初始快照\nbb-browser snapshot -i\n# @1 [button] \"下一页\"\n\n# 点击触发页面变化\nbb-browser click @1\n\n# 必须重新获取快照！\nbb-browser snapshot -i\n# @1 [h1] \"第二页\"  ← 现在是不同的元素\n```\n\n### Ref 失效的场景\n\n1. **页面导航**：点击链接、表单提交后跳转\n2. **动态加载**：AJAX 加载新内容\n3. **DOM 变化**：JavaScript 修改页面结构\n4. **弹窗/下拉**：打开模态框、展开菜单\n\n## 最佳实践\n\n### 1. 操作前先 Snapshot\n\n```bash\n# 正确\nbb-browser open https://example.com\nbb-browser snapshot -i          # 先获取 ref\nbb-browser click @1             # 再使用\n\n# 错误\nbb-browser open https://example.com\nbb-browser click @1             # ref 不存在！\n```\n\n### 2. 导航后重新 Snapshot\n\n```bash\nbb-browser click @5             # 导航到新页面\nbb-browser snapshot -i          # 获取新页面的 ref\nbb-browser click @1             # 使用新的 ref\n```\n\n### 3. 动态变化后重新 Snapshot\n\n```bash\nbb-browser click @1             # 打开下拉菜单\nbb-browser snapshot -i          # 获取菜单项的 ref\nbb-browser click @7             # 选择菜单项\n```\n\n### 4. 等待加载完成\n\n```bash\nbb-browser click @3             # 触发 AJAX 加载\nbb-browser wait 1000            # 等待加载\nbb-browser snapshot -i          # 获取新内容的 ref\n```\n\n## Ref 格式说明\n\n```\n@1 [tag type=\"value\"] \"文本内容\" placeholder=\"提示\"\n│   │   │             │          │\n│   │   │             │          └─ 其他属性\n│   │   │             └─ 可见文本\n│   │   └─ 关键属性\n│   └─ HTML 标签\n└─ 唯一 ref ID\n```\n\n### 常见模式\n\n```\n@1 [button] \"提交\"                    # 按钮\n@2 [input type=\"email\"]               # 邮箱输入框\n@3 [input type=\"password\"]            # 密码输入框\n@4 [a href=\"/page\"] \"链接文字\"         # 链接\n@5 [select]                           # 下拉框\n@6 [textarea] placeholder=\"请输入\"     # 文本域\n@7 [checkbox] checked                 # 已勾选的复选框\n@8 [radio] selected                   # 已选中的单选框\n```\n\n## 常见问题\n\n### \"Ref not found\" 错误\n\n页面已变化，ref 失效了：\n\n```bash\n# 重新获取快照\nbb-browser snapshot -i\n```\n\n### 元素不在快照中\n\n可能需要滚动或等待：\n\n```bash\n# 滚动到底部\nbb-browser scroll down\nbb-browser snapshot -i\n\n# 或等待动态内容\nbb-browser wait 2000\nbb-browser snapshot -i\n```\n\n### 快照太长\n\n使用 `-i` 只显示可交互元素：\n\n```bash\n# 完整快照可能很长\nbb-browser snapshot\n\n# 只显示可交互元素（推荐）\nbb-browser snapshot -i\n```\n\n### 需要操作不可交互元素\n\n使用 `eval` 直接执行 JavaScript：\n\n```bash\nbb-browser eval \"document.querySelector('.hidden-element').click()\"\n```\n\n## 调试技巧\n\n```bash\n# 查看完整页面结构\nbb-browser snapshot\n\n# 只看可交互元素\nbb-browser snapshot -i\n\n# JSON 格式便于解析\nbb-browser snapshot -i --json\n\n# 获取特定元素的文本\nbb-browser get text @5\n```\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/epiral/bb-browser/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# bb-browser Agent 开发规范\n\n## 架构\n\n```\nCLI ──HTTP──▶ Daemon ──controlConn CDP──▶ Chrome\n                │\n                │ --hub 模式 (可选)\n                ├── Hub ProviderStream ──▶ Pinix Hub\n                │\n                │ Protocol 2\n                └── Streamer (bb-viewer) ──captureConn + inputConn CDP──▶ Chrome\n                         │\n                         └── WebRTC video + DataChannel ──▶ Clip Web UI\n```\n\n**三根 CDP 连接到同一个 Chrome：**\n\n| 连接 | Owner | 职责 |\n|------|-------|------|\n| controlConn | daemon | Agent 操作：tab/nav/site/DOM/click-by-ref |\n| inputConn | streamer | Human 实时输入：鼠标/键盘/IME (DataChannel → CDP) |\n| captureConn | streamer | 视频流：screencast → VP8 → WebRTC |\n\n**组件：**\n\n| 组件 | 位置 | 职责 |\n|------|------|------|\n| CLI | `packages/cli/` | 命令行入口，HTTP 调 daemon |\n| Daemon | `packages/daemon/` | 控制中心：CDP controlConn、HTTP API、Hub 连接、site 执行、streamer 管理 |\n| Shared | `packages/shared/` | 统一命令定义 (`commands.ts`)、协议类型 (`protocol.ts`) |\n| Streamer | `bb-viewer` repo | 纯视频 + 实时输入（Go binary，daemon 子进程） |\n\n## Daemon 两种模式\n\n```bash\n# 本地模式：Agent 用 CLI 操作浏览器\nbb-browser daemon start\n\n# Hub 模式：注册到 Pinix Hub，远程可用\nbb-browser daemon start --hub https://hub.pinixai.com --hub-token xxx\n```\n\n## 协议\n\n### Protocol 1: CLI ↔ daemon (`POST /command`)\n\n请求：`{\"method\": \"snap\", \"params\": {\"tab\": \"3ef9\"}}`\n成功：`{\"result\": {\"tab\": \"3ef9\", \"title\": \"Google\", \"snapshot\": \"...\"}}`\n失败：`{\"error\": {\"message\": \"Missing --tab\", \"hint\": \"Run 'bb-browser tab list'\"}}`\n\n### Protocol 2: daemon ↔ streamer (`POST /command`)\n\n| Method | Params | 说明 |\n|--------|--------|------|\n| connect | {cdpUrl, ice?} | 连接 CDP，创建 WebRTC peer |\n| answer | {answer_sdp, candidates} | 完成 WebRTC 信令 |\n| switch | {cdpUrl} | 切到新 tab（peer 不变） |\n| stop | {} | 停止 |\n\n## 统一命令定义\n\n`packages/shared/src/commands.ts` 是所有命令的单一定义源。每个命令包含 `method`、`group`、`description`、`requiresTab`、`params`。CLI 解析、daemon dispatch、Hub 注册都从这里读取。\n\n添加新命令：\n1. `commands.ts` — 添加 CommandDef\n2. `protocol.ts` — 添加 ActionType\n3. `command-dispatch.ts` — 添加处理分支\n4. `packages/cli/src/commands/<name>.ts` + `index.ts` — CLI 命令\n\n## CLI 命令\n\n`--tab` 必填（除 `open`、`site` 组、`tab list/new`、`daemon`）。\n\n| 组 | 命令 |\n|----|------|\n| 导航 | `open <url> [--tab]`, `back --tab`, `forward --tab`, `reload --tab`, `close --tab` |\n| 观察 | `snap --tab`, `screenshot --tab`, `get <attr> --tab`, `eval <js> --tab` |\n| 交互 | `click/hover/fill/type/press/scroll/check/uncheck/select --tab` |\n| Tab | `tab list`, `tab new [url]` |\n| Site | `site list`, `site info <name>`, `site run <name>` |\n| 调试 | `network/console/errors/trace/cookies/source --tab` |\n| 进程 | `daemon start [--hub]`, `daemon stop`, `daemon status` |\n\n### Debug 命令参数约定\n\nDebug 命令（`network`, `console`, `errors`, `trace`, `source`）有子命令的统一用 `--action` 参数：\n\n```bash\n# CLI 用法（位置参数）\nbb-browser source grep \"api.example\" --tab <id>\nbb-browser trace start --tab <id>\n\n# Hub invoke 用法（命名参数）\npinix invoke browser source --action grep --pattern \"api.example\" --tab <id>\npinix invoke browser trace --action start --tab <id>\npinix invoke browser network --action requests --excludeStatic true --tab <id>\n```\n\n旧参数名（`sourceCommand`、`traceCommand` 等）仍可用但已 deprecated。\n\n## 设计不变量\n\n1. **Daemon 是唯一操作 API**。CLI 和 Hub invoke 都通过 daemon。\n2. **Streamer 不做业务逻辑**。不管 tab、不做导航。只做帧编码和输入转发。\n3. **Tab ID 统一用 daemon 分配的短 ID**（如 `3ef9`）。Streamer 不知道 tab ID，只接收 CDP WebSocket URL。\n4. **Site 执行在 daemon 内**。不 shell-out CLI。\n5. **所有操作响应包含 `tab`**（短 ID）。观察类响应包含 `cursor`。\n6. **Per-tab 事件隔离**。tab 关闭时释放短 ID 和事件缓冲。\n7. **`seq` 全局单调递增**，不可回退。\n8. **Daemon 启动时清理旧进程**。`cleanupStaleDaemon()` 确保不残留。\n\n## Hub Clip 注册\n\nHub 模式下 daemon 注册：\n\n| Clip | 命令 |\n|------|------|\n| browser | 所有标准命令 + `stream.start/answer/close/switch` |\n| \\<platform\\> | 每个 site adapter 一个命令（如 `google/search`） |\n\nClip Web UI 通过 Hub invoke 调用标准命令（tab_list、open、reload 等）和 stream 命令。\n\n## Stream 生命周期 (view.html)\n\nClip Web UI (`web/view.html`) 通过心跳 + visibility 检测管理 stream 生命周期：\n\n```\nACTIVE (visible) → Ping 每 5s via DataChannel (opcode 0x08)\nIDLE   (hidden)  → Ping 每 10s, 30s 后自动 disconnect\nCLOSED           → overlay 显示断开原因, 可点 Connect 重连\n```\n\n**前端 30s idle disconnect 是第一道防线，后端 45s watchdog 是安全网。**\n\n`cleanup(reason)` 根据原因显示不同 overlay：\n- `user` → \"Disconnected\"\n- `idle` → \"Stream closed — tab was inactive\"\n- `network` → \"Connection lost\"\n\n关键实现约束：\n- 不用 `setTimeout`（hidden tab 被浏览器节流），用 `setInterval` + 时间戳\n- 不用 `BigInt`（兼容性），用 `Math.floor` + `>>>`\n- `disconnect` 里 `invoke(\"stream.close\")` 是 fire-and-forget，不 await\n\n## Docker 运行模式\n\nDocker 中 Chrome 必须以**有头模式**运行在 Xvfb 虚拟帧缓冲上。\n\n```\nXvfb (:99) ← Chrome (headed, DISPLAY=:99) ← CDP screencast → bb-viewer → WebRTC\n```\n\n**为什么不能用 `--headless=new`：** Linux 上 `--headless=new` 跳过整个 X11/Ozone 显示层，导致 WebGL、`navigator.plugins`、屏幕信息等 API 返回异常值。Google 等反自动化系统检测这些底层渲染差异来识别 headless 浏览器。macOS 上 headless 没问题，因为 Chrome 仍链接完整的 Cocoa/CoreGraphics 框架，只是不显示窗口。\n\nChrome 始终以有头模式启动，不使用 `--headless=new`。macOS 上没有聚焦的用户 session 时 Chrome 自动离屏渲染（无可见窗口），效果等同 headless 但保留完整 GUI API。\n\n**Docker 关键环境变量：**\n- `DISPLAY=:99` — Xvfb 虚拟显示（Dockerfile 已设置）\n\n**Stealth 注入 = 有害：** Google 的反自动化不检测 CDP 本身，而是检测 `Emulation.setUserAgentOverride`、`Page.addScriptToEvaluateOnNewDocument` 等 CDP domain 调用。不注入任何 stealth，用裸 CDP 即可。\n\n### Docker 构建\n\n```bash\n# 构建（单阶段，下载预编译 bb-viewer + Chrome，阿里云 apt/npm 镜像）\ndocker build --platform linux/amd64 -t bb-browser:amd64 .\n\n# 运行（TURN 凭证自动从 Hub API 获取，不需要配置）\ndocker run -d --platform linux/amd64 --name bb-browser \\\n  -v bb-browser-data:/data -p 19825:19824 --shm-size=2g \\\n  -e CHROME_WINDOW_SIZE=1280,720 \\\n  bb-browser:amd64 --host 0.0.0.0 \\\n  --hub https://hub.pinixai.com --hub-token <token>\n```\n\n镜像内预装了 Chrome for Testing + bb-viewer（静态链接），运行时无需下载。\n`--shm-size=2g` 防止 Chrome 因共享内存不足 crash。\nTURN 环境变量（`TURN_URL`/`TURN_SECRET`）仍可用于覆盖默认值。\n\n**推荐：使用 Pinix 全家桶镜像 `lueco/pinix`，包含 pinixd + bb-browser + Chrome，一个 token 启动一切。**\n\n### bb-viewer (streamer)\n\n- 预编译 binary 在 COS 上，daemon 首次 stream 时自动下载（`~/.bb-browser/bin/bb-viewer`）\n- 静态链接 libvpx + libturbojpeg，不依赖用户系统库版本\n- JPEG → I420 解码使用 `tjDecompress2` (RGB) 再手动转 I420，而非 `tjDecompressToYUVPlanes`（后者在 libturbojpeg 2.1.x 对奇数高度帧有 bug）\n- Decoder 自动 round down 到偶数尺寸\n\n### TURN relay\n\nTURN 凭证通过 Hub API `GET /turn/credentials` 动态获取（24h TTL），secret 只在服务端。\ndaemon 启动 streamer 时自动请求，不需要用户配置。\n环境变量 `TURN_URL` / `TURN_SECRET` 可覆盖（standalone 模式）。\n\n## 代码规范\n\n- Commit：`<type>(<scope>): <summary>`，英文\n- 类型：`fix` / `feat` / `refactor` / `chore` / `docs`\n- 构建：`pnpm build`\n- 测试：`pnpm test`\n- lint：`pnpm lint`\n\n## 参考\n\n- [bb-viewer issue #5](https://github.com/epiral/bb-viewer/issues/5) — Stream 生命周期设计\n- [issue #224](https://github.com/epiral/bb-browser/issues/224) — daemon 生命周期（已修复）\n","isInternal":false,"tokens":2844,"sizeBytes":8681},{"name":"SKILL.md","path":"skills/bb-browser-openclaw/SKILL.md","rawUrl":"https://raw.githubusercontent.com/epiral/bb-browser/HEAD/skills/bb-browser-openclaw/SKILL.md","title":"bb-browser-openclaw","category":"anthropic-skill","format":"markdown","content":"---\nname: bb-browser-openclaw\ndescription: Turn any website into a CLI command. 36 platforms, 103 commands — Twitter, Reddit, GitHub, YouTube, Zhihu, Bilibili, Weibo, and more. Uses OpenClaw's browser directly, no extra extension needed.\nrequires:\n  bins: bb-browser\nallowed-tools: Bash(bb-browser:*)\n---\n\n# bb-browser sites — The web as CLI\n\n36 platforms, 103 commands. One-liner structured data from any website using your login state.\n\n**All commands use `--openclaw` to run through OpenClaw's browser. No Chrome extension or daemon needed.**\n\n## Quick Start\n\n```bash\n# First time: pull community adapters\nbb-browser site update\n\n# See what's available\nbb-browser site list\n\n# See which adapters match your browsing habits\nbb-browser site recommend\n\n# Run any adapter via OpenClaw's browser\nbb-browser site reddit/hot --openclaw\nbb-browser site hackernews/top 5 --openclaw\nbb-browser site v2ex/hot --openclaw\n```\n\n## IMPORTANT: Always use --openclaw\n\nEvery `bb-browser site` command MUST include `--openclaw` to use OpenClaw's browser:\n\n```bash\n# Correct\nbb-browser site twitter/search \"AI agent\" --openclaw\nbb-browser site zhihu/hot 10 --openclaw --json\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[] | {name, changePercent}'\n\n# Wrong (requires separate Chrome extension)\nbb-browser site twitter/search \"AI agent\"\n```\n\n## Data Extraction (most common use)\n\n```bash\n# Social media\nbb-browser site twitter/search \"OpenClaw\" --openclaw\nbb-browser site twitter/thread <tweet-url> --openclaw\nbb-browser site reddit/thread <post-url> --openclaw\nbb-browser site weibo/hot --openclaw\nbb-browser site xiaohongshu/search \"query\" --openclaw\n\n# Developer\nbb-browser site github/repo owner/repo --openclaw\nbb-browser site github/issues owner/repo --openclaw\nbb-browser site hackernews/top 10 --openclaw\nbb-browser site stackoverflow/search \"async await\" --openclaw\nbb-browser site arxiv/search \"transformer\" --openclaw\n\n# Finance\nbb-browser site xueqiu/stock SH600519 --openclaw\nbb-browser site xueqiu/hot-stock 5 --openclaw\nbb-browser site eastmoney/stock \"茅台\" --openclaw\n\n# News & Knowledge\nbb-browser site zhihu/hot --openclaw\nbb-browser site 36kr/newsflash --openclaw\nbb-browser site wikipedia/summary \"Python\" --openclaw\n\n# Video\nbb-browser site youtube/transcript VIDEO_ID --openclaw\nbb-browser site bilibili/search \"query\" --openclaw\n```\n\n## Filtering with --jq\n\nUse `--jq` to extract specific fields (no need for `--json`, it's implied):\n\n```bash\n# Just stock names\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[].name'\n\n# Specific fields as objects\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[] | {name, changePercent, heat}'\n\n# Filter results\nbb-browser site reddit/hot --openclaw --jq '.posts[] | {title, score}'\n```\n\n## View adapter details\n\n```bash\n# Check what args an adapter takes\nbb-browser site info xueqiu/stock\n\n# Search adapters by keyword\nbb-browser site search reddit\n```\n\n## Login State\n\nAdapters run inside OpenClaw's browser tabs. If a site requires login:\n\n1. The adapter will return an error like `{\"error\": \"HTTP 401\", \"hint\": \"Not logged in?\"}`\n2. Log in to the site in OpenClaw's browser:\n   ```bash\n   openclaw browser open https://twitter.com\n   ```\n3. Complete login manually in the browser window\n4. Retry the command\n\n## Creating New Adapters\n\nTurn any website into a CLI command:\n\n```bash\n# Read the guide\nbb-browser guide\n\n# Or just tell me: \"turn notion.so into a bb-browser adapter\"\n# I'll reverse-engineer the API, write the adapter, test it, and submit a PR.\n```\n\n## All 36 Platforms\n\n| Category | Platforms |\n|----------|-----------|\n| Search | Google, Baidu, Bing, DuckDuckGo, Sogou WeChat |\n| Social | Twitter/X, Reddit, Weibo, Xiaohongshu, Jike, LinkedIn, Hupu |\n| News | BBC, Reuters, 36kr, Toutiao, Eastmoney |\n| Dev | GitHub, StackOverflow, HackerNews, CSDN, cnblogs, V2EX, Dev.to, npm, PyPI, arXiv |\n| Video | YouTube, Bilibili |\n| Entertainment | Douban, IMDb, Genius, Qidian |\n| Finance | Xueqiu, Eastmoney, Yahoo Finance |\n| Jobs | BOSS Zhipin, LinkedIn |\n| Knowledge | Wikipedia, Zhihu, Open Library |\n| Shopping | SMZDM |\n| Tools | Youdao, GSMArena, Product Hunt, Ctrip |\n","frontmatter":{"name":"bb-browser-openclaw","description":"Turn any website into a CLI command. 36 platforms, 103 commands — Twitter, Reddit, GitHub, YouTube, Zhihu, Bilibili, Weibo, and more. Uses OpenClaw's browser directly, no extra extension needed.","requires":{"bins":"bb-browser"},"allowed-tools":"Bash(bb-browser:*)","allowedTools":["Bash(bb-browser:*)"]},"allowedTools":["Bash(bb-browser:*)"],"isInternal":false,"tokens":1160,"sizeBytes":4168},{"name":"adapter-development.md","path":"skills/bb-browser/references/adapter-development.md","rawUrl":"https://raw.githubusercontent.com/epiral/bb-browser/HEAD/skills/bb-browser/references/adapter-development.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Adapter 开发指南\n\n## 开发流程概览\n\n1. 用 `network` 命令逆向 API\n2. 用 `eval` 测试 fetch 是否可行\n3. 编写 adapter JS 文件\n4. 保存到 `~/.bb-browser/sites/` 测试\n5. 提交 PR 到社区仓库\n\n<!-- 证据来源：packages/cli/src/index.ts:601-658 guide 命令输出 -->\n\n## Step 1：逆向 API\n\n```bash\n# 清空旧记录\nbb-browser network clear --tab <tabId>\n\n# 刷新页面触发请求\nbb-browser refresh --tab <tabId>\n\n# 查看 API 请求（filter 是位置参数，不是 --filter）\nbb-browser network requests \"api\" --with-body --json --tab <tabId>\n```\n\n重点关注：\n- 请求 URL 和参数格式\n- 认证方式（Cookie / Bearer token / CSRF token）\n- 响应数据结构\n\n## Step 2：测试 fetch\n\n```bash\n# 直接在浏览器中测试 fetch（Tier 1 验证）\nbb-browser eval \"fetch('/api/endpoint',{credentials:'include'}).then(r=>r.json())\" --tab <tabId>\n```\n\n根据结果判断复杂度：\n- **能直接拿到数据** → Tier 1（Cookie 认证，如 Reddit/GitHub/V2EX）\n- **需要额外请求头** → Tier 2（如 Twitter：Bearer + CSRF token）\n- **需要请求签名或注入** → Tier 3（如小红书：Pinia store / Webpack 模块）\n\n## Step 3：编写 Adapter\n\n### 元数据格式（`/* @meta */` 块）\n\n<!-- 证据来源：site.ts:56-118 parseSiteMeta() -->\n\n```javascript\n/* @meta\n{\n  \"name\": \"platform/command\",\n  \"description\": \"功能描述\",\n  \"domain\": \"www.example.com\",\n  \"args\": {\n    \"query\": {\"required\": true, \"description\": \"搜索关键词\"},\n    \"count\": {\"required\": false, \"description\": \"返回数量\"}\n  },\n  \"capabilities\": [\"search\"],\n  \"readOnly\": true,\n  \"example\": \"bb-browser site platform/command value\"\n}\n*/\nasync function(args) {\n  // adapter 实现\n}\n```\n\n### 元数据字段说明\n\n| 字段 | 必需 | 说明 |\n|------|------|------|\n| `name` | 是 | 唯一标识，格式 `platform/command` |\n| `description` | 是 | 功能描述 |\n| `domain` | 是 | 目标网站域名（用于自动 tab 匹配） |\n| `args` | 是 | 参数定义，每个参数含 `required` 和 `description` |\n| `capabilities` | 否 | 能力标签数组 |\n| `readOnly` | 否 | 是否只读操作 |\n| `example` | 否 | 使用示例 |\n\n### 旧格式兼容\n\n也支持 `// @tag value` 注释格式（向后兼容）：\n\n```javascript\n// @name platform/command\n// @description 功能描述\n// @domain www.example.com\n// @args query,filter\n// @example bb-browser site platform/command value\n```\n\n<!-- 证据来源：site.ts:100-117 旧格式解析 -->\n\n## 三层复杂度示例\n\n### Tier 1：Cookie 认证（~1 分钟）\n\n直接 fetch，`credentials: 'include'` 自动带 Cookie。\n\n```javascript\n/* @meta\n{\n  \"name\": \"reddit/search\",\n  \"description\": \"Search Reddit posts\",\n  \"domain\": \"www.reddit.com\",\n  \"args\": {\"query\": {\"required\": true, \"description\": \"Search query\"}},\n  \"readOnly\": true,\n  \"example\": \"bb-browser site reddit/search 'local LLM'\"\n}\n*/\nasync function(args) {\n  if (!args.query) return {error: 'Missing argument: query'};\n  const resp = await fetch(\n    '/search.json?q=' + encodeURIComponent(args.query) + '&limit=10',\n    {credentials: 'include'}\n  );\n  if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};\n  const data = await resp.json();\n  return data.data.children.map(c => ({\n    title: c.data.title,\n    url: 'https://www.reddit.com' + c.data.permalink,\n    score: c.data.score,\n    subreddit: c.data.subreddit\n  }));\n}\n```\n\n### Tier 2：Bearer + CSRF token（~3 分钟）\n\n需要从页面提取 token 构造请求头。\n\n```javascript\n/* @meta\n{\n  \"name\": \"twitter/search\",\n  \"description\": \"Search tweets\",\n  \"domain\": \"twitter.com\",\n  \"args\": {\"query\": {\"required\": true, \"description\": \"Search query\"}},\n  \"readOnly\": true\n}\n*/\nasync function(args) {\n  // 从 cookie 提取 CSRF token\n  const csrf = document.cookie.match(/ct0=([^;]+)/)?.[1];\n  if (!csrf) return {error: 'CSRF token not found', hint: 'Not logged in?'};\n\n  // 构造带认证的请求\n  const resp = await fetch('/i/api/2/search/adaptive.json?q=' + encodeURIComponent(args.query), {\n    credentials: 'include',\n    headers: {\n      'authorization': 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs...',\n      'x-csrf-token': csrf\n    }\n  });\n  if (!resp.ok) return {error: 'HTTP ' + resp.status};\n  return await resp.json();\n}\n```\n\n### Tier 3：Webpack 注入 / Pinia store（~10 分钟）\n\n需要访问页面内部状态或调用内部模块。\n\n## 抗变更模式\n\n网站频繁更新前端代码（CSS class、webpack module ID、GraphQL queryId 等）。以下是经过验证的抗变更模式。\n\n### 模式 1：结构化 DOM 提取（替代 CSS class 选择器）\n\n**问题**：网站经常修改 CSS class 名（如 Google 从 `div.g` 改为 `div.MjjYud`），导致 adapter 失效。\n\n**方案**：用语义化 HTML 元素（h3、a、article）定位内容，不依赖任何 class name。\n\n```javascript\n// ❌ 脆弱：依赖 CSS class（Google 已多次更改）\nconst items = doc.querySelectorAll('div.g');\n\n// ✅ 稳健：用语义元素定位\nconst h3s = doc.querySelectorAll('h3');\nfor (const h3 of h3s) {\n  const a = h3.closest('a');\n  if (!a) continue;\n  const link = a.getAttribute('href');\n  if (!link || !link.startsWith('http')) continue;\n  const title = h3.textContent.trim();\n\n  // 向上查找结果容器（找到有多个 h3 兄弟的层级停止）\n  let container = a;\n  while (container.parentElement && container.parentElement.tagName !== 'BODY') {\n    const sibs = [...container.parentElement.children];\n    if (sibs.filter(s => s.querySelector('h3')).length > 1) break;\n    container = container.parentElement;\n  }\n\n  // 在容器内、链接外查找摘要\n  const linkBlock = a.closest('div') || a;\n  let snippet = '';\n  for (const sp of container.querySelectorAll('span')) {\n    if (linkBlock.contains(sp)) continue;\n    const t = sp.textContent.trim();\n    if (t.length > 30 && t !== title) { snippet = t; break; }\n  }\n  results.push({ title, url: link, snippet });\n}\n```\n\n**适用于**：Google、Bing、DuckDuckGo、HackerNews 等搜索/列表页面。\n\n### 模式 2：Webpack 模块动态发现（替代硬编码 module ID）\n\n**问题**：SPA 网站（如 Twitter/X）的 webpack module ID 在每次部署时都会变化，硬编码 ID 很快失效。\n\n**方案**：通过搜索模块源码中的稳定签名来动态查找模块。\n\n```javascript\n// 第一步：获取 webpack require 函数\nlet __webpack_require__;\nconst chunkId = '__bb_' + Date.now();\nwindow.webpackChunk_twitter_responsive_web.push(\n  [[chunkId], {}, (req) => { __webpack_require__ = req; }]\n);\n\n// 第二步：按源码签名查找模块（不依赖 module ID）\n// 示例：查找 Transaction ID 生成器\nlet genTxId;\nfor (const id of Object.keys(__webpack_require__.m)) {\n  const src = __webpack_require__.m[id].toString();\n  // 用模块源码中稳定的字符串特征来匹配\n  if (src.includes('jf.x.com') && src.includes('jJ:')) {\n    genTxId = __webpack_require__(id).jJ;\n    break;\n  }\n}\nif (!genTxId) return {\n  error: 'Cannot find transaction ID generator',\n  hint: 'Twitter webpack structure may have changed.'\n};\n\n// 第三步：按 operationName 查找 GraphQL queryId\nlet queryId;\nfor (const id of Object.keys(__webpack_require__.m)) {\n  const src = __webpack_require__.m[id].toString();\n  const m = src.match(/queryId:\"([^\"]+)\",operationName:\"CreateTweet\"/);\n  if (m) { queryId = m[1]; break; }\n}\nif (!queryId) return {\n  error: 'Cannot find CreateTweet queryId',\n  hint: 'Twitter GraphQL schema may have changed.'\n};\n```\n\n**选择签名的原则**：\n- 选择**业务语义**字符串而非技术细节（`jf.x.com` 比变量名稳定）\n- 选择**多个特征**组合匹配（`includes('A') && includes('B')`）\n- 选择**export 名**匹配（`jJ:` 是 minified 的 export key，比函数体稳定）\n- 对于 GraphQL：`operationName` 几乎不变，`queryId` 会变 → 用前者查后者\n\n**适用于**：Twitter/X、小红书、抖音等 SPA 应用。\n\n### 模式 3：Vue/React 内部状态访问\n\n```javascript\n// Vue 3 + Pinia（如小红号）\nconst app = document.querySelector('#app').__vue_app__;\nconst store = app.config.globalProperties.$pinia._s.get('user');\n\n// React（如 Reddit new）\nconst fiber = document.querySelector('#App')._reactRootContainer?._internalRoot?.current;\n```\n\n**注意**：内部状态访问比 API 调用更脆弱，优先使用 API 逆向。\n\n## Step 4：测试\n\n```bash\n# 保存到私有目录\n# 文件路径：~/.bb-browser/sites/platform/command.js\n\n# 测试运行\nbb-browser site platform/command \"test query\" --json\n\n# 验证输出格式\nbb-browser site platform/command \"test query\"\n```\n\n## Step 5：贡献社区\n\n```bash\n# 方式 A：使用 gh CLI\ngit clone https://github.com/epiral/bb-sites && cd bb-sites\ngit checkout -b feat-platform\n# 添加 adapter 文件\ngit push -u origin feat-platform\ngh pr create --repo epiral/bb-sites\n\n# 方式 B：使用 bb-browser 自身\nbb-browser site github/fork epiral/bb-sites\ngit clone https://github.com/YOUR_USER/bb-sites && cd bb-sites\ngit checkout -b feat-platform\n# 添加 adapter 文件\ngit push -u origin feat-platform\nbb-browser site github/pr-create epiral/bb-sites --title \"feat(platform): add adapters\" --head \"YOUR_USER:feat-platform\"\n```\n\n## 错误处理规范\n\nadapter 返回错误时，使用统一格式：\n\n```javascript\n// 参数缺失\nreturn {error: 'Missing argument: query'};\n\n// HTTP 错误 + 登录提示\nreturn {error: 'HTTP 401', hint: 'Not logged in?'};\n\n// 自定义错误\nreturn {error: 'Rate limited', hint: 'Try again in 60 seconds'};\n```\n\n系统会自动检测 401/403/unauthorized/login 等关键词，生成登录提示。\n\n<!-- 证据来源：site.ts:406-427 错误检测逻辑 -->\n\n## 报告 Adapter Bug\n\n```bash\n# 通过 gh CLI\ngh issue create --repo epiral/bb-sites --title \"[adapter-name] 描述\"\n\n# 通过 bb-browser\nbb-browser site github/issue-create epiral/bb-sites --title \"[adapter-name] 描述\"\n```\n","isInternal":false,"tokens":2976,"sizeBytes":9957},{"name":"fetch-and-network.md","path":"skills/bb-browser/references/fetch-and-network.md","rawUrl":"https://raw.githubusercontent.com/epiral/bb-browser/HEAD/skills/bb-browser/references/fetch-and-network.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Fetch 与 Network 高级功能\n\n## fetch 命令 — 带登录态的 curl\n\n本质：在浏览器上下文中执行 `fetch()`，自动携带 Cookie 和登录态。\n\n<!-- 证据来源：packages/cli/src/commands/fetch.ts 注释 \"curl，但带浏览器登录态\" -->\n\n### 基本用法\n\n```bash\n# GET 请求（绝对路径）\nbb-browser fetch https://www.reddit.com/api/me.json\n\n# GET 请求（相对路径，使用当前 tab 的 origin）\nbb-browser fetch /api/me.json\n\n# POST 请求\nbb-browser fetch https://api.example.com/data --method POST --body '{\"key\":\"value\"}'\n\n# 自定义请求头\nbb-browser fetch https://api.example.com/data --headers '{\"Authorization\":\"Bearer token\"}'\n\n# 保存到文件\nbb-browser fetch https://api.example.com/data --output response.json\n\n# JSON 格式输出\nbb-browser fetch https://www.reddit.com/api/me.json --json\n```\n\n### 完整选项\n\n| 选项 | 说明 |\n|------|------|\n| `--method <GET\\|POST\\|...>` | HTTP 方法，默认 GET |\n| `--body <json>` | 请求体（仅 POST/PUT 等） |\n| `--headers <json>` | 自定义请求头（必须是合法 JSON） |\n| `--output <file>` | 保存响应到文件 |\n| `--json` | JSON 格式输出 |\n| `--tab <tabId>` | 指定操作的 tab（全局选项，非 fetch 专属） |\n\n<!-- 证据来源：fetch.ts:18-25 FetchOptions 接口，--tab 由 index.ts:215-218 全局解析 -->\n\n### 自动域名路由机制\n\nfetch 命令会自动处理 tab 匹配：\n\n1. **相对路径**（如 `/api/me.json`）：使用当前活动 tab 的 origin\n2. **绝对路径**（如 `https://www.reddit.com/...`）：\n   - 先查找已打开的匹配域名 tab\n   - 没有则自动打开新 tab 并等待 3 秒\n   - 在匹配的 tab 上下文中执行 fetch\n\n<!-- 证据来源：fetch.ts:42-62 ensureTabForOrigin()，fetch.ts:123-139 路由逻辑 -->\n\n### 响应处理\n\n- JSON 响应自动解析为对象\n- 非 JSON 响应返回文本\n- `--output` 时自动格式化写入文件\n\n<!-- 证据来源：fetch.ts:84-106 buildFetchScript() 中的 content-type 判断 -->\n\n### 典型场景\n\n```bash\n# 检查登录状态\nbb-browser fetch https://www.reddit.com/api/me.json\n\n# 调用内部 API\nbb-browser fetch https://internal.company.com/api/dashboard --json\n\n# 提交表单数据\nbb-browser fetch https://api.example.com/submit \\\n  --method POST \\\n  --body '{\"name\":\"test\",\"value\":123}' \\\n  --headers '{\"Content-Type\":\"application/json\"}'\n\n# 下载数据到文件\nbb-browser fetch https://api.example.com/export.csv --output data.csv\n```\n\n---\n\n## network 命令 — 网络监控与拦截\n\n<!-- 证据来源：packages/cli/src/commands/network.ts -->\n\n### 子命令一览\n\n```bash\n# 查看网络请求\nbb-browser network requests [filter] [--with-body] [--json]\n\n# 拦截请求（阻止）\nbb-browser network route <url> --abort\n\n# 拦截请求（mock 响应）\nbb-browser network route <url> --body '{\"mock\":\"data\"}'\n\n# 移除指定拦截规则\nbb-browser network unroute <url>\n\n# 移除所有拦截规则\nbb-browser network unroute\n\n# 清空请求记录\nbb-browser network clear\n```\n\n### network requests 详解\n\n```bash\n# 查看所有请求\nbb-browser network requests\n\n# 按关键词过滤（匹配 URL）\nbb-browser network requests \"api\"\n\n# 包含完整请求/响应体\nbb-browser network requests --with-body\n\n# 组合使用\nbb-browser network requests \"api\" --with-body --json\n```\n\n输出格式：\n```\nGET https://api.example.com/data\n  类型: fetch, 状态: 200 OK\n  请求头: 5, 响应头: 8        # --with-body 时显示\n  请求体: {\"query\":\"test\"}    # --with-body 时显示\n  响应体: {\"result\":[...]}    # --with-body 时显示\n```\n\n<!-- 证据来源：network.ts:46-77 requests 输出逻辑 -->\n\n### network route 详解\n\n拦截匹配 URL 的请求：\n\n```bash\n# 阻止广告/追踪请求\nbb-browser network route \"*analytics*\" --abort\n\n# Mock API 响应（用于测试）\nbb-browser network route \"*/api/user\" --body '{\"name\":\"test\",\"role\":\"admin\"}'\n\n# 添加多条规则\nbb-browser network route \"*tracker*\" --abort\nbb-browser network route \"*/api/config\" --body '{\"feature_flag\":true}'\n```\n\n<!-- 证据来源：network.ts:79-92 route 输出逻辑 -->\n\n### network unroute\n\n```bash\n# 移除指定规则\nbb-browser network unroute \"*analytics*\"\n\n# 移除所有规则\nbb-browser network unroute\n```\n\n<!-- 证据来源：network.ts:94-101 unroute 输出逻辑 -->\n\n### network clear\n\n```bash\n# 清空请求记录（重新开始监控）\nbb-browser network clear\n```\n\n<!-- 证据来源：network.ts:103-106 clear 输出逻辑 -->\n\n### API 逆向工程工作流\n\n这是 fetch + network 最强大的组合用法，用于发现网站内部 API：\n\n```bash\n# 1. 清空旧记录\nbb-browser network clear\n\n# 2. 刷新页面触发请求\nbb-browser refresh\n\n# 3. 查看 API 请求（过滤 + 完整体）\nbb-browser network requests \"api\" --with-body --json\n\n# 4. 找到目标 API 后，用 fetch 测试\nbb-browser fetch /api/discovered-endpoint --json\n\n# 5. 确认可行后，编写 site adapter\n# 参见 adapter-development.md\n```\n\n### 全局 --tab 选项\n\n所有 network 子命令都支持 `--tab <tabId>`，指定监控哪个 tab 的网络活动：\n\n```bash\nbb-browser network requests --tab 123\nbb-browser network route \"*api*\" --abort --tab 456\nbb-browser network clear --tab 123\n```\n","isInternal":false,"tokens":1599,"sizeBytes":5243},{"name":"site-system.md","path":"skills/bb-browser/references/site-system.md","rawUrl":"https://raw.githubusercontent.com/epiral/bb-browser/HEAD/skills/bb-browser/references/site-system.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Site 系统 — 把任何网站变成命令行 API\n\n## 核心概念\n\nSite 系统通过 adapter（适配器）将网站功能 CLI 化。每个 adapter 是一个 JS 文件，在用户真实浏览器中执行，复用登录态，返回结构化 JSON。\n\n<!-- 证据来源：packages/cli/src/commands/site.ts -->\n\n## 命令速查\n\n```bash\n# 列出所有可用 adapter（按平台分组）\nbb-browser site list\n\n# 搜索 adapter（模糊匹配名称、描述、域名）\nbb-browser site search <query>\n\n# 运行 adapter（简写，推荐）\nbb-browser site <name> [args...]\n\n# 运行 adapter（完整写法）\nbb-browser site run <name> [args...]\n\n# 更新社区 adapter 库（从 github.com/epiral/bb-sites 拉取）\nbb-browser site update\n\n# 查看 adapter 开发指南\nbb-browser guide\n```\n\n## 参数传递\n\n支持两种格式混合使用：\n\n```bash\n# 位置参数（按 adapter 定义的参数顺序）\nbb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/...\n\n# 命名参数（--flag value 格式）\nbb-browser site github/pr-create epiral/bb-sites --title \"feat: ...\" --head \"user:branch\"\n\n# 混合使用\nbb-browser site twitter/search \"AI agent\" --count 20\n```\n\n<!-- 证据来源：site.ts:290-314 参数解析逻辑 -->\n\n## Adapter 目录与优先级\n\n```\n~/.bb-browser/\n├── sites/              # 私有 adapter（优先级高，覆盖同名社区 adapter）\n│   └── platform/\n│       └── command.js\n└── bb-sites/           # 社区 adapter（通过 bb-browser site update 拉取）\n    └── platform/\n        └── command.js\n```\n\n私有 adapter 优先于社区同名 adapter。\n\n<!-- 证据来源：site.ts:24-26 目录常量，site.ts:148-157 getAllSites() 合并逻辑 -->\n\n## 自动 Tab 管理\n\n运行 adapter 时，系统自动处理 tab：\n\n1. 如果指定了 `--tab <tabId>`，直接使用该 tab\n2. 否则，根据 adapter 的 `domain` 字段查找已打开的匹配 tab\n3. 如果没有匹配的 tab，自动打开新 tab 并等待 3 秒加载\n\n域名匹配规则：精确匹配或子域名匹配（如 `x.com` 匹配 `api.x.com`）。\n\n<!-- 证据来源：site.ts:162-169 matchTabOrigin()，site.ts:342-368 tab 查找逻辑 -->\n\n## 错误处理与登录提示\n\nadapter 返回 `{error: \"...\", hint: \"...\"}` 时，系统自动检测登录相关错误（匹配 `401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth`），并提示用户先在浏览器中登录。\n\n```bash\n# 错误示例\n[error] site twitter/search: HTTP 401\n  Hint: Please log in to https://twitter.com in your browser first, then retry.\n```\n\n<!-- 证据来源：site.ts:406-427 错误处理逻辑 -->\n\n## 36 平台完整列表\n\n<!-- 证据来源：README.md \"36 platforms, 103 commands\"，具体 adapter 数量以 bb-browser site list 实际输出为准 -->\n\n### 搜索引擎\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Google | `site google/search \"query\"` | 搜索 |\n| Baidu | `site baidu/search \"query\"` | 百度搜索 |\n| Bing | `site bing/search \"query\"` | 必应搜索 |\n| DuckDuckGo | `site duckduckgo/search \"query\"` | DuckDuckGo 搜索 |\n| Sogou WeChat | `site sogou/wechat \"query\"` | 搜狗微信文章搜索 |\n\n### 社交媒体\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Twitter/X | `site twitter/search \"query\"` | 搜索推文 |\n| Twitter/X | `site twitter/user <handle>` | 用户信息 |\n| Reddit | `site reddit/thread <url>` | 帖子详情 |\n| Reddit | `site reddit/search \"query\"` | 搜索 |\n| Weibo | `site weibo/search \"query\"` | 微博搜索 |\n| Weibo | `site weibo/hot` | 微博热搜 |\n| Xiaohongshu | `site xiaohongshu/search \"query\"` | 小红书搜索 |\n| Jike | `site jike/feed` | 即刻动态 |\n| LinkedIn | `site linkedin/search \"query\"` | 搜索 |\n| LinkedIn | `site linkedin/profile <url>` | 个人资料 |\n| Hupu | `site hupu/hot` | 虎扑热帖 |\n\n### 新闻资讯\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| BBC | `site bbc/headlines` | BBC 头条 |\n| Reuters | `site reuters/search \"query\"` | 路透社搜索 |\n| 36kr | `site 36kr/newsflash` | 36氪快讯 |\n| Toutiao | `site toutiao/hot` | 今日头条热榜 |\n| Eastmoney | `site eastmoney/news \"query\"` | 东方财富新闻 |\n\n### 技术开发\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| GitHub | `site github/repo <owner/repo>` | 仓库信息 |\n| GitHub | `site github/issues <owner/repo>` | Issue 列表 |\n| GitHub | `site github/pr-create <repo> --title \"...\"` | 创建 PR |\n| StackOverflow | `site stackoverflow/search \"query\"` | 搜索 |\n| HackerNews | `site hackernews/top` | 热门帖子 |\n| CSDN | `site csdn/search \"query\"` | CSDN 搜索 |\n| cnblogs | `site cnblogs/search \"query\"` | 博客园搜索 |\n| V2EX | `site v2ex/hot` | V2EX 热帖 |\n| Dev.to | `site devto/search \"query\"` | Dev.to 搜索 |\n| npm | `site npm/package <name>` | npm 包信息 |\n| PyPI | `site pypi/package <name>` | PyPI 包信息 |\n| arXiv | `site arxiv/search \"query\"` | 论文搜索 |\n\n### 视频平台\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| YouTube | `site youtube/search \"query\"` | 搜索视频 |\n| YouTube | `site youtube/transcript <video_id>` | 获取字幕 |\n| Bilibili | `site bilibili/search \"query\"` | B站搜索 |\n| Bilibili | `site bilibili/popular` | B站热门 |\n\n### 影音娱乐\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Douban | `site douban/movie <name>` | 豆瓣电影 |\n| Douban | `site douban/top250` | 豆瓣 Top250 |\n| IMDb | `site imdb/search \"query\"` | IMDb 搜索 |\n| Genius | `site genius/search \"query\"` | 歌词搜索 |\n| Qidian | `site qidian/search \"query\"` | 起点小说搜索 |\n\n### 财经股票\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Eastmoney | `site eastmoney/stock \"茅台\"` | 股票查询 |\n| Yahoo Finance | `site yahoo-finance/stock <ticker>` | 股票行情 |\n\n### 求职招聘\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| BOSS Zhipin | `site boss/search \"query\"` | BOSS 直聘搜索 |\n| BOSS Zhipin | `site boss/detail <url>` | 职位详情 |\n\n### 知识百科\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Wikipedia | `site wikipedia/search \"query\"` | 维基百科搜索 |\n| Wikipedia | `site wikipedia/summary \"topic\"` | 摘要 |\n| Zhihu | `site zhihu/hot` | 知乎热榜 |\n| Zhihu | `site zhihu/question <id>` | 问题详情 |\n| Open Library | `site openlibrary/search \"query\"` | 图书搜索 |\n\n### 消费购物\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| SMZDM | `site smzdm/search \"query\"` | 什么值得买搜索 |\n\n### 实用工具\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Youdao | `site youdao/translate \"text\"` | 有道翻译 |\n| GSMArena | `site gsmarena/search \"phone\"` | 手机参数 |\n| Product Hunt | `site producthunt/trending` | 热门产品 |\n| Ctrip | `site ctrip/search \"destination\"` | 携程搜索 |\n\n## 常用场景示例\n\n### 信息检索\n\n```bash\n# 搜索技术话题\nbb-browser site twitter/search \"Claude Code\"\nbb-browser site reddit/search \"local LLM\"\nbb-browser site hackernews/top\n\n# 查看热榜\nbb-browser site zhihu/hot\nbb-browser site weibo/hot\nbb-browser site v2ex/hot\n\n# 查询股票\nbb-browser site eastmoney/stock \"茅台\"\n```\n\n### 开发辅助\n\n```bash\n# GitHub 操作\nbb-browser site github/repo anthropics/claude-code\nbb-browser site github/issues owner/repo\n\n# 查包信息\nbb-browser site npm/package bb-browser\nbb-browser site pypi/package requests\n\n# 搜索技术问题\nbb-browser site stackoverflow/search \"async await error handling\"\n```\n\n### 内容获取\n\n```bash\n# 获取 YouTube 字幕\nbb-browser site youtube/transcript dQw4w9WgXcQ\n\n# 获取 Reddit 帖子完整内容\nbb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/...\n\n# 论文搜索\nbb-browser site arxiv/search \"transformer attention mechanism\"\n```\n\n## 与 --json 配合\n\n所有 site 命令都支持 `--json` 输出结构化数据：\n\n```bash\nbb-browser site zhihu/hot --json\nbb-browser site twitter/search \"AI\" --json\n```\n\n## 更多信息\n\n- 创建自定义 adapter：参见 [adapter-development.md](adapter-development.md)\n- 社区 adapter 仓库：https://github.com/epiral/bb-sites\n","isInternal":false,"tokens":2682,"sizeBytes":8223},{"name":"snapshot-refs.md","path":"skills/bb-browser/references/snapshot-refs.md","rawUrl":"https://raw.githubusercontent.com/epiral/bb-browser/HEAD/skills/bb-browser/references/snapshot-refs.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Snapshot + Refs 工作流\n\nbb-browser 使用紧凑的元素引用（ref），大幅减少 AI Agent 的上下文消耗。\n\n## 工作原理\n\n### 传统方式的问题\n\n```\n完整 DOM/HTML 发送给 AI → AI 解析 → 生成 CSS 选择器 → 执行操作\n每次交互 ~3000-5000 tokens\n```\n\n### bb-browser 的方案\n\n```\n紧凑快照 → 分配 @ref → 直接用 ref 操作\n每次交互 ~200-400 tokens\n```\n\n## Snapshot 命令\n\n```bash\n# 完整快照（显示页面结构）\nbb-browser snapshot\n\n# 只显示可交互元素（推荐）\nbb-browser snapshot -i\n\n# JSON 格式输出\nbb-browser snapshot -i --json\n```\n\n## 快照输出格式\n\n```\n页面: Example Site - 首页\nURL: https://example.com\n\n@1 [header]\n  @2 [nav]\n    @3 [a] \"首页\"\n    @4 [a] \"产品\"\n    @5 [a] \"关于\"\n  @6 [button] \"登录\"\n\n@7 [main]\n  @8 [h1] \"欢迎\"\n  @9 [form]\n    @10 [input type=\"email\"] placeholder=\"邮箱\"\n    @11 [input type=\"password\"] placeholder=\"密码\"\n    @12 [button type=\"submit\"] \"登录\"\n\n@13 [footer]\n  @14 [a] \"隐私政策\"\n```\n\n## 使用 Ref\n\n有了 ref 后直接操作：\n\n```bash\n# 点击登录按钮\nbb-browser click @6\n\n# 填写邮箱\nbb-browser fill @10 \"user@example.com\"\n\n# 填写密码\nbb-browser fill @11 \"password123\"\n\n# 提交表单\nbb-browser click @12\n```\n\n## Ref 生命周期\n\n**重要**：页面变化后 ref 会失效！\n\n```bash\n# 获取初始快照\nbb-browser snapshot -i\n# @1 [button] \"下一页\"\n\n# 点击触发页面变化\nbb-browser click @1\n\n# 必须重新获取快照！\nbb-browser snapshot -i\n# @1 [h1] \"第二页\"  ← 现在是不同的元素\n```\n\n### Ref 失效的场景\n\n1. **页面导航**：点击链接、表单提交后跳转\n2. **动态加载**：AJAX 加载新内容\n3. **DOM 变化**：JavaScript 修改页面结构\n4. **弹窗/下拉**：打开模态框、展开菜单\n\n## 最佳实践\n\n### 1. 操作前先 Snapshot\n\n```bash\n# 正确\nbb-browser open https://example.com\nbb-browser snapshot -i          # 先获取 ref\nbb-browser click @1             # 再使用\n\n# 错误\nbb-browser open https://example.com\nbb-browser click @1             # ref 不存在！\n```\n\n### 2. 导航后重新 Snapshot\n\n```bash\nbb-browser click @5             # 导航到新页面\nbb-browser snapshot -i          # 获取新页面的 ref\nbb-browser click @1             # 使用新的 ref\n```\n\n### 3. 动态变化后重新 Snapshot\n\n```bash\nbb-browser click @1             # 打开下拉菜单\nbb-browser snapshot -i          # 获取菜单项的 ref\nbb-browser click @7             # 选择菜单项\n```\n\n### 4. 等待加载完成\n\n```bash\nbb-browser click @3             # 触发 AJAX 加载\nbb-browser wait 1000            # 等待加载\nbb-browser snapshot -i          # 获取新内容的 ref\n```\n\n## Ref 格式说明\n\n```\n@1 [tag type=\"value\"] \"文本内容\" placeholder=\"提示\"\n│   │   │             │          │\n│   │   │             │          └─ 其他属性\n│   │   │             └─ 可见文本\n│   │   └─ 关键属性\n│   └─ HTML 标签\n└─ 唯一 ref ID\n```\n\n### 常见模式\n\n```\n@1 [button] \"提交\"                    # 按钮\n@2 [input type=\"email\"]               # 邮箱输入框\n@3 [input type=\"password\"]            # 密码输入框\n@4 [a href=\"/page\"] \"链接文字\"         # 链接\n@5 [select]                           # 下拉框\n@6 [textarea] placeholder=\"请输入\"     # 文本域\n@7 [checkbox] checked                 # 已勾选的复选框\n@8 [radio] selected                   # 已选中的单选框\n```\n\n## 常见问题\n\n### \"Ref not found\" 错误\n\n页面已变化，ref 失效了：\n\n```bash\n# 重新获取快照\nbb-browser snapshot -i\n```\n\n### 元素不在快照中\n\n可能需要滚动或等待：\n\n```bash\n# 滚动到底部\nbb-browser scroll down\nbb-browser snapshot -i\n\n# 或等待动态内容\nbb-browser wait 2000\nbb-browser snapshot -i\n```\n\n### 快照太长\n\n使用 `-i` 只显示可交互元素：\n\n```bash\n# 完整快照可能很长\nbb-browser snapshot\n\n# 只显示可交互元素（推荐）\nbb-browser snapshot -i\n```\n\n### 需要操作不可交互元素\n\n使用 `eval` 直接执行 JavaScript：\n\n```bash\nbb-browser eval \"document.querySelector('.hidden-element').click()\"\n```\n\n## 调试技巧\n\n```bash\n# 查看完整页面结构\nbb-browser snapshot\n\n# 只看可交互元素\nbb-browser snapshot -i\n\n# JSON 格式便于解析\nbb-browser snapshot -i --json\n\n# 获取特定元素的文本\nbb-browser get text @5\n```\n","isInternal":false,"tokens":1447,"sizeBytes":4431},{"name":"SKILL.md","path":"skills/bb-browser/SKILL.md","rawUrl":"https://raw.githubusercontent.com/epiral/bb-browser/HEAD/skills/bb-browser/SKILL.md","title":"bb-browser","category":"anthropic-skill","format":"markdown","content":"---\nname: bb-browser\ndescription: 强大的信息获取与浏览器自动化工具。通过浏览器 + 用户登录态，获取公域和私域信息。可访问任意网页、内部系统、登录后页面，执行表单填写、信息提取、页面操作。支持 site 系统（36 平台 103 命令一键调用）、带登录态的 fetch、网络请求拦截与 mock、操作录制等高级功能。\nallowed-tools: Bash(bb-browser:*)\n---\n\n# bb-browser - 信息获取与浏览器自动化\n\n## 核心价值\n\n通过浏览器 + 用户登录态，可以获取：\n- 公域信息：任意公开网页、搜索结果、新闻资讯\n- 私域信息：内部系统、企业应用、登录后页面、个人账户数据\n\n还可以代替用户执行浏览器操作：表单填写、按钮点击、数据提取、截图保存、批量操作。\n\n运行在用户真实浏览器中，复用已登录的账号，不触发反爬检测。\n\n## 快速开始\n\n```bash\nbb-browser open <url>        # 打开页面（新 tab）\nbb-browser snapshot -i       # 获取可交互元素\nbb-browser click @5          # 点击元素\nbb-browser fill @3 \"text\"    # 填写输入框\nbb-browser close             # 完成后关闭 tab\n```\n\n## Site 系统 — 把任何网站变成命令行 API\n\nsite 系统是 bb-browser 的核心特性，通过 adapter 将网站功能 CLI 化，覆盖 36+ 平台。\n\n```bash\n# 常用命令\nbb-browser site list                          # 列出所有 adapter\nbb-browser site search <query>                # 搜索 adapter\nbb-browser site <name> [args...]              # 运行 adapter\nbb-browser site update                        # 更新社区 adapter 库\n\n# 使用示例\nbb-browser site twitter/search \"Claude Code\"  # 搜索推文\nbb-browser site zhihu/hot                     # 知乎热榜\nbb-browser site github/repo owner/repo        # 仓库信息\nbb-browser site youtube/transcript <video_id> # 获取字幕\nbb-browser site reddit/thread <url>           # 帖子详情\nbb-browser site eastmoney/stock \"茅台\"         # 股票查询\nbb-browser site weibo/hot                     # 微博热搜\nbb-browser site arxiv/search \"transformer\"    # 论文搜索\n```\n\nadapter 自动处理 tab 管理（查找匹配域名的 tab 或新建），自动检测登录错误并提示。\n\n详细用法和 36 平台完整列表：参见 [references/site-system.md](references/site-system.md)\n创建自定义 adapter：参见 [references/adapter-development.md](references/adapter-development.md)\n\n## fetch — 带登录态的 curl\n\n在浏览器上下文中执行 fetch，自动携带 Cookie 和登录态。\n\n```bash\nbb-browser fetch <url>                                    # GET 请求\nbb-browser fetch <url> --method POST --body '{\"k\":\"v\"}'   # POST 请求\nbb-browser fetch <url> --headers '{\"Auth\":\"Bearer xxx\"}'  # 自定义请求头\nbb-browser fetch <url> --output data.json                 # 保存到文件\nbb-browser fetch /api/me.json                             # 相对路径（用当前 tab 的 origin）\n```\n\n自动域名路由：绝对路径自动查找匹配 tab 或新建；相对路径使用当前 tab。\n\n详细用法：参见 [references/fetch-and-network.md](references/fetch-and-network.md)\n\n## Tab 管理规范\n\n操作完成后必须关闭自己打开的 tab。\n\n```bash\n# 单 tab 场景\nbb-browser open https://example.com    # 打开新 tab\nbb-browser snapshot -i\nbb-browser click @5\nbb-browser close                        # 完成后关闭\n\n# 多 tab 场景\nbb-browser open https://site-a.com     # tabId: 123\nbb-browser open https://site-b.com     # tabId: 456\n# ... 操作 ...\nbb-browser tab close                    # 关闭当前 tab\nbb-browser tab close                    # 关闭剩余 tab\n\n# 指定 tab 操作\nbb-browser open https://example.com --tab current  # 在当前 tab 打开（不新建）\nbb-browser open https://example.com --tab 123      # 在指定 tabId 打开\n```\n\n## 核心工作流\n\n1. `open` 打开页面\n2. `snapshot -i` 查看可操作元素（返回 @ref）\n3. 用 `@ref` 执行操作（click, fill, etc.）\n4. 页面变化后重新 `snapshot -i`\n5. 任务完成后 `close` 关闭 tab\n\n## 命令速查\n\n### 导航\n\n```bash\nbb-browser open <url>                # 打开 URL（新 tab）\nbb-browser open <url> --tab current  # 在当前 tab 打开\nbb-browser back                      # 后退\nbb-browser forward                   # 前进\nbb-browser refresh                   # 刷新\nbb-browser close                     # 关闭当前 tab\n```\n\n### 快照\n\n```bash\nbb-browser snapshot             # 完整页面结构\nbb-browser snapshot -i          # 只显示可交互元素（推荐）\nbb-browser snapshot -c          # 移除空结构节点\nbb-browser snapshot -d 3        # 限制树深度为 3 层\nbb-browser snapshot -s \".main\"  # 限定 CSS 选择器范围\nbb-browser snapshot --json      # JSON 格式输出\n# 选项可组合：bb-browser snapshot -i -c -d 5\n```\n\n### 元素交互\n\n```bash\nbb-browser click @5             # 点击\nbb-browser hover @5             # 悬停\nbb-browser fill @3 \"text\"       # 清空并填写\nbb-browser type @3 \"text\"       # 追加输入（不清空）\nbb-browser check @7             # 勾选复选框\nbb-browser uncheck @7           # 取消勾选\nbb-browser select @4 \"option\"   # 下拉选择\nbb-browser press Enter          # 按键\nbb-browser press Control+a      # 组合键\nbb-browser scroll down          # 向下滚动（默认 300px）\nbb-browser scroll up 500        # 向上滚动 500px\n```\n\n### 获取信息\n\n```bash\nbb-browser get text @5          # 获取元素文本\nbb-browser get url              # 获取当前 URL\nbb-browser get title            # 获取页面标题\n```\n\n### Tab 管理\n\n```bash\nbb-browser tab                  # 列出所有 tab\nbb-browser tab new [url]        # 新建 tab\nbb-browser tab 2                # 切换到第 2 个 tab（按 index）\nbb-browser tab select --id 123  # 切换到指定 tabId 的 tab\nbb-browser tab close            # 关闭当前 tab\nbb-browser tab close 3          # 关闭第 3 个 tab（按 index）\nbb-browser tab close --id 123   # 关闭指定 tabId 的 tab\n```\n\n### 截图\n\n```bash\nbb-browser screenshot           # 截图（自动保存）\nbb-browser screenshot path.png  # 截图到指定路径\n```\n\n### 等待\n\n```bash\nbb-browser wait 2000            # 等待 2 秒\nbb-browser wait @5              # 等待元素出现\n```\n\n### JavaScript\n\n```bash\nbb-browser eval \"document.title\"              # 执行 JS\nbb-browser eval \"window.scrollTo(0, 1000)\"    # 滚动到指定位置\n```\n\n### Frame 切换\n\n```bash\nbb-browser frame \"#iframe-id\"   # 切换到 iframe\nbb-browser frame main           # 返回主 frame\n```\n\n### 对话框处理\n\n```bash\nbb-browser dialog accept        # 确认对话框\nbb-browser dialog dismiss       # 取消对话框\nbb-browser dialog accept \"text\" # 确认并输入（prompt）\n```\n\n### 网络与调试\n\n```bash\nbb-browser network requests                        # 查看网络请求\nbb-browser network requests \"api\" --with-body       # 过滤 + 完整请求/响应体\nbb-browser network route \"*analytics*\" --abort      # 拦截并阻止请求\nbb-browser network route \"*/api/user\" --body '{}'   # 拦截并 mock 响应\nbb-browser network unroute                          # 移除所有拦截规则\nbb-browser network clear                            # 清空请求记录\nbb-browser console                                  # 查看控制台消息\nbb-browser console --clear                          # 清空控制台\nbb-browser errors                                   # 查看 JS 错误\nbb-browser errors --clear                           # 清空错误记录\nbb-browser trace start                              # 开始录制用户操作\nbb-browser trace stop                               # 停止录制，输出事件列表\nbb-browser trace status                             # 查看录制状态\n```\n\n详细的 network 高级用法：参见 [references/fetch-and-network.md](references/fetch-and-network.md)\n\n## 全局选项\n\n```bash\n--json               # 以 JSON 格式输出（所有命令通用）\n--tab <tabId>        # 指定操作的标签页 ID（几乎所有命令通用）\n--mcp                # 启动 MCP server（用于 Claude Code / Cursor 等 AI 工具）\n```\n\n## Ref 使用说明\n\nsnapshot 返回的 `@ref` 是元素的临时标识：\n\n```\n@1 [button] \"提交\"\n@2 [input type=\"text\"] placeholder=\"请输入姓名\"\n@3 [a] \"查看详情\"\n```\n\n注意：\n- 页面导航后 ref 失效，需重新 snapshot\n- 动态内容加载后需重新 snapshot\n- ref 格式：`@1`, `@2`, `@3`...\n\n详细说明：参见 [references/snapshot-refs.md](references/snapshot-refs.md)\n\n## 并发操作\n\n```bash\n# 并发打开多个页面（各自独立 tab）\nbb-browser open https://site-a.com &\nbb-browser open https://site-b.com &\nbb-browser open https://site-c.com &\nwait\n# 每个返回独立的 tabId，互不干扰\n```\n\n## 信息提取 vs 页面操作\n\n根据目的选择不同的方法：\n\n### 提取页面内容（用 eval）\n\n当需要提取文章、正文等长文本时，用 `eval` 直接获取：\n\n```bash\n# 微信公众号文章\nbb-browser eval \"document.querySelector('#js_content').innerText\"\n\n# 知乎回答\nbb-browser eval \"document.querySelector('.RichContent-inner').innerText\"\n\n# 通用：获取页面主体文本\nbb-browser eval \"document.body.innerText.substring(0, 5000)\"\n\n# 获取所有链接\nbb-browser eval \"[...document.querySelectorAll('a')].map(a => a.href).join('\\n')\"\n```\n\n有些网站 DOM 嵌套很深，snapshot 输出冗长，`eval` 直接提取文本更高效。\n\n### 操作页面元素（用 snapshot -i）\n\n当需要点击、填写、选择时，用 `snapshot -i` 获取可交互元素：\n\n```bash\nbb-browser snapshot -i\n# @1 [button] \"登录\"\n# @2 [input] placeholder=\"用户名\"\n# @3 [input type=\"password\"]\n\nbb-browser fill @2 \"username\"\nbb-browser fill @3 \"password\"\nbb-browser click @1\n```\n\n`-i` 只显示可交互元素，过滤掉大量无关内容。\n\n## MCP 集成\n\nbb-browser 提供 MCP server，可与 Claude Code / Cursor 等 AI 工具集成：\n\n```bash\n# 启动 MCP server\nbb-browser --mcp\n```\n\n配置示例（Claude Code / Cursor）：\n```json\n{\n  \"mcpServers\": {\n    \"bb-browser\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"bb-browser\", \"--mcp\"]\n    }\n  }\n}\n```\n\n## 常见任务示例\n\n### 表单填写\n\n```bash\nbb-browser open https://example.com/form\nbb-browser snapshot -i\nbb-browser fill @1 \"张三\"\nbb-browser fill @2 \"zhangsan@example.com\"\nbb-browser click @3\nbb-browser wait 2000\nbb-browser close\n```\n\n### 信息提取\n\n```bash\nbb-browser open https://example.com/dashboard\nbb-browser snapshot -i\nbb-browser get text @5\nbb-browser screenshot report.png\nbb-browser close\n```\n\n### 批量操作\n\n```bash\nfor url in \"url1\" \"url2\" \"url3\"; do\n  bb-browser open \"$url\"\n  bb-browser snapshot -i --json\n  bb-browser close\ndone\n```\n\n## 深入文档\n\n| 文档 | 说明 |\n|------|------|\n| [references/site-system.md](references/site-system.md) | Site 系统完整指南：35 平台列表、命令用法、自动 tab 管理 |\n| [references/adapter-development.md](references/adapter-development.md) | Adapter 开发指南：API 逆向、三层复杂度、元数据格式 |\n| [references/fetch-and-network.md](references/fetch-and-network.md) | Fetch 与 Network 高级功能：带登录态请求、请求拦截与 mock |\n| [references/snapshot-refs.md](references/snapshot-refs.md) | Ref 生命周期、最佳实践、常见问题 |\n","frontmatter":{"name":"bb-browser","description":"强大的信息获取与浏览器自动化工具。通过浏览器 + 用户登录态，获取公域和私域信息。可访问任意网页、内部系统、登录后页面，执行表单填写、信息提取、页面操作。支持 site 系统（36 平台 103 命令一键调用）、带登录态的 fetch、网络请求拦截与 mock、操作录制等高级功能。","allowed-tools":"Bash(bb-browser:*)","allowedTools":["Bash(bb-browser:*)"]},"allowedTools":["Bash(bb-browser:*)"],"isInternal":false,"tokens":3294,"sizeBytes":11475}],"systemPromptSnippet":"<yakaai_skills repo=\"epiral/bb-browser\">\n<!-- File: AGENTS.md (Tokens: ~2844 | Category: root-instruction) -->\n# bb-browser Agent 开发规范\n\n## 架构\n\n```\nCLI ──HTTP──▶ Daemon ──controlConn CDP──▶ Chrome\n                │\n                │ --hub 模式 (可选)\n                ├── Hub ProviderStream ──▶ Pinix Hub\n                │\n                │ Protocol 2\n                └── Streamer (bb-viewer) ──captureConn + inputConn CDP──▶ Chrome\n                         │\n                         └── WebRTC video + DataChannel ──▶ Clip Web UI\n```\n\n**三根 CDP 连接到同一个 Chrome：**\n\n| 连接 | Owner | 职责 |\n|------|-------|------|\n| controlConn | daemon | Agent 操作：tab/nav/site/DOM/click-by-ref |\n| inputConn | streamer | Human 实时输入：鼠标/键盘/IME (DataChannel → CDP) |\n| captureConn | streamer | 视频流：screencast → VP8 → WebRTC |\n\n**组件：**\n\n| 组件 | 位置 | 职责 |\n|------|------|------|\n| CLI | `packages/cli/` | 命令行入口，HTTP 调 daemon |\n| Daemon | `packages/daemon/` | 控制中心：CDP controlConn、HTTP API、Hub 连接、site 执行、streamer 管理 |\n| Shared | `packages/shared/` | 统一命令定义 (`commands.ts`)、协议类型 (`protocol.ts`) |\n| Streamer | `bb-viewer` repo | 纯视频 + 实时输入（Go binary，daemon 子进程） |\n\n## Daemon 两种模式\n\n```bash\n# 本地模式：Agent 用 CLI 操作浏览器\nbb-browser daemon start\n\n# Hub 模式：注册到 Pinix Hub，远程可用\nbb-browser daemon start --hub https://hub.pinixai.com --hub-token xxx\n```\n\n## 协议\n\n### Protocol 1: CLI ↔ daemon (`POST /command`)\n\n请求：`{\"method\": \"snap\", \"params\": {\"tab\": \"3ef9\"}}`\n成功：`{\"result\": {\"tab\": \"3ef9\", \"title\": \"Google\", \"snapshot\": \"...\"}}`\n失败：`{\"error\": {\"message\": \"Missing --tab\", \"hint\": \"Run 'bb-browser tab list'\"}}`\n\n### Protocol 2: daemon ↔ streamer (`POST /command`)\n\n| Method | Params | 说明 |\n|--------|--------|------|\n| connect | {cdpUrl, ice?} | 连接 CDP，创建 WebRTC peer |\n| answer | {answer_sdp, candidates} | 完成 WebRTC 信令 |\n| switch | {cdpUrl} | 切到新 tab（peer 不变） |\n| stop | {} | 停止 |\n\n## 统一命令定义\n\n`packages/shared/src/commands.ts` 是所有命令的单一定义源。每个命令包含 `method`、`group`、`description`、`requiresTab`、`params`。CLI 解析、daemon dispatch、Hub 注册都从这里读取。\n\n添加新命令：\n1. `commands.ts` — 添加 CommandDef\n2. `protocol.ts` — 添加 ActionType\n3. `command-dispatch.ts` — 添加处理分支\n4. `packages/cli/src/commands/<name>.ts` + `index.ts` — CLI 命令\n\n## CLI 命令\n\n`--tab` 必填（除 `open`、`site` 组、`tab list/new`、`daemon`）。\n\n| 组 | 命令 |\n|----|------|\n| 导航 | `open <url> [--tab]`, `back --tab`, `forward --tab`, `reload --tab`, `close --tab` |\n| 观察 | `snap --tab`, `screenshot --tab`, `get <attr> --tab`, `eval <js> --tab` |\n| 交互 | `click/hover/fill/type/press/scroll/check/uncheck/select --tab` |\n| Tab | `tab list`, `tab new [url]` |\n| Site | `site list`, `site info <name>`, `site run <name>` |\n| 调试 | `network/console/errors/trace/cookies/source --tab` |\n| 进程 | `daemon start [--hub]`, `daemon stop`, `daemon status` |\n\n### Debug 命令参数约定\n\nDebug 命令（`network`, `console`, `errors`, `trace`, `source`）有子命令的统一用 `--action` 参数：\n\n```bash\n# CLI 用法（位置参数）\nbb-browser source grep \"api.example\" --tab <id>\nbb-browser trace start --tab <id>\n\n# Hub invoke 用法（命名参数）\npinix invoke browser source --action grep --pattern \"api.example\" --tab <id>\npinix invoke browser trace --action start --tab <id>\npinix invoke browser network --action requests --excludeStatic true --tab <id>\n```\n\n旧参数名（`sourceCommand`、`traceCommand` 等）仍可用但已 deprecated。\n\n## 设计不变量\n\n1. **Daemon 是唯一操作 API**。CLI 和 Hub invoke 都通过 daemon。\n2. **Streamer 不做业务逻辑**。不管 tab、不做导航。只做帧编码和输入转发。\n3. **Tab ID 统一用 daemon 分配的短 ID**（如 `3ef9`）。Streamer 不知道 tab ID，只接收 CDP WebSocket URL。\n4. **Site 执行在 daemon 内**。不 shell-out CLI。\n5. **所有操作响应包含 `tab`**（短 ID）。观察类响应包含 `cursor`。\n6. **Per-tab 事件隔离**。tab 关闭时释放短 ID 和事件缓冲。\n7. **`seq` 全局单调递增**，不可回退。\n8. **Daemon 启动时清理旧进程**。`cleanupStaleDaemon()` 确保不残留。\n\n## Hub Clip 注册\n\nHub 模式下 daemon 注册：\n\n| Clip | 命令 |\n|------|------|\n| browser | 所有标准命令 + `stream.start/answer/close/switch` |\n| \\<platform\\> | 每个 site adapter 一个命令（如 `google/search`） |\n\nClip Web UI 通过 Hub invoke 调用标准命令（tab_list、open、reload 等）和 stream 命令。\n\n## Stream 生命周期 (view.html)\n\nClip Web UI (`web/view.html`) 通过心跳 + visibility 检测管理 stream 生命周期：\n\n```\nACTIVE (visible) → Ping 每 5s via DataChannel (opcode 0x08)\nIDLE   (hidden)  → Ping 每 10s, 30s 后自动 disconnect\nCLOSED           → overlay 显示断开原因, 可点 Connect 重连\n```\n\n**前端 30s idle disconnect 是第一道防线，后端 45s watchdog 是安全网。**\n\n`cleanup(reason)` 根据原因显示不同 overlay：\n- `user` → \"Disconnected\"\n- `idle` → \"Stream closed — tab was inactive\"\n- `network` → \"Connection lost\"\n\n关键实现约束：\n- 不用 `setTimeout`（hidden tab 被浏览器节流），用 `setInterval` + 时间戳\n- 不用 `BigInt`（兼容性），用 `Math.floor` + `>>>`\n- `disconnect` 里 `invoke(\"stream.close\")` 是 fire-and-forget，不 await\n\n## Docker 运行模式\n\nDocker 中 Chrome 必须以**有头模式**运行在 Xvfb 虚拟帧缓冲上。\n\n```\nXvfb (:99) ← Chrome (headed, DISPLAY=:99) ← CDP screencast → bb-viewer → WebRTC\n```\n\n**为什么不能用 `--headless=new`：** Linux 上 `--headless=new` 跳过整个 X11/Ozone 显示层，导致 WebGL、`navigator.plugins`、屏幕信息等 API 返回异常值。Google 等反自动化系统检测这些底层渲染差异来识别 headless 浏览器。macOS 上 headless 没问题，因为 Chrome 仍链接完整的 Cocoa/CoreGraphics 框架，只是不显示窗口。\n\nChrome 始终以有头模式启动，不使用 `--headless=new`。macOS 上没有聚焦的用户 session 时 Chrome 自动离屏渲染（无可见窗口），效果等同 headless 但保留完整 GUI API。\n\n**Docker 关键环境变量：**\n- `DISPLAY=:99` — Xvfb 虚拟显示（Dockerfile 已设置）\n\n**Stealth 注入 = 有害：** Google 的反自动化不检测 CDP 本身，而是检测 `Emulation.setUserAgentOverride`、`Page.addScriptToEvaluateOnNewDocument` 等 CDP domain 调用。不注入任何 stealth，用裸 CDP 即可。\n\n### Docker 构建\n\n```bash\n# 构建（单阶段，下载预编译 bb-viewer + Chrome，阿里云 apt/npm 镜像）\ndocker build --platform linux/amd64 -t bb-browser:amd64 .\n\n# 运行（TURN 凭证自动从 Hub API 获取，不需要配置）\ndocker run -d --platform linux/amd64 --name bb-browser \\\n  -v bb-browser-data:/data -p 19825:19824 --shm-size=2g \\\n  -e CHROME_WINDOW_SIZE=1280,720 \\\n  bb-browser:amd64 --host 0.0.0.0 \\\n  --hub https://hub.pinixai.com --hub-token <token>\n```\n\n镜像内预装了 Chrome for Testing + bb-viewer（静态链接），运行时无需下载。\n`--shm-size=2g` 防止 Chrome 因共享内存不足 crash。\nTURN 环境变量（`TURN_URL`/`TURN_SECRET`）仍可用于覆盖默认值。\n\n**推荐：使用 Pinix 全家桶镜像 `lueco/pinix`，包含 pinixd + bb-browser + Chrome，一个 token 启动一切。**\n\n### bb-viewer (streamer)\n\n- 预编译 binary 在 COS 上，daemon 首次 stream 时自动下载（`~/.bb-browser/bin/bb-viewer`）\n- 静态链接 libvpx + libturbojpeg，不依赖用户系统库版本\n- JPEG → I420 解码使用 `tjDecompress2` (RGB) 再手动转 I420，而非 `tjDecompressToYUVPlanes`（后者在 libturbojpeg 2.1.x 对奇数高度帧有 bug）\n- Decoder 自动 round down 到偶数尺寸\n\n### TURN relay\n\nTURN 凭证通过 Hub API `GET /turn/credentials` 动态获取（24h TTL），secret 只在服务端。\ndaemon 启动 streamer 时自动请求，不需要用户配置。\n环境变量 `TURN_URL` / `TURN_SECRET` 可覆盖（standalone 模式）。\n\n## 代码规范\n\n- Commit：`<type>(<scope>): <summary>`，英文\n- 类型：`fix` / `feat` / `refactor` / `chore` / `docs`\n- 构建：`pnpm build`\n- 测试：`pnpm test`\n- lint：`pnpm lint`\n\n## 参考\n\n- [bb-viewer issue #5](https://github.com/epiral/bb-viewer/issues/5) — Stream 生命周期设计\n- [issue #224](https://github.com/epiral/bb-browser/issues/224) — daemon 生命周期（已修复）\n\n<!-- File: skills/bb-browser-openclaw/SKILL.md (Tokens: ~1160 | Category: anthropic-skill) -->\n---\nname: bb-browser-openclaw\ndescription: Turn any website into a CLI command. 36 platforms, 103 commands — Twitter, Reddit, GitHub, YouTube, Zhihu, Bilibili, Weibo, and more. Uses OpenClaw's browser directly, no extra extension needed.\nrequires:\n  bins: bb-browser\nallowed-tools: Bash(bb-browser:*)\n---\n\n# bb-browser sites — The web as CLI\n\n36 platforms, 103 commands. One-liner structured data from any website using your login state.\n\n**All commands use `--openclaw` to run through OpenClaw's browser. No Chrome extension or daemon needed.**\n\n## Quick Start\n\n```bash\n# First time: pull community adapters\nbb-browser site update\n\n# See what's available\nbb-browser site list\n\n# See which adapters match your browsing habits\nbb-browser site recommend\n\n# Run any adapter via OpenClaw's browser\nbb-browser site reddit/hot --openclaw\nbb-browser site hackernews/top 5 --openclaw\nbb-browser site v2ex/hot --openclaw\n```\n\n## IMPORTANT: Always use --openclaw\n\nEvery `bb-browser site` command MUST include `--openclaw` to use OpenClaw's browser:\n\n```bash\n# Correct\nbb-browser site twitter/search \"AI agent\" --openclaw\nbb-browser site zhihu/hot 10 --openclaw --json\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[] | {name, changePercent}'\n\n# Wrong (requires separate Chrome extension)\nbb-browser site twitter/search \"AI agent\"\n```\n\n## Data Extraction (most common use)\n\n```bash\n# Social media\nbb-browser site twitter/search \"OpenClaw\" --openclaw\nbb-browser site twitter/thread <tweet-url> --openclaw\nbb-browser site reddit/thread <post-url> --openclaw\nbb-browser site weibo/hot --openclaw\nbb-browser site xiaohongshu/search \"query\" --openclaw\n\n# Developer\nbb-browser site github/repo owner/repo --openclaw\nbb-browser site github/issues owner/repo --openclaw\nbb-browser site hackernews/top 10 --openclaw\nbb-browser site stackoverflow/search \"async await\" --openclaw\nbb-browser site arxiv/search \"transformer\" --openclaw\n\n# Finance\nbb-browser site xueqiu/stock SH600519 --openclaw\nbb-browser site xueqiu/hot-stock 5 --openclaw\nbb-browser site eastmoney/stock \"茅台\" --openclaw\n\n# News & Knowledge\nbb-browser site zhihu/hot --openclaw\nbb-browser site 36kr/newsflash --openclaw\nbb-browser site wikipedia/summary \"Python\" --openclaw\n\n# Video\nbb-browser site youtube/transcript VIDEO_ID --openclaw\nbb-browser site bilibili/search \"query\" --openclaw\n```\n\n## Filtering with --jq\n\nUse `--jq` to extract specific fields (no need for `--json`, it's implied):\n\n```bash\n# Just stock names\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[].name'\n\n# Specific fields as objects\nbb-browser site xueqiu/hot-stock 5 --openclaw --jq '.items[] | {name, changePercent, heat}'\n\n# Filter results\nbb-browser site reddit/hot --openclaw --jq '.posts[] | {title, score}'\n```\n\n## View adapter details\n\n```bash\n# Check what args an adapter takes\nbb-browser site info xueqiu/stock\n\n# Search adapters by keyword\nbb-browser site search reddit\n```\n\n## Login State\n\nAdapters run inside OpenClaw's browser tabs. If a site requires login:\n\n1. The adapter will return an error like `{\"error\": \"HTTP 401\", \"hint\": \"Not logged in?\"}`\n2. Log in to the site in OpenClaw's browser:\n   ```bash\n   openclaw browser open https://twitter.com\n   ```\n3. Complete login manually in the browser window\n4. Retry the command\n\n## Creating New Adapters\n\nTurn any website into a CLI command:\n\n```bash\n# Read the guide\nbb-browser guide\n\n# Or just tell me: \"turn notion.so into a bb-browser adapter\"\n# I'll reverse-engineer the API, write the adapter, test it, and submit a PR.\n```\n\n## All 36 Platforms\n\n| Category | Platforms |\n|----------|-----------|\n| Search | Google, Baidu, Bing, DuckDuckGo, Sogou WeChat |\n| Social | Twitter/X, Reddit, Weibo, Xiaohongshu, Jike, LinkedIn, Hupu |\n| News | BBC, Reuters, 36kr, Toutiao, Eastmoney |\n| Dev | GitHub, StackOverflow, HackerNews, CSDN, cnblogs, V2EX, Dev.to, npm, PyPI, arXiv |\n| Video | YouTube, Bilibili |\n| Entertainment | Douban, IMDb, Genius, Qidian |\n| Finance | Xueqiu, Eastmoney, Yahoo Finance |\n| Jobs | BOSS Zhipin, LinkedIn |\n| Knowledge | Wikipedia, Zhihu, Open Library |\n| Shopping | SMZDM |\n| Tools | Youdao, GSMArena, Product Hunt, Ctrip |\n\n<!-- File: skills/bb-browser/references/adapter-development.md (Tokens: ~2976 | Category: anthropic-skill) -->\n# Adapter 开发指南\n\n## 开发流程概览\n\n1. 用 `network` 命令逆向 API\n2. 用 `eval` 测试 fetch 是否可行\n3. 编写 adapter JS 文件\n4. 保存到 `~/.bb-browser/sites/` 测试\n5. 提交 PR 到社区仓库\n\n<!-- 证据来源：packages/cli/src/index.ts:601-658 guide 命令输出 -->\n\n## Step 1：逆向 API\n\n```bash\n# 清空旧记录\nbb-browser network clear --tab <tabId>\n\n# 刷新页面触发请求\nbb-browser refresh --tab <tabId>\n\n# 查看 API 请求（filter 是位置参数，不是 --filter）\nbb-browser network requests \"api\" --with-body --json --tab <tabId>\n```\n\n重点关注：\n- 请求 URL 和参数格式\n- 认证方式（Cookie / Bearer token / CSRF token）\n- 响应数据结构\n\n## Step 2：测试 fetch\n\n```bash\n# 直接在浏览器中测试 fetch（Tier 1 验证）\nbb-browser eval \"fetch('/api/endpoint',{credentials:'include'}).then(r=>r.json())\" --tab <tabId>\n```\n\n根据结果判断复杂度：\n- **能直接拿到数据** → Tier 1（Cookie 认证，如 Reddit/GitHub/V2EX）\n- **需要额外请求头** → Tier 2（如 Twitter：Bearer + CSRF token）\n- **需要请求签名或注入** → Tier 3（如小红书：Pinia store / Webpack 模块）\n\n## Step 3：编写 Adapter\n\n### 元数据格式（`/* @meta */` 块）\n\n<!-- 证据来源：site.ts:56-118 parseSiteMeta() -->\n\n```javascript\n/* @meta\n{\n  \"name\": \"platform/command\",\n  \"description\": \"功能描述\",\n  \"domain\": \"www.example.com\",\n  \"args\": {\n    \"query\": {\"required\": true, \"description\": \"搜索关键词\"},\n    \"count\": {\"required\": false, \"description\": \"返回数量\"}\n  },\n  \"capabilities\": [\"search\"],\n  \"readOnly\": true,\n  \"example\": \"bb-browser site platform/command value\"\n}\n*/\nasync function(args) {\n  // adapter 实现\n}\n```\n\n### 元数据字段说明\n\n| 字段 | 必需 | 说明 |\n|------|------|------|\n| `name` | 是 | 唯一标识，格式 `platform/command` |\n| `description` | 是 | 功能描述 |\n| `domain` | 是 | 目标网站域名（用于自动 tab 匹配） |\n| `args` | 是 | 参数定义，每个参数含 `required` 和 `description` |\n| `capabilities` | 否 | 能力标签数组 |\n| `readOnly` | 否 | 是否只读操作 |\n| `example` | 否 | 使用示例 |\n\n### 旧格式兼容\n\n也支持 `// @tag value` 注释格式（向后兼容）：\n\n```javascript\n// @name platform/command\n// @description 功能描述\n// @domain www.example.com\n// @args query,filter\n// @example bb-browser site platform/command value\n```\n\n<!-- 证据来源：site.ts:100-117 旧格式解析 -->\n\n## 三层复杂度示例\n\n### Tier 1：Cookie 认证（~1 分钟）\n\n直接 fetch，`credentials: 'include'` 自动带 Cookie。\n\n```javascript\n/* @meta\n{\n  \"name\": \"reddit/search\",\n  \"description\": \"Search Reddit posts\",\n  \"domain\": \"www.reddit.com\",\n  \"args\": {\"query\": {\"required\": true, \"description\": \"Search query\"}},\n  \"readOnly\": true,\n  \"example\": \"bb-browser site reddit/search 'local LLM'\"\n}\n*/\nasync function(args) {\n  if (!args.query) return {error: 'Missing argument: query'};\n  const resp = await fetch(\n    '/search.json?q=' + encodeURIComponent(args.query) + '&limit=10',\n    {credentials: 'include'}\n  );\n  if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};\n  const data = await resp.json();\n  return data.data.children.map(c => ({\n    title: c.data.title,\n    url: 'https://www.reddit.com' + c.data.permalink,\n    score: c.data.score,\n    subreddit: c.data.subreddit\n  }));\n}\n```\n\n### Tier 2：Bearer + CSRF token（~3 分钟）\n\n需要从页面提取 token 构造请求头。\n\n```javascript\n/* @meta\n{\n  \"name\": \"twitter/search\",\n  \"description\": \"Search tweets\",\n  \"domain\": \"twitter.com\",\n  \"args\": {\"query\": {\"required\": true, \"description\": \"Search query\"}},\n  \"readOnly\": true\n}\n*/\nasync function(args) {\n  // 从 cookie 提取 CSRF token\n  const csrf = document.cookie.match(/ct0=([^;]+)/)?.[1];\n  if (!csrf) return {error: 'CSRF token not found', hint: 'Not logged in?'};\n\n  // 构造带认证的请求\n  const resp = await fetch('/i/api/2/search/adaptive.json?q=' + encodeURIComponent(args.query), {\n    credentials: 'include',\n    headers: {\n      'authorization': 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs...',\n      'x-csrf-token': csrf\n    }\n  });\n  if (!resp.ok) return {error: 'HTTP ' + resp.status};\n  return await resp.json();\n}\n```\n\n### Tier 3：Webpack 注入 / Pinia store（~10 分钟）\n\n需要访问页面内部状态或调用内部模块。\n\n## 抗变更模式\n\n网站频繁更新前端代码（CSS class、webpack module ID、GraphQL queryId 等）。以下是经过验证的抗变更模式。\n\n### 模式 1：结构化 DOM 提取（替代 CSS class 选择器）\n\n**问题**：网站经常修改 CSS class 名（如 Google 从 `div.g` 改为 `div.MjjYud`），导致 adapter 失效。\n\n**方案**：用语义化 HTML 元素（h3、a、article）定位内容，不依赖任何 class name。\n\n```javascript\n// ❌ 脆弱：依赖 CSS class（Google 已多次更改）\nconst items = doc.querySelectorAll('div.g');\n\n// ✅ 稳健：用语义元素定位\nconst h3s = doc.querySelectorAll('h3');\nfor (const h3 of h3s) {\n  const a = h3.closest('a');\n  if (!a) continue;\n  const link = a.getAttribute('href');\n  if (!link || !link.startsWith('http')) continue;\n  const title = h3.textContent.trim();\n\n  // 向上查找结果容器（找到有多个 h3 兄弟的层级停止）\n  let container = a;\n  while (container.parentElement && container.parentElement.tagName !== 'BODY') {\n    const sibs = [...container.parentElement.children];\n    if (sibs.filter(s => s.querySelector('h3')).length > 1) break;\n    container = container.parentElement;\n  }\n\n  // 在容器内、链接外查找摘要\n  const linkBlock = a.closest('div') || a;\n  let snippet = '';\n  for (const sp of container.querySelectorAll('span')) {\n    if (linkBlock.contains(sp)) continue;\n    const t = sp.textContent.trim();\n    if (t.length > 30 && t !== title) { snippet = t; break; }\n  }\n  results.push({ title, url: link, snippet });\n}\n```\n\n**适用于**：Google、Bing、DuckDuckGo、HackerNews 等搜索/列表页面。\n\n### 模式 2：Webpack 模块动态发现（替代硬编码 module ID）\n\n**问题**：SPA 网站（如 Twitter/X）的 webpack module ID 在每次部署时都会变化，硬编码 ID 很快失效。\n\n**方案**：通过搜索模块源码中的稳定签名来动态查找模块。\n\n```javascript\n// 第一步：获取 webpack require 函数\nlet __webpack_require__;\nconst chunkId = '__bb_' + Date.now();\nwindow.webpackChunk_twitter_responsive_web.push(\n  [[chunkId], {}, (req) => { __webpack_require__ = req; }]\n);\n\n// 第二步：按源码签名查找模块（不依赖 module ID）\n// 示例：查找 Transaction ID 生成器\nlet genTxId;\nfor (const id of Object.keys(__webpack_require__.m)) {\n  const src = __webpack_require__.m[id].toString();\n  // 用模块源码中稳定的字符串特征来匹配\n  if (src.includes('jf.x.com') && src.includes('jJ:')) {\n    genTxId = __webpack_require__(id).jJ;\n    break;\n  }\n}\nif (!genTxId) return {\n  error: 'Cannot find transaction ID generator',\n  hint: 'Twitter webpack structure may have changed.'\n};\n\n// 第三步：按 operationName 查找 GraphQL queryId\nlet queryId;\nfor (const id of Object.keys(__webpack_require__.m)) {\n  const src = __webpack_require__.m[id].toString();\n  const m = src.match(/queryId:\"([^\"]+)\",operationName:\"CreateTweet\"/);\n  if (m) { queryId = m[1]; break; }\n}\nif (!queryId) return {\n  error: 'Cannot find CreateTweet queryId',\n  hint: 'Twitter GraphQL schema may have changed.'\n};\n```\n\n**选择签名的原则**：\n- 选择**业务语义**字符串而非技术细节（`jf.x.com` 比变量名稳定）\n- 选择**多个特征**组合匹配（`includes('A') && includes('B')`）\n- 选择**export 名**匹配（`jJ:` 是 minified 的 export key，比函数体稳定）\n- 对于 GraphQL：`operationName` 几乎不变，`queryId` 会变 → 用前者查后者\n\n**适用于**：Twitter/X、小红书、抖音等 SPA 应用。\n\n### 模式 3：Vue/React 内部状态访问\n\n```javascript\n// Vue 3 + Pinia（如小红号）\nconst app = document.querySelector('#app').__vue_app__;\nconst store = app.config.globalProperties.$pinia._s.get('user');\n\n// React（如 Reddit new）\nconst fiber = document.querySelector('#App')._reactRootContainer?._internalRoot?.current;\n```\n\n**注意**：内部状态访问比 API 调用更脆弱，优先使用 API 逆向。\n\n## Step 4：测试\n\n```bash\n# 保存到私有目录\n# 文件路径：~/.bb-browser/sites/platform/command.js\n\n# 测试运行\nbb-browser site platform/command \"test query\" --json\n\n# 验证输出格式\nbb-browser site platform/command \"test query\"\n```\n\n## Step 5：贡献社区\n\n```bash\n# 方式 A：使用 gh CLI\ngit clone https://github.com/epiral/bb-sites && cd bb-sites\ngit checkout -b feat-platform\n# 添加 adapter 文件\ngit push -u origin feat-platform\ngh pr create --repo epiral/bb-sites\n\n# 方式 B：使用 bb-browser 自身\nbb-browser site github/fork epiral/bb-sites\ngit clone https://github.com/YOUR_USER/bb-sites && cd bb-sites\ngit checkout -b feat-platform\n# 添加 adapter 文件\ngit push -u origin feat-platform\nbb-browser site github/pr-create epiral/bb-sites --title \"feat(platform): add adapters\" --head \"YOUR_USER:feat-platform\"\n```\n\n## 错误处理规范\n\nadapter 返回错误时，使用统一格式：\n\n```javascript\n// 参数缺失\nreturn {error: 'Missing argument: query'};\n\n// HTTP 错误 + 登录提示\nreturn {error: 'HTTP 401', hint: 'Not logged in?'};\n\n// 自定义错误\nreturn {error: 'Rate limited', hint: 'Try again in 60 seconds'};\n```\n\n系统会自动检测 401/403/unauthorized/login 等关键词，生成登录提示。\n\n<!-- 证据来源：site.ts:406-427 错误检测逻辑 -->\n\n## 报告 Adapter Bug\n\n```bash\n# 通过 gh CLI\ngh issue create --repo epiral/bb-sites --title \"[adapter-name] 描述\"\n\n# 通过 bb-browser\nbb-browser site github/issue-create epiral/bb-sites --title \"[adapter-name] 描述\"\n```\n\n<!-- File: skills/bb-browser/references/fetch-and-network.md (Tokens: ~1599 | Category: anthropic-skill) -->\n# Fetch 与 Network 高级功能\n\n## fetch 命令 — 带登录态的 curl\n\n本质：在浏览器上下文中执行 `fetch()`，自动携带 Cookie 和登录态。\n\n<!-- 证据来源：packages/cli/src/commands/fetch.ts 注释 \"curl，但带浏览器登录态\" -->\n\n### 基本用法\n\n```bash\n# GET 请求（绝对路径）\nbb-browser fetch https://www.reddit.com/api/me.json\n\n# GET 请求（相对路径，使用当前 tab 的 origin）\nbb-browser fetch /api/me.json\n\n# POST 请求\nbb-browser fetch https://api.example.com/data --method POST --body '{\"key\":\"value\"}'\n\n# 自定义请求头\nbb-browser fetch https://api.example.com/data --headers '{\"Authorization\":\"Bearer token\"}'\n\n# 保存到文件\nbb-browser fetch https://api.example.com/data --output response.json\n\n# JSON 格式输出\nbb-browser fetch https://www.reddit.com/api/me.json --json\n```\n\n### 完整选项\n\n| 选项 | 说明 |\n|------|------|\n| `--method <GET\\|POST\\|...>` | HTTP 方法，默认 GET |\n| `--body <json>` | 请求体（仅 POST/PUT 等） |\n| `--headers <json>` | 自定义请求头（必须是合法 JSON） |\n| `--output <file>` | 保存响应到文件 |\n| `--json` | JSON 格式输出 |\n| `--tab <tabId>` | 指定操作的 tab（全局选项，非 fetch 专属） |\n\n<!-- 证据来源：fetch.ts:18-25 FetchOptions 接口，--tab 由 index.ts:215-218 全局解析 -->\n\n### 自动域名路由机制\n\nfetch 命令会自动处理 tab 匹配：\n\n1. **相对路径**（如 `/api/me.json`）：使用当前活动 tab 的 origin\n2. **绝对路径**（如 `https://www.reddit.com/...`）：\n   - 先查找已打开的匹配域名 tab\n   - 没有则自动打开新 tab 并等待 3 秒\n   - 在匹配的 tab 上下文中执行 fetch\n\n<!-- 证据来源：fetch.ts:42-62 ensureTabForOrigin()，fetch.ts:123-139 路由逻辑 -->\n\n### 响应处理\n\n- JSON 响应自动解析为对象\n- 非 JSON 响应返回文本\n- `--output` 时自动格式化写入文件\n\n<!-- 证据来源：fetch.ts:84-106 buildFetchScript() 中的 content-type 判断 -->\n\n### 典型场景\n\n```bash\n# 检查登录状态\nbb-browser fetch https://www.reddit.com/api/me.json\n\n# 调用内部 API\nbb-browser fetch https://internal.company.com/api/dashboard --json\n\n# 提交表单数据\nbb-browser fetch https://api.example.com/submit \\\n  --method POST \\\n  --body '{\"name\":\"test\",\"value\":123}' \\\n  --headers '{\"Content-Type\":\"application/json\"}'\n\n# 下载数据到文件\nbb-browser fetch https://api.example.com/export.csv --output data.csv\n```\n\n---\n\n## network 命令 — 网络监控与拦截\n\n<!-- 证据来源：packages/cli/src/commands/network.ts -->\n\n### 子命令一览\n\n```bash\n# 查看网络请求\nbb-browser network requests [filter] [--with-body] [--json]\n\n# 拦截请求（阻止）\nbb-browser network route <url> --abort\n\n# 拦截请求（mock 响应）\nbb-browser network route <url> --body '{\"mock\":\"data\"}'\n\n# 移除指定拦截规则\nbb-browser network unroute <url>\n\n# 移除所有拦截规则\nbb-browser network unroute\n\n# 清空请求记录\nbb-browser network clear\n```\n\n### network requests 详解\n\n```bash\n# 查看所有请求\nbb-browser network requests\n\n# 按关键词过滤（匹配 URL）\nbb-browser network requests \"api\"\n\n# 包含完整请求/响应体\nbb-browser network requests --with-body\n\n# 组合使用\nbb-browser network requests \"api\" --with-body --json\n```\n\n输出格式：\n```\nGET https://api.example.com/data\n  类型: fetch, 状态: 200 OK\n  请求头: 5, 响应头: 8        # --with-body 时显示\n  请求体: {\"query\":\"test\"}    # --with-body 时显示\n  响应体: {\"result\":[...]}    # --with-body 时显示\n```\n\n<!-- 证据来源：network.ts:46-77 requests 输出逻辑 -->\n\n### network route 详解\n\n拦截匹配 URL 的请求：\n\n```bash\n# 阻止广告/追踪请求\nbb-browser network route \"*analytics*\" --abort\n\n# Mock API 响应（用于测试）\nbb-browser network route \"*/api/user\" --body '{\"name\":\"test\",\"role\":\"admin\"}'\n\n# 添加多条规则\nbb-browser network route \"*tracker*\" --abort\nbb-browser network route \"*/api/config\" --body '{\"feature_flag\":true}'\n```\n\n<!-- 证据来源：network.ts:79-92 route 输出逻辑 -->\n\n### network unroute\n\n```bash\n# 移除指定规则\nbb-browser network unroute \"*analytics*\"\n\n# 移除所有规则\nbb-browser network unroute\n```\n\n<!-- 证据来源：network.ts:94-101 unroute 输出逻辑 -->\n\n### network clear\n\n```bash\n# 清空请求记录（重新开始监控）\nbb-browser network clear\n```\n\n<!-- 证据来源：network.ts:103-106 clear 输出逻辑 -->\n\n### API 逆向工程工作流\n\n这是 fetch + network 最强大的组合用法，用于发现网站内部 API：\n\n```bash\n# 1. 清空旧记录\nbb-browser network clear\n\n# 2. 刷新页面触发请求\nbb-browser refresh\n\n# 3. 查看 API 请求（过滤 + 完整体）\nbb-browser network requests \"api\" --with-body --json\n\n# 4. 找到目标 API 后，用 fetch 测试\nbb-browser fetch /api/discovered-endpoint --json\n\n# 5. 确认可行后，编写 site adapter\n# 参见 adapter-development.md\n```\n\n### 全局 --tab 选项\n\n所有 network 子命令都支持 `--tab <tabId>`，指定监控哪个 tab 的网络活动：\n\n```bash\nbb-browser network requests --tab 123\nbb-browser network route \"*api*\" --abort --tab 456\nbb-browser network clear --tab 123\n```\n\n<!-- File: skills/bb-browser/references/site-system.md (Tokens: ~2682 | Category: anthropic-skill) -->\n# Site 系统 — 把任何网站变成命令行 API\n\n## 核心概念\n\nSite 系统通过 adapter（适配器）将网站功能 CLI 化。每个 adapter 是一个 JS 文件，在用户真实浏览器中执行，复用登录态，返回结构化 JSON。\n\n<!-- 证据来源：packages/cli/src/commands/site.ts -->\n\n## 命令速查\n\n```bash\n# 列出所有可用 adapter（按平台分组）\nbb-browser site list\n\n# 搜索 adapter（模糊匹配名称、描述、域名）\nbb-browser site search <query>\n\n# 运行 adapter（简写，推荐）\nbb-browser site <name> [args...]\n\n# 运行 adapter（完整写法）\nbb-browser site run <name> [args...]\n\n# 更新社区 adapter 库（从 github.com/epiral/bb-sites 拉取）\nbb-browser site update\n\n# 查看 adapter 开发指南\nbb-browser guide\n```\n\n## 参数传递\n\n支持两种格式混合使用：\n\n```bash\n# 位置参数（按 adapter 定义的参数顺序）\nbb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/...\n\n# 命名参数（--flag value 格式）\nbb-browser site github/pr-create epiral/bb-sites --title \"feat: ...\" --head \"user:branch\"\n\n# 混合使用\nbb-browser site twitter/search \"AI agent\" --count 20\n```\n\n<!-- 证据来源：site.ts:290-314 参数解析逻辑 -->\n\n## Adapter 目录与优先级\n\n```\n~/.bb-browser/\n├── sites/              # 私有 adapter（优先级高，覆盖同名社区 adapter）\n│   └── platform/\n│       └── command.js\n└── bb-sites/           # 社区 adapter（通过 bb-browser site update 拉取）\n    └── platform/\n        └── command.js\n```\n\n私有 adapter 优先于社区同名 adapter。\n\n<!-- 证据来源：site.ts:24-26 目录常量，site.ts:148-157 getAllSites() 合并逻辑 -->\n\n## 自动 Tab 管理\n\n运行 adapter 时，系统自动处理 tab：\n\n1. 如果指定了 `--tab <tabId>`，直接使用该 tab\n2. 否则，根据 adapter 的 `domain` 字段查找已打开的匹配 tab\n3. 如果没有匹配的 tab，自动打开新 tab 并等待 3 秒加载\n\n域名匹配规则：精确匹配或子域名匹配（如 `x.com` 匹配 `api.x.com`）。\n\n<!-- 证据来源：site.ts:162-169 matchTabOrigin()，site.ts:342-368 tab 查找逻辑 -->\n\n## 错误处理与登录提示\n\nadapter 返回 `{error: \"...\", hint: \"...\"}` 时，系统自动检测登录相关错误（匹配 `401|403|unauthorized|forbidden|not.?logged|login.?required|sign.?in|auth`），并提示用户先在浏览器中登录。\n\n```bash\n# 错误示例\n[error] site twitter/search: HTTP 401\n  Hint: Please log in to https://twitter.com in your browser first, then retry.\n```\n\n<!-- 证据来源：site.ts:406-427 错误处理逻辑 -->\n\n## 36 平台完整列表\n\n<!-- 证据来源：README.md \"36 platforms, 103 commands\"，具体 adapter 数量以 bb-browser site list 实际输出为准 -->\n\n### 搜索引擎\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Google | `site google/search \"query\"` | 搜索 |\n| Baidu | `site baidu/search \"query\"` | 百度搜索 |\n| Bing | `site bing/search \"query\"` | 必应搜索 |\n| DuckDuckGo | `site duckduckgo/search \"query\"` | DuckDuckGo 搜索 |\n| Sogou WeChat | `site sogou/wechat \"query\"` | 搜狗微信文章搜索 |\n\n### 社交媒体\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Twitter/X | `site twitter/search \"query\"` | 搜索推文 |\n| Twitter/X | `site twitter/user <handle>` | 用户信息 |\n| Reddit | `site reddit/thread <url>` | 帖子详情 |\n| Reddit | `site reddit/search \"query\"` | 搜索 |\n| Weibo | `site weibo/search \"query\"` | 微博搜索 |\n| Weibo | `site weibo/hot` | 微博热搜 |\n| Xiaohongshu | `site xiaohongshu/search \"query\"` | 小红书搜索 |\n| Jike | `site jike/feed` | 即刻动态 |\n| LinkedIn | `site linkedin/search \"query\"` | 搜索 |\n| LinkedIn | `site linkedin/profile <url>` | 个人资料 |\n| Hupu | `site hupu/hot` | 虎扑热帖 |\n\n### 新闻资讯\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| BBC | `site bbc/headlines` | BBC 头条 |\n| Reuters | `site reuters/search \"query\"` | 路透社搜索 |\n| 36kr | `site 36kr/newsflash` | 36氪快讯 |\n| Toutiao | `site toutiao/hot` | 今日头条热榜 |\n| Eastmoney | `site eastmoney/news \"query\"` | 东方财富新闻 |\n\n### 技术开发\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| GitHub | `site github/repo <owner/repo>` | 仓库信息 |\n| GitHub | `site github/issues <owner/repo>` | Issue 列表 |\n| GitHub | `site github/pr-create <repo> --title \"...\"` | 创建 PR |\n| StackOverflow | `site stackoverflow/search \"query\"` | 搜索 |\n| HackerNews | `site hackernews/top` | 热门帖子 |\n| CSDN | `site csdn/search \"query\"` | CSDN 搜索 |\n| cnblogs | `site cnblogs/search \"query\"` | 博客园搜索 |\n| V2EX | `site v2ex/hot` | V2EX 热帖 |\n| Dev.to | `site devto/search \"query\"` | Dev.to 搜索 |\n| npm | `site npm/package <name>` | npm 包信息 |\n| PyPI | `site pypi/package <name>` | PyPI 包信息 |\n| arXiv | `site arxiv/search \"query\"` | 论文搜索 |\n\n### 视频平台\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| YouTube | `site youtube/search \"query\"` | 搜索视频 |\n| YouTube | `site youtube/transcript <video_id>` | 获取字幕 |\n| Bilibili | `site bilibili/search \"query\"` | B站搜索 |\n| Bilibili | `site bilibili/popular` | B站热门 |\n\n### 影音娱乐\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Douban | `site douban/movie <name>` | 豆瓣电影 |\n| Douban | `site douban/top250` | 豆瓣 Top250 |\n| IMDb | `site imdb/search \"query\"` | IMDb 搜索 |\n| Genius | `site genius/search \"query\"` | 歌词搜索 |\n| Qidian | `site qidian/search \"query\"` | 起点小说搜索 |\n\n### 财经股票\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Eastmoney | `site eastmoney/stock \"茅台\"` | 股票查询 |\n| Yahoo Finance | `site yahoo-finance/stock <ticker>` | 股票行情 |\n\n### 求职招聘\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| BOSS Zhipin | `site boss/search \"query\"` | BOSS 直聘搜索 |\n| BOSS Zhipin | `site boss/detail <url>` | 职位详情 |\n\n### 知识百科\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Wikipedia | `site wikipedia/search \"query\"` | 维基百科搜索 |\n| Wikipedia | `site wikipedia/summary \"topic\"` | 摘要 |\n| Zhihu | `site zhihu/hot` | 知乎热榜 |\n| Zhihu | `site zhihu/question <id>` | 问题详情 |\n| Open Library | `site openlibrary/search \"query\"` | 图书搜索 |\n\n### 消费购物\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| SMZDM | `site smzdm/search \"query\"` | 什么值得买搜索 |\n\n### 实用工具\n\n| 平台 | 命令示例 | 说明 |\n|------|---------|------|\n| Youdao | `site youdao/translate \"text\"` | 有道翻译 |\n| GSMArena | `site gsmarena/search \"phone\"` | 手机参数 |\n| Product Hunt | `site producthunt/trending` | 热门产品 |\n| Ctrip | `site ctrip/search \"destination\"` | 携程搜索 |\n\n## 常用场景示例\n\n### 信息检索\n\n```bash\n# 搜索技术话题\nbb-browser site twitter/search \"Claude Code\"\nbb-browser site reddit/search \"local LLM\"\nbb-browser site hackernews/top\n\n# 查看热榜\nbb-browser site zhihu/hot\nbb-browser site weibo/hot\nbb-browser site v2ex/hot\n\n# 查询股票\nbb-browser site eastmoney/stock \"茅台\"\n```\n\n### 开发辅助\n\n```bash\n# GitHub 操作\nbb-browser site github/repo anthropics/claude-code\nbb-browser site github/issues owner/repo\n\n# 查包信息\nbb-browser site npm/package bb-browser\nbb-browser site pypi/package requests\n\n# 搜索技术问题\nbb-browser site stackoverflow/search \"async await error handling\"\n```\n\n### 内容获取\n\n```bash\n# 获取 YouTube 字幕\nbb-browser site youtube/transcript dQw4w9WgXcQ\n\n# 获取 Reddit 帖子完整内容\nbb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/...\n\n# 论文搜索\nbb-browser site arxiv/search \"transformer attention mechanism\"\n```\n\n## 与 --json 配合\n\n所有 site 命令都支持 `--json` 输出结构化数据：\n\n```bash\nbb-browser site zhihu/hot --json\nbb-browser site twitter/search \"AI\" --json\n```\n\n## 更多信息\n\n- 创建自定义 adapter：参见 [adapter-development.md](adapter-development.md)\n- 社区 adapter 仓库：https://github.com/epiral/bb-sites\n\n<!-- File: skills/bb-browser/references/snapshot-refs.md (Tokens: ~1447 | Category: anthropic-skill) -->\n# Snapshot + Refs 工作流\n\nbb-browser 使用紧凑的元素引用（ref），大幅减少 AI Agent 的上下文消耗。\n\n## 工作原理\n\n### 传统方式的问题\n\n```\n完整 DOM/HTML 发送给 AI → AI 解析 → 生成 CSS 选择器 → 执行操作\n每次交互 ~3000-5000 tokens\n```\n\n### bb-browser 的方案\n\n```\n紧凑快照 → 分配 @ref → 直接用 ref 操作\n每次交互 ~200-400 tokens\n```\n\n## Snapshot 命令\n\n```bash\n# 完整快照（显示页面结构）\nbb-browser snapshot\n\n# 只显示可交互元素（推荐）\nbb-browser snapshot -i\n\n# JSON 格式输出\nbb-browser snapshot -i --json\n```\n\n## 快照输出格式\n\n```\n页面: Example Site - 首页\nURL: https://example.com\n\n@1 [header]\n  @2 [nav]\n    @3 [a] \"首页\"\n    @4 [a] \"产品\"\n    @5 [a] \"关于\"\n  @6 [button] \"登录\"\n\n@7 [main]\n  @8 [h1] \"欢迎\"\n  @9 [form]\n    @10 [input type=\"email\"] placeholder=\"邮箱\"\n    @11 [input type=\"password\"] placeholder=\"密码\"\n    @12 [button type=\"submit\"] \"登录\"\n\n@13 [footer]\n  @14 [a] \"隐私政策\"\n```\n\n## 使用 Ref\n\n有了 ref 后直接操作：\n\n```bash\n# 点击登录按钮\nbb-browser click @6\n\n# 填写邮箱\nbb-browser fill @10 \"user@example.com\"\n\n# 填写密码\nbb-browser fill @11 \"password123\"\n\n# 提交表单\nbb-browser click @12\n```\n\n## Ref 生命周期\n\n**重要**：页面变化后 ref 会失效！\n\n```bash\n# 获取初始快照\nbb-browser snapshot -i\n# @1 [button] \"下一页\"\n\n# 点击触发页面变化\nbb-browser click @1\n\n# 必须重新获取快照！\nbb-browser snapshot -i\n# @1 [h1] \"第二页\"  ← 现在是不同的元素\n```\n\n### Ref 失效的场景\n\n1. **页面导航**：点击链接、表单提交后跳转\n2. **动态加载**：AJAX 加载新内容\n3. **DOM 变化**：JavaScript 修改页面结构\n4. **弹窗/下拉**：打开模态框、展开菜单\n\n## 最佳实践\n\n### 1. 操作前先 Snapshot\n\n```bash\n# 正确\nbb-browser open https://example.com\nbb-browser snapshot -i          # 先获取 ref\nbb-browser click @1             # 再使用\n\n# 错误\nbb-browser open https://example.com\nbb-browser click @1             # ref 不存在！\n```\n\n### 2. 导航后重新 Snapshot\n\n```bash\nbb-browser click @5             # 导航到新页面\nbb-browser snapshot -i          # 获取新页面的 ref\nbb-browser click @1             # 使用新的 ref\n```\n\n### 3. 动态变化后重新 Snapshot\n\n```bash\nbb-browser click @1             # 打开下拉菜单\nbb-browser snapshot -i          # 获取菜单项的 ref\nbb-browser click @7             # 选择菜单项\n```\n\n### 4. 等待加载完成\n\n```bash\nbb-browser click @3             # 触发 AJAX 加载\nbb-browser wait 1000            # 等待加载\nbb-browser snapshot -i          # 获取新内容的 ref\n```\n\n## Ref 格式说明\n\n```\n@1 [tag type=\"value\"] \"文本内容\" placeholder=\"提示\"\n│   │   │             │          │\n│   │   │             │          └─ 其他属性\n│   │   │             └─ 可见文本\n│   │   └─ 关键属性\n│   └─ HTML 标签\n└─ 唯一 ref ID\n```\n\n### 常见模式\n\n```\n@1 [button] \"提交\"                    # 按钮\n@2 [input type=\"email\"]               # 邮箱输入框\n@3 [input type=\"password\"]            # 密码输入框\n@4 [a href=\"/page\"] \"链接文字\"         # 链接\n@5 [select]                           # 下拉框\n@6 [textarea] placeholder=\"请输入\"     # 文本域\n@7 [checkbox] checked                 # 已勾选的复选框\n@8 [radio] selected                   # 已选中的单选框\n```\n\n## 常见问题\n\n### \"Ref not found\" 错误\n\n页面已变化，ref 失效了：\n\n```bash\n# 重新获取快照\nbb-browser snapshot -i\n```\n\n### 元素不在快照中\n\n可能需要滚动或等待：\n\n```bash\n# 滚动到底部\nbb-browser scroll down\nbb-browser snapshot -i\n\n# 或等待动态内容\nbb-browser wait 2000\nbb-browser snapshot -i\n```\n\n### 快照太长\n\n使用 `-i` 只显示可交互元素：\n\n```bash\n# 完整快照可能很长\nbb-browser snapshot\n\n# 只显示可交互元素（推荐）\nbb-browser snapshot -i\n```\n\n### 需要操作不可交互元素\n\n使用 `eval` 直接执行 JavaScript：\n\n```bash\nbb-browser eval \"document.querySelector('.hidden-element').click()\"\n```\n\n## 调试技巧\n\n```bash\n# 查看完整页面结构\nbb-browser snapshot\n\n# 只看可交互元素\nbb-browser snapshot -i\n\n# JSON 格式便于解析\nbb-browser snapshot -i --json\n\n# 获取特定元素的文本\nbb-browser get text @5\n```\n\n<!-- File: skills/bb-browser/SKILL.md (Tokens: ~3294 | Category: anthropic-skill) -->\n---\nname: bb-browser\ndescription: 强大的信息获取与浏览器自动化工具。通过浏览器 + 用户登录态，获取公域和私域信息。可访问任意网页、内部系统、登录后页面，执行表单填写、信息提取、页面操作。支持 site 系统（36 平台 103 命令一键调用）、带登录态的 fetch、网络请求拦截与 mock、操作录制等高级功能。\nallowed-tools: Bash(bb-browser:*)\n---\n\n# bb-browser - 信息获取与浏览器自动化\n\n## 核心价值\n\n通过浏览器 + 用户登录态，可以获取：\n- 公域信息：任意公开网页、搜索结果、新闻资讯\n- 私域信息：内部系统、企业应用、登录后页面、个人账户数据\n\n还可以代替用户执行浏览器操作：表单填写、按钮点击、数据提取、截图保存、批量操作。\n\n运行在用户真实浏览器中，复用已登录的账号，不触发反爬检测。\n\n## 快速开始\n\n```bash\nbb-browser open <url>        # 打开页面（新 tab）\nbb-browser snapshot -i       # 获取可交互元素\nbb-browser click @5          # 点击元素\nbb-browser fill @3 \"text\"    # 填写输入框\nbb-browser close             # 完成后关闭 tab\n```\n\n## Site 系统 — 把任何网站变成命令行 API\n\nsite 系统是 bb-browser 的核心特性，通过 adapter 将网站功能 CLI 化，覆盖 36+ 平台。\n\n```bash\n# 常用命令\nbb-browser site list                          # 列出所有 adapter\nbb-browser site search <query>                # 搜索 adapter\nbb-browser site <name> [args...]              # 运行 adapter\nbb-browser site update                        # 更新社区 adapter 库\n\n# 使用示例\nbb-browser site twitter/search \"Claude Code\"  # 搜索推文\nbb-browser site zhihu/hot                     # 知乎热榜\nbb-browser site github/repo owner/repo        # 仓库信息\nbb-browser site youtube/transcript <video_id> # 获取字幕\nbb-browser site reddit/thread <url>           # 帖子详情\nbb-browser site eastmoney/stock \"茅台\"         # 股票查询\nbb-browser site weibo/hot                     # 微博热搜\nbb-browser site arxiv/search \"transformer\"    # 论文搜索\n```\n\nadapter 自动处理 tab 管理（查找匹配域名的 tab 或新建），自动检测登录错误并提示。\n\n详细用法和 36 平台完整列表：参见 [references/site-system.md](references/site-system.md)\n创建自定义 adapter：参见 [references/adapter-development.md](references/adapter-development.md)\n\n## fetch — 带登录态的 curl\n\n在浏览器上下文中执行 fetch，自动携带 Cookie 和登录态。\n\n```bash\nbb-browser fetch <url>                                    # GET 请求\nbb-browser fetch <url> --method POST --body '{\"k\":\"v\"}'   # POST 请求\nbb-browser fetch <url> --headers '{\"Auth\":\"Bearer xxx\"}'  # 自定义请求头\nbb-browser fetch <url> --output data.json                 # 保存到文件\nbb-browser fetch /api/me.json                             # 相对路径（用当前 tab 的 origin）\n```\n\n自动域名路由：绝对路径自动查找匹配 tab 或新建；相对路径使用当前 tab。\n\n详细用法：参见 [references/fetch-and-network.md](references/fetch-and-network.md)\n\n## Tab 管理规范\n\n操作完成后必须关闭自己打开的 tab。\n\n```bash\n# 单 tab 场景\nbb-browser open https://example.com    # 打开新 tab\nbb-browser snapshot -i\nbb-browser click @5\nbb-browser close                        # 完成后关闭\n\n# 多 tab 场景\nbb-browser open https://site-a.com     # tabId: 123\nbb-browser open https://site-b.com     # tabId: 456\n# ... 操作 ...\nbb-browser tab close                    # 关闭当前 tab\nbb-browser tab close                    # 关闭剩余 tab\n\n# 指定 tab 操作\nbb-browser open https://example.com --tab current  # 在当前 tab 打开（不新建）\nbb-browser open https://example.com --tab 123      # 在指定 tabId 打开\n```\n\n## 核心工作流\n\n1. `open` 打开页面\n2. `snapshot -i` 查看可操作元素（返回 @ref）\n3. 用 `@ref` 执行操作（click, fill, etc.）\n4. 页面变化后重新 `snapshot -i`\n5. 任务完成后 `close` 关闭 tab\n\n## 命令速查\n\n### 导航\n\n```bash\nbb-browser open <url>                # 打开 URL（新 tab）\nbb-browser open <url> --tab current  # 在当前 tab 打开\nbb-browser back                      # 后退\nbb-browser forward                   # 前进\nbb-browser refresh                   # 刷新\nbb-browser close                     # 关闭当前 tab\n```\n\n### 快照\n\n```bash\nbb-browser snapshot             # 完整页面结构\nbb-browser snapshot -i          # 只显示可交互元素（推荐）\nbb-browser snapshot -c          # 移除空结构节点\nbb-browser snapshot -d 3        # 限制树深度为 3 层\nbb-browser snapshot -s \".main\"  # 限定 CSS 选择器范围\nbb-browser snapshot --json      # JSON 格式输出\n# 选项可组合：bb-browser snapshot -i -c -d 5\n```\n\n### 元素交互\n\n```bash\nbb-browser click @5             # 点击\nbb-browser hover @5             # 悬停\nbb-browser fill @3 \"text\"       # 清空并填写\nbb-browser type @3 \"text\"       # 追加输入（不清空）\nbb-browser check @7             # 勾选复选框\nbb-browser uncheck @7           # 取消勾选\nbb-browser select @4 \"option\"   # 下拉选择\nbb-browser press Enter          # 按键\nbb-browser press Control+a      # 组合键\nbb-browser scroll down          # 向下滚动（默认 300px）\nbb-browser scroll up 500        # 向上滚动 500px\n```\n\n### 获取信息\n\n```bash\nbb-browser get text @5          # 获取元素文本\nbb-browser get url              # 获取当前 URL\nbb-browser get title            # 获取页面标题\n```\n\n### Tab 管理\n\n```bash\nbb-browser tab                  # 列出所有 tab\nbb-browser tab new [url]        # 新建 tab\nbb-browser tab 2                # 切换到第 2 个 tab（按 index）\nbb-browser tab select --id 123  # 切换到指定 tabId 的 tab\nbb-browser tab close            # 关闭当前 tab\nbb-browser tab close 3          # 关闭第 3 个 tab（按 index）\nbb-browser tab close --id 123   # 关闭指定 tabId 的 tab\n```\n\n### 截图\n\n```bash\nbb-browser screenshot           # 截图（自动保存）\nbb-browser screenshot path.png  # 截图到指定路径\n```\n\n### 等待\n\n```bash\nbb-browser wait 2000            # 等待 2 秒\nbb-browser wait @5              # 等待元素出现\n```\n\n### JavaScript\n\n```bash\nbb-browser eval \"document.title\"              # 执行 JS\nbb-browser eval \"window.scrollTo(0, 1000)\"    # 滚动到指定位置\n```\n\n### Frame 切换\n\n```bash\nbb-browser frame \"#iframe-id\"   # 切换到 iframe\nbb-browser frame main           # 返回主 frame\n```\n\n### 对话框处理\n\n```bash\nbb-browser dialog accept        # 确认对话框\nbb-browser dialog dismiss       # 取消对话框\nbb-browser dialog accept \"text\" # 确认并输入（prompt）\n```\n\n### 网络与调试\n\n```bash\nbb-browser network requests                        # 查看网络请求\nbb-browser network requests \"api\" --with-body       # 过滤 + 完整请求/响应体\nbb-browser network route \"*analytics*\" --abort      # 拦截并阻止请求\nbb-browser network route \"*/api/user\" --body '{}'   # 拦截并 mock 响应\nbb-browser network unroute                          # 移除所有拦截规则\nbb-browser network clear                            # 清空请求记录\nbb-browser console                                  # 查看控制台消息\nbb-browser console --clear                          # 清空控制台\nbb-browser errors                                   # 查看 JS 错误\nbb-browser errors --clear                           # 清空错误记录\nbb-browser trace start                              # 开始录制用户操作\nbb-browser trace stop                               # 停止录制，输出事件列表\nbb-browser trace status                             # 查看录制状态\n```\n\n详细的 network 高级用法：参见 [references/fetch-and-network.md](references/fetch-and-network.md)\n\n## 全局选项\n\n```bash\n--json               # 以 JSON 格式输出（所有命令通用）\n--tab <tabId>        # 指定操作的标签页 ID（几乎所有命令通用）\n--mcp                # 启动 MCP server（用于 Claude Code / Cursor 等 AI 工具）\n```\n\n## Ref 使用说明\n\nsnapshot 返回的 `@ref` 是元素的临时标识：\n\n```\n@1 [button] \"提交\"\n@2 [input type=\"text\"] placeholder=\"请输入姓名\"\n@3 [a] \"查看详情\"\n```\n\n注意：\n- 页面导航后 ref 失效，需重新 snapshot\n- 动态内容加载后需重新 snapshot\n- ref 格式：`@1`, `@2`, `@3`...\n\n详细说明：参见 [references/snapshot-refs.md](references/snapshot-refs.md)\n\n## 并发操作\n\n```bash\n# 并发打开多个页面（各自独立 tab）\nbb-browser open https://site-a.com &\nbb-browser open https://site-b.com &\nbb-browser open https://site-c.com &\nwait\n# 每个返回独立的 tabId，互不干扰\n```\n\n## 信息提取 vs 页面操作\n\n根据目的选择不同的方法：\n\n### 提取页面内容（用 eval）\n\n当需要提取文章、正文等长文本时，用 `eval` 直接获取：\n\n```bash\n# 微信公众号文章\nbb-browser eval \"document.querySelector('#js_content').innerText\"\n\n# 知乎回答\nbb-browser eval \"document.querySelector('.RichContent-inner').innerText\"\n\n# 通用：获取页面主体文本\nbb-browser eval \"document.body.innerText.substring(0, 5000)\"\n\n# 获取所有链接\nbb-browser eval \"[...document.querySelectorAll('a')].map(a => a.href).join('\\n')\"\n```\n\n有些网站 DOM 嵌套很深，snapshot 输出冗长，`eval` 直接提取文本更高效。\n\n### 操作页面元素（用 snapshot -i）\n\n当需要点击、填写、选择时，用 `snapshot -i` 获取可交互元素：\n\n```bash\nbb-browser snapshot -i\n# @1 [button] \"登录\"\n# @2 [input] placeholder=\"用户名\"\n# @3 [input type=\"password\"]\n\nbb-browser fill @2 \"username\"\nbb-browser fill @3 \"password\"\nbb-browser click @1\n```\n\n`-i` 只显示可交互元素，过滤掉大量无关内容。\n\n## MCP 集成\n\nbb-browser 提供 MCP server，可与 Claude Code / Cursor 等 AI 工具集成：\n\n```bash\n# 启动 MCP server\nbb-browser --mcp\n```\n\n配置示例（Claude Code / Cursor）：\n```json\n{\n  \"mcpServers\": {\n    \"bb-browser\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"bb-browser\", \"--mcp\"]\n    }\n  }\n}\n```\n\n## 常见任务示例\n\n### 表单填写\n\n```bash\nbb-browser open https://example.com/form\nbb-browser snapshot -i\nbb-browser fill @1 \"张三\"\nbb-browser fill @2 \"zhangsan@example.com\"\nbb-browser click @3\nbb-browser wait 2000\nbb-browser close\n```\n\n### 信息提取\n\n```bash\nbb-browser open https://example.com/dashboard\nbb-browser snapshot -i\nbb-browser get text @5\nbb-browser screenshot report.png\nbb-browser close\n```\n\n### 批量操作\n\n```bash\nfor url in \"url1\" \"url2\" \"url3\"; do\n  bb-browser open \"$url\"\n  bb-browser snapshot -i --json\n  bb-browser close\ndone\n```\n\n## 深入文档\n\n| 文档 | 说明 |\n|------|------|\n| [references/site-system.md](references/site-system.md) | Site 系统完整指南：35 平台列表、命令用法、自动 tab 管理 |\n| [references/adapter-development.md](references/adapter-development.md) | Adapter 开发指南：API 逆向、三层复杂度、元数据格式 |\n| [references/fetch-and-network.md](references/fetch-and-network.md) | Fetch 与 Network 高级功能：带登录态请求、请求拦截与 mock |\n| [references/snapshot-refs.md](references/snapshot-refs.md) | Ref 生命周期、最佳实践、常见问题 |\n</yakaai_skills>"}