dsh-browser-view 0.7.5 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,15 @@
1
1
  # dsh-browser-view
2
2
 
3
- DSH 插件:共享 Playwright 浏览器自动化 + 原生界面实时画面面板。一次安装,三件事:
3
+ DSH 插件 + 独立 MCP server:共享 Playwright 浏览器自动化 + 原生界面实时画面面板。一次安装,三件事(+DSH 之外任意 MCP 客户端可直连):
4
4
 
5
5
  | 模块 | 做什么 |
6
6
  | --- | --- |
7
7
  | 🧭 **浏览器自动化** | 共享 Playwright 浏览器:多标签、多 profile、隔离 context、下载/上传、Cookie、what 意图语义定位(`browser_click { what: "登录" }` 一次调用替代快照→查找→点击三轮,零命中返回相近候选 + SoM 看图兜底)、SoM 标徽点击(`mark: N`)、open Shadow DOM 组合树穿透、SPA 路由后 ref 软失效重锚定、点击效果验证(`effect: true`,点了没动静自动换候选)、跨 Frame/IFrame 穿透定位、视口感知动态快照(`viewportOnly`)、多模态 SoM 视口标徽(`som: true`)、批量表单智能填充(`browser_form_fill`)、增量快照(历史版本环形缓存)、抽取字段自动建议(action:suggest),38 个 `browser_*` 工具 |
8
8
  | 🖥️ **实时画面** | 注入 DSH 界面右下角悬浮面板:浏览器状态 + 实时缩略图,点开大屏模态框(CDP MJPEG 实时流,自动跟随活动标签换页/新开自愈,流不可用时回退轮询截图;访问历史 2 秒轮询);**双向协作**——agent 等待人工操作/敏感审批时显示交互横幅、执行点击/输入前目标元素琥珀色高亮预告(confirmMode:auto 时敏感动作由宿主层强制拦截弹审批,agent 无法绕过),**面板点选教学**(browser_pick_element:疑难元素用户在大图上点一下,直接拿到稳定 selector+指纹+ref),「最近回归」行与报告模态可一键重跑(agent 忙碌自动置灰) |
9
9
  | ✅ **回归测试闭环** | 录制回放 + 选择器自愈 + 参数化/数据驱动 + 失败自动重试(`retryFailed`)+ 视觉回归 + 运行历史/报告导出 + **一键导出原生 Playwright 规范代码(`action: "export"`)** + 面板一键重跑(`/runs/rerun`,agent 忙碌置灰) |
10
+ | 🌐 **MCP 接入** | 独立 stdio server(`dsh-browser-mcp`):Claude Desktop/ZCode/Cursor 等任意 MCP 客户端直连全部 38 个工具,不依赖 DSH;审批闸/录制/打点等安全语义与 DSH 宿主一致 |
10
11
 
11
- 设计原则:能做的一次性操作交给 agent 用 bash 完成,插件只做 bash 做不到的常驻浏览器自动化。唯一第三方运行时依赖是 `playwright-core`(复用系统已装的 Edge/Chrome,无需下载 Chromium),不依赖任何 `@deepseek-ai` 运行时包。
12
+ 设计原则:能做的一次性操作交给 agent 用 bash 完成,插件只做 bash 做不到的常驻浏览器自动化。运行时依赖 `playwright-core`(复用系统已装的 Edge/Chrome,无需下载 Chromium)与 `@modelcontextprotocol/sdk`(仅 MCP 入口使用,不进 DSH 插件加载链),不依赖任何 `@deepseek-ai` 运行时包。
12
13
 
13
14
  ## 安装
14
15
 
