@browserbasehq/cli 0.2.1 → 0.3.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/dist/cli.js +4 -2
- package/dist/commands/b.js +19 -0
- package/dist/commands/browse.js +49 -6
- package/dist/commands/contexts.js +2 -2
- package/dist/commands/fetch.js +8 -13
- package/dist/commands/search.js +27 -0
- package/dist/commands/sessions.js +5 -5
- package/dist/lib/command.js +1 -10
- package/dist/lib/functions/init.js +2 -1
- package/dist/lib/functions/publish.js +33 -12
- package/dist/lib/functions/shared.js +1 -12
- package/package.json +1 -1
- package/dist/commands/dashboard.js +0 -11
package/dist/cli.js
CHANGED
|
@@ -4,13 +4,14 @@ import { attachBrowseCommand } from "./commands/browse.js";
|
|
|
4
4
|
const require = createRequire(import.meta.url);
|
|
5
5
|
const { version } = require("../package.json");
|
|
6
6
|
import { attachContextsCommand } from "./commands/contexts.js";
|
|
7
|
-
import { attachDashboardCommand } from "./commands/dashboard.js";
|
|
8
7
|
import { attachExtensionsCommand } from "./commands/extensions.js";
|
|
9
8
|
import { attachFetchCommand } from "./commands/fetch.js";
|
|
10
9
|
import { attachFunctionsCommand } from "./commands/functions.js";
|
|
10
|
+
import { attachSearchCommand } from "./commands/search.js";
|
|
11
11
|
import { attachProjectsCommand } from "./commands/projects.js";
|
|
12
12
|
import { attachSessionsCommand } from "./commands/sessions.js";
|
|
13
13
|
import { attachSkillsCommand } from "./commands/skills.js";
|
|
14
|
+
import { attachBCommand } from "./commands/b.js";
|
|
14
15
|
export function buildProgram() {
|
|
15
16
|
const program = new Command();
|
|
16
17
|
program
|
|
@@ -24,13 +25,14 @@ export function buildProgram() {
|
|
|
24
25
|
.exitOverride();
|
|
25
26
|
attachBrowseCommand(program);
|
|
26
27
|
attachFetchCommand(program);
|
|
28
|
+
attachSearchCommand(program);
|
|
27
29
|
attachSessionsCommand(program);
|
|
28
|
-
attachDashboardCommand(program);
|
|
29
30
|
attachFunctionsCommand(program);
|
|
30
31
|
attachProjectsCommand(program);
|
|
31
32
|
attachContextsCommand(program);
|
|
32
33
|
attachExtensionsCommand(program);
|
|
33
34
|
attachSkillsCommand(program);
|
|
35
|
+
attachBCommand(program);
|
|
34
36
|
return program;
|
|
35
37
|
}
|
|
36
38
|
export async function run(argv) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const B_LINES = [
|
|
2
|
+
"██████╗ ",
|
|
3
|
+
"██╔══██╗",
|
|
4
|
+
"██████╔╝",
|
|
5
|
+
"██╔══██╗",
|
|
6
|
+
"██████╔╝",
|
|
7
|
+
"╚═════╝ ",
|
|
8
|
+
];
|
|
9
|
+
export function attachBCommand(program) {
|
|
10
|
+
program
|
|
11
|
+
.command("b [extra]", { hidden: true })
|
|
12
|
+
.description("🅱️")
|
|
13
|
+
.action((extra) => {
|
|
14
|
+
const count = 1 + (extra ? extra.length : 0);
|
|
15
|
+
for (const line of B_LINES) {
|
|
16
|
+
console.log(Array(count).fill(line).join(" "));
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
package/dist/commands/browse.js
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
|
+
import * as readline from "node:readline/promises";
|
|
1
2
|
import { fail } from "../lib/command.js";
|
|
2
3
|
import { findExecutable, spawnPassthrough } from "../lib/process.js";
|
|
4
|
+
async function installBrowseCli() {
|
|
5
|
+
const npmPath = await findExecutable("npm");
|
|
6
|
+
if (!npmPath) {
|
|
7
|
+
fail([
|
|
8
|
+
"`npm` is not installed.",
|
|
9
|
+
"Install Node.js from https://nodejs.org to get npm,",
|
|
10
|
+
"then rerun `bb browse`.",
|
|
11
|
+
].join("\n"));
|
|
12
|
+
}
|
|
13
|
+
const exitCode = await spawnPassthrough(npmPath, [
|
|
14
|
+
"install",
|
|
15
|
+
"-g",
|
|
16
|
+
"@browserbasehq/browse-cli",
|
|
17
|
+
]);
|
|
18
|
+
if (exitCode !== 0) {
|
|
19
|
+
process.exitCode = exitCode;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
3
22
|
export function attachBrowseCommand(program) {
|
|
4
23
|
program
|
|
5
24
|
.command("browse")
|
|
@@ -8,18 +27,42 @@ export function attachBrowseCommand(program) {
|
|
|
8
27
|
.allowUnknownOption(true)
|
|
9
28
|
.allowExcessArguments(true)
|
|
10
29
|
.passThroughOptions()
|
|
30
|
+
.helpOption(false)
|
|
11
31
|
.action(async (args) => {
|
|
12
|
-
|
|
32
|
+
let browsePath = await findExecutable("browse");
|
|
13
33
|
if (!browsePath) {
|
|
14
|
-
|
|
34
|
+
console.log([
|
|
35
|
+
"",
|
|
15
36
|
"`browse` is not installed.",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
37
|
+
"Automate web browser interactions using natural language via CLI commands.",
|
|
38
|
+
"",
|
|
39
|
+
" npm install -g @browserbasehq/browse-cli",
|
|
18
40
|
"",
|
|
19
|
-
"Then rerun your `bb browse ...` command.",
|
|
20
41
|
].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;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
rl.close();
|
|
54
|
+
}
|
|
55
|
+
await installBrowseCli();
|
|
56
|
+
browsePath = await findExecutable("browse");
|
|
57
|
+
if (!browsePath) {
|
|
58
|
+
fail([
|
|
59
|
+
"Installation succeeded but `browse` was not found on PATH.",
|
|
60
|
+
"You may need to restart your shell, then rerun your command.",
|
|
61
|
+
].join("\n"));
|
|
62
|
+
}
|
|
21
63
|
}
|
|
22
|
-
const
|
|
64
|
+
const forwardArgs = args && args.length > 0 ? args : ["--help"];
|
|
65
|
+
const exitCode = await spawnPassthrough(browsePath, forwardArgs);
|
|
23
66
|
if (exitCode !== 0) {
|
|
24
67
|
process.exitCode = exitCode;
|
|
25
68
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addCommonApiOptions, createBrowserbaseClient,
|
|
1
|
+
import { addCommonApiOptions, createBrowserbaseClient, outputJson, parseOptionalJsonObjectArg, requestBrowserbase, requestBrowserbaseJson, } from "../lib/command.js";
|
|
2
2
|
export function attachContextsCommand(program) {
|
|
3
3
|
const contexts = program
|
|
4
4
|
.command("contexts")
|
|
@@ -12,7 +12,7 @@ export function attachContextsCommand(program) {
|
|
|
12
12
|
.description("Create a context.")
|
|
13
13
|
.option("--body <body>", "Optional JSON request body.")).action(async (options) => {
|
|
14
14
|
const client = createBrowserbaseClient(options);
|
|
15
|
-
const body =
|
|
15
|
+
const body = parseOptionalJsonObjectArg(options.body, "body");
|
|
16
16
|
outputJson(await client.contexts.create(body));
|
|
17
17
|
});
|
|
18
18
|
addCommonApiOptions(contexts
|
package/dist/commands/fetch.js
CHANGED
|
@@ -16,20 +16,15 @@ export function attachFetchCommand(program) {
|
|
|
16
16
|
});
|
|
17
17
|
if (options.output) {
|
|
18
18
|
await writeOutputFile(options.output, result.content);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
outputJson({
|
|
20
|
+
ok: true,
|
|
21
|
+
outputPath: options.output,
|
|
22
|
+
contentType: result.contentType,
|
|
23
|
+
statusCode: result.statusCode,
|
|
24
|
+
sizeBytes: Buffer.byteLength(result.content, "utf8"),
|
|
25
|
+
});
|
|
24
26
|
return;
|
|
25
27
|
}
|
|
26
|
-
|
|
27
|
-
outputJson(result);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
process.stdout.write(result.content);
|
|
31
|
-
if (!result.content.endsWith("\n")) {
|
|
32
|
-
process.stdout.write("\n");
|
|
33
|
-
}
|
|
28
|
+
outputJson(result);
|
|
34
29
|
});
|
|
35
30
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { addCommonApiOptions, outputJson, requestBrowserbaseJson, writeOutputFile, } from "../lib/command.js";
|
|
2
|
+
export function attachSearchCommand(program) {
|
|
3
|
+
addCommonApiOptions(program
|
|
4
|
+
.command("search <query>")
|
|
5
|
+
.description("Search the web using the Browserbase Search API.")
|
|
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) => {
|
|
8
|
+
const numResults = options.numResults ? parseInt(options.numResults, 10) : undefined;
|
|
9
|
+
const result = await requestBrowserbaseJson(options, "/v1/search", {
|
|
10
|
+
method: "POST",
|
|
11
|
+
headers: { "Content-Type": "application/json" },
|
|
12
|
+
body: JSON.stringify({ query, numResults }),
|
|
13
|
+
});
|
|
14
|
+
if (options.output) {
|
|
15
|
+
await writeOutputFile(options.output, JSON.stringify(result, null, 2));
|
|
16
|
+
outputJson({
|
|
17
|
+
ok: true,
|
|
18
|
+
outputPath: options.output,
|
|
19
|
+
requestId: result.requestId,
|
|
20
|
+
query: result.query,
|
|
21
|
+
resultCount: result.results.length,
|
|
22
|
+
});
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
outputJson(result);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Option } from "commander";
|
|
2
|
-
import { addCommonApiOptions, createBrowserbaseClient, fail,
|
|
2
|
+
import { addCommonApiOptions, createBrowserbaseClient, fail, outputJson, parseOptionalJsonObjectArg, resolveUploadableFile, writeBinaryOutput, } from "../lib/command.js";
|
|
3
3
|
export function attachSessionsCommand(program) {
|
|
4
4
|
const sessions = program
|
|
5
5
|
.command("sessions")
|
|
@@ -11,7 +11,7 @@ export function attachSessionsCommand(program) {
|
|
|
11
11
|
addCommonApiOptions(sessions
|
|
12
12
|
.command("list")
|
|
13
13
|
.description("List sessions.")
|
|
14
|
-
.option("--q <q>",
|
|
14
|
+
.option("--q <q>", `Session metadata query (e.g. "user_metadata['env']:'staging'").`)).action(async (options) => {
|
|
15
15
|
const client = createBrowserbaseClient(options);
|
|
16
16
|
outputJson(await client.sessions.list(options.q ? { q: options.q } : {}));
|
|
17
17
|
});
|
|
@@ -26,7 +26,7 @@ export function attachSessionsCommand(program) {
|
|
|
26
26
|
.description("Create a session.")
|
|
27
27
|
.option("--body <body>", "Optional JSON request body.")).action(async (options) => {
|
|
28
28
|
const client = createBrowserbaseClient(options);
|
|
29
|
-
const body =
|
|
29
|
+
const body = parseOptionalJsonObjectArg(options.body, "body");
|
|
30
30
|
outputJson(await client.sessions.create(body));
|
|
31
31
|
});
|
|
32
32
|
addCommonApiOptions(sessions
|
|
@@ -38,10 +38,10 @@ export function attachSessionsCommand(program) {
|
|
|
38
38
|
.option("--body <body>", "Optional JSON request body. Merged with --status when provided.")).action(async (id, options) => {
|
|
39
39
|
const client = createBrowserbaseClient(options);
|
|
40
40
|
const sessionId = id || fail("Session ID is required.");
|
|
41
|
-
const body =
|
|
41
|
+
const body = {
|
|
42
42
|
...parseOptionalJsonObjectArg(options.body, "body"),
|
|
43
43
|
status: options.status ?? "REQUEST_RELEASE",
|
|
44
|
-
}
|
|
44
|
+
};
|
|
45
45
|
outputJson(await client.sessions.update(sessionId, body));
|
|
46
46
|
});
|
|
47
47
|
addCommonApiOptions(sessions
|
package/dist/lib/command.js
CHANGED
|
@@ -17,10 +17,7 @@ export function fail(message, exitCode = 1) {
|
|
|
17
17
|
export function addCommonApiOptions(command) {
|
|
18
18
|
return command
|
|
19
19
|
.option("--api-key <apiKey>", "Override the Browserbase API key.")
|
|
20
|
-
.option("--
|
|
21
|
-
.option("--base-url <baseUrl>", "Override the Browserbase API base URL.")
|
|
22
|
-
.option("--json", "Print JSON output.")
|
|
23
|
-
.option("--verbose", "Enable verbose logging.");
|
|
20
|
+
.option("--base-url <baseUrl>", "Override the Browserbase API base URL.");
|
|
24
21
|
}
|
|
25
22
|
export function addFunctionsApiOptions(command, options = {}) {
|
|
26
23
|
const { includeProjectId = false, includeVerbose = false } = options;
|
|
@@ -91,12 +88,6 @@ export function parseOptionalJsonValueArg(rawValue, label) {
|
|
|
91
88
|
fail(`Invalid JSON for ${label}: ${error.message}`);
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
|
-
export function mergeProjectIdIntoBody(projectId, body) {
|
|
95
|
-
if (projectId && body.projectId === undefined) {
|
|
96
|
-
return { ...body, projectId };
|
|
97
|
-
}
|
|
98
|
-
return body;
|
|
99
|
-
}
|
|
100
91
|
export function outputJson(value) {
|
|
101
92
|
console.log(JSON.stringify(value, null, 2));
|
|
102
93
|
}
|
|
@@ -64,7 +64,7 @@ export async function initFunctionsProject({ projectName, packageManager, }) {
|
|
|
64
64
|
type: "module",
|
|
65
65
|
scripts: {
|
|
66
66
|
dev: "bb functions dev index.ts",
|
|
67
|
-
|
|
67
|
+
deploy: "bb functions publish index.ts",
|
|
68
68
|
},
|
|
69
69
|
};
|
|
70
70
|
await writeFile(join(projectRoot, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
|
|
@@ -90,6 +90,7 @@ export async function initFunctionsProject({ projectName, packageManager, }) {
|
|
|
90
90
|
console.log(` cd ${projectName}`);
|
|
91
91
|
console.log(" Edit .env with your Browserbase credentials");
|
|
92
92
|
console.log(` ${packageManager === "pnpm" ? "pnpm" : "npm run"} dev`);
|
|
93
|
+
console.log(` ${packageManager === "pnpm" ? "pnpm run deploy" : "npm run deploy"}`);
|
|
93
94
|
}
|
|
94
95
|
function runPackageManager(packageManager, args, cwd) {
|
|
95
96
|
const result = spawnSync(packageManager, args, {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import archiver from "archiver";
|
|
2
2
|
import ignore from "ignore";
|
|
3
|
-
import { createWriteStream, existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { copyFileSync, createWriteStream, existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
4
4
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
5
6
|
import { tmpdir } from "node:os";
|
|
6
7
|
import { join, relative } from "node:path";
|
|
7
8
|
import { randomUUID } from "node:crypto";
|
|
@@ -25,21 +26,24 @@ const defaultIgnorePatterns = [
|
|
|
25
26
|
export async function publishFunction(options) {
|
|
26
27
|
const entrypoint = await resolveEntrypoint(options.entrypoint);
|
|
27
28
|
const config = resolveFunctionsProjectConfig(options);
|
|
28
|
-
const archivePath = await createArchive(process.cwd(), options.dryRun);
|
|
29
|
-
const metadata = {
|
|
30
|
-
entrypoint: relative(process.cwd(), entrypoint),
|
|
31
|
-
projectId: config.projectId,
|
|
32
|
-
};
|
|
33
29
|
if (options.dryRun) {
|
|
30
|
+
const { archivePath, entries } = await createArchive(process.cwd());
|
|
34
31
|
console.log(JSON.stringify({
|
|
35
32
|
dryRun: true,
|
|
36
33
|
apiUrl: config.apiUrl,
|
|
37
34
|
projectId: config.projectId,
|
|
38
|
-
entrypoint:
|
|
35
|
+
entrypoint: relative(process.cwd(), entrypoint),
|
|
39
36
|
archivePath,
|
|
37
|
+
files: entries,
|
|
40
38
|
}, null, 2));
|
|
41
39
|
return;
|
|
42
40
|
}
|
|
41
|
+
ensureNpmLockfile(process.cwd());
|
|
42
|
+
const { archivePath, entries } = await createArchive(process.cwd());
|
|
43
|
+
const metadata = {
|
|
44
|
+
entrypoint: relative(process.cwd(), entrypoint),
|
|
45
|
+
projectId: config.projectId,
|
|
46
|
+
};
|
|
43
47
|
const formData = new FormData();
|
|
44
48
|
formData.append("metadata", JSON.stringify(metadata));
|
|
45
49
|
formData.append("archive", new Blob([await readFile(archivePath)], {
|
|
@@ -69,13 +73,10 @@ export async function publishFunction(options) {
|
|
|
69
73
|
process.exitCode = 1;
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
|
-
async function createArchive(root
|
|
76
|
+
async function createArchive(root) {
|
|
73
77
|
const archivePath = join(tmpdir(), `browserbase-functions-${randomUUID()}.tar.gz`);
|
|
74
78
|
const ignoreMatcher = await loadIgnoreMatcher(root);
|
|
75
79
|
const entries = await listArchiveEntries(root, root, ignoreMatcher);
|
|
76
|
-
if (dryRun) {
|
|
77
|
-
console.log(JSON.stringify({ files: entries }, null, 2));
|
|
78
|
-
}
|
|
79
80
|
await new Promise((resolve, reject) => {
|
|
80
81
|
const output = createWriteStream(archivePath);
|
|
81
82
|
const archive = archiver("tar", {
|
|
@@ -97,7 +98,27 @@ async function createArchive(root, dryRun) {
|
|
|
97
98
|
}
|
|
98
99
|
archive.finalize().catch(reject);
|
|
99
100
|
});
|
|
100
|
-
return archivePath;
|
|
101
|
+
return { archivePath, entries };
|
|
102
|
+
}
|
|
103
|
+
function ensureNpmLockfile(root) {
|
|
104
|
+
if (existsSync(join(root, "package-lock.json"))) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Generate in a temp directory to avoid conflicts with pnpm's node_modules
|
|
108
|
+
// structure, then copy the lockfile back.
|
|
109
|
+
const tmpDir = join(tmpdir(), `bb-lockgen-${randomUUID()}`);
|
|
110
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
111
|
+
copyFileSync(join(root, "package.json"), join(tmpDir, "package.json"));
|
|
112
|
+
const result = spawnSync("npm", ["install", "--package-lock-only"], {
|
|
113
|
+
cwd: tmpDir,
|
|
114
|
+
stdio: "pipe",
|
|
115
|
+
});
|
|
116
|
+
if (result.status !== 0) {
|
|
117
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
118
|
+
fail("Failed to generate package-lock.json. The build server requires an npm lockfile.");
|
|
119
|
+
}
|
|
120
|
+
copyFileSync(join(tmpDir, "package-lock.json"), join(root, "package-lock.json"));
|
|
121
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
101
122
|
}
|
|
102
123
|
async function loadIgnoreMatcher(root) {
|
|
103
124
|
const matcher = ignore();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { stat } from "node:fs/promises";
|
|
3
3
|
import { extname, resolve } from "node:path";
|
|
4
|
-
import { fail,
|
|
4
|
+
import { fail, resolveApiKey, resolveProjectId } from "../command.js";
|
|
5
5
|
const defaultFunctionsApiUrl = "https://api.browserbase.com";
|
|
6
6
|
export function resolveFunctionsApiConfig(args) {
|
|
7
7
|
return {
|
|
@@ -103,14 +103,3 @@ export function ensureCommand(command) {
|
|
|
103
103
|
fail(`${command} is required but was not found on PATH.`);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
export function printFunctionsJsonOrMessage(value, options = {}) {
|
|
107
|
-
if (options.json) {
|
|
108
|
-
outputJson(value);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
if (options.message) {
|
|
112
|
-
console.log(options.message);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
outputJson(value);
|
|
116
|
-
}
|
package/package.json
CHANGED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { openUrl } from "../lib/open.js";
|
|
2
|
-
export function attachDashboardCommand(program) {
|
|
3
|
-
program
|
|
4
|
-
.command("dashboard")
|
|
5
|
-
.description("Open the Browserbase web dashboard for your project.")
|
|
6
|
-
.action(async () => {
|
|
7
|
-
const url = "http://browserbase.com/overview";
|
|
8
|
-
await openUrl(url);
|
|
9
|
-
console.log(`Opened ${url}`);
|
|
10
|
-
});
|
|
11
|
-
}
|