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/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