Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions examples/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# ----------------------------------------------------------------------------
#
# :GUIDE:
#
# Copy `.env.example` to `.env` to run all examples without any
# additional setup. You can also manually export variables
# and run each example separately.
#
# ----------------------------------------------------------------------------

# API ------------------------------------------------------------------------

# Browser Use API Key
BROWSER_USE_API_KEY=""

# Webhooks -------------------------------------------------------------------

# NOTE: Use something simple in development. In production, Browser Use Cloud
# will give you the production secret.
SECRET_KEY="secret"

# ----------------------------------------------------------------------------
5 changes: 4 additions & 1 deletion examples/demo.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#!/usr/bin/env -S npm run tsn -T

import { BrowserUse } from 'browser-use-sdk';
import { spinner } from './utils';

import { env, spinner } from './utils';

env();

// gets API Key from environment variable BROWSER_USE_API_KEY
const browseruse = new BrowserUse();
Expand Down
4 changes: 4 additions & 0 deletions examples/stream-zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import { BrowserUse } from 'browser-use-sdk';
import z from 'zod';

import { env } from './utils';

env();

const HackerNewsResponse = z.object({
title: z.string(),
url: z.string(),
Expand Down
4 changes: 4 additions & 0 deletions examples/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import { BrowserUse } from 'browser-use-sdk';

import { env } from './utils';

env();

async function main() {
// gets API Key from environment variable BROWSER_USE_API_KEY
const browseruse = new BrowserUse();
Expand Down
6 changes: 6 additions & 0 deletions examples/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import dotenv from '@dotenvx/dotenvx';

const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];

/**
Expand Down Expand Up @@ -26,3 +28,7 @@ export function spinner(renderText: () => string): () => void {
}
};
}

export function env() {
dotenv.config({ path: [__dirname + '/.env', '.env'] });
}
177 changes: 177 additions & 0 deletions examples/webhook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#!/usr/bin/env -S npm run tsn -T

import { BrowserUse } from 'browser-use-sdk';
import {
verifyWebhookEventSignature,
type WebhookAgentTaskStatusUpdatePayload,
} from 'browser-use-sdk/lib/webhooks';
import { createServer, IncomingMessage, type Server, type ServerResponse } from 'http';

import { env } from './utils';

env();

const PORT = 3000;
const WAIT_FOR_TASK_FINISH_TIMEOUT = 60_000;

// Environment ---------------------------------------------------------------

const SECRET_KEY = process.env['SECRET_KEY'];

// API -----------------------------------------------------------------------

// gets API Key from environment variable BROWSER_USE_API_KEY
const browseruse = new BrowserUse();

//

const whServerRef: { current: Server | null } = { current: null };

async function main() {
if (!SECRET_KEY) {
console.error('SECRET_KEY is not set');
process.exit(1);
}

console.log('Starting Browser Use Webhook Example');
console.log('Run `browser-use listen --dev http://localhost:3000/webhook`!');

// Start a Webhook Server

const callback: { current: ((event: WebhookAgentTaskStatusUpdatePayload) => Promise<void>) | null } = {
current: null,
};

const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
if (req.method === 'POST' && req.url === '/webhook') {
let body = '';

req.on('data', (chunk) => {
body += chunk.toString();
});

req.on('end', async () => {
try {
const signature = req.headers['x-browser-use-signature'] as string;
const timestamp = req.headers['x-browser-use-timestamp'] as string;

const event = await verifyWebhookEventSignature(
{
evt: body,
signature,
timestamp,
},
{
secret: SECRET_KEY,
},
);

if (!event.ok) {
console.log('❌ Invalid webhook signature');
console.log(body);
console.log(signature, 'signature');
console.log(timestamp, 'timestamp');
console.log(SECRET_KEY, 'SECRET_KEY');

res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid signature' }));
return;
}

switch (event.event.type) {
case 'agent.task.status_update':
await callback.current?.(event.event.payload);

res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ received: true }));
break;
case 'test':
console.log('🧪 Test webhook received');

res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ received: true }));
break;
default:
console.log('🧪 Unknown webhook received');

res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ received: true }));
break;
}
} catch (error) {
console.error(error);
}
});
} else if (req.method === 'GET' && req.url === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', timestamp: new Date().toISOString() }));
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
}
});

whServerRef.current = server;

server.listen(PORT, () => {
console.log(`🌐 Webhook server listening on port ${PORT}`);
console.log(`🔗 Health check: http://localhost:${PORT}/health`);
});

await new Promise((resolve) => setTimeout(resolve, 1000));

// Create Task
console.log('📝 Creating a new task...');
const task = await browseruse.tasks.create({
task: "What's the weather like in San Francisco and what's the current temperature?",
});

console.log(`🔗 Task created: ${task.id}`);

await new Promise<void>((resolve, reject) => {
// NOTE: We set a timeout so we can catch it when the task is stuck
// and stop the example.
const interval = setTimeout(() => {
reject(new Error('Task creation timed out'));
}, WAIT_FOR_TASK_FINISH_TIMEOUT);

// NOTE: We attach the callback to the current reference so we can receive updates from the server.
callback.current = async (payload) => {
if (payload.task_id !== task.id) {
return;
}

console.log('🔄 Task status updated:', payload.status);

if (payload.status === 'finished') {
clearTimeout(interval);
resolve();
}
};
}).catch((error) => {
console.error(error);
process.exit(1);
});

// Fetch final task result
const status = await browseruse.tasks.retrieve(task.id);

console.log('🎯 Final Task Status');
console.log('OUTPUT:');
console.log(status.doneOutput);

server.close();
}

// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('\n👋 Shutting down gracefully...');
whServerRef.current?.close();
process.exit(0);
});

//

if (require.main === module) {
main().catch(console.error);
}
5 changes: 4 additions & 1 deletion examples/zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import { BrowserUse } from 'browser-use-sdk';
import { z } from 'zod';
import { spinner } from './utils';

import { env, spinner } from './utils';

env();

// gets API Key from environment variable BROWSER_USE_API_KEY
const browseruse = new BrowserUse();
Expand Down
12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,26 @@
"format": "./scripts/format",
"prepare": "if ./scripts/utils/check-is-in-git-install.sh; then ./scripts/build && ./scripts/utils/git-swap.sh; fi",
"tsn": "ts-node -r tsconfig-paths/register",
"cli": "ts-node -r tsconfig-paths/register --cwd $PWD ./src/lib/bin/cli.ts",
"lint": "./scripts/lint",
"fix": "./scripts/format"
},
"dependencies": {},
"bin": {
"browser-use": "./dist/lib/bin/cli.js"
},
"dependencies": {
"@dotenvx/dotenvx": "^1.48.4",
"fast-json-stable-stringify": "^2.1.0"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.0",
"@swc/core": "^1.3.102",
"@swc/jest": "^0.2.29",
"@types/jest": "^29.4.0",
"@types/node": "^20.17.6",
"@types/node": "^24.3.0",
"@typescript-eslint/eslint-plugin": "8.31.1",
"@typescript-eslint/parser": "8.31.1",
"commander": "^14.0.0",
"eslint": "^9.20.1",
"eslint-plugin-prettier": "^5.4.1",
"eslint-plugin-unused-imports": "^4.1.4",
Expand Down
63 changes: 63 additions & 0 deletions src/lib/bin/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as dotenv from '@dotenvx/dotenvx';

import { BrowserUse } from '../../';

const API_KEY_ENV_VAR_KEY = 'BROWSER_USE_API_KEY';

/**
* Creates a new BrowserUse client with the API key from the environment variable.
*/
export function createBrowserUseClient() {
let apiKey: string | null = null;

if (process.env[API_KEY_ENV_VAR_KEY]) {
apiKey = process.env[API_KEY_ENV_VAR_KEY];
}

if (apiKey == null) {
const env = dotenv.config({ path: '.env' });

const envApiKey = env.parsed?.[API_KEY_ENV_VAR_KEY];

if (envApiKey) {
apiKey = envApiKey;
}
}

if (apiKey == null) {
console.error(`Missing ${API_KEY_ENV_VAR_KEY} environment variable!`);
process.exit(1);
}

return new BrowserUse({ apiKey });
}

const SECRET_ENV_VAR_KEY = 'SECRET_KEY';

/**
* Loads the Browser Use webhook secret from the environment variable.
*/
export function getBrowserUseWebhookSecret() {
let secret: string | null = null;

if (process.env[SECRET_ENV_VAR_KEY]) {
secret = process.env[SECRET_ENV_VAR_KEY];
}

if (secret == null) {
const env = dotenv.config({ path: '.env' });

const envSecret = env.parsed?.[SECRET_ENV_VAR_KEY];

if (envSecret) {
secret = envSecret;
}
}

if (secret == null) {
console.error(`Missing ${SECRET_ENV_VAR_KEY} environment variable!`);
process.exit(1);
}

return secret;
}
11 changes: 11 additions & 0 deletions src/lib/bin/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env -S npm run tsn -T

import { program } from 'commander';
import { listen } from './commands/listen';

program
.name('browser-use')
.description('CLI to some JavaScript string utilities')
.version('0.8.0')
.addCommand(listen)
.parse(process.argv);
Loading