Skip to content
browse logo

Browser Automation & Functions Skill

browse

Complete guide for creating and deploying browser automation functions using the stagehand CLI

SKILL.md

Full skill instructions

Browser Automation & Functions Skill

Complete guide for creating and deploying browser automation functions using the stagehand CLI.

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

stagehand fn auth status  # Check if configured
stagehand fn auth login   # If needed - get credentials from https://browserbase.com/​settings

Complete Workflow

Step 1: Explore the Site Interactively

Start a local browser session to understand the site structure:

stagehand session create --local
stagehand goto https://example.com
stagehand snapshot                    # Get DOM structure with refs
stagehand screenshot -o page.png      # Visual inspection

Test interactions manually:

stagehand click @0-5
stagehand fill @0-6 "value"
stagehand eval "document.querySelector('.price').textContent"
stagehand session end  # When done exploring

Step 2: Initialize Function Project

stagehand fn init my-automation
cd my-automation

Creates:

  • package.json - Dependencies
  • .env - Credentials (from ~/​.stagehand/​config.json)
  • index.ts - Function template
  • tsconfig.json - TypeScript config

Step 3: ⚠️ FIX package.json IMMEDIATELY

CRITICAL BUG: stagehand fn init generates incomplete package.json that causes deployment to fail with "No functions were built."

REQUIRED FIX - Update package.json before doing anything else:

{
  "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:

pnpm install

Step 4: Write Automation Code

Edit index.ts:

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:

pnpm bb dev index.ts

Server runs at http://127.0.0.1:14113

Invoke with curl:

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

pnpm bb publish index.ts
# or: stagehand fn 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

stagehand fn invoke <function-id> -p '{"param": "value"}'

Or via API:

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

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

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

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

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 from stagehand fn init 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

# Check credentials
stagehand fn auth status

# Re-login if needed
stagehand fn auth login

# 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: stagehand screenshot -o debug.png
  2. Get snapshot: stagehand 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 stagehand fn init
  2. ✅ Explore interactively first - Use local 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 stagehand session create --local
  • Test interactions manually
  • Create project: stagehand fn 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: stagehand fn invoke <function-id>
  • Save function ID

Code Fix Needed (For Maintainers)

File: /​src/​commands/​functions.ts Lines: 146-158 Function: initFunction()

Replace the current packageJson object with:

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.

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View