home / skills / browserbase / skills-old / skills-old
This skill helps you create, configure, and deploy browser automation functions using the browse CLI, enabling scheduled runs and web data extraction.
npx playbooks add skill browserbase/skills-old --skill skillsReview the files below or copy the command above to add this skill to your agents.
---
name: browse
description: Complete guide for creating and deploying browser automation functions using the browse CLI
homepage: https://browserbase.com
metadata: {"moltbot":{"emoji":"π","requires":{"bins":["browse","bb"],"npm":["@browserbasehq/browse-cli","@browserbasehq/sdk-functions"],"env":["BROWSERBASE_API_KEY","BROWSERBASE_PROJECT_ID"]},"primaryEnv":"BROWSERBASE_API_KEY"}}
---
# Browser Automation & Functions Skill
Complete guide for creating and deploying browser automation functions using the `browse` CLI.
## Installation
```bash
npm install -g @browserbasehq/browse-cli
```
Requires Chrome/Chromium installed on the system.
## When to Use
- User wants to automate a website task
- User needs to scrape data from a site
- User wants to create a Browserbase Function
- User wants to deploy automation to run on a schedule or via webhook
## Prerequisites
### Set Up Credentials
Get API key and Project ID from: https://browserbase.com/settings
```bash
export BROWSERBASE_API_KEY="your_api_key"
export BROWSERBASE_PROJECT_ID="your_project_id"
```
## Complete Workflow
### Step 1: Explore the Site Interactively
Start a browser session to understand the site structure:
```bash
browse open https://example.com
browse snapshot # Get DOM structure with refs
browse screenshot page.png # Visual inspection
```
Test interactions manually:
```bash
browse click @0-5
browse fill @0-6 "value"
browse stop # When done exploring
```
### Step 2: Initialize Function Project
```bash
pnpm dlx @browserbasehq/sdk-functions init my-automation
cd my-automation
```
Creates:
- `package.json` - Dependencies
- `.env` - Add credentials here
- `index.ts` - Function template
- `tsconfig.json` - TypeScript config
### Step 3: β οΈ FIX package.json IMMEDIATELY
**CRITICAL BUG**: The init command may generate incomplete `package.json` that causes deployment to fail with "No functions were built."
**REQUIRED FIX** - Update `package.json` before doing anything else:
```json
{
"name": "my-automation",
"version": "1.0.0",
"description": "My automation description",
"main": "index.js",
"type": "module",
"packageManager": "[email protected]",
"scripts": {
"dev": "pnpm bb dev index.ts",
"publish": "pnpm bb publish index.ts"
},
"dependencies": {
"@browserbasehq/sdk-functions": "^0.0.5",
"playwright-core": "^1.58.0"
},
"devDependencies": {
"@types/node": "^25.0.10",
"typescript": "^5.9.3"
}
}
```
**Key changes from generated file:**
- β
Add `description` and `main` fields
- β
Add `packageManager` field
- β
Change `"latest"` to pinned versions like `"^0.0.5"`
- β
Add `devDependencies` with TypeScript and types
Then install:
```bash
pnpm install
```
### Step 4: Write Automation Code
Edit `index.ts`:
```typescript
import { defineFn } from "@browserbasehq/sdk-functions";
import { chromium } from "playwright-core";
defineFn("my-automation", async (context) => {
const { session, params } = context;
console.log("Connecting to browser session:", session.id);
const browser = await chromium.connectOverCDP(session.connectUrl);
const page = browser.contexts()[0]!.pages()[0]!;
// Your automation here
await page.goto("https://example.com");
await page.waitForLoadState("domcontentloaded");
// Extract data
const data = await page.evaluate(() => {
// Complex extraction logic
return Array.from(document.querySelectorAll('.item')).map(el => ({
title: el.querySelector('.title')?.textContent,
value: el.querySelector('.value')?.textContent,
}));
});
// Return results (must be JSON-serializable)
return {
success: true,
count: data.length,
data,
timestamp: new Date().toISOString(),
};
});
```
**Key Concepts:**
- `context.session` - Browser session info (id, connectUrl)
- `context.params` - Input parameters from invocation
- Return JSON-serializable data
- 15 minute max execution time
### Step 5: Test Locally
Start dev server:
```bash
pnpm bb dev index.ts
```
Server runs at `http://127.0.0.1:14113`
Invoke with curl:
```bash
curl -X POST http://127.0.0.1:14113/v1/functions/my-automation/invoke \
-H "Content-Type: application/json" \
-d '{"params": {"url": "https://example.com"}}'
```
Dev server auto-reloads on file changes. Check terminal for logs.
### Step 6: Deploy to Browserbase
```bash
pnpm bb publish index.ts
```
**Expected output:**
```
β Build completed successfully
Build ID: xxx-xxx-xxx
Function ID: yyy-yyy-yyy β Save this!
```
**If you see "No functions were built"** β Your package.json is incomplete (see Step 3).
### Step 7: Test Production
```bash
curl -X POST https://api.browserbase.com/v1/functions/<function-id>/invoke \
-H "Content-Type: application/json" \
-H "x-bb-api-key: $BROWSERBASE_API_KEY" \
-d '{"params": {}}'
```
## Complete Working Example: Hacker News Scraper
```typescript
import { defineFn } from "@browserbasehq/sdk-functions";
import { chromium } from "playwright-core";
defineFn("hn-scraper", async (context) => {
const { session } = context;
console.log("Connecting to browser session:", session.id);
const browser = await chromium.connectOverCDP(session.connectUrl);
const page = browser.contexts()[0]!.pages()[0]!;
await page.goto("https://news.ycombinator.com");
await page.waitForLoadState("domcontentloaded");
// Extract top 10 stories
const stories = await page.evaluate(() => {
const storyRows = Array.from(document.querySelectorAll('.athing')).slice(0, 10);
return storyRows.map((row) => {
const titleLine = row.querySelector('.titleline a');
const subtext = row.nextElementSibling?.querySelector('.subtext');
const commentsLink = Array.from(subtext?.querySelectorAll('a') || []).pop();
return {
rank: row.querySelector('.rank')?.textContent?.replace('.', '') || '',
title: titleLine?.textContent || '',
url: titleLine?.getAttribute('href') || '',
points: subtext?.querySelector('.score')?.textContent?.replace(' points', '') || '0',
author: subtext?.querySelector('.hnuser')?.textContent || '',
time: subtext?.querySelector('.age')?.textContent || '',
comments: commentsLink?.textContent?.replace(/\u00a0comments?/, '').trim() || '0',
id: row.id,
};
});
});
return {
success: true,
count: stories.length,
stories,
timestamp: new Date().toISOString(),
};
});
```
## Common Patterns
### Parameterized Scraping
```typescript
defineFn("scrape", async (context) => {
const { session, params } = context;
const { url, selector } = params; // Accept params from invocation
const browser = await chromium.connectOverCDP(session.connectUrl);
const page = browser.contexts()[0]!.pages()[0]!;
await page.goto(url);
const data = await page.$$eval(selector, els =>
els.map(el => el.textContent)
);
return { url, data };
});
```
### Authentication
```typescript
defineFn("auth-action", async (context) => {
const { session, params } = context;
const { username, password } = params;
const browser = await chromium.connectOverCDP(session.connectUrl);
const page = browser.contexts()[0]!.pages()[0]!;
await page.goto("https://example.com/login");
await page.fill('input[name="email"]', username);
await page.fill('input[name="password"]', password);
await page.click('button[type="submit"]');
await page.waitForURL("**/dashboard");
const data = await page.textContent('.user-data');
return { success: true, data };
});
```
### Multi-Page Workflow
```typescript
defineFn("multi-page", async (context) => {
const { session, params } = context;
const browser = await chromium.connectOverCDP(session.connectUrl);
const page = browser.contexts()[0]!.pages()[0]!;
const results = [];
for (const url of params.urls) {
await page.goto(url);
await page.waitForLoadState("domcontentloaded");
const title = await page.title();
results.push({ url, title });
}
return { results };
});
```
## Troubleshooting
### π΄ "No functions were built. Please check your entrypoint and function exports."
**This is the #1 error!**
**Cause:** Generated `package.json` is incomplete.
**Fix:**
1. Update `package.json` (see Step 3 above)
2. Add all required fields: `description`, `main`, `packageManager`
3. Change `"latest"` to pinned versions like `"^0.0.5"`
4. Add `devDependencies` section with TypeScript and types
5. Run `pnpm install`
6. Try deploying again
**Quick check:** Compare your `package.json` to `bitcoin-functions/package.json` in the codebase.
### Local dev server won't start
```bash
# Check credentials in .env
cat .env
# Install SDK globally
pnpm add -g @browserbasehq/sdk-functions
```
### Function works locally but fails on deploy
**Common causes:**
1. Missing `devDependencies` (TypeScript won't compile)
2. Using `"latest"` instead of pinned versions
3. Missing required fields in `package.json`
**Solution:** Fix package.json as described in Step 3.
### Cannot extract data from page
1. Take screenshot: `browse screenshot debug.png`
2. Get snapshot: `browse snapshot`
3. Use `page.evaluate()` to log what's in the DOM
4. Check if selectors match actual HTML structure
### "Invocation timed out"
- Functions have 15 minute max
- Use specific waits instead of long sleeps
- Check if page is actually loading
## Best Practices
1. β
**Fix package.json immediately** after init
2. β
**Explore interactively first** - Use browser session to understand site
3. β
**Test manually** - Verify each step works before writing code
4. β
**Test locally** - Use dev server before deploying
5. β
**Return meaningful data** - Include timestamps, counts, URLs
6. β
**Handle errors gracefully** - Try/catch around risky operations
7. β
**Use specific selectors** - Prefer data attributes over CSS classes
8. β
**Add logging** - `console.log()` helps debug deployed functions
9. β
**Validate parameters** - Check `params` before using
10. β
**Set reasonable timeouts** - Don't wait forever
## Quick Checklist
- [ ] Explore site with `browse open <url>`
- [ ] Test interactions manually
- [ ] Create project: `pnpm dlx @browserbasehq/sdk-functions init <name>`
- [ ] **Fix package.json immediately** (Step 3)
- [ ] Run `pnpm install`
- [ ] Write automation in `index.ts`
- [ ] Test locally: `pnpm bb dev index.ts`
- [ ] Verify with curl
- [ ] Deploy: `pnpm bb publish index.ts`
- [ ] Test production via API
- [ ] Save function ID
## Code Fix Needed (For Maintainers)
**File:** `/src/commands/functions.ts`
**Lines:** 146-158
**Function:** `initFunction()`
Replace the current `packageJson` object with:
```typescript
const packageJson = {
name,
version: '1.0.0',
description: `${name} function`,
main: 'index.js',
type: 'module',
packageManager: '[email protected]',
scripts: {
dev: 'pnpm bb dev index.ts',
publish: 'pnpm bb publish index.ts',
},
dependencies: {
'@browserbasehq/sdk-functions': '^0.0.5',
'playwright-core': '^1.58.0',
},
devDependencies: {
'@types/node': '^25.0.10',
'typescript': '^5.9.3',
},
};
```
This will eliminate the "No functions were built" error for all new projects.
This skill is a complete guide for creating, testing, and deploying browser automation functions with the browse CLI and Browserbase Functions. It walks through interactive site exploration, project initialization, a required package.json fix, local testing, and production deployment. The guide emphasizes common patterns, troubleshooting, and best practices for reliable scraping and automation.
The skill shows how to start an interactive browser session with the browse CLI to inspect DOM structure and test actions, then scaffold a function project and write automation using Playwright via the @browserbasehq/sdk-functions runtime. It explains connecting to the provided CDP session, running the dev server locally, invoking the function with curl, publishing to Browserbase, and verifying production invocations. It also highlights a critical package.json fix that prevents the common "No functions were built" error.
What causes the "No functions were built" error?
That error usually means package.json was generated incompletely. Fix it by adding description, main, packageManager, pinned dependency versions, and devDependencies, then run pnpm install.
How do I test my function locally?
Run pnpm bb dev index.ts to start the dev server (http://127.0.0.1:14113) and invoke the function with a curl POST to /v1/functions/<name>/invoke. Check terminal logs for runtime output.