Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions typescript/scoped-actions/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Browserbase credentials - get these from https://www.browserbase.com/settings
BROWSERBASE_PROJECT_ID=your_browserbase_project_id
BROWSERBASE_API_KEY=your_browserbase_api_key

# OpenAI API key for gpt-4.1-mini model
OPENAI_API_KEY=your_openai_api_key
64 changes: 64 additions & 0 deletions typescript/scoped-actions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Scoped Actions for Complex Pages

## AT A GLANCE

- Goal: demonstrate how to scope `observe`, `act`, and `extract` to a specific DOM subtree to reduce token usage and improve precision on complex pages.
- Scoped Observe + Act: uses `observe` with a `selector` to find actions within a target element, then passes the result to `act` — no extra inference needed.
- Scoped Extract: uses `extract` with a `selector` to pull structured data from a specific table instead of the entire page.
- Model: uses `openai/gpt-4.1-mini` for fast, cost-effective automation.
Docs → https://docs.stagehand.dev

## GLOSSARY

- observe: find candidate actions on a page, optionally scoped to a selector
Docs → https://docs.stagehand.dev/basics/observe
- act: execute an action (or a pre-observed action) on the page
Docs → https://docs.stagehand.dev/basics/act
- extract: pull structured data from pages using schemas, optionally scoped to a selector
Docs → https://docs.stagehand.dev/basics/extract
- selector: a CSS selector or XPath that restricts the snapshot sent to the model to a specific DOM subtree

## QUICKSTART

1. cd typescript/scoped-actions
2. pnpm install
3. cp .env.example .env
4. Add BROWSERBASE_PROJECT_ID, BROWSERBASE_API_KEY, and OPENAI_API_KEY to .env
5. pnpm start

## EXPECTED OUTPUT

- Initializes a Stagehand session with Browserbase
- Navigates to a Wikipedia GDP page (a data-heavy page with multiple tables)
- **Scoped Extract**: extracts the top countries by GDP from only the target table
- **Scoped Act**: clicks the first country link within the table only
- Outputs JSON to console and closes the session

## COMMON PITFALLS

- "Cannot find module": ensure all dependencies are installed (`pnpm install`)
- Missing credentials: verify .env contains BROWSERBASE_PROJECT_ID, BROWSERBASE_API_KEY, and OPENAI_API_KEY
- Selector not found: if the page layout changes, update SELECTOR_SCOPE in index.ts
- Empty results: the scoped selector must match at least one element on the page

## USE CASES

- Table-level automation: act on or extract from a specific table on pages with many tables
- Form scoping: restrict actions to a specific form on a page with multiple forms
- Component isolation: target a sidebar, modal, or card without the model seeing the rest of the page
- Cost control: reduce token usage by sending only the relevant subtree to inference

## NEXT STEPS

- Adapt the selectors to your own pages and use cases
- Combine scoped observe + act for multi-step workflows within a single component
- Use XPath selectors for cross-iframe scoping (e.g., `/html/body/div[2]/iframe/html/body/table`)

## HELPFUL RESOURCES

📚 Stagehand Docs: https://docs.stagehand.dev/v3/first-steps/introduction
🎮 Browserbase: https://www.browserbase.com
💡 Try it out: https://www.browserbase.com/playground
🔧 Templates: https://www.browserbase.com/templates
📧 Need help? support@browserbase.com
💬 Discord: http://stagehand.dev/discord
85 changes: 85 additions & 0 deletions typescript/scoped-actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Scoped Actions for Complex Pages - See README.md for full documentation

import "dotenv/config";
import { Stagehand } from "@browserbasehq/stagehand";
import { z } from "zod";

// Wikipedia GDP page — data-heavy page with multiple tables, navboxes, and sidebars
const TARGET_URL = "https://en.wikipedia.org/wiki/List_of_countries_by_GDP_(nominal)";

// Scope to the first sortable data table
const SELECTOR_SCOPE = "table.sortable";
Comment thread
kalil0321 marked this conversation as resolved.

// Scoped act: observe within a selector, then act on the best match.
// observe() scopes the snapshot to the selector subtree, and act() on the
// returned action executes with zero additional inference.
async function scopedAct(stagehand: Stagehand, instruction: string, selector: string) {
const actions = await stagehand.observe(instruction, { selector });
if (actions.length === 0) {
console.warn(`No actions found for "${instruction}" within "${selector}"`);
return null;
}
return stagehand.act(actions[0]);
}

// Schema for extracting country GDP data
const GDPSchema = z.object({
countries: z
.array(
z.object({
country: z.string().describe("The country name"),
gdp: z.string().describe("The GDP value in millions of USD"),
}),
)
.describe("The first 3 countries by GDP from the table"),
});

async function main(): Promise<void> {
const stagehand = new Stagehand({
env: "BROWSERBASE",
verbose: 0,
model: "openai/gpt-4.1-mini",
});

await stagehand.init();
console.log(`Session: ${stagehand.browserbaseSessionURL}`);
console.log(`Debug URL: ${stagehand.browserbaseDebugURL}\n`);

const page = stagehand.context.pages()[0];
await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });

// Scoped extract: only the selector subtree is sent to the model, not the full page
console.log(`Scoped extract (selector: "${SELECTOR_SCOPE}")...`);
const data = await stagehand.extract(
"Extract the first 3 countries with their rank, name, and GDP value",
GDPSchema,
{ selector: SELECTOR_SCOPE },
);
console.log("Result:", JSON.stringify(data.countries, null, 2));

// Scoped act: uses the scopedAct wrapper to click within the table only
console.log(`\nScoped act (selector: "${SELECTOR_SCOPE}")...`);
await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });

const result = await scopedAct(
stagehand,
"Click on the link for the first country in the table",
SELECTOR_SCOPE,
);

if (result) {
console.log(`Clicked → navigated to: ${page.url()}`);
}

await stagehand.close();
console.log("\nDone.");
}

main().catch((err) => {
console.error("Error:", err);
console.error("\nCommon issues:");
console.error(" - Check .env file has BROWSERBASE_PROJECT_ID and BROWSERBASE_API_KEY");
console.error(" - Verify OPENAI_API_KEY is set for the model");
Comment thread
kalil0321 marked this conversation as resolved.
console.error("Docs: https://docs.stagehand.dev");
process.exit(1);
});
17 changes: 17 additions & 0 deletions typescript/scoped-actions/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "scoped-actions-template",
"type": "module",
"scripts": {
"build": "tsc",
"start": "tsx index.ts"
},
"dependencies": {
"@browserbasehq/stagehand": "latest",
"dotenv": "^16.4.7",
"zod": "latest"
},
"devDependencies": {
"tsx": "^4.19.2",
"typescript": "^5.0.0"
}
}