mcpField
B
bunwv

bunwv

github.com/naticha测试与质量21941 次安装24 周前更新
代码仓库

关于 bunwv

bunwv 是用于 Agent 驱动浏览器自动化的命令行技能,通过持久化 WebView 会话保留页面状态。命令提供结构化结果与错误,并支持截图和事件捕获。

主要功能

  1. 01持久化 WebView 会话
  2. 02浏览器操作与页面等待
  3. 03DOM 求值和元素检查
  4. 04表单交互与提交
  5. 05截图和图像转换
  6. 06控制台与 CDP 事件捕获

适用场景

  1. 01自动执行多步骤网站任务
  2. 02测试表单提交和页面导航
  3. 03检查页面内容与浏览器错误
  4. 04在 UI 检查中捕获截图

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/naticha/bunwv --skill bunwv

安装后在 Claude Code 中输入/bunwv确认已加载。

详细介绍

bunwv

使用持久化 WebView 会话进行无头浏览器自动化。守护进程会让单个 WebView 实例持续运行,因此页面状态(DOM、模态框、表单、SPA 路由、滚动位置)会在各条命令之间保持不变。

面向 Agent 的使用模式

bunwv 专为通过离散工具调用驱动它的 AI Agent 设计。以下是一些可依赖的约定:

  • 成功的动词命令不会向 stdout 输出任何内容,并以 0 退出。 click、type、navigate、press、scroll、scroll-to、clear、submit、resize、back/forward/reload、close、exists、wait-for、wait-for-gone、cdp-subscribe、cdp-unsubscribe 都遵循此规则。读取类命令(status、evaluate、events、console、cdp、cdp-subscriptions、screenshot、sessions)会输出结果。
  • 错误会以 JSON 格式写入 stderr,并使用稳定的退出码。 根据退出码分支,而不是根据 stderr 文本:
    • 0 正常,1 通用错误,2 用法错误,3 超时,4 未找到元素,5 守护进程无法访问,6 批处理部分失败(仅用于 batch --keep-going)。
  • 执行命令期间,console.error/console.warn 会自动显示。 如果页面在某条命令运行时记录了错误,bunwv 会将 {"console":[…]} 输出到 stderr。无需再次调用即可看到失败信息。
  • 使用游标拉取事件。 events --since <seq> 会返回游标之后的新条目以及一个新游标。请在不同轮次之间保留游标,并在执行操作后重新获取。如果缓冲区已淘汰较早的条目,响应会包含 "truncated":true,"oldest":<seq>。
  • 二进制输出使用文件路径。 默认情况下,bunwv screenshot 会将字节写入 /tmp/bunwv-screenshot-<session>.jpg(JPEG @ q80),并将路径输出到 stdout。使用该路径调用 Read 工具查看图像。
  • 使用 --json 获取统一封装格式。 任何命令加上 --json 都会以单行 JSON 返回 {ok, data?, error?, exitCode}。如果你更倾向于使用统一格式而不是简洁输出,请使用此选项。
  • 标志语法灵活。 --flag value、--flag=value 和重复标志(例如 --mod Shift --mod Control)都有效。标志可以放在命令之前或之后:bunwv --json status 和 bunwv status --json 等效。
  • BUNWV_SESSION 环境变量 — 设置一次后,--session 就是可选的。

命令

使用 bunwv <command> 运行所有命令(通过 bun install -g @naticha/bunwv 全局安装)。

bunwv start [--width N] [--height N] [--data-store PATH] [--idle-timeout ms]
      [--backend webkit|chrome] [--chrome-path PATH] [--chrome-argv '[json]']
      [--chrome-url <ws-url>] [--url <initial-url>]
bunwv navigate <url>
bunwv click --selector <css> | --text <text> | --at <x,y>
      [--text-match exact|contains|regex]   # default: contains (trimmed)
      [--button left|right|middle] [--count 1|2|3]
      [--mod Shift] [--mod Control] [--mod Alt] [--mod Meta]
      [--timeout ms]
