@browserbasehq/cli 0.0.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 +20 -0
- package/dist/cli.js +54 -0
- package/dist/commands/browse.js +27 -0
- package/dist/commands/contexts.js +42 -0
- package/dist/commands/dashboard.js +11 -0
- package/dist/commands/extensions.js +35 -0
- package/dist/commands/fetch.js +35 -0
- package/dist/commands/functions.js +70 -0
- package/dist/commands/projects.js +28 -0
- package/dist/commands/sessions.js +99 -0
- package/dist/lib/command.js +162 -0
- package/dist/lib/functions/dev.js +331 -0
- package/dist/lib/functions/init.js +102 -0
- package/dist/lib/functions/invoke.js +28 -0
- package/dist/lib/functions/publish.js +131 -0
- package/dist/lib/functions/shared.js +116 -0
- package/dist/lib/open.js +29 -0
- package/dist/lib/process.js +45 -0
- package/dist/main.js +4 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Browserbase CLI
|
|
2
|
+
|
|
3
|
+
Browserbase's Bun-native development repo for the `bb` CLI.
|
|
4
|
+
|
|
5
|
+
## Development
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun install
|
|
9
|
+
bun run check
|
|
10
|
+
bun run cli -- --help
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Build
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bun run build
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The published artifact is Node-compatible JavaScript in `dist/`, even though local
|
|
20
|
+
development stays Bun-native.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Command, CommanderError } from "commander";
|
|
2
|
+
import { attachBrowseCommand } from "./commands/browse.js";
|
|
3
|
+
import { attachContextsCommand } from "./commands/contexts.js";
|
|
4
|
+
import { attachDashboardCommand } from "./commands/dashboard.js";
|
|
5
|
+
import { attachExtensionsCommand } from "./commands/extensions.js";
|
|
6
|
+
import { attachFetchCommand } from "./commands/fetch.js";
|
|
7
|
+
import { attachFunctionsCommand } from "./commands/functions.js";
|
|
8
|
+
import { attachProjectsCommand } from "./commands/projects.js";
|
|
9
|
+
import { attachSessionsCommand } from "./commands/sessions.js";
|
|
10
|
+
export function buildProgram() {
|
|
11
|
+
const program = new Command();
|
|
12
|
+
program
|
|
13
|
+
.name("bb")
|
|
14
|
+
.description("Browserbase CLI for platform APIs, functions, and browse passthrough.")
|
|
15
|
+
.version("0.1.0")
|
|
16
|
+
.helpCommand(false)
|
|
17
|
+
.showHelpAfterError()
|
|
18
|
+
.showSuggestionAfterError()
|
|
19
|
+
.enablePositionalOptions()
|
|
20
|
+
.exitOverride();
|
|
21
|
+
attachDashboardCommand(program);
|
|
22
|
+
attachBrowseCommand(program);
|
|
23
|
+
attachFunctionsCommand(program);
|
|
24
|
+
attachSessionsCommand(program);
|
|
25
|
+
attachProjectsCommand(program);
|
|
26
|
+
attachContextsCommand(program);
|
|
27
|
+
attachExtensionsCommand(program);
|
|
28
|
+
attachFetchCommand(program);
|
|
29
|
+
return program;
|
|
30
|
+
}
|
|
31
|
+
export async function run(argv) {
|
|
32
|
+
const program = buildProgram();
|
|
33
|
+
process.exitCode = 0;
|
|
34
|
+
if (argv.length === 0) {
|
|
35
|
+
program.outputHelp();
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await program.parseAsync(argv, { from: "user" });
|
|
40
|
+
return process.exitCode ?? 0;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error instanceof CommanderError) {
|
|
44
|
+
const commanderError = error;
|
|
45
|
+
if (commanderError.code === "commander.helpDisplayed" ||
|
|
46
|
+
commanderError.code === "commander.version") {
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
return commanderError.exitCode;
|
|
50
|
+
}
|
|
51
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
52
|
+
return error?.exitCode ?? 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { fail } from "../lib/command.js";
|
|
2
|
+
import { findExecutable, spawnPassthrough } from "../lib/process.js";
|
|
3
|
+
export function attachBrowseCommand(program) {
|
|
4
|
+
program
|
|
5
|
+
.command("browse")
|
|
6
|
+
.description("Forward commands to the standalone @browserbasehq/browse-cli binary.")
|
|
7
|
+
.argument("[args...]", "Arguments to forward to browse")
|
|
8
|
+
.allowUnknownOption(true)
|
|
9
|
+
.allowExcessArguments(true)
|
|
10
|
+
.passThroughOptions()
|
|
11
|
+
.action(async (args) => {
|
|
12
|
+
const browsePath = await findExecutable("browse");
|
|
13
|
+
if (!browsePath) {
|
|
14
|
+
fail([
|
|
15
|
+
"`browse` is not installed.",
|
|
16
|
+
"Install it with:",
|
|
17
|
+
"npm install -g @browserbasehq/browse-cli",
|
|
18
|
+
"",
|
|
19
|
+
"Then rerun your `bb browse ...` command.",
|
|
20
|
+
].join("\n"));
|
|
21
|
+
}
|
|
22
|
+
const exitCode = await spawnPassthrough(browsePath, args ?? []);
|
|
23
|
+
if (exitCode !== 0) {
|
|
24
|
+
process.exitCode = exitCode;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { addCommonApiOptions, createBrowserbaseClient, mergeProjectIdIntoBody, outputJson, parseOptionalJsonObjectArg, requestBrowserbase, requestBrowserbaseJson, resolveProjectId, } from "../lib/command.js";
|
|
2
|
+
export function attachContextsCommand(program) {
|
|
3
|
+
const contexts = program
|
|
4
|
+
.command("contexts")
|
|
5
|
+
.description("Create, inspect, update, and delete Browserbase contexts.")
|
|
6
|
+
.helpCommand(false);
|
|
7
|
+
contexts.action(() => {
|
|
8
|
+
contexts.outputHelp();
|
|
9
|
+
});
|
|
10
|
+
addCommonApiOptions(contexts
|
|
11
|
+
.command("create")
|
|
12
|
+
.description("Create a context.")
|
|
13
|
+
.option("--body <body>", "Optional JSON request body.")).action(async (options) => {
|
|
14
|
+
const client = createBrowserbaseClient(options);
|
|
15
|
+
const body = mergeProjectIdIntoBody(resolveProjectId(options), parseOptionalJsonObjectArg(options.body, "body"));
|
|
16
|
+
outputJson(await client.contexts.create(body));
|
|
17
|
+
});
|
|
18
|
+
addCommonApiOptions(contexts
|
|
19
|
+
.command("get <id>")
|
|
20
|
+
.description("Get a context by ID.")).action(async (id, options) => {
|
|
21
|
+
const client = createBrowserbaseClient(options);
|
|
22
|
+
outputJson(await client.contexts.retrieve(id));
|
|
23
|
+
});
|
|
24
|
+
addCommonApiOptions(contexts
|
|
25
|
+
.command("update <id>")
|
|
26
|
+
.description("Refresh the upload URL for a context.")).action(async (id, options) => {
|
|
27
|
+
outputJson(await requestBrowserbaseJson(options, `/v1/contexts/${id}`, {
|
|
28
|
+
method: "PUT",
|
|
29
|
+
}));
|
|
30
|
+
});
|
|
31
|
+
addCommonApiOptions(contexts
|
|
32
|
+
.command("delete <id>")
|
|
33
|
+
.description("Delete a context.")).action(async (id, options) => {
|
|
34
|
+
await requestBrowserbase(options, `/v1/contexts/${id}`, {
|
|
35
|
+
method: "DELETE",
|
|
36
|
+
headers: {
|
|
37
|
+
Accept: "*/*",
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
outputJson({ ok: true, id });
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { openUrl } from "../lib/open.js";
|
|
2
|
+
export function attachDashboardCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("dashboard")
|
|
5
|
+
.description("Open Browserbase Overview in your local browser.")
|
|
6
|
+
.action(async () => {
|
|
7
|
+
const url = "http://browserbase.com/overview";
|
|
8
|
+
await openUrl(url);
|
|
9
|
+
console.log(`Opened ${url}`);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { addCommonApiOptions, createBrowserbaseClient, outputJson, requestBrowserbase, resolveUploadableFile, } from "../lib/command.js";
|
|
2
|
+
export function attachExtensionsCommand(program) {
|
|
3
|
+
const extensions = program
|
|
4
|
+
.command("extensions")
|
|
5
|
+
.description("Upload and manage Browserbase extensions.")
|
|
6
|
+
.helpCommand(false);
|
|
7
|
+
extensions.action(() => {
|
|
8
|
+
extensions.outputHelp();
|
|
9
|
+
});
|
|
10
|
+
addCommonApiOptions(extensions
|
|
11
|
+
.command("upload <file>")
|
|
12
|
+
.description("Upload an extension ZIP file.")).action(async (file, options) => {
|
|
13
|
+
const client = createBrowserbaseClient(options);
|
|
14
|
+
outputJson(await client.extensions.create({
|
|
15
|
+
file: await resolveUploadableFile(file, "extension"),
|
|
16
|
+
}));
|
|
17
|
+
});
|
|
18
|
+
addCommonApiOptions(extensions
|
|
19
|
+
.command("get <id>")
|
|
20
|
+
.description("Get an extension by ID.")).action(async (id, options) => {
|
|
21
|
+
const client = createBrowserbaseClient(options);
|
|
22
|
+
outputJson(await client.extensions.retrieve(id));
|
|
23
|
+
});
|
|
24
|
+
addCommonApiOptions(extensions
|
|
25
|
+
.command("delete <id>")
|
|
26
|
+
.description("Delete an extension.")).action(async (id, options) => {
|
|
27
|
+
await requestBrowserbase(options, `/v1/extensions/${id}`, {
|
|
28
|
+
method: "DELETE",
|
|
29
|
+
headers: {
|
|
30
|
+
Accept: "*/*",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
outputJson({ ok: true, id });
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { addCommonApiOptions, createBrowserbaseClient, outputJson, writeOutputFile, } from "../lib/command.js";
|
|
2
|
+
export function attachFetchCommand(program) {
|
|
3
|
+
addCommonApiOptions(program
|
|
4
|
+
.command("fetch <url>")
|
|
5
|
+
.description("Fetch a page with the Browserbase Fetch API.")
|
|
6
|
+
.option("--allow-insecure-ssl", "Bypass TLS certificate verification.")
|
|
7
|
+
.option("--allow-redirects", "Follow HTTP redirects.")
|
|
8
|
+
.option("--proxies", "Enable Browserbase proxy support.")
|
|
9
|
+
.option("--output <output>", "Write the fetched content to a file.")).action(async (url, options) => {
|
|
10
|
+
const client = createBrowserbaseClient(options);
|
|
11
|
+
const result = await client.fetchAPI.create({
|
|
12
|
+
url,
|
|
13
|
+
allowInsecureSsl: options.allowInsecureSsl,
|
|
14
|
+
allowRedirects: options.allowRedirects,
|
|
15
|
+
proxies: options.proxies,
|
|
16
|
+
});
|
|
17
|
+
if (options.output) {
|
|
18
|
+
await writeOutputFile(options.output, result.content);
|
|
19
|
+
if (options.json) {
|
|
20
|
+
outputJson({ ...result, outputPath: options.output });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
console.log(`Saved fetched content to ${options.output}`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (options.json) {
|
|
27
|
+
outputJson(result);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
process.stdout.write(result.content);
|
|
31
|
+
if (!result.content.endsWith("\n")) {
|
|
32
|
+
process.stdout.write("\n");
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Option } from "commander";
|
|
2
|
+
import { addFunctionsApiOptions } from "../lib/command.js";
|
|
3
|
+
import { initFunctionsProject } from "../lib/functions/init.js";
|
|
4
|
+
import { invokeFunction } from "../lib/functions/invoke.js";
|
|
5
|
+
import { startFunctionsDevServer } from "../lib/functions/dev.js";
|
|
6
|
+
import { publishFunction } from "../lib/functions/publish.js";
|
|
7
|
+
const packageManagers = ["npm", "pnpm"];
|
|
8
|
+
export function attachFunctionsCommand(program) {
|
|
9
|
+
const functions = program
|
|
10
|
+
.command("functions")
|
|
11
|
+
.description("Develop, publish, and invoke Browserbase Functions.")
|
|
12
|
+
.helpCommand(false);
|
|
13
|
+
functions.action(() => {
|
|
14
|
+
functions.outputHelp();
|
|
15
|
+
});
|
|
16
|
+
functions
|
|
17
|
+
.command("init [projectName]")
|
|
18
|
+
.description("Initialize a new Browserbase Functions project.")
|
|
19
|
+
.addOption(new Option("--package-manager <packageManager>", "Package manager to use.")
|
|
20
|
+
.choices([...packageManagers])
|
|
21
|
+
.default("pnpm"))
|
|
22
|
+
.action(async (projectName, options) => {
|
|
23
|
+
await initFunctionsProject({
|
|
24
|
+
projectName: projectName ?? "my-browserbase-function",
|
|
25
|
+
packageManager: options.packageManager ?? "pnpm",
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
addFunctionsApiOptions(functions
|
|
29
|
+
.command("dev <entrypoint>")
|
|
30
|
+
.description("Run the local Browserbase Functions development server.")
|
|
31
|
+
.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) => {
|
|
33
|
+
await startFunctionsDevServer({
|
|
34
|
+
entrypoint,
|
|
35
|
+
port: Number(options.port ?? "14113"),
|
|
36
|
+
host: options.host ?? "127.0.0.1",
|
|
37
|
+
apiKey: options.apiKey,
|
|
38
|
+
projectId: options.projectId,
|
|
39
|
+
apiUrl: options.apiUrl,
|
|
40
|
+
verbose: options.verbose ?? false,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
addFunctionsApiOptions(functions
|
|
44
|
+
.command("publish <entrypoint>")
|
|
45
|
+
.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) => {
|
|
47
|
+
await publishFunction({
|
|
48
|
+
entrypoint,
|
|
49
|
+
apiKey: options.apiKey,
|
|
50
|
+
projectId: options.projectId,
|
|
51
|
+
apiUrl: options.apiUrl,
|
|
52
|
+
dryRun: options.dryRun ?? false,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
addFunctionsApiOptions(functions
|
|
56
|
+
.command("invoke [functionId]")
|
|
57
|
+
.description("Invoke a deployed Browserbase Function or check invocation status.")
|
|
58
|
+
.option("--params <params>", "JSON params to pass to the function.")
|
|
59
|
+
.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) => {
|
|
61
|
+
await invokeFunction({
|
|
62
|
+
functionId,
|
|
63
|
+
params: options.params,
|
|
64
|
+
apiKey: options.apiKey,
|
|
65
|
+
apiUrl: options.apiUrl,
|
|
66
|
+
noWait: options.wait === false,
|
|
67
|
+
checkStatus: options.checkStatus,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { addCommonApiOptions, createBrowserbaseClient, outputJson } from "../lib/command.js";
|
|
2
|
+
export function attachProjectsCommand(program) {
|
|
3
|
+
const projects = program
|
|
4
|
+
.command("projects")
|
|
5
|
+
.description("List and inspect Browserbase projects.")
|
|
6
|
+
.helpCommand(false);
|
|
7
|
+
projects.action(() => {
|
|
8
|
+
projects.outputHelp();
|
|
9
|
+
});
|
|
10
|
+
addCommonApiOptions(projects
|
|
11
|
+
.command("list")
|
|
12
|
+
.description("List projects visible to the current API key.")).action(async (options) => {
|
|
13
|
+
const client = createBrowserbaseClient(options);
|
|
14
|
+
outputJson(await client.projects.list());
|
|
15
|
+
});
|
|
16
|
+
addCommonApiOptions(projects
|
|
17
|
+
.command("get <id>")
|
|
18
|
+
.description("Get a project by ID.")).action(async (id, options) => {
|
|
19
|
+
const client = createBrowserbaseClient(options);
|
|
20
|
+
outputJson(await client.projects.retrieve(id));
|
|
21
|
+
});
|
|
22
|
+
addCommonApiOptions(projects
|
|
23
|
+
.command("usage <id>")
|
|
24
|
+
.description("Get project usage.")).action(async (id, options) => {
|
|
25
|
+
const client = createBrowserbaseClient(options);
|
|
26
|
+
outputJson(await client.projects.usage(id));
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Option } from "commander";
|
|
2
|
+
import { addCommonApiOptions, createBrowserbaseClient, fail, mergeProjectIdIntoBody, outputJson, parseOptionalJsonObjectArg, resolveProjectId, resolveUploadableFile, writeBinaryOutput, } from "../lib/command.js";
|
|
3
|
+
export function attachSessionsCommand(program) {
|
|
4
|
+
const sessions = program
|
|
5
|
+
.command("sessions")
|
|
6
|
+
.description("Create, inspect, and manage Browserbase sessions.")
|
|
7
|
+
.helpCommand(false);
|
|
8
|
+
sessions.action(() => {
|
|
9
|
+
sessions.outputHelp();
|
|
10
|
+
});
|
|
11
|
+
addCommonApiOptions(sessions
|
|
12
|
+
.command("list")
|
|
13
|
+
.description("List sessions.")
|
|
14
|
+
.option("--q <q>", "Session metadata query.")).action(async (options) => {
|
|
15
|
+
const client = createBrowserbaseClient(options);
|
|
16
|
+
outputJson(await client.sessions.list(options.q ? { q: options.q } : {}));
|
|
17
|
+
});
|
|
18
|
+
addCommonApiOptions(sessions
|
|
19
|
+
.command("get <id>")
|
|
20
|
+
.description("Get a session by ID.")).action(async (id, options) => {
|
|
21
|
+
const client = createBrowserbaseClient(options);
|
|
22
|
+
outputJson(await client.sessions.retrieve(id));
|
|
23
|
+
});
|
|
24
|
+
addCommonApiOptions(sessions
|
|
25
|
+
.command("create")
|
|
26
|
+
.description("Create a session.")
|
|
27
|
+
.option("--body <body>", "Optional JSON request body.")).action(async (options) => {
|
|
28
|
+
const client = createBrowserbaseClient(options);
|
|
29
|
+
const body = mergeProjectIdIntoBody(resolveProjectId(options), parseOptionalJsonObjectArg(options.body, "body"));
|
|
30
|
+
outputJson(await client.sessions.create(body));
|
|
31
|
+
});
|
|
32
|
+
addCommonApiOptions(sessions
|
|
33
|
+
.command("update <id>")
|
|
34
|
+
.description("Update a session.")
|
|
35
|
+
.addOption(new Option("--status <status>", "Session status update.")
|
|
36
|
+
.choices(["REQUEST_RELEASE"])
|
|
37
|
+
.default("REQUEST_RELEASE"))
|
|
38
|
+
.option("--body <body>", "Optional JSON request body. Merged with --status when provided.")).action(async (id, options) => {
|
|
39
|
+
const client = createBrowserbaseClient(options);
|
|
40
|
+
const sessionId = id || fail("Session ID is required.");
|
|
41
|
+
const body = mergeProjectIdIntoBody(resolveProjectId(options), {
|
|
42
|
+
...parseOptionalJsonObjectArg(options.body, "body"),
|
|
43
|
+
status: options.status ?? "REQUEST_RELEASE",
|
|
44
|
+
});
|
|
45
|
+
outputJson(await client.sessions.update(sessionId, body));
|
|
46
|
+
});
|
|
47
|
+
addCommonApiOptions(sessions
|
|
48
|
+
.command("debug <id>")
|
|
49
|
+
.description("Get live debugger URLs for a session.")).action(async (id, options) => {
|
|
50
|
+
const client = createBrowserbaseClient(options);
|
|
51
|
+
outputJson(await client.sessions.debug(id));
|
|
52
|
+
});
|
|
53
|
+
addCommonApiOptions(sessions
|
|
54
|
+
.command("logs <id>")
|
|
55
|
+
.description("Get session logs.")).action(async (id, options) => {
|
|
56
|
+
const client = createBrowserbaseClient(options);
|
|
57
|
+
outputJson(await client.sessions.logs.list(id));
|
|
58
|
+
});
|
|
59
|
+
addCommonApiOptions(sessions
|
|
60
|
+
.command("recording <id>")
|
|
61
|
+
.description("Get rrweb session recording events.")).action(async (id, options) => {
|
|
62
|
+
const client = createBrowserbaseClient(options);
|
|
63
|
+
outputJson(await client.sessions.recording.retrieve(id));
|
|
64
|
+
});
|
|
65
|
+
const downloads = sessions
|
|
66
|
+
.command("downloads")
|
|
67
|
+
.description("Download session artifacts as a ZIP file.")
|
|
68
|
+
.helpCommand(false);
|
|
69
|
+
downloads.action(() => {
|
|
70
|
+
downloads.outputHelp();
|
|
71
|
+
});
|
|
72
|
+
addCommonApiOptions(downloads
|
|
73
|
+
.command("get <id>")
|
|
74
|
+
.description("Download session files as a ZIP archive.")
|
|
75
|
+
.option("--output <output>", "Path to write the ZIP file.")).action(async (id, options) => {
|
|
76
|
+
const client = createBrowserbaseClient(options);
|
|
77
|
+
const response = await client.sessions.downloads.list(id);
|
|
78
|
+
const defaultPath = `${id}-downloads.zip`;
|
|
79
|
+
const outputPath = options.output ?? defaultPath;
|
|
80
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
81
|
+
await writeBinaryOutput(outputPath, bytes);
|
|
82
|
+
outputJson({ ok: true, outputPath, sizeBytes: bytes.byteLength });
|
|
83
|
+
});
|
|
84
|
+
const uploads = sessions
|
|
85
|
+
.command("uploads")
|
|
86
|
+
.description("Upload files to a running session.")
|
|
87
|
+
.helpCommand(false);
|
|
88
|
+
uploads.action(() => {
|
|
89
|
+
uploads.outputHelp();
|
|
90
|
+
});
|
|
91
|
+
addCommonApiOptions(uploads
|
|
92
|
+
.command("create <id> <file>")
|
|
93
|
+
.description("Upload a file to a session.")).action(async (id, file, options) => {
|
|
94
|
+
const client = createBrowserbaseClient(options);
|
|
95
|
+
outputJson(await client.sessions.uploads.create(id, {
|
|
96
|
+
file: await resolveUploadableFile(file, "session upload"),
|
|
97
|
+
}));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import Browserbase from "@browserbasehq/sdk";
|
|
2
|
+
import { createReadStream, constants } from "node:fs";
|
|
3
|
+
import { access, mkdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
const defaultBrowserbaseApiUrl = "https://api.browserbase.com";
|
|
6
|
+
export class CommandFailure extends Error {
|
|
7
|
+
exitCode;
|
|
8
|
+
constructor(message, exitCode = 1) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "CommandFailure";
|
|
11
|
+
this.exitCode = exitCode;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function fail(message, exitCode = 1) {
|
|
15
|
+
throw new CommandFailure(message, exitCode);
|
|
16
|
+
}
|
|
17
|
+
export function addCommonApiOptions(command) {
|
|
18
|
+
return command
|
|
19
|
+
.option("--api-key <apiKey>", "Override the Browserbase API key.")
|
|
20
|
+
.option("--project-id <projectId>", "Override the Browserbase project ID.")
|
|
21
|
+
.option("--base-url <baseUrl>", "Override the Browserbase API base URL.")
|
|
22
|
+
.option("--json", "Print JSON output.")
|
|
23
|
+
.option("--verbose", "Enable verbose logging.");
|
|
24
|
+
}
|
|
25
|
+
export function addFunctionsApiOptions(command, options = {}) {
|
|
26
|
+
const { includeProjectId = false, includeVerbose = false } = options;
|
|
27
|
+
command
|
|
28
|
+
.option("--api-key <apiKey>", "Override the Browserbase API key.")
|
|
29
|
+
.option("--api-url <apiUrl>", "Override the Browserbase API base URL.");
|
|
30
|
+
if (includeProjectId) {
|
|
31
|
+
command.option("--project-id <projectId>", "Override the Browserbase project ID.");
|
|
32
|
+
}
|
|
33
|
+
if (includeVerbose) {
|
|
34
|
+
command.option("--verbose", "Print verbose server and runtime logs.");
|
|
35
|
+
}
|
|
36
|
+
return command;
|
|
37
|
+
}
|
|
38
|
+
export function resolveApiKey(args) {
|
|
39
|
+
const apiKey = args.apiKey ?? process.env.BROWSERBASE_API_KEY;
|
|
40
|
+
return apiKey || fail("Missing Browserbase API key. Set BROWSERBASE_API_KEY or pass --api-key.");
|
|
41
|
+
}
|
|
42
|
+
export function resolveBaseUrl(args) {
|
|
43
|
+
return args.baseUrl ?? process.env.BROWSERBASE_BASE_URL;
|
|
44
|
+
}
|
|
45
|
+
export function resolveApiBaseUrl(args) {
|
|
46
|
+
return resolveBaseUrl(args) ?? defaultBrowserbaseApiUrl;
|
|
47
|
+
}
|
|
48
|
+
export function resolveProjectId(args, options = {}) {
|
|
49
|
+
const projectId = args.projectId ?? process.env.BROWSERBASE_PROJECT_ID;
|
|
50
|
+
if (options.required && !projectId) {
|
|
51
|
+
fail("Missing Browserbase project ID. Set BROWSERBASE_PROJECT_ID or pass --project-id.");
|
|
52
|
+
}
|
|
53
|
+
return projectId;
|
|
54
|
+
}
|
|
55
|
+
export function createBrowserbaseClient(args) {
|
|
56
|
+
return new Browserbase({
|
|
57
|
+
apiKey: resolveApiKey(args),
|
|
58
|
+
baseURL: resolveBaseUrl(args),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export function parseOptionalJsonObjectArg(rawValue, label) {
|
|
62
|
+
if (!rawValue) {
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
if (typeof rawValue !== "string") {
|
|
66
|
+
fail(`${label} must be provided as a JSON string.`);
|
|
67
|
+
}
|
|
68
|
+
let parsed;
|
|
69
|
+
try {
|
|
70
|
+
parsed = JSON.parse(rawValue);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
fail(`Invalid JSON for ${label}: ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") {
|
|
76
|
+
fail(`${label} must be a JSON object.`);
|
|
77
|
+
}
|
|
78
|
+
return parsed;
|
|
79
|
+
}
|
|
80
|
+
export function parseOptionalJsonValueArg(rawValue, label) {
|
|
81
|
+
if (!rawValue) {
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
if (typeof rawValue !== "string") {
|
|
85
|
+
fail(`${label} must be provided as a JSON string.`);
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(rawValue);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
fail(`Invalid JSON for ${label}: ${error.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export function mergeProjectIdIntoBody(projectId, body) {
|
|
95
|
+
if (projectId && body.projectId === undefined) {
|
|
96
|
+
return { ...body, projectId };
|
|
97
|
+
}
|
|
98
|
+
return body;
|
|
99
|
+
}
|
|
100
|
+
export function outputJson(value) {
|
|
101
|
+
console.log(JSON.stringify(value, null, 2));
|
|
102
|
+
}
|
|
103
|
+
export async function resolveUploadableFile(filePath, label) {
|
|
104
|
+
const absolutePath = resolve(filePath);
|
|
105
|
+
try {
|
|
106
|
+
await access(absolutePath, constants.R_OK);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
fail(`Could not read ${label} file: ${absolutePath}`);
|
|
110
|
+
}
|
|
111
|
+
return createReadStream(absolutePath);
|
|
112
|
+
}
|
|
113
|
+
export async function readBrowserbaseError(response) {
|
|
114
|
+
try {
|
|
115
|
+
const data = await response.json();
|
|
116
|
+
if (typeof data === "object" && data !== null) {
|
|
117
|
+
const message = data.message || data.error;
|
|
118
|
+
if (message) {
|
|
119
|
+
return message;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
try {
|
|
125
|
+
const text = await response.text();
|
|
126
|
+
if (text) {
|
|
127
|
+
return text;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return `${response.status} ${response.statusText}`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return `${response.status} ${response.statusText}`;
|
|
135
|
+
}
|
|
136
|
+
export async function requestBrowserbase(args, pathname, init = {}) {
|
|
137
|
+
const response = await fetch(new URL(pathname, resolveApiBaseUrl(args)), {
|
|
138
|
+
...init,
|
|
139
|
+
headers: {
|
|
140
|
+
"x-bb-api-key": resolveApiKey(args),
|
|
141
|
+
...(init.headers ?? {}),
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
fail(await readBrowserbaseError(response));
|
|
146
|
+
}
|
|
147
|
+
return response;
|
|
148
|
+
}
|
|
149
|
+
export async function requestBrowserbaseJson(args, pathname, init = {}) {
|
|
150
|
+
const response = await requestBrowserbase(args, pathname, init);
|
|
151
|
+
return (await response.json());
|
|
152
|
+
}
|
|
153
|
+
export async function writeOutputFile(pathname, contents) {
|
|
154
|
+
const absolutePath = resolve(pathname);
|
|
155
|
+
await mkdir(dirname(absolutePath), { recursive: true });
|
|
156
|
+
await writeFile(absolutePath, contents, "utf8");
|
|
157
|
+
}
|
|
158
|
+
export async function writeBinaryOutput(pathname, contents) {
|
|
159
|
+
const absolutePath = resolve(pathname);
|
|
160
|
+
await mkdir(dirname(absolutePath), { recursive: true });
|
|
161
|
+
await writeFile(absolutePath, contents);
|
|
162
|
+
}
|