fde2e-bu 0.0.0 → 1.2.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 +65 -0
- package/bin.js +13 -0
- package/package.json +30 -6
- package/src/args.mjs +92 -0
- package/src/cli.mjs +396 -0
- package/src/configdir.mjs +16 -0
- package/src/mcp/bridge.mjs +208 -0
- package/src/mcp/commands-snapshot.json +340 -0
- package/src/mcp/config-cli.mjs +172 -0
- package/src/mcp/config.mjs +257 -0
- package/src/mcp/http.mjs +250 -0
- package/src/mcp/schema.mjs +175 -0
- package/src/mcp/serve.mjs +203 -0
- package/src/misc.mjs +241 -0
- package/src/nm/bind.mjs +229 -0
- package/src/nm/host.mjs +294 -0
- package/src/skills/frontmatter.mjs +150 -0
- package/src/skills/skills.mjs +323 -0
- package/index.js +0 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// 桥通道 HTTP 客户端(task 3.1 execute 面)——传输事实对齐 plugins/dsh/plugin/lib/
|
|
2
|
+
// bridge.js(同一 omni 派发通道,此处为 CLI 独立分发的精简移植,非 import:
|
|
3
|
+
// cli 不进 pnpm workspace、零依赖独立包)。
|
|
4
|
+
//
|
|
5
|
+
// POST {BU_BRIDGE_SERVER|http://127.0.0.1:8788}/api/cmd
|
|
6
|
+
// Content-Type: application/json
|
|
7
|
+
// { "profileId": "...", "command": {...完整 om-adapter/0.7 命令} }
|
|
8
|
+
//
|
|
9
|
+
// 认证(可选内部令牌面,仅回环来源生效):请求头 x-omni-internal(令牌)+
|
|
10
|
+
// x-omni-bound-profile(会话绑定 profileId)——来自环境变量 BU_INTERNAL_TOKEN /
|
|
11
|
+
// BU_BOUND_PROFILE_ID,缺省不发送。r1 P1-5:只在 server URL host ∈ 回环集
|
|
12
|
+
// (127.0.0.1 / ::1 / localhost)时附头;非回环目标不带令牌头(body 照发——
|
|
13
|
+
// 无认证被 401 是服务端正确拒绝),并 stderr 一行提示(每进程一次)。
|
|
14
|
+
//
|
|
15
|
+
// 回执归一双形状(conformance golden C1/C2/C3,判据:payload.ok===true 且无 reply
|
|
16
|
+
// 字段 ⇒ 形状 A=AdapterReply 本体直返(真实服务端形态);有 reply ⇒ 形状 B 历史包装):
|
|
17
|
+
// - 成功(两形状)→ { ok:true, reply:<AdapterReply> }
|
|
18
|
+
// - 失败 → { ok:false, code, message, retryable? }(code 仅放行 /^E_[A-Z0-9_]+$/,
|
|
19
|
+
// 其余折损 E_PROTOCOL——不自造错误码;E_AUTH=传输层本地映射码)
|
|
20
|
+
//
|
|
21
|
+
// 超时预算(对表 dsh/Hermes 同款口径):batch 缺省 305s(300s 批次+5s 余量);
|
|
22
|
+
// 显式 timeoutMs ⇒ max(1s, min(timeoutMs,300s))+5s(C3:低于 1s 按 1s 计,防止
|
|
23
|
+
// 100ms 被算成 5.1s 这种「显式值反而比底价还小」的倒挂);其余缺省 15s。
|
|
24
|
+
// 客户端主动切断=结果未知 ⇒ E_TIMEOUT retryable:false(勿盲目重发)。
|
|
25
|
+
|
|
26
|
+
const CODE_SHAPE = /^E_[A-Z0-9_]+$/;
|
|
27
|
+
const RETRYABLE_CODES = new Set(['E_TIMEOUT', 'E_SELECTOR_NOT_FOUND', 'E_OFFLINE']);
|
|
28
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|
|
29
|
+
const DEFAULT_BATCH_TIMEOUT_MS = 305_000;
|
|
30
|
+
const MAX_COMMAND_TIMEOUT_MS = 300_000;
|
|
31
|
+
const MIN_EXPLICIT_TIMEOUT_MS = 1_000; // C3 底价:显式 timeoutMs 不得低于 1s
|
|
32
|
+
const TRANSPORT_MARGIN_MS = 5_000;
|
|
33
|
+
export const DEFAULT_SERVER = 'http://127.0.0.1:8788';
|
|
34
|
+
|
|
35
|
+
/** 环境变量配置面(task 指定四变量)。 */
|
|
36
|
+
export function bridgeEnv(env = process.env) {
|
|
37
|
+
return {
|
|
38
|
+
server: env.BU_BRIDGE_SERVER || DEFAULT_SERVER,
|
|
39
|
+
profileId: env.BU_BRIDGE_PROFILE_ID || '',
|
|
40
|
+
internalToken: env.BU_INTERNAL_TOKEN || '',
|
|
41
|
+
boundProfileId: env.BU_BOUND_PROFILE_ID || '',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
46
|
+
|
|
47
|
+
// ---------- r1 P1-5:内部令牌仅回环发送 ----------
|
|
48
|
+
|
|
49
|
+
const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', '[::1]', 'localhost']); // [::1]=Node URL.hostname 对 IPv6 保留方括号
|
|
50
|
+
let tokenNoticeShown = false; // stderr 提示每进程一次(长连 serve 不刷屏)
|
|
51
|
+
|
|
52
|
+
/** server URL host 是否回环(解析失败/无 host=非回环,fail-closed 不发令牌)。 */
|
|
53
|
+
export function isLoopbackServer(server) {
|
|
54
|
+
let url;
|
|
55
|
+
try {
|
|
56
|
+
url = new URL(String(server));
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return LOOPBACK_HOSTS.has(url.hostname);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function noticeTokenSuppressed(host) {
|
|
64
|
+
if (tokenNoticeShown) return;
|
|
65
|
+
tokenNoticeShown = true;
|
|
66
|
+
try {
|
|
67
|
+
process.stderr.write(`fde2e-bu: 内部令牌仅回环发送——目标 ${host} 非回环,未附加 x-omni-internal/x-omni-bound-profile(无认证预期 401)\n`);
|
|
68
|
+
} catch { /* stderr 不可写(测试捕获态)时静默 */ }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function failureFrom(src) {
|
|
72
|
+
const code = typeof src.code === 'string' && CODE_SHAPE.test(src.code) ? src.code : 'E_PROTOCOL';
|
|
73
|
+
const envelope = {
|
|
74
|
+
ok: false,
|
|
75
|
+
code,
|
|
76
|
+
message: typeof src.message === 'string' && src.message !== '' ? src.message : `桥命令失败(${code})`,
|
|
77
|
+
retryable: typeof src.retryable === 'boolean' ? src.retryable : RETRYABLE_CODES.has(code),
|
|
78
|
+
};
|
|
79
|
+
if (isRecord(src.detail)) envelope.detail = src.detail;
|
|
80
|
+
return envelope;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* /api/cmd 回执包络归一(成功双形状/失败/包裹失败)——与 conformance golden 对齐。
|
|
85
|
+
* @returns {{ok:true, reply:object} | {ok:false, code:string, message:string, retryable:boolean, detail?:object}}
|
|
86
|
+
*/
|
|
87
|
+
export function normalizeEnvelope(payload) {
|
|
88
|
+
if (!isRecord(payload)) {
|
|
89
|
+
return { ok: false, code: 'E_PROTOCOL', message: '桥服务回执不是 JSON 对象', retryable: false };
|
|
90
|
+
}
|
|
91
|
+
if (payload.ok === true) {
|
|
92
|
+
if ('reply' in payload) {
|
|
93
|
+
const reply = payload.reply;
|
|
94
|
+
if (isRecord(reply) && reply.ok === false) return failureFrom(reply); // 包裹失败 → 外层失败
|
|
95
|
+
return { ok: true, ...(reply !== undefined ? { reply } : {}) };
|
|
96
|
+
}
|
|
97
|
+
return { ok: true, reply: payload }; // 形状 A:本体直返,整体作 reply 透传
|
|
98
|
+
}
|
|
99
|
+
if (payload.ok === false) return failureFrom(payload);
|
|
100
|
+
return { ok: false, code: 'E_PROTOCOL', message: '桥服务回执包络不识别(缺 ok 布尔字段)', retryable: false };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** 单命令等待预算(毫秒)。 */
|
|
104
|
+
export function budgetMsFor(command, settings = {}) {
|
|
105
|
+
const base = settings.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
106
|
+
const want = command?.timeoutMs;
|
|
107
|
+
const explicit = typeof want === 'number' && Number.isFinite(want) && want > 0 ? want : null;
|
|
108
|
+
if (command?.cmd === 'batch') {
|
|
109
|
+
if (explicit !== null) return Math.max(MIN_EXPLICIT_TIMEOUT_MS, Math.min(explicit, MAX_COMMAND_TIMEOUT_MS)) + TRANSPORT_MARGIN_MS;
|
|
110
|
+
return settings.batchTimeoutMs ?? DEFAULT_BATCH_TIMEOUT_MS;
|
|
111
|
+
}
|
|
112
|
+
if (explicit !== null) return Math.max(base, Math.min(explicit, MAX_COMMAND_TIMEOUT_MS) + TRANSPORT_MARGIN_MS);
|
|
113
|
+
return base;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 派发一条命令(execute 通道)。
|
|
118
|
+
* @param {object} settings { server, profileId, internalToken?, boundProfileId?, fetchImpl? }
|
|
119
|
+
* @param {object} command 完整 om-adapter/0.7 命令
|
|
120
|
+
* @returns {Promise<object>} 归一包络
|
|
121
|
+
*/
|
|
122
|
+
export async function dispatchCommand(settings, command) {
|
|
123
|
+
const url = `${settings.server.replace(/\/+$/, '')}/api/cmd`;
|
|
124
|
+
const headers = { 'content-type': 'application/json' };
|
|
125
|
+
// r1 P1-5:令牌头仅回环 host 附加;非回环目标不带(防内部令牌发往任意主机)
|
|
126
|
+
if ((settings.internalToken || settings.boundProfileId) && isLoopbackServer(settings.server)) {
|
|
127
|
+
if (settings.internalToken) headers['x-omni-internal'] = settings.internalToken;
|
|
128
|
+
if (settings.boundProfileId) headers['x-omni-bound-profile'] = settings.boundProfileId;
|
|
129
|
+
} else if (settings.internalToken || settings.boundProfileId) {
|
|
130
|
+
let host = String(settings.server);
|
|
131
|
+
try { host = new URL(host).host || host; } catch { /* 非 URL 形态原样展示 */ }
|
|
132
|
+
noticeTokenSuppressed(host);
|
|
133
|
+
}
|
|
134
|
+
const body = { profileId: settings.profileId, command };
|
|
135
|
+
const timeoutMs = budgetMsFor(command, settings);
|
|
136
|
+
const controller = new AbortController();
|
|
137
|
+
const deadline = setTimeout(() => controller.abort(new Error('deadline')), timeoutMs);
|
|
138
|
+
// C3(P2-8):预算计时必须覆盖响应体读取——此前 finally 在 fetch 返回后立即
|
|
139
|
+
// clearTimeout,服务端先回头再慢吞吞吐 body 时会永久挂住。现统一在 body 解析
|
|
140
|
+
// 之后 clear,切断一律 E_TIMEOUT retryable:false(结果未知,勿盲目重发)。
|
|
141
|
+
const timeoutEnvelope = () => ({
|
|
142
|
+
ok: false,
|
|
143
|
+
code: 'E_TIMEOUT',
|
|
144
|
+
message: `客户端预算耗尽主动切断(${timeoutMs}ms)——结果未知:命令可能已在执行甚至已成功,先复核页面状态再决定是否重发`,
|
|
145
|
+
retryable: false,
|
|
146
|
+
});
|
|
147
|
+
try {
|
|
148
|
+
let response;
|
|
149
|
+
try {
|
|
150
|
+
const fetchImpl = settings.fetchImpl ?? globalThis.fetch;
|
|
151
|
+
response = await fetchImpl(url, {
|
|
152
|
+
method: 'POST',
|
|
153
|
+
headers,
|
|
154
|
+
body: JSON.stringify(body),
|
|
155
|
+
signal: controller.signal,
|
|
156
|
+
});
|
|
157
|
+
} catch (err) {
|
|
158
|
+
if (controller.signal.aborted) return timeoutEnvelope();
|
|
159
|
+
return { ok: false, code: 'E_OFFLINE', message: `桥服务不可达(${url}):${err?.message ?? err}`, retryable: true };
|
|
160
|
+
}
|
|
161
|
+
let payload = null;
|
|
162
|
+
try {
|
|
163
|
+
payload = await response.json();
|
|
164
|
+
} catch {
|
|
165
|
+
payload = null;
|
|
166
|
+
if (controller.signal.aborted) return timeoutEnvelope(); // 读 body 超预算(fetch 已返回但 body 未完)
|
|
167
|
+
}
|
|
168
|
+
if (!isRecord(payload)) {
|
|
169
|
+
if (response.status >= 500) {
|
|
170
|
+
return { ok: false, code: 'E_OFFLINE', message: `桥服务 HTTP ${response.status}(非 JSON 回执)`, retryable: true };
|
|
171
|
+
}
|
|
172
|
+
return { ok: false, code: 'E_PROTOCOL', message: `桥服务回执不是 JSON 包络(HTTP ${response.status})——检查 BU_BRIDGE_SERVER 指向 browser-bridge 派发入口`, retryable: false };
|
|
173
|
+
}
|
|
174
|
+
if (!('ok' in payload) && (response.status === 401 || response.status === 403)) {
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
code: 'E_AUTH',
|
|
178
|
+
message: `桥服务 HTTP ${response.status}(鉴权被拒:BU_INTERNAL_TOKEN 缺失/不匹配,或经公网入口——x-omni-internal 仅回环来源生效;403=BU_BOUND_PROFILE_ID 与 profileId 不一致)`,
|
|
179
|
+
retryable: false,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return normalizeEnvelope(payload);
|
|
183
|
+
} finally {
|
|
184
|
+
clearTimeout(deadline);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** GET {server}/healthz(status/doctor 探活;离线/超时返回 null)。 */
|
|
189
|
+
export async function fetchHealthz(server, { timeoutMs = 4000, fetchImpl } = {}) {
|
|
190
|
+
const url = `${server.replace(/\/+$/, '')}/healthz`;
|
|
191
|
+
const controller = new AbortController();
|
|
192
|
+
const deadline = setTimeout(() => controller.abort(new Error('deadline')), timeoutMs);
|
|
193
|
+
try {
|
|
194
|
+
const impl = fetchImpl ?? globalThis.fetch;
|
|
195
|
+
const res = await impl(url, { signal: controller.signal });
|
|
196
|
+
let body = null;
|
|
197
|
+
try {
|
|
198
|
+
body = await res.json();
|
|
199
|
+
} catch {
|
|
200
|
+
body = null;
|
|
201
|
+
}
|
|
202
|
+
return { ok: res.status >= 200 && res.status < 300, status: res.status, url, body };
|
|
203
|
+
} catch {
|
|
204
|
+
return { ok: false, status: 0, url, body: null };
|
|
205
|
+
} finally {
|
|
206
|
+
clearTimeout(deadline);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$doc": {
|
|
3
|
+
"意图": "browser-bridge 契约层机器可读命令清单(宿主适配层按此声明工具参数;语义骨架见 SKILL.core.md)。",
|
|
4
|
+
"真源": "packages/protocol/src(constants.js COMMANDS + validate.js COMMAND_SPECS)——本文件是生成物,禁止手改;再生成=node plugins/contracts/browser-bridge/scripts/gen-contract-commands.mjs",
|
|
5
|
+
"注意": "required/optional 只是字段名白名单;取值域/二选一分支(如 windowClose 的 windowId|taskId 恰一、imageExtract 的 css|ref 恰一)以 packages/protocol/src/validate.js 与 constraints.json 为准。"
|
|
6
|
+
},
|
|
7
|
+
"protocolVersion": "om-adapter/0.7",
|
|
8
|
+
"commands": [
|
|
9
|
+
{
|
|
10
|
+
"cmd": "batch",
|
|
11
|
+
"required": [
|
|
12
|
+
"steps"
|
|
13
|
+
],
|
|
14
|
+
"optional": [
|
|
15
|
+
"continueOnError",
|
|
16
|
+
"snapshotAfter",
|
|
17
|
+
"snapshotPerStep",
|
|
18
|
+
"timeoutMs",
|
|
19
|
+
"targetTabId",
|
|
20
|
+
"targetWindowId"
|
|
21
|
+
],
|
|
22
|
+
"summary": "一次往返跑一串动作(≤50 步、整批 ≤300s、单步 ≤30s);只在中间结果不改变后续决策时用;外放/窗口/邮箱/媒体族禁入批。"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"cmd": "open",
|
|
26
|
+
"required": [
|
|
27
|
+
"url"
|
|
28
|
+
],
|
|
29
|
+
"optional": [
|
|
30
|
+
"waitSelector",
|
|
31
|
+
"timeoutMs",
|
|
32
|
+
"targetTabId",
|
|
33
|
+
"targetWindowId"
|
|
34
|
+
],
|
|
35
|
+
"summary": "导航到 url 并等加载完成;waitSelector 出现才回执;返回落点 url/title(导航后复检风控)。"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"cmd": "read",
|
|
39
|
+
"required": [],
|
|
40
|
+
"optional": [
|
|
41
|
+
"selector",
|
|
42
|
+
"targetTabId",
|
|
43
|
+
"targetWindowId"
|
|
44
|
+
],
|
|
45
|
+
"summary": "读元素值/文本(表单控件读 value,其余读 innerText);无 selector 读整页可见文本。"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"cmd": "snapshot",
|
|
49
|
+
"required": [],
|
|
50
|
+
"optional": [
|
|
51
|
+
"targetTabId",
|
|
52
|
+
"targetWindowId"
|
|
53
|
+
],
|
|
54
|
+
"summary": "给可交互元素打 data-om-ref=\"@N\" 标并回几何/命中信息;ref 仅下一次命令有效,每次 snapshot 重排。"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"cmd": "click",
|
|
58
|
+
"required": [
|
|
59
|
+
"target"
|
|
60
|
+
],
|
|
61
|
+
"optional": [
|
|
62
|
+
"waitSelector",
|
|
63
|
+
"timeoutMs",
|
|
64
|
+
"targetTabId",
|
|
65
|
+
"targetWindowId"
|
|
66
|
+
],
|
|
67
|
+
"summary": "点击目标:元素级合成事件为主通道,custom element/闭 ShadowDOM 走 CDP 坐标信任点击,回执带 hit(实际点到了什么)。"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"cmd": "fill",
|
|
71
|
+
"required": [
|
|
72
|
+
"target",
|
|
73
|
+
"text"
|
|
74
|
+
],
|
|
75
|
+
"optional": [
|
|
76
|
+
"inputMode",
|
|
77
|
+
"timeoutMs",
|
|
78
|
+
"targetTabId",
|
|
79
|
+
"targetWindowId"
|
|
80
|
+
],
|
|
81
|
+
"summary": "清空后整体设值并派发 input/change(text:\"\"=清空);目标在闭 ShadowDOM 内或为自定义元素宿主时自动下钻内部输入框;inputMode 可选拟人节奏。"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"cmd": "paste",
|
|
85
|
+
"required": [
|
|
86
|
+
"target",
|
|
87
|
+
"text"
|
|
88
|
+
],
|
|
89
|
+
"optional": [
|
|
90
|
+
"html",
|
|
91
|
+
"timeoutMs",
|
|
92
|
+
"targetTabId",
|
|
93
|
+
"targetWindowId"
|
|
94
|
+
],
|
|
95
|
+
"summary": "DataTransfer+ClipboardEvent 通道粘贴(富文本编辑器用);html 走 text/html 受限通道;闭 ShadowDOM 内编辑器可达。"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"cmd": "insertText",
|
|
99
|
+
"required": [
|
|
100
|
+
"text"
|
|
101
|
+
],
|
|
102
|
+
"optional": [
|
|
103
|
+
"targetTabId",
|
|
104
|
+
"targetWindowId"
|
|
105
|
+
],
|
|
106
|
+
"summary": "Input.insertText 无焦点点插入文本。"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"cmd": "press",
|
|
110
|
+
"required": [
|
|
111
|
+
"key"
|
|
112
|
+
],
|
|
113
|
+
"optional": [
|
|
114
|
+
"targetTabId",
|
|
115
|
+
"targetWindowId"
|
|
116
|
+
],
|
|
117
|
+
"summary": "按键/组合键(CDP 信任键盘事件,组件库认);键名封闭表见协议包 constraints,不可解析报 E_BAD_KEY。"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"cmd": "scroll",
|
|
121
|
+
"required": [],
|
|
122
|
+
"optional": [
|
|
123
|
+
"target",
|
|
124
|
+
"x",
|
|
125
|
+
"y",
|
|
126
|
+
"deltaY",
|
|
127
|
+
"deltaX",
|
|
128
|
+
"ticks",
|
|
129
|
+
"timeoutMs",
|
|
130
|
+
"targetTabId",
|
|
131
|
+
"targetWindowId"
|
|
132
|
+
],
|
|
133
|
+
"summary": "滚轮逐格派发(页面自身滚动物理生效);优先用它而非 press PageDown;target 先滚到元素居中再在其中心滚。"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"cmd": "upload",
|
|
137
|
+
"required": [
|
|
138
|
+
"target",
|
|
139
|
+
"files"
|
|
140
|
+
],
|
|
141
|
+
"optional": [
|
|
142
|
+
"timeoutMs",
|
|
143
|
+
"targetTabId",
|
|
144
|
+
"targetWindowId"
|
|
145
|
+
],
|
|
146
|
+
"summary": "两段式上传:资产 URL→下载落地→设文件输入框;target 支持 css/ref/text/role(point 不支持);来源白名单见协议包 constraints(BU=本机优先)。"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"cmd": "waitFor",
|
|
150
|
+
"required": [],
|
|
151
|
+
"optional": [
|
|
152
|
+
"selector",
|
|
153
|
+
"urlPattern",
|
|
154
|
+
"timeoutMs",
|
|
155
|
+
"targetTabId",
|
|
156
|
+
"targetWindowId"
|
|
157
|
+
],
|
|
158
|
+
"summary": "等 selector 出现或 url 匹配正则(至少给一个);完成后复检风控。"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"cmd": "screenshot",
|
|
162
|
+
"required": [],
|
|
163
|
+
"optional": [
|
|
164
|
+
"quality",
|
|
165
|
+
"maxWidth",
|
|
166
|
+
"targetTabId",
|
|
167
|
+
"targetWindowId"
|
|
168
|
+
],
|
|
169
|
+
"summary": "当前页 WebP 截图(quality 默认 60,可限 maxWidth)。"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"cmd": "getState",
|
|
173
|
+
"required": [
|
|
174
|
+
"host"
|
|
175
|
+
],
|
|
176
|
+
"optional": [],
|
|
177
|
+
"summary": "探测 host 登录态三态 loggedIn/loggedOut/unknown(cookie 信号启发式,不返回 cookie 值)。"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"cmd": "windowCreate",
|
|
181
|
+
"required": [
|
|
182
|
+
"url"
|
|
183
|
+
],
|
|
184
|
+
"optional": [
|
|
185
|
+
"name",
|
|
186
|
+
"width",
|
|
187
|
+
"height",
|
|
188
|
+
"status",
|
|
189
|
+
"taskId",
|
|
190
|
+
"sessionId",
|
|
191
|
+
"friendlyName"
|
|
192
|
+
],
|
|
193
|
+
"summary": "开独立 popup 窗口(默认 1280×800);返回 windowId/tabId 供 targetTabId/targetWindowId 复用;可带 taskId/sessionId/friendlyName 软关联。"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"cmd": "windowClose",
|
|
197
|
+
"required": [],
|
|
198
|
+
"optional": [
|
|
199
|
+
"windowId",
|
|
200
|
+
"taskId"
|
|
201
|
+
],
|
|
202
|
+
"summary": "关窗释放(幂等);windowId 与 taskId 二选一至少其一;任务收尾必调。"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"cmd": "windowList",
|
|
206
|
+
"required": [],
|
|
207
|
+
"optional": [],
|
|
208
|
+
"summary": "托管窗口全量清单(taskId/windowId/tabId/state/url/status/sessionId);执行浏览器任务前先查;非本会话窗口不要动。"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"cmd": "screencastStart",
|
|
212
|
+
"required": [],
|
|
213
|
+
"optional": [
|
|
214
|
+
"format",
|
|
215
|
+
"quality",
|
|
216
|
+
"maxWidth",
|
|
217
|
+
"maxHeight",
|
|
218
|
+
"everyNthFrame",
|
|
219
|
+
"push",
|
|
220
|
+
"tabId"
|
|
221
|
+
],
|
|
222
|
+
"summary": "帧流推流开始(webp/jpeg,everyNthFrame ≤60)——云端/视窗观测面,非日常面。"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"cmd": "screencastStop",
|
|
226
|
+
"required": [],
|
|
227
|
+
"optional": [
|
|
228
|
+
"tabId"
|
|
229
|
+
],
|
|
230
|
+
"summary": "帧流推流停止。"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"cmd": "mediaStore",
|
|
234
|
+
"required": [
|
|
235
|
+
"artifactId",
|
|
236
|
+
"operationId"
|
|
237
|
+
],
|
|
238
|
+
"optional": [
|
|
239
|
+
"name",
|
|
240
|
+
"timeoutMs"
|
|
241
|
+
],
|
|
242
|
+
"summary": "媒体资产入库登记(artifactId/operationId 均 UUID;租约/体量约束见协议包 constraints)。"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"cmd": "imageExtract",
|
|
246
|
+
"required": [
|
|
247
|
+
"targetTabId",
|
|
248
|
+
"selector"
|
|
249
|
+
],
|
|
250
|
+
"optional": [
|
|
251
|
+
"source",
|
|
252
|
+
"maxBytes",
|
|
253
|
+
"timeoutMs"
|
|
254
|
+
],
|
|
255
|
+
"summary": "抽取页面图片元素内容;selector 为 MediaSelector(css/ref 恰一,比 ElementSelector 窄);需显式 targetTabId。"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"cmd": "windowFocus",
|
|
259
|
+
"required": [
|
|
260
|
+
"windowId"
|
|
261
|
+
],
|
|
262
|
+
"optional": [],
|
|
263
|
+
"summary": "聚焦指定托管窗口(BU 插件保留命令)。"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"cmd": "windowBounds",
|
|
267
|
+
"required": [
|
|
268
|
+
"windowId"
|
|
269
|
+
],
|
|
270
|
+
"optional": [
|
|
271
|
+
"width",
|
|
272
|
+
"height",
|
|
273
|
+
"left",
|
|
274
|
+
"top",
|
|
275
|
+
"state"
|
|
276
|
+
],
|
|
277
|
+
"summary": "调整/读取托管窗口尺寸、位置、状态(normal/minimized/maximized/fullscreen)(BU 插件保留命令)。"
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"cmd": "windowUpdateMeta",
|
|
281
|
+
"required": [
|
|
282
|
+
"windowId"
|
|
283
|
+
],
|
|
284
|
+
"optional": [
|
|
285
|
+
"name",
|
|
286
|
+
"status"
|
|
287
|
+
],
|
|
288
|
+
"summary": "更新托管窗口展示名/状态等元数据(BU 插件保留命令)。"
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"cmd": "mailboxSend",
|
|
292
|
+
"required": [
|
|
293
|
+
"to",
|
|
294
|
+
"body"
|
|
295
|
+
],
|
|
296
|
+
"optional": [
|
|
297
|
+
"type",
|
|
298
|
+
"meta",
|
|
299
|
+
"replyTo"
|
|
300
|
+
],
|
|
301
|
+
"summary": "向收件方发留言(to='*'|'master'|绑定 id;body ≤64KB;type 缺省 note)(BU 插件保留命令)。"
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"cmd": "mailboxPoll",
|
|
305
|
+
"required": [],
|
|
306
|
+
"optional": [
|
|
307
|
+
"mailbox",
|
|
308
|
+
"since",
|
|
309
|
+
"until",
|
|
310
|
+
"limit",
|
|
311
|
+
"offset",
|
|
312
|
+
"from",
|
|
313
|
+
"type",
|
|
314
|
+
"q",
|
|
315
|
+
"order",
|
|
316
|
+
"unreadOnly",
|
|
317
|
+
"view"
|
|
318
|
+
],
|
|
319
|
+
"summary": "拉取留言(mailbox/since/from/type/q/order/unreadOnly 过滤;单次 ≤200 条)(BU 插件保留命令)。"
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"cmd": "mailboxClear",
|
|
323
|
+
"required": [
|
|
324
|
+
"mailbox"
|
|
325
|
+
],
|
|
326
|
+
"optional": [],
|
|
327
|
+
"summary": "清空指定信箱(BU 插件保留命令)。"
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
"cmd": "mailboxMarkRead",
|
|
331
|
+
"required": [
|
|
332
|
+
"mailbox"
|
|
333
|
+
],
|
|
334
|
+
"optional": [
|
|
335
|
+
"upToSeq"
|
|
336
|
+
],
|
|
337
|
+
"summary": "标记信箱已读(可 upToSeq 截止)(BU 插件保留命令)。"
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
}
|