bunwv exists <selector>                     # silent; exit 0 if present, 4 if not
bunwv type <text>
bunwv press <key> [--mod Shift] [--mod Control] ...
bunwv clear <selector>
bunwv submit [--form <selector>] [--button <text>]
bunwv scroll <dx> <dy>
bunwv scroll-to <selector> [--block start|center|end|nearest] [--timeout ms]
bunwv screenshot [--format png|jpeg|webp|avif|heic] [--quality 0-100]
      [--max-width N] [--max-height N]
      [--placeholder | --metadata]
      [--encoding blob|buffer|base64|shmem] [--out <path>|-]
bunwv image <input> [--out <path>|-] [--format ...] [--quality N]
      [--resize WxH | --max-width N | --max-height N]
      [--rotate 90|180|270] [--flip] [--flop] [--metadata] [--placeholder]
bunwv evaluate <expression>
bunwv console [--clear] [--since <seq>]       # terse "<seq> [<level>] <message>", cursor-based
bunwv events [--since <seq>]
bunwv cdp <method> [--params '{}']
bunwv cdp-subscribe <CDP.event> [<CDP.event> ...]
bunwv cdp-unsubscribe <CDP.event> [<CDP.event> ...]
bunwv cdp-subscriptions
bunwv wait-for <selector> | --url <substring> | --title <substring>
      [--timeout ms]
bunwv wait-for-gone <selector> | --url <substring> | --title <substring>
      [--timeout ms]
bunwv batch [--file <path>] [--keep-going]  # stdin NDJSON of JSON arrays
bunwv status [--json]
bunwv resize <width> <height>
bunwv back / forward / reload
bunwv sessions
bunwv close [--all]
bunwv help

所有命令都接受 --json(可选封装格式)、--session <name>(或 BUNWV_SESSION 环境变量),以及上文所述的灵活标志语法。

默认视口为 1920x1080,以便清晰查看截图。

会话管理

会话是命名且彼此隔离的。每个会话都在独立的 Unix 套接字上运行自己的守护进程。默认会话名为 default;可通过 --session <name> 或 BUNWV_SESSION 环境变量指定其他会话。

bunwv start                          # starts "default" session
bunwv start --session cmais          # separate "cmais" session
BUNWV_SESSION=cmais bunwv navigate http://localhost:3000
bunwv sessions                       # list all running sessions
bunwv close --session cmais          # stop a specific session
bunwv close --all                    # stop every running session

自动关闭:守护进程在闲置 30 分钟后退出。可通过 --idle-timeout 覆盖此设置:

bunwv start --idle-timeout 3600000   # 1 hour
bunwv start --idle-timeout 0         # never auto-shutdown

复用检测:如果会话已存在,运行 bunwv start 会输出当前 URL 并以 0 退出。

最佳实践:在对话开始时运行 bunwv sessions,检查是否存在孤立的守护进程。使用 bunwv close --all 关闭不需要的会话。

核心交互循环

先观察,再操作,然后再次观察。典型的单轮循环如下:

  1. 启动守护进程:bunwv start(如果已在运行则不执行任何操作)
  2. 导航:bunwv navigate http://localhost:3000
  3. 截图:bunwv screenshot — 将 /tmp/bunwv-screenshot-<session>.png 输出到 stdout
  4. 使用 Read 工具读取截图
  5. 操作:bunwv click --selector "button.submit"(或 --text)
  6. 在下一次截图前运行 bunwv wait-for --url "/next"(或 wait-for "<selector>")
  7. 再次截图以验证
  8. 任务完成后运行 bunwv close

对于多步骤流程,优先使用 bunwv batch(见下文)——它会在一个进程中运行整个序列,并返回可供检查的 NDJSON 记录。

点击元素

click 是多态的——恰好使用 --selector、--text 或 --at 中的一个:

bunwv click --selector "button.submit"
bunwv click --text "Sign In"                             # default: trimmed contains match
bunwv click --text "Sign In" --text-match exact
bunwv click --text "^Sign.+In$" --text-match regex
bunwv click --at 100,200

