skills$openclaw/browse
pkiv7.3k

by pkiv

browse – OpenClaw Skill

browse is an OpenClaw Skills integration for coding workflows. Complete guide for creating and deploying browser automation functions using the stagehand CLI

7.3k stars5.7k forksSecurity L1
Updated Feb 7, 2026Created Feb 7, 2026coding

Skill Snapshot

namebrowse
descriptionComplete guide for creating and deploying browser automation functions using the stagehand CLI OpenClaw Skills integration.
ownerpkiv
repositorypkiv/browse
languageMarkdown
licenseMIT
topics
securityL1
installopenclaw add @pkiv/browse
last updatedFeb 7, 2026

Maintainer

pkiv

pkiv

Maintains browse in the OpenClaw Skills directory.

View GitHub profile
File Explorer
14 files
.
skills
auth
SKILL.md
4.2 KB
browser-automation
SKILL.md
10.6 KB
create
SKILL.md
4.5 KB
fix
SKILL.md
4.8 KB
functions
SKILL.md
6.9 KB
_meta.json
267 B
README.md
594 B
SKILL.md
11.2 KB
SKILL.md

name: browse description: Complete guide for creating and deploying browser automation functions using the stagehand CLI homepage: https://browserbase.com metadata: {"moltbot":{"emoji":"🌐","requires":{"bins":["stagehand"],"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 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": "pnpm@10.14.0",
  "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: 'pnpm@10.14.0',
  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.

README.md

Browserbase Skills

Claude skill files for browser automation with Browserbase.

Skills

Usage

Add these skill files to your Claude project or cursor rules to enable browser automation capabilities.

Permissions & Security

Security level L1: Low-risk skills with minimal permissions. Review inputs and outputs before running in production.

Requirements

### Set Up Credentials ```bash stagehand fn auth status # Check if configured stagehand fn auth login # If needed - get credentials from https://browserbase.com/settings ```

FAQ

How do I install browse?

Run openclaw add @pkiv/browse in your terminal. This installs browse into your OpenClaw Skills catalog.

Does this skill run locally or in the cloud?

OpenClaw Skills execute locally by default. Review the SKILL.md and permissions before running any skill.

Where can I verify the source code?

The source repository is available at https://github.com/openclaw/skills/tree/main/skills/pkiv/browse. Review commits and README documentation before installing.