dsh-browser-view 0.3.1 → 0.3.3
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/lib/index.js +61 -12
- package/lib/types/browser/tools.d.ts +12 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2035,9 +2035,28 @@ async function selectTool(config, args) {
|
|
|
2035
2035
|
...await pageIdentity(page)
|
|
2036
2036
|
};
|
|
2037
2037
|
}
|
|
2038
|
+
/**
|
|
2039
|
+
* 防御性解码 eval 脚本:模型/人工偶发把脚本 JSON.stringify 一次再传
|
|
2040
|
+
* (脚本以 \" 开头、换行变字面 \\n),回放/录制链路不纠错会导致语法错误。
|
|
2041
|
+
* 识别条件:字符串以引号包裹且含反斜杠转义,且 JSON.parse 结果仍是字符串。
|
|
2042
|
+
* 无法解析或语义不变场景原样返回(裸字符串字面量如 "hello" 不含转义,不受影响)。
|
|
2043
|
+
*/
|
|
2044
|
+
function unwrapDoubleEncodedScript(script) {
|
|
2045
|
+
if (script.length >= 2 && script.startsWith("\"") && script.endsWith("\"") && script.includes("\\")) try {
|
|
2046
|
+
const parsed = JSON.parse(script);
|
|
2047
|
+
if (typeof parsed === "string" && parsed !== script) return {
|
|
2048
|
+
script: parsed,
|
|
2049
|
+
unwrapped: true
|
|
2050
|
+
};
|
|
2051
|
+
} catch {}
|
|
2052
|
+
return {
|
|
2053
|
+
script,
|
|
2054
|
+
unwrapped: false
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2038
2057
|
async function evalTool(config, args) {
|
|
2039
2058
|
const page = await getPage(config);
|
|
2040
|
-
const script = String(args.script ?? "");
|
|
2059
|
+
const { script, unwrapped } = unwrapDoubleEncodedScript(String(args.script ?? ""));
|
|
2041
2060
|
const wrapped = /[\n;]|^\s*return\b/.test(script) ? `(async () => {\n${script}\n})()` : `(${script})`;
|
|
2042
2061
|
const raw = await page.evaluate(wrapped);
|
|
2043
2062
|
let result;
|
|
@@ -2046,7 +2065,10 @@ async function evalTool(config, args) {
|
|
|
2046
2065
|
} catch {
|
|
2047
2066
|
result = raw === void 0 ? null : String(raw);
|
|
2048
2067
|
}
|
|
2049
|
-
return {
|
|
2068
|
+
return {
|
|
2069
|
+
result,
|
|
2070
|
+
...unwrapped ? { scriptUnwrapped: true } : {}
|
|
2071
|
+
};
|
|
2050
2072
|
}
|
|
2051
2073
|
async function screenshotTool(config, args, exec) {
|
|
2052
2074
|
const page = await getPage(config);
|
|
@@ -3342,15 +3364,27 @@ async function recordTool(args) {
|
|
|
3342
3364
|
if (!name) throw new Error("save 需要 name");
|
|
3343
3365
|
const recorded = lastRecordingOf();
|
|
3344
3366
|
const source = recorded !== null ? "recording" : "recent-log";
|
|
3345
|
-
const
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3367
|
+
const calls = recorded !== null ? recorded : testLogOf().reverse().slice(-100);
|
|
3368
|
+
const includeFailed = args.includeFailed === true;
|
|
3369
|
+
const failedCount = includeFailed ? 0 : calls.filter((c) => c.ok === false).length;
|
|
3370
|
+
const rawSteps = (includeFailed ? calls : calls.filter((c) => c.ok !== false)).map((c) => {
|
|
3371
|
+
if (c.tool === "browser_eval" && typeof c.args?.script === "string") {
|
|
3372
|
+
const { script } = unwrapDoubleEncodedScript(c.args.script);
|
|
3373
|
+
if (script !== c.args.script) return {
|
|
3374
|
+
tool: c.tool,
|
|
3375
|
+
args: {
|
|
3376
|
+
...c.args,
|
|
3377
|
+
script
|
|
3378
|
+
},
|
|
3379
|
+
...c.meta !== void 0 ? { meta: c.meta } : {}
|
|
3380
|
+
};
|
|
3381
|
+
}
|
|
3382
|
+
return {
|
|
3383
|
+
tool: c.tool,
|
|
3384
|
+
args: c.args ?? {},
|
|
3385
|
+
...c.meta !== void 0 ? { meta: c.meta } : {}
|
|
3386
|
+
};
|
|
3387
|
+
});
|
|
3354
3388
|
const steps = args.stripReads === true ? rawSteps.filter((s) => !isReadonlyStep(s)) : rawSteps;
|
|
3355
3389
|
const stripped = rawSteps.length - steps.length;
|
|
3356
3390
|
let varsApplied = 0;
|
|
@@ -3379,6 +3413,7 @@ async function recordTool(args) {
|
|
|
3379
3413
|
source,
|
|
3380
3414
|
file,
|
|
3381
3415
|
...stripped > 0 ? { stripped } : {},
|
|
3416
|
+
...failedCount > 0 ? { skippedFailed: failedCount } : {},
|
|
3382
3417
|
...varsApplied > 0 ? { varsApplied } : {},
|
|
3383
3418
|
...warnings.length > 0 ? { warnings } : {},
|
|
3384
3419
|
hint: "已持久化到磁盘,重启 DSH 后仍可回放"
|
|
@@ -4304,7 +4339,13 @@ function registerTools(ctx, config) {
|
|
|
4304
4339
|
outputSchema: {
|
|
4305
4340
|
type: "object",
|
|
4306
4341
|
additionalProperties: false,
|
|
4307
|
-
properties: {
|
|
4342
|
+
properties: {
|
|
4343
|
+
result: {},
|
|
4344
|
+
scriptUnwrapped: {
|
|
4345
|
+
type: "boolean",
|
|
4346
|
+
description: "入参 script 疑似被 JSON 双重序列化(以引号包裹且含 \\n 字面量),已自动解码一层。"
|
|
4347
|
+
}
|
|
4348
|
+
},
|
|
4308
4349
|
required: ["result"]
|
|
4309
4350
|
},
|
|
4310
4351
|
render: (_args, value) => `Result: ${JSON.stringify(value.result)}`,
|
|
@@ -5409,6 +5450,10 @@ function registerTools(ctx, config) {
|
|
|
5409
5450
|
type: "boolean",
|
|
5410
5451
|
description: "save 时剔除只读步骤(snapshot/elements/eval/network/status/cookies list),默认 false。"
|
|
5411
5452
|
},
|
|
5453
|
+
includeFailed: {
|
|
5454
|
+
type: "boolean",
|
|
5455
|
+
description: "save 时保留失败的实验步骤(默认剔除:超时/无效 selector 的步骤回放必然再次失败并卡住回放)。"
|
|
5456
|
+
},
|
|
5412
5457
|
vars: {
|
|
5413
5458
|
type: "object",
|
|
5414
5459
|
description: "save 时参数化:{ 变量名: \"#步骤号.args.字段\" }(步骤号 1 起),该字段值替换为 {{变量名}} 占位符;replay 时用 vars 传入真实值。"
|
|
@@ -5423,6 +5468,10 @@ function registerTools(ctx, config) {
|
|
|
5423
5468
|
recording: { type: "boolean" },
|
|
5424
5469
|
steps: { type: "integer" },
|
|
5425
5470
|
stripped: { type: "integer" },
|
|
5471
|
+
skippedFailed: {
|
|
5472
|
+
type: "integer",
|
|
5473
|
+
description: "save 时默认剔除的失败实验步骤数。"
|
|
5474
|
+
},
|
|
5426
5475
|
varsApplied: { type: "integer" },
|
|
5427
5476
|
saved: { type: "string" },
|
|
5428
5477
|
source: { type: "string" },
|
|
@@ -155,7 +155,18 @@ export declare function selectTool(config: BrowserConfig, args: Record<string, a
|
|
|
155
155
|
selected: string;
|
|
156
156
|
selector: string;
|
|
157
157
|
}>;
|
|
158
|
+
/**
|
|
159
|
+
* 防御性解码 eval 脚本:模型/人工偶发把脚本 JSON.stringify 一次再传
|
|
160
|
+
* (脚本以 \" 开头、换行变字面 \\n),回放/录制链路不纠错会导致语法错误。
|
|
161
|
+
* 识别条件:字符串以引号包裹且含反斜杠转义,且 JSON.parse 结果仍是字符串。
|
|
162
|
+
* 无法解析或语义不变场景原样返回(裸字符串字面量如 "hello" 不含转义,不受影响)。
|
|
163
|
+
*/
|
|
164
|
+
export declare function unwrapDoubleEncodedScript(script: string): {
|
|
165
|
+
script: string;
|
|
166
|
+
unwrapped: boolean;
|
|
167
|
+
};
|
|
158
168
|
export declare function evalTool(config: BrowserConfig, args: Record<string, any>): Promise<{
|
|
169
|
+
scriptUnwrapped?: boolean | undefined;
|
|
159
170
|
result: unknown;
|
|
160
171
|
}>;
|
|
161
172
|
export declare function screenshotTool(config: BrowserConfig, args: Record<string, any>, exec?: ToolRunContext): Promise<{
|
|
@@ -566,6 +577,7 @@ export declare function recordTool(args: Record<string, any>): Promise<{
|
|
|
566
577
|
hint: string;
|
|
567
578
|
warnings?: string[] | undefined;
|
|
568
579
|
varsApplied?: number | undefined;
|
|
580
|
+
skippedFailed?: number | undefined;
|
|
569
581
|
stripped?: number | undefined;
|
|
570
582
|
saved: string;
|
|
571
583
|
steps: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-browser-view",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "DSH 插件:共享 Playwright 浏览器自动化(36 个 browser_* 工具,含语义元素查找、批处理、视觉回归、选择器自愈、参数化回放、结构化抽取、回归报告与隔离 context)+ 人机协作(人工接管等待、敏感操作审批、人机混合录制、面板双向化:操作预告高亮/内嵌报告)+ 实时画面面板与录制管理。仅依赖 playwright-core,不依赖任何 @deepseek-ai 运行时包。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|