dsh-browser-view 0.2.0 → 0.3.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.
@@ -1,4 +1,4 @@
1
- import type { Page } from "playwright-core";
1
+ import type { BrowserContext, Page } from "playwright-core";
2
2
  import type { BrowserConfig, ProfileState, DownloadEntry, FormField, NavEntry, RefEntry, ReplayStep, SelectorHint } from "./types.js";
3
3
  /** 当前活动 profile 名(默认 "default")。 */
4
4
  export declare let currentProfile: string;
@@ -14,6 +14,15 @@ export declare function setPageRefs(page: Page, refs: Map<string, RefEntry>): vo
14
14
  export declare function pageRef(page: Page, ref: string): RefEntry | undefined;
15
15
  /** 清空页面的 ref 表(导航/新快照时调用)。 */
16
16
  export declare function clearPageRefs(page: Page): void;
17
+ /** 取页面当前完整 ref 表(browser_find 增量续号合并用;无则空 Map)。 */
18
+ export declare function pageRefsOf(page: Page): Map<string, RefEntry>;
19
+ export interface SnapshotCache {
20
+ version: number;
21
+ text: string;
22
+ aria: string;
23
+ }
24
+ export declare function snapshotCacheOf(page: Page): SnapshotCache | undefined;
25
+ export declare function setSnapshotCache(page: Page, cache: SnapshotCache): void;
17
26
  /** 已保存的录制脚本:name → 步骤数组。 */
