核心用法 / Core Usage

交互操作

读到页面之后,就该动手了。这一页覆盖驱动真实浏览器的全部交互动词—— 点击、输入、勾选、下拉、上传、滚动、拖拽。 大多数命令的第一个参数是 snapshot -i 拿到的 @ref(例如 @e42);先定位元素, 再用这里的命令操作它。

点击与指针

最常用的一组动词,都作用在一个 @ref 上。click 会自动把元素滚动进视口, 若坐标点击被遮挡,会回退到 DOM 的 .click()

点击 / 悬停 / 聚焦
$ chrome-use click @e1                   # 点击
$ chrome-use click @e1 --new-tab         # 在新标签页打开链接,而不是当前页导航
$ chrome-use dblclick @e1                # 双击
$ chrome-use hover @e1                   # 悬停
$ chrome-use focus @e1                   # 聚焦(键盘输入前常用)
✅ 点击成功却「没反应」?
典型场景:一个 autocomplete/菜单的 <li> 在输入框失焦时就关掉了。 换成 DOM 派发的点击再试一次: AGENT_BROWSER_CLICK_MODE=dom chrome-use click @e1, 或直接用 eval 在页面里选中那一项。
ℹ️ DOM 派发的点击也会移动焦点
扩展中继下左键点击是通过 DOM 派发的。它现在会像真实点击一样把焦点交给被点元素 (或最近的可聚焦祖先 / label 对应的控件),除非点击处理器已经自己移动了焦点。 所以 click <input> 之后 press Meta+a 落在这个输入框上, 不再落到上一个焦点字段。普通 <li> 没有可聚焦目标,上面的 autocomplete 技巧依然成立。

输入文本

fill 先清空再输入,type 在现有内容后追加。默认走 insertText(快),但很多带联想的字段只对真实按键有反应——这时加 --key-events

fill / type
$ chrome-use fill @e2 "hello"            # 清空后输入
$ chrome-use type @e2 " world"          # 不清空,直接追加
$ chrome-use type @e2 "hello" --clear   # type 也能先清空字段再输入
$ chrome-use type @e2 "hello" --delay 80  # 每个按键之间间隔 80ms

# 真实按键(非 insertText)——用于只认按键事件的联想/组合框,
# 例如输入邮编后自动补全市区(Google Places 一类)
$ chrome-use type @e5 "201-0001" --key-events

# 真实按键并回车提交候选(--enter 隐含 --key-events)——
# 用于异步联想 / 标签控件,例如掘金「添加标签」
$ chrome-use type @e6 "ChatGPT" --enter
ℹ️ --key-events 还是 --enter?
如果只想确认一个标签、且单纯输入不弹下拉,用 --enter 一步到位。 如果你更想从候选列表里挑一个,先 type … --key-events 触发下拉, 再 snapshot -i 找到候选并 click @ref
✅ --clear 与 --delay
--cleartype 也能先清空字段(不用再切回 fill); --delay <ms> 在按键之间插入固定间隔,模拟更慢、更真实的打字节奏。 两者都是各自独立的 flag,不会污染实际打进去的文本。
ℹ️ 页面改写了输入?type 会警告,fill 会报错
type 打完会回读字段:如果里面没有刚输入的文本,会打印 ⚠ 警告,引用写入的内容和字段当前的值 (退出码仍为 0,JSON 多出 readBack)。fill 校验失败时的报错同样引用两边的值, 例如 read back "" after writing "狛江市";若非 ASCII 字符全部消失而 ASCII 仍在, 会指出页面过滤了非拉丁输入(仅限拉丁 / 带掩码的字段),重打一遍没有用。正常字段上两者都能正确输入中日韩文, 这些提示针对的是会改写输入的页面。

字段内部编辑与带格式粘贴

fill 是整体替换,type 是追加。改一段写好的文字里的某一处、 或者只把光标放到某个位置继续写,这两个都做不到 —— 这是 select-text 的活。