修饰键、按钮和点击次数彼此独立:

bunwv click --selector "#ctx" --button right                # context menu
bunwv click --selector ".item" --count 2                    # double-click
bunwv click --selector "a" --mod Shift                      # shift+click
bunwv click --selector "a" --mod Meta --mod Shift           # cmd+shift+click
bunwv click --selector "button" --timeout 60000             # longer actionability wait

--text 默认使用去除首尾空白的子字符串匹配(区分大小写)。使用 --text-match exact 进行严格相等匹配,或使用 --text-match regex 匹配正则表达式模式。--selector 和 --text 都会在等待元素可操作后生成原生的 isTrusted: true 事件;--at 则会跳过等待。

清空和编辑输入字段

不要使用 Cmd+A / Backspace 清空 React 输入框——这不会更新 React 状态。请使用 clear:

bunwv clear "input[name='email']"
bunwv click --selector "input[name='email']"
bunwv type "[email protected]"

编辑现有输入值时,务必先 clear,然后 click,再 type。

等待元素、URL 或标题

执行会触发页面变化的操作后,使用 wait-for:

bunwv click --text "Save Changes"
bunwv wait-for-gone "[role='dialog']"                # wait for modal to close
bunwv screenshot

bunwv click --text "Edit"
bunwv wait-for "[role='dialog']"                     # wait for modal to appear
bunwv wait-for --url "/dashboard"                    # wait until URL contains substring
bunwv wait-for --title "Home"                        # wait until <title> contains substring

--url 会轮询 location.href,--title 会轮询 document.title。必须且只能指定 <selector>、--url、--title 中的一个。默认超时时间为 10s。

检查是否存在

使用 exists 进行低成本探测(不输出内容;存在时退出码为 0,不存在时为 4):

bunwv exists "[data-loaded]"
if [ $? -eq 0 ]; then ... fi

优先使用 exists,而不是 evaluate "!!document.querySelector(...)"——所需令牌更少,行为也更清晰。

提取页面数据

evaluate 会将结果以 JSON 字面量形式输出——字符串保留引号,数字不带引号,对象则以结构化 JSON 形式返回:

bunwv evaluate "document.title"                              # "Example"
bunwv evaluate "document.querySelectorAll('.error').length"  # 3
bunwv evaluate "[...document.querySelectorAll('h2')].map(h => h.textContent)"

语句(const、let、if 等)会自动包装在 IIFE 中。

提交表单

使用 submit,而不是点击提交按钮——它会调用 form.requestSubmit(),React 表单可以接受此方式(JS .click() 生成的 isTrusted:false 会被许多 React 处理程序忽略):

bunwv submit                                    # first form on page
bunwv submit --button "Save Changes"            # submit via a specific button
bunwv submit --form "form.edit-quote"           # target a specific form

提交后,等待 DOM 发生相应变化:

bunwv submit --button "Save Changes"
bunwv wait-for-gone "[role='dialog']"
bunwv screenshot

填写表单

先点击输入框,然后输入。使用 Tab 在字段间移动:

bunwv click --selector "input[name='email']"
bunwv type "[email protected]"
bunwv press Tab
bunwv type "password123"
bunwv submit --button "Sign In"

凭据放在 .env 中(Bun 会自动加载)。Shell 会在 CLI 参数中展开 $VAR:

bunwv type "$TEST_EMAIL"

持久化身份验证

使用 --data-store,在守护进程重启后保留 cookie 和 localStorage:

bunwv start --data-store ./bunwv-session

登录一次,后续会话便会保持已验证状态。

使用控制台捕获进行调试

页面控制台输出会自动捕获。动词执行期间触发的 console.error/console.warn 条目会与动词响应一起输出到 stderr。要获取完整缓冲区:

bunwv console                    # terse: "<seq> [<level>] <message>", one per line
bunwv console --clear            # print then clear
bunwv console --since 42         # only entries with seq > 42 (matches events cursor model)
bunwv --json console              # {messages:[…], cursor, truncated?, oldest?}

