@browserbasehq/browse-cli 0.4.2 → 0.5.0-alpha-666baf1

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.
Files changed (3) hide show
  1. package/README.md +12 -11
  2. package/dist/index.js +212 -47
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -173,22 +173,23 @@ browse env
173
173
  # Switch current session to Browserbase (restarts daemon if needed)
174
174
  browse env remote
175
175
 
176
- # Switch back to local Chrome (auto-discovers existing Chrome, falls back to isolated)
176
+ # Switch back to local Chrome (clean isolated browser by default)
177
177
  browse env local
178
178
  ```
179
179
 
180
180
  #### Local Browser Strategies
181
181
 
182
- By default, `browse env local` auto-discovers an already-running Chrome with remote
183
- debugging enabled. This lets agents use your existing cookies, logins, and browser state.
184
- If no debuggable Chrome is found, it falls back to launching an isolated browser.
182
+ By default, `browse env local` launches a clean isolated local browser.
183
+ Use `browse env local --auto-connect` to opt into reusing an already-running
184
+ Chrome with remote debugging enabled. If no debuggable Chrome is found, it
185
+ falls back to launching an isolated browser.
185
186
 
186
187
  ```bash
187
- # Auto-discover local Chrome, fallback to isolated (default)
188
+ # Use a clean isolated browser (default)
188
189
  browse env local
189
190
 
190
- # Force a clean isolated browser (no auto-discovery)
191
- browse env local --isolated
191
+ # Auto-discover local Chrome, fallback to isolated
192
+ browse env local --auto-connect
192
193
 
193
194
  # Attach to a specific CDP target (port or URL)
194
195
  browse env local 9222
@@ -210,7 +211,7 @@ Use `browse status` to see which strategy was resolved:
210
211
 
211
212
  ```bash
212
213
  browse status
213
- # {"running":true,"session":"default","mode":"local","localStrategy":"auto","localSource":"attached-existing","resolvedCdpUrl":"ws://..."}
214
+ # {"running":true,"session":"default","mode":"local","localStrategy":"isolated","localSource":"isolated"}
214
215
  ```
215
216
 
216
217
  #### General Behavior
@@ -283,19 +284,19 @@ browse --session personal open https://twitter.com
283
284
 
284
285
  ## Direct CDP Connection
285
286
 
286
- Connect to an existing Chrome instance:
287
+ Opt into using an existing Chrome instance:
287
288
 
288
289
  To make your Chrome discoverable:
289
290
 
290
291
  1. Open `chrome://inspect/#remote-debugging`
291
292
  2. Check the box **"Allow remote debugging for this browser instance"**
292
- 3. Re-run the CLI and it will auto-connect!
293
+ 3. Re-run the CLI with auto-connect enabled.
293
294
 
