browser-pilot 0.0.16 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/actions.cjs +797 -69
- package/dist/actions.d.cts +101 -4
- package/dist/actions.d.ts +101 -4
- package/dist/actions.mjs +17 -1
- package/dist/{browser-ZCR6AA4D.mjs → browser-4ZHNAQR5.mjs} +2 -2
- package/dist/browser.cjs +1238 -72
- package/dist/browser.d.cts +229 -5
- package/dist/browser.d.ts +229 -5
- package/dist/browser.mjs +36 -4
- package/dist/{chunk-NNEHWWHL.mjs → chunk-FEEGNSHB.mjs} +584 -4
- package/dist/{chunk-TJ5B56NV.mjs → chunk-IRLHCVNH.mjs} +1 -1
- package/dist/chunk-MIJ7UIKB.mjs +96 -0
- package/dist/{chunk-6GBYX7C2.mjs → chunk-MRY3HRFJ.mjs} +799 -353
- package/dist/chunk-OIHU7OFY.mjs +91 -0
- package/dist/{chunk-V3VLBQAM.mjs → chunk-ZDODXEBD.mjs} +586 -69
- package/dist/cli.mjs +756 -174
- package/dist/combobox-RAKBA2BW.mjs +6 -0
- package/dist/index.cjs +1539 -71
- package/dist/index.d.cts +56 -5
- package/dist/index.d.ts +56 -5
- package/dist/index.mjs +189 -2
- package/dist/{page-IUUTJ3SW.mjs → page-SD64DY3F.mjs} +1 -1
- package/dist/{types-BzM-IfsL.d.ts → types-B_v62K7C.d.ts} +146 -2
- package/dist/{types-BflRmiDz.d.cts → types-Yuybzq53.d.cts} +146 -2
- package/dist/upload-E6MCC2OF.mjs +6 -0
- package/package.json +10 -3
package/dist/cli.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import "./chunk-MIJ7UIKB.mjs";
|
|
3
|
+
import "./chunk-OIHU7OFY.mjs";
|
|
2
4
|
import {
|
|
3
5
|
BrowserEndpointResolutionError,
|
|
4
6
|
connect,
|
|
5
7
|
resolveBrowserEndpoint
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-IRLHCVNH.mjs";
|
|
7
9
|
import "./chunk-LCNFBXB5.mjs";
|
|
8
10
|
import {
|
|
9
11
|
DEEP_QUERY_SCRIPT,
|
|
10
|
-
LiveTraceCollector,
|
|
11
12
|
SENSITIVE_AUTOCOMPLETE_TOKENS,
|
|
12
13
|
TRACE_BINDING_NAME,
|
|
13
14
|
TRACE_SCRIPT,
|
|
@@ -17,13 +18,17 @@ import {
|
|
|
17
18
|
canonicalizeRecordingArtifact,
|
|
18
19
|
createRecordingManifest,
|
|
19
20
|
createTraceId,
|
|
21
|
+
formatConsoleArg,
|
|
20
22
|
fuzzyMatchElements,
|
|
23
|
+
globToRegex,
|
|
21
24
|
grantAudioPermissions,
|
|
22
25
|
normalizeTraceEvent,
|
|
23
26
|
pcmToWav,
|
|
27
|
+
readString,
|
|
28
|
+
readStringOr,
|
|
24
29
|
redactValueForRecording,
|
|
25
30
|
validateSteps
|
|
26
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-MRY3HRFJ.mjs";
|
|
27
32
|
import {
|
|
28
33
|
isRecord
|
|
29
34
|
} from "./chunk-DTVRFXKI.mjs";
|
|
@@ -32,6 +37,68 @@ import {
|
|
|
32
37
|
DAEMON_READY_TIMEOUT_MS
|
|
33
38
|
} from "./chunk-LUGLEMVR.mjs";
|
|
34
39
|
|
|
40
|
+
// src/cli/command-registry.ts
|
|
41
|
+
var CLI_COMMANDS = [
|
|
42
|
+
{ name: "quickstart", description: "Getting started guide", showInRootHelp: true },
|
|
43
|
+
{ name: "connect", description: "Create or resume a browser session", showInRootHelp: true },
|
|
44
|
+
{ name: "exec", description: "Execute high-level actions", showInRootHelp: true },
|
|
45
|
+
{ name: "eval", description: "Run raw JavaScript as an escape hatch", showInRootHelp: true },
|
|
46
|
+
{ name: "snapshot", description: "Inspect current page with refs", showInRootHelp: true },
|
|
47
|
+
{ name: "text", description: "Extract readable page text", showInRootHelp: true },
|
|
48
|
+
{ name: "page", description: "Compact page overview", showInRootHelp: true },
|
|
49
|
+
{ name: "forms", description: "List form controls", showInRootHelp: true },
|
|
50
|
+
{ name: "targets", description: "List available browser tabs", showInRootHelp: true },
|
|
51
|
+
{ name: "diagnose", description: "Debug selectors and targeting failures", showInRootHelp: true },
|
|
52
|
+
{ name: "review", description: "Structured business state after actions", showInRootHelp: true },
|
|
53
|
+
{ name: "screenshot", description: "Capture a page screenshot", showInRootHelp: true },
|
|
54
|
+
{ name: "run", description: "Run a workflow file", showInRootHelp: true },
|
|
55
|
+
{
|
|
56
|
+
name: "record",
|
|
57
|
+
description: "Record a human workflow and derive replayable output",
|
|
58
|
+
showInRootHelp: true
|
|
59
|
+
},
|
|
60
|
+
{ name: "trace", description: "Inspect and analyze behavior over time", showInRootHelp: true },
|
|
61
|
+
{
|
|
62
|
+
name: "audio",
|
|
63
|
+
description: "Set up, validate, and drive voice pipelines",
|
|
64
|
+
showInRootHelp: true
|
|
65
|
+
},
|
|
66
|
+
{ name: "env", description: "Session and browser-environment controls", showInRootHelp: true },
|
|
67
|
+
{ name: "daemon", description: "Manage session daemon", showInRootHelp: true },
|
|
68
|
+
{ name: "list", description: "List sessions", showInRootHelp: true },
|
|
69
|
+
{ name: "close", description: "Close session", showInRootHelp: true },
|
|
70
|
+
{ name: "clean", description: "Clean old sessions and artifacts", showInRootHelp: true },
|
|
71
|
+
{ name: "actions", description: "Complete action reference", showInRootHelp: true }
|
|
72
|
+
];
|
|
73
|
+
var ROOT_HELP_COMMANDS = CLI_COMMANDS.filter((command) => command.showInRootHelp);
|
|
74
|
+
var CLI_ROUTE_GROUPS = [
|
|
75
|
+
{
|
|
76
|
+
label: "Inspect page state",
|
|
77
|
+
commands: ["snapshot", "page", "forms", "review", "text", "targets", "diagnose"]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
label: "Act in the browser",
|
|
81
|
+
commands: ["exec", "run"]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
label: "Capture a human demo",
|
|
85
|
+
commands: ["record"]
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
label: "Analyze behavior over time",
|
|
89
|
+
commands: ["trace"],
|
|
90
|
+
note: "(listen is a compatibility alias)"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
label: "Exercise voice/media",
|
|
94
|
+
commands: ["audio"]
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
label: "Change browser conditions",
|
|
98
|
+
commands: ["env"]
|
|
99
|
+
}
|
|
100
|
+
];
|
|
101
|
+
|
|
35
102
|
// src/cli/commands/actions.ts
|
|
36
103
|
var ACTIONS_HELP = `
|
|
37
104
|
bp actions - Complete action reference
|
|
@@ -232,7 +299,7 @@ EXAMPLES
|
|
|
232
299
|
]'
|
|
233
300
|
|
|
234
301
|
# Use ref from snapshot
|
|
235
|
-
bp snapshot
|
|
302
|
+
bp snapshot -i # Note the refs
|
|
236
303
|
bp exec '{"action":"click","selector":"ref:e4"}'
|
|
237
304
|
|
|
238
305
|
# Scroll and wait
|
|
@@ -359,6 +426,268 @@ Content-Type: ${contentType}\r
|
|
|
359
426
|
parts.push(data);
|
|
360
427
|
}
|
|
361
428
|
|
|
429
|
+
// src/trace/live.ts
|
|
430
|
+
var LiveTraceCollector = class {
|
|
431
|
+
cdp;
|
|
432
|
+
options;
|
|
433
|
+
handlers = [];
|
|
434
|
+
wsUrls = /* @__PURE__ */ new Map();
|
|
435
|
+
httpUrls = /* @__PURE__ */ new Map();
|
|
436
|
+
events = [];
|
|
437
|
+
startTime = Date.now();
|
|
438
|
+
matchRegex;
|
|
439
|
+
constructor(cdp, options = {}) {
|
|
440
|
+
this.cdp = cdp;
|
|
441
|
+
this.options = options;
|
|
442
|
+
this.matchRegex = options.match ? globToRegex(options.match) : null;
|
|
443
|
+
}
|
|
444
|
+
async start() {
|
|
445
|
+
await this.cdp.send("Runtime.enable");
|
|
446
|
+
await this.cdp.send("Page.enable");
|
|
447
|
+
await this.cdp.send("Network.enable");
|
|
448
|
+
await this.cdp.send("Runtime.addBinding", { name: TRACE_BINDING_NAME });
|
|
449
|
+
await this.cdp.send("Page.addScriptToEvaluateOnNewDocument", { source: TRACE_SCRIPT });
|
|
450
|
+
await this.cdp.send("Runtime.evaluate", { expression: TRACE_SCRIPT, awaitPromise: false });
|
|
451
|
+
if ((this.options.mode ?? "all") !== "http") {
|
|
452
|
+
this.subscribe("Network.webSocketCreated", (params) => {
|
|
453
|
+
const requestId = readStringOr(params["requestId"]);
|
|
454
|
+
const url = readStringOr(params["url"]);
|
|
455
|
+
if (!this.matchesUrl(url)) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
this.wsUrls.set(requestId, url);
|
|
459
|
+
void this.emit({
|
|
460
|
+
channel: "ws",
|
|
461
|
+
event: "ws.connection.created",
|
|
462
|
+
summary: `WebSocket opened ${url}`,
|
|
463
|
+
connectionId: requestId,
|
|
464
|
+
requestId,
|
|
465
|
+
url,
|
|
466
|
+
data: { url }
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
this.subscribe("Network.webSocketFrameSent", (params) => {
|
|
470
|
+
const requestId = readStringOr(params["requestId"]);
|
|
471
|
+
const response = params["response"];
|
|
472
|
+
const payload = this.formatPayload(response?.payloadData, response?.opcode ?? 1);
|
|
473
|
+
const url = this.wsUrls.get(requestId);
|
|
474
|
+
if (this.matchRegex && !this.matchRegex.test(url ?? "") && !this.matchRegex.test(payload)) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
void this.emit({
|
|
478
|
+
channel: "ws",
|
|
479
|
+
event: "ws.frame.sent",
|
|
480
|
+
summary: `WebSocket frame sent ${requestId}`,
|
|
481
|
+
connectionId: requestId,
|
|
482
|
+
requestId,
|
|
483
|
+
url,
|
|
484
|
+
data: {
|
|
485
|
+
opcode: response?.opcode ?? 1,
|
|
486
|
+
payload,
|
|
487
|
+
length: response?.payloadData?.length ?? 0
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
this.subscribe("Network.webSocketFrameReceived", (params) => {
|
|
492
|
+
const requestId = readStringOr(params["requestId"]);
|
|
493
|
+
const response = params["response"];
|
|
494
|
+
const payload = this.formatPayload(response?.payloadData, response?.opcode ?? 1);
|
|
495
|
+
const url = this.wsUrls.get(requestId);
|
|
496
|
+
if (this.matchRegex && !this.matchRegex.test(url ?? "") && !this.matchRegex.test(payload)) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
void this.emit({
|
|
500
|
+
channel: "ws",
|
|
501
|
+
event: "ws.frame.received",
|
|
502
|
+
summary: `WebSocket frame received ${requestId}`,
|
|
503
|
+
connectionId: requestId,
|
|
504
|
+
requestId,
|
|
505
|
+
url,
|
|
506
|
+
data: {
|
|
507
|
+
opcode: response?.opcode ?? 1,
|
|
508
|
+
payload,
|
|
509
|
+
length: response?.payloadData?.length ?? 0
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
this.subscribe("Network.webSocketClosed", (params) => {
|
|
514
|
+
const requestId = readStringOr(params["requestId"]);
|
|
515
|
+
const url = this.wsUrls.get(requestId);
|
|
516
|
+
this.wsUrls.delete(requestId);
|
|
517
|
+
void this.emit({
|
|
518
|
+
channel: "ws",
|
|
519
|
+
event: "ws.connection.closed",
|
|
520
|
+
summary: `WebSocket closed ${requestId}`,
|
|
521
|
+
severity: "warn",
|
|
522
|
+
connectionId: requestId,
|
|
523
|
+
requestId,
|
|
524
|
+
url,
|
|
525
|
+
data: { url }
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
if ((this.options.mode ?? "all") !== "ws") {
|
|
530
|
+
this.subscribe("Network.requestWillBeSent", (params) => {
|
|
531
|
+
const request = params["request"];
|
|
532
|
+
const requestId = readStringOr(params["requestId"]);
|
|
533
|
+
const url = request?.url ?? "";
|
|
534
|
+
if (!this.matchesUrl(url)) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
this.httpUrls.set(requestId, url);
|
|
538
|
+
void this.emit({
|
|
539
|
+
channel: "http",
|
|
540
|
+
event: "http.request.sent",
|
|
541
|
+
summary: `${request?.method ?? "GET"} ${url}`,
|
|
542
|
+
requestId,
|
|
543
|
+
url,
|
|
544
|
+
data: {
|
|
545
|
+
method: request?.method ?? "GET",
|
|
546
|
+
headers: request?.headers ?? {},
|
|
547
|
+
body: request?.postData ?? null
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
this.subscribe("Network.responseReceived", (params) => {
|
|
552
|
+
const requestId = readStringOr(params["requestId"]);
|
|
553
|
+
if (!this.httpUrls.has(requestId)) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const response = params["response"];
|
|
557
|
+
void this.emit({
|
|
558
|
+
channel: "http",
|
|
559
|
+
event: "http.response.received",
|
|
560
|
+
summary: `${response?.status ?? 0} ${response?.url ?? this.httpUrls.get(requestId) ?? ""}`,
|
|
561
|
+
requestId,
|
|
562
|
+
url: response?.url ?? this.httpUrls.get(requestId),
|
|
563
|
+
data: {
|
|
564
|
+
status: response?.status ?? 0,
|
|
565
|
+
headers: response?.headers ?? {},
|
|
566
|
+
mimeType: response?.mimeType ?? null
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
this.subscribe("Network.loadingFailed", (params) => {
|
|
571
|
+
const requestId = readStringOr(params["requestId"]);
|
|
572
|
+
const url = readString(params["blockedReason"]) ?? this.httpUrls.get(requestId) ?? "";
|
|
573
|
+
void this.emit({
|
|
574
|
+
channel: "http",
|
|
575
|
+
event: "http.response.failed",
|
|
576
|
+
summary: `HTTP request failed ${requestId}`,
|
|
577
|
+
severity: "error",
|
|
578
|
+
requestId,
|
|
579
|
+
url,
|
|
580
|
+
data: {
|
|
581
|
+
errorText: params["errorText"] ?? null,
|
|
582
|
+
blockedReason: params["blockedReason"] ?? null,
|
|
583
|
+
canceled: params["canceled"] ?? false
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
this.subscribe("Runtime.consoleAPICalled", (params) => {
|
|
589
|
+
const type = readStringOr(params["type"], "log");
|
|
590
|
+
if (type !== "log" && type !== "warn" && type !== "error") {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const args = Array.isArray(params["args"]) ? params["args"] : [];
|
|
594
|
+
const text = args.map(formatConsoleArg).filter(Boolean).join(" ");
|
|
595
|
+
void this.emit({
|
|
596
|
+
channel: "console",
|
|
597
|
+
event: `console.${type}`,
|
|
598
|
+
severity: type === "error" ? "error" : type === "warn" ? "warn" : "info",
|
|
599
|
+
summary: text || `console.${type}`,
|
|
600
|
+
data: { args }
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
this.subscribe("Runtime.exceptionThrown", (params) => {
|
|
604
|
+
const details = params["exceptionDetails"] ?? {};
|
|
605
|
+
const text = readString(details["text"]) ?? "Runtime exception";
|
|
606
|
+
void this.emit({
|
|
607
|
+
channel: "runtime",
|
|
608
|
+
event: "runtime.exception",
|
|
609
|
+
severity: "error",
|
|
610
|
+
summary: text,
|
|
611
|
+
data: details
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
this.subscribe("Runtime.bindingCalled", (params) => {
|
|
615
|
+
if (params["name"] !== TRACE_BINDING_NAME) {
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
const raw = readStringOr(params["payload"]);
|
|
619
|
+
try {
|
|
620
|
+
const payload = JSON.parse(raw);
|
|
621
|
+
const channel = this.channelForTraceEvent(payload.event);
|
|
622
|
+
void this.emit({
|
|
623
|
+
channel,
|
|
624
|
+
event: payload.event,
|
|
625
|
+
severity: payload.severity,
|
|
626
|
+
summary: payload.summary ?? payload.event,
|
|
627
|
+
ts: payload.ts ? new Date(payload.ts).toISOString() : void 0,
|
|
628
|
+
data: payload.data ?? {},
|
|
629
|
+
url: readString(payload.data?.["url"])
|
|
630
|
+
});
|
|
631
|
+
} catch {
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
async stop() {
|
|
636
|
+
for (const { event, handler } of this.handlers) {
|
|
637
|
+
this.cdp.off(event, handler);
|
|
638
|
+
}
|
|
639
|
+
this.handlers.length = 0;
|
|
640
|
+
return [...this.events];
|
|
641
|
+
}
|
|
642
|
+
getEvents() {
|
|
643
|
+
return [...this.events];
|
|
644
|
+
}
|
|
645
|
+
subscribe(event, handler) {
|
|
646
|
+
this.cdp.on(event, handler);
|
|
647
|
+
this.handlers.push({ event, handler });
|
|
648
|
+
}
|
|
649
|
+
matchesUrl(url) {
|
|
650
|
+
if (!this.matchRegex) {
|
|
651
|
+
return true;
|
|
652
|
+
}
|
|
653
|
+
return this.matchRegex.test(url);
|
|
654
|
+
}
|
|
655
|
+
formatPayload(payloadData, opcode) {
|
|
656
|
+
const data = payloadData ?? "";
|
|
657
|
+
const maxPayload = this.options.maxPayload ?? 256;
|
|
658
|
+
if (opcode === 2) {
|
|
659
|
+
const byteLength = Math.floor(data.length * 3 / 4);
|
|
660
|
+
return `[binary: ${byteLength} bytes]`;
|
|
661
|
+
}
|
|
662
|
+
if (data.length > maxPayload) {
|
|
663
|
+
return `${data.slice(0, maxPayload)}... [truncated, ${data.length} total]`;
|
|
664
|
+
}
|
|
665
|
+
return data;
|
|
666
|
+
}
|
|
667
|
+
channelForTraceEvent(eventName) {
|
|
668
|
+
if (eventName.startsWith("ws.")) return "ws";
|
|
669
|
+
if (eventName.startsWith("http.")) return "http";
|
|
670
|
+
if (eventName.startsWith("console.")) return "console";
|
|
671
|
+
if (eventName.startsWith("permission.")) return "permission";
|
|
672
|
+
if (eventName.startsWith("media.")) return "media";
|
|
673
|
+
if (eventName.startsWith("voice.")) return "voice";
|
|
674
|
+
if (eventName.startsWith("dom.")) return "dom";
|
|
675
|
+
if (eventName.startsWith("runtime.")) return "runtime";
|
|
676
|
+
return "session";
|
|
677
|
+
}
|
|
678
|
+
async emit(event) {
|
|
679
|
+
const normalized = normalizeTraceEvent({
|
|
680
|
+
traceId: event.traceId ?? createTraceId(event.channel),
|
|
681
|
+
sessionId: this.options.sessionId,
|
|
682
|
+
targetId: this.options.targetId,
|
|
683
|
+
elapsedMs: event.elapsedMs ?? Date.now() - this.startTime,
|
|
684
|
+
...event
|
|
685
|
+
});
|
|
686
|
+
this.events.push(normalized);
|
|
687
|
+
await this.options.onEvent?.(normalized);
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
|
|
362
691
|
// src/trace/store.ts
|
|
363
692
|
import * as fs from "fs";
|
|
364
693
|
import { homedir } from "os";
|
|
@@ -1220,7 +1549,7 @@ async function audioCommand(args, globalOptions) {
|
|
|
1220
1549
|
event: checkJson.ready ? "voice.pipeline.ready" : "voice.pipeline.notReady",
|
|
1221
1550
|
severity: checkJson.ready ? "info" : "error",
|
|
1222
1551
|
summary: checkJson.ready ? "Audio pipeline ready" : "Audio pipeline not ready",
|
|
1223
|
-
data: checkJson
|
|
1552
|
+
data: { ...checkJson }
|
|
1224
1553
|
});
|
|
1225
1554
|
if (checkJson.agentDetected) {
|
|
1226
1555
|
logger.logTrace({
|
|
@@ -1521,13 +1850,15 @@ bp clean - Remove stale browser sessions
|
|
|
1521
1850
|
Usage:
|
|
1522
1851
|
bp clean [options]
|
|
1523
1852
|
|
|
1524
|
-
|
|
1853
|
+
Local options:
|
|
1525
1854
|
--max-age <hours> Remove sessions older than N hours (default: 24)
|
|
1526
1855
|
--max-size <size> Remove oldest sessions until total size < limit (e.g. "100MB", "1GB")
|
|
1527
1856
|
--dry-run Show what would be removed without deleting
|
|
1528
1857
|
--all Remove all sessions regardless of age
|
|
1529
|
-
|
|
1530
|
-
|
|
1858
|
+
|
|
1859
|
+
Global options:
|
|
1860
|
+
--json Output JSON
|
|
1861
|
+
--pretty Output readable text (default)
|
|
1531
1862
|
-h, --help Show this help
|
|
1532
1863
|
|
|
1533
1864
|
Examples:
|
|
@@ -1731,11 +2062,11 @@ bp close - Close a browser session
|
|
|
1731
2062
|
Usage:
|
|
1732
2063
|
bp close [session-id]
|
|
1733
2064
|
|
|
1734
|
-
|
|
2065
|
+
Global options:
|
|
1735
2066
|
-s, --session <id> Session to close (default: most recent)
|
|
1736
|
-
|
|
1737
|
-
--
|
|
1738
|
-
--
|
|
2067
|
+
--json Output JSON
|
|
2068
|
+
--pretty Output readable text (default)
|
|
2069
|
+
--debug Enable CDP transport debugging
|
|
1739
2070
|
-h, --help Show this help
|
|
1740
2071
|
|
|
1741
2072
|
Examples:
|
|
@@ -1840,18 +2171,30 @@ async function waitForDaemonReady(sessionFilePath, expectedPid, timeoutMs = DAEM
|
|
|
1840
2171
|
var CONNECT_HELP = `
|
|
1841
2172
|
bp connect - Create or resume a browser session
|
|
1842
2173
|
|
|
2174
|
+
When to use:
|
|
2175
|
+
Create a session before running inspect, exec, record, trace, audio, or env commands.
|
|
2176
|
+
|
|
2177
|
+
When not to use:
|
|
2178
|
+
You already have a session and only need to open a page. Use \`bp exec '{"action":"goto","url":"..."}'\`.
|
|
2179
|
+
|
|
2180
|
+
Browser and page URL guidance:
|
|
2181
|
+
Use \`--browser-url\` for a DevTools WebSocket endpoint.
|
|
2182
|
+
Use \`--page-url\` to open a page in the attached tab or a new tab.
|
|
2183
|
+
\`--url\` remains for compatibility and is ambiguous when paired with \`--new-tab\`.
|
|
2184
|
+
|
|
1843
2185
|
Usage:
|
|
1844
2186
|
bp connect [options]
|
|
1845
2187
|
|
|
1846
|
-
|
|
2188
|
+
Local options:
|
|
1847
2189
|
-p, --provider <type> Provider: generic | browserbase | browserless (default: generic)
|
|
1848
|
-
--url <
|
|
1849
|
-
--
|
|
2190
|
+
--browser-url <ws-url> Explicit browser WebSocket URL (preferred)
|
|
2191
|
+
--page-url <url> Page URL to open in the attached tab/new tab (preferred)
|
|
2192
|
+
--url <value> Compatibility shorthand; browser URL, or page URL with --new-tab
|
|
1850
2193
|
--channel <name> Local Chrome channel: stable | beta | dev | canary
|
|
1851
2194
|
--user-data-dir <path> Explicit local Chrome user data dir for auto-discovery
|
|
1852
|
-
--page-url <url> URL to open in the attached page/new tab
|
|
1853
2195
|
-n, --name <id> Custom session name (default: auto-generated)
|
|
1854
2196
|
-r, --resume <id> Resume an existing session by ID
|
|
2197
|
+
-s, --session <id> Alias for --resume
|
|
1855
2198
|
--new-tab Create and attach to a fresh tab instead of reusing an existing one
|
|
1856
2199
|
--target-url <str> Filter targets to those whose URL contains this string
|
|
1857
2200
|
--api-key <key> API key for cloud providers
|
|
@@ -1863,28 +2206,57 @@ Options:
|
|
|
1863
2206
|
--no-highlights Disable visual highlights on screenshots
|
|
1864
2207
|
--no-daemon Skip daemon creation (direct WebSocket only)
|
|
1865
2208
|
--daemon-idle <mins> Daemon idle timeout in minutes (default: 60)
|
|
1866
|
-
|
|
1867
|
-
|
|
2209
|
+
|
|
2210
|
+
Global options:
|
|
2211
|
+
--json Output JSON
|
|
2212
|
+
--pretty Output readable text (default)
|
|
2213
|
+
--debug Enable CDP transport debugging
|
|
1868
2214
|
-h, --help Show this help
|
|
1869
2215
|
|
|
1870
2216
|
Examples:
|
|
1871
|
-
bp connect
|
|
1872
|
-
bp connect --
|
|
1873
|
-
bp connect --
|
|
1874
|
-
bp connect --
|
|
1875
|
-
bp connect --
|
|
1876
|
-
bp connect --
|
|
1877
|
-
bp connect --resume dev # Resume a previous session
|
|
2217
|
+
bp connect # Auto-connect to local Chrome
|
|
2218
|
+
bp connect --name dev # Auto-connect with a custom session name
|
|
2219
|
+
bp connect --resume dev # Resume a previous session
|
|
2220
|
+
bp connect --browser-url ws://localhost:9222/devtools/browser/abc123
|
|
2221
|
+
bp connect --channel beta # Narrow auto-discovery to Chrome Beta
|
|
2222
|
+
bp connect --user-data-dir ~/tmp/chrome-dev # Use a specific Chrome profile
|
|
1878
2223
|
bp connect --target-url localhost:3000 # Attach to tab matching URL
|
|
1879
|
-
bp connect --
|
|
1880
|
-
bp connect --
|
|
2224
|
+
bp connect --record # Connect with session-level recording
|
|
2225
|
+
bp connect --new-tab --page-url https://example.com
|
|
2226
|
+
bp connect --no-daemon # Connect without daemon (file-based only)
|
|
2227
|
+
|
|
2228
|
+
Likely next commands:
|
|
2229
|
+
bp exec -s dev '{"action":"goto","url":"https://example.com"}'
|
|
2230
|
+
bp snapshot -i -s dev
|
|
2231
|
+
bp text -s dev
|
|
1881
2232
|
`.trimEnd();
|
|
2233
|
+
async function resolveInitialPageUrl(page, requestedUrl) {
|
|
2234
|
+
const initialUrl = await page.url();
|
|
2235
|
+
if (!requestedUrl || requestedUrl === "about:blank" || initialUrl !== "about:blank") {
|
|
2236
|
+
return initialUrl;
|
|
2237
|
+
}
|
|
2238
|
+
const deadline = Date.now() + 5e3;
|
|
2239
|
+
while (Date.now() < deadline) {
|
|
2240
|
+
await Bun.sleep(100);
|
|
2241
|
+
const currentUrl = await page.url();
|
|
2242
|
+
if (currentUrl !== "about:blank") {
|
|
2243
|
+
return currentUrl;
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
return initialUrl;
|
|
2247
|
+
}
|
|
1882
2248
|
function parseConnectArgs(args) {
|
|
1883
2249
|
const options = {};
|
|
1884
2250
|
for (let i = 0; i < args.length; i++) {
|
|
1885
2251
|
const arg = args[i];
|
|
1886
2252
|
if (arg === "--provider" || arg === "-p") {
|
|
1887
|
-
|
|
2253
|
+
const p = args[++i];
|
|
2254
|
+
if (p !== "browserbase" && p !== "browserless" && p !== "generic") {
|
|
2255
|
+
throw new Error(
|
|
2256
|
+
`Invalid provider: ${p}. Must be one of: browserbase, browserless, generic`
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
options.provider = p;
|
|
1888
2260
|
} else if (arg === "--url") {
|
|
1889
2261
|
options.url = args[++i];
|
|
1890
2262
|
} else if (arg === "--browser-url") {
|
|
@@ -2016,7 +2388,7 @@ async function connectCommand(args, globalOptions) {
|
|
|
2016
2388
|
void 0,
|
|
2017
2389
|
options.targetUrl ? { targetUrl: options.targetUrl } : void 0
|
|
2018
2390
|
);
|
|
2019
|
-
const currentUrl = await page
|
|
2391
|
+
const currentUrl = await resolveInitialPageUrl(page, pageUrl);
|
|
2020
2392
|
const sessionId = options.name ?? generateSessionId();
|
|
2021
2393
|
let recordSettings;
|
|
2022
2394
|
if (options.record) {
|
|
@@ -2098,10 +2470,12 @@ Subcommands:
|
|
|
2098
2470
|
logs Show daemon log output
|
|
2099
2471
|
|
|
2100
2472
|
Options:
|
|
2101
|
-
-s, --session <id> Target session (default: most recent)
|
|
2102
|
-
-f, --format <fmt> Output format: json | pretty (default: pretty)
|
|
2103
|
-
--json Alias for -f json
|
|
2104
2473
|
-n, --lines <n> Number of log lines to show (default: 50)
|
|
2474
|
+
|
|
2475
|
+
Global options:
|
|
2476
|
+
-s, --session <id> Target session (default: most recent)
|
|
2477
|
+
--json Output JSON
|
|
2478
|
+
--pretty Output readable text (default)
|
|
2105
2479
|
-h, --help Show this help
|
|
2106
2480
|
|
|
2107
2481
|
Examples:
|
|
@@ -2731,11 +3105,15 @@ Examples:
|
|
|
2731
3105
|
bp diagnose "submit" Find elements matching "submit"
|
|
2732
3106
|
bp diagnose "ref:e4" Diagnose by element ref
|
|
2733
3107
|
|
|
2734
|
-
|
|
2735
|
-
--json Output as JSON
|
|
3108
|
+
Local options:
|
|
2736
3109
|
--max <n> Max candidates for fuzzy match (default: 5)
|
|
2737
|
-
|
|
2738
|
-
|
|
3110
|
+
|
|
3111
|
+
Global options:
|
|
3112
|
+
-s, --session <id> Session to use (default: most recent)
|
|
3113
|
+
--json Output JSON
|
|
3114
|
+
--pretty Output readable text (default)
|
|
3115
|
+
--debug Enable CDP transport debugging
|
|
3116
|
+
-h, --help Show this help
|
|
2739
3117
|
|
|
2740
3118
|
Likely next commands:
|
|
2741
3119
|
bp exec '[{"action":"click","selector":"<suggested-selector>"}]'
|
|
@@ -3770,8 +4148,8 @@ async function attachSession(session, options = {}) {
|
|
|
3770
4148
|
const cdp = createCDPClientFromTransport(transport, {
|
|
3771
4149
|
debug: options.trace
|
|
3772
4150
|
});
|
|
3773
|
-
const { Browser: BrowserClass } = await import("./browser-
|
|
3774
|
-
const { Page: PageClass } = await import("./page-
|
|
4151
|
+
const { Browser: BrowserClass } = await import("./browser-4ZHNAQR5.mjs");
|
|
4152
|
+
const { Page: PageClass } = await import("./page-SD64DY3F.mjs");
|
|
3775
4153
|
const browser2 = BrowserClass.fromCDP(cdp, session);
|
|
3776
4154
|
const page2 = session.daemon.cdpSessionId && session.targetId ? addBatchToPage(
|
|
3777
4155
|
await (async () => {
|
|
@@ -3824,25 +4202,29 @@ bp eval - Evaluate JavaScript in the browser
|
|
|
3824
4202
|
|
|
3825
4203
|
Convenience wrapper around exec's evaluate action.
|
|
3826
4204
|
No JSON escaping needed -- just pass a JS expression directly.
|
|
4205
|
+
Use this as an escape hatch after higher-level commands like snapshot, text, review, and exec.
|
|
3827
4206
|
|
|
3828
4207
|
Usage:
|
|
3829
4208
|
bp eval '<expression>' Evaluate inline JavaScript
|
|
3830
4209
|
bp eval -f <file> Evaluate JavaScript from a file
|
|
3831
4210
|
echo '<expr>' | bp eval Evaluate from stdin
|
|
3832
4211
|
|
|
3833
|
-
|
|
3834
|
-
-f, --file <path>
|
|
3835
|
-
--wrap
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
--
|
|
3839
|
-
--
|
|
3840
|
-
|
|
4212
|
+
Local options:
|
|
4213
|
+
-f, --file <path> Read JavaScript from a file
|
|
4214
|
+
--wrap Wrap the expression in an async IIFE
|
|
4215
|
+
|
|
4216
|
+
Global options:
|
|
4217
|
+
-s, --session <id> Session to use (default: most recent)
|
|
4218
|
+
--json Output JSON
|
|
4219
|
+
--pretty Output readable text (default)
|
|
4220
|
+
--debug Enable CDP transport debugging
|
|
4221
|
+
-h, --help Show this help
|
|
3841
4222
|
|
|
3842
4223
|
Examples:
|
|
3843
4224
|
bp eval 'document.title'
|
|
3844
4225
|
bp eval 'document.querySelectorAll("a").length'
|
|
3845
4226
|
bp eval -f scrape.js
|
|
4227
|
+
bp eval --wrap 'await fetch("/health").then((r) => r.status)'
|
|
3846
4228
|
`.trimEnd();
|
|
3847
4229
|
function parseEvalArgs(args) {
|
|
3848
4230
|
const options = {};
|
|
@@ -3938,14 +4320,10 @@ Usage:
|
|
|
3938
4320
|
bp exec -f <file> Execute action(s) from a JSON file
|
|
3939
4321
|
echo '<json>' | bp exec Execute action(s) from stdin
|
|
3940
4322
|
|
|
3941
|
-
|
|
3942
|
-
-f, --file <path>
|
|
3943
|
-
-o, --output <path>
|
|
3944
|
-
--dialog <mode>
|
|
3945
|
-
-s, --session <id> Session to use (default: most recent)
|
|
3946
|
-
-f, --format <fmt> Output format: json | pretty (default: pretty)
|
|
3947
|
-
--json Alias for -f json
|
|
3948
|
-
--debug Enable CDP transport debugging (global option)
|
|
4323
|
+
Local options:
|
|
4324
|
+
-f, --file <path> Read actions from a JSON file
|
|
4325
|
+
-o, --output <path> Write command output to a file instead of stdout
|
|
4326
|
+
--dialog <mode> Handle native dialogs: accept | dismiss
|
|
3949
4327
|
|
|
3950
4328
|
Recording:
|
|
3951
4329
|
--record Enable screenshot recording
|
|
@@ -3955,6 +4333,11 @@ Recording:
|
|
|
3955
4333
|
--no-highlights Disable visual highlights on screenshots
|
|
3956
4334
|
Sensitive fields (passwords, OTPs, card inputs) are redacted
|
|
3957
4335
|
|
|
4336
|
+
Global options:
|
|
4337
|
+
-s, --session <id> Session to use (default: most recent)
|
|
4338
|
+
--json Output JSON
|
|
4339
|
+
--pretty Output readable text (default)
|
|
4340
|
+
--debug Enable CDP transport debugging
|
|
3958
4341
|
-h, --help Show this help
|
|
3959
4342
|
|
|
3960
4343
|
Examples:
|
|
@@ -4181,6 +4564,22 @@ Run 'bp actions' for complete action reference.${evalTip}`
|
|
|
4181
4564
|
data: stepResult.result
|
|
4182
4565
|
});
|
|
4183
4566
|
}
|
|
4567
|
+
if (stepResult.outcomeStatus) {
|
|
4568
|
+
logger.logTrace({
|
|
4569
|
+
channel: "action",
|
|
4570
|
+
event: `action.outcome.${stepResult.outcomeStatus}`,
|
|
4571
|
+
summary: `Outcome: ${stepResult.outcomeStatus}${stepResult.retrySafe === false ? " (unsafe to retry)" : ""}`,
|
|
4572
|
+
data: {
|
|
4573
|
+
outcomeStatus: stepResult.outcomeStatus,
|
|
4574
|
+
retrySafe: stepResult.retrySafe,
|
|
4575
|
+
matchedConditions: stepResult.matchedConditions?.map((mc) => ({
|
|
4576
|
+
kind: mc.condition.kind,
|
|
4577
|
+
matched: mc.matched,
|
|
4578
|
+
detail: mc.detail
|
|
4579
|
+
}))
|
|
4580
|
+
}
|
|
4581
|
+
});
|
|
4582
|
+
}
|
|
4184
4583
|
}
|
|
4185
4584
|
if (result.recordingManifest && session.exportLog) {
|
|
4186
4585
|
mirrorRecordingToExport(result.recordingManifest, session.exportLog);
|
|
@@ -4222,7 +4621,10 @@ Run 'bp actions' for complete action reference.${evalTip}`
|
|
|
4222
4621
|
selectorUsed: s.selectorUsed,
|
|
4223
4622
|
error: s.error,
|
|
4224
4623
|
text: s.text,
|
|
4225
|
-
result: s.result
|
|
4624
|
+
result: s.result,
|
|
4625
|
+
...s.outcomeStatus !== void 0 ? { outcomeStatus: s.outcomeStatus } : {},
|
|
4626
|
+
...s.matchedConditions !== void 0 ? { matchedConditions: s.matchedConditions } : {},
|
|
4627
|
+
...s.retrySafe !== void 0 ? { retrySafe: s.retrySafe } : {}
|
|
4226
4628
|
}));
|
|
4227
4629
|
const payload = {
|
|
4228
4630
|
success: result.success,
|
|
@@ -4332,19 +4734,29 @@ function formatInteractiveElementsPretty(elements, limit = elements.length) {
|
|
|
4332
4734
|
var FORMS_HELP = `
|
|
4333
4735
|
bp forms - List form controls on the current page
|
|
4334
4736
|
|
|
4737
|
+
When to use:
|
|
4738
|
+
You need field names, types, values, or disabled state without the rest of the page.
|
|
4739
|
+
|
|
4740
|
+
When not to use:
|
|
4741
|
+
You need clickable refs or a broader page summary. Use \`bp snapshot -i\` or \`bp page\`.
|
|
4742
|
+
|
|
4335
4743
|
Usage:
|
|
4336
4744
|
bp forms [options]
|
|
4337
4745
|
|
|
4338
|
-
|
|
4746
|
+
Global options:
|
|
4339
4747
|
-s, --session <id> Session to use (default: most recent)
|
|
4340
|
-
|
|
4341
|
-
--
|
|
4342
|
-
--
|
|
4748
|
+
--json Output JSON
|
|
4749
|
+
--pretty Output readable text (default)
|
|
4750
|
+
--debug Enable CDP transport debugging
|
|
4343
4751
|
-h, --help Show this help
|
|
4344
4752
|
|
|
4345
4753
|
Examples:
|
|
4346
4754
|
bp forms
|
|
4347
4755
|
bp forms --json
|
|
4756
|
+
|
|
4757
|
+
Likely next commands:
|
|
4758
|
+
bp exec '[{"action":"fill","selector":"ref:e4","value":"..."}]'
|
|
4759
|
+
bp review --json
|
|
4348
4760
|
`.trimEnd();
|
|
4349
4761
|
async function formsCommand(_args, globalOptions) {
|
|
4350
4762
|
if (globalOptions.help) {
|
|
@@ -4381,11 +4793,14 @@ Usage:
|
|
|
4381
4793
|
bp list -s <id> --log-path Print path to session log file (for analysis)
|
|
4382
4794
|
|
|
4383
4795
|
Options:
|
|
4384
|
-
-s, --session <id> Target session (or uses default session)
|
|
4385
4796
|
--info Show session details and log statistics
|
|
4386
4797
|
--log-tail [n] Show last n action log entries (default: 20)
|
|
4387
4798
|
--log-path Print absolute path to log.jsonl file
|
|
4388
|
-
|
|
4799
|
+
|
|
4800
|
+
Global options:
|
|
4801
|
+
-s, --session <id> Target session (or uses default session)
|
|
4802
|
+
--json Machine-readable JSON output
|
|
4803
|
+
--pretty Output readable text (default)
|
|
4389
4804
|
-h, --help Show this help
|
|
4390
4805
|
|
|
4391
4806
|
Examples:
|
|
@@ -4567,7 +4982,11 @@ When to use:
|
|
|
4567
4982
|
You want a quick summary of URL, title, headings, forms, and interactive controls.
|
|
4568
4983
|
|
|
4569
4984
|
When not to use:
|
|
4570
|
-
You need the full accessibility tree or
|
|
4985
|
+
You need the full accessibility tree or the full ref inventory for precise automation. Use \`bp snapshot\`.
|
|
4986
|
+
|
|
4987
|
+
Common mistake:
|
|
4988
|
+
Treating \`bp page\` as exhaustive. It is a compact overview; the Actions section caches reusable refs,
|
|
4989
|
+
but use \`bp snapshot -i\` when you need the full actionable surface.
|
|
4571
4990
|
|
|
4572
4991
|
Likely next commands:
|
|
4573
4992
|
bp snapshot -i
|
|
@@ -4577,11 +4996,11 @@ Likely next commands:
|
|
|
4577
4996
|
Usage:
|
|
4578
4997
|
bp page [options]
|
|
4579
4998
|
|
|
4580
|
-
|
|
4999
|
+
Global options:
|
|
4581
5000
|
-s, --session <id> Session to use (default: most recent)
|
|
4582
|
-
|
|
4583
|
-
--
|
|
4584
|
-
--
|
|
5001
|
+
--json Output JSON
|
|
5002
|
+
--pretty Output readable text (default)
|
|
5003
|
+
--debug Enable CDP transport debugging
|
|
4585
5004
|
-h, --help Show this help
|
|
4586
5005
|
|
|
4587
5006
|
Examples:
|
|
@@ -4649,7 +5068,16 @@ async function pageCommand(_args, globalOptions) {
|
|
|
4649
5068
|
globalOptions.format === "json" ? summary : formatPageSummary(summary),
|
|
4650
5069
|
globalOptions.format
|
|
4651
5070
|
);
|
|
4652
|
-
await updateSession(session.id, {
|
|
5071
|
+
await updateSession(session.id, {
|
|
5072
|
+
currentUrl: url,
|
|
5073
|
+
metadata: {
|
|
5074
|
+
refCache: {
|
|
5075
|
+
url,
|
|
5076
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5077
|
+
refMap: page.exportRefMap()
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
});
|
|
4653
5081
|
} finally {
|
|
4654
5082
|
await browser.disconnect();
|
|
4655
5083
|
}
|
|
@@ -4660,14 +5088,14 @@ var QUICKSTART = `
|
|
|
4660
5088
|
browser-pilot CLI - Quick Start Guide
|
|
4661
5089
|
|
|
4662
5090
|
STEP 1: CONNECT TO A BROWSER
|
|
4663
|
-
bp connect --
|
|
5091
|
+
bp connect --name mysite
|
|
4664
5092
|
|
|
4665
5093
|
This creates a session. The CLI remembers it for subsequent commands.
|
|
4666
5094
|
|
|
4667
5095
|
STEP 2: NAVIGATE
|
|
4668
|
-
bp exec '{"action":"goto","url":"https://example.com"}'
|
|
5096
|
+
bp exec -s mysite '{"action":"goto","url":"https://example.com"}'
|
|
4669
5097
|
|
|
4670
|
-
STEP 3:
|
|
5098
|
+
STEP 3: CHOOSE THE RIGHT INSPECTION COMMAND
|
|
4671
5099
|
bp snapshot -i
|
|
4672
5100
|
|
|
4673
5101
|
Shows only interactive elements (buttons, inputs, links) with refs:
|
|
@@ -4675,39 +5103,48 @@ STEP 3: GET PAGE SNAPSHOT
|
|
|
4675
5103
|
textbox "Email" ref:e3
|
|
4676
5104
|
link "Forgot password?" ref:e6
|
|
4677
5105
|
|
|
4678
|
-
Other
|
|
4679
|
-
bp
|
|
4680
|
-
bp
|
|
5106
|
+
Other inspection commands:
|
|
5107
|
+
bp page # Compact overview: URL, title, headings, forms, actions
|
|
5108
|
+
bp text # Readable page copy or policy text
|
|
5109
|
+
bp review --json # Structured business state after actions
|
|
5110
|
+
bp diagnose 'submit' # Debug selector or targeting failures
|
|
4681
5111
|
|
|
4682
5112
|
STEP 4: INTERACT USING REFS
|
|
4683
|
-
bp exec '{"action":"fill","selector":"ref:e3","value":"test@example.com"}'
|
|
4684
|
-
bp exec '{"action":"click","selector":"ref:e2"}'
|
|
5113
|
+
bp exec -s mysite '{"action":"fill","selector":"ref:e3","value":"test@example.com"}'
|
|
5114
|
+
bp exec -s mysite '{"action":"click","selector":"ref:e2"}'
|
|
4685
5115
|
|
|
4686
5116
|
STEP 5: BATCH MULTIPLE ACTIONS
|
|
4687
|
-
bp exec '[
|
|
5117
|
+
bp exec -s mysite '[
|
|
4688
5118
|
{"action":"fill","selector":"ref:e3","value":"user@test.com"},
|
|
4689
5119
|
{"action":"click","selector":"ref:e2"},
|
|
4690
5120
|
{"action":"snapshot"}
|
|
4691
5121
|
]'
|
|
4692
5122
|
|
|
4693
5123
|
FOR AI AGENTS
|
|
4694
|
-
|
|
5124
|
+
Start with:
|
|
5125
|
+
bp --help
|
|
5126
|
+
bp --version
|
|
5127
|
+
|
|
5128
|
+
Use bp snapshot -i for most workflows - it shows actionable elements.
|
|
4695
5129
|
Add --json for machine-readable output:
|
|
4696
|
-
bp snapshot -i --json
|
|
4697
|
-
bp exec '{"action":"click","selector":"ref:e3"}' --json
|
|
5130
|
+
bp snapshot -i -s mysite --json
|
|
5131
|
+
bp exec -s mysite '{"action":"click","selector":"ref:e3"}' --json
|
|
4698
5132
|
|
|
4699
5133
|
PAGE DISCOVERY SHORTCUTS
|
|
4700
|
-
bp page
|
|
4701
|
-
bp forms
|
|
4702
|
-
bp
|
|
4703
|
-
bp
|
|
4704
|
-
|
|
5134
|
+
bp page # URL, title, headings, forms, and interactive controls
|
|
5135
|
+
bp forms # Structured list of form fields only
|
|
5136
|
+
bp text --selector '#main' # Focused readable text extraction
|
|
5137
|
+
bp review --json # Structured business state
|
|
5138
|
+
bp targets # All available browser tabs
|
|
5139
|
+
bp connect --new-tab --page-url https://example.com
|
|
5140
|
+
# Convenience: start from a fresh tab
|
|
4705
5141
|
|
|
4706
5142
|
TIPS
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
5143
|
+
- Refs (e1, e2...) are stable within the current page state
|
|
5144
|
+
- After navigation or major DOM changes, take a new snapshot to refresh refs
|
|
5145
|
+
- Use multi-selectors for resilience: ["ref:e3", "#email", "input[type=email]"]
|
|
5146
|
+
- Add "optional":true to skip elements that may not exist
|
|
5147
|
+
- Use bp eval only as an escape hatch when higher-level commands are insufficient
|
|
4711
5148
|
|
|
4712
5149
|
SELECTOR PRIORITY
|
|
4713
5150
|
1. ref:e5 From snapshot - most reliable
|
|
@@ -5524,15 +5961,6 @@ var RECORDER_SCRIPT = `(function() {
|
|
|
5524
5961
|
})();`;
|
|
5525
5962
|
|
|
5526
5963
|
// src/recording/recorder.ts
|
|
5527
|
-
function readString(value) {
|
|
5528
|
-
return typeof value === "string" ? value : void 0;
|
|
5529
|
-
}
|
|
5530
|
-
function readStringOr(value, fallback = "") {
|
|
5531
|
-
return readString(value) ?? fallback;
|
|
5532
|
-
}
|
|
5533
|
-
function formatConsoleArg(entry) {
|
|
5534
|
-
return readString(entry["value"]) ?? readString(entry["description"]) ?? "";
|
|
5535
|
-
}
|
|
5536
5964
|
var Recorder = class {
|
|
5537
5965
|
cdp;
|
|
5538
5966
|
options;
|
|
@@ -6073,11 +6501,6 @@ var Recorder = class {
|
|
|
6073
6501
|
return entries;
|
|
6074
6502
|
}
|
|
6075
6503
|
};
|
|
6076
|
-
function globToRegex(pattern) {
|
|
6077
|
-
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
6078
|
-
const withWildcards = escaped.replace(/\*/g, ".*");
|
|
6079
|
-
return new RegExp(`^${withWildcards}$`);
|
|
6080
|
-
}
|
|
6081
6504
|
|
|
6082
6505
|
// src/cli/commands/record.ts
|
|
6083
6506
|
var RECORD_HELP = `
|
|
@@ -6598,6 +7021,114 @@ async function recordCommand(args, globalOptions) {
|
|
|
6598
7021
|
}
|
|
6599
7022
|
}
|
|
6600
7023
|
|
|
7024
|
+
// src/cli/commands/review.ts
|
|
7025
|
+
var REVIEW_HELP = `
|
|
7026
|
+
bp review - Extract structured business state from the current page
|
|
7027
|
+
|
|
7028
|
+
When to use:
|
|
7029
|
+
You want a structured summary of the page: headings, forms, alerts, tables,
|
|
7030
|
+
key-value pairs, and status labels. Useful for verifying business state after
|
|
7031
|
+
an action sequence, especially on detail, checkout, and confirmation pages.
|
|
7032
|
+
|
|
7033
|
+
When not to use:
|
|
7034
|
+
You need the full accessibility tree with refs. Use \`bp snapshot\`.
|
|
7035
|
+
You want a compact overview. Use \`bp page\`.
|
|
7036
|
+
You are on a dense catalog or marketing page with lots of nav chrome. Use \`bp text\` or \`bp page\`.
|
|
7037
|
+
|
|
7038
|
+
Likely next commands:
|
|
7039
|
+
bp snapshot -i
|
|
7040
|
+
bp exec '[{"action":"click","selector":"ref:e4"}]'
|
|
7041
|
+
|
|
7042
|
+
Usage:
|
|
7043
|
+
bp review [options]
|
|
7044
|
+
|
|
7045
|
+
Global options:
|
|
7046
|
+
-s, --session <id> Session to use (default: most recent)
|
|
7047
|
+
--json Output JSON
|
|
7048
|
+
--pretty Output readable text (default)
|
|
7049
|
+
--debug Enable CDP transport debugging
|
|
7050
|
+
-h, --help Show this help
|
|
7051
|
+
|
|
7052
|
+
Examples:
|
|
7053
|
+
bp review
|
|
7054
|
+
bp review --json
|
|
7055
|
+
bp review -s my-session
|
|
7056
|
+
`.trimEnd();
|
|
7057
|
+
function formatReviewPretty(review) {
|
|
7058
|
+
const lines = [];
|
|
7059
|
+
lines.push(`URL: ${review.url}`);
|
|
7060
|
+
lines.push(`Title: ${review.title}`);
|
|
7061
|
+
lines.push("", "Headings:");
|
|
7062
|
+
if (review.headings.length === 0) {
|
|
7063
|
+
lines.push(" (none)");
|
|
7064
|
+
} else {
|
|
7065
|
+
for (const h of review.headings) {
|
|
7066
|
+
lines.push(` ${h}`);
|
|
7067
|
+
}
|
|
7068
|
+
}
|
|
7069
|
+
if (review.alerts.length > 0) {
|
|
7070
|
+
lines.push("", "Alerts:");
|
|
7071
|
+
for (const a of review.alerts) {
|
|
7072
|
+
lines.push(` ${a}`);
|
|
7073
|
+
}
|
|
7074
|
+
}
|
|
7075
|
+
if (review.statusLabels.length > 0) {
|
|
7076
|
+
lines.push("", "Status:");
|
|
7077
|
+
for (const s of review.statusLabels) {
|
|
7078
|
+
lines.push(` ${s}`);
|
|
7079
|
+
}
|
|
7080
|
+
}
|
|
7081
|
+
if (review.keyValues.length > 0) {
|
|
7082
|
+
lines.push("", "Key-Value Pairs:");
|
|
7083
|
+
for (const kv of review.keyValues) {
|
|
7084
|
+
lines.push(` ${kv.key}: ${kv.value}`);
|
|
7085
|
+
}
|
|
7086
|
+
}
|
|
7087
|
+
if (review.tables.length > 0) {
|
|
7088
|
+
lines.push("", "Tables:");
|
|
7089
|
+
for (const table of review.tables) {
|
|
7090
|
+
if (table.headers.length > 0) {
|
|
7091
|
+
lines.push(` | ${table.headers.join(" | ")} |`);
|
|
7092
|
+
lines.push(` | ${table.headers.map(() => "---").join(" | ")} |`);
|
|
7093
|
+
}
|
|
7094
|
+
for (const row of table.rows) {
|
|
7095
|
+
lines.push(` | ${row.join(" | ")} |`);
|
|
7096
|
+
}
|
|
7097
|
+
lines.push("");
|
|
7098
|
+
}
|
|
7099
|
+
}
|
|
7100
|
+
lines.push("", "Forms:");
|
|
7101
|
+
if (review.forms.length === 0) {
|
|
7102
|
+
lines.push(" (none)");
|
|
7103
|
+
} else {
|
|
7104
|
+
for (const f of review.forms) {
|
|
7105
|
+
const disabled = f.disabled ? " (disabled)" : "";
|
|
7106
|
+
const label = f.label ?? "(unlabeled)";
|
|
7107
|
+
lines.push(` ${label} [${f.type}]: ${f.value ?? ""}${disabled}`);
|
|
7108
|
+
}
|
|
7109
|
+
}
|
|
7110
|
+
return lines.join("\n");
|
|
7111
|
+
}
|
|
7112
|
+
async function reviewCommand(_args, globalOptions) {
|
|
7113
|
+
if (globalOptions.help) {
|
|
7114
|
+
process.stdout.write(`${REVIEW_HELP}
|
|
7115
|
+
`);
|
|
7116
|
+
return;
|
|
7117
|
+
}
|
|
7118
|
+
const session = await resolveSession(globalOptions.session);
|
|
7119
|
+
const { browser, page } = await attachSession(session, { trace: globalOptions.trace });
|
|
7120
|
+
try {
|
|
7121
|
+
const review = await page.review();
|
|
7122
|
+
output(
|
|
7123
|
+
globalOptions.format === "json" ? review : formatReviewPretty(review),
|
|
7124
|
+
globalOptions.format
|
|
7125
|
+
);
|
|
7126
|
+
await updateSession(session.id, { currentUrl: review.url });
|
|
7127
|
+
} finally {
|
|
7128
|
+
await browser.disconnect();
|
|
7129
|
+
}
|
|
7130
|
+
}
|
|
7131
|
+
|
|
6601
7132
|
// src/cli/commands/run.ts
|
|
6602
7133
|
import { readFile } from "fs/promises";
|
|
6603
7134
|
import { resolve as resolve6 } from "path";
|
|
@@ -6740,17 +7271,26 @@ ${result.success ? "Workflow passed" : "Workflow failed"} in ${result.totalDurat
|
|
|
6740
7271
|
var SCREENSHOT_HELP = `
|
|
6741
7272
|
bp screenshot - Take a screenshot of the current page
|
|
6742
7273
|
|
|
7274
|
+
When to use:
|
|
7275
|
+
You need a visual artifact, regression evidence, or a screenshot to attach elsewhere.
|
|
7276
|
+
|
|
7277
|
+
When not to use:
|
|
7278
|
+
You need readable copy or structured business data. Use \`bp text\`, \`bp page\`, or \`bp review\`.
|
|
7279
|
+
|
|
6743
7280
|
Usage:
|
|
6744
7281
|
bp screenshot [options]
|
|
6745
7282
|
|
|
6746
|
-
|
|
7283
|
+
Local options:
|
|
6747
7284
|
-o, --output <path> Save screenshot to file (default: print base64 to stdout)
|
|
6748
7285
|
-f, --format <type> Image format: png | jpeg | webp (default: png)
|
|
6749
7286
|
-q, --quality <n> Image quality 0-100 (jpeg/webp only)
|
|
6750
7287
|
--full-page Capture the full scrollable page
|
|
7288
|
+
|
|
7289
|
+
Global options:
|
|
6751
7290
|
-s, --session <id> Session to use (default: most recent)
|
|
6752
|
-
--json Output
|
|
6753
|
-
--
|
|
7291
|
+
--json Output JSON (base64 data + metadata)
|
|
7292
|
+
--pretty Output readable text for file writes (default)
|
|
7293
|
+
--debug Enable CDP transport debugging
|
|
6754
7294
|
-h, --help Show this help
|
|
6755
7295
|
|
|
6756
7296
|
Examples:
|
|
@@ -7096,23 +7636,27 @@ Common mistake:
|
|
|
7096
7636
|
Usage:
|
|
7097
7637
|
bp snapshot [options]
|
|
7098
7638
|
|
|
7099
|
-
|
|
7100
|
-
-i, --interactive
|
|
7101
|
-
|
|
7102
|
-
--
|
|
7103
|
-
|
|
7104
|
-
-
|
|
7105
|
-
--
|
|
7106
|
-
--
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
--
|
|
7111
|
-
|
|
7639
|
+
Local options:
|
|
7640
|
+
-i, --interactive Shortcut for --view interactive
|
|
7641
|
+
--view <type> Snapshot view: full | interactive | text (default: text)
|
|
7642
|
+
-f, --format <type> Backward-compatible alias for --view
|
|
7643
|
+
--role <roles> Filter snapshot to accessibility roles (for example: radio,checkbox)
|
|
7644
|
+
-o, --output <path> Write command output to a file instead of stdout
|
|
7645
|
+
-d, --diff <file> Compare current page against a saved snapshot JSON
|
|
7646
|
+
--inspect Inject visual ref labels onto the page (auto-removes after 10s)
|
|
7647
|
+
--keep Keep visual ref labels visible (use with --inspect)
|
|
7648
|
+
|
|
7649
|
+
Global options:
|
|
7650
|
+
-s, --session <id> Session to use (default: most recent)
|
|
7651
|
+
--json Output JSON; without --view this returns the full snapshot payload
|
|
7652
|
+
--pretty Output readable text (default)
|
|
7653
|
+
--debug Enable CDP transport debugging
|
|
7654
|
+
-h, --help Show this help
|
|
7112
7655
|
|
|
7113
7656
|
Examples:
|
|
7114
7657
|
bp snapshot # Full accessibility tree as readable text
|
|
7115
7658
|
bp snapshot -i # Interactive elements only; best default for automation
|
|
7659
|
+
bp snapshot --view full # Full structured snapshot
|
|
7116
7660
|
bp snapshot --role radio,checkbox # Focus on specific control roles
|
|
7117
7661
|
bp snapshot --json > page.json # Save full snapshot to file
|
|
7118
7662
|
bp snapshot --diff before.json # Show what changed since before.json
|
|
@@ -7129,7 +7673,7 @@ function parseSnapshotArgs(args) {
|
|
|
7129
7673
|
};
|
|
7130
7674
|
for (let i = 0; i < args.length; i++) {
|
|
7131
7675
|
const arg = args[i];
|
|
7132
|
-
if (arg === "--format" || arg === "-f") {
|
|
7676
|
+
if (arg === "--view" || arg === "--format" || arg === "-f") {
|
|
7133
7677
|
options.format = args[++i];
|
|
7134
7678
|
options.formatExplicit = true;
|
|
7135
7679
|
} else if (arg === "--diff" || arg === "-d") {
|
|
@@ -7262,11 +7806,11 @@ bp targets - List page tabs available in the connected browser
|
|
|
7262
7806
|
Usage:
|
|
7263
7807
|
bp targets [options]
|
|
7264
7808
|
|
|
7265
|
-
|
|
7809
|
+
Global options:
|
|
7266
7810
|
-s, --session <id> Session to use (default: most recent)
|
|
7267
|
-
|
|
7268
|
-
--
|
|
7269
|
-
--
|
|
7811
|
+
--json Output JSON
|
|
7812
|
+
--pretty Output readable text (default)
|
|
7813
|
+
--debug Enable CDP transport debugging
|
|
7270
7814
|
-h, --help Show this help
|
|
7271
7815
|
|
|
7272
7816
|
Examples:
|
|
@@ -7331,28 +7875,31 @@ Common mistake:
|
|
|
7331
7875
|
Usage:
|
|
7332
7876
|
bp text [options]
|
|
7333
7877
|
|
|
7334
|
-
|
|
7335
|
-
--selector <
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
--
|
|
7339
|
-
--
|
|
7340
|
-
|
|
7878
|
+
Local options:
|
|
7879
|
+
--selector <selector> Extract text from a specific element (default: entire page)
|
|
7880
|
+
|
|
7881
|
+
Global options:
|
|
7882
|
+
-s, --session <id> Session to use (default: most recent)
|
|
7883
|
+
--json Output JSON with text, URL, and selector
|
|
7884
|
+
--pretty Output readable text only (default)
|
|
7885
|
+
--debug Enable CDP transport debugging
|
|
7886
|
+
-h, --help Show this help
|
|
7341
7887
|
|
|
7342
7888
|
Examples:
|
|
7343
7889
|
bp text # Extract all text from the page
|
|
7344
7890
|
bp text --selector '#main' # Extract text from #main element only
|
|
7345
|
-
bp text --json
|
|
7891
|
+
bp text -s dev --json # Output JSON with URL and selector info
|
|
7346
7892
|
|
|
7347
7893
|
Likely next commands:
|
|
7348
7894
|
bp snapshot -i
|
|
7895
|
+
bp review --json
|
|
7349
7896
|
bp exec '[{"action":"assertText","expect":"..."}]'
|
|
7350
7897
|
`.trimEnd();
|
|
7351
7898
|
function parseTextArgs(args) {
|
|
7352
7899
|
const options = {};
|
|
7353
7900
|
for (let i = 0; i < args.length; i++) {
|
|
7354
7901
|
const arg = args[i];
|
|
7355
|
-
if (arg === "--selector"
|
|
7902
|
+
if (arg === "--selector") {
|
|
7356
7903
|
options.selector = args[++i];
|
|
7357
7904
|
} else if (arg === "-h" || arg === "--help") {
|
|
7358
7905
|
options.help = true;
|
|
@@ -7360,6 +7907,9 @@ function parseTextArgs(args) {
|
|
|
7360
7907
|
}
|
|
7361
7908
|
return options;
|
|
7362
7909
|
}
|
|
7910
|
+
function looksLikeSelector(value) {
|
|
7911
|
+
return value.startsWith("#") || value.startsWith(".") || value.startsWith("[") || value.startsWith("/") || value.startsWith("ref:") || value.includes(">");
|
|
7912
|
+
}
|
|
7363
7913
|
async function textCommand(args, globalOptions) {
|
|
7364
7914
|
const options = parseTextArgs(args);
|
|
7365
7915
|
if (options.help || globalOptions.help) {
|
|
@@ -7368,7 +7918,18 @@ async function textCommand(args, globalOptions) {
|
|
|
7368
7918
|
}
|
|
7369
7919
|
let session;
|
|
7370
7920
|
if (globalOptions.session) {
|
|
7371
|
-
|
|
7921
|
+
try {
|
|
7922
|
+
session = await loadSession(globalOptions.session);
|
|
7923
|
+
} catch (error) {
|
|
7924
|
+
if (!options.selector && looksLikeSelector(globalOptions.session)) {
|
|
7925
|
+
throw new Error(
|
|
7926
|
+
`bp text uses --selector for element targeting. "-s" is reserved for sessions.
|
|
7927
|
+
|
|
7928
|
+
Try: bp text --selector ${JSON.stringify(globalOptions.session)}`
|
|
7929
|
+
);
|
|
7930
|
+
}
|
|
7931
|
+
throw error;
|
|
7932
|
+
}
|
|
7372
7933
|
} else {
|
|
7373
7934
|
session = await getDefaultSession();
|
|
7374
7935
|
if (!session) {
|
|
@@ -7726,74 +8287,77 @@ async function traceCommand(args, globalOptions) {
|
|
|
7726
8287
|
}
|
|
7727
8288
|
}
|
|
7728
8289
|
|
|
8290
|
+
// src/cli/version.ts
|
|
8291
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
8292
|
+
var cachedCliVersion;
|
|
8293
|
+
function getCliVersion() {
|
|
8294
|
+
if (cachedCliVersion) {
|
|
8295
|
+
return cachedCliVersion;
|
|
8296
|
+
}
|
|
8297
|
+
cachedCliVersion = "0.0.17";
|
|
8298
|
+
return cachedCliVersion;
|
|
8299
|
+
}
|
|
8300
|
+
|
|
7729
8301
|
// src/cli/index.ts
|
|
7730
|
-
|
|
8302
|
+
function buildRootHelp() {
|
|
8303
|
+
const routeLabelWidth = Math.max(...CLI_ROUTE_GROUPS.map((group) => group.label.length)) + 2;
|
|
8304
|
+
const routeLines = CLI_ROUTE_GROUPS.map((group) => {
|
|
8305
|
+
const note = group.note ? ` ${group.note}` : "";
|
|
8306
|
+
return ` ${group.label.padEnd(routeLabelWidth)}${group.commands.join(", ")}${note}`;
|
|
8307
|
+
});
|
|
8308
|
+
const commandLabelWidth = Math.max(...ROOT_HELP_COMMANDS.map((command) => command.name.length)) + 2;
|
|
8309
|
+
const commandLines = ROOT_HELP_COMMANDS.map((command) => {
|
|
8310
|
+
return ` ${command.name.padEnd(commandLabelWidth)}${command.description}`;
|
|
8311
|
+
});
|
|
8312
|
+
return `
|
|
7731
8313
|
bp - automation-first browser CLI for agents
|
|
7732
8314
|
|
|
7733
8315
|
Route the job first:
|
|
7734
|
-
|
|
7735
|
-
Act in the browser exec, run
|
|
7736
|
-
Capture a human demo record
|
|
7737
|
-
Analyze behavior over time trace (listen is a compatibility alias)
|
|
7738
|
-
Exercise voice/media audio
|
|
7739
|
-
Change browser conditions env
|
|
8316
|
+
${routeLines.join("\n")}
|
|
7740
8317
|
|
|
7741
8318
|
Usage:
|
|
7742
8319
|
bp <command> [options]
|
|
7743
8320
|
|
|
7744
8321
|
Commands:
|
|
7745
|
-
|
|
7746
|
-
connect Create a browser session
|
|
7747
|
-
exec Execute high-level actions
|
|
7748
|
-
snapshot Inspect current page with refs
|
|
7749
|
-
record Record a human workflow and derive replayable output
|
|
7750
|
-
trace Inspect and analyze behavior over time (listen alias for live stream)
|
|
7751
|
-
audio Set up/validate/inject/capture voice pipelines
|
|
7752
|
-
env Session and browser-environment controls
|
|
7753
|
-
run Run a workflow file
|
|
7754
|
-
page Compact page overview
|
|
7755
|
-
forms List form controls
|
|
7756
|
-
targets List available browser tabs
|
|
7757
|
-
daemon Manage session daemon
|
|
7758
|
-
list List sessions
|
|
7759
|
-
close Close session
|
|
7760
|
-
clean Clean old sessions and artifacts
|
|
7761
|
-
actions Complete action reference
|
|
8322
|
+
${commandLines.join("\n")}
|
|
7762
8323
|
|
|
7763
8324
|
Golden paths:
|
|
7764
|
-
1.
|
|
7765
|
-
bp connect --
|
|
8325
|
+
1. Connect, open a page, inspect it, then act
|
|
8326
|
+
bp connect --name dev
|
|
8327
|
+
bp exec -s dev '{"action":"goto","url":"https://example.com"}'
|
|
7766
8328
|
bp snapshot -i -s dev
|
|
7767
8329
|
bp exec -s dev '[{"action":"click","selector":"ref:e4"}]'
|
|
7768
8330
|
|
|
7769
|
-
2.
|
|
8331
|
+
2. Read content or verify business state
|
|
8332
|
+
bp text -s dev --selector main
|
|
8333
|
+
bp review -s dev --json
|
|
8334
|
+
|
|
8335
|
+
3. Capture a manual workflow and derive automation
|
|
7770
8336
|
bp record -s demo --profile automation
|
|
7771
8337
|
bp record summary demo/recording.json
|
|
7772
8338
|
bp record derive demo/recording.json -o workflow.json
|
|
7773
8339
|
bp run workflow.json
|
|
7774
8340
|
|
|
7775
|
-
|
|
8341
|
+
4. Debug a realtime or voice session
|
|
7776
8342
|
bp trace start -s dev
|
|
7777
8343
|
bp trace summary -s dev --view ws
|
|
7778
8344
|
bp audio check -s dev
|
|
7779
8345
|
bp trace summary -s dev --view voice
|
|
7780
8346
|
|
|
7781
|
-
4. Exercise failure modes
|
|
7782
|
-
bp env network offline -s dev --duration 10000
|
|
7783
|
-
bp trace watch -s dev --view ws --assert profile:reconnect --timeout 15000
|
|
7784
|
-
|
|
7785
8347
|
Options:
|
|
7786
8348
|
-s, --session <id> Session ID
|
|
7787
8349
|
-f, --format <fmt> json | pretty (default: pretty)
|
|
7788
8350
|
--json Alias for -f json
|
|
8351
|
+
--pretty Alias for -f pretty
|
|
7789
8352
|
--debug Enable debug logs for CDP transport
|
|
7790
8353
|
--trace Legacy alias for --debug
|
|
7791
|
-
--dialog <mode> Handle dialogs: accept | dismiss
|
|
7792
8354
|
-h, --help Show help
|
|
8355
|
+
--version Print CLI version
|
|
7793
8356
|
|
|
7794
8357
|
Notes:
|
|
7795
8358
|
Start with "record summary" or "trace summary" before opening raw artifacts.
|
|
7796
|
-
|
|
8359
|
+
`.trim();
|
|
8360
|
+
}
|
|
7797
8361
|
function parseGlobalOptions(args) {
|
|
7798
8362
|
const options = {
|
|
7799
8363
|
format: "pretty"
|
|
@@ -7869,14 +8433,28 @@ function prettyPrint(obj, lines, indent = 0) {
|
|
|
7869
8433
|
}
|
|
7870
8434
|
async function main() {
|
|
7871
8435
|
const args = process.argv.slice(2);
|
|
7872
|
-
if (args.length === 0) {
|
|
7873
|
-
console.log(
|
|
8436
|
+
if (args.length === 0 || args.length === 1 && (args[0] === "--help" || args[0] === "-h" || args[0] === "help")) {
|
|
8437
|
+
console.log(buildRootHelp());
|
|
7874
8438
|
process.exit(0);
|
|
7875
8439
|
}
|
|
7876
|
-
|
|
7877
|
-
|
|
8440
|
+
if (args.length === 1 && (args[0] === "--version" || args[0] === "version")) {
|
|
8441
|
+
process.stdout.write(`${getCliVersion()}
|
|
8442
|
+
`);
|
|
8443
|
+
process.exit(0);
|
|
8444
|
+
}
|
|
8445
|
+
let command = args[0];
|
|
8446
|
+
let commandArgs = args.slice(1);
|
|
8447
|
+
if (command === "help") {
|
|
8448
|
+
if (commandArgs.length === 0) {
|
|
8449
|
+
console.log(buildRootHelp());
|
|
8450
|
+
process.exit(0);
|
|
8451
|
+
}
|
|
8452
|
+
command = commandArgs[0];
|
|
8453
|
+
commandArgs = [...commandArgs.slice(1), "--help"];
|
|
8454
|
+
}
|
|
8455
|
+
const { options, remaining } = parseGlobalOptions(commandArgs);
|
|
7878
8456
|
if (options.help && !command) {
|
|
7879
|
-
console.log(
|
|
8457
|
+
console.log(buildRootHelp());
|
|
7880
8458
|
process.exit(0);
|
|
7881
8459
|
}
|
|
7882
8460
|
try {
|
|
@@ -7932,6 +8510,9 @@ async function main() {
|
|
|
7932
8510
|
case "record":
|
|
7933
8511
|
await recordCommand(remaining, options);
|
|
7934
8512
|
break;
|
|
8513
|
+
case "review":
|
|
8514
|
+
await reviewCommand(remaining, options);
|
|
8515
|
+
break;
|
|
7935
8516
|
case "trace":
|
|
7936
8517
|
await traceCommand(remaining, options);
|
|
7937
8518
|
break;
|
|
@@ -7950,11 +8531,12 @@ async function main() {
|
|
|
7950
8531
|
case "help":
|
|
7951
8532
|
case "--help":
|
|
7952
8533
|
case "-h":
|
|
7953
|
-
console.log(
|
|
8534
|
+
console.log(buildRootHelp());
|
|
7954
8535
|
break;
|
|
7955
8536
|
default:
|
|
7956
8537
|
console.error(`Unknown command: ${command}`);
|
|
7957
|
-
console.
|
|
8538
|
+
console.error('Run "bp --help" to see the available command tree.');
|
|
8539
|
+
console.log(buildRootHelp());
|
|
7958
8540
|
process.exit(1);
|
|
7959
8541
|
}
|
|
7960
8542
|
} catch (error) {
|