select-text
$ chrome-use select-text @e3 "确认" --prefix "请"   # 选中「确认」,不含「请」
$ chrome-use select-text @e3 "Hi Sam," --cursor-after  # 只放光标
$ chrome-use type @e3 " 顺便说一句:"                # 从光标处继续输入
⚠️ 有多处匹配时它会拒绝,而不是替你挑第一个
prefix / suffix 是消歧用的上下文,不属于被选中的内容。 「找不到」「找到了但上下文对不上」「有 N 处」是三条不同的提示 —— 解法本来就不同。 Monaco / CodeMirror 有自己的选区模型,会被点名拒绝:在那上面做 DOM 选区看起来成功、实际没发生。

富文本编辑器里,敲字和粘贴的结果不一样type "<b>粗体</b>" 得到的是这几个字符,paste --format html 得到的才是粗体。换行同理 —— type 的换行会变成 Enter(在多数编辑器里等于提交或另起一块),paste 不会。

paste
$ chrome-use paste $'第一行\n第二行' --selector "#notes"  # $'...' 才是真换行
$ chrome-use paste "<b>粗体</b>文字" --format html --selector "#editor"
$ chrome-use paste "# 标题" --format md            # Markdown 源码按纯文本插入
✅ 全程不碰你的真实剪贴板
内容走的是合成 ClipboardEvent,不调用 navigator.clipboard,也不模拟 Ctrl+V。 这种事件是 untrusted 的、没有默认行为:监听 paste 的编辑器从自己的 handler 拿到内容, 不监听的则走一次真实插入 —— 回复里会写明走的是哪条。两条都没生效时它报错,不会打 ✓。

键盘

press 在当前焦点上按一个键(按下+抬起),也支持组合键。 keydown/keyup 把按下和抬起拆开,成对使用可以实现「按住」。

press / keydown / keyup
$ chrome-use press Enter                 # 在当前焦点按一个键,输出会报告落点
                                       # ✓ Pressed Enter → textarea[name="q"]
$ chrome-use press Enter --selector @e2  # 先聚焦目标再按(别名 --on)——每次调用
                                       # 都是独立进程,别假设焦点还在原处
$ chrome-use press Control+a             # 组合键
$ chrome-use keydown d                   # 按住某键不放(不自动抬起)
$ chrome-use keyup d                     # 抬起——成对用来「按住移动」
                                       # 游戏里:keydown d; sleep; keyup d
⚠️ 按键「确实什么都没做」时会有警告
对只靠页面 JavaScript 才有效果的键(文本框上的 Arrow/Home/End/PageUp/PageDown、Escape、表单外裸输入框上的 Enter), press 会探测从焦点元素、祖先到 document / window 有没有 keydown/keyup/keypress 监听器。 一个都没有时仍退出 0,但会打印 ⚠ 警告说明页面无法响应该键,建议直接 click 目标选项; JSON 多出 keyListeners: <count>。组合键(Ctrl/Meta+键)、Tab、Backspace、可打印字符和原生默认行为不探测。
ℹ️ 游戏 / Canvas 里更精确的按住
对 canvas / WebGL 应用,用 press d --hold 800 更好——按住时长在守护进程里计时, 比 keydown + shell sleep + keyup 每轮少 ~250ms 抖动。详见 Canvas 与游戏

勾选、选择与下拉

复选框用 check/uncheck。下拉:原生 <select>自定义组合框(react-select / ARIA 等)都可以用 select—— 自 v1.5.72 起,select 遇到非原生控件会自动走 portal-aware 逻辑(开控件→等选项→按可见文本点); pick 是同一能力的显式别名。 原生下拉使用平台自带的 value/selected setter,并同时触发 inputchange,因此 React/Vue 受控表单也会提交新状态。

check / select / pick
$ chrome-use check @e3                   # 勾选复选框
$ chrome-use uncheck @e3                 # 取消勾选

$ chrome-use select @e4 "Pageview"       # 原生 <select> 或自定义组合框(v1.5.72+)
$ chrome-use select @e4 "a" "b"          # 多选