@@ -282,6 +283,37 @@ browser_confirm({
282
283
  - **实时缩略图**:浏览器打开时显示,点击弹出大屏模态框——2 秒轮询实时画面,下方列出**访问历史**(最近 50 条,点击可在新标签打开);
283
284
  - **最近回归**:面板底部一行显示最近一次 `browser_replay` / `browser_batch` 的结果(通过数 / 自愈数 / 时间)。
284
285
 
286
+ ## MCP 接入(独立 server,不依赖 DSH)
287
+
288
+ v0.8.0 起本包附带独立 MCP 入口 `dsh-browser-mcp`(stdio),DSH 之外的 MCP 客户端(Claude Desktop / ZCode / Cursor 等)可直接调用全部 38 个工具——走与 DSH 宿主**同一条** registerTools 装配链,审批闸/录制打点/时光机行为一致:
289
+
290
+ ```jsonc
291
+ // 客户端 mcpServers 配置示例
292
+ {
293
+ "mcpServers": {
294
+ "dsh-browser": {
295
+ "command": "npx",
296
+ "args": ["-y", "dsh-browser-view"],
297
+ "env": {
298
+ "DSH_BROWSER_HEADLESS": "true",
299
+ "DSH_BROWSER_USER_DATA_DIR": "~/.dsh-browser/mcp-profile",
300
+ "DSH_BROWSER_TOOLSET": "core"
301
+ }
302
+ }
303
+ }
304
+ }
305
+ ```
306
+
307
+ - 已安装本包时可直接用 `"command": "dsh-browser-mcp"` 或 `"dsh-browser-view"`(双 bin 别名);
308
+ - 配置走环境变量 `DSH_BROWSER_<大写蛇形>` → 同名配置键(`TIMEOUT_MS`→`timeoutMs`),`true/false` 转 boolean、纯数字转 number、列表键逗号分隔;校验与默认值复用同一套(见上方配置表,不新增配置键);
309
+ - 工具使用指引由 server `instructions` 下发(与 DSH systemPrompt 同源),`what:"登录"` 语义定位等用法完全一致;
310
+ - 工具元数据支持 MCP 2025 标准 `annotations`(`readOnlyHint` / `destructiveHint` 等属性);
311
+ - 工具返回同时带 text 渲染与 `structuredContent`;`browser_screenshot` 额外返回原生多模态 `ImageContent`(base64 内联 PNG),多模态客户端(如 Claude Desktop)可直接渲染截图。
312
+
313
+ **与 DSH 并行运行的注意**:两者是独立进程、各自独立浏览器会话——同一持久化 `userDataDir` 会因 Chromium 单例锁让后启动者失败,截图目录相同会写入交错,并行时务必给 MCP 配独立的 `DSH_BROWSER_USER_DATA_DIR` / `DSH_BROWSER_SCREENSHOT_DIR`。
314
+
315
+ **人机协作降级(Fast-fail)**:MCP 模式没有 DSH 前端面板,`confirmMode: auto` 时敏感操作与 `browser_confirm` 均**立即 fail-closed 抛错拒绝**(Fast-fail,不再无谓空转 60s);`browser_pick_element` 快速返回失败;需要人工登录或人机辅助的会话设 `DSH_BROWSER_HEADLESS=false`,用户直接在弹出的真实浏览器窗口操作。
316
+
285
317
  ## 开发
286
318
 
287
319
  ```sh
package/lib/index.js CHANGED
@@ -47,6 +47,150 @@ function resolveConfig(moduleName, schema, defaults, raw) {
47
47
  return config;
48
48
  }
49
49
  //#endregion
50
+ //#region lib/types/lib/schema.js
51
+ const BROWSER_CONFIG_SCHEMA = {
52
+ enabled: {
53
+ type: "boolean",
54
+ optional: true
55
+ },
56
+ channel: { type: "string" },
57
+ executablePath: { type: "string" },
58
+ headless: { type: "boolean" },
59
+ userDataDir: { type: "string" },
60
+ profiles: { type: "any" },
61
+ screenshotDir: { type: "string" },
62
+ downloadDir: { type: "string" },
63
+ workspaceDir: { type: "string" },
64
+ screenshotMaxAgeDays: {
65
+ type: "number",
66
+ min: 0
67
+ },
68
+ screenshotMaxCount: {
69
+ type: "number",
70
+ min: 0
71
+ },
72
+ basePath: { type: "string" },
73
+ recordBodies: { type: "boolean" },
74
+ maxTextChars: {
75
+ type: "number",
76
+ min: 1
77
+ },
78
+ maxLinks: {
79
+ type: "number",
80
+ min: 1
81
+ },
82
+ timeoutMs: {
83
+ type: "number",
84
+ min: 1
85
+ },
86
+ baselineDir: { type: "string" },
87
+ selectorHealing: { type: "boolean" },
88
+ healTimeoutMs: {
89
+ type: "number",
90
+ min: 100
91
+ },
92
+ maxRefs: {
93
+ type: "number",
94
+ min: 1
95
+ },
96
+ maxRows: {
97
+ type: "number",
98
+ min: 1
99
+ },
100
+ snapshotHistory: {
101
+ type: "number",
102
+ min: 1,
103
+ max: 20
104
+ },
105
+ confirmMode: {
106
+ type: "string",
107
+ enum: [
108
+ "off",
109
+ "manual",
110
+ "auto"
111
+ ]
112
+ },
113
+ confirmTimeoutMs: {
114
+ type: "number",
115
+ min: 1e3
116
+ },
117
+ sensitivePatterns: { type: "string[]" },
118
+ sensitiveTools: { type: "string[]" },
119
+ confirmPolicies: {
120
+ type: "any",
121
+ optional: true
122
+ },
123
+ humanize: {
124
+ type: "any",
125
+ optional: true
126
+ },
127
+ humanizeSpeed: {
128
+ type: "string",
129
+ enum: [
130
+ "slow",
131
+ "normal",
132
+ "fast"
133
+ ],
134
+ optional: true
135
+ },
136
+ confirmInReplay: { type: "boolean" },
137
+ somFallback: { type: "boolean" },
138
+ refReanchor: { type: "boolean" },
139
+ toolset: {
140
+ type: "string",
141
+ enum: ["core", "all"]
142
+ }
143
+ };
144
+ const BROWSER_DEFAULT_CONFIG = {
145
+ channel: "auto",
146
+ executablePath: "",
147
+ headless: false,
148
+ userDataDir: "",
149
+ profiles: {},
150
+ screenshotDir: ".dsh-browser/screenshots",
151
+ downloadDir: ".dsh-browser/downloads",
152
+ workspaceDir: "",
153
+ screenshotMaxAgeDays: 7,
154
+ screenshotMaxCount: 200,
155
+ basePath: "/browser",
156
+ recordBodies: true,
157
+ maxTextChars: 2e4,
158
+ maxLinks: 50,
159
+ timeoutMs: 3e4,
160
+ baselineDir: ".dsh-browser/baselines",
161
+ selectorHealing: true,
162
+ healTimeoutMs: 3e3,
163
+ maxRefs: 200,
164
+ maxRows: 1e3,
165
+ snapshotHistory: 5,
166
+ confirmMode: "off",
167
+ confirmTimeoutMs: 6e4,
168
+ sensitivePatterns: [
169
+ "提交",
170
+ "删除",
171
+ "支付"
172
+ ],
173
+ sensitiveTools: ["browser_click"],
174
+ confirmInReplay: true,
175
+ somFallback: true,
176
+ refReanchor: true,
177
+ toolset: "all"
178
+ };
179
+ //#endregion
180
+ //#region lib/types/lib/prompts.js
181
+ const FULL_TOOL_PROMPT = "浏览器工具操作一个共享浏览器会话,支持多标签页与多配置文件。定位操作元素最省事的方式是意图语义定位:browser_click/browser_type/browser_select 直接传 what(元素名称/label,如 what:'登录'),页内打分匹配并自动注册 ref,一次调用替代 快照→查找→点击 三轮;零命中报错会附最相近候选。需要精确控制时再用 selector(CSS)/ref(aria 快照)。打开页面后可先 browser_snapshot 读文本与链接、browser_elements 读表单结构(含关联 label 与展开的下拉选项);browser_find(query 传名称,只返回命中 ref+区域上下文,大页面最省 token;零命中会给相近候选)适合多目标排查,需要全页结构时再用 browser_snapshot { mode: \"aria\" }(可交互元素带 ref=eN,click/type/select/press 直接传 ref 代替 CSS 选择器);下拉选择(原生 select 与 antd/element 组件库)用 browser_select 传 selector/ref/what + option 文本一步完成;固定小序列(点击→等待→输入→提交)用 browser_batch 一次执行减少往返;标签管理用 browser_tabs;下载用 browser_download(wait=true 可等新下载);登录态用 browser_cookies。做自动化测试时:用 browser_wait_for 显式等待页面稳定/元素出现,用 browser_assert 断言(失败自动截图;visual={baseline} 可做视觉回归,先用 browser_screenshot { baseline } 建基线),用 browser_network 检查接口调用与 4xx/5xx,跑完一轮可用 browser_record save 保存操作序列、browser_replay 回放做回归(选择器失效自动按指纹自愈;vars/data 支持参数化与数据驱动)。每次 replay/batch 都记入运行历史,browser_report list/export 可查看与导出回归报告。抓取结构化数据用 browser_extract(fields 定义 CSS 字段,可翻页,可导出 CSV/JSONL)。遇到需要登录的网站:先 browser_open 打开登录页,告知用户请在浏览器窗口中完成登录,再调用 browser_wait_for_login 等待(可给 successSelector/successUrl);验证码/二次确认等更广的「人补一步」场景用 browser_pause_for_human(提示语会显示在实时画面面板);配置 userDataDir 时登录态会持久化。用户可通过界面右下角面板的实时画面查看浏览器。";
182
+ const CORE_TOOL_PROMPT = "浏览器工具操作一个共享浏览器会话(toolset=core:已精简工具集)。定位操作元素最省事的方式是意图语义定位:browser_click/browser_type/browser_select 直接传 what(元素名称/label,如 what:'登录'),一次调用替代快照→查找→点击;零命中报错附最相近候选。需要精确控制时再用 selector/ref:打开页面后用 browser_snapshot 读内容(scope 限定区域、since 增量重读省 token),browser_find(query 传名称,返回 ref),点击/输入/按键/下拉用 browser_click/browser_type/browser_press/browser_select(selector 或 ref);固定小序列用 browser_batch;等待用 browser_wait/browser_wait_for,断言用 browser_assert;接口检查用 browser_network;标签 browser_tabs;下载/上传 browser_download/browser_upload。需要人工:登录用 browser_wait_for_login,验证码/二次确认用 browser_pause_for_human,敏感动作(提交/删除/支付)前用 browser_confirm。环境异常先跑 browser_doctor 诊断。录制/回放/报告/抽取/profile/隔离 context 等工具当前未注册(toolset=core),需要时把配置改为 toolset: all 并重启 DSH。用户可通过界面右下角面板的实时画面查看浏览器。";
183
+ /** confirmMode 附加指引(与 DSH 入口同款语义,manual 面向宿主审批、auto 说明硬闸)。 */
184
+ function confirmGuidanceOf(confirmMode) {
185
+ if (confirmMode === "manual") return "本环境 confirmMode 为 manual:凡提交/删除/支付等不可逆或影响线上数据的敏感动作,执行前必须先调用 browser_confirm(reason 说明操作与影响,等待用户在实时画面面板点允许/拒绝;拒绝或超时即放弃该动作,不得换路径绕过)。";
186
+ if (confirmMode === "auto") return "本环境 confirmMode 为 auto:宿主层已在敏感动作(命中敏感模式的目标元素)前强制弹审批卡,agent 无法绕过;审批被拒绝或超时的动作不可换路径重试,应向用户说明并等待指示。";
187
+ return "";
188
+ }
189
+ /** 组装完整的工具使用指引文本(DSH systemPrompt 段落与 MCP instructions 共用)。 */
190
+ function browserSystemPrompt(config) {
191
+ return (config.toolset === "core" ? CORE_TOOL_PROMPT : FULL_TOOL_PROMPT) + confirmGuidanceOf(config.confirmMode);
192
+ }
193
+ //#endregion
50
194
  //#region lib/types/lib/http.js
51
195
  /** Parse the request URL pathname (query strings are ignored). */
52
196
  function urlPath(req) {
@@ -397,6 +541,10 @@ function snapshotVersionEntry(cache, version) {
397
541
  * 供实时画面面板展示「等待人工操作」提示。
398
542
  */
399
543
  let pauseState = null;
544
+ let interactivePanelPresent = true;
545
+ function isInteractivePanelPresent() {
546
+ return interactivePanelPresent;
547
+ }
400
548
  function setPauseInstruction(instruction) {
401
549
  pauseState = {
402
550
  instruction,
@@ -5699,6 +5847,7 @@ async function pauseForHumanTool(config, args) {
5699
5847
  async function confirmTool(config, args) {
5700
5848
  const reason = typeof args.reason === "string" ? args.reason.trim() : "";
5701
5849
  if (!reason) throw new Error("缺少 reason: 请说明要执行什么敏感操作、为什么需要审批(如「提交白名单表单,会产生线上数据变更」)");
5850
+ if (!isInteractivePanelPresent()) throw new Error("敏感操作审批失败: 当前环境无前端面板支持人工审批(已按安全策略 fail-closed 拒绝)");
5702
5851
  const timeoutMs = Math.min(Math.max(Number(args.timeoutMs ?? config.confirmTimeoutMs ?? 6e4) || 6e4, 1e3), 6e5);
5703
5852
  const id = `cfm-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
5704
5853
  setPendingConfirm({
@@ -5768,6 +5917,14 @@ const describePoint = injectWalker((payload, w) => {
5768
5917
  */
5769
5918
  async function pickToolOnPage(page, config, args) {
5770
5919
  const instruction = typeof args.instruction === "string" && args.instruction.trim() !== "" ? args.instruction.trim() : "请在实时画面大图上点击要教学的目标元素";
5920
+ if (!isInteractivePanelPresent()) return {
5921
+ picked: false,
5922
+ status: "missed",
5923
+ instruction,
5924
+ detail: "面板点选教学失败: 当前环境无前端面板供用户交互式点选",
5925
+ waitedMs: 0,
5926
+ ...await pageIdentity(page)
5927
+ };
5771
5928
  const timeoutMs = Math.min(Math.max(Number(args.timeoutMs ?? 3e5) || 3e5, 1e3), 6e5);
5772
5929
  const id = `pick-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
5773
5930
  const meta = screencastManager.getLastFrame()?.metadata ?? void 0;
@@ -9767,136 +9924,8 @@ function registerBrowserApi(ctx, config, base) {
9767
9924
  //#region lib/types/index.js
9768
9925
  const name = "dsh-browser-view";
9769
9926
  const inject = [];
9770
- const SCHEMA = {
9771
- enabled: {
9772
- type: "boolean",
9773
- optional: true
9774
- },
9775
- channel: { type: "string" },
9776
- executablePath: { type: "string" },
9777
- headless: { type: "boolean" },
9778
- userDataDir: { type: "string" },
9779
- profiles: { type: "any" },
9780
- screenshotDir: { type: "string" },
9781
- downloadDir: { type: "string" },
9782
- workspaceDir: { type: "string" },
9783
- screenshotMaxAgeDays: {
9784
- type: "number",
9785
- min: 0
9786
- },
9787
- screenshotMaxCount: {
9788
- type: "number",
9789
- min: 0
9790
- },
9791
- basePath: { type: "string" },
9792
- recordBodies: { type: "boolean" },
9793
- maxTextChars: {
9794
- type: "number",
9795
- min: 1
9796
- },
9797
- maxLinks: {
9798
- type: "number",
9799
- min: 1
9800
- },
9801
- timeoutMs: {
9802
- type: "number",
9803
- min: 1
9804
- },
9805
- baselineDir: { type: "string" },
9806
- selectorHealing: { type: "boolean" },
9807
- healTimeoutMs: {
9808
- type: "number",
9809
- min: 100
9810
- },
9811
- maxRefs: {
9812
- type: "number",
9813
- min: 1
9814
- },
9815
- maxRows: {
9816
- type: "number",
9817
- min: 1
9818
- },
9819
- snapshotHistory: {
9820
- type: "number",
9821
- min: 1,
9822
- max: 20
9823
- },
9824
- confirmMode: {
9825
- type: "string",
9826
- enum: [
9827
- "off",
9828
- "manual",
9829
- "auto"
9830
- ]
9831
- },
9832
- confirmTimeoutMs: {
9833
- type: "number",
9834
- min: 1e3
9835
- },
9836
- sensitivePatterns: { type: "string[]" },
9837
- sensitiveTools: { type: "string[]" },
9838
- confirmPolicies: {
9839
- type: "any",
9840
- optional: true
9841
- },
9842
- humanize: {
9843
- type: "any",
9844
- optional: true
9845
- },
9846
- humanizeSpeed: {
9847
- type: "string",
9848
- enum: [
9849
- "slow",
9850
- "normal",
9851
- "fast"
9852
- ],
9853
- optional: true
9854
- },
9855
- confirmInReplay: { type: "boolean" },
9856
- somFallback: { type: "boolean" },
9857
- refReanchor: { type: "boolean" },
9858
- toolset: {
9859
- type: "string",
9860
- enum: ["core", "all"]
9861
- }
9862
- };
9863
- const DEFAULT_CONFIG = {
9864
- channel: "auto",
9865
- executablePath: "",
9866
- headless: false,
9867
- userDataDir: "",
9868
- profiles: {},
9869
- screenshotDir: ".dsh-browser/screenshots",
9870
- downloadDir: ".dsh-browser/downloads",
9871
- workspaceDir: "",
9872
- screenshotMaxAgeDays: 7,
9873
- screenshotMaxCount: 200,
9874
- basePath: "/browser",
9875
- recordBodies: true,
9876
- maxTextChars: 2e4,
9877
- maxLinks: 50,
9878
- timeoutMs: 3e4,
9879
- baselineDir: ".dsh-browser/baselines",
9880
- selectorHealing: true,
9881
- healTimeoutMs: 3e3,
9882
- maxRefs: 200,
9883
- maxRows: 1e3,
9884
- snapshotHistory: 5,
9885
- confirmMode: "off",
9886
- confirmTimeoutMs: 6e4,
9887
- sensitivePatterns: [
9888
- "提交",
9889
- "删除",
9890
- "支付"
9891
- ],
9892
- sensitiveTools: ["browser_click"],
9893
- confirmInReplay: true,
9894
- somFallback: true,
9895
- refReanchor: true,
9896
- toolset: "all"
9897
- };
9898
9927
  function apply(ctx, rawConfig) {
9899
- const resolved = resolveConfig("dsh-browser-view", SCHEMA, DEFAULT_CONFIG, rawConfig);
9928
+ const resolved = resolveConfig("dsh-browser-view", BROWSER_CONFIG_SCHEMA, BROWSER_DEFAULT_CONFIG, rawConfig);
9900
9929
  if (resolved.enabled === false) return;
9901
9930
  const config = resolved;
9902
9931
  const base = (config.basePath ?? "/browser").replace(/\/+$/, "");
@@ -9912,11 +9941,11 @@ function apply(ctx, rawConfig) {
9912
9941
  const bindPrompt = (promptCtx) => {
9913
9942
  const systemPrompt = promptCtx.get("systemPrompt") ?? promptCtx.systemPrompt;
9914
9943
  if (systemPrompt === void 0) return;
9915
- const confirmGuidance = config.confirmMode === "manual" ? "本环境 confirmMode 为 manual:凡提交/删除/支付等不可逆或影响线上数据的敏感动作,执行前必须先调用 browser_confirm(reason 说明操作与影响,等待用户在实时画面面板点允许/拒绝;拒绝或超时即放弃该动作,不得换路径绕过)。" : config.confirmMode === "auto" ? "本环境 confirmMode 为 auto:宿主层已在敏感动作(命中敏感模式的目标元素)前强制弹审批卡,agent 无法绕过;审批被拒绝或超时的动作不可换路径重试,应向用户说明并等待指示。" : "";
9944
+ const toolPrompt = browserSystemPrompt(config);
9916
9945
  const sectionDispose = systemPrompt?.section?.({
9917
9946
  name: "tool:browser",
9918
9947
  order: 200,
9919
- text: (config.toolset === "core" ? "浏览器工具操作一个共享浏览器会话(toolset=core:已精简工具集)。定位操作元素最省事的方式是意图语义定位:browser_click/browser_type/browser_select 直接传 what(元素名称/label,如 what:'登录'),一次调用替代快照→查找→点击;零命中报错附最相近候选。需要精确控制时再用 selector/ref:打开页面后用 browser_snapshot 读内容(scope 限定区域、since 增量重读省 token),browser_find(query 传名称,返回 ref),点击/输入/按键/下拉用 browser_click/browser_type/browser_press/browser_select(selector 或 ref);固定小序列用 browser_batch;等待用 browser_wait/browser_wait_for,断言用 browser_assert;接口检查用 browser_network;标签 browser_tabs;下载/上传 browser_download/browser_upload。需要人工:登录用 browser_wait_for_login,验证码/二次确认用 browser_pause_for_human,敏感动作(提交/删除/支付)前用 browser_confirm。环境异常先跑 browser_doctor 诊断。录制/回放/报告/抽取/profile/隔离 context 等工具当前未注册(toolset=core),需要时把配置改为 toolset: all 并重启 DSH。用户可通过界面右下角面板的实时画面查看浏览器。" : "浏览器工具操作一个共享浏览器会话,支持多标签页与多配置文件。定位操作元素最省事的方式是意图语义定位:browser_click/browser_type/browser_select 直接传 what(元素名称/label,如 what:'登录'),页内打分匹配并自动注册 ref,一次调用替代 快照→查找→点击 三轮;零命中报错会附最相近候选。需要精确控制时再用 selector(CSS)/ref(aria 快照)。打开页面后可先 browser_snapshot 读文本与链接、browser_elements 读表单结构(含关联 label 与展开的下拉选项);browser_find(query 传名称,只返回命中 ref+区域上下文,大页面最省 token;零命中会给相近候选)适合多目标排查,需要全页结构时再用 browser_snapshot { mode: \"aria\" }(可交互元素带 ref=eN,click/type/select/press 直接传 ref 代替 CSS 选择器);下拉选择(原生 select 与 antd/element 组件库)用 browser_select 传 selector/ref/what + option 文本一步完成;固定小序列(点击→等待→输入→提交)用 browser_batch 一次执行减少往返;标签管理用 browser_tabs;下载用 browser_download(wait=true 可等新下载);登录态用 browser_cookies。做自动化测试时:用 browser_wait_for 显式等待页面稳定/元素出现,用 browser_assert 断言(失败自动截图;visual={baseline} 可做视觉回归,先用 browser_screenshot { baseline } 建基线),用 browser_network 检查接口调用与 4xx/5xx,跑完一轮可用 browser_record save 保存操作序列、browser_replay 回放做回归(选择器失效自动按指纹自愈;vars/data 支持参数化与数据驱动)。每次 replay/batch 都记入运行历史,browser_report list/export 可查看与导出回归报告。抓取结构化数据用 browser_extract(fields 定义 CSS 字段,可翻页,可导出 CSV/JSONL)。遇到需要登录的网站:先 browser_open 打开登录页,告知用户请在浏览器窗口中完成登录,再调用 browser_wait_for_login 等待(可给 successSelector/successUrl);验证码/二次确认等更广的「人补一步」场景用 browser_pause_for_human(提示语会显示在实时画面面板);配置 userDataDir 时登录态会持久化。用户可通过界面右下角面板的实时画面查看浏览器。") + confirmGuidance
9948
+ text: toolPrompt
9920
9949
  });
9921
9950
  if (sectionDispose !== void 0) promptCtx.effect(() => sectionDispose);
9922
9951
  };