简洁输出会对消息中的 \n 和 \r 进行转义,使每条记录都保持在一行内。缓冲区为空时不输出内容(退出码为 0)。将你看到的最大 seq(每行的第一个字段)用作 --since 的值,以推进读取位置。需要原始消息文本(例如多行堆栈跟踪)或截断标记时,使用 --json。

导航和 CDP 事件

导航事件和已订阅的 CDP 事件会进入环形缓冲区。使用游标获取它们:

bunwv events                          # full buffer, prints {events, cursor}
bunwv events --since 42                # only events with seq > 42

订阅 CDP 事件(仅限 Chrome 后端;需先启用对应域)。每次调用可指定多个类型:

bunwv cdp Network.enable
bunwv cdp-subscribe Network.responseReceived Network.requestWillBeSent
bunwv navigate https://example.com
bunwv events --since 0                 # inspect events
bunwv cdp-unsubscribe Network.responseReceived Network.requestWillBeSent
bunwv cdp-subscriptions                # list active subscriptions, one per line

如果缓冲区移除了较旧的条目,events 会返回 "truncated":true,"oldest":<seq>。

截图选项

默认情况下,截图会以 JPEG 格式(质量为 80)写入会话专属文件,并输出文件路径。默认使用 JPEG,是因为它通常比相同视口尺寸的 PNG 小 5–15×,从而大幅降低截图循环的成本。

bunwv screenshot                                    # /tmp/bunwv-screenshot-<session>.jpg (JPEG @ q80)
bunwv screenshot --format png                       # opt back into PNG for pixel-exact comparisons
bunwv screenshot --format webp --quality 70         # smaller again at the cost of decode speed
bunwv screenshot --max-width 1024                   # cap longest side; aspect preserved; never upscales
bunwv screenshot --max-width 800 --max-height 600   # bound both axes
bunwv screenshot --out shot.jpg                     # write to a specific path
bunwv screenshot --out -                            # bytes to stdout
bunwv screenshot --encoding base64                  # base64 string to stdout

无需完整像素时的低成本预览:

bunwv screenshot --metadata     # {"width":1920,"height":1080,"format":"png"} — size-check before Reading
bunwv screenshot --placeholder  # data:image/png;base64,... (tiny blur-up preview, no file)

--placeholder 和 --metadata 会在 stdout 输出结构化结果,而不是写入文件;二者不能同时使用。它们不能与 --encoding shmem 组合使用。

**平台说明:**在 Apple Silicon 上运行的 macOS/Windows 才支持编码 --format avif 和 --format heic。其他平台会返回结构化错误。为确保代码可移植,请使用 jpeg/webp/png。

--encoding shmem(Kitty 终端)会输出 {name, size},并将 POSIX shm 段留给调用方取消链接。它会绕过 Bun.Image 流程,因此与之组合使用时会拒绝 --max-width/--max-height/--placeholder/--metadata。

转换任意图像:bunwv image

对于截图循环之外的本地图像文件(上传文件、下载的资源、测试夹具),bunwv image 会直接在 CLI 进程中运行 Bun.Image——无需守护进程,也无需会话。

bunwv image input.png                                  # input.jpg (jpeg @ q80, same dir)
bunwv image input.png --out small.webp --max-width 512 # webp, capped at 512 wide, aspect preserved
bunwv image input.png --resize 200x200                 # explicit resize (may distort if aspect differs)
bunwv image input.png --rotate 90                      # rotate 90/180/270
bunwv image input.png --flip                           # vertical flip; --flop is horizontal
bunwv image input.png --metadata                       # {"width","height","format"} JSON on stdout
bunwv image input.png --placeholder                    # data: URL on stdout

如果未指定 --format,输出格式会根据 --out 的扩展名推断;否则默认为 JPEG。如果省略 --out,结果会以新扩展名写入输入文件旁边。

Chrome 后端与 CDP