# 任意组合框:打开它、等菜单出现(含 portal 渲染)、按可见文本匹配、
# 触发正确事件;若选项始终不出现会「报错」(不会静默无操作)
$ chrome-use pick @e4 --option "Europe"
✅ v1.5.72:原生命令扛 shadow-DOM 重站(#105)
以前 select 对自定义下拉会「返回 ✓ 却没变化」(静默假成功)——现已修复: 非原生控件走 portal-aware 逻辑,选项始终不出现时明确报错并列出可见选项,不再静默。 fill 同样加固:通过 Monaco 的全局或 AMD model API 做一次原子的 setValue,并在返回成功前精确回读 model。若应用隐藏了该 API,则执行一次可信的编辑器 paste,通过编辑器自己的 copy 处理器精确回读,并在结束后恢复浏览器剪贴板; 操作无法校验或内容不一致时会明确报错。@ref 落在包裹元素上的受控输入仍会自动下钻到内部 editable。
受 CSP 限制的页面
Cloudflare Zaraz 和启用严格内容安全策略的页面不能依赖 eval。 请使用原生 selectfill 命令,以免要求页面开放 unsafe-eval

点击之外的动作

有些控件不止能点:折叠块要展开、菜单按钮要弹出、数字框和滑块要按范围步进。 actions 告诉你这个元素此刻支持哪些, do 只执行其中之一。

actions / do
$ chrome-use actions @e15
@e15 DisclosureTriangle "Disclosure summary"
  expand

$ chrome-use do @e15 expand
✓ expand on @e15
now: collapse

动作集是实时读的,不是从快照里带出来的 —— 它是状态而不是身份:你截图时收着的折叠块现在可能已经开了, 这时候再给它 expand 就是让你做反。

集合之外的动作会被拒绝并附上支持列表,而不是勉强做点相近的事。 执行完会再报一次动作集,所以一个没动的控件不会读起来像成功。

ℹ️ 支持哪些
expand / collapse(折叠块)、showMenu(会弹出的控件)、 increment / decrement(有取值范围的控件)、 toggle(可按下的控件)。都由元素的无障碍属性推导, disabled 的元素不提供任何动作。

上传文件

upload 给文件 <input> 或拖拽/粘贴式的编辑器(如 X 的输入框)投喂文件。 它在扩展中继模式下也能工作。 React dropzone 消费文件后立即清空或替换隐藏 input 时,命令会成功并给出提示,不再误报页面拒绝了上传。

upload
$ chrome-use upload @e5 file1.pdf        # 上传文件
ℹ️ 为什么中继模式也能上传
chrome.debugger 禁止 setFileInputFiles,所以文件字节会被流式送进页面、 在页面里重建成一个 File(按 native-messaging 的 1 MiB 上限分块传输)。 因此文件 <input> 和 drop/paste 编辑器都能收到。

滚动

scroll 按方向滚动页面;scrollintoview 把某个元素滚进视口。 对跨源 iframe(支付 / 结算 / KYC 组件)里的内容,普通页面滚动够不着,用 --at x,y 在像素点上滚滚轮,或 --frame n 指定帧。

scroll / scrollintoview
$ chrome-use scroll down 500              # 滚动页面(up/down/left/right)
$ chrome-use scroll down 700 --at 640,400  # 在某像素点滚滚轮——能滚到普通页面
                                        # 滚不到的跨源 iframe(Stripe/结算/KYC)
$ chrome-use scroll down 700 --frame 2    # 滚 `chrome-use frames` 里的第 2 帧
$ chrome-use scrollintoview @e1           # 把元素滚进视口

拖拽与滑块验证码

drag 既能在两个 ref 之间拖放,也能把一个手柄按像素偏移拖动(滑块 / canvas)。 对无人值守登录常见的网易易盾滑块验证码,用 solve-slider 自动过。

drag / solve-slider
$ chrome-use drag @e1 @e2                # 拖放
$ chrome-use drag @e1 60                 # 把手柄拖 +60px(滑块/canvas);`+60,-3` 表示 dx,dy
$ chrome-use solve-slider                # 自动解页面上的网易易盾滑块验证码
$ chrome-use solve-slider 5              # ……最多重试 5 次(每次失败会刷新拼图)
ℹ️ solve-slider 是怎么过的
它按 URL 抓取验证码自己的背景图 + 拼图切片(不截图),离线用边缘 + 掩码互相关定位缺口, 再用拟人化、自校准的闭环轨迹把手柄拖进缺口——正是这段人类般的运动通过了易盾的行为检测。 float(内嵌)和 popup(模态,如知乎)两种模式都支持,在触发滑块的提交之后立即运行即可。 这个拖拽会强制启用 humanize 轨迹,不受全局 AGENT_BROWSER_HUMANIZE 影响。
⚠️ 还没覆盖的验证码
易盾的增强版滑块(图标形状拼块 + 干扰项)和点选(按顺序点击) 是更难的挑战,目前尚未处理。

下载

ab-connect 0.5.13 及以上版本会通过 Chrome downloads API 下载 HTTP(S) 链接。chrome-use 会先解析链接的 href,包括 snapshot -i 中能看到的动态 anchor,从而避免跨源 download 链接把当前标签页导航到媒体 URL。

bash
$ chrome-use download @e2 ./video.mp4
$ chrome-use download-url https://example.com/report.pdf ./report.pdf
$ chrome-use downloads --limit 10 --json
$ chrome-use downloads --clear

download-url 省略目标路径时保留 Chrome 的默认下载位置。 downloads --clear 只清除历史,不会删除文件。


跨源 iframe:始终按 ref 操作

内嵌的支付 / 结算 / KYC 组件(Google Payments、Stripe 等)是跨进程的 out-of-process iframe。 用 ref 驱动它们,永远不要靠截图。 snapshot -i 会穿透这些 iframe,按 @ref 列出里面的元素(含输入框的值); get text --all-frames 读它们的文本。拿到 ref 后照常操作即可。

在 iframe 里按 ref 操作
# snapshot -i 列出跨源 iframe 内的元素,然后:
$ chrome-use click @e
$ chrome-use type @e "…"
$ chrome-use hover @e
$ chrome-use dblclick @e
$ chrome-use drag @a @b

# 帧内的邮编/联想框——用真实按键:
$ chrome-use type @e "201-0001" --key-events
⚠️ 不要把 <iframe> 元素本身当作目标
Iframe ref 执行 focus / press,命中的是父文档里的 容器元素,按键会落到父页面而不是帧内的输入框。 现在这两个命令会返回一条指明帧边界的警告,而不是在错误目标上打一个干净的 。 请从帧内部操作:先用 chrome-use frames 列出所有帧,再 chrome-use frame <id> 并操作帧内部的 ref,或者用 chrome-use eval --frame <id> "…"
✅ 为什么按 ref 而不是坐标
在扩展中继模式下,这些操作是通过 DOM 在元素自己的帧里派发的, 所以能精准命中正确标签页里的正确元素。而坐标点击/滚动可能漂移到当前前台的其它标签页上—— 所以优先用 ref。帧内需要滚动到下方内容时,用 scroll down N --at x,y(帧上的一个像素点)或 --frame n
⚠️ find 够不到闭合 shadow root 或跨源 iframe
find/选择器匹配的是页面 DOM(querySelectorAll),所以对这两类元素会报 「Element not found」——即便 snapshot -i 能列出它们(它走 CDP 可访问性树,能穿透两者)、 get text 能读到。这时请用快照里的 @ref 定位,而不是 findbox @ref 可给出坐标兜底。另外,先核对准确的标签文案再断定它「不存在」: LinkedIn 的「Save」按钮实际标注为 收藏 而非 保存snapshot -i 一直显示着 button "收藏" [ref=eN],直接 click @eN 即可。

接下来

交互只是核心循环的一环,搭配这几页一起看: