diff --git a/README.md b/README.md new file mode 100644 index 00000000..316753e4 --- /dev/null +++ b/README.md @@ -0,0 +1,91 @@ +# Stagehand + Browserbase Templates + +A comprehensive collection of ready-to-use automation templates demonstrating the power of Stagehand and Browserbase for web automation, data extraction, and AI-powered browser interactions. + +## 🚀 Quick Start + +1. **Choose your language**: TypeScript or Python +2. **Browse available templates** in the respective language folder +3. **Read the template's README** for detailed setup instructions and use cases +4. **Start automating!** + +## 📁 Available Templates + +### TypeScript Templates (`/typescript/`) +- **context** - AI-powered context switching and page analysis +- **formFilling** - Automated form submission and data entry +- **giftfinder** - AI-powered product discovery and recommendation +- **pickleball** - Court booking automation with user interaction +- **proxies** - Proxy testing and geolocation verification +- **realEstateCheck** - License verification and data extraction + +### Python Templates (`/python/`) +- **context** - AI-powered context switching and page analysis +- **formFilling** - Automated form submission and data entry +- **giftfinder** - AI-powered product discovery and recommendation +- **pickleball** - Court booking automation with user interaction +- **proxies** - Proxy testing and geolocation verification +- **realEstateCheck** - License verification and data extraction + +> **📖 Each template includes a comprehensive README with:** +> - Detailed setup instructions +> - Required environment variables +> - Use cases and examples +> - Troubleshooting guides +> - Dependencies and installation steps + +## 🔧 Getting Started + +1. **Choose a template** from the TypeScript or Python folders +2. **Read the template's README** for specific setup instructions +3. **Set up your environment** with the required API keys and dependencies +4. **Run the template** and start automating! + +> **💡 Pro Tip**: Each template's README contains detailed installation steps, environment variable requirements, and troubleshooting guides specific to that template. + +## 📚 Resources + +### Documentation +- **Stagehand Docs**: https://docs.browserbase.com/stagehand +- **Browserbase Docs**: https://docs.browserbase.com +- **API Reference**: https://docs.browserbase.com/api + +### Support +- **Community**: Join our Discord community +- **Email Support**: support@browserbase.com +- **GitHub Issues**: Report bugs and request features + +### Examples & Tutorials +- **Getting Started Guide**: Learn the basics of Stagehand +- **Advanced Patterns**: Complex automation workflows +- **Best Practices**: Tips for reliable automation + +## 🤝 Contributing + +We welcome contributions! Here's how you can help: + +1. **Report Bugs**: Use GitHub issues to report problems +2. **Suggest Features**: Propose new templates or improvements +3. **Submit Pull Requests**: Contribute code improvements +4. **Share Templates**: Create and share your own templates + +### Template Guidelines +- Follow the established structure and naming conventions +- Include comprehensive README documentation +- Add proper error handling and logging +- Test templates thoroughly before submitting + +## 📄 License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## 🙏 Acknowledgments + +- **Stagehand Team**: For the amazing automation framework +- **Browserbase Team**: For reliable cloud browser infrastructure +- **Community Contributors**: For templates and improvements +- **Open Source Projects**: For the tools and libraries we build upon + +--- + +**Ready to start automating?** Pick a template and follow its README to get started! 🚀 diff --git a/python/context/README.md b/python/context/README.md index 775cc6df..c575cf22 100644 --- a/python/context/README.md +++ b/python/context/README.md @@ -14,18 +14,14 @@ Docs → https://docs.stagehand.dev/basics/act ## QUICKSTART - 1) Create virtual environment (optional but recommended): - python -m venv venv - source venv/bin/activate # On Windows: venv\Scripts\activate - 2) Install dependencies: - pip install stagehand browserbase python-dotenv pydantic requests - 3) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - SF_REC_PARK_EMAIL=... - SF_REC_PARK_PASSWORD=... - 4) Run: - python index.py + 1) cd context-template + 2) python -m venv venv + 3) source venv/bin/activate # On Windows: venv\Scripts\activate + 4) pip install -r requirements.txt + 5) pip install browserbase pydantic requests + 6) cp .env.example .env + 7) Add your Browserbase API key, Project ID, and SF Rec Park credentials to .env + 8) python main.py ## EXPECTED OUTPUT - Creates context, performs login, saves auth state diff --git a/python/context/index.py b/python/context/main.py similarity index 92% rename from python/context/index.py rename to python/context/main.py index bf545f31..59b6115e 100644 --- a/python/context/index.py +++ b/python/context/main.py @@ -41,9 +41,12 @@ async def create_session_context_id(): env="BROWSERBASE", api_key=os.environ.get("BROWSERBASE_API_KEY"), project_id=os.environ.get("BROWSERBASE_PROJECT_ID"), - model_name="gpt-4o", + model_name="openai/gpt-4.1", model_api_key=os.environ.get("OPENAI_API_KEY"), browserbase_session_id=session.id, + verbose=1 # 0 = errors only, 1 = info, 2 = debug + # (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) + # https://docs.stagehand.dev/configuration/logging ) async with Stagehand(config) as stagehand: @@ -102,7 +105,7 @@ async def main(): env="BROWSERBASE", api_key=os.environ.get("BROWSERBASE_API_KEY"), project_id=os.environ.get("BROWSERBASE_PROJECT_ID"), - model_name="gpt-4o", + model_name="openai/gpt-4.1", model_api_key=os.environ.get("OPENAI_API_KEY"), browserbase_session_create_params={ "project_id": os.environ.get("BROWSERBASE_PROJECT_ID"), @@ -112,7 +115,10 @@ async def main(): "persist": True, } } - } + }, + verbose=1 # 0 = errors only, 1 = info, 2 = debug + # (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) + # https://docs.stagehand.dev/configuration/logging ) async with Stagehand(config) as stagehand: diff --git a/python/formFilling/README.md b/python/form-filling/README.md similarity index 87% rename from python/formFilling/README.md rename to python/form-filling/README.md index 458aefb3..53445766 100644 --- a/python/formFilling/README.md +++ b/python/form-filling/README.md @@ -15,17 +15,13 @@ - variable substitution: inject dynamic values into actions using `%variable%` syntax ## QUICKSTART - 1) Create virtual environment (optional but recommended): - python -m venv venv - source venv/bin/activate # On Windows: venv\Scripts\activate - 2) Install dependencies: - pip install stagehand python-dotenv - 3) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - OPENAI_API_KEY=... - 4) Run: - python index.py + 1) cd form-fill-template + 2) python -m venv venv + 3) source venv/bin/activate # On Windows: venv\Scripts\activate + 4) pip install -r requirements.txt + 5) cp .env.example .env + 6) Add your Browserbase API key and Project ID to .env + 7) python main.py ## EXPECTED OUTPUT - Initializes Stagehand session with Browserbase diff --git a/python/formFilling/index.py b/python/form-filling/main.py similarity index 87% rename from python/formFilling/index.py rename to python/form-filling/main.py index 70811156..570a12f8 100644 --- a/python/formFilling/index.py +++ b/python/form-filling/main.py @@ -26,11 +26,14 @@ async def main(): env="BROWSERBASE", api_key=os.environ.get("BROWSERBASE_API_KEY"), project_id=os.environ.get("BROWSERBASE_PROJECT_ID"), - model_name="gpt-4o", + model_name="openai/gpt-4.1", model_api_key=os.environ.get("OPENAI_API_KEY"), browserbase_session_create_params={ "project_id": os.environ.get("BROWSERBASE_PROJECT_ID"), - } + }, + verbose=1 # 0 = errors only, 1 = info, 2 = debug + # (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) + # https://docs.stagehand.dev/configuration/logging ) try: @@ -52,15 +55,7 @@ async def main(): timeout=60000 # Extended timeout for reliable page loading. ) - # Analyze form structure to identify fillable fields before attempting to fill. - print("Analyzing form fields...") - contact_page = await page.observe( - instruction="What are the fields that can be filled in?", - return_action=True # Return action objects for potential reuse with act(). - ) - print(f"Available form fields: {contact_page}") - - # Fill form using a more reliable approach with individual field targeting. + # Fill form using individual act() calls for reliability print("Filling in contact form...") # Fill each field individually for better reliability diff --git a/python/giftfinder/README.md b/python/gift-finder/README.md similarity index 88% rename from python/giftfinder/README.md rename to python/gift-finder/README.md index 3ffbbec9..c8a391b6 100644 --- a/python/giftfinder/README.md +++ b/python/gift-finder/README.md @@ -17,17 +17,14 @@ Docs → https://docs.browserbase.com/features/proxies ## QUICKSTART - 1) Create virtual environment (optional but recommended): - python -m venv venv - source venv/bin/activate # On Windows: venv\Scripts\activate - 2) Install dependencies: - pip install stagehand python-dotenv InquirerPy openai pydantic - 3) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - OPENAI_API_KEY=... - 4) Run: - python index.py + 1) cd gift-finder-template + 2) python -m venv venv + 3) source venv/bin/activate # On Windows: venv\Scripts\activate + 4) pip install -r requirements.txt + 5) pip install InquirerPy pydantic openai + 6) cp .env.example .env + 7) Add your Browserbase API key, Project ID, and OpenAI API key to .env + 8) python main.py ## EXPECTED OUTPUT - Prompts user for recipient and description diff --git a/python/giftfinder/index.py b/python/gift-finder/main.py similarity index 84% rename from python/giftfinder/index.py rename to python/gift-finder/main.py index ea513706..d4d317b0 100644 --- a/python/giftfinder/index.py +++ b/python/gift-finder/main.py @@ -37,13 +37,19 @@ class SearchResult(BaseModel): async def generate_search_queries(recipient: str, description: str) -> List[str]: + """ + Generate intelligent search queries based on recipient profile. + + Uses AI to create thoughtful, complementary gift search terms that go beyond + obvious basics to find unique and meaningful gifts. + """ print(f"Generating search queries for {recipient}...") # Use AI to generate search terms based on recipient profile # This avoids generic searches and focuses on thoughtful, complementary gifts response = await asyncio.to_thread( client.chat.completions.create, - model="gpt-4o", + model="gpt-4.1", messages=[ { "role": "user", @@ -80,6 +86,12 @@ async def score_products( recipient: str, description: str, ) -> List[Product]: + """ + Score and rank products based on recipient profile using AI. + + Analyzes each product against the recipient's interests, relationship context, + value, uniqueness, and practical usefulness to find the best gift matches. + """ print("AI is analyzing gift options based on recipient profile...") # Flatten all products from multiple search sessions into single array @@ -99,7 +111,7 @@ async def score_products( response = await asyncio.to_thread( client.chat.completions.create, - model="gpt-4o", + model="gpt-4.1", messages=[ { "role": "user", @@ -176,6 +188,12 @@ async def score_products( async def get_user_input() -> GiftFinderAnswers: + """ + Collect user input for gift recipient and description. + + Uses interactive CLI prompts with validation to gather information + needed for intelligent gift recommendations. + """ print("Welcome to the Gift Finder App!") print("Find the perfect gift with intelligent web browsing") @@ -208,16 +226,26 @@ def get_description(): async def main() -> None: + """ + Main application entry point. + + Orchestrates the entire gift finding process: + 1. Collects user input + 2. Generates intelligent search queries + 3. Runs concurrent browser searches + 4. Scores and ranks products with AI + 5. Displays top recommendations + """ print("Starting Gift Finder Application...") + # Step 1: Collect user input user_input = await get_user_input() recipient = user_input.recipient description = user_input.description print(f"User input received: {recipient} - {description}") + # Step 2: Generate search queries using AI print("\nGenerating intelligent search queries...") - - # Generate search queries with fallback for reliability try: search_queries = await generate_search_queries(recipient, description) @@ -227,39 +255,40 @@ async def main() -> None: print(f" {index + 1}. {cleaned_query}") except Exception as error: print(f"Error generating search queries: {error}") - # Fallback queries + # Fallback queries ensure app continues working search_queries = ["gifts", "accessories", "items"] print("Using fallback search queries") + # Step 3: Start concurrent browser searches print("\nStarting concurrent browser searches...") async def run_single_search(query: str, session_index: int) -> SearchResult: print(f"Starting search session {session_index + 1} for: \"{query}\"") # Create separate Stagehand instance for each search to run concurrently - # Each session searches independently to maximize speed + # Each session searches independently to maximize speed and parallel processing config = StagehandConfig( env="BROWSERBASE", api_key=os.environ.get("BROWSERBASE_API_KEY"), project_id=os.environ.get("BROWSERBASE_PROJECT_ID"), - verbose=0, - # 0 = errors only, 1 = info, 2 = debug - # (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) + verbose=1, # Silent logging to avoid cluttering output + # Logging levels: 0 = errors only, 1 = info, 2 = debug + # When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs # https://docs.stagehand.dev/configuration/logging - model_name="gpt-4o", + model_name="openai/gpt-4.1", model_api_key=os.environ.get("OPENAI_API_KEY"), browserbase_session_create_params={ "project_id": os.environ.get("BROWSERBASE_PROJECT_ID"), - # Proxies require Developer Plan or higher - comment in if you have a Developer Plan or higher - # "proxies": [ + # Proxies require Developer Plan or higher - uncomment if you have a Developer Plan or higher + # "proxies": [ # { - # "type": "browserbase", - # "geolocation": { - # "city": "LONDON", - # "country": "GB" - # } + # "type": "browserbase", + # "geolocation": { + # "city": "LONDON", + # "country": "GB" + # } # } - # ], + # ], "region": "us-east-1", "timeout": 900, "browser_settings": { @@ -272,6 +301,7 @@ async def run_single_search(query: str, session_index: int) -> SearchResult: ) try: + # Initialize browser session with Stagehand async with Stagehand(config) as session_stagehand: session_page = session_stagehand.page @@ -286,7 +316,7 @@ async def run_single_search(query: str, session_index: int) -> SearchResult: live_view_url = f"https://www.browserbase.com/sessions/{session_id}" print(f"Session {session_index + 1} Live View: {live_view_url}") - # Navigate to European gift site - proxies help with regional access + # Navigate to European gift site print(f"Session {session_index + 1}: Navigating to Firebox.eu...") await session_page.goto("https://firebox.eu/") @@ -299,7 +329,7 @@ async def run_single_search(query: str, session_index: int) -> SearchResult: # Extract structured product data using Pydantic schema for type safety print(f"Session {session_index + 1}: Extracting product data...") - # Define schema using Pydantic + # Define Pydantic schemas for structured data extraction class ProductItem(BaseModel): title: str = Field(..., description="the title/name of the product") url: HttpUrl = Field(..., description="the full URL link to the product page") @@ -343,12 +373,13 @@ class ProductsData(BaseModel): products=[] ) + # Create concurrent search tasks for all generated queries search_promises = [run_single_search(query, index) for index, query in enumerate(search_queries)] print("\nBrowser Sessions Starting...") print("Live view links will appear as each session initializes") - # Wait for all concurrent searches to complete + # Execute all searches concurrently using asyncio.gather() all_results = await asyncio.gather(*search_promises) # Calculate total products found across all search sessions @@ -360,6 +391,7 @@ class ProductsData(BaseModel): for result in all_results: all_products_flat.extend(result.products) + # Step 4: Score and rank products with AI if len(all_products_flat) > 0: try: # AI scores all products and ranks them by relevance to recipient @@ -367,6 +399,7 @@ class ProductsData(BaseModel): top3_products = scored_products[:3] print("\nTOP 3 RECOMMENDED GIFTS:") + print("=" * 50) # Display top 3 products with AI reasoning for transparency for index, product in enumerate(top3_products): @@ -374,19 +407,21 @@ class ProductsData(BaseModel): print(f"\n{rank} - {product.title}") print(f"Price: {product.price}") print(f"Rating: {product.rating}") - print(f"Score: {product.ai_score}/10") + print(f"AI Score: {product.ai_score}/10") print(f"Why: {product.ai_reason}") print(f"Link: {product.url}") + print("-" * 30) print( f"\nGift finding complete! Found {total_products} products, analyzed {len(scored_products)} with AI." ) except Exception as error: + # Handle AI scoring errors print(f"Error scoring products: {error}") print(f"Target: {recipient}") print(f"Profile: {description}") else: - # Handle case where no products were found across all searches + # Handle case where no products were found print("No products found to score") print("Try adjusting your recipient description or check if the website is accessible") @@ -399,4 +434,4 @@ class ProductsData(BaseModel): except Exception as err: print(f"Application error: {err}") print("Check your environment variables and internet connection") - exit(1) + exit(1) \ No newline at end of file diff --git a/python/realEstateCheck/README.md b/python/license-verification/README.md similarity index 88% rename from python/realEstateCheck/README.md rename to python/license-verification/README.md index 1b96bd60..8405010a 100644 --- a/python/realEstateCheck/README.md +++ b/python/license-verification/README.md @@ -17,16 +17,14 @@ - structured scraping: extracting consistent, typed data that can flow into apps, CRMs, or compliance systems. ## QUICKSTART - 1) Create virtual environment (optional but recommended): - python -m venv venv - source venv/bin/activate # On Windows: venv\Scripts\activate - 2) Install dependencies: - pip install stagehand python-dotenv pydantic - 3) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - 4) Run: - python index.py + 1) cd license-verification-template + 2) python -m venv venv + 3) source venv/bin/activate # On Windows: venv\Scripts\activate + 4) pip install -r requirements.txt + 5) pip install pydantic + 6) cp .env.example .env + 7) Add your Browserbase API key and Project ID to .env + 8) python main.py ## EXPECTED OUTPUT - Navigates to California DRE license verification website diff --git a/python/realEstateCheck/index.py b/python/license-verification/main.py similarity index 98% rename from python/realEstateCheck/index.py rename to python/license-verification/main.py index 907b8097..b0011c08 100644 --- a/python/realEstateCheck/index.py +++ b/python/license-verification/main.py @@ -22,11 +22,11 @@ async def main(): env="BROWSERBASE", # Use Browserbase cloud browsers for reliable automation. api_key=os.environ.get("BROWSERBASE_API_KEY"), project_id=os.environ.get("BROWSERBASE_PROJECT_ID"), - verbose=0, + verbose=1, # 0 = errors only, 1 = info, 2 = debug # (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) # https://docs.stagehand.dev/configuration/logging - model_name="gpt-4o", + model_name="openai/gpt-4.1", model_api_key=os.environ.get("OPENAI_API_KEY"), ) diff --git a/python/pickleball/README.md b/python/pickleball/README.md index 531383d4..63e46c45 100644 --- a/python/pickleball/README.md +++ b/python/pickleball/README.md @@ -20,18 +20,14 @@ ## QUICKSTART 1) Create an account with SF Recreation & Parks website -> https://www.rec.us/organizations/san-francisco-rec-park -2) Create virtual environment (optional but recommended): - python -m venv venv - source venv/bin/activate # On Windows: venv\Scripts\activate -3) Install dependencies: - pip install stagehand python-dotenv InquirerPy pydantic -4) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - SF_REC_PARK_EMAIL=... - SF_REC_PARK_PASSWORD=... -5) Run: - python index.py +2) cd pickleball-template +3) python -m venv venv +4) source venv/bin/activate # On Windows: venv\Scripts\activate +5) pip install -r requirements.txt +6) pip install InquirerPy pydantic +7) cp .env.example .env +8) Add your Browserbase API key, Project ID, and SF Rec Park credentials to .env +9) python main.py ## EXPECTED OUTPUT - Prompts user for activity type (Tennis/Pickleball), date, and time diff --git a/python/pickleball/index.py b/python/pickleball/main.py similarity index 99% rename from python/pickleball/index.py rename to python/pickleball/main.py index 976ebf20..bac60f47 100644 --- a/python/pickleball/index.py +++ b/python/pickleball/main.py @@ -326,11 +326,11 @@ async def book_tennis_paddle_court(): env="BROWSERBASE", api_key=os.environ.get("BROWSERBASE_API_KEY"), project_id=os.environ.get("BROWSERBASE_PROJECT_ID"), - verbose=0, + verbose=1, # 0 = errors only, 1 = info, 2 = debug # (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) # https://docs.stagehand.dev/configuration/logging - model_name="gpt-4o", + model_name="openai/gpt-4.1", model_api_key=os.environ.get("OPENAI_API_KEY"), browserbase_session_create_params={ "project_id": os.environ.get("BROWSERBASE_PROJECT_ID"), diff --git a/python/proxies/README.md b/python/proxies/README.md index df61a21d..45286133 100644 --- a/python/proxies/README.md +++ b/python/proxies/README.md @@ -8,17 +8,15 @@ Docs → https://docs.browserbase.com/features/proxies ## QUICKSTART - 1) Create virtual environment (optional but recommended): - python -m venv venv - source venv/bin/activate # On Windows: venv\Scripts\activate - 2) Install dependencies: - pip install playwright browserbase python-dotenv - playwright install chromium - 3) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - 4) Run: - python index.py + 1) cd proxies-template + 2) python -m venv venv + 3) source venv/bin/activate # On Windows: venv\Scripts\activate + 4) pip install -r requirements.txt + 5) pip install browserbase playwright + 6) playwright install chromium + 7) cp .env.example .env + 8) Add your Browserbase API key and Project ID to .env + 9) python main.py ## EXPECTED OUTPUT - Tests built-in proxy rotation diff --git a/python/proxies/index.py b/python/proxies/main.py similarity index 61% rename from python/proxies/index.py rename to python/proxies/main.py index bf9de69e..5341a082 100644 --- a/python/proxies/index.py +++ b/python/proxies/main.py @@ -4,6 +4,8 @@ import asyncio from playwright.async_api import async_playwright from browserbase import Browserbase +from stagehand import Stagehand, StagehandConfig +from pydantic import BaseModel, Field from dotenv import load_dotenv load_dotenv() @@ -11,6 +13,19 @@ bb = Browserbase(api_key=os.environ.get("BROWSERBASE_API_KEY")) +class GeoInfo(BaseModel): + """Schema for IP information and geolocation data""" + ip: str = Field(..., description="The IP address") + city: str = Field(..., description="The city name") + region: str = Field(..., description="The state or region") + country: str = Field(..., description="The country code") + loc: str = Field(..., description="The latitude and longitude coordinates") + timezone: str = Field(..., description="The timezone") + org: str = Field(..., description="The organization or ISP") + postal: str = Field(..., description="The postal code") + hostname: str = Field(..., description="The hostname if available") + + async def create_session_with_built_in_proxies(): # Use Browserbase's default proxy rotation for enhanced privacy and IP diversity. session = await asyncio.to_thread( @@ -75,10 +90,41 @@ async def test_session(session_function, session_name: str): if not page: raise Exception("No page found in default context") - # Navigate to IP info service to verify proxy location and IP address. - await page.goto("https://ipinfo.io/json", wait_until="domcontentloaded") - geo_info = await page.text_content('pre') # Extract JSON response containing IP and location data. - print(f"Geo Info: {geo_info}") + # Initialize Stagehand for structured data extraction + stagehand_config = StagehandConfig( + env="BROWSERBASE", + api_key=os.environ.get("BROWSERBASE_API_KEY"), + project_id=os.environ.get("BROWSERBASE_PROJECT_ID"), + verbose=1, + # 0 = errors only, 1 = info, 2 = debug + # (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) + # https://docs.stagehand.dev/configuration/logging + model_name="openai/gpt-4.1", + model_api_key=os.environ.get("OPENAI_API_KEY"), + browserbase_session_id=session.id, # Use the existing Browserbase session + ) + + stagehand = Stagehand(stagehand_config) + + try: + # Initialize Stagehand + await stagehand.init() + + # Navigate to IP info service to verify proxy location and IP address. + await stagehand.page.goto("https://ipinfo.io/json", wait_until="domcontentloaded") + + # Extract structured IP and location data using Stagehand and Pydantic schema + geo_info = await stagehand.page.extract( + instruction="Extract all IP information and geolocation data from the JSON response", + schema=GeoInfo + ) + + print("Geo Info:", geo_info.model_dump_json(indent=2)) + + # Close Stagehand session + await stagehand.close() + except Exception as error: + print(f"Error during Stagehand extraction: {error}") # Close browser to release resources and end the test session. await browser.close() diff --git a/typescript/context/README.md b/typescript/context/README.md index 7c6fe3a8..9655a83c 100644 --- a/typescript/context/README.md +++ b/typescript/context/README.md @@ -14,16 +14,12 @@ Docs → https://docs.stagehand.dev/basics/act ## QUICKSTART - 1) pnpm init - 2) pnpm add @browserbasehq/stagehand dotenv @types/node axios zod - pnpm add -D typescript && pnpm tsc --init - 3) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - SF_REC_PARK_EMAIL=... - SF_REC_PARK_PASSWORD=... - 4) Compile + run: - pnpm tsc && node context.js + 1) cd context-template + 2) npm install + 3) npm install axios + 4) cp .env.example .env + 5) Add your Browserbase API key, Project ID, and SF Rec Park credentials to .env + 6) npm start ## EXPECTED OUTPUT - Creates context, performs login, saves auth state diff --git a/typescript/context/index.ts b/typescript/context/index.ts index 3327e364..3cf41523 100644 --- a/typescript/context/index.ts +++ b/typescript/context/index.ts @@ -33,7 +33,8 @@ async function createSessionContextID() { console.log("Connecting Stagehand to session..."); const stagehand = new Stagehand({ env: "BROWSERBASE", - modelName: "gpt-4o", + modelName: "openai/gpt-4.1", + verbose: 1, browserbaseSessionID: session.id, }); @@ -95,7 +96,8 @@ async function main() { // persist: true ensures any new changes (cookies, cache) are saved back to context. const stagehand = new Stagehand({ env: "BROWSERBASE", - modelName: "gpt-4o", + modelName: "openai/gpt-4.1", + verbose: 1, browserbaseSessionCreateParams: { projectId: process.env.BROWSERBASE_PROJECT_ID!, browserSettings: { diff --git a/typescript/formFilling/README.md b/typescript/form-filling/README.md similarity index 89% rename from typescript/formFilling/README.md rename to typescript/form-filling/README.md index 225cab80..b7cb3f74 100644 --- a/typescript/formFilling/README.md +++ b/typescript/form-filling/README.md @@ -15,15 +15,11 @@ - variable substitution: inject dynamic values into actions using `%variable%` syntax ## QUICKSTART - 1) pnpm init - 2) pnpm add @browserbasehq/stagehand dotenv @types/node zod - pnpm add -D typescript && pnpm tsc --init - 3) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - OPENAI_API_KEY=... - 4) Compile + run: - pnpm tsc && node formFilling.js + 1) cd form-fill-template + 2) npm install + 3) cp .env.example .env + 4) Add your Browserbase API key, Project ID, and OpenAI API key to .env + 5) npm start ## EXPECTED OUTPUT - Initializes Stagehand session with Browserbase diff --git a/typescript/formFilling/index.ts b/typescript/form-filling/index.ts similarity index 60% rename from typescript/formFilling/index.ts rename to typescript/form-filling/index.ts index 6622f4b6..0acfbd06 100644 --- a/typescript/formFilling/index.ts +++ b/typescript/form-filling/index.ts @@ -18,7 +18,8 @@ async function main() { // Initialize Stagehand with Browserbase for cloud-based browser automation. const stagehand = new Stagehand({ env: "BROWSERBASE", - modelName: "gpt-4o", + modelName: "openai/gpt-4.1", + verbose: 1, browserbaseSessionCreateParams: { projectId: process.env.BROWSERBASE_PROJECT_ID!, } @@ -39,34 +40,42 @@ async function main() { timeout: 60000 // Extended timeout for reliable page loading. }); - // Analyze form structure to identify fillable fields before attempting to fill. - console.log("Analyzing form fields..."); - const contact_page = await page.observe({ - instruction: "What are the fields that can be filled in?", - returnAction: true // Return action objects for potential reuse with act(). + // Single observe call to plan all form filling + const formFields = await page.observe({ + instruction: `Find form fields for: first name, last name, company, job title, email, message`, + returnAction: true }); - console.log("Available form fields:", contact_page); - // Fill form using a more reliable approach with individual field targeting. - console.log("Filling in contact form..."); + // Execute all actions without LLM calls + for (const field of formFields) { + // Match field to data based on description + let value = ''; + const desc = field.description.toLowerCase(); + + if (desc.includes('first name')) value = firstName; + else if (desc.includes('last name')) value = lastName; + else if (desc.includes('company')) value = company; + else if (desc.includes('job title')) value = jobTitle; + else if (desc.includes('email')) value = email; + else if (desc.includes('message')) value = message; + + if (value) { + await page.act({ + ...field, + arguments: [value] + }); + } + } - // Fill each field individually for better reliability - await page.act(`Fill in the first name field with "${firstName}"`); - await page.act(`Fill in the last name field with "${lastName}"`); - await page.act(`Fill in the company field with "${company}"`); - await page.act(`Fill in the job title field with "${jobTitle}"`); - await page.act(`Fill in the email field with "${email}"`); - await page.act(`Fill in the message field with "${message}"`); - - // Language choice in Stagehand act() is crucial for reliable automation. - // Use "click" for dropdown interactions rather than "select" - await page.act("Click on the How Can we help? dropdown"); - await page.waitForTimeout(500); - await page.act("Click on the first option from the dropdown"); - // await page.act("Select the first option from the dropdown"); // Less reliable than "click" + // Language choice in Stagehand act() is crucial for reliable automation. + // Use "click" for dropdown interactions rather than "select" + await page.act("Click on the How Can we help? dropdown"); + await page.waitForTimeout(500); + await page.act("Click on the first option from the dropdown"); + // await page.act("Select the first option from the dropdown"); // Less reliable than "click" - // Uncomment the line below if you want to submit the form - // await page.act("Click the submit button"); + // Uncomment the line below if you want to submit the form + // await page.act("Click the submit button"); console.log("Form filled successfully! Waiting 3 seconds..."); await page.waitForTimeout(30000); diff --git a/typescript/giftfinder/README.md b/typescript/gift-finder/README.md similarity index 90% rename from typescript/giftfinder/README.md rename to typescript/gift-finder/README.md index 4713aaf7..a5b349de 100644 --- a/typescript/giftfinder/README.md +++ b/typescript/gift-finder/README.md @@ -17,15 +17,12 @@ Docs → https://docs.browserbase.com/features/proxies ## QUICKSTART - 1) pnpm init - 2) pnpm add @browserbasehq/stagehand dotenv @types/node inquirer openai zod - pnpm add -D typescript && pnpm tsc --init - 3) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - OPENAI_API_KEY=... - 4) Compile + run: - pnpm tsc && node giftFinder.js + 1) cd gift-finder-template + 2) npm install + 3) npm install inquirer openai + 4) cp .env.example .env + 5) Add your Browserbase API key, Project ID, and OpenAI API key to .env + 6) npm start ## EXPECTED OUTPUT - Prompts user for recipient and description diff --git a/typescript/giftfinder/index.ts b/typescript/gift-finder/index.ts similarity index 99% rename from typescript/giftfinder/index.ts rename to typescript/gift-finder/index.ts index 3bdf026b..e318eff7 100644 --- a/typescript/giftfinder/index.ts +++ b/typescript/gift-finder/index.ts @@ -34,7 +34,7 @@ async function generateSearchQueries(recipient: string, description: string): Pr // Use AI to generate search terms based on recipient profile // This avoids generic searches and focuses on thoughtful, complementary gifts const response = await client.chat.completions.create({ - model: "gpt-4o", + model: "gpt-4.1", messages: [ { role: "user", @@ -93,7 +93,7 @@ async function scoreProducts( console.log(`Scoring ${allProducts.length} products...`); const response = await client.chat.completions.create({ - model: "gpt-4o", + model: "gpt-4.1", messages: [ { role: "user", @@ -232,11 +232,11 @@ async function main(): Promise { // Each session searches independently to maximize speed const sessionStagehand = new Stagehand({ env: "BROWSERBASE", - verbose: 0, + verbose: 1, // 0 = errors only, 1 = info, 2 = debug // (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) // https://docs.stagehand.dev/configuration/logging - modelName: "gpt-4o", + modelName: "openai/gpt-4.1", browserbaseSessionCreateParams: { projectId: process.env.BROWSERBASE_PROJECT_ID!, // Proxies require Developer Plan or higher - comment in if you have a Developer Plan or higher @@ -387,5 +387,4 @@ main().catch((err) => { console.error("Application error:", err); console.log("Check your environment variables and internet connection"); process.exit(1); -}); - +}); \ No newline at end of file diff --git a/typescript/realEstateCheck/README.md b/typescript/license-verification/README.md similarity index 90% rename from typescript/realEstateCheck/README.md rename to typescript/license-verification/README.md index 5557d533..8e553e1c 100644 --- a/typescript/realEstateCheck/README.md +++ b/typescript/license-verification/README.md @@ -17,14 +17,11 @@ - structured scraping: extracting consistent, typed data that can flow into apps, CRMs, or compliance systems. ## QUICKSTART - 1) pnpm init - 2) pnpm add @browserbasehq/stagehand dotenv @types/node zod - pnpm add -D typescript && pnpm tsc --init - 3) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - 4) Compile + run: - pnpm tsc && node product.js + 1) cd license-verification-template + 2) npm install + 3) cp .env.example .env + 4) Add your Browserbase API key and Project ID to .env + 5) npm start ## EXPECTED OUTPUT - Navigates to California DRE license verification website diff --git a/typescript/realEstateCheck/index.ts b/typescript/license-verification/index.ts similarity index 96% rename from typescript/realEstateCheck/index.ts rename to typescript/license-verification/index.ts index bcb73ceb..73b644ed 100644 --- a/typescript/realEstateCheck/index.ts +++ b/typescript/license-verification/index.ts @@ -1,4 +1,4 @@ -// Stagehand + Browserbase: Data Extraction with Structured Schemas - See README.md for full documentation +// Real Estate License Verification - See README.md for full documentation import "dotenv/config"; import { Stagehand } from "@browserbasehq/stagehand"; @@ -13,7 +13,8 @@ async function main() { // Initialize Stagehand with Browserbase for cloud-based browser automation. const stagehand = new Stagehand({ env: "BROWSERBASE", // Use Browserbase cloud browsers for reliable automation. - verbose: 0, + verbose: 1, + modelName: "openai/gpt-4.1", // 0 = errors only, 1 = info, 2 = debug // (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) // https://docs.stagehand.dev/configuration/logging @@ -74,4 +75,3 @@ main().catch((err) => { console.error(err); process.exit(1); }); - diff --git a/typescript/pickleball/README.md b/typescript/pickleball/README.md index af8dea32..5cd72898 100644 --- a/typescript/pickleball/README.md +++ b/typescript/pickleball/README.md @@ -20,16 +20,12 @@ ## QUICKSTART 1) Create an account with SF Recreation & Parks website -> https://www.rec.us/organizations/san-francisco-rec-park -2) pnpm init -3) pnpm add @browserbasehq/stagehand dotenv @types/node inquirer zod - pnpm add -D typescript && pnpm tsc --init -4) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - SF_REC_PARK_EMAIL=... - SF_REC_PARK_PASSWORD=... -5) Compile + run: - pnpm tsc && node pickleball.js +2) cd pickleball-template +3) npm install +4) npm install inquirer +5) cp .env.example .env +6) Add your Browserbase API key, Project ID, and SF Rec Park credentials to .env +7) npm start ## EXPECTED OUTPUT - Prompts user for activity type (Tennis/Pickleball), date, and time diff --git a/typescript/pickleball/index.ts b/typescript/pickleball/index.ts index 3c350d92..5b6adc0d 100644 --- a/typescript/pickleball/index.ts +++ b/typescript/pickleball/index.ts @@ -1,10 +1,10 @@ // SF Court Booking Automation - See README.md for full documentation import "dotenv/config"; -import { Stagehand } from "@browserbasehq/stagehand"; +import { Stagehand, StagehandPage } from "@browserbasehq/stagehand"; import inquirer from 'inquirer'; import { z } from 'zod'; -async function loginToSite(page: any, email: string, password: string): Promise { +async function loginToSite(page: StagehandPage, email: string, password: string): Promise { console.log("Logging in..."); // Perform login sequence: each step is atomic to handle dynamic page changes. await page.act("Click the Login button"); @@ -15,7 +15,7 @@ async function loginToSite(page: any, email: string, password: string): Promise< console.log("Logged in"); } -async function selectFilters(page: any, activity: string, timeOfDay: string, selectedDate: string): Promise { +async function selectFilters(page: StagehandPage, activity: string, timeOfDay: string, selectedDate: string): Promise { console.log("Selecting the activity"); // Filter by activity type first to narrow down available courts. await page.act(`Click the activites drop down menu`); @@ -54,7 +54,7 @@ async function selectFilters(page: any, activity: string, timeOfDay: string, sel await page.act(`Click the Done button`); } -async function checkAndExtractCourts(page: any, timeOfDay: string): Promise { +async function checkAndExtractCourts(page: StagehandPage, timeOfDay: string): Promise { console.log("Checking for available courts..."); // First observe the page to find all available court booking options. @@ -171,7 +171,7 @@ async function checkAndExtractCourts(page: any, timeOfDay: string): Promise { +async function bookCourt(page: StagehandPage): Promise { console.log("Starting court booking process..."); try { @@ -352,11 +352,11 @@ async function bookTennisPaddleCourt() { console.log("Initializing Stagehand with Browserbase"); const stagehand = new Stagehand({ env: "BROWSERBASE", - verbose: 0, + verbose: 1, // 0 = errors only, 1 = info, 2 = debug // (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) // https://docs.stagehand.dev/configuration/logging - modelName: "gpt-4o", + modelName: "openai/gpt-4.1", browserbaseSessionCreateParams: { projectId: process.env.BROWSERBASE_PROJECT_ID!, timeout: 900, diff --git a/typescript/proxies/README.md b/typescript/proxies/README.md index 457a86a7..945b76d3 100644 --- a/typescript/proxies/README.md +++ b/typescript/proxies/README.md @@ -8,14 +8,12 @@ Docs → https://docs.browserbase.com/features/proxies ## QUICKSTART - 1) pnpm init - 2) pnpm add @browserbasehq/sdk playwright-core dotenv - pnpm add -D typescript tsx && pnpm tsc --init - 3) Create .env with: - BROWSERBASE_PROJECT_ID=... - BROWSERBASE_API_KEY=... - 4) Run the script: - pnpm run proxies + 1) cd proxies-template + 2) npm install + 3) npm install @browserbasehq/sdk playwright-core + 4) cp .env.example .env + 5) Add your Browserbase API key and Project ID to .env + 6) npm start ## EXPECTED OUTPUT - Tests built-in proxy rotation diff --git a/typescript/proxies/index.ts b/typescript/proxies/index.ts index c917349c..40d377c4 100644 --- a/typescript/proxies/index.ts +++ b/typescript/proxies/index.ts @@ -2,6 +2,8 @@ import { chromium } from "playwright-core"; import { Browserbase } from "@browserbasehq/sdk"; +import { Stagehand } from "@browserbasehq/stagehand"; +import { z } from "zod"; import dotenv from "dotenv"; dotenv.config(); @@ -13,10 +15,6 @@ async function createSessionWithBuiltInProxies() { const session = await bb.sessions.create({ projectId: process.env.BROWSERBASE_PROJECT_ID!, proxies: true, // Enables automatic proxy rotation across different IP addresses. - verbose: 0, - // 0 = errors only, 1 = info, 2 = debug - // (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) - // https://docs.stagehand.dev/configuration/logging }); return session; } @@ -25,10 +23,6 @@ async function createSessionWithGeoLocation() { // Route traffic through specific geographic location to test location-based restrictions. const session = await bb.sessions.create({ projectId: process.env.BROWSERBASE_PROJECT_ID!, - verbose: 0, - // 0 = errors only, 1 = info, 2 = debug - // (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) - // https://docs.stagehand.dev/configuration/logging proxies: [ { "type": "browserbase", // Use Browserbase's managed proxy infrastructure. @@ -47,10 +41,6 @@ async function createSessionWithCustomProxies() { // Use external proxy servers for custom routing or specific proxy requirements. const session = await bb.sessions.create({ projectId: process.env.BROWSERBASE_PROJECT_ID!, - verbose: 0, - // 0 = errors only, 1 = info, 2 = debug - // (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) - // https://docs.stagehand.dev/configuration/logging proxies: [ { "type": "external", // Connect to your own proxy server infrastructure. @@ -82,10 +72,47 @@ async function testSession(sessionFunction: () => Promise, sessionName: str throw new Error("No page found in default context"); } - // Navigate to IP info service to verify proxy location and IP address. - await page.goto("https://ipinfo.io/json", { waitUntil: "domcontentloaded" }); - const geoInfo = await page.textContent('pre'); // Extract JSON response containing IP and location data. - console.log("Geo Info:", geoInfo); + // Initialize Stagehand for structured data extraction + const stagehand = new Stagehand({ + env: "BROWSERBASE", + verbose: 1, + // 0 = errors only, 1 = info, 2 = debug + // (When handling sensitive data like passwords or API keys, set verbose: 0 to prevent secrets from appearing in logs.) + // https://docs.stagehand.dev/configuration/logging + modelName: "openai/gpt-4.1", + browserbaseSessionID: session.id, // Use the existing Browserbase session + }); + + try { + // Initialize Stagehand + await stagehand.init(); + + // Navigate to IP info service to verify proxy location and IP address. + await stagehand.page.goto("https://ipinfo.io/json", { waitUntil: "domcontentloaded" }); + + // Extract structured IP and location data using Stagehand and Zod schema + const geoInfo = await stagehand.page.extract({ + instruction: "Extract all IP information and geolocation data from the JSON response", + schema: z.object({ + ip: z.string().optional().describe("The IP address"), + city: z.string().optional().describe("The city name"), + region: z.string().optional().describe("The state or region"), + country: z.string().optional().describe("The country code"), + loc: z.string().optional().describe("The latitude and longitude coordinates"), + timezone: z.string().optional().describe("The timezone"), + org: z.string().optional().describe("The organization or ISP"), + postal: z.string().optional().describe("The postal code"), + hostname: z.string().optional().describe("The hostname if available") + }) + }); + + console.log("Geo Info:", JSON.stringify(geoInfo, null, 2)); + + // Close Stagehand session + await stagehand.close(); + } catch (error) { + console.error("Error during Stagehand extraction:", error); + } // Close browser to release resources and end the test session. await browser.close(); @@ -104,5 +131,4 @@ async function main() { console.log("\n=== All tests completed ==="); } -main(); - +main(); \ No newline at end of file