browser-opt 1.0.1
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/dist/browser-opt/index.d.ts +9 -0
- package/dist/browser-opt/index.d.ts.map +1 -0
- package/dist/browser-opt/index.js +7 -0
- package/dist/browser-opt/index.js.map +1 -0
- package/dist/browser-opt/runner/actions/index.d.ts +13 -0
- package/dist/browser-opt/runner/actions/index.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/index.js +44 -0
- package/dist/browser-opt/runner/actions/index.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/click-action.d.ts +10 -0
- package/dist/browser-opt/runner/actions/utils/click-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/click-action.js +17 -0
- package/dist/browser-opt/runner/actions/utils/click-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/date-action.d.ts +21 -0
- package/dist/browser-opt/runner/actions/utils/date-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/date-action.js +464 -0
- package/dist/browser-opt/runner/actions/utils/date-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/dom-action.d.ts +8 -0
- package/dist/browser-opt/runner/actions/utils/dom-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/dom-action.js +167 -0
- package/dist/browser-opt/runner/actions/utils/dom-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/fill-action.d.ts +10 -0
- package/dist/browser-opt/runner/actions/utils/fill-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/fill-action.js +20 -0
- package/dist/browser-opt/runner/actions/utils/fill-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/handoff-action.d.ts +10 -0
- package/dist/browser-opt/runner/actions/utils/handoff-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/handoff-action.js +6 -0
- package/dist/browser-opt/runner/actions/utils/handoff-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/open-action.d.ts +10 -0
- package/dist/browser-opt/runner/actions/utils/open-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/open-action.js +9 -0
- package/dist/browser-opt/runner/actions/utils/open-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/select-option-action.d.ts +17 -0
- package/dist/browser-opt/runner/actions/utils/select-option-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/select-option-action.js +532 -0
- package/dist/browser-opt/runner/actions/utils/select-option-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/upload-action.d.ts +7 -0
- package/dist/browser-opt/runner/actions/utils/upload-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/upload-action.js +173 -0
- package/dist/browser-opt/runner/actions/utils/upload-action.js.map +1 -0
- package/dist/browser-opt/runner/evidence.d.ts +18 -0
- package/dist/browser-opt/runner/evidence.d.ts.map +1 -0
- package/dist/browser-opt/runner/evidence.js +116 -0
- package/dist/browser-opt/runner/evidence.js.map +1 -0
- package/dist/browser-opt/runner/handoff.d.ts +28 -0
- package/dist/browser-opt/runner/handoff.d.ts.map +1 -0
- package/dist/browser-opt/runner/handoff.js +132 -0
- package/dist/browser-opt/runner/handoff.js.map +1 -0
- package/dist/browser-opt/runner/index.d.ts +11 -0
- package/dist/browser-opt/runner/index.d.ts.map +1 -0
- package/dist/browser-opt/runner/index.js +244 -0
- package/dist/browser-opt/runner/index.js.map +1 -0
- package/dist/browser-opt/runner/step-executor.d.ts +5 -0
- package/dist/browser-opt/runner/step-executor.d.ts.map +1 -0
- package/dist/browser-opt/runner/step-executor.js +302 -0
- package/dist/browser-opt/runner/step-executor.js.map +1 -0
- package/dist/browser-opt/type.d.ts +120 -0
- package/dist/browser-opt/type.d.ts.map +1 -0
- package/dist/browser-opt/type.js +2 -0
- package/dist/browser-opt/type.js.map +1 -0
- package/dist/browser-opt/utils.d.ts +48 -0
- package/dist/browser-opt/utils.d.ts.map +1 -0
- package/dist/browser-opt/utils.js +885 -0
- package/dist/browser-opt/utils.js.map +1 -0
- package/dist/browser-opt/workflow/index.d.ts +7 -0
- package/dist/browser-opt/workflow/index.d.ts.map +1 -0
- package/dist/browser-opt/workflow/index.js +6 -0
- package/dist/browser-opt/workflow/index.js.map +1 -0
- package/dist/browser-opt/workflow/matcher.d.ts +12 -0
- package/dist/browser-opt/workflow/matcher.d.ts.map +1 -0
- package/dist/browser-opt/workflow/matcher.js +102 -0
- package/dist/browser-opt/workflow/matcher.js.map +1 -0
- package/dist/browser-opt/workflow/store.d.ts +15 -0
- package/dist/browser-opt/workflow/store.d.ts.map +1 -0
- package/dist/browser-opt/workflow/store.js +175 -0
- package/dist/browser-opt/workflow/store.js.map +1 -0
- package/dist/browser-opt/workflow/type.d.ts +43 -0
- package/dist/browser-opt/workflow/type.d.ts.map +1 -0
- package/dist/browser-opt/workflow/type.js +6 -0
- package/dist/browser-opt/workflow/type.js.map +1 -0
- package/dist/cli/browser-opt.d.ts +3 -0
- package/dist/cli/browser-opt.d.ts.map +1 -0
- package/dist/cli/browser-opt.js +11 -0
- package/dist/cli/browser-opt.js.map +1 -0
- package/dist/cli/commands/browser-opt.d.ts +2 -0
- package/dist/cli/commands/browser-opt.d.ts.map +1 -0
- package/dist/cli/commands/browser-opt.js +514 -0
- package/dist/cli/commands/browser-opt.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +10 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +35 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/utils/args.d.ts +17 -0
- package/dist/cli/utils/args.d.ts.map +1 -0
- package/dist/cli/utils/args.js +74 -0
- package/dist/cli/utils/args.js.map +1 -0
- package/dist/cli/utils/constants.d.ts +8 -0
- package/dist/cli/utils/constants.d.ts.map +1 -0
- package/dist/cli/utils/constants.js +18 -0
- package/dist/cli/utils/constants.js.map +1 -0
- package/dist/cli/utils/output.d.ts +7 -0
- package/dist/cli/utils/output.d.ts.map +1 -0
- package/dist/cli/utils/output.js +45 -0
- package/dist/cli/utils/output.js.map +1 -0
- package/package.json +35 -0
- package/skills/browser-opt/SKILL.md +249 -0
- package/skills/browser-opt/manifest.json +75 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browser-opt 上传动作执行器,负责定位上传控件、准备本地文件并下发 upload。
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import { findUploadRef } from '../../../utils.js';
|
|
7
|
+
import { captureTransientSnapshot } from '../../evidence.js';
|
|
8
|
+
/** 执行上传动作,优先使用快照 ref,再用隐藏 input 和长表单滚动搜索兜底。 */
|
|
9
|
+
export async function executeUploadAction(agent, action, snapshot, outputDir, options) {
|
|
10
|
+
let ref = findUploadRef(snapshot, action.field);
|
|
11
|
+
const searchOutput = [];
|
|
12
|
+
if (!ref) {
|
|
13
|
+
const domTarget = findHiddenUploadInputSelector(agent, action.field);
|
|
14
|
+
if (domTarget) {
|
|
15
|
+
ref = domTarget.selector;
|
|
16
|
+
searchOutput.push(`upload dom selector ${domTarget.selector}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (!ref && options.allowViewportSearch) {
|
|
20
|
+
const scrolled = searchUploadInLongForm(agent, action.field);
|
|
21
|
+
if (scrolled) {
|
|
22
|
+
ref = scrolled.ref;
|
|
23
|
+
searchOutput.push(...scrolled.logs);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!ref) {
|
|
27
|
+
throw new Error(`无法找到上传控件:${action.field}`);
|
|
28
|
+
}
|
|
29
|
+
const filePath = await prepareUploadFile(action.source, outputDir);
|
|
30
|
+
const output = agent.upload(ref, [filePath]);
|
|
31
|
+
return [
|
|
32
|
+
...searchOutput,
|
|
33
|
+
`upload @${ref} ${filePath}`,
|
|
34
|
+
output,
|
|
35
|
+
].filter(Boolean).join('\n').trim();
|
|
36
|
+
}
|
|
37
|
+
/** Ant Upload 等组件会隐藏真实 file input,snapshot 缺失时改用 DOM 字段邻近关系定位。 */
|
|
38
|
+
function findHiddenUploadInputSelector(agent, field) {
|
|
39
|
+
const script = `(() => {
|
|
40
|
+
const uploadHelper = ${uploadDomHelperSource()};
|
|
41
|
+
const result = uploadHelper.findUploadInputByField(${JSON.stringify(field)});
|
|
42
|
+
return JSON.stringify(result);
|
|
43
|
+
})()
|
|
44
|
+
`;
|
|
45
|
+
try {
|
|
46
|
+
const parsed = parseEvalJson(agent.evaluate(script));
|
|
47
|
+
return parsed.found && parsed.selector ? { selector: parsed.selector } : null;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** 返回在页面上下文执行的上传控件定位工具源码,兼容隐藏 input[type=file]。 */
|
|
54
|
+
function uploadDomHelperSource() {
|
|
55
|
+
return `
|
|
56
|
+
(() => {
|
|
57
|
+
const normalizeBrowserOptUploadText = (value) => String(value || '').replace(/[\\s::,,。;*"'‘’“”]/g, '').toLowerCase();
|
|
58
|
+
const browserOptUploadVisible = (element) => {
|
|
59
|
+
const style = window.getComputedStyle(element);
|
|
60
|
+
const rect = element.getBoundingClientRect();
|
|
61
|
+
return style.visibility !== 'hidden' && style.display !== 'none' && rect.width > 0 && rect.height > 0;
|
|
62
|
+
};
|
|
63
|
+
const browserOptUploadAncestorChain = (element) => {
|
|
64
|
+
const chain = [];
|
|
65
|
+
let current = element;
|
|
66
|
+
for (let depth = 0; current && depth < 12; depth += 1) {
|
|
67
|
+
chain.push(current);
|
|
68
|
+
current = current.parentElement;
|
|
69
|
+
}
|
|
70
|
+
return chain;
|
|
71
|
+
};
|
|
72
|
+
const browserOptUploadContext = (input, fieldText) => {
|
|
73
|
+
const chain = browserOptUploadAncestorChain(input);
|
|
74
|
+
const directMatch = chain
|
|
75
|
+
.map((element, depth) => ({ element, depth, text: normalizeBrowserOptUploadText(element.textContent || '') }))
|
|
76
|
+
.find((item) => item.text.includes(fieldText));
|
|
77
|
+
if (directMatch) {
|
|
78
|
+
return { text: directMatch.text, depth: directMatch.depth, source: 'ancestor' };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const item of chain.map((element, depth) => ({ element, depth }))) {
|
|
82
|
+
const siblings = [item.element.previousElementSibling, item.element.nextElementSibling]
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
.map((element) => normalizeBrowserOptUploadText(element.textContent || ''));
|
|
85
|
+
const siblingText = siblings.find((text) => text.includes(fieldText));
|
|
86
|
+
if (siblingText) {
|
|
87
|
+
return { text: siblingText, depth: item.depth + 0.5, source: 'sibling' };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const parent = item.element.parentElement;
|
|
91
|
+
const parentText = normalizeBrowserOptUploadText(parent?.textContent || '');
|
|
92
|
+
const className = String(parent?.className || '');
|
|
93
|
+
if (/ant-form-item|form-item|field|row|item/i.test(className) && parentText.includes(fieldText)) {
|
|
94
|
+
return { text: parentText, depth: item.depth + 1, source: 'form-item' };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { text: normalizeBrowserOptUploadText(chain.map((element) => element.textContent || '').join(' ')), depth: 99, source: 'none' };
|
|
99
|
+
};
|
|
100
|
+
function findUploadInputByField(field) {
|
|
101
|
+
const fieldText = normalizeBrowserOptUploadText(field);
|
|
102
|
+
const inputs = [...document.querySelectorAll('input[type="file"]')];
|
|
103
|
+
const candidates = inputs.map((input, index) => {
|
|
104
|
+
const chain = browserOptUploadAncestorChain(input);
|
|
105
|
+
const context = browserOptUploadContext(input, fieldText);
|
|
106
|
+
const visibleAncestor = chain.find((element) => browserOptUploadVisible(element));
|
|
107
|
+
const rect = visibleAncestor?.getBoundingClientRect?.();
|
|
108
|
+
const score = context.text.includes(fieldText) ? 3 : fieldText.includes(context.text) && context.text ? 2 : 0;
|
|
109
|
+
return { input, index, text: context.text, depth: context.depth, score, visible: Boolean(rect && rect.width > 0 && rect.height > 0) };
|
|
110
|
+
}).sort((a, b) => b.score - a.score || a.depth - b.depth || Number(b.visible) - Number(a.visible) || a.index - b.index);
|
|
111
|
+
const best = candidates[0];
|
|
112
|
+
if (!best || best.score <= 0) {
|
|
113
|
+
if (inputs.length === 1) {
|
|
114
|
+
inputs[0].setAttribute('data-browser-opt-upload-id', 'browser-opt-upload-0');
|
|
115
|
+
return { found: true, selector: '[data-browser-opt-upload-id="browser-opt-upload-0"]', fallbackSingleInput: true };
|
|
116
|
+
}
|
|
117
|
+
return { found: false, count: inputs.length };
|
|
118
|
+
}
|
|
119
|
+
const id = 'browser-opt-upload-' + best.index;
|
|
120
|
+
best.input.setAttribute('data-browser-opt-upload-id', id);
|
|
121
|
+
return { found: true, selector: '[data-browser-opt-upload-id="' + id + '"]', matchedText: best.text };
|
|
122
|
+
}
|
|
123
|
+
return { findUploadInputByField };
|
|
124
|
+
})()
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
/** 长表单上传区常位于当前视口外,按视口上下搜索可上传节点。 */
|
|
128
|
+
function searchUploadInLongForm(agent, field) {
|
|
129
|
+
const logs = [];
|
|
130
|
+
const moves = [
|
|
131
|
+
{ direction: 'up', amount: 900 },
|
|
132
|
+
{ direction: 'down', amount: 900 },
|
|
133
|
+
{ direction: 'down', amount: 900 },
|
|
134
|
+
{ direction: 'down', amount: 900 },
|
|
135
|
+
{ direction: 'down', amount: 900 },
|
|
136
|
+
];
|
|
137
|
+
for (const move of moves) {
|
|
138
|
+
const output = agent.scroll(move.direction, move.amount);
|
|
139
|
+
logs.push(`scroll ${move.direction} ${move.amount}${output.trim() ? `\n${output.trim()}` : ''}`);
|
|
140
|
+
agent.waitMs(200);
|
|
141
|
+
const snapshot = captureTransientSnapshot(agent);
|
|
142
|
+
const ref = findUploadRef(snapshot, field);
|
|
143
|
+
if (ref) {
|
|
144
|
+
return { ref, logs };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
function parseEvalJson(raw) {
|
|
150
|
+
const decoded = JSON.parse(raw.trim());
|
|
151
|
+
return typeof decoded === 'string'
|
|
152
|
+
? JSON.parse(decoded)
|
|
153
|
+
: decoded;
|
|
154
|
+
}
|
|
155
|
+
/** 将远程上传素材下载到本次证据目录,让 agent-browser upload 使用稳定的本地路径。 */
|
|
156
|
+
async function prepareUploadFile(source, outputDir) {
|
|
157
|
+
if (!/^https?:\/\//i.test(source)) {
|
|
158
|
+
return path.resolve(source);
|
|
159
|
+
}
|
|
160
|
+
const uploadsDir = path.join(outputDir, 'uploads');
|
|
161
|
+
fs.mkdirSync(uploadsDir, { recursive: true });
|
|
162
|
+
const url = new URL(source);
|
|
163
|
+
const basename = path.basename(url.pathname) || 'upload-file';
|
|
164
|
+
const filePath = path.join(uploadsDir, basename);
|
|
165
|
+
const response = await fetch(source);
|
|
166
|
+
if (!response.ok) {
|
|
167
|
+
throw new Error(`下载上传文件失败:${response.status} ${response.statusText}`);
|
|
168
|
+
}
|
|
169
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
170
|
+
fs.writeFileSync(filePath, buffer);
|
|
171
|
+
return filePath;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=upload-action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-action.js","sourceRoot":"","sources":["../../../../../src/browser-opt/runner/actions/utils/upload-action.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,+CAA+C;AAC/C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAmB,EACnB,MAAwD,EACxD,QAA0B,EAC1B,SAAiB,EACjB,OAAsC;IAEtC,IAAI,GAAG,GAAG,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,SAAS,GAAG,6BAA6B,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACrE,IAAI,SAAS,EAAE,CAAC;YACd,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC;YACzB,YAAY,CAAC,IAAI,CAAC,uBAAuB,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7D,IAAI,QAAQ,EAAE,CAAC;YACb,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,YAAY,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7C,OAAO;QACL,GAAG,YAAY;QACf,WAAW,GAAG,IAAI,QAAQ,EAAE;QAC5B,MAAM;KACP,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AACtC,CAAC;AAED,kEAAkE;AAClE,SAAS,6BAA6B,CAAC,KAAmB,EAAE,KAAa;IACvE,MAAM,MAAM,GAAG;yBACQ,qBAAqB,EAAE;uDACO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;;;CAG3E,CAAC;IAEA,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACrD,OAAO,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,mDAAmD;AACnD,SAAS,qBAAqB;IAC5B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsER,CAAC;AACF,CAAC;AAED,mCAAmC;AACnC,SAAS,sBAAsB,CAAC,KAAmB,EAAE,KAAa;IAChE,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAwD;QACjE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;QAChC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE;QAClC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE;QAClC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE;QAClC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE;KACnC,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACjG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,MAAM,QAAQ,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,GAAG,GAAG,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC3C,IAAI,GAAG,EAAE,CAAC;YACR,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAY,CAAC;IAClD,OAAO,OAAO,OAAO,KAAK,QAAQ;QAChC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAqC;QACzD,CAAC,CAAC,OAA2C,CAAC;AAClD,CAAC;AAED,yDAAyD;AACzD,KAAK,UAAU,iBAAiB,CAAC,MAAc,EAAE,SAAiB;IAChE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACnD,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE9C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,aAAa,CAAC;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,YAAY,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACzD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnC,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BrowserAgent } from '@browser-automated/browser-core/agent';
|
|
2
|
+
import type { SnapshotEvidence } from '../type.js';
|
|
3
|
+
interface CaptureSettledSnapshotOptions {
|
|
4
|
+
reloadAfterBlank?: boolean;
|
|
5
|
+
targetUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
/** 采集一份机器可读快照并同时落盘,供动作匹配与报告复用。 */
|
|
8
|
+
export declare function captureSnapshot(agent: BrowserAgent, filePath: string): SnapshotEvidence;
|
|
9
|
+
/** 仅供动作内部临时判断使用的快照,不落盘,避免打乱步骤证据文件命名。 */
|
|
10
|
+
export declare function captureTransientSnapshot(agent: BrowserAgent): SnapshotEvidence;
|
|
11
|
+
/** 打开页面后短暂等待空白初始页退场,必要时刷新一次,避免卡在 SPA 半初始化状态。 */
|
|
12
|
+
export declare function captureSettledSnapshot(agent: BrowserAgent, filePath: string, logs: string[], options?: CaptureSettledSnapshotOptions): SnapshotEvidence;
|
|
13
|
+
export declare function isBlankInitialSnapshot(snapshot: SnapshotEvidence): boolean;
|
|
14
|
+
/** 判断证据与浏览器当前地址是否都表明页面仍停留在初始空白页。 */
|
|
15
|
+
export declare function isAboutBlankOpen(agent: BrowserAgent, snapshot: SnapshotEvidence): boolean;
|
|
16
|
+
export declare function normalizeUrlForCompare(value: string): string;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../../src/browser-opt/runner/evidence.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,UAAU,6BAA6B;IACrC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,kCAAkC;AAClC,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAQvF;AAED,wCAAwC;AACxC,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,YAAY,GAAG,gBAAgB,CAO9E;AAED,gDAAgD;AAChD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,6BAAkC,GAC1C,gBAAgB,CAuBlB;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAE1E;AAkCD,oCAAoC;AACpC,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAQzF;AAsBD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ5D"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browser-opt 证据采集工具,负责 snapshot 落盘、临时快照和初始页面稳定等待。
|
|
3
|
+
* 这些函数不判断业务语义,只为执行层提供一致的页面证据。
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import { countSnapshotNodes, snapshotText } from '../utils.js';
|
|
7
|
+
/** 采集一份机器可读快照并同时落盘,供动作匹配与报告复用。 */
|
|
8
|
+
export function captureSnapshot(agent, filePath) {
|
|
9
|
+
const output = agent.snapshotJson();
|
|
10
|
+
fs.writeFileSync(filePath, JSON.stringify(output.data ?? { raw: output.raw, parseError: output.parseError }, null, 2));
|
|
11
|
+
return {
|
|
12
|
+
output,
|
|
13
|
+
text: snapshotText(output),
|
|
14
|
+
nodeCount: countSnapshotNodes(output.data),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** 仅供动作内部临时判断使用的快照,不落盘,避免打乱步骤证据文件命名。 */
|
|
18
|
+
export function captureTransientSnapshot(agent) {
|
|
19
|
+
const output = agent.snapshotJson();
|
|
20
|
+
return {
|
|
21
|
+
output,
|
|
22
|
+
text: snapshotText(output),
|
|
23
|
+
nodeCount: countSnapshotNodes(output.data),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** 打开页面后短暂等待空白初始页退场,必要时刷新一次,避免卡在 SPA 半初始化状态。 */
|
|
27
|
+
export function captureSettledSnapshot(agent, filePath, logs, options = {}) {
|
|
28
|
+
let snapshot = captureSnapshot(agent, filePath);
|
|
29
|
+
let blankWaits = 0;
|
|
30
|
+
for (let attempt = 1; attempt <= 5 && isBlankInitialSnapshot(snapshot); attempt += 1) {
|
|
31
|
+
blankWaits = attempt;
|
|
32
|
+
logs.push(`open-wait ${attempt}: snapshot 仍为空白页,等待页面接管后重试。`);
|
|
33
|
+
agent.waitMs(500);
|
|
34
|
+
snapshot = captureSnapshot(agent, filePath);
|
|
35
|
+
}
|
|
36
|
+
snapshot = recoverNonBlankTab(agent, snapshot, filePath, logs, options.targetUrl);
|
|
37
|
+
if (blankWaits > 0 && options.reloadAfterBlank) {
|
|
38
|
+
logs.push(`open-reload: 初始打开经历空白页,刷新一次避免停留在半初始化页面。`);
|
|
39
|
+
agent.reload();
|
|
40
|
+
agent.waitMs(500);
|
|
41
|
+
snapshot = captureSnapshot(agent, filePath);
|
|
42
|
+
for (let attempt = 1; attempt <= 5 && isBlankInitialSnapshot(snapshot); attempt += 1) {
|
|
43
|
+
logs.push(`open-reload-wait ${attempt}: 刷新后 snapshot 仍为空白页,继续短暂等待。`);
|
|
44
|
+
agent.waitMs(500);
|
|
45
|
+
snapshot = captureSnapshot(agent, filePath);
|
|
46
|
+
}
|
|
47
|
+
snapshot = recoverNonBlankTab(agent, snapshot, filePath, logs, options.targetUrl);
|
|
48
|
+
}
|
|
49
|
+
return snapshot;
|
|
50
|
+
}
|
|
51
|
+
export function isBlankInitialSnapshot(snapshot) {
|
|
52
|
+
return snapshot.nodeCount === 0 && snapshot.text.trim() === '(no interactive elements)';
|
|
53
|
+
}
|
|
54
|
+
/** 空白活动页持续不退场时,尝试切换到同一会话中已经打开的业务页或登录页。 */
|
|
55
|
+
function recoverNonBlankTab(agent, snapshot, filePath, logs, targetUrl) {
|
|
56
|
+
if (!isBlankInitialSnapshot(snapshot) || !isAboutBlankUrl(readCurrentUrl(agent))) {
|
|
57
|
+
return snapshot;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const tabs = agent.getTabs().filter((tab) => tab.type === 'page' && !isAboutBlankUrl(tab.url));
|
|
61
|
+
const targetOrigin = targetUrl ? readOrigin(targetUrl) : '';
|
|
62
|
+
const targetTab = tabs.find((tab) => targetOrigin && readOrigin(tab.url) === targetOrigin) ?? tabs.at(-1);
|
|
63
|
+
if (!targetTab) {
|
|
64
|
+
logs.push('open-tab-recovery: 当前会话没有可切换的非空页面。');
|
|
65
|
+
return snapshot;
|
|
66
|
+
}
|
|
67
|
+
logs.push(`open-tab-recovery: 当前活动页为 about:blank,切换到标签页 ${targetTab.tabId} (${targetTab.url})。`);
|
|
68
|
+
agent.switchTab(targetTab.tabId);
|
|
69
|
+
agent.waitMs(250);
|
|
70
|
+
return captureSnapshot(agent, filePath);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
74
|
+
logs.push(`open-tab-recovery: 标签页恢复失败:${message}`);
|
|
75
|
+
return snapshot;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** 判断证据与浏览器当前地址是否都表明页面仍停留在初始空白页。 */
|
|
79
|
+
export function isAboutBlankOpen(agent, snapshot) {
|
|
80
|
+
if (!isBlankInitialSnapshot(snapshot)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const data = snapshot.output.data;
|
|
84
|
+
const snapshotOrigin = data?.data?.origin;
|
|
85
|
+
return snapshotOrigin === 'about:blank' || isAboutBlankUrl(readCurrentUrl(agent));
|
|
86
|
+
}
|
|
87
|
+
function readCurrentUrl(agent) {
|
|
88
|
+
try {
|
|
89
|
+
return agent.getUrl();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function isAboutBlankUrl(value) {
|
|
96
|
+
return value.trim().toLowerCase() === 'about:blank';
|
|
97
|
+
}
|
|
98
|
+
function readOrigin(value) {
|
|
99
|
+
try {
|
|
100
|
+
return new URL(value).origin;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function normalizeUrlForCompare(value) {
|
|
107
|
+
try {
|
|
108
|
+
const url = new URL(value);
|
|
109
|
+
url.hash = '';
|
|
110
|
+
return url.toString().replace(/\/$/, '');
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return value.trim().replace(/\/$/, '');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../../src/browser-opt/runner/evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BrowserAgent } from '@browser-automated/browser-core/agent';
|
|
2
|
+
import type { BrowserOptHandoffContext, BrowserOptHandoffOptions, SnapshotEvidence } from '../type.js';
|
|
3
|
+
/** 把 handoff 文案与底层输出组装成统一上下文,供恢复逻辑和 CLI 提示复用。 */
|
|
4
|
+
export declare function buildHandoffContext(agent: BrowserAgent, message: string, output: string): BrowserOptHandoffContext;
|
|
5
|
+
/** 用于识别确定性动作返回值里是否已经进入 handoff。 */
|
|
6
|
+
export declare function isHandoffActionOutput(actionOutput: string): boolean;
|
|
7
|
+
/** 尽量从 handoff 动作日志里还原给用户看的提示文案。 */
|
|
8
|
+
export declare function extractHandoffMessage(actionOutput: string): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* 只有目标并非登录流程、但页面明显停在登录页时,才自动转入 handoff,避免误伤本来就要测登录的场景。
|
|
11
|
+
*/
|
|
12
|
+
export declare function shouldTriggerLoginHandoff(flowOrInstruction: string, targetUrl: string | undefined, snapshot: SnapshotEvidence): boolean;
|
|
13
|
+
/** 统一生成登录态失效时的 handoff 文案,并把提示写入日志。 */
|
|
14
|
+
export declare function triggerLoginHandoff(agent: BrowserAgent, logs: string[], reason: string): BrowserOptHandoffContext;
|
|
15
|
+
/** 识别上传后进入图片裁剪、确认上传等交互式后处理页,需要人工确认后才能继续流程。 */
|
|
16
|
+
export declare function shouldTriggerUploadPostProcessHandoff(snapshot: SnapshotEvidence): boolean;
|
|
17
|
+
/** 上传后出现必须人工处理的裁剪/确认界面时,统一生成 handoff 上下文并记录日志。 */
|
|
18
|
+
export declare function triggerUploadPostProcessHandoff(agent: BrowserAgent, logs: string[], field: string): BrowserOptHandoffContext;
|
|
19
|
+
/** 登录页拦截统一走 handoff,而不是把它当成普通动作异常直接抛出。 */
|
|
20
|
+
export declare function buildLoginHandoffActionOutput(agent: BrowserAgent, message: string): string;
|
|
21
|
+
/** 当调用方提供等待逻辑时,进入 handoff 后暂停等待用户恢复,再继续当前会话。 */
|
|
22
|
+
export declare function resumeFromHandoff(agent: BrowserAgent, logs: string[], handoff: BrowserOptHandoffContext, options?: BrowserOptHandoffOptions): Promise<boolean>;
|
|
23
|
+
/** 人工接管后等待页面离开登录态再保存,避免用户刚恢复就把中转态误判成最终登录结果。 */
|
|
24
|
+
export declare function saveAuthenticatedHandoffState(agent: BrowserAgent, authStateSavePath: string | undefined, logs: string[], snapshot: SnapshotEvidence): void;
|
|
25
|
+
/** 保存 cookies 与 storage,失败只记录日志,并返回是否成功供 profile 导入流程判断。 */
|
|
26
|
+
export declare function saveAuthState(agent: BrowserAgent, authStateSavePath: string, logs: string[]): boolean;
|
|
27
|
+
export declare function isLoginLikeSnapshot(snapshot: SnapshotEvidence): boolean;
|
|
28
|
+
//# sourceMappingURL=handoff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff.d.ts","sourceRoot":"","sources":["../../../src/browser-opt/runner/handoff.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGvG,iDAAiD;AACjD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,wBAAwB,CAMlH;AAED,mCAAmC;AACnC,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,oCAAoC;AACpC,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYzE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,iBAAiB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAEvI;AAED,uCAAuC;AACvC,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,wBAAwB,CAMjH;AAED,8CAA8C;AAC9C,wBAAgB,qCAAqC,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAazF;AAED,kDAAkD;AAClD,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,MAAM,GACZ,wBAAwB,CAO1B;AAED,0CAA0C;AAC1C,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAG1F;AAWD,gDAAgD;AAChD,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,wBAAwB,EACjC,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,OAAO,CAAC,CAWlB;AAED,+CAA+C;AAC/C,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,YAAY,EACnB,iBAAiB,EAAE,MAAM,GAAG,SAAS,EACrC,IAAI,EAAE,MAAM,EAAE,EACd,QAAQ,EAAE,gBAAgB,GACzB,IAAI,CAiBN;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAcrG;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAEvE"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browser-opt 人工接管工具,负责登录态失效识别、handoff 上下文、恢复控制和登录态保存。
|
|
3
|
+
* 执行层只消费这里的统一结果,不直接拼接接管日志和提示文案。
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import { captureTransientSnapshot, normalizeUrlForCompare } from './evidence.js';
|
|
8
|
+
/** 把 handoff 文案与底层输出组装成统一上下文,供恢复逻辑和 CLI 提示复用。 */
|
|
9
|
+
export function buildHandoffContext(agent, message, output) {
|
|
10
|
+
return {
|
|
11
|
+
message,
|
|
12
|
+
output,
|
|
13
|
+
sessionId: agent.getSessionId(),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** 用于识别确定性动作返回值里是否已经进入 handoff。 */
|
|
17
|
+
export function isHandoffActionOutput(actionOutput) {
|
|
18
|
+
return actionOutput.startsWith('handoff ');
|
|
19
|
+
}
|
|
20
|
+
/** 尽量从 handoff 动作日志里还原给用户看的提示文案。 */
|
|
21
|
+
export function extractHandoffMessage(actionOutput) {
|
|
22
|
+
const firstLine = actionOutput.split('\n')[0] ?? '';
|
|
23
|
+
const rawMessage = firstLine.replace(/^handoff\s+/, '').trim();
|
|
24
|
+
if (!rawMessage) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(rawMessage);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return rawMessage;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 只有目标并非登录流程、但页面明显停在登录页时,才自动转入 handoff,避免误伤本来就要测登录的场景。
|
|
36
|
+
*/
|
|
37
|
+
export function shouldTriggerLoginHandoff(flowOrInstruction, targetUrl, snapshot) {
|
|
38
|
+
return isLoginLikeSnapshot(snapshot) && !isExpectedLoginFlow(flowOrInstruction, targetUrl);
|
|
39
|
+
}
|
|
40
|
+
/** 统一生成登录态失效时的 handoff 文案,并把提示写入日志。 */
|
|
41
|
+
export function triggerLoginHandoff(agent, logs, reason) {
|
|
42
|
+
const message = `${reason},疑似登录态已失效。请在浏览器中完成登录,然后继续当前 browser-opt 流程。`;
|
|
43
|
+
const output = buildLoginHandoffActionOutput(agent, message);
|
|
44
|
+
logs.push(`handoff: ${message}`);
|
|
45
|
+
logs.push(`handoff-output: ${output}`);
|
|
46
|
+
return buildHandoffContext(agent, message, output);
|
|
47
|
+
}
|
|
48
|
+
/** 识别上传后进入图片裁剪、确认上传等交互式后处理页,需要人工确认后才能继续流程。 */
|
|
49
|
+
export function shouldTriggerUploadPostProcessHandoff(snapshot) {
|
|
50
|
+
const text = snapshot.text.replace(/\s+/g, '');
|
|
51
|
+
const hasImageAsset = /\.(?:png|jpe?g|webp|gif|bmp|svg)(?:\b|[^\w])/i.test(text)
|
|
52
|
+
|| /图片|照片|封面|白底图|主图|配图/.test(text);
|
|
53
|
+
const hasCropAction = /图片裁(?:切|剪)|裁(?:切|剪)图片|裁(?:切|剪)图|裁(?:切|剪)|剪裁|裁剪框|编辑图片|调整图片|调整裁剪/.test(text);
|
|
54
|
+
const hasUploadConfirm = /保存并上传|确认上传|上传并保存|保存上传|确定上传|确认提交|保存图片|确定使用|确认使用|使用图片|完成/.test(text);
|
|
55
|
+
const hasPendingState = /待处理|待裁(?:切|剪)|未处理|处理中|等待处理|待确认|待上传|未上传/.test(text);
|
|
56
|
+
const hasCropControl = /缩放|放大|缩小|旋转|比例|目标尺寸|目标大小|推荐尺寸|预览|slider/.test(text);
|
|
57
|
+
const hasBypassAction = /跳过|取消|重新选择|重新上传|更换图片/.test(text);
|
|
58
|
+
return (hasCropAction && (hasUploadConfirm || hasCropControl || hasPendingState))
|
|
59
|
+
|| (hasImageAsset && hasUploadConfirm && (hasPendingState || hasCropAction || hasCropControl))
|
|
60
|
+
|| (hasPendingState && hasCropAction && hasBypassAction);
|
|
61
|
+
}
|
|
62
|
+
/** 上传后出现必须人工处理的裁剪/确认界面时,统一生成 handoff 上下文并记录日志。 */
|
|
63
|
+
export function triggerUploadPostProcessHandoff(agent, logs, field) {
|
|
64
|
+
const message = `上传“${field}”后页面进入图片裁剪或确认上传流程。请在浏览器中完成裁剪、确认上传,然后继续当前 browser-opt 流程。`;
|
|
65
|
+
const output = agent.handoff(message);
|
|
66
|
+
const actionOutput = `handoff ${JSON.stringify(message)}\n${output}`.trim();
|
|
67
|
+
logs.push(`handoff: ${message}`);
|
|
68
|
+
logs.push(`handoff-output: ${actionOutput}`);
|
|
69
|
+
return buildHandoffContext(agent, message, actionOutput);
|
|
70
|
+
}
|
|
71
|
+
/** 登录页拦截统一走 handoff,而不是把它当成普通动作异常直接抛出。 */
|
|
72
|
+
export function buildLoginHandoffActionOutput(agent, message) {
|
|
73
|
+
const output = agent.handoff(message);
|
|
74
|
+
return `handoff ${JSON.stringify(message)}\n${output}`.trim();
|
|
75
|
+
}
|
|
76
|
+
/** 粗略识别“本次流程本来就是要去登录页”的场景,避免错误触发 handoff。 */
|
|
77
|
+
function isExpectedLoginFlow(flowOrInstruction, targetUrl) {
|
|
78
|
+
if (targetUrl && /login|signin|sign-in|auth|oauth|sso/i.test(normalizeUrlForCompare(targetUrl))) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return /登录页|登录|登陆|sign\s*in|log\s*in|输入密码|验证码|短信码|二次验证/i.test(flowOrInstruction);
|
|
82
|
+
}
|
|
83
|
+
/** 当调用方提供等待逻辑时,进入 handoff 后暂停等待用户恢复,再继续当前会话。 */
|
|
84
|
+
export async function resumeFromHandoff(agent, logs, handoff, options) {
|
|
85
|
+
if (!options?.waitForUserResume) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
await options.onHandoffRequired?.(handoff);
|
|
89
|
+
await options.waitForUserResume(handoff);
|
|
90
|
+
const resumeOutput = agent.resume();
|
|
91
|
+
logs.push(`resume: ${resumeOutput}`);
|
|
92
|
+
await options.onHandoffCompleted?.(handoff);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
/** 人工接管后等待页面离开登录态再保存,避免用户刚恢复就把中转态误判成最终登录结果。 */
|
|
96
|
+
export function saveAuthenticatedHandoffState(agent, authStateSavePath, logs, snapshot) {
|
|
97
|
+
if (!authStateSavePath) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
let currentSnapshot = snapshot;
|
|
101
|
+
for (let attempt = 1; attempt <= 5 && isLoginLikeSnapshot(currentSnapshot); attempt += 1) {
|
|
102
|
+
logs.push(`auth-state-save-wait ${attempt}: 人工接管恢复后仍停留在登录页,等待跳转完成后重试。`);
|
|
103
|
+
agent.waitMs(500);
|
|
104
|
+
currentSnapshot = captureTransientSnapshot(agent);
|
|
105
|
+
}
|
|
106
|
+
if (isLoginLikeSnapshot(currentSnapshot)) {
|
|
107
|
+
logs.push('auth-state-save-skipped: 人工接管恢复后仍停留在登录页。');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
saveAuthState(agent, authStateSavePath, logs);
|
|
111
|
+
}
|
|
112
|
+
/** 保存 cookies 与 storage,失败只记录日志,并返回是否成功供 profile 导入流程判断。 */
|
|
113
|
+
export function saveAuthState(agent, authStateSavePath, logs) {
|
|
114
|
+
try {
|
|
115
|
+
fs.mkdirSync(path.dirname(authStateSavePath), { recursive: true });
|
|
116
|
+
const saveOutput = agent.stateSave(authStateSavePath);
|
|
117
|
+
logs.push(`auth-state-save: ${authStateSavePath}`);
|
|
118
|
+
if (saveOutput.trim()) {
|
|
119
|
+
logs.push(`auth-state-save-output: ${saveOutput.trim()}`);
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
125
|
+
logs.push(`auth-state-save-failed: ${message}`);
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export function isLoginLikeSnapshot(snapshot) {
|
|
130
|
+
return /登录|登\s*录|login|请输入手机号|请输入密码/i.test(snapshot.text);
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=handoff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff.js","sourceRoot":"","sources":["../../../src/browser-opt/runner/handoff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type BrowserAgentFactory } from '@browser-automated/browser-core/agent';
|
|
2
|
+
import type { BrowserOptRunResult, BrowserOptRunnerOptions } from '../type.js';
|
|
3
|
+
export { browserOptTemplate, extractBrowserOptUrl, splitBrowserOptSteps, } from '../utils.js';
|
|
4
|
+
export declare class BrowserOptRunner {
|
|
5
|
+
private readonly agentFactory;
|
|
6
|
+
/** 注入可替换的 agent 工厂,便于真实运行与单测共用一套 runner。 */
|
|
7
|
+
constructor(agentFactory?: BrowserAgentFactory);
|
|
8
|
+
/** 执行完整的 browser-opt 流程,产出报告、截图、日志与 PASS/FAIL/HANDOFF 结论。 */
|
|
9
|
+
run(flow: string, options?: BrowserOptRunnerOptions): Promise<BrowserOptRunResult>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/browser-opt/runner/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoC,KAAK,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAEnH,OAAO,KAAK,EAEV,mBAAmB,EACnB,uBAAuB,EAGxB,MAAM,YAAY,CAAC;AAqBpB,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA2C;IAExE,4CAA4C;gBAChC,YAAY,GAAE,mBAAwC;IAIlE,6DAA6D;IACvD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,mBAAmB,CAAC;CA8L7F"}
|