18
27
  export declare const savedRecordings: Map<string, {
19
28
  steps: ReplayStep[];
@@ -30,7 +39,10 @@ export interface TestCall {
30
39
  /** 录制时采集的选择器指纹(回放自愈用;register 包装层异步补齐)。 */
31
40
  meta?: {
32
41
  hint?: SelectorHint;
42
+ source?: "human";
33
43
  };
44
+ /** source: "human" 表示这条来自用户在浏览器中的手动操作(混合录制)。 */
45
+ source?: "human";
34
46
  }
35
47
  export declare function isRecording(): boolean;
36
48
  export declare function startRecording(): void;
@@ -41,7 +53,52 @@ export declare function lastRecordingOf(): TestCall[] | null;
41
53
  export declare function recordCall(call: TestCall): TestCall;
42
54
  /** 操作轨迹(按时间倒序,最新在前)。 */
43
55
  export declare function testLogOf(): TestCall[];
56
+ export declare function setPauseInstruction(instruction: string): void;
57
+ export declare function clearPauseInstruction(): void;
58
+ export declare function currentPause(): {
59
+ instruction: string;
60
+ startedAt: number;
61
+ } | null;
62
+ /** 一次待审批的敏感操作(browser_confirm 等待期间供面板展示与提交决策)。 */
63
+ export interface PendingConfirm {
64
+ id: string;
65
+ reason: string;
66
+ tool?: string;
67
+ args?: Record<string, any>;
68
+ startedAt: number;
69
+ decision?: "allowed" | "denied";
70
+ }
71
+ export declare function setPendingConfirm(entry: PendingConfirm): void;
72
+ export declare function getPendingConfirm(): PendingConfirm | null;
73
+ export declare function clearPendingConfirm(): void;
74
+ /** 面板提交审批决策;id 不匹配(已被新审批取代/超时清理)时返回 false。 */
75
+ export declare function resolveConfirm(id: string, decision: "allowed" | "denied"): boolean;
44
76
  export declare function clearTestLog(): void;
77
+ /** window 上的回传 binding 名(须为合法 JS 标识符)。 */
78
+ export declare const HUMAN_BINDING = "__dshBrowserHumanEvent";
79
+ /** agent 即将/刚刚执行了 DOM 操作(register 包装层调用);1.5s 内的页内事件视为工具回声。 */
80
+ export declare function noteAgentAction(): void;
81
+ /** 导出供测试:距上次 agent 操作是否在去重窗口内。 */
82
+ export declare function agentActionRecent(now?: number, windowMs?: number): boolean;
83
+ /**
84
+ * 页内人工操作事件 → 录制步骤(纯函数,导出供测试)。
85
+ * payload 由 installHumanRecorder 生成:{ kind, selector, value?, testid?, role?, name?, id?, text?, tag? }。
86
+ * 无法映射(缺 selector / 未知 kind / 空输入)返回 null。
87
+ */
88
+ export declare function humanEventToStep(payload: unknown): {
89
+ tool: string;
90
+ args: Record<string, any>;
91
+ meta: {
92
+ hint: SelectorHint;
93
+ source: "human";
94
+ };
95
+ } | null;
96
+ /**
97
+ * 页内注入的人工操作监听(addInitScript 序列化;每次导航自动重装)。
98
+ * 监听常驻但 node 侧只在录制中生效,关闭录制等价于"移除"。
99
+ * 只上报可交互元素上的 click 与表单控件的 change,避免逐键噪音。
100
+ */
101
+ export declare function installHumanRecorder(): void;
45
102
  /** 网络请求/响应记录。 */
46
103
  export interface NetworkEntry {
47
104
  url: string;
@@ -71,6 +128,14 @@ export declare function loadPlaywright(): Promise<typeof import("playwright-core
71
128
  export declare function launchProfile(name: string, config: BrowserConfig): Promise<void>;
72
129
  /** 当前 profile 状态(未启动时创建空状态)。 */
73
130
  export declare function stateOf(): ProfileState;
131
+ export interface NamedContext {
132
+ context: BrowserContext;
133
+ pageIds: Set<number>;
134
+ createdAt: number;
135
+ }
136
+ export declare function namedContextsOf(): Map<string, NamedContext>;
137
+ /** 把一个已 attach 的页面注册进 profile 标签表并激活,返回分配的 tab id。 */
138
+ export declare function registerTabPage(state: ProfileState, page: Page): number;
74
139
  /** 新建一个页面(当前 profile 会话未启动时先启动)。 */
75
140
  export declare function newPage(config: BrowserConfig): Promise<Page>;
76
141
  /** 返回当前活动页面(没有则新建),并保证浏览器已启动。 */
@@ -1,6 +1,6 @@
1
1
  import type { Locator, Page } from "playwright-core";
2
2
  import type { ToolRunContext } from "../lib/types.js";
3
- import type { BrowserConfig, ReplayStep, SelectorHint } from "./types.js";
3
+ import type { BrowserConfig, RefEntry, ReplayStep, SelectorHint } from "./types.js";
4
4
  import { type RunRecord } from "./reports.js";
5
5
  /** 读取 PNG 文件头(IHDR)中的宽高,不解码像素(导出供测试)。 */
6
6
  export declare function pngSize(buf: Buffer): {
@@ -32,27 +32,94 @@ export declare function openTool(config: BrowserConfig, args: Record<string, any
32
32
  * 只折叠完全相同的相邻行,不重排、不删改非重复内容。
33
33
  */
34
34
  export declare function compressText(text: string): string;
35
+ /** 最长公共子串长度(小写比较;零依赖,导出供测试)。 */
36
+ export declare function lcsScore(a: string, b: string): number;
37
+ /** 零命中时按相似度挑 top5 候选(导出供测试)。 */
38
+ export declare function topSuggestions(query: string, candidates: {
39
+ role: string;
40
+ name: string;
41
+ }[], count?: number): {
42
+ role: string;
43
+ name: string;
44
+ score: number;
45
+ }[];
46
+ /**
47
+ * 为命中元素增量分配 ref:沿用既有注册表的编号继续(不与上次快照冲突),
48
+ * 合并写回;返回带 ref 的命中列表(上限 maxRefs,超出标 truncated)。
49
+ * 导出供测试(纯逻辑,不依赖浏览器)。
50
+ */
51
+ export declare function assignRefs(existing: Map<string, RefEntry>, matches: {
52
+ role: string;
53
+ name: string;
54
+ selector: string;
55
+ ancestors: string[];
56
+ }[], maxRefs: number): {
57
+ withRefs: {
58
+ ref: string;
59
+ role: string;
60
+ name: string;
61
+ selector: string;
62
+ ancestors: string[];
63
+ }[];
64
+ merged: Map<string, RefEntry>;
65
+ truncated: boolean;
66
+ };
67
+ /**
68
+ * 语义元素定位:按名称(或 id/data-testid)在可交互元素中查找,只返回命中的
69
+ * ref + 区域上下文,不输出全量 aria 树 —— 大页面定位单个目标时 token 开销
70
+ * 远小于 browser_snapshot { mode: "aria" }。返回的 ref 可直接用于
71
+ * click/type/select/press。零命中不抛错,附最相近候选供二次定位。
72
+ */
73
+ export declare function findTool(config: BrowserConfig, args: Record<string, any>): Promise<{
74
+ scanned: number;
75
+ url: string;
76
+ title: string;
77
+ truncated?: true | undefined;
78
+ suggestions?: {
79
+ role: string;
80
+ name: string;
81
+ score: number;
82
+ }[] | undefined;
83
+ found: number;
84
+ matches: {
85
+ ref: string;
86
+ role: string;
87
+ name: string;
88
+ ancestors: string[];
89
+ }[];
90
+ }>;
91
+ /**
92
+ * 行级 diff:掐掉公共前缀/后缀,返回变化区。导出供测试。
93
+ * 全等返回 { unchanged: true };否则 changedFrom 为新文本中变化区首行(1 起)。
94
+ */
95
+ export declare function diffLines(oldText: string, newText: string): {
96
+ unchanged: boolean;
97
+ changedFrom?: number;
98
+ lines?: string[];
99
+ removed?: number;
100
+ };
35
101
  export declare function snapshotTool(config: BrowserConfig, args: Record<string, any>): Promise<{
36
102
  url: string;
37
103
  title: string;
38
- tree: string;
104
+ scope?: string | undefined;
105
+ unchanged: boolean;
106
+ contentVersion: number;
107
+ } | {
108
+ url: string;
109
+ title: string;
39
110
  truncated: boolean;
40
- refs: number;
41
- text?: undefined;
42
- links?: undefined;
43
- inputs?: undefined;
111
+ scope?: string | undefined;
112
+ diff: boolean;
113
+ contentVersion: number;
114
+ changedFrom: number | undefined;
115
+ removed: number | undefined;
44
116
  } | {
45
117
  url: string;
46
118
  title: string;
47
- text: string;
48
119
  truncated: boolean;
49
- links: {
50
- text: string;
51
- href: string;
52
- }[];
53
- inputs: number;
54
- tree?: undefined;
55
- refs?: undefined;
120
+ scope?: string | undefined;
121
+ stale?: boolean | undefined;
122
+ contentVersion: number;
56
123
  }>;
57
124
  /**
58
125
  * 解析点击/输入目标为 Locator:selector 直译;ref 走注册表——
@@ -117,6 +184,95 @@ export declare function reloadTool(config: BrowserConfig): Promise<{
117
184
  url: string;
118
185
  title: string;
119
186
  }>;
187
+ /** 一项诊断结果(ok=false 时附 advice 修复建议)。导出供测试。 */
188
+ export interface DoctorCheck {
189
+ name: string;
190
+ ok: boolean;
191
+ detail: string;
192
+ advice?: string;
193
+ }
194
+ export declare function setDoctorProbe(fn: ((basePath: string) => Promise<{
195
+ ok: boolean;
196
+ detail: string;
197
+ advice?: string;
198
+ }>) | null): void;
199
+ /** 目录可写性检查(mkdir + 写探针文件 + 删除)。导出供测试。 */
200
+ export declare function checkWritableDir(dir: string): {
201
+ ok: boolean;
202
+ detail: string;
203
+ };
204
+ /** 可执行文件存在性检查(executablePath 显式配置用)。导出供测试。 */
205
+ export declare function checkExecutable(path: string): DoctorCheck;
206
+ /**
207
+ * 命名隔离上下文:同一浏览器进程内独立 cookies/storage 的"标签组"。
208
+ * 页注册进 profile 标签表(所有既有工具直接可用),context 只做归属记录:
209
+ * list / new(name?, url?) / switch(激活该组的一页,没有则新开) / close(整组关闭)。
210
+ * 持久模式(userDataDir)浏览器只有单一 context,不支持本工具(用 profiles 替代)。
211
+ */
212
+ export declare function contextTool(config: BrowserConfig, args: Record<string, any>): Promise<{
213
+ contexts: {
214
+ name: string;
215
+ pages: number;
216
+ active: boolean;
217
+ createdAt: number;
218
+ }[];
219
+ profile: string;
220
+ closed?: undefined;
221
+ remainingTabs?: undefined;
222
+ } | {
223
+ url: string;
224
+ title: string;
225
+ navigated?: string | undefined;
226
+ created: string;
227
+ contexts?: undefined;
228
+ profile?: undefined;
229
+ closed?: undefined;
230
+ remainingTabs?: undefined;
231
+ } | {
232
+ url: string;
233
+ title: string;
234
+ switched: string;
235
+ contexts?: undefined;
236
+ profile?: undefined;
237
+ closed?: undefined;
238
+ remainingTabs?: undefined;
239
+ } | {
240
+ url: string;
241
+ title: string;
242
+ switched: string;
243
+ pages: number;
244
+ contexts?: undefined;
245
+ profile?: undefined;
246
+ closed?: undefined;
247
+ remainingTabs?: undefined;
248
+ } | {
249
+ closed: string;
250
+ remainingTabs: number;
251
+ contexts?: undefined;
252
+ profile?: undefined;
253
+ }>;
254
+ /**
255
+ * 页内拾取器(evaluate 注入):一次性 click 捕获 —— 点击任意元素时阻止默认行为,
256
+ * 计算稳定 CSS 路径 + 元素信息存 window.__dshPickResult(面板轮询读取后自清)。
257
+ * 与 aria walker 的 cssPath 同一套语义(#id → [data-testid] → nth-of-type 链)。
258
+ */
259
+ export declare function pickElementInjector(): void;
260
+ /** 读取并清空拾取结果(ui.ts /pick GET 调用;无结果返回 null)。 */
261
+ export declare function takePickResult(page: Page): Promise<Record<string, unknown> | null>;
262
+ /**
263
+ * 操作预告高亮:agent 即将 click/type/select/press 的元素在页面上琥珀色描边
264
+ * ~1.5 秒。异步不阻塞(fire-and-forget,任何失败静默),不影响操作时序。
265
+ */
266
+ export declare function previewHighlight(page: Page | null, selector: string, label: string): void;
267
+ /** 一次性健康诊断:不启动浏览器,只做静态/本地检查。 */
268
+ export declare function doctorTool(config: BrowserConfig, _args: Record<string, any>, exec?: ToolRunContext): Promise<{
269
+ checks: DoctorCheck[];
270
+ summary: {
271
+ ok: boolean;
272
+ passed: number;
273
+ failed: number;
274
+ };
275
+ }>;
120
276
  export declare function statusTool(): Promise<{
121
277
  open: boolean;
122
278
  profile: string;
@@ -293,6 +449,42 @@ export declare function waitForLoginTool(config: BrowserConfig, args: Record<str
293
449
  title: string;
294
450
  loggedIn: boolean;
295
451
  }>;
452
+ /**
453
+ * 人工接管等待(通用版 wait_for_login):agent 把控制权交还给用户(验证码 /
454
+ * 二次确认 / 人工复核等)。instruction 会显示在实时画面面板;完成条件三选一:
455
+ * successSelector(元素出现)/ successUrl(URL 正则)/ 两者都不给则以
456
+ * URL 变化或页面内容签名变化视为完成。轮询间隔 2 秒,超时返回 completed:false
457
+ * (不抛错)。导出 runPauseOnPage 供测试注入 fake page。
458
+ */
459
+ export declare function runPauseOnPage(page: Page, args: Record<string, any>): Promise<{
460
+ completed: boolean;
461
+ waitedMs: number;
462
+ detail: string;
463
+ }>;
464
+ /** 人工接管等待工具入口(缺 instruction 在启动浏览器前即校验,失败更快)。 */
465
+ export declare function pauseForHumanTool(config: BrowserConfig, args: Record<string, any>): Promise<{
466
+ url: string;
467
+ title: string;
468
+ completed: boolean;
469
+ status: "completed" | "timedOut";
470
+ instruction: string;
471
+ detail: string;
472
+ waitedMs: number;
473
+ }>;
474
+ /**
475
+ * 敏感操作审批:agent 在执行提交/删除/支付类动作前调用,把审批请求推到实时
476
+ * 画面面板,挂起等待用户点「允许/拒绝」。超时视为拒绝(fail-closed)。
477
+ * 用户允许时返回 { decision: "allowed", ... };拒绝/超时**抛错**(消息含原因),
478
+ * 使 live 调用与 replay/batch 步骤统一按失败处理,agent 不应继续敏感动作。
479
+ * 决策通过 session.resolveConfirm(面板 POST)写入。
480
+ */
481
+ export declare function confirmTool(config: BrowserConfig, args: Record<string, any>): Promise<{
482
+ decision: "allowed";
483
+ id: string;
484
+ reason: string;
485
+ waitedMs: number;
486
+ detail: string;
487
+ }>;
296
488
  /** 断言:条件在 timeoutMs 内成立则通过,否则失败并自动截图存证据。visual 断言走像素比对。 */
297
489
  export declare function assertTool(config: BrowserConfig, args: Record<string, any>, exec?: ToolRunContext): Promise<{
298
490
  url: string;
@@ -31,6 +31,18 @@ export interface BrowserConfig {
31
31
  maxRefs?: number;
32
32
  /** browser_extract 单次最多聚合的行数(默认 1000)。 */
33
33
  maxRows?: number;
34
+ /**
35
+ * 敏感操作审批模式:off(默认,行为与 v0.2 一致,工具仍可手动调用)|
36
+ * manual(systemPrompt 指示 agent 在提交/删除/支付类动作前调用 browser_confirm)。
37
+ */
38
+ confirmMode?: "off" | "manual";
39
+ /** browser_confirm 等待人工决策的默认超时毫秒(默认 60000)。 */
40
+ confirmTimeoutMs?: number;
41
+ /**
42
+ * 工具集:all(默认,全部工具)| core(只注册高频核心集,低频的
43
+ * 录制/回放/报告/抽取/profile 等不注册,降 systemPrompt 常驻 token)。
44
+ */
45
+ toolset?: "core" | "all";
34
46
  }
35
47
  /** 一个命名 profile 的会话状态。 */
36
48
  export interface ProfileState {
@@ -86,6 +98,7 @@ export interface ReplayStep {
86
98
  args: any;
87
99
  meta?: {
88
100
  hint?: SelectorHint;
101
+ source?: "human";
89
102
  };
90
103
  }
91
104
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-browser-view",
3
- "version": "0.2.0",
4
- "description": "DSH 插件:共享 Playwright 浏览器自动化(31 个 browser_* 工具,含 ARIA ref 交互、批处理、视觉回归、选择器自愈、参数化回放、结构化抽取与回归报告)+ 原生界面实时画面面板 + 官方设置弹窗内的浏览器录制管理。仅依赖 playwright-core,不依赖任何 @deepseek-ai 运行时包。",
3
+ "version": "0.3.0",
4
+ "description": "DSH 插件:共享 Playwright 浏览器自动化(36 个 browser_* 工具,含语义元素查找、批处理、视觉回归、选择器自愈、参数化回放、结构化抽取、回归报告与隔离 context)+ 人机协作(人工接管等待、敏感操作审批、人机混合录制、面板双向化:点选元素/操作预告高亮/内嵌报告)+ 实时画面面板与录制管理。仅依赖 playwright-core,不依赖任何 @deepseek-ai 运行时包。",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/types/index.d.ts",