diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..961f0ab --- /dev/null +++ b/examples/README.md @@ -0,0 +1,38 @@ +# Examples + +Runnable examples demonstrating Open Browser's capabilities. + +## Prerequisites + +```bash +bun install +``` + +Set at least one API key: + +```bash +export ANTHROPIC_API_KEY=sk-... +# or +export OPENAI_API_KEY=sk-... +# or +export GOOGLE_GENERATIVE_AI_API_KEY=... +``` + +## Examples + +| Example | Description | +|---|---| +| [basic-agent.ts](./basic-agent.ts) | Give a task in natural language, get a result | +| [extract-data.ts](./extract-data.ts) | Extract structured data from a web page | +| [multi-provider.ts](./multi-provider.ts) | Switch between OpenAI, Anthropic, and Google models | +| [step-callbacks.ts](./step-callbacks.ts) | Monitor agent progress with `onStepStart`/`onStepEnd` hooks | +| [headless-vs-visible.ts](./headless-vs-visible.ts) | Run with or without a visible browser window | +| [url-security.ts](./url-security.ts) | Restrict which URLs the agent can visit | + +## Running + +```bash +bun run examples/basic-agent.ts +bun run examples/multi-provider.ts openai +bun run examples/headless-vs-visible.ts visible +``` diff --git a/examples/basic-agent.ts b/examples/basic-agent.ts new file mode 100644 index 0000000..e3a6b6b --- /dev/null +++ b/examples/basic-agent.ts @@ -0,0 +1,39 @@ +/** + * Basic agent example — give a task in natural language and let the agent complete it. + * + * Usage: + * ANTHROPIC_API_KEY=sk-... bun run examples/basic-agent.ts + */ +import { createAnthropic } from '@ai-sdk/anthropic'; +import { Agent, Viewport, VercelModelAdapter } from 'open-browser'; + +const anthropic = createAnthropic({}); +const model = new VercelModelAdapter({ + model: anthropic('claude-haiku-4-5-20251001'), +}); + +const browser = new Viewport({ headless: true }); +await browser.start(); + +try { + const agent = new Agent({ + task: 'Go to https://news.ycombinator.com and tell me the title of the top story', + model, + browser, + settings: { + stepLimit: 10, + }, + }); + + const result = await agent.run(); + + console.log('Success:', result.success); + console.log('Result:', result.finalResult); + + if (result.totalCost) { + console.log(`Cost: $${result.totalCost.totalCost.toFixed(4)}`); + console.log(`Tokens: ${result.totalCost.totalInputTokens + result.totalCost.totalOutputTokens}`); + } +} finally { + await browser.close(); +} diff --git a/examples/extract-data.ts b/examples/extract-data.ts new file mode 100644 index 0000000..7fd7ca2 --- /dev/null +++ b/examples/extract-data.ts @@ -0,0 +1,41 @@ +/** + * Structured data extraction — extract typed data from a web page using a Zod schema. + * + * Usage: + * ANTHROPIC_API_KEY=sk-... bun run examples/extract-data.ts + */ +import { z } from 'zod'; +import { createAnthropic } from '@ai-sdk/anthropic'; +import { Agent, Viewport, VercelModelAdapter } from 'open-browser'; + +const anthropic = createAnthropic({}); +const model = new VercelModelAdapter({ + model: anthropic('claude-haiku-4-5-20251001'), +}); + +const browser = new Viewport({ headless: true }); +await browser.start(); + +try { + const agent = new Agent({ + task: `Go to https://news.ycombinator.com and extract the top 5 stories. +For each story, get the title, URL, points, and number of comments. +Return the data as your final result.`, + model, + browser, + settings: { + stepLimit: 10, + }, + }); + + const result = await agent.run(); + + if (result.success && result.finalResult) { + console.log('Extracted data:'); + console.log(result.finalResult); + } else { + console.error('Extraction failed:', result.errors); + } +} finally { + await browser.close(); +} diff --git a/examples/headless-vs-visible.ts b/examples/headless-vs-visible.ts new file mode 100644 index 0000000..a81cffe --- /dev/null +++ b/examples/headless-vs-visible.ts @@ -0,0 +1,35 @@ +/** + * Headless vs visible browser — run with or without a visible browser window. + * + * Usage: + * ANTHROPIC_API_KEY=sk-... bun run examples/headless-vs-visible.ts # headless (default) + * ANTHROPIC_API_KEY=sk-... bun run examples/headless-vs-visible.ts visible # show browser + */ +import { createAnthropic } from '@ai-sdk/anthropic'; +import { Agent, Viewport, VercelModelAdapter } from 'open-browser'; + +const headless = process.argv[2] !== 'visible'; + +const anthropic = createAnthropic({}); +const model = new VercelModelAdapter({ + model: anthropic('claude-haiku-4-5-20251001'), +}); + +const browser = new Viewport({ headless }); +await browser.start(); + +console.log(`Browser mode: ${headless ? 'headless' : 'visible'}`); + +try { + const agent = new Agent({ + task: 'Go to https://example.com and tell me the page title', + model, + browser, + settings: { stepLimit: 5 }, + }); + + const result = await agent.run(); + console.log('Result:', result.finalResult); +} finally { + await browser.close(); +} diff --git a/examples/multi-provider.ts b/examples/multi-provider.ts new file mode 100644 index 0000000..6e741ec --- /dev/null +++ b/examples/multi-provider.ts @@ -0,0 +1,53 @@ +/** + * Multi-provider example — use different LLM providers (OpenAI, Anthropic, Google). + * + * Usage: + * OPENAI_API_KEY=sk-... bun run examples/multi-provider.ts openai + * ANTHROPIC_API_KEY=sk-... bun run examples/multi-provider.ts anthropic + * GOOGLE_GENERATIVE_AI_API_KEY=... bun run examples/multi-provider.ts google + */ +import { Agent, Viewport, VercelModelAdapter, type LanguageModel } from 'open-browser'; + +const provider = process.argv[2] ?? 'anthropic'; + +async function createModel(provider: string): Promise { + switch (provider) { + case 'openai': { + const { createOpenAI } = await import('@ai-sdk/openai'); + const openai = createOpenAI({}); + return new VercelModelAdapter({ model: openai('gpt-4o-mini') }); + } + case 'anthropic': { + const { createAnthropic } = await import('@ai-sdk/anthropic'); + const anthropic = createAnthropic({}); + return new VercelModelAdapter({ model: anthropic('claude-haiku-4-5-20251001') }); + } + case 'google': { + const { createGoogleGenerativeAI } = await import('@ai-sdk/google'); + const google = createGoogleGenerativeAI({}); + return new VercelModelAdapter({ model: google('gemini-2.0-flash') }); + } + default: + throw new Error(`Unknown provider: ${provider}. Use: openai, anthropic, google`); + } +} + +const model = await createModel(provider); +const browser = new Viewport({ headless: true }); +await browser.start(); + +try { + console.log(`Using provider: ${provider} (${model.modelId})`); + + const agent = new Agent({ + task: 'Go to https://example.com and tell me the main heading text', + model, + browser, + settings: { stepLimit: 5 }, + }); + + const result = await agent.run(); + console.log('Result:', result.finalResult); +} finally { + await browser.close(); +} diff --git a/examples/step-callbacks.ts b/examples/step-callbacks.ts new file mode 100644 index 0000000..8132cf0 --- /dev/null +++ b/examples/step-callbacks.ts @@ -0,0 +1,50 @@ +/** + * Step callbacks — monitor agent progress with onStepStart/onStepEnd hooks. + * + * Usage: + * ANTHROPIC_API_KEY=sk-... bun run examples/step-callbacks.ts + */ +import { createAnthropic } from '@ai-sdk/anthropic'; +import { Agent, Viewport, VercelModelAdapter } from 'open-browser'; + +const anthropic = createAnthropic({}); +const model = new VercelModelAdapter({ + model: anthropic('claude-haiku-4-5-20251001'), +}); + +const browser = new Viewport({ headless: true }); +await browser.start(); + +try { + const agent = new Agent({ + task: 'Go to https://example.com, click the "More information..." link, and tell me the page title', + model, + browser, + settings: { stepLimit: 10 }, + onStepStart: (step) => { + console.log(`\n--- Step ${step} starting ---`); + }, + onStepEnd: (step, results) => { + for (const result of results) { + const status = result.success ? '✓' : '✗'; + const action = result.isDone ? 'done' : 'action'; + console.log(` ${status} ${action}`); + + if (result.extractedContent) { + console.log(` extracted: ${result.extractedContent.slice(0, 100)}`); + } + if (result.error) { + console.log(` error: ${result.error}`); + } + } + }, + onDone: (result) => { + console.log(`\n=== Done (${result.success ? 'success' : 'failed'}) ===`); + }, + }); + + const result = await agent.run(); + console.log('Final result:', result.finalResult); +} finally { + await browser.close(); +} diff --git a/examples/url-security.ts b/examples/url-security.ts new file mode 100644 index 0000000..ba40ca3 --- /dev/null +++ b/examples/url-security.ts @@ -0,0 +1,37 @@ +/** + * URL security policies — restrict which URLs the agent can navigate to. + * + * Usage: + * ANTHROPIC_API_KEY=sk-... bun run examples/url-security.ts + */ +import { createAnthropic } from '@ai-sdk/anthropic'; +import { Agent, Viewport, VercelModelAdapter } from 'open-browser'; + +const anthropic = createAnthropic({}); +const model = new VercelModelAdapter({ + model: anthropic('claude-haiku-4-5-20251001'), +}); + +// Allow only specific domains +const browser = new Viewport({ + headless: true, + allowedUrls: ['https://example.com/*', 'https://*.iana.org/*'], +}); +await browser.start(); + +try { + const agent = new Agent({ + task: 'Go to https://example.com and tell me what you see', + model, + browser, + settings: { stepLimit: 5 }, + }); + + const result = await agent.run(); + console.log('Result:', result.finalResult); + + // The agent cannot navigate outside the allowed domains. + // If it tries, the UrlPolicyGuard will block the navigation. +} finally { + await browser.close(); +}