bb-browser 0.1.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/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/chunk-KATHFDYJ.js +20 -0
- package/dist/chunk-KATHFDYJ.js.map +1 -0
- package/dist/cli.js +1800 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon.js +441 -0
- package/dist/daemon.js.map +1 -0
- package/extension/background.js +2943 -0
- package/extension/background.js.map +1 -0
- package/extension/buildDomTree.js +1505 -0
- package/extension/content/trace.js +339 -0
- package/extension/content/trace.js.map +1 -0
- package/extension/dist/background.js +2943 -0
- package/extension/dist/background.js.map +1 -0
- package/extension/dist/buildDomTree.js +1505 -0
- package/extension/dist/content/trace.js +339 -0
- package/extension/dist/content/trace.js.map +1 -0
- package/extension/dist/manifest.json +25 -0
- package/extension/manifest.json +25 -0
- package/package.json +60 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1800 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
COMMAND_TIMEOUT,
|
|
4
|
+
DAEMON_BASE_URL,
|
|
5
|
+
generateId
|
|
6
|
+
} from "./chunk-KATHFDYJ.js";
|
|
7
|
+
|
|
8
|
+
// packages/cli/src/client.ts
|
|
9
|
+
async function sendCommand(request) {
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
const timeoutId = setTimeout(() => controller.abort(), COMMAND_TIMEOUT);
|
|
12
|
+
try {
|
|
13
|
+
const res = await fetch(`${DAEMON_BASE_URL}/command`, {
|
|
14
|
+
method: "POST",
|
|
15
|
+
headers: {
|
|
16
|
+
"Content-Type": "application/json"
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify(request),
|
|
19
|
+
signal: controller.signal
|
|
20
|
+
});
|
|
21
|
+
clearTimeout(timeoutId);
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
if (res.status === 408) {
|
|
24
|
+
return {
|
|
25
|
+
id: request.id,
|
|
26
|
+
success: false,
|
|
27
|
+
error: "\u547D\u4EE4\u6267\u884C\u8D85\u65F6"
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (res.status === 503) {
|
|
31
|
+
return {
|
|
32
|
+
id: request.id,
|
|
33
|
+
success: false,
|
|
34
|
+
error: "\u6269\u5C55\u672A\u8FDE\u63A5"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
id: request.id,
|
|
39
|
+
success: false,
|
|
40
|
+
error: `HTTP \u9519\u8BEF: ${res.status} ${res.statusText}`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return await res.json();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
clearTimeout(timeoutId);
|
|
46
|
+
if (error instanceof Error) {
|
|
47
|
+
if (error.name === "AbortError") {
|
|
48
|
+
return {
|
|
49
|
+
id: request.id,
|
|
50
|
+
success: false,
|
|
51
|
+
error: "\u8BF7\u6C42\u8D85\u65F6"
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (error.message.includes("fetch failed") || error.message.includes("ECONNREFUSED")) {
|
|
55
|
+
throw new Error("\u65E0\u6CD5\u8FDE\u63A5\u5230 Daemon\uFF0C\u8BF7\u786E\u4FDD Daemon \u6B63\u5728\u8FD0\u884C");
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// packages/cli/src/daemon-manager.ts
|
|
64
|
+
import { spawn } from "child_process";
|
|
65
|
+
import { existsSync } from "fs";
|
|
66
|
+
import { fileURLToPath } from "url";
|
|
67
|
+
import { dirname, resolve } from "path";
|
|
68
|
+
function getDaemonPath() {
|
|
69
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
70
|
+
const currentDir = dirname(currentFile);
|
|
71
|
+
const sameDirPath = resolve(currentDir, "daemon.js");
|
|
72
|
+
if (existsSync(sameDirPath)) {
|
|
73
|
+
return sameDirPath;
|
|
74
|
+
}
|
|
75
|
+
return resolve(currentDir, "../../daemon/dist/index.js");
|
|
76
|
+
}
|
|
77
|
+
var DAEMON_START_TIMEOUT = 5e3;
|
|
78
|
+
var POLL_INTERVAL = 200;
|
|
79
|
+
async function isDaemonRunning() {
|
|
80
|
+
try {
|
|
81
|
+
const controller = new AbortController();
|
|
82
|
+
const timeoutId = setTimeout(() => controller.abort(), 2e3);
|
|
83
|
+
const response = await fetch(`${DAEMON_BASE_URL}/status`, {
|
|
84
|
+
signal: controller.signal
|
|
85
|
+
});
|
|
86
|
+
clearTimeout(timeoutId);
|
|
87
|
+
return response.ok;
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function waitForDaemon(timeoutMs) {
|
|
93
|
+
const startTime = Date.now();
|
|
94
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
95
|
+
if (await isDaemonRunning()) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
await new Promise((resolve2) => setTimeout(resolve2, POLL_INTERVAL));
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
function spawnDaemon() {
|
|
103
|
+
const daemonPath = getDaemonPath();
|
|
104
|
+
const daemonProcess = spawn(process.execPath, [daemonPath], {
|
|
105
|
+
detached: true,
|
|
106
|
+
stdio: "ignore",
|
|
107
|
+
env: { ...process.env }
|
|
108
|
+
});
|
|
109
|
+
daemonProcess.unref();
|
|
110
|
+
}
|
|
111
|
+
async function ensureDaemonRunning() {
|
|
112
|
+
if (await isDaemonRunning()) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
spawnDaemon();
|
|
116
|
+
const ready = await waitForDaemon(DAEMON_START_TIMEOUT);
|
|
117
|
+
if (!ready) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
"\u65E0\u6CD5\u542F\u52A8 Daemon\u3002\u8BF7\u624B\u52A8\u8FD0\u884C bb-browser daemon \u6216 bb-daemon \u542F\u52A8\u670D\u52A1"
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function stopDaemon() {
|
|
124
|
+
try {
|
|
125
|
+
const controller = new AbortController();
|
|
126
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
127
|
+
const response = await fetch(`${DAEMON_BASE_URL}/shutdown`, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
signal: controller.signal
|
|
130
|
+
});
|
|
131
|
+
clearTimeout(timeoutId);
|
|
132
|
+
return response.ok;
|
|
133
|
+
} catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// packages/cli/src/commands/open.ts
|
|
139
|
+
async function openCommand(url, options = {}) {
|
|
140
|
+
if (!url) {
|
|
141
|
+
throw new Error("\u7F3A\u5C11 URL \u53C2\u6570");
|
|
142
|
+
}
|
|
143
|
+
await ensureDaemonRunning();
|
|
144
|
+
let normalizedUrl = url;
|
|
145
|
+
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
146
|
+
normalizedUrl = "https://" + url;
|
|
147
|
+
}
|
|
148
|
+
const request = {
|
|
149
|
+
id: generateId(),
|
|
150
|
+
action: "open",
|
|
151
|
+
url: normalizedUrl
|
|
152
|
+
};
|
|
153
|
+
if (options.tab !== void 0) {
|
|
154
|
+
if (options.tab === "current") {
|
|
155
|
+
request.tabId = "current";
|
|
156
|
+
} else {
|
|
157
|
+
const tabId = parseInt(options.tab, 10);
|
|
158
|
+
if (isNaN(tabId)) {
|
|
159
|
+
throw new Error(`\u65E0\u6548\u7684 tabId: ${options.tab}`);
|
|
160
|
+
}
|
|
161
|
+
request.tabId = tabId;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const response = await sendCommand(request);
|
|
165
|
+
if (options.json) {
|
|
166
|
+
console.log(JSON.stringify(response, null, 2));
|
|
167
|
+
} else {
|
|
168
|
+
if (response.success) {
|
|
169
|
+
console.log(`\u5DF2\u6253\u5F00: ${response.data?.url ?? normalizedUrl}`);
|
|
170
|
+
if (response.data?.title) {
|
|
171
|
+
console.log(`\u6807\u9898: ${response.data.title}`);
|
|
172
|
+
}
|
|
173
|
+
if (response.data?.tabId) {
|
|
174
|
+
console.log(`Tab ID: ${response.data.tabId}`);
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// packages/cli/src/commands/snapshot.ts
|
|
184
|
+
async function snapshotCommand(options = {}) {
|
|
185
|
+
await ensureDaemonRunning();
|
|
186
|
+
const request = {
|
|
187
|
+
id: generateId(),
|
|
188
|
+
action: "snapshot",
|
|
189
|
+
interactive: options.interactive
|
|
190
|
+
};
|
|
191
|
+
const response = await sendCommand(request);
|
|
192
|
+
if (options.json) {
|
|
193
|
+
console.log(JSON.stringify(response, null, 2));
|
|
194
|
+
} else {
|
|
195
|
+
if (response.success) {
|
|
196
|
+
console.log(`\u6807\u9898: ${response.data?.title ?? "(\u65E0\u6807\u9898)"}`);
|
|
197
|
+
console.log(`URL: ${response.data?.url ?? "(\u672A\u77E5)"}`);
|
|
198
|
+
if (response.data?.snapshotData?.snapshot) {
|
|
199
|
+
console.log("");
|
|
200
|
+
console.log(response.data.snapshotData.snapshot);
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// packages/cli/src/commands/click.ts
|
|
210
|
+
function parseRef(ref) {
|
|
211
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
212
|
+
}
|
|
213
|
+
async function clickCommand(ref, options = {}) {
|
|
214
|
+
if (!ref) {
|
|
215
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
216
|
+
}
|
|
217
|
+
await ensureDaemonRunning();
|
|
218
|
+
const parsedRef = parseRef(ref);
|
|
219
|
+
const request = {
|
|
220
|
+
id: generateId(),
|
|
221
|
+
action: "click",
|
|
222
|
+
ref: parsedRef
|
|
223
|
+
};
|
|
224
|
+
const response = await sendCommand(request);
|
|
225
|
+
if (options.json) {
|
|
226
|
+
console.log(JSON.stringify(response, null, 2));
|
|
227
|
+
} else {
|
|
228
|
+
if (response.success) {
|
|
229
|
+
const role = response.data?.role ?? "element";
|
|
230
|
+
const name = response.data?.name;
|
|
231
|
+
if (name) {
|
|
232
|
+
console.log(`\u5DF2\u70B9\u51FB: ${role} "${name}"`);
|
|
233
|
+
} else {
|
|
234
|
+
console.log(`\u5DF2\u70B9\u51FB: ${role}`);
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// packages/cli/src/commands/hover.ts
|
|
244
|
+
function parseRef2(ref) {
|
|
245
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
246
|
+
}
|
|
247
|
+
async function hoverCommand(ref, options = {}) {
|
|
248
|
+
if (!ref) {
|
|
249
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
250
|
+
}
|
|
251
|
+
await ensureDaemonRunning();
|
|
252
|
+
const parsedRef = parseRef2(ref);
|
|
253
|
+
const request = {
|
|
254
|
+
id: generateId(),
|
|
255
|
+
action: "hover",
|
|
256
|
+
ref: parsedRef
|
|
257
|
+
};
|
|
258
|
+
const response = await sendCommand(request);
|
|
259
|
+
if (options.json) {
|
|
260
|
+
console.log(JSON.stringify(response, null, 2));
|
|
261
|
+
} else {
|
|
262
|
+
if (response.success) {
|
|
263
|
+
const role = response.data?.role ?? "element";
|
|
264
|
+
const name = response.data?.name;
|
|
265
|
+
if (name) {
|
|
266
|
+
console.log(`\u5DF2\u60AC\u505C: ${role} "${name}"`);
|
|
267
|
+
} else {
|
|
268
|
+
console.log(`\u5DF2\u60AC\u505C: ${role}`);
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// packages/cli/src/commands/fill.ts
|
|
278
|
+
function parseRef3(ref) {
|
|
279
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
280
|
+
}
|
|
281
|
+
async function fillCommand(ref, text, options = {}) {
|
|
282
|
+
if (!ref) {
|
|
283
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
284
|
+
}
|
|
285
|
+
if (text === void 0 || text === null) {
|
|
286
|
+
throw new Error("\u7F3A\u5C11 text \u53C2\u6570");
|
|
287
|
+
}
|
|
288
|
+
await ensureDaemonRunning();
|
|
289
|
+
const parsedRef = parseRef3(ref);
|
|
290
|
+
const request = {
|
|
291
|
+
id: generateId(),
|
|
292
|
+
action: "fill",
|
|
293
|
+
ref: parsedRef,
|
|
294
|
+
text
|
|
295
|
+
};
|
|
296
|
+
const response = await sendCommand(request);
|
|
297
|
+
if (options.json) {
|
|
298
|
+
console.log(JSON.stringify(response, null, 2));
|
|
299
|
+
} else {
|
|
300
|
+
if (response.success) {
|
|
301
|
+
const role = response.data?.role ?? "element";
|
|
302
|
+
const name = response.data?.name;
|
|
303
|
+
if (name) {
|
|
304
|
+
console.log(`\u5DF2\u586B\u5145: ${role} "${name}"`);
|
|
305
|
+
} else {
|
|
306
|
+
console.log(`\u5DF2\u586B\u5145: ${role}`);
|
|
307
|
+
}
|
|
308
|
+
console.log(`\u5185\u5BB9: "${text}"`);
|
|
309
|
+
} else {
|
|
310
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// packages/cli/src/commands/type.ts
|
|
317
|
+
function parseRef4(ref) {
|
|
318
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
319
|
+
}
|
|
320
|
+
async function typeCommand(ref, text, options = {}) {
|
|
321
|
+
if (!ref) {
|
|
322
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
323
|
+
}
|
|
324
|
+
if (text === void 0 || text === null) {
|
|
325
|
+
throw new Error("\u7F3A\u5C11 text \u53C2\u6570");
|
|
326
|
+
}
|
|
327
|
+
await ensureDaemonRunning();
|
|
328
|
+
const parsedRef = parseRef4(ref);
|
|
329
|
+
const request = {
|
|
330
|
+
id: generateId(),
|
|
331
|
+
action: "type",
|
|
332
|
+
ref: parsedRef,
|
|
333
|
+
text
|
|
334
|
+
};
|
|
335
|
+
const response = await sendCommand(request);
|
|
336
|
+
if (options.json) {
|
|
337
|
+
console.log(JSON.stringify(response, null, 2));
|
|
338
|
+
} else {
|
|
339
|
+
if (response.success) {
|
|
340
|
+
const role = response.data?.role ?? "element";
|
|
341
|
+
const name = response.data?.name;
|
|
342
|
+
if (name) {
|
|
343
|
+
console.log(`\u5DF2\u8F93\u5165: ${role} "${name}"`);
|
|
344
|
+
} else {
|
|
345
|
+
console.log(`\u5DF2\u8F93\u5165: ${role}`);
|
|
346
|
+
}
|
|
347
|
+
console.log(`\u5185\u5BB9: "${text}"`);
|
|
348
|
+
} else {
|
|
349
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// packages/cli/src/commands/close.ts
|
|
356
|
+
async function closeCommand(options = {}) {
|
|
357
|
+
await ensureDaemonRunning();
|
|
358
|
+
const request = {
|
|
359
|
+
id: generateId(),
|
|
360
|
+
action: "close"
|
|
361
|
+
};
|
|
362
|
+
const response = await sendCommand(request);
|
|
363
|
+
if (options.json) {
|
|
364
|
+
console.log(JSON.stringify(response, null, 2));
|
|
365
|
+
} else {
|
|
366
|
+
if (response.success) {
|
|
367
|
+
const title = response.data?.title ?? "";
|
|
368
|
+
if (title) {
|
|
369
|
+
console.log(`\u5DF2\u5173\u95ED: "${title}"`);
|
|
370
|
+
} else {
|
|
371
|
+
console.log("\u5DF2\u5173\u95ED\u5F53\u524D\u6807\u7B7E\u9875");
|
|
372
|
+
}
|
|
373
|
+
} else {
|
|
374
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// packages/cli/src/commands/get.ts
|
|
381
|
+
function parseRef5(ref) {
|
|
382
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
383
|
+
}
|
|
384
|
+
async function getCommand(attribute, ref, options = {}) {
|
|
385
|
+
if (attribute === "text" && !ref) {
|
|
386
|
+
throw new Error("get text \u9700\u8981 ref \u53C2\u6570\uFF0C\u5982: get text @5");
|
|
387
|
+
}
|
|
388
|
+
await ensureDaemonRunning();
|
|
389
|
+
const request = {
|
|
390
|
+
id: generateId(),
|
|
391
|
+
action: "get",
|
|
392
|
+
attribute,
|
|
393
|
+
ref: ref ? parseRef5(ref) : void 0
|
|
394
|
+
};
|
|
395
|
+
const response = await sendCommand(request);
|
|
396
|
+
if (options.json) {
|
|
397
|
+
console.log(JSON.stringify(response, null, 2));
|
|
398
|
+
} else {
|
|
399
|
+
if (response.success) {
|
|
400
|
+
const value = response.data?.value ?? "";
|
|
401
|
+
console.log(value);
|
|
402
|
+
} else {
|
|
403
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
404
|
+
process.exit(1);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// packages/cli/src/commands/screenshot.ts
|
|
410
|
+
import fs from "fs";
|
|
411
|
+
import path from "path";
|
|
412
|
+
import os from "os";
|
|
413
|
+
function getDefaultPath() {
|
|
414
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
415
|
+
const filename = `bb-screenshot-${timestamp}.png`;
|
|
416
|
+
return path.join(os.tmpdir(), filename);
|
|
417
|
+
}
|
|
418
|
+
function saveBase64Image(dataUrl, filePath) {
|
|
419
|
+
const base64Data = dataUrl.replace(/^data:image\/png;base64,/, "");
|
|
420
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
421
|
+
const dir = path.dirname(filePath);
|
|
422
|
+
if (!fs.existsSync(dir)) {
|
|
423
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
424
|
+
}
|
|
425
|
+
fs.writeFileSync(filePath, buffer);
|
|
426
|
+
}
|
|
427
|
+
async function screenshotCommand(outputPath, options = {}) {
|
|
428
|
+
await ensureDaemonRunning();
|
|
429
|
+
const filePath = outputPath ? path.resolve(outputPath) : getDefaultPath();
|
|
430
|
+
const request = {
|
|
431
|
+
id: generateId(),
|
|
432
|
+
action: "screenshot"
|
|
433
|
+
};
|
|
434
|
+
const response = await sendCommand(request);
|
|
435
|
+
if (response.success && response.data?.dataUrl) {
|
|
436
|
+
const dataUrl = response.data.dataUrl;
|
|
437
|
+
saveBase64Image(dataUrl, filePath);
|
|
438
|
+
if (options.json) {
|
|
439
|
+
console.log(JSON.stringify({
|
|
440
|
+
success: true,
|
|
441
|
+
path: filePath,
|
|
442
|
+
base64: dataUrl
|
|
443
|
+
}, null, 2));
|
|
444
|
+
} else {
|
|
445
|
+
console.log(`\u622A\u56FE\u5DF2\u4FDD\u5B58: ${filePath}`);
|
|
446
|
+
}
|
|
447
|
+
} else {
|
|
448
|
+
if (options.json) {
|
|
449
|
+
console.log(JSON.stringify(response, null, 2));
|
|
450
|
+
} else {
|
|
451
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
452
|
+
}
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// packages/cli/src/commands/wait.ts
|
|
458
|
+
function isTimeWait(target) {
|
|
459
|
+
return /^\d+$/.test(target);
|
|
460
|
+
}
|
|
461
|
+
function parseRef6(ref) {
|
|
462
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
463
|
+
}
|
|
464
|
+
async function waitCommand(target, options = {}) {
|
|
465
|
+
if (!target) {
|
|
466
|
+
throw new Error("\u7F3A\u5C11\u7B49\u5F85\u76EE\u6807\u53C2\u6570");
|
|
467
|
+
}
|
|
468
|
+
await ensureDaemonRunning();
|
|
469
|
+
let request;
|
|
470
|
+
if (isTimeWait(target)) {
|
|
471
|
+
const ms = parseInt(target, 10);
|
|
472
|
+
request = {
|
|
473
|
+
id: generateId(),
|
|
474
|
+
action: "wait",
|
|
475
|
+
waitType: "time",
|
|
476
|
+
ms
|
|
477
|
+
};
|
|
478
|
+
} else {
|
|
479
|
+
const ref = parseRef6(target);
|
|
480
|
+
request = {
|
|
481
|
+
id: generateId(),
|
|
482
|
+
action: "wait",
|
|
483
|
+
waitType: "element",
|
|
484
|
+
ref
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const response = await sendCommand(request);
|
|
488
|
+
if (options.json) {
|
|
489
|
+
console.log(JSON.stringify(response, null, 2));
|
|
490
|
+
} else {
|
|
491
|
+
if (response.success) {
|
|
492
|
+
if (isTimeWait(target)) {
|
|
493
|
+
console.log(`\u5DF2\u7B49\u5F85 ${target}ms`);
|
|
494
|
+
} else {
|
|
495
|
+
console.log(`\u5143\u7D20 @${parseRef6(target)} \u5DF2\u51FA\u73B0`);
|
|
496
|
+
}
|
|
497
|
+
} else {
|
|
498
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
499
|
+
process.exit(1);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// packages/cli/src/commands/press.ts
|
|
505
|
+
function parseKey(keyString) {
|
|
506
|
+
const parts = keyString.split("+");
|
|
507
|
+
const modifierNames = ["Control", "Alt", "Shift", "Meta"];
|
|
508
|
+
const modifiers = [];
|
|
509
|
+
let key = "";
|
|
510
|
+
for (const part of parts) {
|
|
511
|
+
if (modifierNames.includes(part)) {
|
|
512
|
+
modifiers.push(part);
|
|
513
|
+
} else {
|
|
514
|
+
key = part;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return { key, modifiers };
|
|
518
|
+
}
|
|
519
|
+
async function pressCommand(keyString, options = {}) {
|
|
520
|
+
if (!keyString) {
|
|
521
|
+
throw new Error("\u7F3A\u5C11 key \u53C2\u6570");
|
|
522
|
+
}
|
|
523
|
+
await ensureDaemonRunning();
|
|
524
|
+
const { key, modifiers } = parseKey(keyString);
|
|
525
|
+
if (!key) {
|
|
526
|
+
throw new Error("\u65E0\u6548\u7684\u6309\u952E\u683C\u5F0F");
|
|
527
|
+
}
|
|
528
|
+
const request = {
|
|
529
|
+
id: generateId(),
|
|
530
|
+
action: "press",
|
|
531
|
+
key,
|
|
532
|
+
modifiers
|
|
533
|
+
};
|
|
534
|
+
const response = await sendCommand(request);
|
|
535
|
+
if (options.json) {
|
|
536
|
+
console.log(JSON.stringify(response, null, 2));
|
|
537
|
+
} else {
|
|
538
|
+
if (response.success) {
|
|
539
|
+
const displayKey = modifiers.length > 0 ? `${modifiers.join("+")}+${key}` : key;
|
|
540
|
+
console.log(`\u5DF2\u6309\u4E0B: ${displayKey}`);
|
|
541
|
+
} else {
|
|
542
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
543
|
+
process.exit(1);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// packages/cli/src/commands/scroll.ts
|
|
549
|
+
var VALID_DIRECTIONS = ["up", "down", "left", "right"];
|
|
550
|
+
var DEFAULT_PIXELS = 300;
|
|
551
|
+
async function scrollCommand(direction, pixels, options = {}) {
|
|
552
|
+
if (!direction) {
|
|
553
|
+
throw new Error("\u7F3A\u5C11 direction \u53C2\u6570");
|
|
554
|
+
}
|
|
555
|
+
if (!VALID_DIRECTIONS.includes(direction)) {
|
|
556
|
+
throw new Error(
|
|
557
|
+
`\u65E0\u6548\u7684\u6EDA\u52A8\u65B9\u5411: ${direction}\uFF0C\u652F\u6301: ${VALID_DIRECTIONS.join(", ")}`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
let pixelValue = DEFAULT_PIXELS;
|
|
561
|
+
if (pixels !== void 0) {
|
|
562
|
+
pixelValue = parseInt(pixels, 10);
|
|
563
|
+
if (isNaN(pixelValue) || pixelValue <= 0) {
|
|
564
|
+
throw new Error(`\u65E0\u6548\u7684\u50CF\u7D20\u503C: ${pixels}`);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
await ensureDaemonRunning();
|
|
568
|
+
const request = {
|
|
569
|
+
id: generateId(),
|
|
570
|
+
action: "scroll",
|
|
571
|
+
direction,
|
|
572
|
+
pixels: pixelValue
|
|
573
|
+
};
|
|
574
|
+
const response = await sendCommand(request);
|
|
575
|
+
if (options.json) {
|
|
576
|
+
console.log(JSON.stringify(response, null, 2));
|
|
577
|
+
} else {
|
|
578
|
+
if (response.success) {
|
|
579
|
+
console.log(`\u5DF2\u6EDA\u52A8: ${direction} ${pixelValue}px`);
|
|
580
|
+
} else {
|
|
581
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// packages/cli/src/commands/daemon.ts
|
|
588
|
+
async function daemonCommand(options = {}) {
|
|
589
|
+
if (await isDaemonRunning()) {
|
|
590
|
+
if (options.json) {
|
|
591
|
+
console.log(JSON.stringify({ success: false, error: "Daemon \u5DF2\u5728\u8FD0\u884C" }));
|
|
592
|
+
} else {
|
|
593
|
+
console.log("Daemon \u5DF2\u5728\u8FD0\u884C");
|
|
594
|
+
}
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
try {
|
|
598
|
+
const { startDaemon } = await import("@bb-browser/daemon");
|
|
599
|
+
if (options.json) {
|
|
600
|
+
console.log(JSON.stringify({ success: true, message: "Daemon \u542F\u52A8\u4E2D..." }));
|
|
601
|
+
} else {
|
|
602
|
+
console.log("Daemon \u542F\u52A8\u4E2D...");
|
|
603
|
+
}
|
|
604
|
+
await startDaemon();
|
|
605
|
+
} catch (error) {
|
|
606
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
607
|
+
if (options.json) {
|
|
608
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
609
|
+
} else {
|
|
610
|
+
console.error(`\u542F\u52A8\u5931\u8D25: ${message}`);
|
|
611
|
+
}
|
|
612
|
+
process.exit(1);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
async function stopCommand(options = {}) {
|
|
616
|
+
if (!await isDaemonRunning()) {
|
|
617
|
+
if (options.json) {
|
|
618
|
+
console.log(JSON.stringify({ success: false, error: "Daemon \u672A\u8FD0\u884C" }));
|
|
619
|
+
} else {
|
|
620
|
+
console.log("Daemon \u672A\u8FD0\u884C");
|
|
621
|
+
}
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
const stopped = await stopDaemon();
|
|
625
|
+
if (stopped) {
|
|
626
|
+
if (options.json) {
|
|
627
|
+
console.log(JSON.stringify({ success: true, message: "Daemon \u5DF2\u505C\u6B62" }));
|
|
628
|
+
} else {
|
|
629
|
+
console.log("Daemon \u5DF2\u505C\u6B62");
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
if (options.json) {
|
|
633
|
+
console.log(JSON.stringify({ success: false, error: "\u65E0\u6CD5\u505C\u6B62 Daemon" }));
|
|
634
|
+
} else {
|
|
635
|
+
console.error("\u65E0\u6CD5\u505C\u6B62 Daemon");
|
|
636
|
+
}
|
|
637
|
+
process.exit(1);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
async function statusCommand(options = {}) {
|
|
641
|
+
const running = await isDaemonRunning();
|
|
642
|
+
if (options.json) {
|
|
643
|
+
console.log(JSON.stringify({ running }));
|
|
644
|
+
} else {
|
|
645
|
+
console.log(running ? "Daemon \u8FD0\u884C\u4E2D" : "Daemon \u672A\u8FD0\u884C");
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// packages/cli/src/commands/reload.ts
|
|
650
|
+
import WebSocket from "ws";
|
|
651
|
+
var EXTENSION_NAME = "bb-browser";
|
|
652
|
+
async function reloadCommand(options = {}) {
|
|
653
|
+
const port = options.port || 9222;
|
|
654
|
+
try {
|
|
655
|
+
const listRes = await fetch(`http://127.0.0.1:${port}/json/list`);
|
|
656
|
+
if (!listRes.ok) {
|
|
657
|
+
throw new Error(`CDP \u672A\u542F\u7528\u3002\u8BF7\u7528 --remote-debugging-port=${port} \u542F\u52A8 Chrome`);
|
|
658
|
+
}
|
|
659
|
+
const list = await listRes.json();
|
|
660
|
+
const extPage = list.find(
|
|
661
|
+
(t) => t.type === "page" && t.url.includes("chrome://extensions")
|
|
662
|
+
);
|
|
663
|
+
if (!extPage) {
|
|
664
|
+
throw new Error("\u8BF7\u5148\u6253\u5F00 chrome://extensions \u9875\u9762");
|
|
665
|
+
}
|
|
666
|
+
const result = await new Promise((resolve2, reject) => {
|
|
667
|
+
const ws = new WebSocket(extPage.webSocketDebuggerUrl);
|
|
668
|
+
let resolved = false;
|
|
669
|
+
const timeout = setTimeout(() => {
|
|
670
|
+
if (!resolved) {
|
|
671
|
+
resolved = true;
|
|
672
|
+
ws.close();
|
|
673
|
+
reject(new Error("CDP \u8FDE\u63A5\u8D85\u65F6"));
|
|
674
|
+
}
|
|
675
|
+
}, 1e4);
|
|
676
|
+
ws.on("open", () => {
|
|
677
|
+
const script = `
|
|
678
|
+
(async function() {
|
|
679
|
+
if (!chrome || !chrome.developerPrivate) {
|
|
680
|
+
return { error: 'developerPrivate API not available' };
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
try {
|
|
684
|
+
const exts = await chrome.developerPrivate.getExtensionsInfo();
|
|
685
|
+
const bbExt = exts.find(e => e.name === '${EXTENSION_NAME}');
|
|
686
|
+
|
|
687
|
+
if (!bbExt) {
|
|
688
|
+
return { error: '${EXTENSION_NAME} \u6269\u5C55\u672A\u5B89\u88C5' };
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (bbExt.state !== 'ENABLED') {
|
|
692
|
+
return { error: '${EXTENSION_NAME} \u6269\u5C55\u5DF2\u7981\u7528' };
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
await chrome.developerPrivate.reload(bbExt.id, {failQuietly: true});
|
|
696
|
+
return { success: true, extensionId: bbExt.id };
|
|
697
|
+
} catch (e) {
|
|
698
|
+
return { error: e.message };
|
|
699
|
+
}
|
|
700
|
+
})()
|
|
701
|
+
`;
|
|
702
|
+
ws.send(JSON.stringify({
|
|
703
|
+
id: 1,
|
|
704
|
+
method: "Runtime.evaluate",
|
|
705
|
+
params: {
|
|
706
|
+
expression: script,
|
|
707
|
+
awaitPromise: true,
|
|
708
|
+
returnByValue: true
|
|
709
|
+
}
|
|
710
|
+
}));
|
|
711
|
+
});
|
|
712
|
+
ws.on("message", (data) => {
|
|
713
|
+
const msg = JSON.parse(data.toString());
|
|
714
|
+
if (msg.id === 1) {
|
|
715
|
+
clearTimeout(timeout);
|
|
716
|
+
resolved = true;
|
|
717
|
+
ws.close();
|
|
718
|
+
const value = msg.result?.result?.value;
|
|
719
|
+
if (value?.success) {
|
|
720
|
+
resolve2({
|
|
721
|
+
success: true,
|
|
722
|
+
message: "\u6269\u5C55\u5DF2\u91CD\u8F7D",
|
|
723
|
+
extensionId: value.extensionId
|
|
724
|
+
});
|
|
725
|
+
} else if (value?.error) {
|
|
726
|
+
reject(new Error(value.error));
|
|
727
|
+
} else {
|
|
728
|
+
reject(new Error(`\u91CD\u8F7D\u5931\u8D25: ${JSON.stringify(value)}`));
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
ws.on("error", (err) => {
|
|
733
|
+
clearTimeout(timeout);
|
|
734
|
+
if (!resolved) {
|
|
735
|
+
resolved = true;
|
|
736
|
+
reject(new Error(`CDP \u8FDE\u63A5\u5931\u8D25: ${err.message}`));
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
if (options.json) {
|
|
741
|
+
console.log(JSON.stringify(result));
|
|
742
|
+
} else {
|
|
743
|
+
console.log(`${result.message} (${result.extensionId})`);
|
|
744
|
+
}
|
|
745
|
+
} catch (error) {
|
|
746
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
747
|
+
if (options.json) {
|
|
748
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
749
|
+
} else {
|
|
750
|
+
console.error(`\u9519\u8BEF: ${message}`);
|
|
751
|
+
}
|
|
752
|
+
process.exit(1);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// packages/cli/src/commands/nav.ts
|
|
757
|
+
async function backCommand(options = {}) {
|
|
758
|
+
await ensureDaemonRunning();
|
|
759
|
+
const request = {
|
|
760
|
+
id: generateId(),
|
|
761
|
+
action: "back"
|
|
762
|
+
};
|
|
763
|
+
const response = await sendCommand(request);
|
|
764
|
+
if (options.json) {
|
|
765
|
+
console.log(JSON.stringify(response, null, 2));
|
|
766
|
+
} else {
|
|
767
|
+
if (response.success) {
|
|
768
|
+
const url = response.data?.url ?? "";
|
|
769
|
+
if (url) {
|
|
770
|
+
console.log(`\u540E\u9000\u81F3: ${url}`);
|
|
771
|
+
} else {
|
|
772
|
+
console.log("\u5DF2\u540E\u9000");
|
|
773
|
+
}
|
|
774
|
+
} else {
|
|
775
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
776
|
+
process.exit(1);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
async function forwardCommand(options = {}) {
|
|
781
|
+
await ensureDaemonRunning();
|
|
782
|
+
const request = {
|
|
783
|
+
id: generateId(),
|
|
784
|
+
action: "forward"
|
|
785
|
+
};
|
|
786
|
+
const response = await sendCommand(request);
|
|
787
|
+
if (options.json) {
|
|
788
|
+
console.log(JSON.stringify(response, null, 2));
|
|
789
|
+
} else {
|
|
790
|
+
if (response.success) {
|
|
791
|
+
const url = response.data?.url ?? "";
|
|
792
|
+
if (url) {
|
|
793
|
+
console.log(`\u524D\u8FDB\u81F3: ${url}`);
|
|
794
|
+
} else {
|
|
795
|
+
console.log("\u5DF2\u524D\u8FDB");
|
|
796
|
+
}
|
|
797
|
+
} else {
|
|
798
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
799
|
+
process.exit(1);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
async function refreshCommand(options = {}) {
|
|
804
|
+
await ensureDaemonRunning();
|
|
805
|
+
const request = {
|
|
806
|
+
id: generateId(),
|
|
807
|
+
action: "refresh"
|
|
808
|
+
};
|
|
809
|
+
const response = await sendCommand(request);
|
|
810
|
+
if (options.json) {
|
|
811
|
+
console.log(JSON.stringify(response, null, 2));
|
|
812
|
+
} else {
|
|
813
|
+
if (response.success) {
|
|
814
|
+
const title = response.data?.title ?? "";
|
|
815
|
+
if (title) {
|
|
816
|
+
console.log(`\u5DF2\u5237\u65B0: "${title}"`);
|
|
817
|
+
} else {
|
|
818
|
+
console.log("\u5DF2\u5237\u65B0\u9875\u9762");
|
|
819
|
+
}
|
|
820
|
+
} else {
|
|
821
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
822
|
+
process.exit(1);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// packages/cli/src/commands/check.ts
|
|
828
|
+
function parseRef7(ref) {
|
|
829
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
830
|
+
}
|
|
831
|
+
async function checkCommand(ref, options = {}) {
|
|
832
|
+
if (!ref) {
|
|
833
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
834
|
+
}
|
|
835
|
+
await ensureDaemonRunning();
|
|
836
|
+
const parsedRef = parseRef7(ref);
|
|
837
|
+
const request = {
|
|
838
|
+
id: generateId(),
|
|
839
|
+
action: "check",
|
|
840
|
+
ref: parsedRef
|
|
841
|
+
};
|
|
842
|
+
const response = await sendCommand(request);
|
|
843
|
+
if (options.json) {
|
|
844
|
+
console.log(JSON.stringify(response, null, 2));
|
|
845
|
+
} else {
|
|
846
|
+
if (response.success) {
|
|
847
|
+
const role = response.data?.role ?? "checkbox";
|
|
848
|
+
const name = response.data?.name;
|
|
849
|
+
const wasAlreadyChecked = response.data?.wasAlreadyChecked;
|
|
850
|
+
if (wasAlreadyChecked) {
|
|
851
|
+
if (name) {
|
|
852
|
+
console.log(`\u5DF2\u52FE\u9009\uFF08\u4E4B\u524D\u5DF2\u52FE\u9009\uFF09: ${role} "${name}"`);
|
|
853
|
+
} else {
|
|
854
|
+
console.log(`\u5DF2\u52FE\u9009\uFF08\u4E4B\u524D\u5DF2\u52FE\u9009\uFF09: ${role}`);
|
|
855
|
+
}
|
|
856
|
+
} else {
|
|
857
|
+
if (name) {
|
|
858
|
+
console.log(`\u5DF2\u52FE\u9009: ${role} "${name}"`);
|
|
859
|
+
} else {
|
|
860
|
+
console.log(`\u5DF2\u52FE\u9009: ${role}`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
} else {
|
|
864
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
865
|
+
process.exit(1);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
async function uncheckCommand(ref, options = {}) {
|
|
870
|
+
if (!ref) {
|
|
871
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
872
|
+
}
|
|
873
|
+
await ensureDaemonRunning();
|
|
874
|
+
const parsedRef = parseRef7(ref);
|
|
875
|
+
const request = {
|
|
876
|
+
id: generateId(),
|
|
877
|
+
action: "uncheck",
|
|
878
|
+
ref: parsedRef
|
|
879
|
+
};
|
|
880
|
+
const response = await sendCommand(request);
|
|
881
|
+
if (options.json) {
|
|
882
|
+
console.log(JSON.stringify(response, null, 2));
|
|
883
|
+
} else {
|
|
884
|
+
if (response.success) {
|
|
885
|
+
const role = response.data?.role ?? "checkbox";
|
|
886
|
+
const name = response.data?.name;
|
|
887
|
+
const wasAlreadyUnchecked = response.data?.wasAlreadyUnchecked;
|
|
888
|
+
if (wasAlreadyUnchecked) {
|
|
889
|
+
if (name) {
|
|
890
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009\uFF08\u4E4B\u524D\u672A\u52FE\u9009\uFF09: ${role} "${name}"`);
|
|
891
|
+
} else {
|
|
892
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009\uFF08\u4E4B\u524D\u672A\u52FE\u9009\uFF09: ${role}`);
|
|
893
|
+
}
|
|
894
|
+
} else {
|
|
895
|
+
if (name) {
|
|
896
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009: ${role} "${name}"`);
|
|
897
|
+
} else {
|
|
898
|
+
console.log(`\u5DF2\u53D6\u6D88\u52FE\u9009: ${role}`);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
} else {
|
|
902
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
903
|
+
process.exit(1);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// packages/cli/src/commands/select.ts
|
|
909
|
+
function parseRef8(ref) {
|
|
910
|
+
return ref.startsWith("@") ? ref.slice(1) : ref;
|
|
911
|
+
}
|
|
912
|
+
async function selectCommand(ref, value, options = {}) {
|
|
913
|
+
if (!ref) {
|
|
914
|
+
throw new Error("\u7F3A\u5C11 ref \u53C2\u6570");
|
|
915
|
+
}
|
|
916
|
+
if (value === void 0 || value === null) {
|
|
917
|
+
throw new Error("\u7F3A\u5C11 value \u53C2\u6570");
|
|
918
|
+
}
|
|
919
|
+
await ensureDaemonRunning();
|
|
920
|
+
const parsedRef = parseRef8(ref);
|
|
921
|
+
const request = {
|
|
922
|
+
id: generateId(),
|
|
923
|
+
action: "select",
|
|
924
|
+
ref: parsedRef,
|
|
925
|
+
value
|
|
926
|
+
};
|
|
927
|
+
const response = await sendCommand(request);
|
|
928
|
+
if (options.json) {
|
|
929
|
+
console.log(JSON.stringify(response, null, 2));
|
|
930
|
+
} else {
|
|
931
|
+
if (response.success) {
|
|
932
|
+
const role = response.data?.role ?? "combobox";
|
|
933
|
+
const name = response.data?.name;
|
|
934
|
+
const selectedValue = response.data?.selectedValue;
|
|
935
|
+
const selectedLabel = response.data?.selectedLabel;
|
|
936
|
+
if (name) {
|
|
937
|
+
console.log(`\u5DF2\u9009\u62E9: ${role} "${name}"`);
|
|
938
|
+
} else {
|
|
939
|
+
console.log(`\u5DF2\u9009\u62E9: ${role}`);
|
|
940
|
+
}
|
|
941
|
+
if (selectedLabel && selectedLabel !== selectedValue) {
|
|
942
|
+
console.log(`\u9009\u9879: "${selectedLabel}" (value="${selectedValue}")`);
|
|
943
|
+
} else {
|
|
944
|
+
console.log(`\u9009\u9879: "${selectedValue}"`);
|
|
945
|
+
}
|
|
946
|
+
} else {
|
|
947
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
948
|
+
process.exit(1);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// packages/cli/src/commands/eval.ts
|
|
954
|
+
async function evalCommand(script, options = {}) {
|
|
955
|
+
if (!script) {
|
|
956
|
+
throw new Error("\u7F3A\u5C11 script \u53C2\u6570");
|
|
957
|
+
}
|
|
958
|
+
await ensureDaemonRunning();
|
|
959
|
+
const request = {
|
|
960
|
+
id: generateId(),
|
|
961
|
+
action: "eval",
|
|
962
|
+
script
|
|
963
|
+
};
|
|
964
|
+
const response = await sendCommand(request);
|
|
965
|
+
if (options.json) {
|
|
966
|
+
console.log(JSON.stringify(response, null, 2));
|
|
967
|
+
} else {
|
|
968
|
+
if (response.success) {
|
|
969
|
+
const result = response.data?.result;
|
|
970
|
+
if (result !== void 0) {
|
|
971
|
+
if (typeof result === "object" && result !== null) {
|
|
972
|
+
console.log(JSON.stringify(result, null, 2));
|
|
973
|
+
} else {
|
|
974
|
+
console.log(result);
|
|
975
|
+
}
|
|
976
|
+
} else {
|
|
977
|
+
console.log("undefined");
|
|
978
|
+
}
|
|
979
|
+
} else {
|
|
980
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
981
|
+
process.exit(1);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// packages/cli/src/commands/tab.ts
|
|
987
|
+
function parseTabSubcommand(args) {
|
|
988
|
+
if (args.length === 0) {
|
|
989
|
+
return { action: "tab_list" };
|
|
990
|
+
}
|
|
991
|
+
const first = args[0];
|
|
992
|
+
if (first === "new") {
|
|
993
|
+
return { action: "tab_new", url: args[1] };
|
|
994
|
+
}
|
|
995
|
+
if (first === "close") {
|
|
996
|
+
const indexArg = args[1];
|
|
997
|
+
if (indexArg !== void 0) {
|
|
998
|
+
const index2 = parseInt(indexArg, 10);
|
|
999
|
+
if (isNaN(index2) || index2 < 0) {
|
|
1000
|
+
throw new Error(`\u65E0\u6548\u7684\u6807\u7B7E\u9875\u7D22\u5F15: ${indexArg}`);
|
|
1001
|
+
}
|
|
1002
|
+
return { action: "tab_close", index: index2 };
|
|
1003
|
+
}
|
|
1004
|
+
return { action: "tab_close" };
|
|
1005
|
+
}
|
|
1006
|
+
const index = parseInt(first, 10);
|
|
1007
|
+
if (!isNaN(index) && index >= 0) {
|
|
1008
|
+
return { action: "tab_select", index };
|
|
1009
|
+
}
|
|
1010
|
+
throw new Error(`\u672A\u77E5\u7684 tab \u5B50\u547D\u4EE4: ${first}`);
|
|
1011
|
+
}
|
|
1012
|
+
function formatTabList(tabs, activeIndex) {
|
|
1013
|
+
const lines = [];
|
|
1014
|
+
lines.push(`\u6807\u7B7E\u9875\u5217\u8868\uFF08\u5171 ${tabs.length} \u4E2A\uFF0C\u5F53\u524D #${activeIndex}\uFF09\uFF1A`);
|
|
1015
|
+
for (const tab of tabs) {
|
|
1016
|
+
const prefix = tab.active ? "*" : " ";
|
|
1017
|
+
const title = tab.title || "(\u65E0\u6807\u9898)";
|
|
1018
|
+
lines.push(`${prefix} [${tab.index}] ${tab.url} - ${title}`);
|
|
1019
|
+
}
|
|
1020
|
+
return lines.join("\n");
|
|
1021
|
+
}
|
|
1022
|
+
async function tabCommand(args, options = {}) {
|
|
1023
|
+
await ensureDaemonRunning();
|
|
1024
|
+
const parsed = parseTabSubcommand(args);
|
|
1025
|
+
const request = {
|
|
1026
|
+
id: generateId(),
|
|
1027
|
+
action: parsed.action,
|
|
1028
|
+
url: parsed.url,
|
|
1029
|
+
index: parsed.index
|
|
1030
|
+
};
|
|
1031
|
+
const response = await sendCommand(request);
|
|
1032
|
+
if (options.json) {
|
|
1033
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1034
|
+
} else {
|
|
1035
|
+
if (response.success) {
|
|
1036
|
+
switch (parsed.action) {
|
|
1037
|
+
case "tab_list": {
|
|
1038
|
+
const tabs = response.data?.tabs ?? [];
|
|
1039
|
+
const activeIndex = response.data?.activeIndex ?? 0;
|
|
1040
|
+
console.log(formatTabList(tabs, activeIndex));
|
|
1041
|
+
break;
|
|
1042
|
+
}
|
|
1043
|
+
case "tab_new": {
|
|
1044
|
+
const url = response.data?.url ?? "about:blank";
|
|
1045
|
+
console.log(`\u5DF2\u521B\u5EFA\u65B0\u6807\u7B7E\u9875: ${url}`);
|
|
1046
|
+
break;
|
|
1047
|
+
}
|
|
1048
|
+
case "tab_select": {
|
|
1049
|
+
const title = response.data?.title ?? "(\u65E0\u6807\u9898)";
|
|
1050
|
+
const url = response.data?.url ?? "";
|
|
1051
|
+
console.log(`\u5DF2\u5207\u6362\u5230\u6807\u7B7E\u9875 #${parsed.index}: ${title}`);
|
|
1052
|
+
console.log(` URL: ${url}`);
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
case "tab_close": {
|
|
1056
|
+
const closedTitle = response.data?.title ?? "(\u65E0\u6807\u9898)";
|
|
1057
|
+
console.log(`\u5DF2\u5173\u95ED\u6807\u7B7E\u9875: ${closedTitle}`);
|
|
1058
|
+
break;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
} else {
|
|
1062
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1063
|
+
process.exit(1);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// packages/cli/src/commands/frame.ts
|
|
1069
|
+
async function frameCommand(selector, options = {}) {
|
|
1070
|
+
if (!selector) {
|
|
1071
|
+
throw new Error("\u7F3A\u5C11 selector \u53C2\u6570");
|
|
1072
|
+
}
|
|
1073
|
+
await ensureDaemonRunning();
|
|
1074
|
+
const request = {
|
|
1075
|
+
id: generateId(),
|
|
1076
|
+
action: "frame",
|
|
1077
|
+
selector
|
|
1078
|
+
};
|
|
1079
|
+
const response = await sendCommand(request);
|
|
1080
|
+
if (options.json) {
|
|
1081
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1082
|
+
} else {
|
|
1083
|
+
if (response.success) {
|
|
1084
|
+
const frameInfo = response.data?.frameInfo;
|
|
1085
|
+
if (frameInfo?.url) {
|
|
1086
|
+
console.log(`\u5DF2\u5207\u6362\u5230 frame: ${selector} (${frameInfo.url})`);
|
|
1087
|
+
} else {
|
|
1088
|
+
console.log(`\u5DF2\u5207\u6362\u5230 frame: ${selector}`);
|
|
1089
|
+
}
|
|
1090
|
+
} else {
|
|
1091
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1092
|
+
process.exit(1);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
async function frameMainCommand(options = {}) {
|
|
1097
|
+
await ensureDaemonRunning();
|
|
1098
|
+
const request = {
|
|
1099
|
+
id: generateId(),
|
|
1100
|
+
action: "frame_main"
|
|
1101
|
+
};
|
|
1102
|
+
const response = await sendCommand(request);
|
|
1103
|
+
if (options.json) {
|
|
1104
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1105
|
+
} else {
|
|
1106
|
+
if (response.success) {
|
|
1107
|
+
console.log("\u5DF2\u8FD4\u56DE\u4E3B frame");
|
|
1108
|
+
} else {
|
|
1109
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1110
|
+
process.exit(1);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// packages/cli/src/commands/dialog.ts
|
|
1116
|
+
async function dialogCommand(subCommand, promptText, options = {}) {
|
|
1117
|
+
if (!subCommand || !["accept", "dismiss"].includes(subCommand)) {
|
|
1118
|
+
throw new Error("\u8BF7\u4F7F\u7528 'dialog accept [text]' \u6216 'dialog dismiss'");
|
|
1119
|
+
}
|
|
1120
|
+
await ensureDaemonRunning();
|
|
1121
|
+
const request = {
|
|
1122
|
+
id: generateId(),
|
|
1123
|
+
action: "dialog",
|
|
1124
|
+
dialogResponse: subCommand,
|
|
1125
|
+
promptText: subCommand === "accept" ? promptText : void 0
|
|
1126
|
+
};
|
|
1127
|
+
const response = await sendCommand(request);
|
|
1128
|
+
if (options.json) {
|
|
1129
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1130
|
+
} else {
|
|
1131
|
+
if (response.success) {
|
|
1132
|
+
const dialogInfo = response.data?.dialogInfo;
|
|
1133
|
+
if (dialogInfo) {
|
|
1134
|
+
const action = subCommand === "accept" ? "\u5DF2\u63A5\u53D7" : "\u5DF2\u62D2\u7EDD";
|
|
1135
|
+
console.log(`${action}\u5BF9\u8BDD\u6846\uFF08${dialogInfo.type}\uFF09: "${dialogInfo.message}"`);
|
|
1136
|
+
} else {
|
|
1137
|
+
console.log("\u5BF9\u8BDD\u6846\u5DF2\u5904\u7406");
|
|
1138
|
+
}
|
|
1139
|
+
} else {
|
|
1140
|
+
console.error(`\u9519\u8BEF: ${response.error}`);
|
|
1141
|
+
process.exit(1);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// packages/cli/src/commands/network.ts
|
|
1147
|
+
async function networkCommand(subCommand, urlOrFilter, options = {}) {
|
|
1148
|
+
const response = await sendCommand({
|
|
1149
|
+
id: crypto.randomUUID(),
|
|
1150
|
+
action: "network",
|
|
1151
|
+
networkCommand: subCommand,
|
|
1152
|
+
url: subCommand === "route" || subCommand === "unroute" ? urlOrFilter : void 0,
|
|
1153
|
+
filter: subCommand === "requests" ? urlOrFilter : void 0,
|
|
1154
|
+
routeOptions: subCommand === "route" ? {
|
|
1155
|
+
abort: options.abort,
|
|
1156
|
+
body: options.body
|
|
1157
|
+
} : void 0
|
|
1158
|
+
});
|
|
1159
|
+
if (options.json) {
|
|
1160
|
+
console.log(JSON.stringify(response));
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
if (!response.success) {
|
|
1164
|
+
throw new Error(response.error || "Network command failed");
|
|
1165
|
+
}
|
|
1166
|
+
const data = response.data;
|
|
1167
|
+
switch (subCommand) {
|
|
1168
|
+
case "requests": {
|
|
1169
|
+
const requests = data?.networkRequests || [];
|
|
1170
|
+
if (requests.length === 0) {
|
|
1171
|
+
console.log("\u6CA1\u6709\u7F51\u7EDC\u8BF7\u6C42\u8BB0\u5F55");
|
|
1172
|
+
console.log("\u63D0\u793A: \u4F7F\u7528 network requests \u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1173
|
+
} else {
|
|
1174
|
+
console.log(`\u7F51\u7EDC\u8BF7\u6C42 (${requests.length} \u6761):
|
|
1175
|
+
`);
|
|
1176
|
+
for (const req of requests) {
|
|
1177
|
+
const status = req.failed ? `FAILED (${req.failureReason})` : req.status ? `${req.status} ${req.statusText || ""}` : "pending";
|
|
1178
|
+
console.log(`${req.method} ${req.url}`);
|
|
1179
|
+
console.log(` \u7C7B\u578B: ${req.type}, \u72B6\u6001: ${status}`);
|
|
1180
|
+
console.log("");
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
break;
|
|
1184
|
+
}
|
|
1185
|
+
case "route": {
|
|
1186
|
+
console.log(`\u5DF2\u6DFB\u52A0\u62E6\u622A\u89C4\u5219: ${urlOrFilter}`);
|
|
1187
|
+
if (options.abort) {
|
|
1188
|
+
console.log(" \u884C\u4E3A: \u963B\u6B62\u8BF7\u6C42");
|
|
1189
|
+
} else if (options.body) {
|
|
1190
|
+
console.log(" \u884C\u4E3A: \u8FD4\u56DE mock \u6570\u636E");
|
|
1191
|
+
} else {
|
|
1192
|
+
console.log(" \u884C\u4E3A: \u7EE7\u7EED\u8BF7\u6C42");
|
|
1193
|
+
}
|
|
1194
|
+
console.log(`\u5F53\u524D\u89C4\u5219\u6570: ${data?.routeCount || 0}`);
|
|
1195
|
+
break;
|
|
1196
|
+
}
|
|
1197
|
+
case "unroute": {
|
|
1198
|
+
if (urlOrFilter) {
|
|
1199
|
+
console.log(`\u5DF2\u79FB\u9664\u62E6\u622A\u89C4\u5219: ${urlOrFilter}`);
|
|
1200
|
+
} else {
|
|
1201
|
+
console.log("\u5DF2\u79FB\u9664\u6240\u6709\u62E6\u622A\u89C4\u5219");
|
|
1202
|
+
}
|
|
1203
|
+
console.log(`\u5269\u4F59\u89C4\u5219\u6570: ${data?.routeCount || 0}`);
|
|
1204
|
+
break;
|
|
1205
|
+
}
|
|
1206
|
+
case "clear": {
|
|
1207
|
+
console.log("\u5DF2\u6E05\u7A7A\u7F51\u7EDC\u8BF7\u6C42\u8BB0\u5F55");
|
|
1208
|
+
break;
|
|
1209
|
+
}
|
|
1210
|
+
default:
|
|
1211
|
+
throw new Error(`\u672A\u77E5\u7684 network \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// packages/cli/src/commands/console.ts
|
|
1216
|
+
async function consoleCommand(options = {}) {
|
|
1217
|
+
const response = await sendCommand({
|
|
1218
|
+
id: crypto.randomUUID(),
|
|
1219
|
+
action: "console",
|
|
1220
|
+
consoleCommand: options.clear ? "clear" : "get"
|
|
1221
|
+
});
|
|
1222
|
+
if (options.json) {
|
|
1223
|
+
console.log(JSON.stringify(response));
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
if (!response.success) {
|
|
1227
|
+
throw new Error(response.error || "Console command failed");
|
|
1228
|
+
}
|
|
1229
|
+
if (options.clear) {
|
|
1230
|
+
console.log("\u5DF2\u6E05\u7A7A\u63A7\u5236\u53F0\u6D88\u606F");
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
const messages = response.data?.consoleMessages || [];
|
|
1234
|
+
if (messages.length === 0) {
|
|
1235
|
+
console.log("\u6CA1\u6709\u63A7\u5236\u53F0\u6D88\u606F");
|
|
1236
|
+
console.log("\u63D0\u793A: console \u547D\u4EE4\u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
console.log(`\u63A7\u5236\u53F0\u6D88\u606F (${messages.length} \u6761):
|
|
1240
|
+
`);
|
|
1241
|
+
const typeColors = {
|
|
1242
|
+
log: "",
|
|
1243
|
+
info: "[INFO]",
|
|
1244
|
+
warn: "[WARN]",
|
|
1245
|
+
error: "[ERROR]",
|
|
1246
|
+
debug: "[DEBUG]"
|
|
1247
|
+
};
|
|
1248
|
+
for (const msg of messages) {
|
|
1249
|
+
const prefix = typeColors[msg.type] || `[${msg.type.toUpperCase()}]`;
|
|
1250
|
+
const location = msg.url ? ` (${msg.url}${msg.lineNumber ? `:${msg.lineNumber}` : ""})` : "";
|
|
1251
|
+
if (prefix) {
|
|
1252
|
+
console.log(`${prefix} ${msg.text}${location}`);
|
|
1253
|
+
} else {
|
|
1254
|
+
console.log(`${msg.text}${location}`);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
// packages/cli/src/commands/errors.ts
|
|
1260
|
+
async function errorsCommand(options = {}) {
|
|
1261
|
+
const response = await sendCommand({
|
|
1262
|
+
id: crypto.randomUUID(),
|
|
1263
|
+
action: "errors",
|
|
1264
|
+
errorsCommand: options.clear ? "clear" : "get"
|
|
1265
|
+
});
|
|
1266
|
+
if (options.json) {
|
|
1267
|
+
console.log(JSON.stringify(response));
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
if (!response.success) {
|
|
1271
|
+
throw new Error(response.error || "Errors command failed");
|
|
1272
|
+
}
|
|
1273
|
+
if (options.clear) {
|
|
1274
|
+
console.log("\u5DF2\u6E05\u7A7A JS \u9519\u8BEF\u8BB0\u5F55");
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
const errors = response.data?.jsErrors || [];
|
|
1278
|
+
if (errors.length === 0) {
|
|
1279
|
+
console.log("\u6CA1\u6709 JS \u9519\u8BEF");
|
|
1280
|
+
console.log("\u63D0\u793A: errors \u547D\u4EE4\u4F1A\u81EA\u52A8\u5F00\u59CB\u76D1\u63A7");
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
console.log(`JS \u9519\u8BEF (${errors.length} \u6761):
|
|
1284
|
+
`);
|
|
1285
|
+
for (const err of errors) {
|
|
1286
|
+
console.log(`[ERROR] ${err.message}`);
|
|
1287
|
+
if (err.url) {
|
|
1288
|
+
console.log(` \u4F4D\u7F6E: ${err.url}:${err.lineNumber || 0}:${err.columnNumber || 0}`);
|
|
1289
|
+
}
|
|
1290
|
+
if (err.stackTrace) {
|
|
1291
|
+
console.log(` \u5806\u6808:`);
|
|
1292
|
+
console.log(err.stackTrace.split("\n").map((line) => ` ${line}`).join("\n"));
|
|
1293
|
+
}
|
|
1294
|
+
console.log("");
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// packages/cli/src/commands/trace.ts
|
|
1299
|
+
async function traceCommand(subCommand, options = {}) {
|
|
1300
|
+
const response = await sendCommand({
|
|
1301
|
+
id: crypto.randomUUID(),
|
|
1302
|
+
action: "trace",
|
|
1303
|
+
traceCommand: subCommand
|
|
1304
|
+
});
|
|
1305
|
+
if (options.json) {
|
|
1306
|
+
console.log(JSON.stringify(response));
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
if (!response.success) {
|
|
1310
|
+
throw new Error(response.error || "Trace command failed");
|
|
1311
|
+
}
|
|
1312
|
+
const data = response.data;
|
|
1313
|
+
switch (subCommand) {
|
|
1314
|
+
case "start": {
|
|
1315
|
+
const status = data?.traceStatus;
|
|
1316
|
+
console.log("\u5F00\u59CB\u5F55\u5236\u7528\u6237\u64CD\u4F5C");
|
|
1317
|
+
console.log(`\u6807\u7B7E\u9875 ID: ${status?.tabId || "N/A"}`);
|
|
1318
|
+
console.log("\n\u5728\u6D4F\u89C8\u5668\u4E2D\u8FDB\u884C\u64CD\u4F5C\uFF0C\u5B8C\u6210\u540E\u8FD0\u884C 'bb-browser trace stop' \u505C\u6B62\u5F55\u5236");
|
|
1319
|
+
break;
|
|
1320
|
+
}
|
|
1321
|
+
case "stop": {
|
|
1322
|
+
const events = data?.traceEvents || [];
|
|
1323
|
+
const status = data?.traceStatus;
|
|
1324
|
+
console.log(`\u5F55\u5236\u5B8C\u6210\uFF0C\u5171 ${events.length} \u4E2A\u4E8B\u4EF6
|
|
1325
|
+
`);
|
|
1326
|
+
if (events.length === 0) {
|
|
1327
|
+
console.log("\u6CA1\u6709\u5F55\u5236\u5230\u4EFB\u4F55\u64CD\u4F5C");
|
|
1328
|
+
break;
|
|
1329
|
+
}
|
|
1330
|
+
for (let i = 0; i < events.length; i++) {
|
|
1331
|
+
const event = events[i];
|
|
1332
|
+
const refStr = event.ref !== void 0 ? `@${event.ref}` : "";
|
|
1333
|
+
switch (event.type) {
|
|
1334
|
+
case "navigation":
|
|
1335
|
+
console.log(`${i + 1}. \u5BFC\u822A\u5230: ${event.url}`);
|
|
1336
|
+
break;
|
|
1337
|
+
case "click":
|
|
1338
|
+
console.log(`${i + 1}. \u70B9\u51FB ${refStr} [${event.elementRole}] "${event.elementName || ""}"`);
|
|
1339
|
+
break;
|
|
1340
|
+
case "fill":
|
|
1341
|
+
console.log(`${i + 1}. \u586B\u5145 ${refStr} [${event.elementRole}] "${event.elementName || ""}" <- "${event.value}"`);
|
|
1342
|
+
break;
|
|
1343
|
+
case "select":
|
|
1344
|
+
console.log(`${i + 1}. \u9009\u62E9 ${refStr} [${event.elementRole}] "${event.elementName || ""}" <- "${event.value}"`);
|
|
1345
|
+
break;
|
|
1346
|
+
case "check":
|
|
1347
|
+
console.log(`${i + 1}. ${event.checked ? "\u52FE\u9009" : "\u53D6\u6D88\u52FE\u9009"} ${refStr} [${event.elementRole}] "${event.elementName || ""}"`);
|
|
1348
|
+
break;
|
|
1349
|
+
case "press":
|
|
1350
|
+
console.log(`${i + 1}. \u6309\u952E ${event.key}`);
|
|
1351
|
+
break;
|
|
1352
|
+
case "scroll":
|
|
1353
|
+
console.log(`${i + 1}. \u6EDA\u52A8 ${event.direction} ${event.pixels}px`);
|
|
1354
|
+
break;
|
|
1355
|
+
default:
|
|
1356
|
+
console.log(`${i + 1}. ${event.type}`);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
console.log(`
|
|
1360
|
+
\u72B6\u6001: ${status?.recording ? "\u5F55\u5236\u4E2D" : "\u5DF2\u505C\u6B62"}`);
|
|
1361
|
+
break;
|
|
1362
|
+
}
|
|
1363
|
+
case "status": {
|
|
1364
|
+
const status = data?.traceStatus;
|
|
1365
|
+
if (status?.recording) {
|
|
1366
|
+
console.log(`\u5F55\u5236\u4E2D (\u6807\u7B7E\u9875 ${status.tabId})`);
|
|
1367
|
+
console.log(`\u5DF2\u5F55\u5236 ${status.eventCount} \u4E2A\u4E8B\u4EF6`);
|
|
1368
|
+
} else {
|
|
1369
|
+
console.log("\u672A\u5728\u5F55\u5236");
|
|
1370
|
+
}
|
|
1371
|
+
break;
|
|
1372
|
+
}
|
|
1373
|
+
default:
|
|
1374
|
+
throw new Error(`\u672A\u77E5\u7684 trace \u5B50\u547D\u4EE4: ${subCommand}`);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// packages/cli/src/index.ts
|
|
1379
|
+
var VERSION = "0.1.0";
|
|
1380
|
+
var HELP_TEXT = `
|
|
1381
|
+
bb-browser - AI Agent \u6D4F\u89C8\u5668\u81EA\u52A8\u5316\u5DE5\u5177
|
|
1382
|
+
|
|
1383
|
+
\u7528\u6CD5\uFF1A
|
|
1384
|
+
bb-browser <command> [options]
|
|
1385
|
+
|
|
1386
|
+
\u547D\u4EE4\uFF1A
|
|
1387
|
+
open <url> [--tab] \u6253\u5F00\u6307\u5B9A URL\uFF08\u9ED8\u8BA4\u65B0 tab\uFF0C--tab current \u5F53\u524D tab\uFF09
|
|
1388
|
+
snapshot \u83B7\u53D6\u5F53\u524D\u9875\u9762\u5FEB\u7167\uFF08\u9ED8\u8BA4\u5B8C\u6574\u6811\uFF09
|
|
1389
|
+
click <ref> \u70B9\u51FB\u5143\u7D20\uFF08ref \u5982 @5 \u6216 5\uFF09
|
|
1390
|
+
hover <ref> \u60AC\u505C\u5728\u5143\u7D20\u4E0A
|
|
1391
|
+
fill <ref> <text> \u586B\u5145\u8F93\u5165\u6846\uFF08\u6E05\u7A7A\u540E\u586B\u5165\uFF09
|
|
1392
|
+
type <ref> <text> \u9010\u5B57\u7B26\u8F93\u5165\uFF08\u4E0D\u6E05\u7A7A\uFF09
|
|
1393
|
+
check <ref> \u52FE\u9009\u590D\u9009\u6846
|
|
1394
|
+
uncheck <ref> \u53D6\u6D88\u52FE\u9009\u590D\u9009\u6846
|
|
1395
|
+
select <ref> <val> \u4E0B\u62C9\u6846\u9009\u62E9
|
|
1396
|
+
eval "<js>" \u6267\u884C JavaScript
|
|
1397
|
+
close \u5173\u95ED\u5F53\u524D\u6807\u7B7E\u9875
|
|
1398
|
+
get text <ref> \u83B7\u53D6\u5143\u7D20\u6587\u672C
|
|
1399
|
+
get url \u83B7\u53D6\u5F53\u524D\u9875\u9762 URL
|
|
1400
|
+
get title \u83B7\u53D6\u9875\u9762\u6807\u9898
|
|
1401
|
+
screenshot [path] \u622A\u53D6\u5F53\u524D\u9875\u9762
|
|
1402
|
+
wait <ms|@ref> \u7B49\u5F85\u65F6\u95F4\u6216\u5143\u7D20
|
|
1403
|
+
press <key> \u53D1\u9001\u952E\u76D8\u6309\u952E\uFF08\u5982 Enter, Tab, Control+a\uFF09
|
|
1404
|
+
scroll <dir> [px] \u6EDA\u52A8\u9875\u9762\uFF08up/down/left/right\uFF0C\u9ED8\u8BA4 300px\uFF09
|
|
1405
|
+
daemon \u524D\u53F0\u542F\u52A8 Daemon
|
|
1406
|
+
start \u524D\u53F0\u542F\u52A8 Daemon\uFF08daemon \u7684\u522B\u540D\uFF09
|
|
1407
|
+
stop \u505C\u6B62 Daemon
|
|
1408
|
+
status \u67E5\u770B Daemon \u72B6\u6001
|
|
1409
|
+
reload \u91CD\u8F7D\u6269\u5C55\uFF08\u9700\u8981 CDP \u6A21\u5F0F\uFF09
|
|
1410
|
+
back \u540E\u9000
|
|
1411
|
+
forward \u524D\u8FDB
|
|
1412
|
+
refresh \u5237\u65B0\u9875\u9762
|
|
1413
|
+
tab \u5217\u51FA\u6240\u6709\u6807\u7B7E\u9875
|
|
1414
|
+
tab new [url] \u65B0\u5EFA\u6807\u7B7E\u9875
|
|
1415
|
+
tab <n> \u5207\u6362\u5230\u7B2C n \u4E2A\u6807\u7B7E\u9875
|
|
1416
|
+
tab close [n] \u5173\u95ED\u6807\u7B7E\u9875\uFF08\u9ED8\u8BA4\u5F53\u524D\uFF09
|
|
1417
|
+
frame <selector> \u5207\u6362\u5230\u6307\u5B9A iframe
|
|
1418
|
+
frame main \u8FD4\u56DE\u4E3B frame
|
|
1419
|
+
dialog accept [text] \u63A5\u53D7\u5BF9\u8BDD\u6846\uFF08alert/confirm/prompt\uFF09
|
|
1420
|
+
dialog dismiss \u62D2\u7EDD/\u5173\u95ED\u5BF9\u8BDD\u6846
|
|
1421
|
+
network requests [filter] \u67E5\u770B\u7F51\u7EDC\u8BF7\u6C42
|
|
1422
|
+
network route <url> [--abort|--body <json>] \u62E6\u622A\u8BF7\u6C42
|
|
1423
|
+
network unroute [url] \u79FB\u9664\u62E6\u622A\u89C4\u5219
|
|
1424
|
+
network clear \u6E05\u7A7A\u8BF7\u6C42\u8BB0\u5F55
|
|
1425
|
+
console \u67E5\u770B\u63A7\u5236\u53F0\u6D88\u606F
|
|
1426
|
+
console --clear \u6E05\u7A7A\u63A7\u5236\u53F0
|
|
1427
|
+
errors \u67E5\u770B JS \u9519\u8BEF
|
|
1428
|
+
errors --clear \u6E05\u7A7A\u9519\u8BEF\u8BB0\u5F55
|
|
1429
|
+
trace start \u5F00\u59CB\u5F55\u5236\u7528\u6237\u64CD\u4F5C
|
|
1430
|
+
trace stop \u505C\u6B62\u5F55\u5236\uFF0C\u8F93\u51FA\u4E8B\u4EF6\u5217\u8868
|
|
1431
|
+
trace status \u67E5\u770B\u5F55\u5236\u72B6\u6001
|
|
1432
|
+
|
|
1433
|
+
\u9009\u9879\uFF1A
|
|
1434
|
+
--json \u4EE5 JSON \u683C\u5F0F\u8F93\u51FA
|
|
1435
|
+
-i, --interactive \u53EA\u8F93\u51FA\u53EF\u4EA4\u4E92\u5143\u7D20\uFF08snapshot \u547D\u4EE4\uFF09
|
|
1436
|
+
--help, -h \u663E\u793A\u5E2E\u52A9\u4FE1\u606F
|
|
1437
|
+
--version, -v \u663E\u793A\u7248\u672C\u53F7
|
|
1438
|
+
|
|
1439
|
+
\u793A\u4F8B\uFF1A
|
|
1440
|
+
bb-browser open https://example.com
|
|
1441
|
+
bb-browser snapshot --json
|
|
1442
|
+
bb-browser click @5
|
|
1443
|
+
bb-browser fill @3 "hello world"
|
|
1444
|
+
bb-browser type @3 "append text"
|
|
1445
|
+
bb-browser get text @5
|
|
1446
|
+
bb-browser get url
|
|
1447
|
+
bb-browser press Enter
|
|
1448
|
+
bb-browser press Control+a
|
|
1449
|
+
bb-browser daemon
|
|
1450
|
+
bb-browser stop
|
|
1451
|
+
`.trim();
|
|
1452
|
+
function parseArgs(argv) {
|
|
1453
|
+
const args = argv.slice(2);
|
|
1454
|
+
const result = {
|
|
1455
|
+
command: null,
|
|
1456
|
+
args: [],
|
|
1457
|
+
flags: {
|
|
1458
|
+
json: false,
|
|
1459
|
+
help: false,
|
|
1460
|
+
version: false,
|
|
1461
|
+
interactive: false
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
for (const arg of args) {
|
|
1465
|
+
if (arg === "--json") {
|
|
1466
|
+
result.flags.json = true;
|
|
1467
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
1468
|
+
result.flags.help = true;
|
|
1469
|
+
} else if (arg === "--version" || arg === "-v") {
|
|
1470
|
+
result.flags.version = true;
|
|
1471
|
+
} else if (arg === "--interactive" || arg === "-i") {
|
|
1472
|
+
result.flags.interactive = true;
|
|
1473
|
+
} else if (arg.startsWith("-")) {
|
|
1474
|
+
} else if (result.command === null) {
|
|
1475
|
+
result.command = arg;
|
|
1476
|
+
} else {
|
|
1477
|
+
result.args.push(arg);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return result;
|
|
1481
|
+
}
|
|
1482
|
+
async function main() {
|
|
1483
|
+
const parsed = parseArgs(process.argv);
|
|
1484
|
+
if (parsed.flags.version) {
|
|
1485
|
+
console.log(VERSION);
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
if (parsed.flags.help || !parsed.command) {
|
|
1489
|
+
console.log(HELP_TEXT);
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
try {
|
|
1493
|
+
switch (parsed.command) {
|
|
1494
|
+
case "open": {
|
|
1495
|
+
const url = parsed.args[0];
|
|
1496
|
+
if (!url) {
|
|
1497
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 URL \u53C2\u6570");
|
|
1498
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser open <url> [--tab current|<tabId>]");
|
|
1499
|
+
process.exit(1);
|
|
1500
|
+
}
|
|
1501
|
+
const tabIndex = process.argv.findIndex((a) => a === "--tab");
|
|
1502
|
+
const tab = tabIndex >= 0 ? process.argv[tabIndex + 1] : void 0;
|
|
1503
|
+
await openCommand(url, { json: parsed.flags.json, tab });
|
|
1504
|
+
break;
|
|
1505
|
+
}
|
|
1506
|
+
case "snapshot": {
|
|
1507
|
+
await snapshotCommand({ json: parsed.flags.json, interactive: parsed.flags.interactive });
|
|
1508
|
+
break;
|
|
1509
|
+
}
|
|
1510
|
+
case "click": {
|
|
1511
|
+
const ref = parsed.args[0];
|
|
1512
|
+
if (!ref) {
|
|
1513
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1514
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser click <ref>");
|
|
1515
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser click @5");
|
|
1516
|
+
process.exit(1);
|
|
1517
|
+
}
|
|
1518
|
+
await clickCommand(ref, { json: parsed.flags.json });
|
|
1519
|
+
break;
|
|
1520
|
+
}
|
|
1521
|
+
case "hover": {
|
|
1522
|
+
const ref = parsed.args[0];
|
|
1523
|
+
if (!ref) {
|
|
1524
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1525
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser hover <ref>");
|
|
1526
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser hover @5");
|
|
1527
|
+
process.exit(1);
|
|
1528
|
+
}
|
|
1529
|
+
await hoverCommand(ref, { json: parsed.flags.json });
|
|
1530
|
+
break;
|
|
1531
|
+
}
|
|
1532
|
+
case "check": {
|
|
1533
|
+
const ref = parsed.args[0];
|
|
1534
|
+
if (!ref) {
|
|
1535
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1536
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser check <ref>");
|
|
1537
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser check @5");
|
|
1538
|
+
process.exit(1);
|
|
1539
|
+
}
|
|
1540
|
+
await checkCommand(ref, { json: parsed.flags.json });
|
|
1541
|
+
break;
|
|
1542
|
+
}
|
|
1543
|
+
case "uncheck": {
|
|
1544
|
+
const ref = parsed.args[0];
|
|
1545
|
+
if (!ref) {
|
|
1546
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1547
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser uncheck <ref>");
|
|
1548
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser uncheck @5");
|
|
1549
|
+
process.exit(1);
|
|
1550
|
+
}
|
|
1551
|
+
await uncheckCommand(ref, { json: parsed.flags.json });
|
|
1552
|
+
break;
|
|
1553
|
+
}
|
|
1554
|
+
case "fill": {
|
|
1555
|
+
const ref = parsed.args[0];
|
|
1556
|
+
const text = parsed.args[1];
|
|
1557
|
+
if (!ref) {
|
|
1558
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1559
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser fill <ref> <text>");
|
|
1560
|
+
console.error('\u793A\u4F8B\uFF1Abb-browser fill @3 "hello world"');
|
|
1561
|
+
process.exit(1);
|
|
1562
|
+
}
|
|
1563
|
+
if (text === void 0) {
|
|
1564
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 text \u53C2\u6570");
|
|
1565
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser fill <ref> <text>");
|
|
1566
|
+
console.error('\u793A\u4F8B\uFF1Abb-browser fill @3 "hello world"');
|
|
1567
|
+
process.exit(1);
|
|
1568
|
+
}
|
|
1569
|
+
await fillCommand(ref, text, { json: parsed.flags.json });
|
|
1570
|
+
break;
|
|
1571
|
+
}
|
|
1572
|
+
case "type": {
|
|
1573
|
+
const ref = parsed.args[0];
|
|
1574
|
+
const text = parsed.args[1];
|
|
1575
|
+
if (!ref) {
|
|
1576
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1577
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser type <ref> <text>");
|
|
1578
|
+
console.error('\u793A\u4F8B\uFF1Abb-browser type @3 "append text"');
|
|
1579
|
+
process.exit(1);
|
|
1580
|
+
}
|
|
1581
|
+
if (text === void 0) {
|
|
1582
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 text \u53C2\u6570");
|
|
1583
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser type <ref> <text>");
|
|
1584
|
+
console.error('\u793A\u4F8B\uFF1Abb-browser type @3 "append text"');
|
|
1585
|
+
process.exit(1);
|
|
1586
|
+
}
|
|
1587
|
+
await typeCommand(ref, text, { json: parsed.flags.json });
|
|
1588
|
+
break;
|
|
1589
|
+
}
|
|
1590
|
+
case "select": {
|
|
1591
|
+
const ref = parsed.args[0];
|
|
1592
|
+
const value = parsed.args[1];
|
|
1593
|
+
if (!ref) {
|
|
1594
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 ref \u53C2\u6570");
|
|
1595
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser select <ref> <value>");
|
|
1596
|
+
console.error('\u793A\u4F8B\uFF1Abb-browser select @4 "option1"');
|
|
1597
|
+
process.exit(1);
|
|
1598
|
+
}
|
|
1599
|
+
if (value === void 0) {
|
|
1600
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 value \u53C2\u6570");
|
|
1601
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser select <ref> <value>");
|
|
1602
|
+
console.error('\u793A\u4F8B\uFF1Abb-browser select @4 "option1"');
|
|
1603
|
+
process.exit(1);
|
|
1604
|
+
}
|
|
1605
|
+
await selectCommand(ref, value, { json: parsed.flags.json });
|
|
1606
|
+
break;
|
|
1607
|
+
}
|
|
1608
|
+
case "eval": {
|
|
1609
|
+
const script = parsed.args[0];
|
|
1610
|
+
if (!script) {
|
|
1611
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 script \u53C2\u6570");
|
|
1612
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser eval <script>");
|
|
1613
|
+
console.error('\u793A\u4F8B\uFF1Abb-browser eval "document.title"');
|
|
1614
|
+
process.exit(1);
|
|
1615
|
+
}
|
|
1616
|
+
await evalCommand(script, { json: parsed.flags.json });
|
|
1617
|
+
break;
|
|
1618
|
+
}
|
|
1619
|
+
case "get": {
|
|
1620
|
+
const attribute = parsed.args[0];
|
|
1621
|
+
if (!attribute) {
|
|
1622
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11\u5C5E\u6027\u53C2\u6570");
|
|
1623
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser get <text|url|title> [ref]");
|
|
1624
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser get text @5");
|
|
1625
|
+
console.error(" bb-browser get url");
|
|
1626
|
+
process.exit(1);
|
|
1627
|
+
}
|
|
1628
|
+
if (!["text", "url", "title"].includes(attribute)) {
|
|
1629
|
+
console.error(`\u9519\u8BEF\uFF1A\u672A\u77E5\u5C5E\u6027 "${attribute}"`);
|
|
1630
|
+
console.error("\u652F\u6301\u7684\u5C5E\u6027\uFF1Atext, url, title");
|
|
1631
|
+
process.exit(1);
|
|
1632
|
+
}
|
|
1633
|
+
const ref = parsed.args[1];
|
|
1634
|
+
await getCommand(attribute, ref, { json: parsed.flags.json });
|
|
1635
|
+
break;
|
|
1636
|
+
}
|
|
1637
|
+
case "daemon":
|
|
1638
|
+
case "start": {
|
|
1639
|
+
await daemonCommand({ json: parsed.flags.json });
|
|
1640
|
+
break;
|
|
1641
|
+
}
|
|
1642
|
+
case "stop": {
|
|
1643
|
+
await stopCommand({ json: parsed.flags.json });
|
|
1644
|
+
break;
|
|
1645
|
+
}
|
|
1646
|
+
case "status": {
|
|
1647
|
+
await statusCommand({ json: parsed.flags.json });
|
|
1648
|
+
break;
|
|
1649
|
+
}
|
|
1650
|
+
case "reload": {
|
|
1651
|
+
await reloadCommand({ json: parsed.flags.json });
|
|
1652
|
+
break;
|
|
1653
|
+
}
|
|
1654
|
+
case "close": {
|
|
1655
|
+
await closeCommand({ json: parsed.flags.json });
|
|
1656
|
+
break;
|
|
1657
|
+
}
|
|
1658
|
+
case "back": {
|
|
1659
|
+
await backCommand({ json: parsed.flags.json });
|
|
1660
|
+
break;
|
|
1661
|
+
}
|
|
1662
|
+
case "forward": {
|
|
1663
|
+
await forwardCommand({ json: parsed.flags.json });
|
|
1664
|
+
break;
|
|
1665
|
+
}
|
|
1666
|
+
case "refresh": {
|
|
1667
|
+
await refreshCommand({ json: parsed.flags.json });
|
|
1668
|
+
break;
|
|
1669
|
+
}
|
|
1670
|
+
case "screenshot": {
|
|
1671
|
+
const outputPath = parsed.args[0];
|
|
1672
|
+
await screenshotCommand(outputPath, { json: parsed.flags.json });
|
|
1673
|
+
break;
|
|
1674
|
+
}
|
|
1675
|
+
case "wait": {
|
|
1676
|
+
const target = parsed.args[0];
|
|
1677
|
+
if (!target) {
|
|
1678
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11\u7B49\u5F85\u76EE\u6807\u53C2\u6570");
|
|
1679
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser wait <ms|@ref>");
|
|
1680
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser wait 2000");
|
|
1681
|
+
console.error(" bb-browser wait @5");
|
|
1682
|
+
process.exit(1);
|
|
1683
|
+
}
|
|
1684
|
+
await waitCommand(target, { json: parsed.flags.json });
|
|
1685
|
+
break;
|
|
1686
|
+
}
|
|
1687
|
+
case "press": {
|
|
1688
|
+
const key = parsed.args[0];
|
|
1689
|
+
if (!key) {
|
|
1690
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 key \u53C2\u6570");
|
|
1691
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser press <key>");
|
|
1692
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser press Enter");
|
|
1693
|
+
console.error(" bb-browser press Control+a");
|
|
1694
|
+
process.exit(1);
|
|
1695
|
+
}
|
|
1696
|
+
await pressCommand(key, { json: parsed.flags.json });
|
|
1697
|
+
break;
|
|
1698
|
+
}
|
|
1699
|
+
case "scroll": {
|
|
1700
|
+
const direction = parsed.args[0];
|
|
1701
|
+
const pixels = parsed.args[1];
|
|
1702
|
+
if (!direction) {
|
|
1703
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11\u65B9\u5411\u53C2\u6570");
|
|
1704
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser scroll <up|down|left|right> [pixels]");
|
|
1705
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser scroll down");
|
|
1706
|
+
console.error(" bb-browser scroll up 500");
|
|
1707
|
+
process.exit(1);
|
|
1708
|
+
}
|
|
1709
|
+
await scrollCommand(direction, pixels, { json: parsed.flags.json });
|
|
1710
|
+
break;
|
|
1711
|
+
}
|
|
1712
|
+
case "tab": {
|
|
1713
|
+
await tabCommand(parsed.args, { json: parsed.flags.json });
|
|
1714
|
+
break;
|
|
1715
|
+
}
|
|
1716
|
+
case "frame": {
|
|
1717
|
+
const selectorOrMain = parsed.args[0];
|
|
1718
|
+
if (!selectorOrMain) {
|
|
1719
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11 selector \u53C2\u6570");
|
|
1720
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser frame <selector>");
|
|
1721
|
+
console.error('\u793A\u4F8B\uFF1Abb-browser frame "iframe#editor"');
|
|
1722
|
+
console.error(" bb-browser frame main");
|
|
1723
|
+
process.exit(1);
|
|
1724
|
+
}
|
|
1725
|
+
if (selectorOrMain === "main") {
|
|
1726
|
+
await frameMainCommand({ json: parsed.flags.json });
|
|
1727
|
+
} else {
|
|
1728
|
+
await frameCommand(selectorOrMain, { json: parsed.flags.json });
|
|
1729
|
+
}
|
|
1730
|
+
break;
|
|
1731
|
+
}
|
|
1732
|
+
case "dialog": {
|
|
1733
|
+
const subCommand = parsed.args[0];
|
|
1734
|
+
if (!subCommand) {
|
|
1735
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11\u5B50\u547D\u4EE4");
|
|
1736
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser dialog <accept|dismiss> [text]");
|
|
1737
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser dialog accept");
|
|
1738
|
+
console.error(' bb-browser dialog accept "my input"');
|
|
1739
|
+
console.error(" bb-browser dialog dismiss");
|
|
1740
|
+
process.exit(1);
|
|
1741
|
+
}
|
|
1742
|
+
const promptText = parsed.args[1];
|
|
1743
|
+
await dialogCommand(subCommand, promptText, { json: parsed.flags.json });
|
|
1744
|
+
break;
|
|
1745
|
+
}
|
|
1746
|
+
case "network": {
|
|
1747
|
+
const subCommand = parsed.args[0] || "requests";
|
|
1748
|
+
const urlOrFilter = parsed.args[1];
|
|
1749
|
+
const abort = process.argv.includes("--abort");
|
|
1750
|
+
const bodyIndex = process.argv.findIndex((a) => a === "--body");
|
|
1751
|
+
const body = bodyIndex >= 0 ? process.argv[bodyIndex + 1] : void 0;
|
|
1752
|
+
await networkCommand(subCommand, urlOrFilter, { json: parsed.flags.json, abort, body });
|
|
1753
|
+
break;
|
|
1754
|
+
}
|
|
1755
|
+
case "console": {
|
|
1756
|
+
const clear = process.argv.includes("--clear");
|
|
1757
|
+
await consoleCommand({ json: parsed.flags.json, clear });
|
|
1758
|
+
break;
|
|
1759
|
+
}
|
|
1760
|
+
case "errors": {
|
|
1761
|
+
const clear = process.argv.includes("--clear");
|
|
1762
|
+
await errorsCommand({ json: parsed.flags.json, clear });
|
|
1763
|
+
break;
|
|
1764
|
+
}
|
|
1765
|
+
case "trace": {
|
|
1766
|
+
const subCmd = parsed.args[0];
|
|
1767
|
+
if (!subCmd || !["start", "stop", "status"].includes(subCmd)) {
|
|
1768
|
+
console.error("\u9519\u8BEF\uFF1A\u7F3A\u5C11\u6216\u65E0\u6548\u7684\u5B50\u547D\u4EE4");
|
|
1769
|
+
console.error("\u7528\u6CD5\uFF1Abb-browser trace <start|stop|status>");
|
|
1770
|
+
console.error("\u793A\u4F8B\uFF1Abb-browser trace start");
|
|
1771
|
+
console.error(" bb-browser trace stop");
|
|
1772
|
+
console.error(" bb-browser trace status");
|
|
1773
|
+
process.exit(1);
|
|
1774
|
+
}
|
|
1775
|
+
await traceCommand(subCmd, { json: parsed.flags.json });
|
|
1776
|
+
break;
|
|
1777
|
+
}
|
|
1778
|
+
default: {
|
|
1779
|
+
console.error(`\u9519\u8BEF\uFF1A\u672A\u77E5\u547D\u4EE4 "${parsed.command}"`);
|
|
1780
|
+
console.error("\u8FD0\u884C bb-browser --help \u67E5\u770B\u53EF\u7528\u547D\u4EE4");
|
|
1781
|
+
process.exit(1);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
} catch (error) {
|
|
1785
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1786
|
+
if (parsed.flags.json) {
|
|
1787
|
+
console.log(
|
|
1788
|
+
JSON.stringify({
|
|
1789
|
+
success: false,
|
|
1790
|
+
error: message
|
|
1791
|
+
})
|
|
1792
|
+
);
|
|
1793
|
+
} else {
|
|
1794
|
+
console.error(`\u9519\u8BEF\uFF1A${message}`);
|
|
1795
|
+
}
|
|
1796
|
+
process.exit(1);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
main();
|
|
1800
|
+
//# sourceMappingURL=cli.js.map
|