browser-pilot-cli 0.2.2 → 0.3.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -6
- package/dist/cli.js +177 -8
- package/dist/daemon.js +806 -72
- package/dist/managed-target-janitor.js +6 -1
- package/docs/architecture/browser-pilot-platform-spec.md +83 -12
- package/docs/integration/stdio-bridge.md +53 -8
- package/docs/plans/profile-context-routing.md +197 -0
- package/docs/releases/v0.3.0-rc.1.md +49 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -28,6 +28,9 @@ a Chrome WebSocket and never display the Allow dialog.
|
|
|
28
28
|
Use `bp connect --browser <id|product|channel>` for an explicit choice. Product
|
|
29
29
|
embedders can pass the same selector to `browser-pilot bridge --stdio
|
|
30
30
|
--browser <selector>`; otherwise selection follows a stable platform order.
|
|
31
|
+
One connection covers every live Chrome Profile on that endpoint. When several
|
|
32
|
+
Profiles are open, `bp connect` lists them without creating a Pilot window; use
|
|
33
|
+
`bp profiles` and `bp profile <index>`, or pass `--profile` to `bp open --new`.
|
|
31
34
|
|
|
32
35
|
### 2. Install the plugin for your agent
|
|
33
36
|
|
|
@@ -66,6 +69,7 @@ The agent will use `bp` commands automatically. Your real login sessions are pre
|
|
|
66
69
|
|
|
67
70
|
- **No extension required** — Uses Chrome 144's native remote debugging toggle, not the Extension Debugger API
|
|
68
71
|
- **Real login sessions** — Operates your actual browser profile. Cookies, extensions, logins all intact
|
|
72
|
+
- **Multi-Profile routing** — Lists user tabs across live Chrome Profiles and creates managed work only in a resolved Profile
|
|
69
73
|
- **CLI-native** — Any agent with bash access can use it. No MCP protocol, no SDK integration needed
|
|
70
74
|
- **Auto-snapshot** — Every action returns page state with numbered `[ref]` elements, so the agent always knows what's on screen
|
|
71
75
|
- **Adaptive page views** — Bounded read, text search, DOM metadata, page geometry, and annotated screenshots let an agent choose the smallest useful representation
|
|
@@ -139,8 +143,9 @@ launching the bridge. Bulk cleanup remains limited to managed tabs, and user
|
|
|
139
143
|
tabs remain open when a session ends.
|
|
140
144
|
|
|
141
145
|
The `browser-pilot bridge --stdio` transport, Broker lifecycle, browser tool
|
|
142
|
-
dispatch, event replay, protected Artifacts,
|
|
143
|
-
negotiation
|
|
146
|
+
dispatch, event replay, protected Artifacts, protocol 1.1 transport limit
|
|
147
|
+
negotiation, and protocol 1.2 Chrome Profile routing are implemented. Browser
|
|
148
|
+
disconnect and explicit reconnect handling, scoped
|
|
144
149
|
download Artifacts, Workspace resource isolation, and typed watchdog events for
|
|
145
150
|
stalled navigation, selected-frame detach, pending dialogs, and repeated
|
|
146
151
|
browser-observable no-progress actions are also implemented. Bounded page
|
|
@@ -177,7 +182,7 @@ A project can pin Browser Pilot locally and run the same CLI without a global
|
|
|
177
182
|
installation:
|
|
178
183
|
|
|
179
184
|
```bash
|
|
180
|
-
npm install --save-exact browser-pilot-cli@0.
|
|
185
|
+
npm install --save-exact browser-pilot-cli@0.3.0-rc.1
|
|
181
186
|
npx --no-install browser-pilot tabs
|
|
182
187
|
```
|
|
183
188
|
|
|
@@ -205,6 +210,7 @@ an incompatible isolated Broker.
|
|
|
205
210
|
| Command | Returns | Description |
|
|
206
211
|
|---------|---------|-------------|
|
|
207
212
|
| `bp open <url>` | snapshot | Navigate to URL |
|
|
213
|
+
| `bp open <url> --new --profile <selector>` | snapshot | Create managed work in one live Chrome Profile |
|
|
208
214
|
| `bp snapshot` | snapshot | Get interactive elements |
|
|
209
215
|
| `bp read [selector]` | text | Get bounded readable page/region content |
|
|
210
216
|
| `bp search <text>` | matches | Find bounded visible text and nearby context |
|
|
@@ -243,8 +249,9 @@ dialogs are scoped to the compatibility Workspace and isolated from embedded
|
|
|
243
249
|
Broker clients.
|
|
244
250
|
|
|
245
251
|
Run `bp tabs` to list Pilot-managed tabs, their popups, and eligible tabs the
|
|
246
|
-
user opened elsewhere in the same browser.
|
|
247
|
-
|
|
252
|
+
user opened elsewhere across all live Profiles in the same browser endpoint.
|
|
253
|
+
Each JSON entry includes its opaque `profileContextId`; `bp tab <n>` switches
|
|
254
|
+
control to any listed tab.
|
|
248
255
|
|
|
249
256
|
### Network
|
|
250
257
|
|
|
@@ -263,8 +270,10 @@ listed tab.
|
|
|
263
270
|
|
|
264
271
|
| Command | Description |
|
|
265
272
|
|---------|-------------|
|
|
266
|
-
| `bp connect` | Connect to Chrome
|
|
273
|
+
| `bp connect` | Connect to Chrome; create a Pilot window immediately only when Profile routing is unambiguous |
|
|
267
274
|
| `bp disconnect` | Close the CLI Pilot window; stop the daemon when no embedded client is live |
|
|
275
|
+
| `bp profiles` | List live Chrome Profile contexts and representative tabs |
|
|
276
|
+
| `bp profile <index\|id\|label\|name>` | Select a Profile for subsequent managed tabs |
|
|
268
277
|
| `bp tabs` | List all controllable tabs in the current browser |
|
|
269
278
|
| `bp tab <n>` | Switch to any listed managed or user tab |
|
|
270
279
|
| `bp close` | Close the current tab (`--all` closes Pilot-managed tabs only) |
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { writeFileSync as writeFileSync2, readFileSync as readFileSync4, existsS
|
|
|
6
6
|
import { resolve as resolvePath } from "path";
|
|
7
7
|
|
|
8
8
|
// src/version.ts
|
|
9
|
-
var BROWSER_PILOT_VERSION = "0.
|
|
9
|
+
var BROWSER_PILOT_VERSION = "0.3.0-rc.1";
|
|
10
10
|
|
|
11
11
|
// src/protocol/errors.ts
|
|
12
12
|
var ERROR_CODES = [
|
|
@@ -17,6 +17,9 @@ var ERROR_CODES = [
|
|
|
17
17
|
"browser_not_authorized",
|
|
18
18
|
"browser_disconnected",
|
|
19
19
|
"broker_in_use",
|
|
20
|
+
"profile_selection_required",
|
|
21
|
+
"profile_context_stale",
|
|
22
|
+
"profile_context_unavailable",
|
|
20
23
|
"workspace_not_found",
|
|
21
24
|
"lease_expired",
|
|
22
25
|
"target_not_owned",
|
|
@@ -1929,6 +1932,7 @@ function resultSchema(context, properties = {}, required = []) {
|
|
|
1929
1932
|
}
|
|
1930
1933
|
if (context === "target") {
|
|
1931
1934
|
contextProperties.targetId = opaqueIdSchema;
|
|
1935
|
+
contextProperties.profileContextId = opaqueIdSchema;
|
|
1932
1936
|
contextProperties.url = boundedUrl;
|
|
1933
1937
|
contextRequired.push("targetId", "url");
|
|
1934
1938
|
}
|
|
@@ -2008,6 +2012,27 @@ var dropdownChoiceSchema = {
|
|
|
2008
2012
|
}, ["by", "value"])
|
|
2009
2013
|
]
|
|
2010
2014
|
};
|
|
2015
|
+
var profileRepresentativeTabSchema = objectSchema({
|
|
2016
|
+
targetId: opaqueIdSchema,
|
|
2017
|
+
title: sensitive(stringSchema({ maxLength: 4096 }), "browser_data"),
|
|
2018
|
+
url: boundedUrl
|
|
2019
|
+
}, ["targetId", "title", "url"]);
|
|
2020
|
+
var profileContextSchema = objectSchema({
|
|
2021
|
+
profileContextId: opaqueIdSchema,
|
|
2022
|
+
label: stringSchema({ minLength: 1, maxLength: 128 }),
|
|
2023
|
+
displayName: sensitive(stringSchema({ minLength: 1, maxLength: 4096 }), "browser_data"),
|
|
2024
|
+
tabCount: integerSchema({ minimum: 0 }),
|
|
2025
|
+
eligibleTabCount: integerSchema({ minimum: 0 }),
|
|
2026
|
+
selected: booleanSchema(),
|
|
2027
|
+
representativeTabs: arraySchema(profileRepresentativeTabSchema, { maxItems: 3 })
|
|
2028
|
+
}, [
|
|
2029
|
+
"profileContextId",
|
|
2030
|
+
"label",
|
|
2031
|
+
"tabCount",
|
|
2032
|
+
"eligibleTabCount",
|
|
2033
|
+
"selected",
|
|
2034
|
+
"representativeTabs"
|
|
2035
|
+
]);
|
|
2011
2036
|
function tool(definition) {
|
|
2012
2037
|
return definition;
|
|
2013
2038
|
}
|
|
@@ -2067,6 +2092,40 @@ var TOOL_DEFINITIONS = [
|
|
|
2067
2092
|
sensitivity: { input: [], output: ["browser_data"] },
|
|
2068
2093
|
artifactKinds: []
|
|
2069
2094
|
}),
|
|
2095
|
+
tool({
|
|
2096
|
+
name: "browser.profiles.list",
|
|
2097
|
+
title: "List browser Profiles",
|
|
2098
|
+
description: "Passively list live Profile contexts and bounded representative tabs.",
|
|
2099
|
+
context: "workspace",
|
|
2100
|
+
inputSchema: emptyInput,
|
|
2101
|
+
outputSchema: resultSchema("workspace", {
|
|
2102
|
+
profiles: arraySchema(profileContextSchema, { maxItems: 128 })
|
|
2103
|
+
}, ["profiles"]),
|
|
2104
|
+
requiredCapabilities: ["browser.control"],
|
|
2105
|
+
mutating: false,
|
|
2106
|
+
idempotency: "read_only",
|
|
2107
|
+
cancellation: "not_applicable",
|
|
2108
|
+
sensitivity: { input: [], output: ["browser_data"] },
|
|
2109
|
+
artifactKinds: []
|
|
2110
|
+
}),
|
|
2111
|
+
tool({
|
|
2112
|
+
name: "browser.profiles.select",
|
|
2113
|
+
title: "Select browser Profile",
|
|
2114
|
+
description: "Select one current Profile context for new managed targets in this Workspace.",
|
|
2115
|
+
context: "workspace",
|
|
2116
|
+
inputSchema: objectSchema({ profileContextId: opaqueIdSchema }, ["profileContextId"]),
|
|
2117
|
+
outputSchema: resultSchema("workspace", {
|
|
2118
|
+
profileContextId: opaqueIdSchema,
|
|
2119
|
+
label: stringSchema({ minLength: 1, maxLength: 128 }),
|
|
2120
|
+
displayName: sensitive(stringSchema({ minLength: 1, maxLength: 4096 }), "browser_data")
|
|
2121
|
+
}, ["profileContextId", "label"]),
|
|
2122
|
+
requiredCapabilities: ["browser.control"],
|
|
2123
|
+
mutating: true,
|
|
2124
|
+
idempotency: "idempotent",
|
|
2125
|
+
cancellation: "before_dispatch",
|
|
2126
|
+
sensitivity: { input: [], output: ["browser_data"] },
|
|
2127
|
+
artifactKinds: []
|
|
2128
|
+
}),
|
|
2070
2129
|
tool({
|
|
2071
2130
|
name: "browser.open",
|
|
2072
2131
|
title: "Open URL",
|
|
@@ -2076,6 +2135,7 @@ var TOOL_DEFINITIONS = [
|
|
|
2076
2135
|
url: boundedUrl,
|
|
2077
2136
|
targetId: opaqueIdSchema,
|
|
2078
2137
|
newTarget: booleanSchema(),
|
|
2138
|
+
profileContextId: opaqueIdSchema,
|
|
2079
2139
|
observationLimit: integerSchema({ minimum: 1, maximum: OBSERVATION_V1_LIMITS.maxElements })
|
|
2080
2140
|
}, ["url"]),
|
|
2081
2141
|
outputSchema: observationOutput,
|
|
@@ -2481,6 +2541,7 @@ var TOOL_DEFINITIONS = [
|
|
|
2481
2541
|
outputSchema: resultSchema("workspace", {
|
|
2482
2542
|
targets: arraySchema(objectSchema({
|
|
2483
2543
|
targetId: opaqueIdSchema,
|
|
2544
|
+
profileContextId: opaqueIdSchema,
|
|
2484
2545
|
title: sensitive(stringSchema({ maxLength: 4096 }), "browser_data"),
|
|
2485
2546
|
url: boundedUrl,
|
|
2486
2547
|
active: booleanSchema(),
|
|
@@ -3180,7 +3241,7 @@ var CompatibilityBrokerClient = class _CompatibilityBrokerClient {
|
|
|
3180
3241
|
version: executableVersion,
|
|
3181
3242
|
instanceId: "local:one-shot"
|
|
3182
3243
|
},
|
|
3183
|
-
protocol: { min: { major: 1, minor: 1 }, max: { major: 1, minor:
|
|
3244
|
+
protocol: { min: { major: 1, minor: 1 }, max: { major: 1, minor: 2 } },
|
|
3184
3245
|
requestedCapabilities: [...CAPABILITIES],
|
|
3185
3246
|
launchMode: "one-shot"
|
|
3186
3247
|
}), "initialize");
|
|
@@ -3229,6 +3290,24 @@ var CompatibilityBrokerClient = class _CompatibilityBrokerClient {
|
|
|
3229
3290
|
}
|
|
3230
3291
|
return result.targets;
|
|
3231
3292
|
}
|
|
3293
|
+
async listProfiles() {
|
|
3294
|
+
const result = await this.callTool("browser.profiles.list");
|
|
3295
|
+
if (!Array.isArray(result.profiles)) {
|
|
3296
|
+
throw new BrowserPilotError("internal_error", "browser.profiles.list returned invalid Profiles");
|
|
3297
|
+
}
|
|
3298
|
+
return result.profiles;
|
|
3299
|
+
}
|
|
3300
|
+
async selectProfile(profileContextId) {
|
|
3301
|
+
const result = await this.callTool("browser.profiles.select", { profileContextId });
|
|
3302
|
+
const profiles = await this.listProfiles();
|
|
3303
|
+
const selected = profiles.find((profile) => profile.profileContextId === result.profileContextId);
|
|
3304
|
+
if (!selected) {
|
|
3305
|
+
throw new BrowserPilotError("profile_context_stale", "Selected Profile context is no longer available", {
|
|
3306
|
+
retryable: true
|
|
3307
|
+
});
|
|
3308
|
+
}
|
|
3309
|
+
return selected;
|
|
3310
|
+
}
|
|
3232
3311
|
async ensureTarget() {
|
|
3233
3312
|
const targets = await this.listTabs("all");
|
|
3234
3313
|
const selected = targets.find((target) => target.active) ?? targets.find((target) => target.origin !== "user_tab");
|
|
@@ -3261,6 +3340,7 @@ var CompatibilityBrokerClient = class _CompatibilityBrokerClient {
|
|
|
3261
3340
|
});
|
|
3262
3341
|
return {
|
|
3263
3342
|
targetId: opened.targetId,
|
|
3343
|
+
profileContextId: opened.profileContextId,
|
|
3264
3344
|
title: String(opened.title ?? ""),
|
|
3265
3345
|
url: String(opened.url),
|
|
3266
3346
|
active: true,
|
|
@@ -3411,7 +3491,10 @@ var program = new Command();
|
|
|
3411
3491
|
program.name("bp").description("Control your browser from the command line").version(BROWSER_PILOT_VERSION).option("--human", "force human-readable output (default when TTY)").addHelpText("after", `
|
|
3412
3492
|
Workflow:
|
|
3413
3493
|
bp connect # one-time setup (click Allow in Chrome)
|
|
3494
|
+
bp profiles # list live Chrome Profile contexts
|
|
3495
|
+
bp profile <index> # route new managed tabs to one Profile
|
|
3414
3496
|
bp open <url> # navigate \u2014 returns snapshot with [ref] numbers
|
|
3497
|
+
bp open <url> --new --profile <id> # create managed work in one Profile
|
|
3415
3498
|
bp click <ref> # interact \u2014 returns updated snapshot
|
|
3416
3499
|
bp click --xy 400,300 # click at coordinates (canvas/maps)
|
|
3417
3500
|
bp locate ".selector" # get element coordinates for click --xy
|
|
@@ -3498,7 +3581,8 @@ function emitObservation(result) {
|
|
|
3498
3581
|
truncated,
|
|
3499
3582
|
truncationReasons,
|
|
3500
3583
|
...Array.isArray(result.hints) ? { hints: result.hints } : {},
|
|
3501
|
-
...result.evidence && typeof result.evidence === "object" ? { evidence: result.evidence } : {}
|
|
3584
|
+
...result.evidence && typeof result.evidence === "object" ? { evidence: result.evidence } : {},
|
|
3585
|
+
...typeof result.profileContextId === "string" ? { profileContextId: result.profileContextId } : {}
|
|
3502
3586
|
}));
|
|
3503
3587
|
} else {
|
|
3504
3588
|
const lines = [`[page] ${title} | ${url}`, ""];
|
|
@@ -3576,6 +3660,40 @@ async function requireCompatibility() {
|
|
|
3576
3660
|
if (!client) throw new Error("Not connected");
|
|
3577
3661
|
return client;
|
|
3578
3662
|
}
|
|
3663
|
+
async function resolveCliProfile(client, selector) {
|
|
3664
|
+
const profiles = await client.listProfiles();
|
|
3665
|
+
const exactId = profiles.find((profile) => profile.profileContextId === selector);
|
|
3666
|
+
if (exactId) return exactId;
|
|
3667
|
+
if (/^\d+$/.test(selector)) {
|
|
3668
|
+
const index = Number(selector);
|
|
3669
|
+
if (Number.isSafeInteger(index) && profiles[index]) return profiles[index];
|
|
3670
|
+
throw invalidCliProfile(`Profile index out of range (0-${Math.max(0, profiles.length - 1)})`, selector);
|
|
3671
|
+
}
|
|
3672
|
+
const normalized = selector.trim().toLocaleLowerCase();
|
|
3673
|
+
const matches = profiles.filter((profile) => profile.label.toLocaleLowerCase() === normalized || profile.displayName?.toLocaleLowerCase() === normalized);
|
|
3674
|
+
if (matches.length === 1) return matches[0];
|
|
3675
|
+
if (matches.length > 1) {
|
|
3676
|
+
throw invalidCliProfile("Profile selector is ambiguous; use its index or Profile context ID", selector);
|
|
3677
|
+
}
|
|
3678
|
+
throw invalidCliProfile("Profile selector does not match a live Chrome Profile", selector);
|
|
3679
|
+
}
|
|
3680
|
+
function invalidCliProfile(message, selector) {
|
|
3681
|
+
return new BrowserPilotError("invalid_argument", message, {
|
|
3682
|
+
context: { field: "profile", selector }
|
|
3683
|
+
});
|
|
3684
|
+
}
|
|
3685
|
+
function cliProfile(profile, index) {
|
|
3686
|
+
return {
|
|
3687
|
+
index,
|
|
3688
|
+
profileContextId: profile.profileContextId,
|
|
3689
|
+
label: profile.label,
|
|
3690
|
+
...profile.displayName ? { displayName: profile.displayName } : {},
|
|
3691
|
+
tabCount: profile.tabCount,
|
|
3692
|
+
eligibleTabCount: profile.eligibleTabCount,
|
|
3693
|
+
selected: profile.selected,
|
|
3694
|
+
representativeTabs: profile.representativeTabs
|
|
3695
|
+
};
|
|
3696
|
+
}
|
|
3579
3697
|
function withCliTarget(operation) {
|
|
3580
3698
|
return withCompatibilityTarget(BROWSER_PILOT_VERSION, operation);
|
|
3581
3699
|
}
|
|
@@ -3611,7 +3729,7 @@ program.command("browsers").description("List supported local browsers and their
|
|
|
3611
3729
|
return details.join("\n");
|
|
3612
3730
|
}).join("\n\n"));
|
|
3613
3731
|
}));
|
|
3614
|
-
program.command("connect").description("Connect to Chrome and
|
|
3732
|
+
program.command("connect").description("Connect to Chrome and prepare unambiguous managed browsing").option("-b, --browser <name>", "browser to connect to").addHelpText("after", "\nExamples:\n bp connect\n bp connect --browser brave").action(action(async (opts) => {
|
|
3615
3733
|
if (!useJson()) {
|
|
3616
3734
|
console.log("Connecting to Chrome...");
|
|
3617
3735
|
console.log(`If prompted, click "Allow" in Chrome's authorization dialog.
|
|
@@ -3619,10 +3737,20 @@ program.command("connect").description("Connect to Chrome and create pilot windo
|
|
|
3619
3737
|
}
|
|
3620
3738
|
const client = await connectCompatibility(BROWSER_PILOT_VERSION, opts.browser);
|
|
3621
3739
|
await client.connectBrowser();
|
|
3622
|
-
await client.
|
|
3740
|
+
const profiles = await client.listProfiles();
|
|
3741
|
+
if (profiles.length <= 1) await client.ensureManagedTarget();
|
|
3623
3742
|
const browser = client.initialized.browsers.find((candidate) => candidate.state === "ready")?.product ?? "browser";
|
|
3743
|
+
if (profiles.length > 1) {
|
|
3744
|
+
const listed = profiles.map(cliProfile);
|
|
3745
|
+
emit(
|
|
3746
|
+
{ ok: true, browser, profileSelectionRequired: true, profiles: listed },
|
|
3747
|
+
`\u2713 Connected to ${browser}
|
|
3748
|
+
Multiple Chrome Profiles are open. Run 'bp profiles', then 'bp profile <index>'.`
|
|
3749
|
+
);
|
|
3750
|
+
return;
|
|
3751
|
+
}
|
|
3624
3752
|
emit(
|
|
3625
|
-
{ ok: true, browser },
|
|
3753
|
+
{ ok: true, browser, profileSelectionRequired: false },
|
|
3626
3754
|
`\u2713 Connected to ${browser}
|
|
3627
3755
|
\u2713 Pilot window ready (daemon running in background)
|
|
3628
3756
|
|
|
@@ -3652,13 +3780,54 @@ program.command("disconnect").description("Release CLI browser state and stop an
|
|
|
3652
3780
|
await shutdownCompatibility(BROWSER_PILOT_VERSION);
|
|
3653
3781
|
emit({ ok: true }, "\u2713 Disconnected");
|
|
3654
3782
|
}));
|
|
3655
|
-
program.command("
|
|
3783
|
+
program.command("profiles").description("List live Chrome Profile contexts").action(action(async () => {
|
|
3784
|
+
const profiles = (await (await requireCompatibility()).listProfiles()).map(cliProfile);
|
|
3785
|
+
if (useJson()) {
|
|
3786
|
+
console.log(JSON.stringify({ ok: true, profiles }));
|
|
3787
|
+
return;
|
|
3788
|
+
}
|
|
3789
|
+
if (profiles.length === 0) {
|
|
3790
|
+
console.log("No live Chrome Profile contexts found.");
|
|
3791
|
+
return;
|
|
3792
|
+
}
|
|
3793
|
+
for (const profile of profiles) {
|
|
3794
|
+
const name = profile.displayName ? ` (${profile.displayName})` : "";
|
|
3795
|
+
console.log(`${profile.selected ? "*" : " "} ${profile.index} ${profile.label}${name} ${profile.tabCount} tab(s)`);
|
|
3796
|
+
}
|
|
3797
|
+
}));
|
|
3798
|
+
program.command("profile <selector>").description("Select a Chrome Profile context for new managed tabs").action(action(async (selector) => {
|
|
3799
|
+
const client = await requireCompatibility();
|
|
3800
|
+
const profile = await resolveCliProfile(client, String(selector));
|
|
3801
|
+
const selected = await client.selectProfile(profile.profileContextId);
|
|
3802
|
+
emit(
|
|
3803
|
+
{
|
|
3804
|
+
ok: true,
|
|
3805
|
+
profileContextId: selected.profileContextId,
|
|
3806
|
+
label: selected.label,
|
|
3807
|
+
...selected.displayName ? { displayName: selected.displayName } : {}
|
|
3808
|
+
},
|
|
3809
|
+
`\u2713 Selected ${selected.displayName ?? selected.label}`
|
|
3810
|
+
);
|
|
3811
|
+
}));
|
|
3812
|
+
program.command("open <url>").description("Navigate to URL and return page snapshot").option("-n, --new", "open in new tab").option("--profile <selector>", "Profile index, ID, label, or verified display name (requires --new)").option("-l, --limit <n>", "max elements in snapshot", "50").addHelpText("after", "\nExamples:\n bp open https://github.com\n bp open github.com --new\n bp open https://example.com --new --profile 1\n bp open https://example.com --limit 20").action(action(async (url, opts) => {
|
|
3656
3813
|
url = normalizeUrl(url);
|
|
3657
3814
|
const limit = parseLimit(opts.limit);
|
|
3815
|
+
if (opts.profile && !opts.new) throw new Error("--profile requires --new");
|
|
3816
|
+
if (opts.new) {
|
|
3817
|
+
const client = await requireCompatibility();
|
|
3818
|
+
const profile = opts.profile ? await resolveCliProfile(client, String(opts.profile)) : void 0;
|
|
3819
|
+
emitObservation(await client.callTool("browser.open", {
|
|
3820
|
+
url,
|
|
3821
|
+
newTarget: true,
|
|
3822
|
+
...profile ? { profileContextId: profile.profileContextId } : {},
|
|
3823
|
+
observationLimit: limit
|
|
3824
|
+
}));
|
|
3825
|
+
return;
|
|
3826
|
+
}
|
|
3658
3827
|
await withCliTarget(async (client, target) => {
|
|
3659
3828
|
const result = await client.callTool("browser.open", {
|
|
3660
3829
|
url,
|
|
3661
|
-
|
|
3830
|
+
targetId: target.targetId,
|
|
3662
3831
|
observationLimit: limit
|
|
3663
3832
|
});
|
|
3664
3833
|
emitObservation(result);
|