macOS 默认使用 WebKit;Linux/Windows 会自动使用 Chrome。可在任意位置覆盖:

bunwv start --backend chrome
bunwv start --chrome-path /path/to/chromium
bunwv start --chrome-argv '["--headless=new"]'
bunwv start --chrome-url ws://127.0.0.1:9222/devtools/browser/<id>   # attach to an existing Chrome

原始 CDP 调用(仅限 Chrome):

bunwv cdp "Page.getLayoutMetrics"
bunwv cdp "Runtime.evaluate" --params '{"expression": "1+1"}'
bunwv cdp "Emulation.setDeviceMetricsOverride" --params '{"width":375,"height":812,"deviceScaleFactor":2,"mobile":true}'

WebKit 后端不支持 CDP。

调试崩溃的后端

将后端进程的标准输入输出重定向到守护进程的标准输入输出(仅供人工调试;Agent 无需使用这些功能):

bunwv start --backend chrome --chrome-stderr inherit
bunwv start --webkit-stderr inherit

批处理模式

bunwv batch 会在单个进程中执行多条命令——每个动词只需一次 socket 往返,无需为每条命令单独启动 Bun。每一行标准输入都是一个 JSON 参数数组;每个响应都是标准输出中的一个 NDJSON 信封。batch 上的标志(例如 --session)会应用于每一行,除非该行指定了自己的标志。

$ cat <<'EOF' | bunwv batch --session cmais --keep-going
["navigate","http://localhost:3000/login"]
["click","--selector","input[name='email']"]
["type","[email protected]"]
["press","Tab"]
["type","hunter2"]
["submit","--button","Sign In"]
["wait-for","--url","/dashboard"]
["screenshot"]
EOF
{"argv":[...],"ok":true,"exitCode":0}
{"argv":[...],"ok":true,"exitCode":0}
...

如果某一行失败,--keep-going 仍会执行完整列表;只要有任何行失败,进程就会以 6(batch-partial)退出;如果全部成功,则以 0 退出;如果关闭了 --keep-going,则以失败行对应的退出码退出。--file <path> 会从文件读取,而不是从标准输入读取。

stdout 字段包含命令的简短输出(例如,evaluate 的输出为 "\"Example Domain\"");stdoutBytes 是二进制输出(例如 screenshot --out -)的 base64 编码。

错误恢复

如果命令失败或超时:

  1. 截图查看页面状态
  2. 运行 bunwv console 查看捕获到的错误
  3. 运行 bunwv events --since 0 查看导航/CDP 事件
  4. 运行 bunwv evaluate 检查 DOM
  5. 如果无法访问守护进程(退出码 5),运行 bunwv start——数据存储会保留身份验证信息

Socket 权限

每个会话的 Unix socket(/tmp/bunwv-<session>.sock)和 PID 文件都设置为 chmod 0600——只有启动守护进程的用户才能与其通信。在共享机器(容器、构建机)上,这可以防止其他本地用户控制你的浏览器会话。

已知限制

  • macOS:默认使用 WebKit(无依赖项)。Linux/Windows:自动检测 Chrome。使用 --backend 覆盖。
  • click --selector / --text 会自动等待元素可操作(可见、稳定且未被遮挡);WebView 默认超时为 30s,可通过 --timeout 覆盖。
  • **--text 默认使用去除首尾空白后的子字符串匹配(包含匹配)。**使用 --text-match exact|regex 更改匹配方式。
  • --at 会跳过可操作性等待——需要知道精确坐标。如果 CSS/文本不起作用,请使用 evaluate + getBoundingClientRect()。
  • React 输入框需要使用 clear——Cmd+A/Backspace 不会更新 React 的内部状态。
  • **CDP 仅支持 Chrome。**WebKit 不支持 bunwv cdp 和 bunwv cdp-subscribe。
  • 事件缓冲区:最多保存 1000 条记录或 10 MB,以先达到者为准。较早的记录会被静默丢弃;如果有记录遗漏,events --since 会报告 truncated。

评论

支持 Markdown