dsh-browser-view 0.3.3 → 0.4.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.
@@ -9,6 +9,12 @@ export declare function loadRecordings(): number;
9
9
  export declare function saveRecordingFile(name: string, steps: ReplayStep[]): string;
10
10
  /** 删除磁盘上的录制文件;返回是否存在并删除。 */
11
11
  export declare function deleteRecordingFile(name: string): boolean;
12
+ /**
13
+ * 磁盘热重载:录制文件被外部编辑(手修 JSON)后,同会话内 browser_replay 仍用内存旧缓存。
14
+ * 比较文件 mtime 与内存 savedAt(留 2s 余量容忍写入延迟),更新过则重新加载进内存表。
15
+ * 返回是否发生了重载;文件不存在或损坏时静默保留内存版。
16
+ */
17
+ export declare function reloadRecordingIfChanged(name: string): boolean;
12
18
  /** 录制列表(面板 / browser_record list 共用)。 */
13
19
  export declare function listRecordings(): Array<{
14
20
  name: string;
@@ -21,6 +21,13 @@ export interface RunRecord {
21
21
  durationMs: number;
22
22
  /** data 数据集模式时的行号(单次运行为 undefined)。 */
23
23
  row?: number;
24
+ /** retryFailed 自动重试统计(F2:attempted 总尝试次数,recovered 经重试挽回的步骤数)。 */
25
+ retries?: {
26
+ attempted: number;
27
+ recovered: number;
28
+ };
29
+ /** 面板一键重跑产生的运行所引用的原 runId(F2)。 */
30
+ retryOf?: string;
24
31
  results: RunStepResult[];
25
32
  }
26
33
  /** 报告存储目录($DSH_HOME/.dsh-browser/reports)。 */
@@ -16,13 +16,37 @@ export declare function pageRef(page: Page, ref: string): RefEntry | undefined;
16
16
  export declare function clearPageRefs(page: Page): void;
17
17
  /** 取页面当前完整 ref 表(browser_find 增量续号合并用;无则空 Map)。 */
18
18
  export declare function pageRefsOf(page: Page): Map<string, RefEntry>;
19
- export interface SnapshotCache {
19
+ /** 一个历史版本的快照内容(text/aria 两种模式共用同一条版本序列)。 */
20
+ export interface SnapshotEntry {
20
21
  version: number;
21
22
  text: string;
22
23
  aria: string;
23
24
  }
25
+ /**
26
+ * F3 增量快照历史:per-page 环形数组保存最近 K 个版本(K = snapshotHistory,
27
+ * 默认 5,上限 20;text 模式 ≤20KB/版本 → 内存上界 ≈100KB/page)。
28
+ * 任意 since ∈ [latest-K+1, latest] 都能 diff;更旧版本回落全量标 stale。
29
+ */
30
+ export interface SnapshotCache {
31
+ /** 最新版本号(单调递增,不因环形淘汰回退)。 */
32
+ version: number;
33
+ /** 最近 K 个版本,按 version 升序;长度 ≤ K。 */
34
+ history: SnapshotEntry[];
35
+ }
24
36
  export declare function snapshotCacheOf(page: Page): SnapshotCache | undefined;
25
37
  export declare function setSnapshotCache(page: Page, cache: SnapshotCache): void;
38
+ /** 环形容量钳制(1..20,非法值回落默认 5)。导出供测试。 */
39
+ export declare function snapshotCapacity(k: unknown): number;
40
+ /**
41
+ * 追加一个新版本并淘汰超限的最旧版本(纯逻辑,导出供测试)。
42
+ * cache 为 undefined 时从 version 1 开始;返回新缓存(不修改入参)。
43
+ */
44
+ export declare function pushSnapshotVersion(cache: SnapshotCache | undefined, content: {
45
+ text: string;
46
+ aria: string;
47
+ }, capacity: unknown): SnapshotCache;
48
+ /** 按版本号取历史条目;不在环形窗口内(更旧/未知)返回 undefined。 */
49
+ export declare function snapshotVersionEntry(cache: SnapshotCache | undefined, version: number): SnapshotEntry | undefined;
26
50
  /** 已保存的录制脚本:name → 步骤数组。 */
27
51
  export declare const savedRecordings: Map<string, {
28
52
  steps: ReplayStep[];
@@ -74,6 +98,12 @@ export declare function clearPendingConfirm(): void;
74
98
  /** 面板提交审批决策;id 不匹配(已被新审批取代/超时清理)时返回 false。 */
75
99
  export declare function resolveConfirm(id: string, decision: "allowed" | "denied"): boolean;
76
100
  export declare function clearTestLog(): void;
101
+ /** live 工具调用开始(register 包装层调用)。 */
102
+ export declare function noteToolStart(): void;
103
+ /** live 工具调用结束(成功/失败都算结束)。 */
104
+ export declare function noteToolEnd(): void;
105
+ /** 当前在途的 live 工具调用数(0 = agent 空闲)。 */
106
+ export declare function activeToolCalls(): number;
77
107
  /** window 上的回传 binding 名(须为合法 JS 标识符)。 */
78
108
  export declare const HUMAN_BINDING = "__dshBrowserHumanEvent";
79
109
  /** agent 即将/刚刚执行了 DOM 操作(register 包装层调用);1.5s 内的页内事件视为工具回声。 */
@@ -97,8 +127,10 @@ export declare function humanEventToStep(payload: unknown): {
97
127
  * 页内注入的人工操作监听(addInitScript 序列化;每次导航自动重装)。
98
128
  * 监听常驻但 node 侧只在录制中生效,关闭录制等价于"移除"。
99
129
  * 只上报可交互元素上的 click 与表单控件的 change,避免逐键噪音。
130
+ * F5 收敛:cssPath 等元素描述来自 walker.ts 单一来源(构建期拼装注入),
131
+ * 不再持有内联副本;payload 字段语义与 v0.3 完全一致。
100
132
  */
101
- export declare function installHumanRecorder(): void;
133
+ export declare const installHumanRecorder: (payload: unknown) => void;
102
134
  /** 网络请求/响应记录。 */
103
135
  export interface NetworkEntry {
104
136
  url: string;
@@ -149,7 +181,7 @@ export declare function tabList(): Promise<{
149
181
  title: string;
150
182
  }[]>;
151
183
  export declare function closeBrowser(): Promise<void>;
152
- /** Best-effort current page identity, safe when nothing is open. */
184
+ /** Best-effort current page identity, safe when nothing is open.title 有界防页面忙时挂起。 */
153
185
  export declare function pageIdentity(page: Page): Promise<{
154
186
  url: string;
155
187
  title: string;
@@ -144,6 +144,8 @@ export declare function pressTool(config: BrowserConfig, args: Record<string, an
144
144
  title: string;
145
145
  pressed: string;
146
146
  }>;
147
+ /** 给本身无超时的 Playwright 调用(evaluate/allTextContents)加有界保护,防页面主线程忙时无限挂起。 */
148
+ export declare function withTimeout<T>(p: Promise<T>, ms: number, what: string): Promise<T>;
147
149
  /**
148
150
  * 下拉选择原语:原生 <select> 走 selectOption;组件库下拉(antd/element/listbox)
149
151
  * 走「点击触发框 → 等待可见选项 → 按文本点击」。找不到选项时抛出错误并附当前可见
@@ -488,6 +490,33 @@ export declare function confirmTool(config: BrowserConfig, args: Record<string,
488
490
  waitedMs: number;
489
491
  detail: string;
490
492
  }>;
493
+ /** auto 模式默认敏感文本模式(提交/删除/支付类)。 */
494
+ export declare const DEFAULT_SENSITIVE_PATTERNS: string[];
495
+ /** auto 模式默认参与判定的工具(已决:只拦 browser_click,type 误报率高)。 */
496
+ export declare const DEFAULT_SENSITIVE_TOOLS: string[];
497
+ /** 敏感判定的元素描述(与 walker describeElement 字段子集对应)。 */
498
+ export interface SensitiveDesc {
499
+ role: string;
500
+ name: string;
501
+ text: string;
502
+ id: string;
503
+ testid: string;
504
+ tag: string;
505
+ }
506
+ /**
507
+ * 纯逻辑:元素描述命中了哪些敏感模式(role/name/text/id/testid/tag 拼接后
508
+ * 子串匹配,大小写不敏感;空白模式忽略)。返回命中的模式列表(空 = 放行)。
509
+ * 导出供测试。
510
+ */
511
+ export declare function sensitiveHits(desc: SensitiveDesc | null, patterns: string[]): string[];
512
+ /**
513
+ * auto 模式审批闸:命中敏感模式时经 confirmTool 弹审批等待用户决策,
514
+ * 拒绝/超时抛错(调用方该步失败),允许则返回继续执行原工具。
515
+ * 不满足前置条件(非 auto / 工具不在 sensitiveTools / 无模式 / 无活动页 /
516
+ * 无目标 selector / 页内取描述失败)一律直接放行,由原工具自行处理错误。
517
+ * page 参数仅供测试注入;缺省取当前活动页。
518
+ */
519
+ export declare function confirmGate(config: BrowserConfig, tool: string, args: Record<string, any>, page?: Page | null): Promise<void>;
491
520
  /** 断言:条件在 timeoutMs 内成立则通过,否则失败并自动截图存证据。visual 断言走像素比对。 */
492
521
  export declare function assertTool(config: BrowserConfig, args: Record<string, any>, exec?: ToolRunContext): Promise<{
493
522
  url: string;
@@ -551,6 +580,11 @@ export declare const SELECTOR_STEP_TOOLS: Set<string>;
551
580
  /**
552
581
  * 采集选择器指向元素的指纹(页内 evaluate;找不到元素返回 null)。
553
582
  * 指纹是回放自愈的降级候选来源:testid / role+name / text / id / tag+text。
583
+ * F5 收敛:元素描述(role/name/text)改用 walker 单一来源
584
+ * (describeElement),与 aria 快照/find/录制回声共用同一套语义;相比旧
585
+ * 内联副本,role 推断更完整(slider/spinbutton/searchbox/heading 等),
586
+ * name 补齐 labelledby/label[for]/title 兜底 —— 指纹只作自愈降级候选,
587
+ * 语义增强只会让自愈命中率更高,不改变任何主链路行为。
554
588
  */
555
589
  export declare function captureSelectorHint(page: Page, selector: string): Promise<SelectorHint | null>;
556
590
  /** 由指纹生成自愈降级候选(按优先级排序;导出供测试)。 */
@@ -607,10 +641,12 @@ export declare function recordTool(args: Record<string, any>): Promise<{
607
641
  steps?: undefined;
608
642
  recordings?: undefined;
609
643
  }>;
610
- /** 回放选项(自愈开关与单次尝试超时,来自 config)。 */
644
+ /** 回放选项(自愈开关与单次尝试超时,来自 config;retryFailed 为自动重试次数)。 */
611
645
  export interface ReplayOptions {
612
646
  healing?: boolean;
613
647
  healTimeoutMs?: number;
648
+ /** F2:步骤失败(含自愈链耗尽)后自动重试的次数,步骤间 500ms×n 线性退避。 */
649
+ retryFailed?: number;
614
650
  }
615
651
  /** 回放执行器(步骤 → 工具调用),由 register.ts 注入实现 map。支持选择器自愈。 */
616
652
  export declare function runReplay(steps: ReplayStep[], impls: Record<string, (args: any) => Promise<unknown>>, failFast: boolean, opts?: ReplayOptions): Promise<{
@@ -618,6 +654,10 @@ export declare function runReplay(steps: ReplayStep[], impls: Record<string, (ar
618
654
  passed: number;
619
655
  failed: number;
620
656
  healedCount: number;
657
+ retries?: {
658
+ attempted: number;
659
+ recovered: number;
660
+ };
621
661
  stoppedAt?: number;
622
662
  results: any[];
623
663
  }>;
@@ -639,7 +679,73 @@ export declare function workspaceFilePath(cwd: string, file: string): string;
639
679
  * 规则化抽取:fields 定义抽取结构,next 选择器驱动翻页(maxPages 上限),
640
680
  * 行数超 maxRows 截断;format/file 控制序列化与落盘(json/csv/jsonl/markdown)。
641
681
  */
682
+ /** suggest 采集的表格单元格信息(页内采集,纯函数消费)。 */
683
+ export interface SuggestCell {
684
+ text: string;
685
+ href?: string;
686
+ src?: string;
687
+ }
688
+ /** suggest 页内分析结果(自包含 injectWalker 注入采集)。 */
689
+ export interface SuggestAnalysis {
690
+ ok: boolean;
691
+ /** table: 表格行模式;list: 重复卡片/列表项模式。 */
692
+ kind?: "table" | "list";
693
+ /** 行/卡片选择器(抽取 fields 顶层的 multiple selector)。 */
694
+ rowSelector?: string;
695
+ headerTexts?: string[];
696
+ cells?: SuggestCell[];
697
+ item?: SuggestCell;
698
+ reason?: string;
699
+ overview?: {
700
+ links: number;
701
+ images: number;
702
+ buttons: number;
703
+ headings: number;
704
+ rows: number;
705
+ textChars: number;
706
+ };
707
+ }
708
+ /** 字段名净化:空白→下划线,限长 30,空回落 col_N。导出供测试。 */
709
+ export declare function suggestFieldName(header: string | undefined, index: number): string;
710
+ /**
711
+ * 纯逻辑:由表头与首行单元格特征推断表格 fields(单元格文本/链接 href/图片 src)。
712
+ * 返回可直接放进 rows.fields 的嵌套字段数组。导出供测试。
713
+ */
714
+ export declare function fieldsFromCells(headerTexts: string[], cells: SuggestCell[]): ExtractField[];
715
+ /**
716
+ * 纯逻辑:由首条卡片信息推断列表 fields(项文本/链接/图片)。
717
+ * 文本取第一个子元素(项自身无法用 querySelector 自选)。导出供测试。
718
+ */
719
+ export declare function fieldsFromCard(item: SuggestCell): ExtractField[];
720
+ /** 纯逻辑:由 fields 与单元格样本拼出首行样例(与抽取输出同构;按 td 序号对位)。导出供测试。 */
721
+ export declare function sampleOfFields(fields: ExtractField[], cells: SuggestCell[]): Record<string, unknown>;
722
+ /**
723
+ * suggest 汇总(纯逻辑):由页内分析结果组装「可直接交给抽取模式」的顶层 fields
724
+ * (单个 multiple rows 字段 + 行内嵌套字段)与样本。推断失败原样返回。导出供测试。
725
+ */
726
+ export declare function suggestWrap(analysis: SuggestAnalysis): {
727
+ suggested: boolean;
728
+ kind?: "table" | "list";
729
+ rowSelector?: string;
730
+ headerTexts?: string[];
731
+ fields?: ExtractField[];
732
+ sample?: Record<string, unknown>;
733
+ reason?: string;
734
+ overview?: SuggestAnalysis["overview"];
735
+ };
642
736
  export declare function extractTool(config: BrowserConfig, args: Record<string, any>, exec?: ToolRunContext): Promise<{
737
+ url: string;
738
+ title: string;
739
+ suggested: boolean;
740
+ kind?: "table" | "list";
741
+ rowSelector?: string;
742
+ headerTexts?: string[];
743
+ fields?: ExtractField[];
744
+ sample?: Record<string, unknown>;
745
+ reason?: string;
746
+ overview?: SuggestAnalysis["overview"];
747
+ action: string;
748
+ } | {
643
749
  url: string;
644
750
  title: string;
645
751
  file?: string | undefined;
@@ -695,6 +801,10 @@ export declare function batchTool(args: Record<string, any>, impls: Record<strin
695
801
  passed: number;
696
802
  failed: number;
697
803
  healedCount: number;
804
+ retries?: {
805
+ attempted: number;
806
+ recovered: number;
807
+ };
698
808
  stoppedAt?: number;
699
809
  results: any[];
700
810
  }>;
@@ -722,6 +832,10 @@ export declare function replayTool(args: Record<string, any>, impls: Record<stri
722
832
  passed: number;
723
833
  failed: number;
724
834
  healedCount: number;
835
+ retries?: {
836
+ attempted: number;
837
+ recovered: number;
838
+ };
725
839
  stoppedAt?: number;
726
840
  results: any[];
727
841
  source: string;
@@ -730,4 +844,19 @@ export declare function replayTool(args: Record<string, any>, impls: Record<stri
730
844
  failedRows?: undefined;
731
845
  runs?: undefined;
732
846
  }>;
847
+ /** 重跑请求:source 为已保存录制名,retryOf 为被重跑的原 runId(记录用)。 */
848
+ export interface RerunRequest {
849
+ source: string;
850
+ retryOf?: string;
851
+ }
852
+ type RerunHandler = (req: RerunRequest) => Promise<unknown>;
853
+ /** registerTools 注册重跑入口(stepImpls 同路径);web-only 进程保持 null。 */
854
+ export declare function setRerunHandler(fn: RerunHandler): void;
855
+ /** 当前可用的重跑入口(null = 本进程没有 tools 服务,无法重跑)。 */
856
+ export declare function getRerunHandler(): RerunHandler | null;
857
+ /** 重跑是否正在执行(端点互斥;导出供测试)。 */
858
+ export declare function rerunBusy(): boolean;
859
+ /** 执行一次面板重跑(busy 检查 + 互斥标记;返回 replayTool 结果)。 */
860
+ export declare function runPanelRerun(req: RerunRequest): Promise<unknown>;
861
+ export {};
733
862
  //# sourceMappingURL=tools.d.ts.map
@@ -31,13 +31,35 @@ export interface BrowserConfig {
31
31
  maxRefs?: number;
32
32
  /** browser_extract 单次最多聚合的行数(默认 1000)。 */
33
33
  maxRows?: number;
34
+ /**
35
+ * 增量快照保留的历史版本数 K(默认 5,上限 20;内存上界 ≈100KB/page)。
36
+ * since ∈ [latest-K+1, latest] 都能 diff,更旧版本回落全量标 stale。
37
+ */
38
+ snapshotHistory?: number;
34
39
  /**
35
40
  * 敏感操作审批模式:off(默认,行为与 v0.2 一致,工具仍可手动调用)|
36
- * manual(systemPrompt 指示 agent 在提交/删除/支付类动作前调用 browser_confirm)。
41
+ * manual(systemPrompt 指示 agent 在提交/删除/支付类动作前调用 browser_confirm)|
42
+ * auto(硬约束:宿主层在 sensitiveTools 工具执行前强制解析目标元素并与
43
+ * sensitivePatterns 匹配,命中即弹审批卡,拒绝/超时该步失败,agent 无法绕过)。
37
44
  */
38
- confirmMode?: "off" | "manual";
45
+ confirmMode?: "off" | "manual" | "auto";
39
46
  /** browser_confirm 等待人工决策的默认超时毫秒(默认 60000)。 */
40
47
  confirmTimeoutMs?: number;
48
+ /**
49
+ * auto 模式敏感文本模式:对目标元素的 role/name/text/id/testid/tag 做
50
+ * 子串匹配(大小写不敏感),命中任一即拦。默认 提交/删除/支付。
51
+ */
52
+ sensitivePatterns?: string[];
53
+ /**
54
+ * auto 模式参与判定的工具(默认只拦 browser_click —— type 误报率高,
55
+ * 需要拦输入类动作时显式加入)。
56
+ */
57
+ sensitiveTools?: string[];
58
+ /**
59
+ * replay/batch 是否同样走 auto 审批闸(默认 true)。设为 false 表示回放
60
+ * 不再弹审批 —— 数据驱动/定时回归的逃生口,风险自担(见 README)。
61
+ */
62
+ confirmInReplay?: boolean;
41
63
  /**
42
64
  * 工具集:all(默认,全部工具)| core(只注册高频核心集,低频的
43
65
  * 录制/回放/报告/抽取/profile 等不注册,降 systemPrompt 常驻 token)。
@@ -0,0 +1,30 @@
1
+ /** 页内 walker 核心(自包含;勿引用模块级变量、勿导入任何模块)。 */
2
+ declare function __walkerCore(): {
3
+ INTERACTIVE: Set<string>;
4
+ STRUCTURAL: Set<string>;
5
+ SKIP_TAGS: Set<string>;
6
+ roleOf: (el: Element) => string;
7
+ nameOf: (el: Element, role: string) => string;
8
+ cssPath: (el: Element) => string;
9
+ isVisible: (el: Element) => boolean;
10
+ describeElement: (el: Element) => {
11
+ role: string;
12
+ name: string;
13
+ selector: string;
14
+ testid: string;
15
+ id: string;
16
+ tag: string;
17
+ text: string;
18
+ visible: boolean;
19
+ };
20
+ };
21
+ /** walker 核心注入后暴露给 entry 的 API(类型层)。 */
22
+ export type WalkerApi = ReturnType<typeof __walkerCore>;
23
+ /**
24
+ * 把消费方 entry(自包含函数,签名 (payload, w: WalkerApi) => R)拼装成
25
+ * 可被 page.evaluate 序列化的独立函数 (payload: P) => R。
26
+ * 拼装发生在模块加载期(new Function 一次性),调用期零开销。
27
+ */
28
+ export declare function injectWalker<P, R>(entry: (payload: P, w: WalkerApi) => R): (payload: P) => R;
29
+ export {};
30
+ //# sourceMappingURL=walker.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-browser-view",
3
- "version": "0.3.3",
4
- "description": "DSH 插件:共享 Playwright 浏览器自动化(36 个 browser_* 工具,含语义元素查找、批处理、视觉回归、选择器自愈、参数化回放、结构化抽取、回归报告与隔离 context)+ 人机协作(人工接管等待、敏感操作审批、人机混合录制、面板双向化:操作预告高亮/内嵌报告)+ 实时画面面板与录制管理。仅依赖 playwright-core,不依赖任何 @deepseek-ai 运行时包。",
3
+ "version": "0.4.0",
4
+ "description": "DSH 插件:共享 Playwright 浏览器自动化(36 个 browser_* 工具,含语义元素查找、批处理、视觉回归、选择器自愈、参数化/数据驱动回放、失败自动重试、结构化抽取与字段建议、增量快照历史版本、回归报告与隔离 context)+ 人机协作(人工接管等待、敏感操作审批(confirmMode:auto 宿主层硬拦截)、人机混合录制、面板一键重跑、面板双向化:操作预告高亮/内嵌报告)+ 实时画面面板与录制管理。仅依赖 playwright-core,不依赖任何 @deepseek-ai 运行时包。",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/types/index.d.ts",