@browserbasehq/cli 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,6 +19,19 @@ async function installBrowseCli() {
19
19
  process.exitCode = exitCode;
20
20
  }
21
21
  }
22
+ function extractYesFlag(args) {
23
+ const remaining = [];
24
+ let yes = false;
25
+ for (const arg of args) {
26
+ if (arg === "--yes" || arg === "-y") {
27
+ yes = true;
28
+ }
29
+ else {
30
+ remaining.push(arg);
31
+ }
32
+ }
33
+ return { yes, remaining };
34
+ }
22
35
  export function attachBrowseCommand(program) {
23
36
  program
24
37
  .command("browse")
@@ -28,7 +41,15 @@ export function attachBrowseCommand(program) {
28
41
  .allowExcessArguments(true)
29
42
  .passThroughOptions()
30
43
  .helpOption(false)
44
+ .addHelpText("after", [
45
+ "",
46
+ "Examples:",
47
+ " bb browse --url https://example.com",
48
+ ' bb browse --url https://example.com --text "Click Login"',
49
+ " bb browse --yes (auto-install if not present)",
50
+ ].join("\n"))
31
51
  .action(async (args) => {
52
+ const { yes, remaining: forwardArgs } = extractYesFlag(args ?? []);
32
53
  let browsePath = await findExecutable("browse");
33
54
  if (!browsePath) {
34
55
  console.log([
@@ -39,18 +60,23 @@ export function attachBrowseCommand(program) {
39
60
  " npm install -g @browserbasehq/browse-cli",
40
61
  "",
41
62
  ].join("\n"));
42
- const rl = readline.createInterface({
43
- input: process.stdin,
44
- output: process.stdout,
45
- });
46
- try {
47
- const answer = await rl.question("Install now? [Y/n] ");
48
- if (answer.trim().toLowerCase() === "n") {
49
- return;
63
+ if (!yes) {
64
+ if (!process.stdin.isTTY) {
65
+ fail("Cannot prompt for install in non-interactive mode. Use --yes to auto-install.");
66
+ }
67
+ const rl = readline.createInterface({
68
+ input: process.stdin,
69
+ output: process.stdout,
70
+ });
71
+ try {
72
+ const answer = await rl.question("Install now? [Y/n] ");
73
+ if (answer.trim().toLowerCase() === "n") {
74
+ return;
75
+ }
76
+ }
77
+ finally {
78
+ rl.close();
50
79
  }
51
- }
52
- finally {
53
- rl.close();
54
80
  }
55
81
  await installBrowseCli();
56
82
  browsePath = await findExecutable("browse");
@@ -61,8 +87,8 @@ export function attachBrowseCommand(program) {
61
87
  ].join("\n"));
62
88
  }
63
89
  }
64
- const forwardArgs = args && args.length > 0 ? args : ["--help"];
65
- const exitCode = await spawnPassthrough(browsePath, forwardArgs);
90
+ const finalArgs = forwardArgs.length > 0 ? forwardArgs : ["--help"];
91
+ const exitCode = await spawnPassthrough(browsePath, finalArgs);
66
92
  if (exitCode !== 0) {
67
93
  process.exitCode = exitCode;
68
94
  }
@@ -1,4 +1,4 @@
1
- import { addCommonApiOptions, createBrowserbaseClient, outputJson, parseOptionalJsonObjectArg, requestBrowserbase, requestBrowserbaseJson, } from "../lib/command.js";
1
+ import { addCommonApiOptions, addExamples, createBrowserbaseClient, outputJson, resolveBody, requestBrowserbase, requestBrowserbaseJson, } from "../lib/command.js";
2
2
  export function attachContextsCommand(program) {
3
3
  const contexts = program
4
4
  .command("contexts")
@@ -7,30 +7,35 @@ export function attachContextsCommand(program) {
7
7
  contexts.action(() => {
8
8
  contexts.outputHelp();
9
9
  });
10
- addCommonApiOptions(contexts
10
+ addExamples(addCommonApiOptions(contexts
11
11
  .command("create")
12
12
  .description("Create a context.")
13
- .option("--body <body>", "Optional JSON request body.")).action(async (options) => {
13
+ .option("--body <body>", "Optional JSON request body.")
14
+ .option("--stdin", "Read JSON request body from stdin.")), [
15
+ "bb contexts create",
16
+ `bb contexts create --body '{"projectId":"proj_abc"}'`,
17
+ `echo '{"projectId":"proj_abc"}' | bb contexts create --stdin`,
18
+ ]).action(async (options) => {
14
19
  const client = createBrowserbaseClient(options);
15
- const body = parseOptionalJsonObjectArg(options.body, "body");
20
+ const body = await resolveBody(options);
16
21
  outputJson(await client.contexts.create(body));
17
22
  });
18
- addCommonApiOptions(contexts
23
+ addExamples(addCommonApiOptions(contexts
19
24
  .command("get <id>")
20
- .description("Get a context by ID.")).action(async (id, options) => {
25
+ .description("Get a context by ID.")), ["bb contexts get <context-id>"]).action(async (id, options) => {
21
26
  const client = createBrowserbaseClient(options);
22
27
  outputJson(await client.contexts.retrieve(id));
23
28
  });
24
- addCommonApiOptions(contexts
29
+ addExamples(addCommonApiOptions(contexts
25
30
  .command("update <id>")
26
- .description("Refresh the upload URL for a context.")).action(async (id, options) => {
31
+ .description("Refresh the upload URL for a context.")), ["bb contexts update <context-id>"]).action(async (id, options) => {
27
32
  outputJson(await requestBrowserbaseJson(options, `/v1/contexts/${id}`, {
28
33
  method: "PUT",
29
34
  }));
30
35
  });
31
- addCommonApiOptions(contexts
36
+ addExamples(addCommonApiOptions(contexts
32
37
  .command("delete <id>")
33
- .description("Delete a context.")).action(async (id, options) => {
38
+ .description("Delete a context.")), ["bb contexts delete <context-id>"]).action(async (id, options) => {
34
39
  await requestBrowserbase(options, `/v1/contexts/${id}`, {
35
40
  method: "DELETE",
36
41
  headers: {
@@ -1,4 +1,4 @@
1
- import { addCommonApiOptions, createBrowserbaseClient, outputJson, requestBrowserbase, resolveUploadableFile, } from "../lib/command.js";
1
+ import { addCommonApiOptions, addExamples, createBrowserbaseClient, outputJson, requestBrowserbase, resolveUploadableFile, } from "../lib/command.js";
2
2
  export function attachExtensionsCommand(program) {
3
3
  const extensions = program
4
4
  .command("extensions")
@@ -7,23 +7,23 @@ export function attachExtensionsCommand(program) {
7
7
  extensions.action(() => {
8
8
  extensions.outputHelp();
9
9
  });
10
- addCommonApiOptions(extensions
10
+ addExamples(addCommonApiOptions(extensions
11
11
  .command("upload <file>")
12
- .description("Upload an extension ZIP file.")).action(async (file, options) => {
12
+ .description("Upload an extension ZIP file.")), ["bb extensions upload ./extension.zip"]).action(async (file, options) => {
13
13
  const client = createBrowserbaseClient(options);
14
14
  outputJson(await client.extensions.create({
15
15
  file: await resolveUploadableFile(file, "extension"),
16
16
  }));
17
17
  });
18
- addCommonApiOptions(extensions
18
+ addExamples(addCommonApiOptions(extensions
19
19
  .command("get <id>")
20
- .description("Get an extension by ID.")).action(async (id, options) => {
20
+ .description("Get an extension by ID.")), ["bb extensions get <extension-id>"]).action(async (id, options) => {
21
21
  const client = createBrowserbaseClient(options);
22
22
  outputJson(await client.extensions.retrieve(id));
23
23
  });
24
- addCommonApiOptions(extensions
24
+ addExamples(addCommonApiOptions(extensions
25
25
  .command("delete <id>")
26
- .description("Delete an extension.")).action(async (id, options) => {
26
+ .description("Delete an extension.")), ["bb extensions delete <extension-id>"]).action(async (id, options) => {
27
27
  await requestBrowserbase(options, `/v1/extensions/${id}`, {
28
28
  method: "DELETE",
29
29
  headers: {
@@ -1,12 +1,16 @@
1
- import { addCommonApiOptions, createBrowserbaseClient, outputJson, writeOutputFile, } from "../lib/command.js";
1
+ import { addCommonApiOptions, addExamples, createBrowserbaseClient, outputJson, writeOutputFile, } from "../lib/command.js";
2
2
  export function attachFetchCommand(program) {
3
- addCommonApiOptions(program
3
+ addExamples(addCommonApiOptions(program
4
4
  .command("fetch <url>")
5
5
  .description("Retrieve webpage content without a full browser session using the lightweight Browserbase Fetch API.")
6
6
  .option("--allow-insecure-ssl", "Bypass TLS certificate verification.")
7
7
  .option("--allow-redirects", "Follow HTTP redirects.")
8
8
  .option("--proxies", "Enable Browserbase proxy support.")
9
- .option("--output <output>", "Write the fetched content to a file.")).action(async (url, options) => {
9
+ .option("--output <output>", "Write the fetched content to a file.")), [
10
+ "bb fetch https://www.google.com",
11
+ "bb fetch https://example.com --allow-insecure-ssl --output page.html",
12
+ "bb fetch https://example.com --allow-redirects --proxies",
13
+ ]).action(async (url, options) => {
10
14
  const client = createBrowserbaseClient(options);
11
15
  const result = await client.fetchAPI.create({
12
16
  url,
@@ -1,5 +1,5 @@
1
1
  import { Option } from "commander";
2
- import { addFunctionsApiOptions } from "../lib/command.js";
2
+ import { addExamples, addFunctionsApiOptions } from "../lib/command.js";
3
3
  import { initFunctionsProject } from "../lib/functions/init.js";
4
4
  import { invokeFunction } from "../lib/functions/invoke.js";
5
5
  import { startFunctionsDevServer } from "../lib/functions/dev.js";
@@ -13,23 +13,28 @@ export function attachFunctionsCommand(program) {
13
13
  functions.action(() => {
14
14
  functions.outputHelp();
15
15
  });
16
- functions
16
+ addExamples(functions
17
17
  .command("init [projectName]")
18
18
  .description("Initialize a new Browserbase Functions project.")
19
19
  .addOption(new Option("--package-manager <packageManager>", "Package manager to use.")
20
20
  .choices([...packageManagers])
21
- .default("pnpm"))
22
- .action(async (projectName, options) => {
21
+ .default("pnpm")), [
22
+ "bb functions init my-function",
23
+ "bb functions init my-function --package-manager npm",
24
+ ]).action(async (projectName, options) => {
23
25
  await initFunctionsProject({
24
26
  projectName: projectName ?? "my-browserbase-function",
25
27
  packageManager: options.packageManager ?? "pnpm",
26
28
  });
27
29
  });
28
- addFunctionsApiOptions(functions
30
+ addExamples(addFunctionsApiOptions(functions
29
31
  .command("dev <entrypoint>")
30
32
  .description("Run the local Browserbase Functions development server.")
31
33
  .option("--port <port>", "Port to listen on.", "14113")
32
- .option("--host <host>", "Host to bind to.", "127.0.0.1"), { includeProjectId: true, includeVerbose: true }).action(async (entrypoint, options) => {
34
+ .option("--host <host>", "Host to bind to.", "127.0.0.1"), { includeProjectId: true, includeVerbose: true }), [
35
+ "bb functions dev index.ts",
36
+ "bb functions dev index.ts --port 3000 --verbose",
37
+ ]).action(async (entrypoint, options) => {
33
38
  await startFunctionsDevServer({
34
39
  entrypoint,
35
40
  port: Number(options.port ?? "14113"),
@@ -40,10 +45,13 @@ export function attachFunctionsCommand(program) {
40
45
  verbose: options.verbose ?? false,
41
46
  });
42
47
  });
43
- addFunctionsApiOptions(functions
48
+ addExamples(addFunctionsApiOptions(functions
44
49
  .command("publish <entrypoint>")
45
50
  .description("Package and upload a Browserbase Function build.")
46
- .option("--dry-run", "Show what would be published without uploading."), { includeProjectId: true }).action(async (entrypoint, options) => {
51
+ .option("--dry-run", "Show what would be published without uploading."), { includeProjectId: true }), [
52
+ "bb functions publish index.ts --dry-run",
53
+ "bb functions publish index.ts",
54
+ ]).action(async (entrypoint, options) => {
47
55
  await publishFunction({
48
56
  entrypoint,
49
57
  apiKey: options.apiKey,
@@ -52,12 +60,16 @@ export function attachFunctionsCommand(program) {
52
60
  dryRun: options.dryRun ?? false,
53
61
  });
54
62
  });
55
- addFunctionsApiOptions(functions
63
+ addExamples(addFunctionsApiOptions(functions
56
64
  .command("invoke [functionId]")
57
65
  .description("Invoke a deployed Browserbase Function or check invocation status.")
58
66
  .option("--params <params>", "JSON params to pass to the function.")
59
67
  .option("--no-wait", "Return immediately after creating the invocation.")
60
- .option("--check-status <checkStatus>", "Invocation ID to inspect without creating a new invocation.")).action(async (functionId, options) => {
68
+ .option("--check-status <checkStatus>", "Invocation ID to inspect without creating a new invocation.")), [
69
+ `bb functions invoke <function-id> --params '{"url":"https://example.com"}'`,
70
+ "bb functions invoke <function-id> --no-wait",
71
+ "bb functions invoke --check-status <invocation-id>",
72
+ ]).action(async (functionId, options) => {
61
73
  await invokeFunction({
62
74
  functionId,
63
75
  params: options.params,
@@ -1,4 +1,4 @@
1
- import { addCommonApiOptions, createBrowserbaseClient, outputJson } from "../lib/command.js";
1
+ import { addCommonApiOptions, addExamples, createBrowserbaseClient, outputJson } from "../lib/command.js";
2
2
  export function attachProjectsCommand(program) {
3
3
  const projects = program
4
4
  .command("projects")
@@ -7,21 +7,21 @@ export function attachProjectsCommand(program) {
7
7
  projects.action(() => {
8
8
  projects.outputHelp();
9
9
  });
10
- addCommonApiOptions(projects
10
+ addExamples(addCommonApiOptions(projects
11
11
  .command("list")
12
- .description("List projects visible to the current API key.")).action(async (options) => {
12
+ .description("List projects visible to the current API key.")), ["bb projects list"]).action(async (options) => {
13
13
  const client = createBrowserbaseClient(options);
14
14
  outputJson(await client.projects.list());
15
15
  });
16
- addCommonApiOptions(projects
16
+ addExamples(addCommonApiOptions(projects
17
17
  .command("get <id>")
18
- .description("Get a project by ID.")).action(async (id, options) => {
18
+ .description("Get a project by ID.")), ["bb projects get <project-id>"]).action(async (id, options) => {
19
19
  const client = createBrowserbaseClient(options);
20
20
  outputJson(await client.projects.retrieve(id));
21
21
  });
22
- addCommonApiOptions(projects
22
+ addExamples(addCommonApiOptions(projects
23
23
  .command("usage <id>")
24
- .description("Get project usage.")).action(async (id, options) => {
24
+ .description("Get project usage.")), ["bb projects usage <project-id>"]).action(async (id, options) => {
25
25
  const client = createBrowserbaseClient(options);
26
26
  outputJson(await client.projects.usage(id));
27
27
  });
@@ -1,10 +1,14 @@
1
- import { addCommonApiOptions, outputJson, requestBrowserbaseJson, writeOutputFile, } from "../lib/command.js";
1
+ import { addCommonApiOptions, addExamples, outputJson, requestBrowserbaseJson, writeOutputFile, } from "../lib/command.js";
2
2
  export function attachSearchCommand(program) {
3
- addCommonApiOptions(program
3
+ addExamples(addCommonApiOptions(program
4
4
  .command("search <query>")
5
5
  .description("Search the web using the Browserbase Search API.")
6
6
  .option("--num-results <count>", "Number of results to return (1-25, default 10).")
7
- .option("--output <output>", "Write the search results as JSON to a file.")).action(async (query, options) => {
7
+ .option("--output <output>", "Write the search results as JSON to a file.")), [
8
+ `bb search "best restaurants in SF"`,
9
+ `bb search "web scraping tools" --num-results 5`,
10
+ `bb search "browserbase docs" --output results.json`,
11
+ ]).action(async (query, options) => {
8
12
  const numResults = options.numResults ? parseInt(options.numResults, 10) : undefined;
9
13
  const result = await requestBrowserbaseJson(options, "/v1/search", {
10
14
  method: "POST",
@@ -1,5 +1,5 @@
1
1
  import { Option } from "commander";
2
- import { addCommonApiOptions, createBrowserbaseClient, fail, outputJson, parseOptionalJsonObjectArg, resolveUploadableFile, writeBinaryOutput, } from "../lib/command.js";
2
+ import { addCommonApiOptions, addExamples, createBrowserbaseClient, fail, outputJson, resolveBody, resolveUploadableFile, writeBinaryOutput, } from "../lib/command.js";
3
3
  export function attachSessionsCommand(program) {
4
4
  const sessions = program
5
5
  .command("sessions")
@@ -8,57 +8,69 @@ export function attachSessionsCommand(program) {
8
8
  sessions.action(() => {
9
9
  sessions.outputHelp();
10
10
  });
11
- addCommonApiOptions(sessions
11
+ addExamples(addCommonApiOptions(sessions
12
12
  .command("list")
13
13
  .description("List sessions.")
14
- .option("--q <q>", `Session metadata query (e.g. "user_metadata['env']:'staging'").`)).action(async (options) => {
14
+ .option("--q <q>", `Session metadata query (e.g. "user_metadata['env']:'staging'").`)), [
15
+ "bb sessions list",
16
+ `bb sessions list --q "user_metadata['env']:'staging'"`,
17
+ ]).action(async (options) => {
15
18
  const client = createBrowserbaseClient(options);
16
19
  outputJson(await client.sessions.list(options.q ? { q: options.q } : {}));
17
20
  });
18
- addCommonApiOptions(sessions
21
+ addExamples(addCommonApiOptions(sessions
19
22
  .command("get <id>")
20
- .description("Get a session by ID.")).action(async (id, options) => {
23
+ .description("Get a session by ID.")), ["bb sessions get <session-id>"]).action(async (id, options) => {
21
24
  const client = createBrowserbaseClient(options);
22
25
  outputJson(await client.sessions.retrieve(id));
23
26
  });
24
- addCommonApiOptions(sessions
27
+ addExamples(addCommonApiOptions(sessions
25
28
  .command("create")
26
29
  .description("Create a session.")
27
- .option("--body <body>", "Optional JSON request body.")).action(async (options) => {
30
+ .option("--body <body>", "Optional JSON request body.")
31
+ .option("--stdin", "Read JSON request body from stdin.")), [
32
+ "bb sessions create",
33
+ `bb sessions create --body '{"proxies":true}'`,
34
+ `echo '{"proxies":true}' | bb sessions create --stdin`,
35
+ ]).action(async (options) => {
28
36
  const client = createBrowserbaseClient(options);
29
- const body = parseOptionalJsonObjectArg(options.body, "body");
37
+ const body = await resolveBody(options);
30
38
  outputJson(await client.sessions.create(body));
31
39
  });
32
- addCommonApiOptions(sessions
40
+ addExamples(addCommonApiOptions(sessions
33
41
  .command("update <id>")
34
42
  .description("Update a session.")
35
43
  .addOption(new Option("--status <status>", "Session status update.")
36
44
  .choices(["REQUEST_RELEASE"])
37
45
  .default("REQUEST_RELEASE"))
38
- .option("--body <body>", "Optional JSON request body. Merged with --status when provided.")).action(async (id, options) => {
46
+ .option("--body <body>", "Optional JSON request body. Merged with --status when provided.")
47
+ .option("--stdin", "Read JSON request body from stdin.")), [
48
+ "bb sessions update <session-id> --status REQUEST_RELEASE",
49
+ `bb sessions update <session-id> --body '{"status":"REQUEST_RELEASE"}'`,
50
+ ]).action(async (id, options) => {
39
51
  const client = createBrowserbaseClient(options);
40
52
  const sessionId = id || fail("Session ID is required.");
41
53
  const body = {
42
- ...parseOptionalJsonObjectArg(options.body, "body"),
54
+ ...(await resolveBody(options)),
43
55
  status: options.status ?? "REQUEST_RELEASE",
44
56
  };
45
57
  outputJson(await client.sessions.update(sessionId, body));
46
58
  });
47
- addCommonApiOptions(sessions
59
+ addExamples(addCommonApiOptions(sessions
48
60
  .command("debug <id>")
49
- .description("Get live debugger URLs for a session.")).action(async (id, options) => {
61
+ .description("Get live debugger URLs for a session.")), ["bb sessions debug <session-id>"]).action(async (id, options) => {
50
62
  const client = createBrowserbaseClient(options);
51
63
  outputJson(await client.sessions.debug(id));
52
64
  });
53
- addCommonApiOptions(sessions
65
+ addExamples(addCommonApiOptions(sessions
54
66
  .command("logs <id>")
55
- .description("Get session logs.")).action(async (id, options) => {
67
+ .description("Get session logs.")), ["bb sessions logs <session-id>"]).action(async (id, options) => {
56
68
  const client = createBrowserbaseClient(options);
57
69
  outputJson(await client.sessions.logs.list(id));
58
70
  });
59
- addCommonApiOptions(sessions
71
+ addExamples(addCommonApiOptions(sessions
60
72
  .command("recording <id>")
61
- .description("Get rrweb session recording events.")).action(async (id, options) => {
73
+ .description("Get rrweb session recording events.")), ["bb sessions recording <session-id>"]).action(async (id, options) => {
62
74
  const client = createBrowserbaseClient(options);
63
75
  outputJson(await client.sessions.recording.retrieve(id));
64
76
  });
@@ -69,10 +81,13 @@ export function attachSessionsCommand(program) {
69
81
  downloads.action(() => {
70
82
  downloads.outputHelp();
71
83
  });
72
- addCommonApiOptions(downloads
84
+ addExamples(addCommonApiOptions(downloads
73
85
  .command("get <id>")
74
86
  .description("Download session files as a ZIP archive.")
75
- .option("--output <output>", "Path to write the ZIP file.")).action(async (id, options) => {
87
+ .option("--output <output>", "Path to write the ZIP file.")), [
88
+ "bb sessions downloads get <session-id>",
89
+ "bb sessions downloads get <session-id> --output ./downloads.zip",
90
+ ]).action(async (id, options) => {
76
91
  const client = createBrowserbaseClient(options);
77
92
  const response = await client.sessions.downloads.list(id);
78
93
  const defaultPath = `${id}-downloads.zip`;
@@ -88,9 +103,9 @@ export function attachSessionsCommand(program) {
88
103
  uploads.action(() => {
89
104
  uploads.outputHelp();
90
105
  });
91
- addCommonApiOptions(uploads
106
+ addExamples(addCommonApiOptions(uploads
92
107
  .command("create <id> <file>")
93
- .description("Upload a file to a session.")).action(async (id, file, options) => {
108
+ .description("Upload a file to a session.")), ["bb sessions uploads create <session-id> ./file.pdf"]).action(async (id, file, options) => {
94
109
  const client = createBrowserbaseClient(options);
95
110
  outputJson(await client.sessions.uploads.create(id, {
96
111
  file: await resolveUploadableFile(file, "session upload"),
@@ -25,7 +25,15 @@ export function attachSkillsCommand(program) {
25
25
  const skills = program
26
26
  .command("skills")
27
27
  .description("Install Browserbase agent skills for Claude Code.")
28
- .action(async () => {
28
+ .option("-y, --yes", "Auto-accept installation prompts.")
29
+ .addHelpText("after", [
30
+ "",
31
+ "Examples:",
32
+ " bb skills (interactive install prompt)",
33
+ " bb skills --yes (auto-install without prompting)",
34
+ " bb skills install (non-interactive install)",
35
+ ].join("\n"))
36
+ .action(async (options) => {
29
37
  console.log([
30
38
  "",
31
39
  "Browserbase Skills for Claude Code",
@@ -38,18 +46,23 @@ export function attachSkillsCommand(program) {
38
46
  " Run: npx skills add browserbase/skills --yes --global",
39
47
  "",
40
48
  ].join("\n"));
41
- const rl = readline.createInterface({
42
- input: process.stdin,
43
- output: process.stdout,
44
- });
45
- try {
46
- const answer = await rl.question("Install skills? [Y/n] ");
47
- if (answer.trim().toLowerCase() === "n") {
48
- return;
49
+ if (!options.yes) {
50
+ if (!process.stdin.isTTY) {
51
+ fail("Cannot prompt for install in non-interactive mode. Use --yes to auto-install.");
52
+ }
53
+ const rl = readline.createInterface({
54
+ input: process.stdin,
55
+ output: process.stdout,
56
+ });
57
+ try {
58
+ const answer = await rl.question("Install skills? [Y/n] ");
59
+ if (answer.trim().toLowerCase() === "n") {
60
+ return;
61
+ }
62
+ }
63
+ finally {
64
+ rl.close();
49
65
  }
50
- }
51
- finally {
52
- rl.close();
53
66
  }
54
67
  await installSkills();
55
68
  });
@@ -2,6 +2,7 @@ import Browserbase from "@browserbasehq/sdk";
2
2
  import { createReadStream, constants } from "node:fs";
3
3
  import { access, mkdir, writeFile } from "node:fs/promises";
4
4
  import { dirname, resolve } from "node:path";
5
+ import { Readable } from "node:stream";
5
6
  const defaultBrowserbaseApiUrl = "https://api.browserbase.com";
6
7
  export class CommandFailure extends Error {
7
8
  exitCode;
@@ -151,3 +152,27 @@ export async function writeBinaryOutput(pathname, contents) {
151
152
  await mkdir(dirname(absolutePath), { recursive: true });
152
153
  await writeFile(absolutePath, contents);
153
154
  }
155
+ export function addExamples(command, examples) {
156
+ const text = "\nExamples:\n" + examples.map((e) => ` ${e}`).join("\n");
157
+ return command.addHelpText("after", text);
158
+ }
159
+ export async function readStdin() {
160
+ if (process.stdin.isTTY) {
161
+ fail("--stdin requires piped input. Example: echo '{\"key\":\"value\"}' | bb <command> --stdin");
162
+ }
163
+ const chunks = [];
164
+ for await (const chunk of Readable.toWeb(process.stdin)) {
165
+ chunks.push(Buffer.from(chunk));
166
+ }
167
+ return Buffer.concat(chunks).toString("utf8").trim();
168
+ }
169
+ export async function resolveBody(options) {
170
+ if (options.body && options.stdin) {
171
+ fail("Cannot use both --body and --stdin. Provide one or the other.");
172
+ }
173
+ if (options.stdin) {
174
+ const input = await readStdin();
175
+ return parseOptionalJsonObjectArg(input, "stdin");
176
+ }
177
+ return parseOptionalJsonObjectArg(options.body, "body");
178
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserbasehq/cli",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Browserbase CLI for platform APIs, functions, and browse passthrough.",
5
5
  "type": "module",
6
6
  "private": false,