294
295
  For more information, see the [Chrome DevTools docs](https://developer.chrome.com/blog/chrome-devtools-mcp-debug-your-browser-session).
295
296
 
296
297
  ```bash
297
298
  # Auto-discover Chrome with remote debugging enabled
298
- browse env local
299
+ browse env local --auto-connect
299
300
  browse open https://example.com
300
301
 
301
302
  # Or target a specific port / WebSocket URL
package/dist/index.js CHANGED
@@ -164652,7 +164652,72 @@ var import_child_process4 = require("child_process");
164652
164652
  var readline = __toESM(require("readline"));
164653
164653
 
164654
164654
  // package.json
164655
- var version3 = "0.4.2";
164655
+ var version3 = "0.5.0";
164656
+
164657
+ // src/local-strategy.ts
164658
+ init_cjs_shims();
164659
+ var DEFAULT_LOCAL_CONFIG = { strategy: "isolated" };
164660
+ var ISOLATED_MODE_HINT = "Hint: Run `browse env local --auto-connect` to reuse your local browsing credentials and cookies.";
164661
+ var ATTACHED_EXISTING_HINT = "Hint: Run `browse env local` without `--auto-connect` to switch back to an isolated Chromium browser.";
164662
+ function getLocalModeHint(localConfig, localInfo) {
164663
+ if (localInfo?.localSource === "attached-existing") {
164664
+ return ATTACHED_EXISTING_HINT;
164665
+ }
164666
+ if (localInfo?.localSource === "isolated-fallback") {
164667
+ return null;
164668
+ }
164669
+ if (localConfig.strategy === "auto" && !localInfo) {
164670
+ return ATTACHED_EXISTING_HINT;
164671
+ }
164672
+ if (localInfo?.localSource === "isolated" || localConfig.strategy === "isolated" && !localInfo) {
164673
+ return ISOLATED_MODE_HINT;
164674
+ }
164675
+ return null;
164676
+ }
164677
+ async function resolveLocalStrategy({
164678
+ localConfig,
164679
+ headless,
164680
+ defaultViewport,
164681
+ discoverLocalCdp: discoverLocalCdp2,
164682
+ resolveWsTarget: resolveWsTarget2
164683
+ }) {
164684
+ if (localConfig.strategy === "isolated") {
164685
+ return {
164686
+ localLaunchOptions: { headless, viewport: defaultViewport },
164687
+ localInfo: { localSource: "isolated" }
164688
+ };
164689
+ }
164690
+ if (localConfig.strategy === "cdp") {
164691
+ if (!localConfig.cdpTarget) {
164692
+ throw new Error("Local CDP strategy requires a cdpTarget");
164693
+ }
164694
+ const cdpUrl = await resolveWsTarget2(localConfig.cdpTarget);
164695
+ return {
164696
+ localLaunchOptions: { cdpUrl },
164697
+ localInfo: {
164698
+ localSource: "attached-explicit",
164699
+ resolvedCdpUrl: cdpUrl
164700
+ }
164701
+ };
164702
+ }
164703
+ const discovered = await discoverLocalCdp2();
164704
+ if (discovered) {
164705
+ return {
164706
+ localLaunchOptions: { cdpUrl: discovered.wsUrl },
164707
+ localInfo: {
164708
+ localSource: "attached-existing",
164709
+ resolvedCdpUrl: discovered.wsUrl
164710
+ }
164711
+ };
164712
+ }
164713
+ return {
164714
+ localLaunchOptions: { headless, viewport: defaultViewport },
164715
+ localInfo: {
164716
+ localSource: "isolated-fallback",
164717
+ fallbackReason: "no debuggable local browser found"
164718
+ }
164719
+ };
164720
+ }
164656
164721
 
164657
164722
  // src/resolve-ws.ts
164658
164723
  init_cjs_shims();
@@ -164778,12 +164843,15 @@ function getLocalConfigPath(session) {
164778
164843
  function getLocalInfoPath(session) {
164779
164844
  return path11.join(SOCKET_DIR, `browse-${session}.local-info`);
164780
164845
  }
164846
+ function getSessionParamsPath(session) {
164847
+ return path11.join(SOCKET_DIR, `browse-${session}.session-params`);
164848
+ }
164781
164849
  async function readLocalConfig(session) {
164782
164850
  try {
164783
164851
  const raw = await import_fs11.promises.readFile(getLocalConfigPath(session), "utf-8");
164784
164852
  return JSON.parse(raw);
164785
164853
  } catch {
164786
- return { strategy: "auto" };
164854
+ return { ...DEFAULT_LOCAL_CONFIG };
164787
164855
  }
164788
164856
  }
164789
164857
  async function writeLocalConfig(session, config3) {
@@ -164800,6 +164868,23 @@ async function readLocalInfo(session) {
164800
164868
  return null;
164801
164869
  }
164802
164870
  }
164871
+ async function waitForLocalInfo(session, timeoutMs = 1500) {
164872
+ const startTime = Date.now();
164873
+ while (Date.now() - startTime < timeoutMs) {
164874
+ const localInfo = await readLocalInfo(session);
164875
+ if (localInfo) {
164876
+ return localInfo;
164877
+ }
164878
+ await new Promise((resolve4) => setTimeout(resolve4, 50));
164879
+ }
164880
+ return readLocalInfo(session);
164881
+ }
164882
+ function logLocalModeHint(localConfig, localInfo) {
164883
+ const hint = getLocalModeHint(localConfig, localInfo);
164884
+ if (hint) {
164885
+ console.error(hint);
164886
+ }
164887
+ }
164803
164888
  function hasBrowserbaseCredentials() {
164804
164889
  return Boolean(process.env.BROWSERBASE_API_KEY);
164805
164890
  }
@@ -165017,7 +165102,8 @@ async function cleanupStaleFiles(session) {
165017
165102
  // Client-written config, only cleaned on full shutdown
165018
165103
  getContextPath(session),
165019
165104
  getConnectPath(session),
165020
- getLocalConfigPath(session)
165105
+ getLocalConfigPath(session),
165106
+ getSessionParamsPath(session)
165021
165107
  ];
165022
165108
  for (const file2 of files) {
165023
165109
  try {
@@ -165094,36 +165180,24 @@ async function runDaemon(session, headless) {
165094
165180
  connectSessionId = (await import_fs11.promises.readFile(getConnectPath(session), "utf-8")).trim();
165095
165181
  } catch {
165096
165182
  }
165183
+ let sessionParams = {};
165184
+ try {
165185
+ const raw = await import_fs11.promises.readFile(getSessionParamsPath(session), "utf-8");
165186
+ sessionParams = JSON.parse(raw);
165187
+ } catch {
165188
+ }
165097
165189
  let localLaunchOptions;
165098
165190
  let localInfo;
165099
165191
  if (!useBrowserbase) {
165100
- const localConfig = await readLocalConfig(session);
165101
- if (localConfig.strategy === "isolated") {
165102
- localLaunchOptions = { headless, viewport: DEFAULT_VIEWPORT2 };
165103
- localInfo = { localSource: "isolated" };
165104
- } else if (localConfig.strategy === "cdp") {
165105
- const cdpUrl = await resolveWsTarget(localConfig.cdpTarget);
165106
- localLaunchOptions = { cdpUrl };
165107
- localInfo = {
165108
- localSource: "attached-explicit",
165109
- resolvedCdpUrl: cdpUrl
165110
- };
165111
- } else {
165112
- const discovered = await discoverLocalCdp();
165113
- if (discovered) {
165114
- localLaunchOptions = { cdpUrl: discovered.wsUrl };
165115
- localInfo = {
165116
- localSource: "attached-existing",
165117
- resolvedCdpUrl: discovered.wsUrl
165118
- };
165119
- } else {
165120
- localLaunchOptions = { headless, viewport: DEFAULT_VIEWPORT2 };
165121
- localInfo = {
165122
- localSource: "isolated-fallback",
165123
- fallbackReason: "no debuggable local browser found"
165124
- };
165125
- }
165126
- }
165192
+ const resolvedLocalStrategy = await resolveLocalStrategy({
165193
+ localConfig: await readLocalConfig(session),
165194
+ headless,
165195
+ defaultViewport: DEFAULT_VIEWPORT2,
165196
+ discoverLocalCdp,
165197
+ resolveWsTarget
165198
+ });
165199
+ localLaunchOptions = resolvedLocalStrategy.localLaunchOptions;
165200
+ localInfo = resolvedLocalStrategy.localInfo;
165127
165201
  }
165128
165202
  stagehand = new V3({
165129
165203
  env: useBrowserbase ? "BROWSERBASE" : "LOCAL",
@@ -165136,14 +165210,19 @@ async function runDaemon(session, headless) {
165136
165210
  keepAlive: true
165137
165211
  } : {},
165138
165212
  ...!connectSessionId ? {
165139
- browserbaseSessionCreateParams: {
165140
- userMetadata: { browse_cli: "true" },
165141
- ...contextConfig ? {
165213
+ browserbaseSessionCreateParams: (() => {
165214
+ const sessionBrowserSettings = sessionParams.browserSettings || {};
165215
+ const { browserSettings: _2, ...sessionParamsWithoutBS } = sessionParams;
165216
+ void _2;
165217
+ return {
165218
+ userMetadata: { browse_cli: "true" },
165219
+ ...sessionParamsWithoutBS,
165142
165220
  browserSettings: {
165143
- context: contextConfig
165221
+ ...sessionBrowserSettings,
165222
+ ...contextConfig ? { context: contextConfig } : {}
165144
165223
  }
165145
- } : {}
165146
- }
165224
+ };
165225
+ })()
165147
165226
  } : {}
165148
165227
  } : {
165149
165228
  localBrowserLaunchOptions: localLaunchOptions
@@ -165955,6 +166034,24 @@ function getSession(opts) {
165955
166034
  function isHeadless(opts) {
165956
166035
  return opts.headless === true && opts.headed !== true;
165957
166036
  }
166037
+ function buildSessionParamsFromOpts(opts) {
166038
+ const params = {};
166039
+ const browserSettings = {};
166040
+ if (opts.proxies) params.proxies = true;
166041
+ if (opts.region) params.region = opts.region;
166042
+ if (opts.keepAlive) params.keepAlive = true;
166043
+ if (opts.sessionTimeout !== void 0) params.timeout = opts.sessionTimeout;
166044
+ if (opts.advancedStealth) browserSettings.advancedStealth = true;
166045
+ if (opts.blockAds) browserSettings.blockAds = true;
166046
+ if (opts.solveCaptchas !== void 0) {
166047
+ browserSettings.solveCaptchas = opts.solveCaptchas;
166048
+ }
166049
+ if (Object.keys(browserSettings).length > 0) {
166050
+ params.browserSettings = browserSettings;
166051
+ }
166052
+ if (Object.keys(params).length === 0) return null;
166053
+ return params;
166054
+ }
165958
166055
  function output(data, json2) {
165959
166056
  if (json2) {
165960
166057
  console.log(JSON.stringify(data, null, 2));
@@ -166009,6 +166106,29 @@ async function runCommand(command, args) {
166009
166106
  } catch {
166010
166107
  }
166011
166108
  }
166109
+ const sessionParams = buildSessionParamsFromOpts(opts);
166110
+ if (sessionParams) {
166111
+ const desiredMode = await getDesiredMode(session);
166112
+ if (desiredMode !== "browserbase") {
166113
+ console.error(
166114
+ JSON.stringify({
166115
+ error: "Session flags (--proxies, --advanced-stealth, etc.) are only supported in remote mode. Run 'browse env remote' first."
166116
+ })
166117
+ );
166118
+ process.exit(1);
166119
+ }
166120
+ const paramsPath = getSessionParamsPath(session);
166121
+ const newParamsJson = JSON.stringify(sessionParams);
166122
+ let currentParamsJson = "";
166123
+ try {
166124
+ currentParamsJson = await import_fs11.promises.readFile(paramsPath, "utf-8");
166125
+ } catch {
166126
+ }
166127
+ await import_fs11.promises.writeFile(paramsPath, newParamsJson);
166128
+ if (currentParamsJson !== newParamsJson && await isDaemonRunning(session)) {
166129
+ await stopDaemonAndCleanup(session);
166130
+ }
166131
+ }
166012
166132
  await ensureDaemon(session, headless);
166013
166133
  return sendCommand(session, command, args, headless);
166014
166134
  }
@@ -166021,6 +166141,19 @@ program.name("browse").description("Browser automation CLI for AI agents").versi
166021
166141
  ).option(
166022
166142
  "--connect <session-id>",
166023
166143
  "Connect to an existing Browserbase session by ID"
166144
+ ).option("--proxies", "Enable Browserbase proxy (remote only)").option("--advanced-stealth", "Enable advanced stealth mode (remote only)").option("--solve-captchas", "Enable automatic CAPTCHA solving (remote only)").option(
166145
+ "--no-solve-captchas",
166146
+ "Disable automatic CAPTCHA solving (remote only)"
166147
+ ).option("--block-ads", "Enable ad blocking (remote only)").option(
166148
+ "--region <region>",
166149
+ "Session region: us-west-2, us-east-1, eu-central-1, ap-southeast-1 (remote only)"
166150
+ ).option(
166151
+ "--keep-alive",
166152
+ "Keep session alive after disconnection (remote only)"
166153
+ ).option(
166154
+ "--session-timeout <seconds>",
166155
+ "Session timeout in seconds (remote only)",
166156
+ parseInt
166024
166157
  );
166025
166158
  program.command("start").description("Start browser daemon (auto-started by other commands)").action(async () => {
166026
166159
  const opts = program.opts();
@@ -166072,13 +166205,20 @@ program.command("status").description("Check daemon status").action(async () =>
166072
166205
  }
166073
166206
  if (mode === "local") {
166074
166207
  const localConfig = await readLocalConfig(session);
166075
- const localInfo = await readLocalInfo(session);
166208
+ const localInfo = await readLocalInfo(session) ?? await waitForLocalInfo(session);
166209
+ logLocalModeHint(localConfig, localInfo);
166076
166210
  localDetails = {
166077
166211
  localStrategy: localConfig.strategy,
166078
166212
  ...localInfo ?? {}
166079
166213
  };
166080
166214
  }
166081
166215
  }
166216
+ let sessionParams = null;
166217
+ try {
166218
+ const raw = await import_fs11.promises.readFile(getSessionParamsPath(session), "utf-8");
166219
+ sessionParams = JSON.parse(raw);
166220
+ } catch {
166221
+ }
166082
166222
  console.log(
166083
166223
  JSON.stringify({
166084
166224
  running,
@@ -166086,13 +166226,25 @@ program.command("status").description("Check daemon status").action(async () =>
166086
166226
  wsUrl,
166087
166227
  mode,
166088
166228
  browserbaseSessionId,
166089
- ...localDetails
166229
+ ...localDetails,
166230
+ ...sessionParams ? { sessionParams } : {}
166090
166231
  })
166091
166232
  );
166092
166233
  });
166093
- program.command("env [target] [cdpTarget]").description(
166094
- "Show or switch browser environment (local | remote)\n\n browse env Show current environment\n browse env local Auto-discover local Chrome, fallback to isolated\n browse env local --isolated Force clean isolated browser\n browse env local <port|url> Attach to specific CDP target\n browse env remote Use Browserbase (requires API key)"
166095
- ).option("--isolated", "Force isolated local browser (no auto-discovery)").action(
166234
+ var envUsage = "Usage: browse env [local|remote]\n browse env local [--auto-connect|<port|url>]";
166235
+ var envCommand = program.command("env [target] [cdpTarget]").description(
166236
+ "Show or switch browser environment (local | remote)\n\n browse env Show current environment\n browse env local Use clean isolated local browser (default)\n browse env local --auto-connect Auto-discover local Chrome, fallback to isolated\n browse env local <port|url> Attach to specific CDP target\n browse env remote Use Browserbase (requires API key)"
166237
+ ).option(
166238
+ "--auto-connect",
166239
+ "Auto-discover an existing local Chrome instance via CDP"
166240
+ );
166241
+ envCommand.addOption(
166242
+ new import_commander.Option(
166243
+ "--isolated",
166244
+ "Deprecated alias for the default isolated local browser"
166245
+ ).hideHelp()
166246
+ );
166247
+ envCommand.action(
166096
166248
  async (target, cdpTarget, cmdOpts) => {
166097
166249
  const opts = program.opts();
166098
166250
  const session = getSession(opts);
@@ -166123,9 +166275,7 @@ program.command("env [target] [cdpTarget]").description(
166123
166275
  };
166124
166276
  const mapped = modeMap[target];
166125
166277
  if (!mapped) {
166126
- console.error(
166127
- "Usage: browse env [local|remote]\n browse env local [--isolated] [<port|url>]"
166128
- );
166278
+ console.error(envUsage);
166129
166279
  process.exit(1);
166130
166280
  }
166131
166281
  try {
@@ -166134,10 +166284,22 @@ program.command("env [target] [cdpTarget]").description(
166134
166284
  console.error(err instanceof Error ? err.message : String(err));
166135
166285
  process.exit(1);
166136
166286
  }
166137
- let localConfig = { strategy: "auto" };
166287
+ let localConfig = { ...DEFAULT_LOCAL_CONFIG };
166138
166288
  if (mapped === "local") {
166139
- if (cmdOpts.isolated) {
166140
- localConfig = { strategy: "isolated" };
166289
+ const selectedLocalStrategies = [
166290
+ Boolean(cmdOpts.autoConnect),
166291
+ Boolean(cmdOpts.isolated),
166292
+ Boolean(cdpTarget)
166293
+ ].filter(Boolean);
166294
+ if (selectedLocalStrategies.length > 1) {
166295
+ console.error(envUsage);
166296
+ console.error(
166297
+ "Use only one of --auto-connect, --isolated, or <port|url>."
166298
+ );
166299
+ process.exit(1);
166300
+ }
166301
+ if (cmdOpts.autoConnect) {
166302
+ localConfig = { strategy: "auto" };
166141
166303
  } else if (cdpTarget) {
166142
166304
  localConfig = { strategy: "cdp", cdpTarget };
166143
166305
  }
@@ -166160,6 +166322,9 @@ program.command("env [target] [cdpTarget]").description(
166160
166322
  await stopDaemonAndCleanup(session);
166161
166323
  }
166162
166324
  await ensureDaemon(session, isHeadless(opts));
166325
+ if (mapped === "local") {
166326
+ logLocalModeHint(localConfig, await waitForLocalInfo(session));
166327
+ }
166163
166328
  console.log(
166164
166329
  JSON.stringify({
166165
166330
  mode: toModeTarget(mapped),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserbasehq/browse-cli",
3
- "version": "0.4.2",
3
+ "version": "0.5.0-alpha-666baf1",
4
4
  "description": "Browser automation CLI for AI agents, built on Stagehand",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",