@browserbasehq/sdk-functions 0.0.5 → 0.0.6
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 +203 -0
- package/dist/cli.js +46 -32
- package/dist/cli.js.map +1 -1
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Browserbase Functions Node SDK
|
|
2
|
+
|
|
3
|
+
[](https://npmjs.org/package/@browserbasehq/sdk-functions)
|
|
4
|
+
|
|
5
|
+
The Browserbase Functions SDK lets you define, develop, and deploy serverless browser automation functions on [Browserbase](https://browserbase.com). Each function gets a managed browser session — write your automation logic, test it locally, and publish it to the cloud.
|
|
6
|
+
|
|
7
|
+
The full documentation can be found on [docs.browserbase.com](https://docs.browserbase.com/functions/quickstart).
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm add @browserbasehq/sdk-functions
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
or with npm:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install @browserbasehq/sdk-functions
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
Scaffold a new project with the CLI:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pnpm dlx @browserbasehq/sdk-functions init my-project
|
|
27
|
+
cd my-project
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Add your Browserbase credentials to `.env`:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
BROWSERBASE_API_KEY=your_api_key_here
|
|
34
|
+
BROWSERBASE_PROJECT_ID=your_project_id_here
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Start the local development server:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
pnpm bb dev index.ts
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
When ready, publish to Browserbase:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
pnpm bb publish index.ts
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
### Basic Function
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { defineFn } from "@browserbasehq/sdk-functions";
|
|
55
|
+
|
|
56
|
+
defineFn("hello-world", async () => {
|
|
57
|
+
return { message: "Hello from Browserbase!" };
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Browser Automation
|
|
62
|
+
|
|
63
|
+
Every function receives a `context` with a managed browser session. Connect to it with Playwright:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { defineFn } from "@browserbasehq/sdk-functions";
|
|
67
|
+
import { chromium } from "playwright-core";
|
|
68
|
+
|
|
69
|
+
defineFn("scrape-titles", async (context) => {
|
|
70
|
+
const browser = await chromium.connectOverCDP(context.session.connectUrl);
|
|
71
|
+
const page = browser.contexts()[0]!.pages()[0]!;
|
|
72
|
+
|
|
73
|
+
await page.goto("https://news.ycombinator.com");
|
|
74
|
+
const titles = await page.$$eval(".titleline > a", (els) =>
|
|
75
|
+
els.slice(0, 5).map((el) => el.textContent),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return { titles };
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Parameter Validation
|
|
83
|
+
|
|
84
|
+
Use [Zod](https://zod.dev) schemas to validate parameters passed to your function:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { defineFn } from "@browserbasehq/sdk-functions";
|
|
88
|
+
import z from "zod";
|
|
89
|
+
|
|
90
|
+
defineFn(
|
|
91
|
+
"multiply",
|
|
92
|
+
async (_context, params) => {
|
|
93
|
+
return { result: params.a * params.b };
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
parametersSchema: z.object({
|
|
97
|
+
a: z.number(),
|
|
98
|
+
b: z.number(),
|
|
99
|
+
}),
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Custom Browser Configuration
|
|
105
|
+
|
|
106
|
+
Pass `sessionConfig` to customize the browser session (uses the same options as the [Browserbase SDK session create params](https://docs.browserbase.com/reference/api/create-a-session)):
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { defineFn } from "@browserbasehq/sdk-functions";
|
|
110
|
+
import { chromium } from "playwright-core";
|
|
111
|
+
|
|
112
|
+
defineFn(
|
|
113
|
+
"stealth-scraper",
|
|
114
|
+
async (context) => {
|
|
115
|
+
const browser = await chromium.connectOverCDP(context.session.connectUrl);
|
|
116
|
+
const page = browser.contexts()[0]!.pages()[0]!;
|
|
117
|
+
|
|
118
|
+
await page.goto("https://example.com");
|
|
119
|
+
return { content: await page.textContent("body") };
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
sessionConfig: {
|
|
123
|
+
browserSettings: { advancedStealth: true },
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## CLI Reference
|
|
130
|
+
|
|
131
|
+
The `bb` CLI is included with the package.
|
|
132
|
+
|
|
133
|
+
| Command | Description |
|
|
134
|
+
| ------------------------- | -------------------------------------------------------------- |
|
|
135
|
+
| `bb init [project-name]` | Scaffold a new project (defaults to `my-browserbase-function`) |
|
|
136
|
+
| `bb dev <entrypoint>` | Start a local development server |
|
|
137
|
+
| `bb publish <entrypoint>` | Deploy your function to Browserbase |
|
|
138
|
+
| `bb invoke <functionId>` | Invoke a deployed function |
|
|
139
|
+
|
|
140
|
+
### `bb init`
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
bb init my-project
|
|
144
|
+
bb init my-project --package-manager npm
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Options:
|
|
148
|
+
|
|
149
|
+
- `-p, --package-manager <manager>` — Package manager to use (`npm` or `pnpm`, defaults to `pnpm`)
|
|
150
|
+
|
|
151
|
+
### `bb dev`
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
bb dev index.ts
|
|
155
|
+
bb dev index.ts --port 3000
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Options:
|
|
159
|
+
|
|
160
|
+
- `-p, --port <number>` — Port to listen on (default: `14113`)
|
|
161
|
+
- `-h, --host <string>` — Host to bind to (default: `127.0.0.1`)
|
|
162
|
+
|
|
163
|
+
### `bb publish`
|
|
164
|
+
|
|
165
|
+
```sh
|
|
166
|
+
bb publish index.ts
|
|
167
|
+
bb publish index.ts --dry-run
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Options:
|
|
171
|
+
|
|
172
|
+
- `--dry-run` — Show what would be published without uploading
|
|
173
|
+
- `-u, --api-url <url>` — Custom API endpoint URL
|
|
174
|
+
|
|
175
|
+
### `bb invoke`
|
|
176
|
+
|
|
177
|
+
```sh
|
|
178
|
+
bb invoke <functionId>
|
|
179
|
+
bb invoke <functionId> --params '{"key": "value"}'
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Options:
|
|
183
|
+
|
|
184
|
+
- `-p, --params <json>` — JSON parameters to pass to the function
|
|
185
|
+
- `--no-wait` — Don't wait for the invocation to complete
|
|
186
|
+
- `--check-status <invocationId>` — Check the status of an existing invocation
|
|
187
|
+
- `-u, --api-url <url>` — Custom API endpoint URL
|
|
188
|
+
|
|
189
|
+
## Configuration
|
|
190
|
+
|
|
191
|
+
Set your Browserbase credentials as environment variables or in a `.env` file:
|
|
192
|
+
|
|
193
|
+
| Variable | Required | Description |
|
|
194
|
+
| ------------------------ | -------- | --------------------------- |
|
|
195
|
+
| `BROWSERBASE_API_KEY` | Yes | Your Browserbase API key |
|
|
196
|
+
| `BROWSERBASE_PROJECT_ID` | Yes | Your Browserbase project ID |
|
|
197
|
+
|
|
198
|
+
Get your API key and project ID from [browserbase.com](https://browserbase.com).
|
|
199
|
+
|
|
200
|
+
## Requirements
|
|
201
|
+
|
|
202
|
+
- Node.js 18+
|
|
203
|
+
- TypeScript >= 4.5
|
package/dist/cli.js
CHANGED
|
@@ -1,53 +1,67 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import a from 'chalk';
|
|
3
|
-
import
|
|
3
|
+
import 'dotenv/config';
|
|
4
|
+
import * as Q from 'fs';
|
|
4
5
|
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, readdirSync } from 'fs';
|
|
5
|
-
import * as
|
|
6
|
+
import * as Y from 'path';
|
|
6
7
|
import { dirname, resolve, join } from 'path';
|
|
7
|
-
import '
|
|
8
|
-
import Ge from 'archiver';
|
|
8
|
+
import vn from 'archiver';
|
|
9
9
|
import { Command } from 'commander';
|
|
10
10
|
import { createServer } from 'http';
|
|
11
11
|
import { randomUUID } from 'crypto';
|
|
12
|
-
import
|
|
13
|
-
import
|
|
12
|
+
import We from '@browserbasehq/sdk';
|
|
13
|
+
import R, { z } from 'zod';
|
|
14
14
|
import { execSync, spawn } from 'child_process';
|
|
15
15
|
import { createRequire } from 'module';
|
|
16
16
|
import { fileURLToPath } from 'url';
|
|
17
17
|
|
|
18
|
-
var
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
\u{
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
var ue=Object.defineProperty;var Je=(n,e,o)=>e in n?ue(n,e,{enumerable:true,configurable:true,writable:true,value:o}):n[e]=o;var E=(n,e)=>()=>(n&&(e=n(n=0)),e);var de=(n,e)=>{for(var o in e)ue(n,o,{get:e[o],enumerable:true});};var g=(n,e,o)=>Je(n,typeof e!="symbol"?e+"":e,o);function H(){let n=process.env.BROWSERBASE_API_KEY;return n||(console.error(a.red("Error: BROWSERBASE_API_KEY not found in environment variables.")),console.log(a.gray("Please set BROWSERBASE_API_KEY in your .env file or environment.")),process.exit(1)),n}function oe(){let n=process.env.BROWSERBASE_PROJECT_ID;return n||(console.error(a.red("Error: BROWSERBASE_PROJECT_ID not found in environment variables.")),console.log(a.gray("Please set BROWSERBASE_PROJECT_ID in your .env file or environment.")),process.exit(1)),n}function K(n){return n||process.env.BROWSERBASE_API_BASE_URL||hn}function te(n){n.startsWith("bb_")||console.warn(a.yellow("Warning: API key doesn't start with 'bb_'. Make sure you're using a valid Browserbase API key."));}function re(n){return {apiKey:H(),apiUrl:K(n?.apiUrl)}}var hn,be=E(()=>{hn="https://api.browserbase.com";});async function F(n){let e=`HTTP ${n.status}: ${n.statusText}`;try{let o=await n.json();if(typeof o=="object"&&o!==null&&("message"in o||"error"in o)){let t=o;e=t.message||t.error||e;}}catch{try{let o=await n.text();o&&(e=o);}catch{}}return e}function Se(n,e){let o=n instanceof Error?n.message:"Unknown error occurred";return n&&typeof n=="object"&&"code"in n&&n.code==="ECONNREFUSED"&&console.log(a.yellow(`
|
|
19
|
+
Cannot connect to ${e}. Make sure the API server is reachable.`)),o}async function G(n,e){try{let o=`${n.apiUrl}${e}`,t=await fetch(o,{method:"GET",headers:{"x-bb-api-key":n.apiKey}});if(!t.ok){let r=await F(t);return console.error(a.red(`API error: ${r}`)),null}return await t.json()}catch(o){return Se(o,n.apiUrl),null}}async function se(n,e,o){try{let t=`${n.apiUrl}${e}`,r=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","x-bb-api-key":n.apiKey},body:JSON.stringify(o)});return r.ok?{success:!0,data:await r.json()}:{success:!1,error:await F(r)}}catch(t){return {success:false,error:Se(t,n.apiUrl)}}}function ie(n){return n!=="PENDING"&&n!=="RUNNING"}function ae(n){return n!=="PENDING"&&n!=="RUNNING"}async function j(n,e,o,t){let r=t?.intervalMs??1e3,s=t?.maxAttempts??900,i=t?.waitingMessage??"Waiting for completion...",u=t?.timeoutMessage??"Still running after maximum wait time.";console.log(a.cyan(`
|
|
20
|
+
${i}`));for(let c=0;c<s;c++){let d=await n();if(!d)return console.error(a.red("Failed to get status")),null;if(process.stdout.write(`\r${a.gray(`Status: ${o(d)}... (${c+1}/${s})`)}`),e(d))return process.stdout.write("\r"+" ".repeat(70)+"\r"),d;await new Promise(h=>setTimeout(h,r));}return process.stdout.write("\r"+" ".repeat(70)+"\r"),console.error(a.yellow(u)),null}var Re=E(()=>{});var V=E(()=>{be();Re();});function ke(n){let e=H(),o=oe(),t=K(n.apiUrl),r=n.entrypoint||"main.ts",s=Y.resolve(r);Q.existsSync(s)||(console.error(a.red(`Error: Entrypoint file not found: ${s}`)),process.exit(1));let i=Y.extname(r).toLowerCase();return [".ts",".js",".mjs",".mts"].includes(i)||(console.error(a.red(`Error: Invalid entrypoint extension: ${i}. Must be .ts, .js, .mjs, or .mts`)),process.exit(1)),{apiKey:e,projectId:o,apiUrl:t,entrypoint:r,workingDirectory:process.cwd()}}function Ee(n){te(n.apiKey);}var Ce=E(()=>{V();});function wn(n){let e=Y.join(n,".gitignore"),o=["node_modules/**",".git/**",".env",".env.*","*.log",".DS_Store","dist/**","build/**","*.zip","*.tar","*.tar.gz",".vscode/**",".idea/**"];if(!Q.existsSync(e))return o;try{let r=Q.readFileSync(e,"utf-8").split(`
|
|
21
|
+
`).map(s=>s.trim()).filter(s=>s&&!s.startsWith("#")).map(s=>s.endsWith("/")?`${s}**`:s);return [...o,...r]}catch(t){return console.warn(a.yellow(t,"Warning: Could not read .gitignore file, using defaults")),o}}async function $e(n,e){return new Promise((o,t)=>{console.log(a.cyan("Creating archive..."));let r=vn("tar",{gzip:true,gzipOptions:{level:9}}),s=[],i=0;r.on("data",c=>{s.push(c);}),r.on("entry",c=>{if(!c.stats?.isDirectory()&&(i++,e?.dryRun)){let d=Y.relative(n,c.name);console.log(a.gray(` + ${d}`));}}),r.on("end",()=>{let c=Buffer.concat(s),d=(c.length/(1024*1024)).toFixed(2);console.log(a.green(`\u2713 Archive created: ${i} files, ${d} MB`)),o({buffer:c,size:c.length,fileCount:i});}),r.on("error",c=>{console.error(a.red(`Archive error: ${c.message}`)),t(c);}),r.on("warning",c=>{c.code==="ENOENT"?console.warn(a.yellow(`Warning: ${c.message}`)):t(c);});let u=wn(n);e?.dryRun&&(console.log(a.gray(`
|
|
22
|
+
Ignoring patterns:`)),u.forEach(c=>{console.log(a.gray(` - ${c}`));}),console.log(a.gray(`
|
|
23
|
+
Including files:`))),r.glob("**/*",{cwd:n,ignore:u,dot:true,follow:false}),r.finalize();})}function Be(n,e=50){let o=n/1048576;o>e&&(console.error(a.red(`Error: Archive size (${o.toFixed(2)} MB) exceeds maximum allowed size (${e} MB)`)),console.log(a.gray("Consider adding more patterns to .gitignore to reduce archive size")),process.exit(1));}var Ae=E(()=>{});async function Me(n,e,o){if(o?.dryRun)return console.log(a.cyan(`
|
|
24
|
+
[Dry run] Would upload to:`)),console.log(a.gray(` URL: ${n.apiUrl}/v1/functions/builds`)),console.log(a.gray(` Project ID: ${n.projectId}`)),console.log(a.gray(` Entrypoint: ${n.entrypoint}`)),console.log(a.gray(` Archive size: ${(e.length/(1024*1024)).toFixed(2)} MB`)),{success:true,message:"Dry run completed successfully"};console.log(a.cyan(`
|
|
25
|
+
Uploading build...`));try{let t=new FormData,r={entrypoint:n.entrypoint,projectId:n.projectId};t.append("metadata",JSON.stringify(r));let s=new Blob([e],{type:"application/gzip"});t.append("archive",s,"archive.tar.gz");let i=`${n.apiUrl}/v1/functions/builds`;console.log(a.gray(`Uploading to: ${i}`));let u=await fetch(i,{method:"POST",headers:{"x-bb-api-key":n.apiKey},body:t});if(!u.ok){let d=await F(u);return console.error(a.red(`Upload failed: ${d}`)),{success:!1,message:d}}let c={};try{let d=await u.json();typeof d=="object"&&d!==null&&(c=d);}catch{}return c.id?(console.log(a.green("\u2713 Build uploaded successfully")),console.log(a.gray(`Build ID: ${c.id}`)),{success:!0,buildId:c.id,message:"Build uploaded successfully"}):(console.error(a.red("Upload failed: No build ID received in response")),{success:!1,message:"No build ID received in response"})}catch(t){let r=t instanceof Error?t.message:"Unknown error occurred";return console.error(a.red(`Upload error: ${r}`)),t&&typeof t=="object"&&"code"in t&&t.code==="ECONNREFUSED"&&console.log(a.yellow(`
|
|
26
|
+
Cannot connect to ${n.apiUrl}. Make sure the API server is running.`)),{success:false,message:r}}}async function In(n,e){try{let o=`${n.apiUrl}/v1/functions/builds/${e}`,t=await fetch(o,{method:"GET",headers:{"x-bb-api-key":n.apiKey}});return t.ok?await t.json():(console.error(a.red(`Failed to get build status: HTTP ${t.status}`)),null)}catch(o){return console.error(a.red(`Error fetching build status: ${o instanceof Error?o.message:"Unknown error"}`)),null}}async function Ne(n,e,o){console.log(a.gray("(Builds typically take around 1 minute to complete)"));let t=await j(()=>In(n,e),r=>ie(r.status),r=>r.status,{intervalMs:2e3,maxAttempts:100,waitingMessage:"Waiting for build to complete...",timeoutMessage:"Build is still running after maximum wait time (~3 minutes). Please check the dashboard for the current build status."});return t&&(t.status==="COMPLETED"?console.log(a.green("\u2713 Build completed successfully")):t.status==="FAILED"&&console.error(a.red("\u2717 Build failed"))),t}var Te=E(()=>{V();});var je={};de(je,{publishFunction:()=>bn});function Fe(n){if(console.log(a.bold.cyan(`
|
|
27
|
+
\u{1F4E6} Build Details`)),console.log(a.gray("\u2500".repeat(50))),console.log(a.white(`Build ID: ${a.cyan(n.id)}`)),console.log(a.white(`Project ID: ${a.cyan(n.projectId)}`)),console.log(a.white(`Status: ${a.green(n.status)}`)),n.request?.entrypoint&&console.log(a.white(`Entrypoint: ${a.cyan(n.request.entrypoint)}`)),n.startedAt&&console.log(a.white(`Started: ${a.gray(new Date(n.startedAt).toLocaleString())}`)),n.endedAt&&(console.log(a.white(`Completed: ${a.gray(new Date(n.endedAt).toLocaleString())}`)),n.startedAt)){let e=new Date(n.endedAt).getTime()-new Date(n.startedAt).getTime(),o=Math.floor(e/1e3);console.log(a.white(`Duration: ${a.cyan(`${o} seconds`)}`));}n.expiresAt&&console.log(a.white(`Expires: ${a.gray(new Date(n.expiresAt).toLocaleString())}`)),n.builtFunctions&&n.builtFunctions.length>0?(console.log(a.bold.cyan(`
|
|
28
|
+
\u{1F680} Built Functions`)),console.log(a.gray("\u2500".repeat(50))),n.builtFunctions.forEach((e,o)=>{if(console.log(a.bold.white(`
|
|
29
|
+
${o+1}. ${e.name}`)),console.log(a.white(` Function ID: ${a.cyan(e.id)}`)),e.createdVersion&&(console.log(a.white(` Version ID: ${a.cyan(e.createdVersion.id)}`)),e.createdVersion.sessionCreateParams)){let t=e.createdVersion.sessionCreateParams;Object.keys(t).length>0&&(console.log(a.white(" Browser Settings:")),Object.entries(t).forEach(([s,i])=>{console.log(a.gray(` - ${s}: ${JSON.stringify(i)}`));}));}}),console.log(a.bold.cyan(`
|
|
30
|
+
\u2728 Next Steps`)),console.log(a.gray("\u2500".repeat(50))),console.log(a.white("Your functions are ready to be invoked! Invoke using cURL:")),n.builtFunctions.forEach(e=>{console.log(a.gray(`
|
|
30
31
|
curl --request POST \\`)),console.log(a.gray(` --url ${e.projectId?"https":"http"}://api.browserbase.com/v1/functions/${e.id}/invoke \\`)),console.log(a.gray(" --header 'Content-Type: application/json' \\")),console.log(a.gray(" --header 'x-bb-api-key: YOUR_API_KEY' \\")),console.log(a.gray(` --data '{"params": {}}'`));})):console.log(a.yellow(`
|
|
31
|
-
No functions were built. Please check your entrypoint and function exports.`));}async function
|
|
32
|
+
No functions were built. Please check your entrypoint and function exports.`));}async function bn(n){console.log(a.bold.cyan(`
|
|
32
33
|
Browserbase Functions - Publish
|
|
33
|
-
`));try{let e={};
|
|
34
|
+
`));try{let e={};n.entrypoint!==void 0&&(e.entrypoint=n.entrypoint),n.apiUrl!==void 0&&(e.apiUrl=n.apiUrl);let o=ke(e);Ee(o),console.log(a.gray(`Working directory: ${o.workingDirectory}`)),console.log(a.gray(`Entrypoint: ${o.entrypoint}`)),console.log(a.gray(`API URL: ${o.apiUrl}`)),console.log(a.gray(`Project ID: ${o.projectId}`)),n.dryRun&&console.log(a.yellow(`
|
|
34
35
|
[Dry run mode - no files will be uploaded]
|
|
35
|
-
`));let
|
|
36
|
-
\u2717 Publish failed`)),process.exit(1)),
|
|
36
|
+
`));let t={};n.dryRun!==void 0&&(t.dryRun=n.dryRun);let r=await $e(o.workingDirectory,t);Be(r.size);let s={};n.dryRun!==void 0&&(s.dryRun=n.dryRun);let i=await Me(o,r.buffer,s);if(i.success||(console.error(a.red(`
|
|
37
|
+
\u2717 Publish failed`)),process.exit(1)),n.dryRun)console.log(a.bold.green(`
|
|
37
38
|
\u2713 Dry run completed successfully!`)),console.log(a.cyan(`
|
|
38
39
|
Your function would have been published. Run without --dry-run to publish.`));else if(console.log(a.bold.green(`
|
|
39
|
-
\u2713 Function uploaded successfully!`)),
|
|
40
|
-
Build ID: ${
|
|
41
|
-
\u{1F389} Your function has been deployed and is ready for invocation!`)),
|
|
42
|
-
\u2717 Build failed during processing`)),
|
|
40
|
+
\u2713 Function uploaded successfully!`)),i.buildId){console.log(a.gray(`
|
|
41
|
+
Build ID: ${i.buildId}`));let u=await Ne(o,i.buildId);u?.status==="COMPLETED"?(console.log(a.bold.green(`
|
|
42
|
+
\u{1F389} Your function has been deployed and is ready for invocation!`)),Fe(u)):u?.status==="FAILED"?(console.error(a.red(`
|
|
43
|
+
\u2717 Build failed during processing`)),u&&Fe(u),process.exit(1)):console.log(a.yellow(`
|
|
43
44
|
Build status could not be determined. Check the dashboard for updates.`));}else console.log(a.cyan(`
|
|
44
45
|
Your function will be available for invocation once the build is processed.`));}catch(e){console.error(a.red(`
|
|
45
|
-
\u2717 Publish failed: ${e.message??"unknown error"}`)),process.exit(1);}}var
|
|
46
|
+
\u2717 Publish failed: ${e.message??"unknown error"}`)),process.exit(1);}}var De=E(()=>{Ce();Ae();Te();});var Ue={};de(Ue,{invoke:()=>Sn});function Oe(n){if(console.log(a.bold.cyan(`
|
|
47
|
+
\u{1F4CB} Invocation Details`)),console.log(a.gray("\u2500".repeat(50))),console.log(a.white(`Invocation ID: ${a.cyan(n.id)}`)),console.log(a.white(`Function ID: ${a.cyan(n.functionId)}`)),console.log(a.white(`Status: ${a.cyan(n.status)}`)),n.sessionId&&console.log(a.white(`Session ID: ${a.cyan(n.sessionId)}`)),n.startedAt&&console.log(a.white(`Started: ${a.gray(new Date(n.startedAt).toLocaleString())}`)),n.endedAt&&(console.log(a.white(`Ended: ${a.gray(new Date(n.endedAt).toLocaleString())}`)),n.startedAt)){let o=((new Date(n.endedAt).getTime()-new Date(n.startedAt).getTime())/1e3).toFixed(2);console.log(a.white(`Duration: ${a.cyan(`${o}s`)}`));}n.results&&Object.keys(n.results).length>0&&(console.log(a.bold.cyan(`
|
|
48
|
+
\u{1F4E6} Results`)),console.log(a.gray("\u2500".repeat(50))),console.log(JSON.stringify(n.results,null,2)));}async function Sn(n){console.log(a.bold.cyan(`
|
|
49
|
+
Browserbase Functions - Invoke
|
|
50
|
+
`));try{let e=re(n.apiUrl?{apiUrl:n.apiUrl}:void 0);if(n.checkStatus){console.log(a.gray(`Checking status for invocation: ${n.checkStatus}`));let i=await G(e,`/v1/functions/invocations/${n.checkStatus}`);i||(console.error(a.red(`
|
|
51
|
+
\u2717 Failed to get invocation status`)),process.exit(1)),Oe(i);return}let o={};if(n.params)try{o=JSON.parse(n.params);}catch{console.error(a.red("Error: Invalid JSON provided for --params flag")),console.log(a.gray(`Example: --params '{"key": "value"}'`)),process.exit(1);}console.log(a.gray(`Function ID: ${n.functionId}`)),console.log(a.gray(`API URL: ${e.apiUrl}`)),Object.keys(o).length>0&&console.log(a.gray(`Params: ${JSON.stringify(o)}`)),console.log(a.cyan(`
|
|
52
|
+
Invoking function...`));let t=`/v1/functions/${n.functionId}/invoke`;console.log(a.gray(`POST ${e.apiUrl}${t}`));let r=await se(e,t,{params:o});if(r.success||(console.error(a.red(`
|
|
53
|
+
Invoke failed: ${r.error}`)),process.exit(1)),console.log(a.green("\u2713 Function invoked successfully")),console.log(a.gray(`Invocation ID: ${r.data.id}`)),n.noWait){console.log(a.bold.green(`
|
|
54
|
+
\u2713 Function invoked!`)),console.log(a.gray(`
|
|
55
|
+
Invocation ID: ${r.data.id}`)),console.log(a.cyan(`
|
|
56
|
+
To check status later, run:
|
|
57
|
+
bb invoke ${n.functionId} --check-status ${r.data.id}`));return}let s=await j(()=>G(e,`/v1/functions/invocations/${r.data.id}`),i=>ae(i.status),i=>i.status,{intervalMs:1e3,maxAttempts:900,waitingMessage:"Waiting for invocation to complete...",timeoutMessage:"Invocation is still running after maximum wait time. Use 'bb invoke --check-status <invocationId>' to check later."});s||(console.error(a.red(`
|
|
58
|
+
\u2717 Failed to get final invocation status`)),process.exit(1)),s.status==="COMPLETED"?console.log(a.green("\u2713 Invocation completed successfully")):s.status==="FAILED"&&console.error(a.red("\u2717 Invocation failed")),Oe(s),s.status==="FAILED"&&process.exit(1);}catch(e){console.error(a.red(`
|
|
59
|
+
\u2717 Invoke failed: ${e.message??"unknown error"}`)),process.exit(1);}}var qe=E(()=>{V();});var O=class{constructor(e=false){g(this,"nextConnection",null);g(this,"invokeConnection",null);g(this,"currentRequestId",null);g(this,"currentFunctionName",null);g(this,"currentSessionId",null);g(this,"sessionCleanupCallback",null);g(this,"verbose");g(this,"runtimeConnectedOnce",false);this.verbose=e;}setSessionCleanupCallback(e){this.sessionCleanupCallback=e;}holdNextConnection(e){this.nextConnection&&(this.nextConnection.response.writeHead(503,{"Content-Type":"application/json"}),this.nextConnection.response.end(JSON.stringify({error:"Another runtime connected"}))),this.nextConnection={response:e,timestamp:Date.now()},this.runtimeConnectedOnce=true,this.verbose&&console.log(a.cyan("\u{1F50C} Function runtime connected, ready for invocations"));}triggerInvocation(e,o,t,r){if(!this.nextConnection)return this.verbose&&console.log(a.yellow("\u26A0\uFE0F No runtime connected to handle invocation")),false;if(this.invokeConnection)return this.verbose&&console.log(a.yellow("\u26A0\uFE0F Another invocation is already in progress")),false;let s=randomUUID();this.currentRequestId=s,this.currentFunctionName=e,this.currentSessionId=t.session.id,this.invokeConnection={response:r,timestamp:Date.now()};let i={functionName:e,params:o,context:t};return this.nextConnection.response.writeHead(200,{"Content-Type":"application/json","Lambda-Runtime-Aws-Request-Id":s,"Lambda-Runtime-Deadline-Ms":String(Date.now()+3e5),"Lambda-Runtime-Invoked-Function-Arn":`arn:aws:lambda:us-east-1:000000000000:function:${e}`}),this.nextConnection.response.end(JSON.stringify(i)),this.nextConnection=null,console.log(a.blue(`\u{1F680} Invoking function '${e}' (request-id: ${s})`)),true}completeWithSuccess(e,o){return e!==this.currentRequestId?(this.verbose&&console.log(a.yellow(`\u26A0\uFE0F Request ID mismatch: expected ${this.currentRequestId}, got ${e}`)),false):this.invokeConnection?(this.invokeConnection.response.writeHead(200,{"Content-Type":"application/json"}),this.invokeConnection.response.end(JSON.stringify(o??{})),console.log(a.green(`\u2713 Function '${this.currentFunctionName}' completed successfully`)),this.sessionCleanupCallback&&this.currentSessionId&&this.sessionCleanupCallback(this.currentSessionId).catch(t=>{console.error(a.red("Failed to cleanup session:"),t);}),this.invokeConnection=null,this.currentRequestId=null,this.currentFunctionName=null,this.currentSessionId=null,true):(this.verbose&&console.log(a.yellow("\u26A0\uFE0F No active invocation to complete")),false)}completeWithError(e,o){return e!==this.currentRequestId?(this.verbose&&console.log(a.yellow(`\u26A0\uFE0F Request ID mismatch: expected ${this.currentRequestId}, got ${e}`)),false):this.invokeConnection?(this.invokeConnection.response.writeHead(500,{"Content-Type":"application/json"}),this.invokeConnection.response.end(JSON.stringify({error:{message:o.errorMessage,type:o.errorType,stackTrace:o.stackTrace}})),console.log(a.red(`\u2717 Function '${this.currentFunctionName}' failed: ${o.errorMessage}`)),this.sessionCleanupCallback&&this.currentSessionId&&this.sessionCleanupCallback(this.currentSessionId).catch(t=>{console.error(a.red("Failed to cleanup session:"),t);}),this.invokeConnection=null,this.currentRequestId=null,this.currentFunctionName=null,this.currentSessionId=null,true):(this.verbose&&console.log(a.yellow("\u26A0\uFE0F No active invocation to complete")),false)}isReady(){return this.nextConnection!==null&&this.invokeConnection===null}hasActiveInvocation(){return this.invokeConnection!==null}getCurrentRequestId(){return this.currentRequestId}isRuntimeConnected(){return this.runtimeConnectedOnce&&this.nextConnection!==null}};var U=class{constructor(){g(this,"browserbaseClient",null);g(this,"projectId");g(this,"apiKey");g(this,"initialized",false);let e=process.env.BROWSERBASE_PROJECT_ID,o=process.env.BROWSERBASE_API_KEY;if(!e||!o)throw console.error(a.red(`\u2717 Browserbase credentials not found.
|
|
46
60
|
`)+a.red(` Please set BROWSERBASE_PROJECT_ID and BROWSERBASE_API_KEY in your .env file.
|
|
47
|
-
`)+a.gray(" Copy .env.example to .env and fill in your credentials.")),new Error("Missing Browserbase credentials");this.projectId=e,this.apiKey=
|
|
48
|
-
`).forEach(
|
|
49
|
-
`).forEach(
|
|
50
|
-
Make sure tsx is installed: npm install -g tsx or pnpm add tsx`)):console.error(a.red("\u2717 Failed to start runtime process:"),
|
|
51
|
-
\u{1F4E6} Shutting down...`)),await
|
|
61
|
+
`)+a.gray(" Copy .env.example to .env and fill in your credentials.")),new Error("Missing Browserbase credentials");this.projectId=e,this.apiKey=o;}async initialize(){this.initialized||(this.browserbaseClient=new We({apiKey:this.apiKey}),this.initialized=true,console.log(a.green("\u2713 Browserbase client initialized")));}async createSession(e){if(!this.browserbaseClient)throw new Error("Browser manager not initialized");console.log(a.cyan("Creating browser session..."));let o=await this.browserbaseClient.sessions.create({projectId:this.projectId,...e}),t={id:o.id,connectUrl:o.connectUrl};return console.log(a.green(`\u2713 Browser session created: ${t.id}`)),t}async closeSession(e){if(!this.browserbaseClient)throw new Error("Browser manager not initialized");try{console.log(a.cyan(`Closing browser session: ${e}...`)),await this.browserbaseClient.sessions.update(e,{projectId:this.projectId,status:"REQUEST_RELEASE"}),console.log(a.green(`\u2713 Browser session closed: ${e}`));}catch(o){console.warn(a.yellow(`\u26A0\uFE0F Could not close session ${e}:`),o instanceof Error?o.message:String(o));}}getProjectId(){return this.projectId}isInitialized(){return this.initialized}};var q=class{constructor(e){g(this,"manifests",new Map);g(this,"manifestsPath");this.manifestsPath=e||join(process.cwd(),".browserbase","functions","manifests");}loadManifests(){if(!existsSync(this.manifestsPath)){console.log(a.yellow(`\u26A0\uFE0F No ${this.manifestsPath} directory found`)),console.log(a.gray(" Run your entrypoint file first to generate manifests"));return}try{let o=readdirSync(this.manifestsPath).filter(t=>t.endsWith(".json"));for(let t of o){let r=join(this.manifestsPath,t),s=readFileSync(r,"utf-8"),i=JSON.parse(s);this.manifests.set(i.name,i),console.log(a.gray(` Loaded manifest for function: ${i.name}`));}this.manifests.size>0?console.log(a.green(`\u2713 Loaded ${this.manifests.size} function manifest(s)`)):console.log(a.yellow("\u26A0\uFE0F No function manifests found in .browserbase directory"));}catch(e){console.error(a.red("Failed to load function manifests:"),e);}}getManifest(e){return this.manifests.get(e)}getSize(){return this.manifests.size}hasManifest(e){return this.manifests.has(e)}getManifestNames(){return Array.from(this.manifests.keys())}};var _={async parseJsonBody(n){return new Promise((e,o)=>{let t="";n.on("data",r=>{t+=r.toString();}),n.on("end",()=>{try{let r=t?JSON.parse(t):{};e(r);}catch(r){o(new Error("Invalid JSON body",{cause:r}));}}),n.on("error",o);})},async parseAndValidate(n,e){let o=await this.parseJsonBody(n);return e.parse(o)}};var y={sendJson(n,e,o){n.writeHead(e,{"Content-Type":"application/json"}),n.end(JSON.stringify(o));},sendSuccess(n,e,o=200){let t={status:"success",...e!==void 0&&{data:e}};this.sendJson(n,o,t);},sendError(n,e,o=500,t,r){let s={error:e};t&&(s.message=t),r&&(s.details=r),this.sendJson(n,o,s);},sendBadRequest(n,e,o){this.sendError(n,"Bad Request",400,e,o);},sendNotFound(n,e){this.sendError(n,"Not Found",404,e);},sendInternalError(n,e="An internal error occurred",o){this.sendError(n,"Internal Server Error",500,e,o);},sendServiceUnavailable(n,e){this.sendError(n,"Service Unavailable",503,e);},sendAccepted(n,e){let o=e||{status:"accepted"};this.sendJson(n,202,o);}};var Ve=R.looseObject({id:R.string(),connectUrl:R.string()}),J=R.looseObject({session:Ve});var Ye=R.object({functionName:R.string().min(1),params:R.looseObject({}),context:J});R.object({requestId:R.string().min(1),event:Ye});var me=R.object({errorMessage:R.string().min(1),errorType:R.string().min(1),stackTrace:R.array(R.string().min(1))});var L=class{constructor(e){g(this,"bridge");g(this,"browserManager");g(this,"manifestStore");this.bridge=e.bridge,this.browserManager=e.browserManager,this.manifestStore=e.manifestStore,this.bridge.setSessionCleanupCallback(async o=>{await this.cleanupSession(o);});}async handleInvocationNext(e,o){this.bridge.holdNextConnection(o);}async handleFunctionInvoke(e,o,t){try{let r=z.object({functionName:z.string().optional(),params:z.unknown().default({}),context:J.optional()}),s=await _.parseAndValidate(e,r),i=t||s.functionName;if(!i){y.sendBadRequest(o,"Function name is required");return}let u=this.manifestStore.getManifest(i);if(!u){console.error(a.red(`\u2717 Function "${i}" not found in registry`)),console.error(a.gray(" Make sure the function is defined in your entrypoint file")),y.sendNotFound(o,`Function "${i}" not found in registry. Make sure it is defined with defineFn() in your entrypoint file.`);return}let c;try{console.log(a.cyan(`Creating browser session for ${i}...`));let w=u?.config?.sessionConfig||{};c=await this.browserManager.createSession(w);}catch(w){console.error(a.red("Failed to create browser session:"),w),y.sendInternalError(o,"Failed to create browser session",w instanceof Error?w.message:String(w));return}let d=s.context||{invocation:{id:randomUUID(),region:"local"},session:c};if(d.session=c,!this.bridge.triggerInvocation(i,s.params,d,o)){await this.cleanupSession(c.id),y.sendServiceUnavailable(o,this.bridge.hasActiveInvocation()?"Another invocation is in progress":"No runtime connected");return}}catch(r){r instanceof z.ZodError?y.sendBadRequest(o,"Invalid request body",r):(console.error(a.red("Error handling invoke:"),r),y.sendInternalError(o));}}async handleInvocationResponse(e,o,t){try{let r=await _.parseJsonBody(e);if(!this.bridge.completeWithSuccess(t,r)){y.sendBadRequest(o,"No matching invocation or request ID mismatch");return}y.sendAccepted(o);}catch(r){console.error(a.red("Error handling response:"),r),y.sendInternalError(o);}}async handleInvocationError(e,o,t){try{let r=await _.parseAndValidate(e,me);if(!this.bridge.completeWithError(t,r)){y.sendBadRequest(o,"No matching invocation or request ID mismatch");return}y.sendAccepted(o);}catch(r){r instanceof z.ZodError?y.sendBadRequest(o,"Invalid error format",r):(console.error(a.red("Error handling error report:"),r),y.sendInternalError(o));}}async cleanupSession(e){try{await this.browserManager.closeSession(e);}catch(o){console.error(a.red(`Failed to cleanup session ${e}:`),o);}}};async function Xe(n,e,o){let{handlers:t}=o,r=new URL(n.url||"",`http://${n.headers.host}`),s=n.method||"GET",i=r.pathname;if(console.log(a.gray(`[${s}] ${i}`)),e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type"),s==="OPTIONS"){e.writeHead(200),e.end();return}try{if(s==="GET"&&i==="/"){e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!0}));return}if(s==="GET"&&i==="/2018-06-01/runtime/invocation/next"){await t.handleInvocationNext(n,e);return}let u=i.match(/^\/v1\/functions\/([^/]+)\/invoke$/);if(s==="POST"&&u&&u[1]){let h=u[1];await t.handleFunctionInvoke(n,e,h);return}let c=i.match(/^\/2018-06-01\/runtime\/invocation\/([^/]+)\/response$/);if(s==="POST"&&c&&c[1]){let h=c[1];await t.handleInvocationResponse(n,e,h);return}let d=i.match(/^\/2018-06-01\/runtime\/invocation\/([^/]+)\/error$/);if(s==="POST"&&d&&d[1]){let h=d[1];await t.handleInvocationError(n,e,h);return}e.writeHead(404,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:"Not found"}));}catch(u){console.error(a.red("Server error:"),u),e.writeHead(500,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:"Internal server error"}));}}async function ye(n){let{port:e,host:o,handlers:s}=n,i=createServer(async(u,c)=>{await Xe(u,c,{handlers:s});});return new Promise((u,c)=>{i.listen(e,o,()=>{u(i);}),i.on("error",d=>{d.code==="EADDRINUSE"?c(new Error(`Port ${e} is already in use`)):d.code==="EACCES"?c(new Error(`Permission denied to bind to port ${e}`)):c(d);});})}var W=class{constructor(e){g(this,"process",null);g(this,"entrypoint");g(this,"runtimeApiUrl");g(this,"verbose");g(this,"isShuttingDown",false);this.entrypoint=e.entrypoint,this.runtimeApiUrl=e.runtimeApiUrl,this.verbose=e.verbose;}async start(){if(this.process)throw new Error("Process is already running");this.verbose&&(console.log(a.gray("Starting runtime process...")),console.log(a.gray(` Command: tsx watch --clear-screen=false ${this.entrypoint}`)),console.log(a.gray(` Working directory: ${process.cwd()}`)),console.log(a.gray(` Runtime API: ${this.runtimeApiUrl}`)));let o=createRequire(import.meta.url).resolve("tsx/cli"),t=["watch","--clear-screen=false",this.entrypoint];if(this.process=spawn(process.execPath,[o,...t],{cwd:process.cwd(),env:{...process.env,AWS_LAMBDA_RUNTIME_API:this.runtimeApiUrl,BB_FUNCTIONS_PHASE:"runtime",NODE_ENV:"local"},stdio:["ignore","pipe","pipe"]}),this.process.stdout?.on("data",r=>{r.toString().trim().split(`
|
|
62
|
+
`).forEach(i=>{i.trim()&&console.log(a.blue("[Runtime]"),i);});}),this.process.stderr?.on("data",r=>{r.toString().trim().split(`
|
|
63
|
+
`).forEach(i=>{i.trim()&&(i.includes("Watching for file changes")?console.log(a.green("\u2713 Runtime watching for file changes")):i.includes("Restarting")?console.log(a.yellow("\u21BB Runtime restarting due to file change...")):console.error(a.red("[Runtime Error]"),i));});}),this.process.on("exit",(r,s)=>{this.isShuttingDown||(r!==0?(console.error(a.red(`\u2717 Runtime process exited unexpectedly with code ${r}`)),s&&console.error(a.red(` Signal: ${s}`))):console.log(a.gray("Runtime process exited")),this.process=null);}),this.process.on("error",r=>{r.code==="ENOENT"?console.error(a.red("\u2717 Failed to start runtime: tsx not found"),a.yellow(`
|
|
64
|
+
Make sure tsx is installed: npm install -g tsx or pnpm add tsx`)):console.error(a.red("\u2717 Failed to start runtime process:"),r),this.process=null;}),await new Promise(r=>setTimeout(r,100)),!this.process||this.process.exitCode!==null)throw new Error("Failed to start runtime process");console.log(a.green("\u2713 Runtime process started"));}async stop(){if(this.process)return this.isShuttingDown=true,this.verbose&&console.log(a.gray("Stopping runtime process...")),new Promise(e=>{if(!this.process){e();return}let o=setTimeout(()=>{this.process&&(console.log(a.yellow("\u26A0\uFE0F Force killing runtime process")),this.process.kill("SIGKILL"));},5e3);this.process.on("exit",()=>{clearTimeout(o),this.process=null,console.log(a.green("\u2713 Runtime process stopped")),e();}),this.process.kill("SIGTERM");})}isRunning(){return this.process!==null&&this.process.exitCode===null}};async function he(n){let{entrypoint:e,port:o,host:t,verbose:r}=n;process.env.NODE_ENV==="production"&&console.warn(a.yellow("\u26A0\uFE0F Warning: Running dev server in production mode. This is not recommended."));let s=`${t}:${o}`;r&&console.log(a.gray(`Runtime API URL: ${s}`));let i=new O(r),u=new U;await u.initialize();let c=new q;c.loadManifests();let d=new L({bridge:i,browserManager:u,manifestStore:c}),h=new W({entrypoint:e,runtimeApiUrl:s,verbose:r}),w=null;try{w=await ye({port:o,host:t,bridge:i,browserManager:u,handlers:d}),console.log(a.green(`\u2713 Development server listening on http://${t}:${o}`)),console.log(a.cyan("Starting runtime process...")),await h.start();let D=1e4,_e=200,ze=Date.now(),le=!1;for(;Date.now()-ze<D;){if(i.isRuntimeConnected()){le=!0,console.log(a.green("\u2713 Runtime connected and ready")),c.loadManifests();break}await new Promise(X=>setTimeout(X,_e));}le||(console.log(a.yellow("\u26A0\uFE0F Runtime is taking longer than expected to connect...")),c.loadManifests());let pe=async()=>(console.log(a.cyan(`
|
|
65
|
+
\u{1F4E6} Shutting down...`)),await h.stop(),new Promise(X=>{w?.close(()=>{console.log(a.green("\u2713 Server closed")),X();});}));process.on("SIGINT",async()=>{await pe(),process.exit(0);}),process.on("SIGTERM",async()=>{await pe(),process.exit(0);});}catch(D){throw console.error(a.red("Failed to start:"),D),h.isRunning()&&await h.stop(),w&&w.close(),D}}var an=fileURLToPath(import.meta.url),ne=dirname(an);async function Ie(n){if(!yn(n.projectName))throw new Error(`Invalid project name "${n.projectName}". Project names must start with a letter and contain only letters, numbers, hyphens, and underscores.`);let e=resolve(process.cwd(),n.projectName);if(existsSync(e))throw new Error(`Directory "${n.projectName}" already exists. Please choose a different name or delete the existing directory.`);console.log(a.cyan(`\u{1F680} Creating new Browserbase Functions project: ${a.bold(n.projectName)}`)),mkdirSync(e,{recursive:true});try{cn(),existsSync(join(e,".git"))?console.log(a.yellow("\u2713 Git repository already exists")):(console.log(a.gray("Initializing git repository...")),execSync("git init",{cwd:e,stdio:"pipe"}),console.log(a.green("\u2713 Git repository initialized"))),gn(e),existsSync(join(e,"package.json"))?console.log(a.yellow("\u2713 package.json already exists")):(console.log(a.gray("Creating package.json...")),execSync("pnpm init",{cwd:e,stdio:"pipe"}),console.log(a.green("\u2713 package.json created")));let o=ln(n.packageManager);pn(e,o),console.log(a.gray("Installing dependencies...")),un(e,o),console.log(a.green("\u2713 Dependencies installed")),dn(e),existsSync(join(e,"tsconfig.json"))?console.log(a.yellow("\u2713 TypeScript configuration already exists")):(console.log(a.gray("Initializing TypeScript configuration...")),execSync(`${o==="pnpm"?"pnpm":"npx"} tsc --init`,{cwd:e,stdio:"pipe"}),fn(e),console.log(a.green("\u2713 TypeScript configuration created"))),mn(e),console.log(""),console.log(a.green.bold("\u2728 Project initialized successfully!")),console.log(""),console.log(a.cyan("Next steps:")),console.log(a.gray("1. Navigate to your project:")),console.log(a.white(` cd ${n.projectName}`)),console.log(a.gray("2. Add your Browserbase API key and project ID to .env")),console.log(a.gray("3. Run your function locally:")),console.log(a.white(` ${o==="pnpm"?"pnpm":"npx"} bb dev index.ts`)),console.log(a.gray("4. When ready, publish your function:")),console.log(a.white(` ${o==="pnpm"?"pnpm":"npx"} bb publish index.ts`)),console.log(""),console.log(a.gray("Learn more at https://browserbase.com/docs"));}catch(o){console.error(a.red("\u274C Initialization failed:"),o instanceof Error?o.message:o),process.exit(1);}}function cn(){let n=[{command:"node --version",name:"Node.js"},{command:"pnpm --version",name:"pnpm"},{command:"git --version",name:"git"}];for(let{command:e,name:o}of n)try{execSync(e,{stdio:"pipe"});}catch{throw new Error(`${o} is not installed. Please install ${o} and try again.`)}}function ln(n){if(n)return n;let e=process.env.npm_config_user_agent;return e&&e.includes("pnpm"),"pnpm"}function pn(n,e){let o=join(n,"package.json"),t=JSON.parse(readFileSync(o,"utf-8")),r;try{e==="pnpm"?(r=execSync("pnpm --version",{stdio:"pipe"}).toString().trim(),t.packageManager=`pnpm@${r}`):(r=execSync("npm --version",{stdio:"pipe"}).toString().trim(),t.packageManager=`npm@${r}`);}catch{t.packageManager=e==="pnpm"?"pnpm@9.0.0":"npm@10.0.0";}t.type="module",writeFileSync(o,JSON.stringify(t,null,2)),console.log(a.green(`\u2713 Package manager set to ${t.packageManager}`));}function un(n,e){let o=e==="pnpm"?"pnpm add":"npm install",t=e==="pnpm"?"pnpm add -D":"npm install --save-dev";console.log(a.gray(" Installing @browserbasehq/sdk-functions...")),execSync(`${o} @browserbasehq/sdk-functions`,{cwd:n,stdio:"pipe"}),console.log(a.gray(" Installing playwright-core...")),execSync(`${o} playwright-core`,{cwd:n,stdio:"pipe"}),console.log(a.gray(" Installing zod...")),execSync(`${o} zod`,{cwd:n,stdio:"pipe"}),console.log(a.gray(" Installing TypeScript and type definitions...")),execSync(`${t} typescript @types/node`,{cwd:n,stdio:"pipe"});}function dn(n){let e=join(n,".env");if(existsSync(e))console.log(a.yellow("\u2713 .env file already exists"));else {let o=join(ne,"templates",".env.template");copyFileSync(o,e),console.log(a.green("\u2713 .env file created"));}}function gn(n){let e=join(n,".gitignore");if(existsSync(e))console.log(a.yellow("\u2713 .gitignore file already exists"));else {let o=join(ne,"templates",".gitignore.template");copyFileSync(o,e),console.log(a.green("\u2713 .gitignore file created"));}}function mn(n){let e=join(n,"index.ts");if(existsSync(e))console.log(a.yellow("\u2713 index.ts already exists"));else {let o=join(ne,"templates","starter-function.ts.template");copyFileSync(o,e),console.log(a.green("\u2713 Starter function created (index.ts)"));}}function fn(n){let e=join(n,"tsconfig.json");try{let o=JSON.parse(readFileSync(e,"utf-8"));o.compilerOptions={...o.compilerOptions,target:"ES2022",module:"NodeNext",moduleResolution:"NodeNext",esModuleInterop:!0,forceConsistentCasingInFileNames:!0,strict:!0,skipLibCheck:!0,resolveJsonModule:!0},writeFileSync(e,JSON.stringify(o,null,2));}catch{console.log(a.yellow(" Using default TypeScript configuration"));}}function yn(n){return /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(n)}var M=new Command;M.name("bb").description("Browserbase Functions CLI").version("0.0.6");var ce=["npm","pnpm"];M.command("init [project-name]").description("Initialize a new Browserbase Functions project (defaults to my-browserbase-function)").option("-p, --package-manager <manager>",`Package manager to use (${ce.join(" or ")})`,"pnpm").action(async(n,e)=>{try{let o=n||"my-browserbase-function",t=e.packageManager.toLowerCase();ce.includes(t)||(console.error(a.red(`Error: Invalid package manager "${e.packageManager}"`)),console.error(a.gray(`Valid options are: ${ce.join(", ")}`)),process.exit(1)),await Ie({projectName:o,packageManager:t});}catch(o){console.error(a.red("Initialization failed:"),o),process.exit(1);}});M.command("dev").description("Start a local development server for testing Browserbase Functions").argument("<entrypoint>","Path to the TypeScript/JavaScript file that imports all your functions").option("-p, --port <number>","Port to listen on","14113").option("-h, --host <string>","Host to bind to","127.0.0.1").action(async(n,e)=>{try{let o=await import('fs'),t=await import('path'),r=t.resolve(n);o.existsSync(r)||(console.error(a.red(`Error: Entrypoint file not found: ${r}`)),process.exit(1));let s=t.extname(r);[".ts",".tsx",".js",".jsx",".mjs",".cjs"].includes(s)||(console.error(a.red("Error: Invalid file extension. Expected .ts, .tsx, .js, .jsx, .mjs, or .cjs")),process.exit(1));let i=parseInt(e.port,10);(isNaN(i)||i<1||i>65535)&&(console.error(a.red("Error: Invalid port number. Must be between 1 and 65535.")),process.exit(1)),console.log(a.cyan("Starting Browserbase Functions development server...")),console.log(a.gray(`Entrypoint: ${r}`)),await he({entrypoint:r,port:i,host:e.host,verbose:e.verbose});}catch(o){console.error(a.red("Failed to start development server:"),o),process.exit(1);}});M.command("publish").description("Publish your Browserbase Function to the cloud").argument("<entrypoint>","Path to the TypeScript/JavaScript file that imports all your functions").option("-u, --api-url <url>","API endpoint URL").option("--dry-run","Show what would be published without uploading").action(async(n,e)=>{try{let{publishFunction:o}=await Promise.resolve().then(()=>(De(),je));await o({entrypoint:n,apiUrl:e.apiUrl,dryRun:e.dryRun});}catch(o){console.error(a.red("Publish failed:"),o),process.exit(1);}});M.command("invoke").description("Invoke a deployed Browserbase Function").argument("<functionId>","The function ID to invoke").option("-p, --params <json>","JSON parameters to pass to the function").option("-u, --api-url <url>","API endpoint URL").option("--no-wait","Don't wait for the invocation to complete").option("--check-status <invocationId>","Check the status of an existing invocation").action(async(n,e)=>{try{let{invoke:o}=await Promise.resolve().then(()=>(qe(),Ue));await o({functionId:n,params:e.params,apiUrl:e.apiUrl,noWait:e.noWait,checkStatus:e.checkStatus});}catch(o){console.error(a.red("Invoke failed:"),o),process.exit(1);}});M.parse();
|
|
52
66
|
//# sourceMappingURL=cli.js.map
|
|
53
67
|
//# sourceMappingURL=cli.js.map
|