关于 chrome-agent
chrome-agent 是供 Agent 使用的命令行浏览器自动化技能,可检查页面、操作元素并验证结果。它通过 verdict、投递检查和断言区分命令执行与页面确实发生变化。
主要功能
- 01浏览器导航与页面检查
- 02按 UID、CSS 选择器或坐标定位
- 03写入表单后回读实际值
- 04提供 verdict 和指针投递检查
- 05断言页面状态与内容
适用场景
- 01自动执行登录与表单提交
- 02检查页面是否达到预期状态
- 03诊断点击是否被覆盖层拦截
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sderosiaux/ chrome-agent --skill chrome-agent安装后在 Claude Code 中输入/chrome-agent确认已加载。
详细介绍
chrome-agent — 浏览器自动化
which chrome-agent || npm install -g chrome-agent # or: cargo install chrome-agent
阅读响应
每个有变更的操作都会附带 verdict + verdict_reason + next。每个指针定位操作还会附带 delivery(即将派发事件的坐标处的命中测试);若其他元素接收了事件,还会附带 intercepted_by。每次写入都会在 value.requested 旁附带 value.actual,并在 observed_after_ms 之后读取回显;fill、select、check/uncheck 使用相同字段和时间窗口。
ok:true 表示命令已运行,不代表页面已按要求执行。读取 verdict,并在报告成功前读取任何写入操作后的 value.verbatim。根据 next 分支处理,而不是根据 verdict 的文字。 next 是以下六个固定值之一:proceed inspect retry confirm dismiss stop
verdict | verdict_reason | next | 该怎么做 |
|---|---|---|---|
changed | tree_delta | proceed | 页面发生了变化;delta 会说明具体变化。 |
changed | nodes_moved | proceed | 节点未变,只是顺序调整了(拖动操作已生效)。 |
changed | focus_only | proceed | 只有焦点发生了移动,且移到了真实元素上——对于没有命中测试的路径,这是操作已送达的唯一迹象。focus.to 通常是你点击元素的可聚焦祖先;请读取 uid,确认你指向的元素。焦点移至文档时,则会得到 identical_tree。 |
changed | value_kept | proceed / 检查 | 回读时元素仍保留该值,但变化无法通过 delta 显示(秘密内容会渲染为固定标记;首次操作没有可比较的树)。fill、select 和 check/uncheck 都会落入此行;证据是 value.verbatim。这是唯一一个 next 不只取决于原因的行:如果页面读取也失败,则会得到 inspect——字段已确认,但从未看到周围的页面。 |
changed | values_lost | 确认 | 页面发生了变化,且清空了一个原本有值的字段;values_lost:[{uid,role,name,was}] 会列出每个字段。提交后清空与未发送就丢弃,表现完全相同。重新填写前,请用 assert text --contains 或 network 确认——再次提交可能会重复发送。 |
navigated | document_replaced | 检查 | 文档已更换。所有已存储的 uid 都已失效。 |
intercepted | hit_test_receiver | 关闭 | 另一个元素占据了目标点并接收了事件;intercepted_by 会列出该元素的信息(tag/id/class/uid/z_index/text/modal)。目标元素的情况未知。这是你实际会遇到的情况:在 61 个真实网站中发生了 8 次拦截,全部属于这一种。请读取 tag/id/class,而非 z_index——8 次中有 7 次的值都是 auto。 |
intercepted | modal_dialog | 关闭 | 通过 showModal() 打开的 <dialog>(或全屏元素)占据了顶层,并接收其外部的所有事件。请先按 Escape。此情况很少见——在 61 个真实网站中发生了 0 次:modal 的值是 el.matches(':modal'),因此大多数网站使用的 <div role="dialog"> 覆盖层实际会归为 hit_test_receiver。 |
not_kept | value_reverted | 停止 | 写入已送达元素,但之后元素中什么都没有。请读取 value.actual。不要再次填写——相同的写入会得到相同的结果。 |
not_kept | value_rewritten | 停止 | 元素中保留了其他内容——例如掩码、修剪器或规范化器的结果。请读取 value.actual,判断这是否是你想要的值。 |
no_effect | delivered_no_change | 确认 | 已证明事件送达目标,且在 observed_after_ms 时间内页面树保持不变。这是能给出的最强结论。重复操作是唯一无济于事的做法。 |
unchanged | identical_tree | 确认 | 工具观察期间页面树完全相同。尚未证明事件已送达。 |
unknown | no_baseline | 检查 | 这是此页面上的首次操作;没有可供比较的基准。 |
unknown | read_failed | 检查 | 操作已执行;但之后读取页面失败。 |
unknown | identity_unreadable | 检查 | 两棵树可能不属于同一文档。 |
unknown | aim_point_off_target | 检查 | 两次读取结果一致,但仍无法将目标点瞄准目标元素——未分发任何事件,且此次偏差是稳定的,完全相同的重试仍会同样偏离。aim 会说明两种情况的区别:目标点在屏幕上,但位于元素自身边框之外(换行的行内框、被裁剪的容器)→ 瞄准具有边框的子元素;目标点在视口之外(固定的遮挡层、滚动已锁定)→ 改变页面状态,滚动无法使其移动。 |
unknown | scroll_not_settled | 重试 | 两次读取的目标点不一致:它仍在移动。未分发任何事件,此次偏差是暂时的,重试即可解决。请先 wait,然后重试。 |
not_checked | reporting_disabled | proceed | 你传入了 --verdict off。没有报告是你自己的选择,并非页面所致。 |
delivery 适用于以指针为目标的操作(click、dblclick、check/uncheck 点击),并决定以下两个强判断是否成立: |
delivery | 含义 | 许可 |
|---|---|---|
target_hit | 目标点落在目标、其后代、其标签对应的控件或其影子宿主上。 | 唯一允许得出 no_effect 的值。 |
intercepted | 目标点落在目标扁平子树之外的元素上。 | → verdict: intercepted;intercepted_by 指明接收元素。 |
off_target | 无法确定目标点,且两次探测结果一致:目标点位于元素自身客户端矩形之外,或位于视口之外且固定在那里。 | 未派发任何操作,因此重复操作不会造成重复效果;但偏移位置稳定,因此操作也不会成功。所以应 inspect。 |
not_settled | 两次探测结果不一致:稳定等待预算耗尽时,目标点仍在移动。 | 未派发任何操作,偏移是暂时的,因此安全的做法是重试,且这能解决问题。所以应 retry。 |
js | 通过 JS click()/MouseEvent 执行,不进行命中测试。 | 不适用拦截,而非未检测到拦截。不许可任何结论。 |
not_probed | 未运行命中测试,或测试结果不涵盖相关文档(目标位于 iframe 内)。 | 没有证据。不许可任何结论。 |
--on-intercept 有三种模式。dispatch(默认)会通过任何接收元素派发操作,这正是指针的行为。refuse 遇到任何拦截都会报错。guard 会通过非活动接收元素派发操作,并拒绝看起来像控件的接收元素;它会读取同一次探测调用返回的 intercepted_by.actionable:原生交互标签、ARIA 交互角色、tabIndex >= 0 或 cursor: pointer。在 guard 模式下,<iframe> 接收元素总会被拒绝——其内容不透明,因此只能假定它是非活动的。任何拒绝都会返回 ok:false(退出码 1),字段与派发时相同,另有 dispatched:false;凡是确定了目标点却未派发操作的响应都会包含此字段;next 会提示 dismiss,重新确定目标点不会导致重复操作。
绝不要报告工具未确认的成功。 被覆盖层吞掉的点击也会表现为 unchanged:重复前先用 assert 确认,因为重复操作会再执行一次真实点击。unknown 表示工具无法比较,绝不表示“什么都没发生”——运行一次 inspect,然后根据所见继续;唯一例外是 scroll_not_settled,此时未派发任何操作。waited_ms 仅在操作后等待页面加载时出现。Chrome 未在 8 s 内确认的指针事件会返回 ok:false,而不会一直等待 --timeout;该事件可能已经到达页面,因此应运行 inspect,而不是重试。click、dblclick、hover 和 drag 会先将页面置于前台(后台标签页会在 5 s 定时器上响应指针事件)。
两个盲区,重试都无法解决。 观察窗口固定为 60 ms(observed_after_ms):它能捕捉微任务队列、setTimeout(0) 或动画帧中的还原,但无法捕捉 400 ms 后清除字段的验证器——对此应先 wait,再 assert value。Canvas、WebGL 和仅由 CSS 产生的效果在无障碍树中不可见:类、透明度、变换和重绘都会表现为 unchanged 或 no_effect,因此应通过 screenshot 或 eval 确认,绝不要再点击一次。
工作流:inspect → act → assert
chrome-agent goto https://app.com/login --inspect # uids
chrome-agent fill --uid n52 "[email protected]" # value: what the page kept
chrome-agent click n63 # verdict + delivery + next
chrome-agent assert text --contains "Dashboard" # exit 0 = evidence you may quote
当 verdict 为 navigated、next 提示 inspect,以及任何 SPA 路由变更之后(back、forward、会触发重新渲染的点击),都应重新检查——uid 会重新分配。对于 SPA 详情页,优先使用 goto <direct-url>,而不是可能打开模态框的 click。
assert:退出代码就是答案
先证明最终状态,再报告任务已完成。唯一可以引用的证据是退出代码 0。
chrome-agent assert value --selector "#email" --equals "[email protected]"
chrome-agent assert text --selector "#status" --contains "Shipped"
chrome-agent assert url --matches "/orders/[0-9]+$"
chrome-agent assert state --selector "#terms" --checked # native OR aria-checked
chrome-agent assert state --uid n15 --selected "California" # a dropdown's current option
chrome-agent assert state --selector "#submit" --disabled # :disabled OR aria-disabled
chrome-agent assert state --selector ".modal" --visible # rendered, opaque, not hidden
chrome-agent assert exists --selector ".result" --count 10 # --min N, or --count 0 for absence
chrome-agent assert value --selector "#quantity" --equals "1" --within 5
对于延迟结果,请使用 --within N(正整数秒);否则断言只检查一次。
只有读取操作会重复执行,首次匹配的观察结果即视为成功。这不能证明状态稳定。
JSON 会添加 assertion.wait:within_ms、elapsed_ms、observations、timed_out。等待期限届满时,退出代码为 2,
并附上最后一次比较结果。目标不可读、读取受阻和连接中断仍返回退出代码 1,
并提供 error、wait 和可选的 last_observation。缺失的值/文本/状态目标仍属于错误;请先用 assert exists --within N 等待目标出现。管道和宏可在断言命令中接受
"within":5。该时间窗口限制整个读取过程;--timeout 仍会限制单次 CDP 调用。
- 0 条件成立 · 2 条件不成立(报告或修复) · 1 检查无法完成:没有浏览器、选择器未匹配到任何内容、正则表达式无法解析、CDP 超时。
macro run守卫或discover step断言不成立时,也会退出 2。 标志无效时退出 1。在pipe/batch中,断言本身没有退出代码:它会返回ok:false和相同的assertion对象;batch因断言而停止时,退出代码为 1,而不是 2。 assert是读取操作:不会报告变更、不会给出判断,也绝不会点击。密钥会参与比较,但不会打印(响应会给出两者的长度)。--checked读取与check/uncheck应用相同的分类,--selected读取与select使用相同的结果,因此断言不会与操作结果冲突。--matches使用 Rust 正则表达式:\d、\w、\s仅匹配 ASCII 字符,不支持\p{...}和环视;使用(?i)可忽略大小写。--contains是区分大小写的纯子字符串匹配。
目标定位有三种模式:uid(来自 inspect,例如 n47,在同一页面的多次检查中保持稳定)、--selector(CSS)、--xy(坐标)。每个定位目标的操作都会返回实际解析出的 uid,即使你使用 --selector 定位,也应将其与 delta 交叉核对。
click --selector 与 click <uid> 是同一种操作:两者都会解析视口中心并分发原生输入,因此,若按钮被 Cookie 横幅遮挡,使用 --selector 定位该按钮时会点击横幅。这正是指针的行为,也正是 intercepted_by 所说明的内容。
命令
# Navigation
chrome-agent goto <url> [--inspect] [--wait-for "selector"] [--header "K: V"]
chrome-agent back [--inspect] # answers {url, title}
chrome-agent forward [--inspect] # at either end: a message, no url
chrome-agent scroll down|up|<uid>
chrome-agent wait network-idle [--idle-ms N] [--timeout N] # deterministic SPA/XHR settle
chrome-agent wait text|url|selector "pattern" [--timeout N]
chrome-agent history [--filter "pattern"] [--limit N]
# Inspection — narrowing flags change what is PRINTED only; `diff` always compares the full tree
chrome-agent inspect [--max-depth N] [--filter "button,link"] [--uid nN] [--urls]
chrome-agent inspect --filter "article" --scroll --limit 50 # infinite-scroll feeds
chrome-agent inspect --max-chars 4000 [--offset 4000] # cap/page huge trees
chrome-agent diff # what changed since last inspect
# Pointer actions — uid, --selector or --xy
chrome-agent click <uid> [--inspect]
chrome-agent click --xy 100,200 # or --selector "css"
chrome-agent dblclick <uid> [--inspect]
chrome-agent hover <uid>
chrome-agent drag <from-uid> <to-uid> # mouse events; NOT HTML5 DnD
# Writes
chrome-agent fill --uid <uid> <value> [--secret] # or --selector "css"
chrome-agent fill-form n20="[email protected]" n30="password" # one value report per field
chrome-agent type "text" [--selector "input.search"] [--secret]
# --secret reports lengths instead of the value. It only ADDS to what the element declares;
# there is no flag that turns redaction off.
chrome-agent press Enter|Tab|Escape # a single printable char types it
chrome-agent select --uid <uid> "Option text" # option.value first, then visible text
chrome-agent check <uid> | chrome-agent uncheck <uid> # idempotent; refuse what they cannot check
chrome-agent upload --uid <uid> /path/to/file.pdf # --selector too (file inputs hide from a11y)
# Reads
chrome-agent read [--truncate N] # articles, via Readability
chrome-agent text [--selector "main"] [--truncate N] # visible text
chrome-agent extract [--selector "css"] [--limit N] [--scroll] [--a11y]
chrome-agent eval "JSON.stringify(...)" [--selector "h1"]
chrome-agent network [--filter "pattern"] [--body] [--limit N] # already loaded, stealth-safe
chrome-agent network --live 5 --body --filter "graphql" # capture live traffic
chrome-agent network --abort "*tracking*" --live 30 # blocking; start before navigating
chrome-agent console [--level error] [--clear]
# --body with --filter fetches every match whatever its MIME; without --filter, textual types only
# Files — written 0600 under ~/.chrome-agent/tmp; path on stdout, never base64
chrome-agent screenshot [--format jpeg] [--quality N] [--max-width N] [--uid nN|--selector "css"]
chrome-agent pdf [--filename name] [--landscape] [--background]
chrome-agent download <url> [--out path] [--max-bytes N] # in-page fetch, keeps the login
chrome-agent download <--uid nN|--selector "css"> [--out path] # CLICK it, capture the download
# Device metrics — explicit values, scoped to one named page, no preset catalog
chrome-agent --page mobile emulate device --label "checkout phone" --width 412 --height 915 --dpr 2.625 --mobile --touch
chrome-agent --page mobile emulate status
chrome-agent --page mobile emulate reset
# Persisted per named page, reapplied on each connection; a Chrome restart discards it. Acting on
# an emulated page activates its tab and backgrounds siblings. Under --touch, click/check tap.
# WebMCP tools — document.modelContext.getTools()/.executeTool()
chrome-agent webmcp list # name, description, inputSchema
chrome-agent inspect # baseline first, like any action
chrome-agent webmcp call add_to_cart --args '{"item":"Espresso Blend"}'
# The protocol defines no outputSchema, so the accessibility-tree diff is the only corroboration
# for what a tool DECLARED: one that declares success and moves nothing reads
# unchanged/identical_tree. Under `frame` the response carries frame_scoped:true — an empty list
# there is unproven, not "none". Most installs lack native WebMCP: use --chrome-arg above.
# Multi-step — pipe keeps one connection, so uids stay valid across commands
echo '[{"cmd":"goto","url":"..."},{"cmd":"inspect"},{"cmd":"click","uid":"n12"}]' | chrome-agent --json batch
echo '{"cmd":"goto","url":"...","inspect":true}' | chrome-agent pipe
# Iframes — the frame switch lives on the connection, so use pipe/batch
printf '%s\n' \
'{"cmd":"frame","target":"iframe[src*=\"checkout\"]"}' \
'{"cmd":"inspect"}' \
'{"cmd":"fill","uid":"n42","value":"..."}' \
'{"cmd":"frame","target":"main"}' | chrome-agent pipe
# Separate CLI calls (frame then inspect) do NOT work — each is a fresh connection.
# frame scopes eval + inspect, NOT --selector targeting. Act by uid after switching.
# Session, tabs, and paths that already worked
chrome-agent status # browsers, pids, orphan= lines
chrome-agent tabs
chrome-agent macro list # named paths, with their guards
chrome-agent macro check checkout --var [email protected] # validate offline before opening Chrome
chrome-agent macro run checkout --var [email protected] # stops at the first failed guard
chrome-agent replay recording.jsonl # a pipe --record file, no guards
chrome-agent close [--purge] [--orphans] # --orphans: browsers no session claims
宏。 macro record <name> --from-recording <file>(或在刚执行过相关操作的管道会话中使用 {"cmd":"macro","action":"record","name":"x"})会提炼操作、等待、断言、读取、下载和受支持的上下文。发现快照、差异和历史记录会被丢弃。若存在失败或未送达的步骤,或不受支持的定位器,整个录制都会被拒绝,且不会保存或替换宏。派生的防护条件包括 delivery: target_hit、verdict: changed|navigated、verbatim: true、downloaded: true 和针对路径的 url_matches。通过 uid 指定目标的步骤会记录为角色 + 可访问名称,否则会被拒绝。机密字段会成为声明的参数,且永不保存,因此 macro run 在缺少 --var 时会拒绝执行。若防护条件不成立,运行会停止,并指出步骤索引、防护条件、观察到的情况和操作自身的 next,同时以 stopped_by: "guard" 和退出码 2 结束——这与断言失败使用相同的代码,因为两者属于同类声明。若运行因其他原因停止(步骤失败、页面无法读取或不存在该宏),则以 stopped_by: "error" 和退出码 1 结束。无法做出任何保证的步骤会标记为 unguarded,并计入两份报告——在信任绿色运行结果之前,请先查看此数字。显式断言和等待都计为检查。
macro check 会离线准备所有命令、参数和防护条件;macro run 会在执行任何操作前进行相同的准备。多个输入可重复使用 --var;逗号会保留在值中。替换只会对命令和防护条件中的字符串值执行一次。CSS、JavaScript 和实时页面条件会在重放期间检查。使用 --json 可获取每个已完成的 steps[].result 和停止步骤的 result,其中包含数据、断言证据和下载路径。声明为机密的输入会被隐藏。任务成功需要调用方提供显式的结果断言。
可在新对话中保留的发现记录
探索陌生任务且可能需要由另一个进程继续时,请使用 discover。推理由你负责;CLI 会持久化目标、假设、未知项和命令响应。它不会根据浏览器命令成功就推断任务已完成。
chrome-agent --json discover start discovery.json --goal "Read the entry page" --url https://example.com --max-commands 10
chrome-agent --json discover step discovery.json --proposal '{"id":"open","revision":0,"reason":"Observe the entry page","command":{"cmd":"goto","url":"https://example.com"},"checks":[{"cmd":"assert","what":"exists","selector":"h1"}]}'
chrome-agent --json discover show discovery.json
chrome-agent --json discover export discovery.json --name entry-page --steps open
后续提案请使用返回的修订版本,以及启动时提供的浏览器/页面名称。具有相同 ID 的相同提案会检索先前的回执(replayed:true);同一 ID 下有变化的提案会被拒绝。进程丢失会留下结果不确定的待处理实验。请读取记录并提交新的观察;不要盲目重复旧步骤。
初始配置允许使用同源的 goto、inspect、read、text、extract、assert 和 wait。首次实验必须导航至入口 URL。默认限制为 50 条命令和 1,800 秒,其中包括调用间隔。检查以及失败或未解决的尝试都会消耗预算。超过 64 KiB 的响应会以结果不确定告终;请缩小下一次读取的范围。文件包含原始输入和观察结果,并保存在本地。网站 JavaScript 仍可能造成外部影响;此命令配置不是用于运行不可信操作方案的沙箱。
导出会按顺序选取成功的实验,要求包含起始导航和最终断言,拒绝文档 uid,并创建新的宏名称。它会保留参数模板,但不包含输入默认值。候选宏仍需使用新输入进行重放,并独立检查结果。旧版 macro run 不会强制执行发现记录的来源或预算。若观察到命令/检查,discover step 以 0 退出;若断言不成立,则以 2 退出;若出现错误或结果不确定,则以 1 退出。start、show 和 export 都不会打开 Chrome。
全局标志
可放在动词的任意一侧。有两个例外:--timeout 和 --max-depth。声明了这些标志的命令,必须将其放在动词之后(wait selector ".x" --timeout 5);其他情况下,必须将其放在动词之前。在未声明该标志的命令中传入该标志,会报退出码为 1 的用法错误,并指出正确的调用方式。
--stealth # 7 anti-detection CDP patches
--json # structured JSON on stdout; errors exit 1 with {"ok":false,...} still on stdout
--browser <name> # isolate parallel agents — sharing "default" corrupts sessions
--page <name> # named tabs
--max-depth N # limit inspect tree depth
--verdict MODE # auto (default): report what changed. off: report the action only
--budget N # cap the change report, in chars (default 1200; 0 = uncapped)
--on-intercept M # dispatch (default) | guard | refuse
--timeout N # deadline on every CDP call (default 30s)
--headed # show the window
--connect URL # attach to a real Chrome (DataDome/Kasada)
--proxy-server URL # http(s)/socks4/5://host:port; launch-only
--copy-cookies # use cookies from your real Chrome profile
--chrome-arg FLAG # extra flag for the launched Chrome (repeatable); launch-only
--dialog MODE # accept (default) | dismiss | manual · --dialog-text for prompt()
--chrome-arg 仅适用于 chrome-agent 启动 Chrome 的情况(使用 --connect 时会拒绝该标志,而非忽略),并且在命名浏览器的整个生命周期内都固定不变;要修改这些参数,请关闭或清除该浏览器。以下参数会被直接拒绝:--user-data-dir、--remote-debugging-port、--remote-debugging-pipe、--proxy-server(请使用全局参数)、--headless(请使用 --headed)。
退出码:0 成功 · 1 错误(包括错误的标志,以及因 batch --stop-on-error 而停止)· 2 断言(包括 discover step)或 macro run 守卫条件未满足 · 130 Ctrl+C。JS 对话框会自动确认,因此页面永远不会卡住。
机器人防护
| 网站防护 | 解决方案 | 测量结果 |
|---|---|---|
| 无 | chrome-agent goto ... | — |
| Cloudflare JS 挑战(“Just a moment…”) | chrome-agent --stealth goto ... | shop.app:不使用时为 403 / 6 个节点,使用时为 200 / 真实页面 |
| Cloudflare 托管式 Turnstile | --stealth 无法通过 | nowsecure.nl:使用和不使用时均为 10 个节点 |
| 已登录的网站(X、Gmail) | chrome-agent --stealth --copy-cookies goto ... | — |
| DataDome/Kasada | google-chrome --remote-debugging-port=9222 &,然后运行 chrome-agent --connect http://127.0.0.1:9222 goto ... | leboncoin.fr:使用和不使用 --stealth 时均为 403 |
上述两种 Cloudflare 防护是不同的产品:“Just a moment…” 是七项补丁可以应对的 JS 挑战,而托管式 Turnstile 会对浏览器进行指纹识别,二进制文件中的补丁无法改变这一点。对于这种情况以及 DataDome,唯一的办法是通过 --connect 连接到真实的 Chrome。导航后请阅读页面内容,不要想当然地认为标志已生效。
响应格式(--json)
Success: {"ok":true, ...}
Error: {"ok":false, "error":"message", "hint":"what to do next"}
-
goto→{"url","title","landed":{"requested","final","redirected","http_status","serving","challenge_from"?}}。 检查redirected:过期会话跳转到登录页面,否则会被视为加载成功。仅片段和尾随斜杠不同不算重定向。http_status是最终跳转的状态码;浏览器未报告时不提供(绝不会是 0)。重定向到/login、/signin、/auth、/sso时,会根据 URL 猜测并添加hint。goto不包含判定结果。 -
serving表示实际响应方,是分支判断的依据。serving含义 操作 page没有测量结果与加载状态相矛盾 继续 challenge存在反机器人供应商的框架或脚本,且网站没有自己的表单; challenge_from指明供应商主机使用 --connect连接真实的 Chrome——--stealth无法绕过这些防护error服务器返回了 4xx/5xx;查看 http_status修正 URL(404)、进行身份验证(401/403),或等待(429/5xx) nothing_actionable没有链接、表单控件或脚本,文本也几乎为空 放弃前先运行 inspectunreadable形状探测未运行 inspectpage表示没有反面证据,并非保证:付费墙、Cookie 墙和未知验证码供应商都显示为page。nothing_actionable是测量结果,并不表示你被拦截——边缘服务返回的拒绝页面和尚未完成渲染的页面看起来完全相同。 -
click/fill/select/check/… →{"message", "uid", "verdict", "verdict_reason", "next", "verdict_hint"?, "changed":{"added","removed","changed","unchanged","moved","anonymous", "document_changed","identity_known"}, "delta":"+ uid=n88 heading \"已保存\""},焦点发生变化时还会包含"focus":{"from","to"}(有意不计为内容变化)。 -
fill还会返回"value":{"requested","actual","verbatim","observed_after_ms","caveat"?}。对于机密字段(type=password,或autocomplete指明密码、银行卡、CVC、一次性验证码),则改为报告{"redacted":true,"requested_length","actual_length","verbatim"}。写入内容超过maxlength时会提供caveat。fill-form/fill_and_submit会返回"values":[...],每个字段各一项,并以相同方式隐藏机密值;对于fill_and_submit,这是唯一的凭据,因为变更报告是在表单提交后生成的。 -
select→"value"包含选项文本;check/uncheck→"value"为checked/unchecked(原生复选框处于混合状态时为indeterminate)。两者都会在顶层提供observed_after_ms,如果页面在该时间窗口内恢复原状,两者都会拒绝操作(退出码非零)。如果元素原本就处于目标状态,则不会提供value——没有分发任何事件,因此该响应会返回no_baseline/identical_tree,也不能证明此操作改变了什么。 -
以指针为目标的操作会返回
"delivery"、"aim":[x,y],以及在被拦截时返回"intercepted_by":{...}。 -
inspect --max-chars→{"total_chars","truncated","next_offset"}·read/text→{"text"}·eval→{"result"}·network→{"requests":[...]}·console→{"messages":[...]}·batch→{"results":[...]}·screenshot/pdf→{"path"}·download <url>→{"via":"fetch","downloaded","path","bytes","mime"}。 -
download --uid/--selector会点击并捕获浏览器原生下载——这是获取页面自行构建(Blob)或通过 POST 提供、且没有任何锚点指向的文件的唯一途径。根据downloaded判断,绝不要根据ok判断:点击已成功但未生成文件时,会返回ok:true和downloaded:false,并附带禁止重试的提示。--timeout限制整个时间窗口(开始和结束);超过--max-bytes时会取消下载并删除不完整文件。目标必须且只能指定一个——URL、--uid或--selector;未指定或指定两个时,会在解析浏览器前将其视为用法错误,向 stderr 输出信息并以退出码 1 退出。
低成本读取:选择合适的方式
遇到看起来像列表的内容时,优先使用 extract。以下数据是在 Hacker News 首页测得的;它们会随页面内容变化,因此仅供相对参考。
| 工具 | 适用场景 | HN 上的 Token 数 |
|---|---|---|
extract | 重复记录:网格、信息流、表格、结果。无需选择器。 | 全部 30 条约 1,570 |
read | 文章、博客文章、产品页面(Mozilla Readability) | 约 950 |
text --selector "main" | 指定范围内的可见文本 | 未指定范围时约 1,040 |
network --filter "api" | 页面通过 XHR 获取数据时——直接提取载荷 | 通常最干净 |
eval "JSON.stringify(...)" | 记录视图未包含的属性 | 不定 |
inspect --filter "button,link" | 查找要操作的内容,而非读取内容 | 约 1,735 |
inspect | 包含 uid 的完整树 | 约 5,650 |
extract --scroll 会滚动页面,并通过 MutationObserver 等待延迟加载的内容;extract --a11y
适用于 DOM 没有稳定结构的 React SPA(X.com)。
控制预算:优先用 inspect 而非 screenshot(截图不提供 uid,因此仍需检查);
使用 --filter "button,link"、--max-depth 2、read --truncate 1000、
text --selector "main" --truncate 500 缩小范围;--budget N 会限制变更报告,
--verdict off 则会完全移除操作后的读取,也会移除本文开头所述的所有保证;多步流程请使用
pipe/batch——只需一次连接,uid 始终有效,还能省去每条命令约 12 毫秒的开销
(读取流的实测速度提升 1.5 倍,填写和点击提升 1.1 倍)。使用它是为了获得稳定的 uid,
而不是为了速度。