MCP Explorer

Herd - Browser Superpowers & MCP Servers for AI Agents

DialtoneApp is using the stored discovery files and saved MCP scan metadata for this domain to look for an MCP endpoint, verify the handshake, and turn the result into a readable chat.

DialtoneApp found a lead, but the endpoint did not complete a usable MCP handshake.

idle
Visit site

Discovered endpoint

No endpoint found in stored discovery content

Server info

No successful initialize result yet.

Live Chat

MCP conversation

Statusinitialize handshake...

DialtoneApp could not start a live chat with this MCP server.

MCP Explorer

Herd - Browser Superpowers & MCP Servers for AI Agents

DialtoneApp is using the stored discovery files and saved MCP scan metadata for this domain to look for an MCP endpoint, verify the handshake, and turn the result into a readable chat.

DialtoneApp found a lead, but the endpoint did not complete a usable MCP handshake.

idle
Visit site

Discovered endpoint

No endpoint found in stored discovery content

Server info

No successful initialize result yet.

Live Chat

MCP conversation

Statusinitialize handshake...

DialtoneApp could not start a live chat with this MCP server.

method returns the first element that matches the CSS selector, or `null` if no element is found.\n\n#### Typing Text\n\nTo type text into an input field:\n\nCode (javascript):\n// Type text into an input field\nawait page.type('input[name=\"q\"]', 'Monitoro Herd automation');\nconsole.log('Text entered into search box');\n\nThe `type` method finds the element using the CSS selector and simulates typing the specified text.\n\n#### Clicking Elements\n\nTo click a button or link:\n\nCode (javascript):\n// Click a button\nawait page.click('input[type=\"submit\"]');\nconsole.log('Search button clicked');\n\nBy default, the `click` method just clicks the element. If you want to wait for navigation to complete after clicking:\n\nCode (javascript):\n// Click and wait for navigation\nawait page.click('input[type=\"submit\"]', { \n waitForNavigation: 'networkidle2' \n});\nconsole.log('Search button clicked and navigation completed');\n\nThe `networkidle2` option waits until there are no more than 2 network connections for at least 500ms.\n\n#### Waiting for Elements\n\nSometimes you need to wait for elements to appear on the page:\n\nCode (javascript):\n// Wait for an element to appear\nawait page.waitForSelector('#search');\nconsole.log('Search results have loaded');\n\nThis is useful when dealing with dynamic content that loads after the initial page load.\n\n#### Search Engine Example\n\nLet's put these concepts together in a search engine example:\n\nCode (javascript):\nasync function searchExample() {\n const client = new HerdClient('your-token');\n \n try {\n await client.initialize();\n const devices = await client.listDevices();\n const device = devices[0];\n const page = await device.newPage();\n \n // Navigate to a search engine\n console.log('Navigating to Google...');\n await page.goto('https://www.google.com');\n \n // Type in the search box\n console.log('Entering search query...');\n await page.type('input[name=\"q\"]', 'Monitoro Herd automation');\n \n // Submit the search form and wait for results\n console.log('Submitting search...');\n await page.click('input[type=\"submit\"]', { \n waitForNavigation: 'networkidle2' \n });\n \n // Wait for results to load completely\n console.log('Waiting for search results...');\n await page.waitForSelector('#search');\n \n // Extract search result titles\n console.log('Extracting search results...');\n const searchResults = await page.extract({\n titles: {\n _$r: '#search .g h3', // _$r extracts multiple elements\n text: ':root' // For each match, get its text\n }\n });\n \n // Display the search result titles\n console.log('\\nSearch Results:');\n searchResults.titles.forEach((result, index) =\u003e {\n console.log(`${index + 1}. ${result.text}`);\n });\n \n } catch (error) {\n console.error('Error:', error);\n } finally {\n await client.close();\n }\n}\n\n## python\n\n## Python SDK\n\n### Setting Up Your Environment\n\nBefore writing any code, you'll need to set up your Python environment and install the Herd SDK:\n\n1. Make sure you have Python 3.8+ installed\n2. Create a virtual environment (recommended)\n3. Install the SDK using pip:\n\nCode (bash):\npip install monitoro-herd\n\n### Initializing the Client\n\nThe first step in any automation is to initialize the Herd client with your API credentials:\n\nCode (python):\n# Import the Herd client\nfrom monitoro_herd import HerdClient\n\n# Initialize the client with your API URL and token\nclient = HerdClient('your-token')\n\n# Always initialize the client before using it\nclient.initialize()\nNote: **Note:** Replace the token with your actual Herd API token from your dashboard.\n\n### Connecting to a Device\n\nNext, connect to a device that will run your automation:\n\nCode (python):\n# Get available devices\ndevices = await client.list_devices()\n\n# Connect to the first device\ndevice = devices[0]\n\nprint(f\"Connected to device: {device.id}\")\n\n### Creating a Page and Navigating\n\nNow create a browser page and navigate to a website:\n\nCode (python):\n# Create a new page\npage = await device.new_page()\n\n# Navigate to a website\nawait page.goto(\"https://example.com\")\n\nprint(\"Successfully navigated to example.com\")\n\n### Extracting Basic Information\n\nExtract information from the page using CSS selectors:\n\nCode (python):\n# Extract basic information\ndata = await page.extract({\n \"title\": \"h1\", # Main heading\n \"description\": \"p\", # First paragraph\n \"link\": \"a\" # First link text\n})\n\n# Display the extracted data\nprint(\"Extracted data:\")\nprint(f\"Title: {data['title']}\")\nprint(f\"Description: {data['description']}\")\nprint(f\"Link: {data['link']}\")\n\n### Resource Management\n\nAlways close resources when you're done:\n\nCode (python):\n# Close the page\nawait page.close()\n\n# Close the client\nawait client.close()\n\n### Complete Basic Example\n\nHere's a complete example putting all these concepts together:\n\nCode (python):\nimport asyncio\nfrom monitoro_herd import HerdClient\n\nasync def basic_extraction():\n # Initialize the client\n client = HerdClient(\"your-token\")\n \n try:\n # Initialize the connection\n await client.initialize()\n print(\"Client initialized successfully\")\n \n # Get the first available device\n devices = await client.list_devices()\n if not devices:\n raise Exception(\"No devices available\")\n device = devices[0]\n print(f\"Connected to device: {device.id}\")\n \n # Create a new page\n page = await device.new_page()\n print(\"New page created\")\n \n # Navigate to a website\n print(\"Navigating to example.com...\")\n await page.goto(\"https://example.com\")\n print(\"Navigation complete\")\n \n # Extract data using simple selectors\n print(\"Extracting content...\")\n data = await page.extract({\n \"title\": \"h1\",\n \"description\": \"p\",\n \"link\": \"a\"\n })\n \n # Display the extracted data\n print(\"\\nExtracted data:\")\n print(f\"Title: {data['title']}\")\n print(f\"Description: {data['description']}\")\n print(f\"Link: {data['link']}\")\n \n except Exception as e:\n print(f\"Error during automation: {e}\")\n finally:\n # Always close resources\n print(\"Closing client connection...\")\n await client.close()\n print(\"Client connection closed\")\n\n# Run the async function\nasyncio.run(basic_extraction())\n\n### Working with Lists and Structured Data\n\nOne of the most powerful features of Herd is the ability to extract structured data from lists of elements. This is perfect for scraping search results, product listings, or article collections.\n\n#### The `_$r` Selector\n\nTo extract multiple elements that match a pattern, use the `_$r` (repeat) selector:\n\nCode (python):\n# Extract a list of items\ndata = await page.extract({\n \"items\": {\n \"_$r\": \".item\", # Find all elements with class \"item\"\n \"name\": \".item-name\", # For each item, get the name\n \"price\": \".price\" # For each item, get the price\n }\n})\n\n# Access the extracted items\nfor item in data[\"items\"]:\n print(f\"Name: {item['name']}, Price: {item['price']}\")\n\nThe `_$r` selector tells Herd to find all elements matching the selector and extract the specified properties for each one.\n\n#### Extracting Attributes\n\nSometimes you need to extract an attribute rather than the text content:\n\nCode (python):\n# Extract links and their href attributes\ndata = await page.extract({\n \"links\": {\n \"_$r\": \"a\", # Find all links\n \"text\": \":root\", # Get the link text\n \"url\": {\n \"_$\": \":root\", # Reference the same element\n \"attribute\": \"href\" # Get its href attribute\n }\n }\n})\n\n# Display the links\nfor link in data[\"links\"]:\n print(f\"Link: {link['text']} -\u003e {link['url']}\")\n\n#### Hacker News Example\n\nLet's put these concepts together to scrape stories from Hacker News:\n\nCode (python):\nimport asyncio\nfrom monitoro_herd import HerdClient\n\nasync def scrape_hacker_news():\n client = HerdClient(\"your-token\")\n \n try:\n await client.initialize()\n devices = await client.list_devices()\n device = devices[0]\n page = await device.new_page()\n \n # Navigate to Hacker News\n print(\"Navigating to Hacker News...\")\n await page.goto(\"https://news.ycombinator.com\")\n \n # Extract stories and their metadata\n print(\"Extracting stories...\")\n data = await page.extract({\n # Extract the story elements\n \"stories\": {\n \"_$r\": \".athing\", # Each story row\n \"title\": \".titleline \u003e a\", # Story title\n \"site\": \".sitestr\", # Source website\n \"link\": {\n \"_$\": \".titleline \u003e a\", # Story link\n \"attribute\": \"href\" # Get the URL\n }\n },\n # Extract the metadata (points, author, etc.)\n \"metadata\": {\n \"_$r\": \".subline\", # Metadata rows\n \"points\": \".score\", # Points count\n \"author\": \".hnuser\", # Author username\n \"time\": \".age\" # Submission time\n }\n })\n \n # Combine stories with their metadata\n # (They're in separate lists but in the same order)\n combined_stories = list(zip(data[\"stories\"], data[\"metadata\"]))\n \n # Display the first 3 stories\n print(f\"\\nExtracted {len(combined_stories)} stories:\")\n for i, (story, meta) in enumerate(combined_stories[:3]):\n print(f\"\\nStory {i+1}:\")\n print(f\"Title: {story['title']}\")\n if \"site\" in story:\n print(f\"Site: {story['site']}\")\n print(f\"Link: {story['link']}\")\n if \"points\" in meta:\n print(f\"Points: {meta['points']}\")\n if \"author\" in meta:\n print(f\"Author: {meta['author']}\")\n if \"time\" in meta:\n print(f\"Posted: {meta['time']}\")\n \n finally:\n await page.close()\n await client.close()\n\n# Run the function\nasyncio.run(scrape_hacker_news())\n\n## Tips for Successful Automation\n\n1. **Start Simple**: Begin with basic extractions before moving to complex interactions\n2. **Use Appropriate Selectors**: Learn CSS selectors to target elements precisely\n3. **Handle Errors**: Always include try/catch (JavaScript) or try/except (Python) blocks\n4. **Close Resources**: Always close pages and clients when done to avoid resource leaks\n5. **Test Incrementally**: Build your automation step by step, testing each part\n6. **Add Delays When Needed**: For dynamic content, use `waitForSelector` or similar methods\n7. **Debug with Screenshots**: Take screenshots during automation to see what's happening\n\n## Next Steps\n\nNow that you've created your first automation, you can:\n\n- Explore more complex selectors and extraction patterns\n- Learn how to handle authentication and login flows\n- Set up scheduled automations for regular data collection\n- Integrate with your existing systems via APIs\n\n==\n\nDocument: Data Extraction\nURL: https://herd.garden/docs/data-extraction\n\n# Data Extraction\n\nWelcome to Monitoro Herd's powerful data extraction system! This guide will walk you through how to extract structured data from web pages using our intuitive selector system and transformation pipelines.\n\n## Understanding Selectors\n\nHerd provides a flexible and powerful way to extract data from web pages using a declarative JSON-based selector system.\n\n### Basic Extraction\n\n## javascript\n\nThe simplest form of extraction uses CSS selectors to target elements:\n\nCode (javascript):\n// Extract basic text content\nconst data = await page.extract({\n title: 'h1', // Extracts the main heading\n description: 'p', // Extracts the first paragraph\n link: 'a' // Extracts the first link text\n});\n\nconsole.log(data.title); // \"Welcome to Our Website\"\nconsole.log(data.description); // \"This is our homepage.\"\n\n## python\n\nThe simplest form of extraction uses CSS selectors to target elements:\n\nCode (python):\n# Extract basic text content\ndata = await page.extract({\n \"title\": \"h1\", # Extracts the main heading\n \"description\": \"p\", # Extracts the first paragraph\n \"link\": \"a\" # Extracts the first link text\n})\n\nprint(data[\"title\"]) # \"Welcome to Our Website\"\nprint(data[\"description\"]) # \"This is our homepage.\"\n\n### Advanced Selector Syntax\n\n## javascript\n\nFor more complex extraction needs, use the expanded object syntax:\n\nCode (javascript):\nconst data = await page.extract({\n title: {\n _$: 'h1', // CSS selector\n attribute: 'id' // Extract the ID attribute instead of text\n },\n price: {\n _$: '.price', // Target price element\n pipes: ['parseNumber'] // Apply transformation\n }\n});\n\n## python\n\nFor more complex extraction needs, use the expanded object syntax:\n\nCode (python):\ndata = await page.extract({\n \"title\": {\n \"_$\": \"h1\", # CSS selector\n \"attribute\": \"id\" # Extract the ID attribute instead of text\n },\n \"price\": {\n \"_$\": \".price\", # Target price element\n \"pipes\": [\"parseNumber\"] # Apply transformation\n }\n})\n\n### Extracting Lists of Items\n\n## javascript\n\nTo extract multiple elements that match a pattern, use the `_$r` (repeat) selector:\n\nCode (javascript):\nconst data = await page.extract({\n items: {\n _$r: '.item', // Find all elements with class \"item\"\n title: 'h2', // For each item, get the title\n price: '.price', // For each item, get the price\n date: 'time' // For each item, get the date\n }\n});\n\n// Access the extracted items\ndata.items.forEach(item =\u003e {\n console.log(`${item.title}: ${item.price}, Posted: ${item.date}`);\n});\n\n## python\n\nTo extract multiple elements that match a pattern, use the `_$r` (repeat) selector:\n\nCode (python):\ndata = await page.extract({\n \"items\": {\n \"_$r\": \".item\", # Find all elements with class \"item\"\n \"title\": \"h2\", # For each item, get the title\n \"price\": \".price\", # For each item, get the price\n \"date\": \"time\" # For each item, get the date\n }\n})\n\n# Access the extracted items\nfor item in data[\"items\"]:\n print(f\"{item['title']}: {item['price']}, Posted: {item['date']}\")\n\n### Nested Extraction\n\n## javascript\n\nYou can nest selectors to extract hierarchical data:\n\nCode (javascript):\nconst data = await page.extract({\n product: {\n name: '.product-name',\n details: {\n _$: '.product-details',\n specs: {\n _$r: '.spec-item',\n label: '.spec-label',\n value: '.spec-value'\n }\n }\n }\n});\n\n## python\n\nYou can nest selectors to extract hierarchical data:\n\nCode (python):\ndata = await page.extract({\n \"product\": {\n \"name\": \".product-name\",\n \"details\": {\n \"_$\": \".product-details\",\n \"specs\": {\n \"_$r\": \".spec-item\",\n \"label\": \".spec-label\",\n \"value\": \".spec-value\"\n }\n }\n }\n})\n\n## Special Selectors\n\nHerd provides special selectors to handle various extraction scenarios:\n\n### Root Selector (`:root`)\n\nThe `:root` selector refers to the current element in context:\n\n## javascript\n\nCode (javascript):\nconst data = await page.extract({\n items: {\n _$r: '.item',\n someElement: ':root', // Extract text of the .item element itself\n classes: {\n _$: ':root',\n attribute: 'class' // Extract class attribute of the same element\n }\n }\n});\n\n## python\n\nCode (python):\ndata = await page.extract({\n \"items\": {\n \"_$r\": \".item\",\n \"someElement\": \":root\", # Extract text of the .item element itself\n \"classes\": {\n \"_$\": \":root\",\n \"attribute\": \"class\" # Extract class attribute of the same element\n }\n }\n})\n\n### Property Extraction\n\nYou can extract JavaScript properties from elements:\n\n## javascript\n\nCode (javascript):\nconst data = await page.extract({\n dimensions: {\n _$: '.box',\n property: 'getBoundingClientRect' // Get element dimensions\n },\n html: {\n _$: '.content',\n property: 'innerHTML' // Get inner HTML\n }\n});\n\n## python\n\nCode (python):\ndata = await page.extract({\n \"dimensions\": {\n \"_$\": \".box\",\n \"property\": \"getBoundingClientRect\" # Get element dimensions\n },\n \"html\": {\n \"_$\": \".content\",\n \"property\": \"innerHTML\" # Get inner HTML\n }\n})\n\n## Transformation Pipelines\n\nHerd includes powerful transformation pipelines to process extracted data:\n\n### Available Transformations\n\n| Pipe | Description | Example Input | Example Output |\n|------|-------------|--------------|----------------|\n| `trim` | Removes whitespace from start/end | `\" Hello \"` | `\"Hello\"` |\n| `toLowerCase` | Converts text to lowercase | `\"HELLO\"` | `\"hello\"` |\n| `toUpperCase` | Converts text to uppercase | `\"hello\"` | `\"HELLO\"` |\n| `parseNumber` | Extracts numbers from text | `\"$1,2K.45\"` | `1200.45` |\n| `parseDate` | Converts text to date | `\"2024-01-15\"` | `\"2024-01-15T00:00:00.000Z\"` |\n| `parseDateTime` | Converts text to datetime | `\"2024-01-15T12:00:00Z\"` | `\"2024-01-15T12:00:00.000Z\"` |\n\n### Using Transformations\n\nApply transformations using the `pipes` property:\n\n## javascript\n\nCode (javascript):\nconst data = await page.extract({\n price: {\n _$: '.price',\n pipes: ['parseNumber'] // Convert \"$1,234.56\" to 1234.56\n },\n title: {\n _$: 'h1',\n pipes: ['trim', 'toLowerCase'] // Apply multiple transformations\n }\n});\n\n## python\n\nCode (python):\ndata = await page.extract({\n \"price\": {\n \"_$\": \".price\",\n \"pipes\": [\"parseNumber\"] # Convert \"$1,234.56\" to 1234.56\n },\n \"title\": {\n \"_$\": \"h1\",\n \"pipes\": [\"trim\", \"toLowerCase\"] # Apply multiple transformations\n }\n})\n\n### Handling Currency and Large Numbers\n\nThe `parseNumber` transformation handles various formats:\n\n## javascript\n\nCode (javascript):\nconst data = await page.extract({\n price1: {\n _$: '.price-1', // Contains \"$1,234.56\"\n pipes: ['parseNumber'] // Result: 1234.56\n },\n price2: {\n _$: '.price-2', // Contains \"$1.5M\"\n pipes: ['parseNumber'] // Result: 1500000\n },\n price3: {\n _$: '.price-3', // Contains \"1.5T€\"\n pipes: ['parseNumber'] // Result: 1500000000000\n }\n});\n\n## python\n\nCode (python):\ndata = await page.extract({\n \"price1\": {\n \"_$\": \".price-1\", # Contains \"$1,234.56\"\n \"pipes\": [\"parseNumber\"] # Result: 1234.56\n },\n \"price2\": {\n \"_$\": \".price-2\", # Contains \"$1.5M\"\n \"pipes\": [\"parseNumber\"] # Result: 1500000\n },\n \"price3\": {\n \"_$\": \".price-3\", # Contains \"1.5T€\"\n \"pipes\": [\"parseNumber\"] # Result: 1500000000000\n }\n})\n\n## Real-World Examples\n\nLet's look at some practical examples of data extraction:\n\n### E-commerce Product Listing\n\nExtract products from a search results page:\n\n## javascript\n\nCode (javascript):\nconst searchResults = await page.extract({\n products: {\n _$r: '[data-component-type=\"s-search-result\"]',\n title: {\n _$: 'h2 .a-link-normal',\n pipes: ['trim']\n },\n price: {\n _$: '.a-price .a-offscreen',\n pipes: ['parseNumber']\n },\n rating: {\n _$: '.a-icon-star-small .a-icon-alt',\n pipes: ['trim']\n },\n reviews: {\n _$: '.a-size-base.s-underline-text',\n pipes: ['trim']\n }\n }\n});\n\n## python\n\nCode (python):\nsearchResults = await page.extract({\n \"products\": {\n \"_$r\": '[data-component-type=\"s-search-result\"]',\n \"title\": {\n \"_$\": \"h2 .a-link-normal\",\n \"pipes\": [\"trim\"]\n },\n \"price\": {\n \"_$\": \".a-price .a-offscreen\",\n \"pipes\": [\"parseNumber\"]\n },\n \"rating\": {\n \"_$\": \".a-icon-star-small .a-icon-alt\",\n \"pipes\": [\"trim\"]\n },\n \"reviews\": {\n \"_$\": \".a-size-base.s-underline-text\",\n \"pipes\": [\"trim\"]\n }\n }\n})\n\n### News Article List\n\nExtract articles from a news site:\n\n## javascript\n\nCode (javascript):\nconst articles = await page.extract({\n items: {\n _$r: '.item',\n title: {\n _$: 'h2',\n pipes: ['trim', 'toLowerCase']\n },\n price: {\n _$: '.price',\n pipes: ['parseNumber']\n },\n date: {\n _$: 'time',\n pipes: ['parseDate']\n }\n }\n});\n\n## python\n\nCode (python):\narticles = await page.extract({\n \"items\": {\n \"_$r\": \".item\",\n \"title\": {\n \"_$\": \"h2\",\n \"pipes\": [\"trim\", \"toLowerCase\"]\n },\n \"price\": {\n \"_$\": \".price\",\n \"pipes\": [\"parseNumber\"]\n },\n \"date\": {\n \"_$\": \"time\",\n \"pipes\": [\"parseDate\"]\n }\n }\n})\n\n## Advanced Techniques\n\n### Handling Dynamic Content\n\nFor dynamic content that loads after the page is ready:\n\n## javascript\n\nCode (javascript):\n// Wait for dynamic content to load\nawait page.waitForElement('#dynamic span');\n\n// Then extract the content\nconst data = await page.extract({\n content: '#dynamic span'\n});\n\n## python\n\nCode (python):\n# Wait for dynamic content to load\nawait page.waitForElement('#dynamic span')\n\n# Then extract the content\ndata = await page.extract({\n \"content\": \"#dynamic span\"\n})\n\n### Extracting Page Metadata\n\nExtract information about the page itself:\n\n## javascript\n\nCode (javascript):\nconst pageInfo = await page.extract({\n title: 'title',\n metaDescription: 'meta[name=\"description\"]',\n canonicalUrl: {\n _$: 'link[rel=\"canonical\"]',\n attribute: 'href'\n }\n});\n\n## python\n\nCode (python):\npageInfo = await page.extract({\n \"title\": \"title\",\n \"metaDescription\": 'meta[name=\"description\"]',\n \"canonicalUrl\": {\n \"_$\": 'link[rel=\"canonical\"]',\n \"attribute\": \"href\"\n }\n})\n\n## Tips for Effective Extraction\n\n1. **Use Specific Selectors**: The more specific your CSS selectors, the more reliable your extraction\n2. **Test Incrementally**: Build your extraction schema step by step, testing each part\n3. **Handle Missing Data**: Always account for elements that might not exist on the page\n4. **Apply Appropriate Transformations**: Use pipes to clean and format data as needed\n5. **Combine with Interactions**: For complex sites, interact with the page before extraction\n\n## Next Steps\n\nNow that you understand Herd's data extraction system, you can:\n\n- Create complex extraction schemas for any website\n- Transform raw data into structured, usable formats\n- Build powerful automations that collect and process web data\n\n==\n\nDocument: Getting Started\nURL: https://herd.garden/docs/getting-started\n\n# Getting Started with Herd\n\nThis guide will help you get up and running with Herd quickly to run your first trail.\n\n## What is Herd?\n\nHerd connects AI Agents to websites using your own browser credentials. It enables you to:\n\n- **Run Trails** - pre-built automations for specific websites and tasks\n- **Extract data and interact with websites** using your logged-in browser sessions\n- **Interact with web pages** through AI Agents like OpenAI's ChatGPT and Anthropic's Claude\n\n## Quick Start\n\n### 1. Install the Browser Extension\n\n Chrome\n\n Edge\n\n Brave\n\n### 2. Register Your Browser\n\nAfter installing the extension:\n\n1. Click the Herd icon in your browser toolbar\n2. Sign in with your Herd account (or create one)\n3. Name your device and register it\n\n![Browser Registration](https://herd.garden/register-device.png)\n\n### 3. Install the Herd SDK\n\nInstall the Herd SDK using npm:\n\n## npm\n\nCode (bash):\nnpm install -g @monitoro/herd\n\n## yarn\n\nCode (bash):\nyarn global add @monitoro/herd\n\n## pnpm\n\nCode (bash):\npnpm add -g @monitoro/herd\n\n### 4. Run Your First Trail\n\nThe browser trail provides core functionality for navigating and extracting data from any website. Run this command to test it out:\n\nCode (bash):\nherd trail run @herd/browser -a markdown -p '{\"url\": \"https://example.com\"}'\n\nThat's it! Add it to your MCP config to use it in your AI agents like in this example. Note, you can add as many trails as you want to your MCP config:\n\nCode (json):\n{\n \"mcpServers\": {\n \"browser\": {\n \"command\": \"herd\",\n \"args\": [\n \"trail\",\n \"server\",\n \"@herd/browser\"\n ]\n }\n }\n}\n\n## For Developers\n\nYou can also automate your browser with the Herd SDK. Connect to it with your AI agents or code:\n\n## javascript\n\nCode (javascript):\n// Connect to your Herd device\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Create a new page and navigate\nconst page = await device.newPage();\nawait page.goto(\"https://example.com\");\n\n// Extract data using simple selectors\nconst data = await page.extract({\n title: \"h1\",\n description: \"p\",\n link: \"a\"\n});\n\nconsole.log(\"Extracted data:\", data);\n\n## python\n\nCode (python):\nfrom monitoro_herd import HerdClient\n\n# Connect to your Herd device\nclient = HerdClient(\"your-token\")\nawait client.initialize()\ndevices = await client.list_devices()\ndevice = devices[0]\n\n# Create a new page and navigate\npage = await device.new_page()\nawait page.goto(\"https://example.com\")\n\n# Extract data using simple selectors\ndata = await page.extract({\n \"title\": \"h1\",\n \"description\": \"p\",\n \"link\": \"a\"\n})\n\nprint(\"Extracted data:\", data)\n\n## What's Next?\n\nNow that you've run your first trail, you can:\n\n- [Explore available trails](/trails) - Browse pre-built trails for various websites\n- [Learn about data extraction](/docs/data-extraction) - Extract structured data from web pages\n- [Create your own trail](/docs/trails-automations) - Build and share your own custom trails\n\n## Need Help?\n\nIf you encounter any issues during setup:\n\n- Make sure your browser extension is correctly installed and you're signed in\n- Check that your device is registered in the [device dashboard](/devices)\n- Visit our [troubleshooting guide](/docs/troubleshooting) for common solutions\n\n.browser-btn {\n display: inline-flex;\n align-items: center;\n padding: 0.2rem 1rem;\n background-color: #1f2937;\n color: white;\n border-radius: 0.375rem;\n font-size: 1.2rem;\n font-weight: 500;\n text-decoration: none;\n border: 1px solid rgba(255, 255, 255, 0.1);\n}\n\n.browser-btn:hover {\n background-color: #374151;\n}\n\n==\n\nDocument: Trails Automations\nURL: https://herd.garden/docs/trails-automations\n\n# Trails\n\nTrails in the Herd platform are packaged automations that perform specific tasks such as extracting data or submitting forms. They make it easy to reuse automation logic across different projects and achieve high reliability through a solid and accessible testing process.\n\n## javascript\n\nTrails are fully supported in the JavaScript SDK.\n\n## Creating a Trail\n\nA trail defines the following components:\n\n- **urls.ts**: Exports an array of URL definitions\n- **selectors.ts**: Exports an array of selector configurations\n- **actions.ts**: Exports action classes that implement the `TrailAction` interface\n\nA trail at the minimum has the following structure:\n\nCode:\ngoogle-search/\n urls.ts\n selectors.ts\n actions.ts\n package.json\n\nThe `package.json` file defines the trail and its dependencies:\n\nCode (json):\n{\n \"name\": \"google-search\",\n \"description\": \"Search google for webpages.\",\n \"version\": \"1.0.0\",\n \"dependencies\": {\n \"@monitoro/herd\": \"latest\"\n }\n}\n\nYou should never run the .ts files manually. Instead, use the Herd CLI to run, test, debug, and publish trails. Make sure to use version control to manage your trails, as publishing submits a built version to the Herd registry, not the source code.\n\n## Trail Implementation Guide\n\n### Step 1: Set up the trail structure\n\nCreate a new directory for your trail with the following files:\n\nCode:\nmy-trail/\n urls.ts\n selectors.ts\n actions.ts\n package.json\n\nAdd the basic package.json configuration:\n\nCode (json):\n{\n \"name\": \"my-trail\",\n \"version\": \"1.0.0\",\n \"dependencies\": {\n \"@monitoro/herd\": \"latest\"\n }\n}\n\n### Step 2: Define URLs\n\nIn `urls.ts`, export an array of URL definitions that your trail will interact with:\n\nCode (typescript):\nexport default [{\n \"id\": \"my-url\",\n \"template\": \"https://example.com/path?param1={param1}\u0026param2={param2}\",\n \"description\": \"Description of what this URL represents\",\n \"examples\": [\n { \"param1\": \"value1\", \"param2\": \"value2\" }\n ],\n \"params\": {\n \"param1\": {\n \"type\": \"string\",\n \"required\": true,\n \"description\": \"Description of param1\"\n },\n \"param2\": {\n \"type\": \"string\",\n \"required\": false,\n \"default\": \"defaultValue\",\n \"description\": \"Description of param2\"\n }\n }\n}];\n\n### Step 3: Define Selectors\n\nIn `selectors.ts`, export an array of selector configurations that define how to extract data from web pages:\n\nCode (typescript):\nexport default [{\n \"id\": \"my-selector\",\n \"value\": {\n \"dataKey\": {\n \"_$r\": \"#main-container\",\n \"title\": { \"_$\": \".title\" },\n \"description\": { \"_$\": \".description\" },\n \"link\": { \"_$\": \"a.link\", \"attribute\": \"href\" }\n }\n },\n \"description\": \"Selector for extracting specific data\",\n \"examples\": [\n {\n \"urlId\": \"my-url\",\n \"urlParams\": { \"param1\": \"value1\", \"param2\": \"value2\" }\n }\n ]\n}];\n\n### Step 4: Implement Actions\n\nIn `actions.ts`, define one or more action classes that implement the `TrailAction` interface:\n\nCode (typescript):\nimport type { Device, TrailAction, TrailActionManifest, TrailRunResources } from \"@monitoro/herd\";\n\nexport class MyTrailAction implements TrailAction {\n manifest: TrailActionManifest = {\n name: \"my-action\",\n description: \"Description of what this action does\",\n params: {\n param1: {\n type: \"string\",\n description: \"Description of param1\"\n },\n param2: {\n type: \"number\",\n description: \"Description of param2\",\n default: 10\n }\n },\n result: {\n type: \"array\",\n description: \"Description of the result\",\n items: {\n type: \"object\",\n properties: {\n property1: { type: \"string\" },\n property2: { type: \"number\" }\n }\n }\n },\n examples: [\n {\n \"param1\": \"value1\",\n \"param2\": 10\n }\n ]\n }\n\n async test(device: Device, params: Record, resources: TrailRunResources) {\n try {\n const result = await this.run(device, params, resources);\n // Validate result\n if (!result || result.length === 0) {\n return { status: \"error\", message: \"No results found\", result: [] };\n }\n return { status: \"success\", result };\n } catch (e) {\n return { status: \"error\", message: `Error: ${e}`, result: null };\n }\n }\n\n async run(device: Device, params: Record, resources: TrailRunResources) {\n const { param1, param2 } = params;\n const page = await device.newPage();\n \n try {\n // Navigate to URL using the URL template from urls.ts\n await page.goto(resources.url('my-url', { param1, param2 }));\n \n // Extract data using selectors from selectors.ts\n const extracted = await page.extract(resources.selector('my-selector'));\n \n // Process extracted data\n const results = (extracted as any)?.dataKey || [];\n \n // Return processed results\n return results;\n } finally {\n await page.close();\n }\n }\n}\n\n### Step 5: Testing and debugging\n\nUse the Herd CLI to test your trail locally (make sure to define test cases as `examples` in the trail action manifest):\n\nCode (bash):\nherd trail test --action my-trail\n\nYou can also watch for changes in the trail and re-run tests:\n\nCode (bash):\nherd trail test --action my-trail --watch # or herd trail test -a my-trail -w\n\nAnd you can also test selectors only:\n\nCode (bash):\nherd trail test --selector my-selector\n\nAnd watch for changes in the selectors:\n\nCode (bash):\nherd trail test --selector my-selector --watch # or herd trail test -s my-selector -w\n\n### Step 6: Publish your trail\n\nPublishing is coming soon!\n\n### Best Practices\n\n1. **Error handling**: Implement robust error handling in your actions to handle network issues, missing elements, etc.\n2. **Performance**: Minimize page loads and extract as much data as possible from each page.\n3. **Maintainability**: Use descriptive names and add comments to make your trail easier to maintain.\n4. **Testing**: Test your trail with different parameters to ensure it works in various scenarios.\n5. **Versioning**: Increment your trail's version in package.json when making changes.\n\n## python\n\nTrails support for Python SDK is coming soon. Stay tuned for updates!\n\nIn the meantime, you can use the JavaScript SDK to create trails and then use the Herd CLI to publish them.\n\n==\n","llms_full":"Herd Documentation\n================================================================================\n\nDocument: Alternative Herd Vs Apify\nURL: https://herd.garden/docs/alternative-herd-vs-apify\n\n# Herd vs Apify: A Cost-Effective Alternative for Web Automation\n\nApify is a popular platform for web scraping and automation, but its cloud-based infrastructure can be costly and limiting. Herd offers a compelling alternative with similar capabilities but a fundamentally different approach that eliminates infrastructure costs and provides more control over your automation.\n\n## Quick Comparison\n\n| Feature | Herd | Apify |\n| --- | --- | --- |\n| **Primary Focus** | Browser automation \u0026 web scraping | Web scraping platform with cloud infrastructure |\n| **Infrastructure** | Uses your existing browser | Cloud-based platform with managed instances |\n| **Pricing Model** | Flat rate subscription | Usage-based pricing |\n| **Browser Control** | Direct control of your browser | Remote control of cloud browsers |\n| **Browser Support** | Chrome, Edge, Brave, Arc, Opera | Chrome/Chromium in cloud |\n| **Setup Required** | Simple browser extension | No browser setup (cloud-based) |\n| **Authentication** | Uses existing browser sessions | Requires manual setup |\n| **Data Extraction** | Built-in extraction tools | Various actor-based extraction tools |\n| **Resource Usage** | Minimal (shared with browser) | Separate cloud resources (higher cost) |\n| **Customization** | Full control of local environment | Limited to platform capabilities |\n\n## Key Differences in Depth\n\n### Infrastructure and Pricing Model\n\n**Apify:**\n- Cloud-based platform with usage-based pricing\n- Costs scale with computation time and resource usage\n- Requires paid proxy services for many use cases\n- Monthly subscription plus usage-based fees\n\n**Herd:**\n- Uses your existing browser and computer\n- No cloud infrastructure required\n- Direct control of your local resources \n- Supports Chrome, Edge, Brave, Arc, Opera\n- No additional infrastructure costs\n- No separate compute units to pay for\n- Simple and predictable pricing\n\n### Browser Control and Authentication\n\n**Apify:**\n- Operates browsers in the cloud\n- Must manually set up authentication flows\n- Sessions are isolated and temporary\n- Limited access to browser-specific features\n\n**Herd:**\n- Direct control of your own browser\n- Uses your existing authenticated sessions\n- Persistent cookies and storage between runs\n- Full access to browser capabilities and extensions\n\n### Setup and Customization\n\n## herd\n\nCode (javascript):\n// Install the Herd SDK\nnpm install @monitoro/herd\n\n// Simple setup code\nimport { HerdClient } from '@monitoro/herd';\n\n// Connect to your own browser\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Create a page and automate it\nconst page = await device.newPage();\nawait page.goto('https://example.com');\n\n## apify\n\nCode (javascript):\n// Install the Apify SDK\nnpm install apify\n\n// Create an actor\nimport { Actor } from 'apify';\n\n// Run in Apify cloud environment\nawait Actor.init();\n\n// Launch a browser in the cloud\nconst browser = await Actor.launchPuppeteer();\nconst page = await browser.newPage();\nawait page.goto('https://example.com');\n\n// Must manually handle stopping the actor\nawait Actor.exit();\n\n### Data Extraction Capabilities\n\n**Apify:**\n- Offers pre-built actors for common websites\n- Large marketplace of ready-made solutions\n- Extraction limited to what actors provide\n- Can become expensive for large-scale extraction\n\n**Herd:**\n- Powerful built-in extraction API\n- Simple selector-based data retrieval\n- Access to authenticated content\n- Extract data without usage limits\n\n## Use Case Comparisons\n\n### Web Scraping with Authentication\n\n## herd-auth\n\nCode (javascript):\n// Using Herd with an already authenticated browser\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Access a site where you're already logged in\nconst page = await device.newPage();\nawait page.goto('https://account.example.com/dashboard');\n\n// Extract authenticated data directly\nconst userData = await page.extract({\n username: '.user-profile .username',\n accountType: '.account-type',\n balance: '.account-balance',\n transactions: {\n _$r: '.transaction-item',\n date: '.transaction-date',\n amount: '.transaction-amount',\n description: '.transaction-description'\n }\n});\n\nconsole.log(userData);\nawait client.close();\n\n## apify-auth\n\nCode (javascript):\n// Using Apify with manual authentication\nimport { Actor } from 'apify';\n\nawait Actor.init();\nconst browser = await Actor.launchPuppeteer();\nconst page = await browser.newPage();\n\n// Need to manually log in first\nawait page.goto('https://example.com/login');\nawait page.type('#username', 'your-username');\nawait page.type('#password', 'your-password');\nawait page.click('.login-button');\nawait page.waitForNavigation();\n\n// Now navigate to the dashboard\nawait page.goto('https://account.example.com/dashboard');\n\n// Extract data with multiple evaluations\nconst username = await page.$eval('.user-profile .username', el =\u003e el.textContent);\nconst accountType = await page.$eval('.account-type', el =\u003e el.textContent);\nconst balanceText = await page.$eval('.account-balance', el =\u003e el.textContent);\nconst balance = parseFloat(balanceText.replace(/[^0-9.-]+/g, ''));\n\n// Extract transaction data\nconst transactions = await page.$eval('.transaction-item', items =\u003e \n items.map(item =\u003e ({\n date: item.querySelector('.transaction-date').textContent,\n amount: item.querySelector('.transaction-amount').textContent,\n description: item.querySelector('.transaction-description').textContent\n }))\n);\n\nconst userData = {\n username,\n accountType,\n balance,\n transactions\n};\n\nconsole.log(userData);\nawait Actor.exit();\n\n### Multi-Page Crawling\n\n## herd-crawl\n\nCode (javascript):\n// Using Herd for multi-page crawling\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\nconst page = await device.newPage();\nawait page.goto('https://example.com/products');\n\n// Collect all product links first\nconst productLinks = await page.extract({\n links: {\n _$r: '.product-card a',\n url: { attribute: 'href' }\n }\n});\n\n// Visit each product page and extract details\nconst products = [];\nfor (const { url } of productLinks.links) {\n await page.goto(url);\n \n const productData = await page.extract({\n name: '.product-name',\n price: '.product-price',\n description: '.product-description',\n specs: {\n _$r: '.spec-item',\n name: '.spec-name',\n value: '.spec-value'\n }\n });\n \n products.push(productData);\n}\n\nconsole.log(products);\nawait client.close();\n\n## apify-crawl\n\nCode (javascript):\n// Using Apify for multi-page crawling\nimport { Actor, PuppeteerCrawler } from 'apify';\n\nawait Actor.init();\n\n// Define the crawler\nconst crawler = new PuppeteerCrawler({\n async requestHandler({ request, page }) {\n console.log(`Processing ${request.url}...`);\n \n if (request.userData.detailPage) {\n // Extract product details\n const productData = {\n url: request.url,\n name: await page.$eval('.product-name', el =\u003e el.textContent),\n price: await page.$eval('.product-price', el =\u003e el.textContent),\n description: await page.$eval('.product-description', el =\u003e el.textContent),\n specs: await page.$eval('.spec-item', items =\u003e \n items.map(item =\u003e ({\n name: item.querySelector('.spec-name').textContent,\n value: item.querySelector('.spec-value').textContent\n }))\n )\n };\n \n // Save the extracted data\n await Actor.pushData(productData);\n } else {\n // On the listing page, extract links to products\n const productLinks = await page.$eval('.product-card a', links =\u003e \n links.map(link =\u003e link.href)\n );\n \n // Enqueue product detail pages\n for (const url of productLinks) {\n await crawler.requestQueue.addRequest({\n url,\n userData: { detailPage: true }\n });\n }\n }\n },\n maxRequestsPerCrawl: 100,\n});\n\n// Start with the product listing page\nawait crawler.run(['https://example.com/products']);\n\nawait Actor.exit();\n\n## Migration Guide: From Apify to Herd\n\nTransitioning from Apify to Herd is straightforward. Here's a guide to help you migrate:\n\n### 1. Installation\n\n1. Install the Herd SDK:\n \nCode (bash):\n npm install @monitoro/herd\n\n2. Install the Herd browser extension in your preferred browser\n\n3. Register your browser as a device in the Herd dashboard\n\n### 2. Code Migration\n\n| Apify | Herd | Notes |\n| --- | --- | --- |\n| `Actor.init()` | `const client = new HerdClient(apiUrl, token)` `await client.initialize()` | Herd uses a simple client-server model |\n| `Actor.launchPuppeteer()` | `const devices = await client.listDevices()` `const device = devices[0]` | Herd connects to your existing browser |\n| `browser.newPage()` | `await device.newPage()` | Similar API |\n| `page.goto(url)` | `await page.goto(url)` | Identical usage |\n| `page.$eval(selector, fn)` | `await page.extract({ key: selector })` | Herd has a more powerful extraction API |\n| `Actor.pushData(data)` | Store data directly in your code | No platform-specific storage |\n| `Actor.exit()` | `await client.close()` | Herd just disconnects, browser stays open |\n\n### 3. Handling Authentication\n\n**Apify:**\n\nCode (javascript):\n// Manual login process\nawait page.goto('https://example.com/login');\nawait page.type('#username', 'user');\nawait page.type('#password', 'pass');\nawait page.click('#login-button');\n\n**Herd:**\n\nCode (javascript):\n// Simply use your already authenticated browser\nawait page.goto('https://example.com/dashboard'); // Already logged in\n\n## Why Choose Herd Over Apify?\n\n### 1. Cost Efficiency\n\nHerd eliminates the need for cloud infrastructure, resulting in:\n- No usage-based computation costs\n- No proxy costs for most use cases\n- Significant cost savings for regular automation\n- Predictable pricing independent of usage volume\n\n### 2. Use Existing Authentication\n\nWith Herd, you can automate tasks in your already authenticated browser:\n- No need to handle authentication flows in code\n- Access to sites with complex auth (2FA, CAPTCHA)\n- Use existing cookies, local storage, and sessions\n\n### 3. Local Control and Privacy\n\nHerd provides:\n- Full control over the automation environment\n- Higher privacy (data stays on your machine)\n- Direct access to local resources when needed\n- No dependence on third-party cloud infrastructure\n\n### 4. Simpler Development Experience\n\nHerd offers:\n- More intuitive APIs for common tasks\n- Real-time debugging in your browser\n- No need to deploy or manage cloud resources\n- Faster iteration during development\n\n## Get Started with Herd Today\n\nReady to try a more flexible alternative to Apify? Get started with Herd:\n\n1. [Create a Herd account](/register)\n2. [Install the Herd browser extension](/docs/installation) in Chrome, Edge, or Brave (Firefox and Safari not supported)\n3. [Connect your browser](/docs/connect-your-browser)\n4. [Run your first automation](/docs/automation-basics)\n\nDiscover how Herd can provide all the capabilities you need for web automation and scraping at a fraction of the cost of cloud-based solutions like Apify.\n\n================================================================================\n\nDocument: Alternative Herd Vs Browserbase\nURL: https://herd.garden/docs/alternative-herd-vs-browserbase\n\n# Herd vs Browserbase: No-Infrastructure Browser Automation\n\nBrowserbase provides cloud-based headless browsers for automation and AI applications. While it offers a reliable platform for running browser instances, Herd takes a fundamentally different approach by leveraging your existing browsers, eliminating infrastructure costs and complexity.\n\n## Quick Comparison\n\n| Feature | Herd | Browserbase |\n| --- | --- | --- |\n| **Browser Location** | Your local machine | Cloud-based infrastructure |\n| **Infrastructure Needed** | None (uses your browser) | Managed cloud infrastructure |\n| **Pricing Model** | Flat subscription | Usage-based pricing |\n| **Browser Support** | Chrome, Edge, Brave, Arc, Opera | Chromium in cloud |\n| **Latency** | Minimal (1-hop) | Higher (multiple hops) |\n| **Authentication** | Uses existing browser sessions | Requires manual setup |\n| **Framework Support** | JavaScript/Python SDKs | Stagehand, Playwright, Puppeteer, Selenium |\n| **Setup Required** | Browser extension installation | No installation (cloud-based) |\n| **Resource Constraints** | Depends on local resources | Limited by pricing tier |\n\n## Key Differences in Depth\n\n### Infrastructure and Cost Model\n\n**Browserbase:**\n- Runs browsers on managed cloud infrastructure\n- Costs scale with usage and session duration\n- Requires networking between your code and cloud\n- Additional costs for premium features like proxies\n\n**Herd:**\n- Uses browsers already installed on your machine\n- No cloud infrastructure required\n- Direct access to your local machine resources\n- Supports Chrome, Edge, Brave, Arc, Opera\n- Fixed, predictable pricing not tied to usage\n- Local execution with minimal network overhead\n- No additional infrastructure costs to manage\n\n### Setup and Integration\n\n## herd\n\nCode (javascript):\n// Simple installation - no code required\n// 1. Install the Herd browser extension in Chrome, Edge, or Brave (Firefox/Safari not supported)\n// 2. Connect your browser to Herd\n\n// Simple connection to your browser\nimport { HerdClient } from '@monitoro/herd';\n\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Create a new page\nconst page = await device.newPage();\nawait page.goto('https://example.com');\n\n## browserbase\n\nCode (javascript):\n// Install the SDK\nnpm install browserbase\n\n// Connect to cloud infrastructure\nimport { Browserbase } from 'browserbase';\n\nconst bb = new Browserbase({\n api_key: 'your-api-key'\n});\n\n// Create a session on cloud infrastructure\nconst session = await bb.sessions.create({\n timeout: 60, // Session timeout in seconds\n});\n\n// Create a page in the cloud browser\nconst page = await session.newPage();\nawait page.goto('https://example.com');\n\n### Session Management and Performance\n\n**Browserbase:**\n- Cloud-based sessions with timeout limits\n- Performance dependent on cloud resources and network\n- Sessions isolated from local environment\n- Must explicitly manage session lifecycle\n\n**Herd:**\n- Direct access to local browser sessions\n- Local performance without network latency\n- Integrated with your local environment\n- Sessions persist with your browser\n\n### Authentication and User Context\n\n**Browserbase:**\n- Requires implementing authentication for each session\n- Isolated sessions without access to existing cookies\n- Must manually handle login flows\n- Credentials need to be stored and managed\n\n**Herd:**\n- Uses your browser's existing authenticated sessions\n- Access to all cookies, local storage, and session data\n- No need to implement authentication flows\n- Use sites you're already logged into\n\n## Use Case Comparisons\n\n### Web Automation for Logged-in Services\n\n## herd-auth\n\nCode (javascript):\n// Using Herd with pre-authenticated browser\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Directly access authenticated service\nconst page = await device.newPage();\nawait page.goto('https://app.example.com/dashboard'); // Already logged in\n\n// Perform actions on authenticated page\nawait page.click('.create-new-button');\nawait page.type('#item-name', 'New Item');\nawait page.click('.save-button');\n\n// Verify result\nconst confirmationText = await page.$eval('.confirmation', el =\u003e el.textContent);\nconsole.log(confirmationText);\n\n## browserbase-auth\n\nCode (javascript):\n// Using Browserbase with manual authentication\nconst bb = new Browserbase({\n api_key: 'your-api-key'\n});\n\n// Create cloud browser session\nconst session = await bb.sessions.create({\n timeout: 300, // Longer timeout for auth flow\n});\n\n// Create page and handle login manually\nconst page = await session.newPage();\n\n// Navigate to login page\nawait page.goto('https://app.example.com/login');\n\n// Fill login form\nawait page.type('#email', 'user@example.com');\nawait page.type('#password', 'your-secure-password');\nawait page.click('#login-button');\n\n// Wait for login to complete\nawait page.waitForNavigation();\n\n// Now navigate to dashboard\nawait page.goto('https://app.example.com/dashboard');\n\n// Perform actions on authenticated page\nawait page.click('.create-new-button');\nawait page.type('#item-name', 'New Item');\nawait page.click('.save-button');\n\n// Verify result\nconst confirmationText = await page.$eval('.confirmation', el =\u003e el.textContent);\nconsole.log(confirmationText);\n\n// Must explicitly end session\nawait session.close();\n\n### Data Extraction at Scale\n\n## herd-extract\n\nCode (javascript):\n// Using Herd for data extraction\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Open multiple pages for parallel extraction\nconst pages = await Promise.all([1, 2, 3].map(() =\u003e device.newPage()));\n\n// Extract data from multiple sources in parallel\nconst results = await Promise.all(pages.map(async (page, index) =\u003e {\n await page.goto(`https://example.com/category/${index+1}`);\n \n // Use Herd's extraction API\n const data = await page.extract({\n categoryName: '.category-header h1',\n items: {\n _$r: '.product-item',\n title: '.product-title',\n price: '.product-price',\n rating: '.rating-value'\n }\n });\n \n return data;\n}));\n\n// Close pages when done\nawait Promise.all(pages.map(page =\u003e page.close()));\nawait client.close();\n\nconsole.log(results);\n\n## browserbase-extract\n\nCode (javascript):\n// Using Browserbase for data extraction\nconst bb = new Browserbase({\n api_key: 'your-api-key'\n});\n\n// Create separate sessions for parallel extraction\nconst sessions = await Promise.all([1, 2, 3].map(() =\u003e \n bb.sessions.create({ timeout: 120 })\n));\n\n// Extract data from multiple sources\nconst results = await Promise.all(sessions.map(async (session, index) =\u003e {\n const page = await session.newPage();\n await page.goto(`https://example.com/category/${index+1}`);\n \n // Extract data using Puppeteer-style selectors\n const categoryName = await page.$eval('.category-header h1', el =\u003e el.textContent);\n \n const itemElements = await page.$('.product-item');\n const items = [];\n \n for (const element of itemElements) {\n const title = await element.$eval('.product-title', el =\u003e el.textContent);\n const price = await element.$eval('.product-price', el =\u003e el.textContent);\n \n let rating = null;\n try {\n rating = await element.$eval('.rating-value', el =\u003e el.textContent);\n } catch (e) {\n // Element might not exist\n }\n \n items.push({ title, price, rating });\n }\n \n // Must close session when done\n await session.close();\n \n return { categoryName, items };\n}));\n\nconsole.log(results);\n\n## Migration Guide: From Browserbase to Herd\n\nTransitioning from Browserbase to Herd is straightforward. Here's a guide to help you migrate:\n\n### Installation Steps\n\n1. [Sign up for a Herd account](/register)\n2. Install the Herd browser extension in Chrome, Edge, or Brave (Firefox and Safari not supported)\n3. Register your browser as a device in the Herd dashboard\n\n### 2. Code Migration\n\n| Browserbase | Herd | Notes |\n| --- | --- | --- |\n| `new Browserbase({ api_key })` | `new HerdClient(apiUrl, token)` `await client.initialize()` | Herd uses a client-server architecture |\n| `bb.sessions.create()` | `const devices = await client.listDevices()` `const device = devices[0]` | Herd connects to your existing browser |\n| `session.newPage()` | `await device.newPage()` | Similar API |\n| `await page.goto(url)` | `await page.goto(url)` | Identical usage |\n| `await page.type(selector, text)` | `await page.type(selector, text)` | Identical usage |\n| `await page.click(selector)` | `await page.click(selector)` | Identical usage |\n| `await page.$eval(selector, fn)` | `await page.extract({ key: selector })` | Herd offers a more powerful extraction API |\n| `await session.close()` | `await client.close()` | Herd just disconnects, browser stays open |\n\n### 3. Framework Integration\n\n**Browserbase:**\n\nCode (javascript):\n// Browserbase with Playwright\nconst { chromium } = require('playwright');\nconst browser = await chromium.connectOverCDP(session.wsEndpoint);\nconst page = await browser.newPage();\n\n**Herd:**\n\nCode (javascript):\n// Herd uses its own API directly\nconst page = await device.newPage();\n// Direct integrations with testing frameworks available\n\n## Why Choose Herd Over Browserbase?\n\n### 1. No Cloud Infrastructure Required\n\nHerd eliminates the need for cloud infrastructure, providing:\n- Zero dependency on remote browser instances\n- No need to manage cloud resources\n- Reduced latency with local execution\n- Complete isolation from cloud service disruptions\n\n### 2. Cost Predictability and Efficiency\n\nHerd offers a more predictable and often lower cost:\n- No usage-based billing surprises\n- No charges based on session duration\n- No additional costs for scaling automation\n- Fixed costs regardless of automation volume\n\n### 3. Use Existing Browser State and Auth\n\nWith Herd, you can leverage your browser's existing state:\n- Use sites you're already logged into\n- Access to all browser extensions\n- Utilize stored passwords and authentication\n- No need to manage credentials in code\n\n### 4. Lower Latency and Higher Performance\n\nHerd provides performance advantages with local execution:\n- No network latency between code and browser\n- Faster execution of automation tasks\n- Direct access to local resources\n- No timeout limitations on sessions\n\n## Get Started with Herd Today\n\nReady to try a more flexible alternative to Browserbase? Get started with Herd:\n\n1. [Create a Herd account](/register)\n2. [Install the browser extension](/docs/installation)\n3. [Connect your browser](/docs/connect-your-browser)\n4. [Run your first automation](/docs/automation-basics)\n\nDiscover how Herd can provide all the capabilities you need for web automation without the complexity and costs of cloud-based infrastructure.\n\n================================================================================\n\nDocument: Alternative Herd Vs Firecrawl\nURL: https://herd.garden/docs/alternative-herd-vs-firecrawl\n\n# Herd vs Firecrawl: Flexible Browser Automation and Data Extraction\n\nFirecrawl is a specialized web scraping and crawling tool designed primarily for extracting and cleaning web content, especially for use with LLMs. While Firecrawl offers powerful crawling capabilities, Herd provides a more comprehensive browser automation solution with greater flexibility and control over the browsing experience.\n\n## Quick Comparison\n\n| Feature | Herd | Firecrawl |\n| --- | --- | --- |\n| **Primary Focus** | Complete browser automation | Web crawling and content extraction |\n| **Infrastructure** | Uses your existing browser | Cloud-based crawling infrastructure |\n| **Pricing Model** | Flat subscription | Usage-based pricing |\n| **Browser Support** | Chrome, Edge, Brave, Arc, Opera | Managed cloud browsers |\n| **Browser Control** | Full interactive browser control | Limited to crawling and extraction |\n| **Authentication** | Uses existing browser sessions | Limited authentication capabilities |\n| **Content Processing** | Raw and structured data extraction | Optimized for clean text/markdown output |\n| **Usage Flexibility** | General-purpose automation | Specialized for content crawling |\n| **Interactive Workflows** | Supports complex interactions | Limited to extraction patterns |\n\n## Key Differences in Depth\n\n### Primary Focus and Capabilities\n\n**Firecrawl:**\n- Specialized in high-quality web content extraction\n- Optimized for converting websites to clean markdown\n- Focused on crawling through website links\n- Built primarily for LLM data ingestion\n- Limited interactive capabilities\n\n**Herd:**\n- Complete browser automation platform\n- Full interactive control of browser actions\n- Supports Chrome, Edge, Brave, Arc, Opera\n- Supports both data extraction and automation workflows\n- General-purpose browser control\n- Rich interaction with web applications\n\n### Infrastructure and Execution Model\n\n## herd\n\nCode (javascript):\n// Install the Herd SDK\nnpm install @monitoro/herd\n\n// Connect to your existing browser\nimport { HerdClient } from '@monitoro/herd';\n\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Full browser automation capabilities\nconst page = await device.newPage();\nawait page.goto('https://example.com');\n\n## firecrawl\n\nCode (javascript):\n// Install the Firecrawl SDK\nnpm install @mendable/firecrawl-js\n\n// Connect to Firecrawl's cloud service\nimport FirecrawlApp from '@mendable/firecrawl-js';\n\nconst app = new FirecrawlApp({ apiKey: \"fc-YOUR_API_KEY\" });\n\n// Send request to cloud-based crawling service\nconst result = await app.scrapeUrl('example.com');\nconsole.log(result.markdown);\n\n### Data Extraction Approaches\n\n**Firecrawl:**\n- Specializes in converting HTML to clean markdown\n- Automatically handles JavaScript rendering\n- Built-in content cleaning and formatting\n- Output optimized for LLM consumption\n- Limited customization of extraction patterns\n\n**Herd:**\n- Flexible data extraction patterns\n- CSS selector-based extraction\n- Support for complex nested data structures\n- Raw data access and custom transformations\n- Complete control over extraction logic\n\n## Use Case Comparisons\n\n### Website Content Extraction\n\n## herd-extract\n\nCode (javascript):\n// Using Herd for content extraction\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\nconst page = await device.newPage();\nawait page.goto('https://example.com/article');\n\n// Extract structured content with control over the format\nconst articleData = await page.extract({\n title: '.article-title',\n author: '.author-name',\n published: {\n _$: '.publish-date',\n pipes: ['parseDate']\n },\n content: '.article-body',\n tags: {\n _$r: '.tag',\n text: ':root'\n }\n});\n\nconsole.log(articleData);\nawait client.close();\n\n## firecrawl-extract\n\nCode (javascript):\n// Using Firecrawl for content extraction\nimport FirecrawlApp from '@mendable/firecrawl-js';\n\nconst app = new FirecrawlApp({ apiKey: \"fc-YOUR_API_KEY\" });\n\n// Simple URL-based extraction\nconst scrapeResult = await app.scrapeUrl('https://example.com/article');\n\nif (scrapeResult.success) {\n // Get the clean markdown content\n console.log(scrapeResult.markdown);\n \n // Access metadata\n console.log(scrapeResult.metadata);\n} else {\n console.error('Failed to scrape:', scrapeResult.error);\n}\n\n### Interactive Web Automation\n\n## herd-automation\n\nCode (javascript):\n// Using Herd for interactive web automation\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\nconst page = await device.newPage();\n\n// Navigate to a web application\nawait page.goto('https://app.example.com/dashboard');\n\n// Fill out a form\nawait page.click('.create-new-button');\nawait page.type('#title-input', 'New Project');\nawait page.type('#description-input', 'This is a test project created by automation');\nawait page.select('#category-select', 'development');\n\n// Upload a file\nconst fileInput = await page.$('input[type=\"file\"]');\nawait fileInput.uploadFile('/path/to/local/file.pdf');\n\n// Submit the form\nawait page.click('.submit-button');\n\n// Wait for confirmation and extract result\nawait page.waitForSelector('.success-message');\nconst confirmationText = await page.$eval('.success-message', el =\u003e el.textContent);\nconsole.log('Form submitted successfully:', confirmationText);\n\nawait client.close();\n\n## firecrawl-automation\n\nCode (javascript):\n// Firecrawl has limited interactive capabilities\nimport FirecrawlApp from '@mendable/firecrawl-js';\n\nconst app = new FirecrawlApp({ apiKey: \"fc-YOUR_API_KEY\" });\n\n// For interactive tasks like form submission, \n// Firecrawl has limited capabilities, primarily focused\n// on crawling and content extraction.\n\n// You could use the actions feature for limited interactions:\nconst result = await app.scrapeUrl('https://app.example.com', {\n actions: [\n { type: 'click', selector: '.login-button' },\n { type: 'wait', time: 2000 }\n // Limited set of basic actions available\n ]\n});\n\n// But complex workflows like file uploads or \n// multi-step interactions require a more \n// comprehensive automation tool like Herd\n\n### Multi-Page Crawling\n\n## herd-crawl\n\nCode (javascript):\n// Using Herd for custom crawling\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\nconst page = await device.newPage();\nawait page.goto('https://example.com/products');\n\n// Extract all product links\nconst productLinks = await page.extract({\n links: {\n _$r: '.product-card a',\n url: { attribute: 'href' }\n }\n});\n\n// Custom crawling logic with full control\nconst productDetails = [];\nfor (const { url } of productLinks.links) {\n // Navigate to each product page\n await page.goto(url);\n \n // Extract detailed information\n const product = await page.extract({\n name: '.product-name',\n price: '.product-price',\n description: '.product-description',\n inStock: '.stock-status'\n });\n \n productDetails.push(product);\n \n // You can implement custom logic: only continue if conditions are met\n if (productDetails.length \u003e= 10) break;\n}\n\nconsole.log(productDetails);\nawait client.close();\n\n## firecrawl-crawl\n\nCode (javascript):\n// Using Firecrawl for website crawling\nimport FirecrawlApp from '@mendable/firecrawl-js';\n\nconst app = new FirecrawlApp({ apiKey: \"fc-YOUR_API_KEY\" });\n\n// Crawl an entire site\nconst crawlResult = await app.crawlWebsite('example.com', {\n maxPages: 10, // Limit the crawl\n includeSitemap: true, // Use sitemap if available\n followExternalLinks: false // Stay on the same domain\n});\n\nif (crawlResult.success) {\n // Access all crawled pages\n for (const page of crawlResult.pages) {\n console.log(`Page: ${page.url}`);\n console.log(`Content: ${page.markdown}`);\n }\n} else {\n console.error('Crawl failed:', crawlResult.error);\n}\n\n## Migration Guide: From Firecrawl to Herd\n\nTransitioning from Firecrawl to Herd is straightforward. Here's a guide to help you migrate:\n\n### Installation Steps\n\n1. [Sign up for a Herd account](/register)\n2. Install the Herd browser extension in Chrome, Edge, or Brave (Firefox and Safari not supported)\n3. Register your browser as a device in the Herd dashboard\n\n### 2. Code Migration\n\n| Firecrawl | Herd | Notes |\n| --- | --- | --- |\n| `new FirecrawlApp({ apiKey })` | `new HerdClient(apiUrl, token)` `await client.initialize()` `const devices = await client.listDevices()` `const device = devices[0]` | Herd connects to your existing browser |\n| `app.scrapeUrl(url)` | `const page = await device.newPage()` `await page.goto(url)` `const data = await page.extract(...)` | More granular control in Herd |\n| `result.markdown` | Custom extraction patterns with formatting | More flexible data extraction options |\n| `app.crawlWebsite(domain)` | Custom crawling logic implemented with Herd's navigation and extraction APIs | Full control over crawling behavior |\n\n### 3. Implementing Markdown Conversion\n\nIf you specifically need markdown output like Firecrawl provides:\n\nCode (javascript):\n// Helper function to convert extracted HTML to markdown\nfunction htmlToMarkdown(html) {\n // Use a library like turndown\n const turndownService = new TurndownService();\n return turndownService.turndown(html);\n}\n\n// Extract with Herd and convert to markdown\nconst content = await page.extract({\n body: {\n _$: '.article-content',\n attribute: 'innerHTML'\n }\n});\n\n## Why Choose Herd Over Firecrawl?\n\n### 1. Comprehensive Browser Control\n\nHerd provides full browser automation capabilities:\n- Complete interactive control beyond just crawling\n- Support for complex user interactions\n- Ability to automate any browser-based workflow\n- Full access to browser APIs and capabilities\n\n### 2. Flexible Authentication and Sessions\n\nHerd's approach offers significant authentication advantages:\n- Use your existing authenticated browser sessions\n- No need to implement login flows\n- Support for complex authentication scenarios\n- Access to secure content without credential management\n\n### 3. Customizable Extraction and Processing\n\nHerd gives you complete control over data extraction:\n- Custom extraction patterns for any website structure\n- Flexible transformation of extracted data\n- Support for complex nested data extraction\n- Processing options beyond just markdown conversion\n\n### 4. Broader Use Case Support\n\nHerd supports a wider range of automation scenarios:\n- Form submission and interactive workflows\n- File uploads and downloads\n- Conditional logic based on page content\n- Testing and verification workflows\n\n## Get Started with Herd Today\n\nReady to try a more flexible alternative to Firecrawl? Get started with Herd:\n\n1. [Create a Herd account](/register)\n2. [Install the browser extension](/docs/installation)\n3. [Connect your browser](/docs/connect-your-browser)\n4. [Run your first automation](/docs/automation-basics)\n\nDiscover how Herd can provide enhanced capabilities for both content extraction and browser automation, giving you more control and flexibility than specialized crawling tools like Firecrawl.\n\n================================================================================\n\nDocument: Alternative Herd Vs Mcp Sdk\nURL: https://herd.garden/docs/alternative-herd-vs-mcp-sdk\n\n# Herd vs MCP SDK: Streamlined Browser Automation\n\nThe Model Context Protocol (MCP) SDK provides browser automation capabilities primarily focused on AI agent integration. While MCP SDK offers powerful integration with Large Language Models, Herd provides a more accessible and straightforward approach to browser automation with simplified setup and direct browser control.\n\n## Quick Comparison\n\n| Feature | Herd | MCP SDK |\n| --- | --- | --- |\n| **Primary Focus** | General browser automation | AI agent browser automation |\n| **Infrastructure** | Uses your existing browser | Requires separate browser setup |\n| **API Design** | Simple, direct browser control | Protocol-oriented architecture |\n| **Integration** | JavaScript/Python SDKs | Multiple languages supported |\n| **Setup Complexity** | Simple browser extension | More complex server configuration |\n| **Authentication** | Uses existing browser sessions | Requires manual configuration |\n| **Learning Curve** | Shallow, familiar browser API | Steeper with protocol concepts |\n| **Use Cases** | General automation and extraction | AI agent browsing tasks |\n\n## Key Differences in Depth\n\n### Focus and Architecture\n\n**MCP SDK:**\n- Designed primarily for AI model integration\n- Protocol-based communication model\n- Server-client architecture\n- Focus on enabling AI agents to browse\n- More complex protocol implementation\n\n**Herd:**\n- Direct browser automation focus\n- Simple client-browser connection\n- Intuitive API design\n- Supports Chrome, Edge, Brave, Arc, Opera\n- Streamlined API for common tasks\n- Designed for developers first\n- Lower implementation complexity\n\n### Setup and Integration\n\n## herd\n\nCode (javascript):\n// Install the Herd SDK\nnpm install @monitoro/herd\n\n// Simple direct connection to your browser\nimport { HerdClient } from '@monitoro/herd';\n\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Create a page and automate it\nconst page = await device.newPage();\nawait page.goto('https://example.com');\n\n## mcp\n\nCode (javascript):\n// Install MCP SDK\nnpm install mcp-browser-automation\n\n// Set up the MCP server\nconst { MCPServer } = require('mcp-browser-automation');\nconst server = new MCPServer({\n port: 8000,\n // Additional configuration for the server\n});\n\n// Start the server\nawait server.start();\n\n// Client side needs to implement MCP protocol\n// to communicate with the server\n\n// Then through MCP protocol interactions:\n// 1. Create a browser session\n// 2. Navigate to URL\n// 3. Interact with page\n\n### Integration with AI Systems\n\n**MCP SDK:**\n- Designed specifically for AI model integration\n- Protocol-based approach for AI agents\n- Built to enable AI systems to browse the web\n- Complex implementation for general use cases\n- Strong focus on AI agent capabilities\n\n**Herd:**\n- General-purpose browser automation\n- Can be integrated with AI systems through standard APIs\n- Simpler implementation for most use cases\n- Direct browser control paradigm\n- Focus on developer experience and simplicity\n\n## Use Case Comparisons\n\n### Basic Web Automation\n\n## herd-auto\n\nCode (javascript):\n// Using Herd for basic web automation\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Create a new page\nconst page = await device.newPage();\n\n// Navigate to a website\nawait page.goto('https://example.com/login');\n\n// Fill login form\nawait page.type('#username', 'test_user');\nawait page.type('#password', 'password123');\nawait page.click('.login-button');\n\n// Wait for navigation and verify login\nawait page.waitForSelector('.dashboard');\nconst welcomeText = await page.$eval('.welcome-message', el =\u003e el.textContent);\nconsole.log('Login successful:', welcomeText);\n\nawait client.close();\n\n## mcp-auto\n\nCode (javascript):\n// Using MCP SDK for basic web automation\n// First, set up and start the MCP server\nconst { MCPServer } = require('mcp-browser-automation');\nconst server = new MCPServer({ port: 8000 });\nawait server.start();\n\n// For client-side interaction, implement the MCP protocol\n// This is a simplified example showing the concept\nconst response = await fetch('http://localhost:8000/mcp', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n action: 'createSession',\n params: {}\n })\n});\nconst { sessionId } = await response.json();\n\n// Navigate to a website\nawait fetch('http://localhost:8000/mcp', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n action: 'navigate',\n sessionId,\n params: { url: 'https://example.com/login' }\n })\n});\n\n// Fill login form (multiple requests needed)\nawait fetch('http://localhost:8000/mcp', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n action: 'type',\n sessionId,\n params: { selector: '#username', text: 'test_user' }\n })\n});\n\n// Additional requests for password and clicking login button\n// ...\n\n// MCP SDK is more verbose for simple automation tasks\n// and requires protocol knowledge\n\n### Data Extraction Tasks\n\n## herd-extract\n\nCode (javascript):\n// Using Herd for data extraction\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\nconst page = await device.newPage();\nawait page.goto('https://example.com/products');\n\n// Extract data using Herd's powerful extraction API\nconst productData = await page.extract({\n title: '.page-title',\n products: {\n _$r: '.product-item', // Repeat for all products\n name: '.product-name',\n price: '.product-price',\n rating: '.rating-stars',\n available: '.stock-status'\n }\n});\n\nconsole.log(productData);\nawait client.close();\n\n## mcp-extract\n\nCode (javascript):\n// Using MCP SDK for data extraction\n// First, set up and start the MCP server\nconst { MCPServer } = require('mcp-browser-automation');\nconst server = new MCPServer({ port: 8000 });\nawait server.start();\n\n// For client-side extraction, implement the MCP protocol\n// This is a simplified example showing the concept\nconst response = await fetch('http://localhost:8000/mcp', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n action: 'createSession',\n params: {}\n })\n});\nconst { sessionId } = await response.json();\n\n// Navigate to the products page\nawait fetch('http://localhost:8000/mcp', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n action: 'navigate',\n sessionId,\n params: { url: 'https://example.com/products' }\n })\n});\n\n// Extract the page title\nconst titleResponse = await fetch('http://localhost:8000/mcp', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n action: 'evaluate',\n sessionId,\n params: { \n script: \"document.querySelector('.page-title').textContent\" \n }\n })\n});\nconst { result: title } = await titleResponse.json();\n\n// Extract product data would require more complex script execution\n// or multiple requests to get all the product information\n// ...\n\n// MCP SDK requires more complex orchestration for data extraction\n// compared to Herd's simplified extraction API\n\n### AI Integration\n\n## herd-ai\n\nCode (javascript):\n// Using Herd with AI integration\n// First, set up Herd client as usual\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Create a function to handle AI requests for web content\nasync function fetchWebContentForAI(url, query) {\n const page = await device.newPage();\n await page.goto(url);\n \n // Extract relevant content based on the AI query\n const content = await page.extract({\n title: 'h1',\n mainContent: '.main-content',\n relevantSections: {\n _$r: '.content-section',\n heading: 'h2',\n text: 'p'\n }\n });\n \n // Close the page when done\n await page.close();\n \n // Return the content for AI processing\n return content;\n}\n\n// Example usage with an AI system\nconst aiPrompt = \"Summarize the information about machine learning from example.com\";\nconst url = \"https://example.com/machine-learning\";\n\n// Get the content for the AI\nconst webContent = await fetchWebContentForAI(url, \"machine learning\");\n\n// Feed the content to the AI system\nconst aiResponse = await callAISystem(aiPrompt, webContent);\nconsole.log(aiResponse);\n\nawait client.close();\n\n// This pattern keeps the AI integration simple and separate from\n// the browser automation concerns\n\n## mcp-ai\n\nCode (javascript):\n// Using MCP SDK with AI integration\n// MCP is specifically designed for this use case\n\n// First, set up and start the MCP server\nconst { MCPServer } = require('mcp-browser-automation');\nconst server = new MCPServer({ port: 8000 });\nawait server.start();\n\n// This server can now be used by AI agents through the MCP protocol\nconsole.log('MCP Browser Automation server is running on port 8000');\n\n// When an AI system wants to browse, it would send requests like:\n/*\n{\n \"action\": \"createSession\",\n \"params\": {}\n}\n*/\n\n// And receive responses like:\n/*\n{\n \"sessionId\": \"session123\",\n \"status\": \"success\"\n}\n*/\n\n// The AI can then navigate, interact with pages, and extract content\n// through the MCP protocol\n\n// This architecture is optimized for AI systems that implement\n// the MCP protocol, but requires more complex integration\n// than direct SDK usage like Herd offers\n\n## Migration Guide: From MCP SDK to Herd\n\nTransitioning from MCP SDK to Herd simplifies your browser automation code. Here's a migration guide:\n\n### Installation Steps\n\n1. [Sign up for a Herd account](/register)\n2. Install the Herd browser extension in Chrome, Edge, or Brave (Firefox and Safari not supported)\n3. Register your browser as a device in the Herd dashboard\n\n### 2. Code Migration\n\n| MCP SDK | Herd | Notes |\n| --- | --- | --- |\n| MCP server setup | `new HerdClient(apiUrl, token)` `await client.initialize()` | No server needed with Herd |\n| Session creation via protocol | `const devices = await client.listDevices()` `const device = devices[0]` | Simplified session management |\n| Navigation requests | `const page = await device.newPage()` `await page.goto(url)` | Direct browser control |\n| Element interactions via protocol | `await page.click(selector)` `await page.type(selector, text)` | Simpler interaction API |\n| Data extraction with custom scripts | `await page.extract(selectors)` | Powerful extraction API |\n| Session closing via protocol | `await client.close()` | Simple cleanup |\n\n### 3. AI Integration Approach\n\n**MCP SDK:**\n\nCode (javascript):\n// AI systems implement MCP protocol directly\n// Complex integration but designed for AI\n\n// AI system would send requests like:\nconst request = {\n action: \"evaluateElement\",\n params: { selector: \".content\", attribute: \"textContent\" }\n};\n// And process responses\n\n**Herd:**\n\nCode (javascript):\n// Build an adapter layer for AI integration\nasync function getWebContent(url, aiQuery) {\n // Use Herd to fetch and extract the content\n const page = await device.newPage();\n await page.goto(url);\n const content = await page.extract({ /* ... */ });\n await page.close();\n return content;\n}\n\n// Then use this in your AI system\nconst content = await getWebContent(url, query);\nconst aiResponse = await yourAISystem.process(content);\n\n### 4. Simpler Setup\n\nHerd simplifies your automation workflow:\n\n- No need to run a server process\n- Uses your existing browser\n- Simple browser extension rather than complex setup\n- Supports Chrome, Edge, Brave, Arc, Opera\n- Works with browsers you're already using every day\n\n## Why Choose Herd Over MCP SDK?\n\n### 1. Simpler Developer Experience\n\nHerd provides a more straightforward approach to browser automation:\n- No complex protocol implementation\n- Direct browser control API\n- Familiar programming model\n- Lower learning curve\n\n### 2. Less Infrastructure to Manage\n\nHerd eliminates the need for additional infrastructure:\n- No server to set up and maintain\n- Uses your existing browser\n- Simpler deployment architecture\n- Fewer moving parts\n\n### 3. Powerful Built-in Capabilities\n\nHerd includes powerful features out of the box:\n- Sophisticated data extraction API\n- Session and cookie management\n- Authentication handling\n- Browser state persistence\n\n### 4. Broader Applicability\n\nWhile MCP SDK focuses on AI integration, Herd supports:\n- General browser automation\n- Web scraping and data extraction\n- Testing and monitoring\n- Process automation\n\n## Get Started with Herd\n\nReady to try a more straightforward alternative to MCP SDK? Get started with Herd:\n\n1. [Create a Herd account](/register)\n2. [Install the browser extension](/docs/installation) in Chrome, Edge, or Brave (Firefox and Safari not supported)\n3. [Connect your browser](/docs/connect-your-browser)\n4. [Run your first automation](/docs/automation-basics)\n\nDiscover how Herd can simplify your browser automation tasks with its intuitive API and direct browser control, providing a more accessible alternative to protocol-based approaches like MCP SDK.\n\n================================================================================\n\nDocument: Alternative Herd Vs Monitoro\nURL: https://herd.garden/docs/alternative-herd-vs-monitoro\n\n# Herd vs Monitoro: Complementary Browser Tools\n\nHerd and Monitoro are sister products created by the same team, designed to serve complementary but distinct purposes. While Herd provides programmatic browser control and multi-browser orchestration capabilities, Monitoro focuses on no-code monitoring and data extraction. Understanding their differences helps you choose the right tool for your specific needs.\n\n## Quick Comparison\n\n| Feature | Herd | Monitoro |\n| --- | --- | --- |\n| **Primary Focus** | Browser automation \u0026 orchestration | Website monitoring \u0026 data extraction |\n| **Core Function** | Programmatic browser control | Monitoring webpages for changes |\n| **Target User** | Developers \u0026 automation teams | Non-technical users \u0026 business teams |\n| **Coding Required** | Yes (JavaScript/Python) | No (visual interface) |\n| **Browser Support** | Chrome, Edge, Brave, Arc, Opera | Chrome, Edge, Brave |\n| **Browser Orchestration** | Multiple browsers and devices | Single-focus monitoring |\n| **Implementation** | JavaScript/Python SDK | No-code browser extension |\n| **Data Processing** | Programmatic data workflows | Automated alerts and integrations |\n| **Best For** | Complex automation needs | Monitoring \u0026 simple data extraction |\n\n## Understanding the Difference\n\n### Herd\n\nHerd is designed for **programmatic browser control and orchestration**, allowing developers to:\n- Automate complex web tasks across multiple browsers\n- Extract data from websites at scale\n- Leverage their existing browsers for automation\n- Build persistent automation workflows\n- Orchestrate multiple browser instances simultaneously\n- Create sophisticated data processing pipelines\n- Works with Chrome, Edge, Brave, Arc, Opera\n\n### Monitoro\n\nMonitoro is designed for **no-code webpage monitoring and alerts**, allowing anyone to:\n- Track changes on websites without coding\n- Set up alerts when specific data changes\n- Extract structured data from webpages\n- Send notifications to various channels (Slack, Discord, etc.)\n- Create automated workflows based on webpage changes\n- Integrate with tools like Google Sheets and Airtable\n\n## When to Use Each Tool\n\n## herd-use\n\n**Use Herd when you need to:**\n\n- Create complex automation scripts\n- Orchestrate multiple browsers\n- Build sophisticated data extraction pipelines\n- Integrate browser automation into your application\n- Control browsers programmatically\n- Run continuous automation jobs\n- Implement advanced web interaction patterns\n- Scale automation across multiple devices\n\n**Example use cases:**\n- Large-scale data extraction projects\n- Multi-step workflow automation\n- Integration testing across browsers\n- Sophisticated web monitoring systems\n- Building browser-based APIs\n- Authentication-required data access\n\n## monitoro-use\n\n**Use Monitoro when you need to:**\n\n- Monitor websites for changes without coding\n- Get alerts when specific data changes\n- Extract data and send it to other tools\n- Create simple automations based on website changes\n- Set up monitoring dashboards\n- Share monitoring alerts with teams\n\n**Example use cases:**\n- Price monitoring on e-commerce sites\n- Tracking product availability\n- Monitoring competitor websites\n- Setting up alerts for new content\n- Syncing web data to spreadsheets\n- Creating webhooks for website changes\n\n## Implementation Comparison\n\n### Herd Implementation\n\n## herd-js\n\nCode (javascript):\n// Using Herd for browser automation\nimport { HerdClient } from '@monitoro/herd';\n\n// Initialize the client\nconst client = new HerdClient('your-token');\nawait client.initialize();\n\n// Get a device\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Create a new page and automate it\nconst page = await device.newPage();\nawait page.goto('https://example.com');\n\n// Interact with the page\nawait page.type('#search', 'automation');\nawait page.click('.search-button');\n\n// Extract data\nconst results = await page.extract({\n titles: {\n _$r: '.result-item',\n title: '.item-title',\n description: '.item-description'\n }\n});\n\nconsole.log(results);\nawait client.close();\n\n## herd-py\n\nCode (python):\n# Using Herd for browser automation\nfrom monitoro_herd import HerdClient\n\n# Initialize the client\nclient = HerdClient('your-token')\nclient.initialize()\n\n# Get a device\ndevices = client.list_devices()\ndevice = devices[0]\n\n# Create a new page and automate it\npage = device.new_page()\npage.goto('https://example.com')\n\n# Interact with the page\npage.type('#search', 'automation')\npage.click('.search-button')\n\n# Extract data\nresults = page.extract({\n \"titles\": {\n \"_$r\": \".result-item\",\n \"title\": \".item-title\",\n \"description\": \".item-description\"\n }\n})\n\nprint(results)\nclient.close()\n\n### Monitoro Implementation\n\nMonitoro works through a no-code interface:\n\n1. **Install the Monitoro browser extension**\n2. **Create a monitor:**\n - Navigate to the webpage you want to monitor\n - Use the Monitoro interface to select elements to track\n - Set up conditions for when alerts should trigger\n - Configure how often the page should be checked\n\n3. **Configure integrations:**\n - Connect to notification channels like Discord, Slack, or Telegram\n - Set up data destinations like Google Sheets or Airtable\n - Create webhooks for custom integrations\n\nCode:\n# No code required for Monitoro - it's all done through the visual interface\n\n# Example monitoring workflow:\n1. Set up a monitor for a product page on an e-commerce site\n2. Configure it to check for price changes or \"In Stock\" status\n3. Set up alerts to Discord when conditions are met\n4. Configure Google Sheets integration to log all price changes\n\n## Using Herd and Monitoro Together\n\nThese tools can work together in a complementary fashion:\n\n1. **Use Monitoro for initial monitoring and alerts:**\n - Set up no-code monitors for important websites\n - Get alerted when specific changes occur\n - Collect initial data in spreadsheets\n\n2. **Use Herd for advanced follow-up automation:**\n - Trigger Herd workflows based on Monitoro alerts\n - Perform complex interactions beyond Monitoro's capabilities\n - Extract deeper data that requires authentication or multiple steps\n - Orchestrate actions across multiple browsers\n\n### Example workflow:\n\n1. Monitoro monitors competitor pricing and alerts when prices change\n2. A webhook from Monitoro triggers a Herd automation\n3. Herd performs a deep analysis across multiple pages, including login-required areas\n4. Herd generates a comprehensive report and updates internal systems\n\n## When to Upgrade from Monitoro to Herd\n\nConsider upgrading from Monitoro to Herd when:\n\n1. You need to go beyond simple monitoring to complex automation\n2. Your workflows require orchestration of multiple browsers\n3. You need to access authenticated areas of websites\n4. You require sophisticated data processing capabilities\n5. You need programmatic control over browser behavior\n6. Your team has development resources for coding solutions\n\n## monitoro-simple\n\nCode:\n# Monitoro approach (no-code)\n\n1. Set up a monitor for an e-commerce product page\n2. Configure it to check price every hour\n3. Send price alerts to Slack channel\n4. Log all prices to Google Sheets\n\n## herd-advanced\n\nCode (javascript):\n// Herd advanced solution\n\nimport { HerdClient } from '@monitoro/herd';\n\nasync function monitorPrices() {\n const client = new HerdClient('your-token');\n await client.initialize();\n const devices = await client.listDevices();\n const device = devices[0];\n \n // Open multiple pages for parallel extraction\n const productUrls = [\n 'https://example.com/product/1',\n 'https://example.com/product/2',\n 'https://example.com/product/3'\n ];\n \n // Login first to access special pricing\n const page = await device.newPage();\n await page.goto('https://example.com/login');\n await page.type('#email', process.env.USERNAME);\n await page.type('#password', process.env.PASSWORD);\n await page.click('#login-button');\n await page.waitForNavigation();\n \n // Now check all products in parallel\n const priceData = [];\n for (const url of productUrls) {\n await page.goto(url);\n \n const data = await page.extract({\n productId: {\n _$: '.product-id',\n regex: 'ID: (.*)'\n },\n regularPrice: '.regular-price',\n salePrice: '.sale-price',\n memberPrice: '.member-price',\n availability: '.stock-status',\n shipping: '.shipping-info'\n });\n \n // Compare with historical data in database\n const priceChanged = await comparePriceWithHistory(data);\n if (priceChanged) {\n // Send custom notification with detailed info\n await sendDetailedAlert(data);\n // Update database with new price info\n await updatePriceDatabase(data);\n }\n \n priceData.push(data);\n }\n \n await client.close();\n return priceData;\n}\n\n// Run on schedule\nsetInterval(monitorPrices, 3600000); // Every hour\n\n## Why Use Both Herd and Monitoro?\n\n### 1. Complementary Capabilities\n\n- **Monitoro:** Fast, no-code setup for basic monitoring needs\n- **Herd:** Developer-focused tool for complex automation requirements\n\n### 2. Different Team Members, Different Needs\n\n- **Business Teams:** Can use Monitoro without technical skills\n- **Developers:** Can use Herd for advanced customization\n- **Operations:** Can set up Monitoro for quick insights\n- **Engineering:** Can build on those insights with Herd\n\n### 3. Staged Implementation\n\nMonitoro and Herd support a natural progression:\n- Start with simple Monitoro monitoring\n- Identify areas that need deeper automation\n- Implement targeted Herd solutions for those areas\n- Maintain both for their distinct advantages\n\n## Get Started with Herd and Monitoro\n\nReady to implement a complete browser automation and monitoring solution?\n\n### For Herd:\n\n1. [Create a Herd account](/register)\n2. [Install the Herd browser extension](/docs/installation) in Chrome, Edge, or Brave (Firefox and Safari not supported)\n3. [Connect your browser](/docs/connect-your-browser)\n4. [Run your first automation](/docs/automation-basics)\n\n### For Monitoro:\n\n1. Visit [Monitoro.co](https://monitoro.co) to sign up\n2. Install the Monitoro browser extension\n3. Navigate to the website you want to monitor\n4. Use the visual interface to set up your first monitor\n\nChoose the right tool for each specific need, or use them together for a comprehensive web monitoring and automation solution.\n\n================================================================================\n\nDocument: Alternative Herd Vs Puppeteer\nURL: https://herd.garden/docs/alternative-herd-vs-puppeteer\n\n# Herd vs Puppeteer: A Better Alternative for Browser Automation\n\nIn the world of browser automation and web scraping, Puppeteer has long been a popular choice for developers. However, Herd provides a compelling alternative that addresses many of Puppeteer's limitations while offering unique advantages for modern development workflows.\n\n## Quick Comparison\n\n| Feature | Herd | Puppeteer |\n| --- | --- | --- |\n| **Browser Type** | Your existing browser | Separate Chromium instance |\n| **Browser Support** | Chrome, Edge, Brave, Arc, Opera | Chromium, Chrome (limited Firefox) |\n| **Infrastructure Requirements** | None (uses your browser) | Requires separate browser instances |\n| **Authentication** | Uses existing sessions | Requires manual setup |\n| **Setup Complexity** | Simple extension installation | More complex setup |\n| **Programming Languages** | JavaScript, Python | JavaScript/TypeScript only |\n| **Session Management** | Persistent across runs | Must be rebuilt each run |\n| **Resource Usage** | Minimal (shared with browser) | High (separate processes) |\n\n## Key Differences in Depth\n\n### Infrastructure Requirements\n\n**Puppeteer:**\n- Requires installing and managing separate Chromium instances\n- Needs dedicated resources for each browser instance\n- Increases infrastructure costs in cloud environments\n- Requires managing updates to the Chromium engine\n\n**Herd:**\n- Uses your existing browser installation\n- No additional browser instances to manage\n- Significantly lower resource utilization\n- Leverages native browser capabilities\n\n### Setup and Installation Process\n\n## herd\n\nCode (bash):\n# Install the Herd SDK\nnpm install @monitoro/herd\n\n# Then install the browser extension and connect your browser\n# That's it! No browser installation or management needed\n\n## puppeteer\n\nCode (bash):\n# Install Puppeteer\nnpm install puppeteer\n\n# Puppeteer will download and manage its own Chromium instance\n# You'll need to handle this in deployment environments\n# Additional setup for proxies, authentication, etc.\n\n### Browser Support\n\n**Puppeteer:**\n- Primarily designed for Chrome/Chromium browsers\n- Limited experimental support for Firefox\n- No support for Safari or Edge\n\n**Herd:**\n- Works with Chrome, Edge, Brave, Arc, Opera \n- Consistent experience across all supported Chromium-based browsers\n\n### Authentication and Session Handling\n\n**Puppeteer:**\n- Sessions must be recreated for each new Puppeteer instance\n- Requires manually handling cookies and authentication flows\n- Accessing logged-in state requires additional code\n- Difficult to use existing authenticated sessions\n\n**Herd:**\n- Uses your existing browser's authenticated sessions\n- No need to handle authentication separately\n- Persistent cookies and storage between sessions\n- Access to browser extensions that manage authentication\n\n## Use Case Comparisons\n\n### Data Extraction\n\n## herd-extract\n\nCode (javascript):\n// Initialize the client and connect to your browser\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Extract data using simple selectors\nconst page = await device.newPage();\nawait page.goto('https://example.com');\nconst data = await page.extract({\n title: 'h1',\n description: 'p',\n links: {\n _$r: 'a', // Extract all links\n href: { attribute: 'href' },\n text: ':root'\n }\n});\n\nconsole.log(data);\n\n## puppeteer-extract\n\nCode (javascript):\n// Launch a separate browser instance\nconst browser = await puppeteer.launch();\nconst page = await browser.newPage();\nawait page.goto('https://example.com');\n\n// Extract data with multiple evaluations\nconst data = {\n title: await page.$eval('h1', el =\u003e el.textContent),\n description: await page.$eval('p', el =\u003e el.textContent),\n links: await page.$eval('a', elements =\u003e \n elements.map(el =\u003e ({\n href: el.getAttribute('href'),\n text: el.textContent\n }))\n )\n};\n\nconsole.log(data);\nawait browser.close();\n\n### Web Automation\n\n## herd-auto\n\nCode (javascript):\n// Initialize and connect to your browser\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Use the existing browser with current sessions\nconst page = await device.newPage();\nawait page.goto('https://myapp.com/dashboard'); // Already logged in\n\n// Interact with the page\nawait page.click('.new-item-button');\nawait page.type('#item-name', 'New Task');\nawait page.click('.save-button');\n\n// Process results\nconst notification = await page.waitForSelector('.success-message');\nconsole.log(notification.textContent);\n\n## puppeteer-auto\n\nCode (javascript):\n// Launch a separate browser instance\nconst browser = await puppeteer.launch();\nconst page = await browser.newPage();\n\n// Need to handle login first\nawait page.goto('https://myapp.com/login');\nawait page.type('#username', 'user@example.com');\nawait page.type('#password', 'password');\nawait page.click('.login-button');\nawait page.waitForNavigation();\n\n// Now navigate to dashboard after login\nawait page.goto('https://myapp.com/dashboard');\n\n// Interact with the page\nawait page.click('.new-item-button');\nawait page.type('#item-name', 'New Task');\nawait page.click('.save-button');\n\n// Process results\nconst notification = await page.waitForSelector('.success-message');\nconsole.log(await notification.evaluate(el =\u003e el.textContent));\nawait browser.close();\n\n## Migration Guide: From Puppeteer to Herd\n\nTransitioning from Puppeteer to Herd is straightforward. Here's a simple migration guide:\n\n### 1. Installation\n\n1. Install the Herd SDK:\n \nCode (bash):\n npm install @monitoro/herd\n\n2. Install the Herd browser extension in your preferred browser\n\n3. Register your browser as a device in the Herd dashboard\n\n### 2. Code Migration\n\n| Puppeteer | Herd | Notes |\n| --- | --- | --- |\n| `const browser = await puppeteer.launch()` | `const client = new HerdClient(apiUrl, token)` `await client.initialize()` `const devices = await client.listDevices()` `const device = devices[0]` | Herd connects to your existing browser |\n| `const page = await browser.newPage()` | `const page = await device.newPage()` | Similar API, different source |\n| `await page.goto(url)` | `await page.goto(url)` | Identical usage |\n| `await page.type(selector, text)` | `await page.type(selector, text)` | Identical usage |\n| `await page.click(selector)` | `await page.click(selector)` | Identical usage |\n| `await page.$eval(selector, fn)` | `const element = await page.$(selector)` `await element.evaluate(fn)` | Slightly different approach |\n| `await page.screenshot()` | `await page.screenshot()` | Identical usage |\n| `await browser.close()` | `await client.close()` | Browser stays open, just disconnects client |\n\n## Why Choose Herd Over Puppeteer?\n\n### 1. No Infrastructure Management\n\nHerd eliminates the need to maintain separate browser instances, significantly reducing:\n- Memory and CPU usage\n- Infrastructure costs for cloud deployments\n- Maintenance overhead for browser updates\n\n### 2. Use Existing Authentication\n\nWith Herd, you can automate tasks in your already authenticated browser:\n- No need to handle authentication flows in code\n- Access to sites that require complex authentication\n- Use existing cookies, local storage, and sessions\n\n### 3. Cross-Browser Compatibility\n\nWhile Puppeteer is primarily focused on Chromium:\n- Herd works across Chrome, Edge, and Brave\n- Same code works on any supported browser\n- Test on multiple browsers with minimal configuration changes\n\n### 4. Simpler Development Experience\n\nHerd provides:\n- More intuitive APIs for common tasks\n- Better debugging experience (view automation in real browser)\n- Easier integration with existing workflows\n\n## Get Started with Herd Today\n\nReady to try a better alternative to Puppeteer? Get started with Herd:\n\n1. [Create a Herd account](/register)\n2. [Install the browser extension](/docs/installation)\n3. [Connect your browser](/docs/connect-your-browser)\n4. [Run your first automation](/docs/automation-basics)\n\nDiscover how Herd can simplify your browser automation workflows while reducing infrastructure costs and complexity.\n\n================================================================================\n\nDocument: Alternative Herd Vs Selenium\nURL: https://herd.garden/docs/alternative-herd-vs-selenium\n\n# Herd vs Selenium: A More Efficient Browser Automation Alternative\n\nSelenium has been the industry standard for browser automation for many years, but its architecture presents significant challenges for modern development workflows. Herd offers a compelling alternative that addresses many of Selenium's pain points while providing a more intuitive experience.\n\n## Quick Comparison\n\n| Feature | Herd | Selenium |\n| --- | --- | --- |\n| **Driver Requirements** | No drivers needed | Requires WebDriver for each browser |\n| **Browser Type** | Your existing browser | Creates new browser instances |\n| **Browser Support** | Chrome, Edge, Brave, Arc, Opera | Chrome, Firefox, Edge, Safari, IE |\n| **Infrastructure** | Uses your existing browser | Requires WebDriver servers |\n| **Authentication** | Uses existing sessions | Requires manual setup |\n| **Programming Languages** | JavaScript, Python | Java, Python, C#, Ruby, JavaScript |\n| **Setup Complexity** | Simple browser extension | WebDriver setup for each browser |\n| **Maintenance Required** | Minimal (browser updates only) | High (drivers must match browser versions) |\n| **Session Management** | Persistent across runs | Must be rebuilt each run |\n\n## Key Differences in Depth\n\n### Driver and Infrastructure Requirements\n\n**Selenium:**\n- Requires installation and management of WebDrivers for each browser\n- WebDrivers must be kept in sync with browser versions\n- Separate browser instances for automation\n- Complex setup in CI/CD environments\n- High resource usage (separate process for each browser)\n\n**Herd:**\n- No WebDrivers or separate drivers needed\n- Works directly with your installed browser\n- No version synchronization issues\n- Simple setup in any environment\n- Low resource usage (shares existing browser process)\n\n### Setup and Installation Process\n\n## herd\n\nCode (bash):\n# JavaScript\nnpm install @monitoro/herd\n\n# Python\npip install herd-client\n\n# Then install the browser extension and connect your browser\n# That's it! No WebDrivers or browser drivers to manage\n\n## selenium\n\nCode (bash):\n# JavaScript\nnpm install selenium-webdriver\n\n# Python\npip install selenium\n\n# Additionally, you must:\n# 1. Download the correct WebDriver for each browser\n# 2. Ensure WebDriver versions match browser versions\n# 3. Add WebDrivers to your PATH or specify their location\n# 4. Update WebDrivers when browsers update\n\n### Browser Support and Consistency\n\n**Selenium:**\n- Supports all major browsers including Chrome, Firefox, Safari, Edge, and IE\n- Requires separate WebDriver configurations for each browser\n- May exhibit inconsistent behavior across different browsers\n- Requires updates when browsers update\n\n**Herd:**\n- Supports Chrome, Edge, Brave, Arc, Opera\n- Uniform behavior across supported Chromium-based browsers\n- No additional configuration needed for different browsers\n\n### Authentication and Session Handling\n\n**Selenium:**\n- Sessions are isolated and temporary\n- Requires manually handling authentication steps\n- Session storage is cleared between runs\n- Difficult to use existing authenticated sessions\n\n**Herd:**\n- Uses your browser's existing authenticated sessions\n- Access sites you're already logged into\n- Persistent cookies and storage\n- Access to browser extensions that manage authentication\n\n## Use Case Comparisons\n\n### Web Testing\n\n## herd-test\n\nCode (javascript):\n// JavaScript\nimport { HerdClient } from '@monitoro/herd';\n\nasync function runTest() {\n // Connect to your existing browser\n const client = new HerdClient('your-token');\n await client.initialize();\n const devices = await client.listDevices();\n const device = devices[0];\n \n // Create a new page for testing\n const page = await device.newPage();\n await page.goto('https://example.com');\n \n // Test interactions\n await page.click('.nav-item');\n await page.waitForSelector('.content-loaded');\n \n // Assert condition\n const header = await page.$('.header');\n const text = await header.getText();\n console.assert(text.includes('Expected Text'), 'Header text verification failed');\n \n // Cleanup\n await page.close();\n await client.close();\n}\n\nrunTest();\n\n## selenium-test\n\nCode (javascript):\n// JavaScript\nimport { Builder, By, until } from 'selenium-webdriver';\n\nasync function runTest() {\n // Launch a separate browser instance with WebDriver\n const driver = await new Builder()\n .forBrowser('chrome')\n .build();\n \n try {\n // Navigate to the test site\n await driver.get('https://example.com');\n \n // Test interactions\n await driver.findElement(By.css('.nav-item')).click();\n await driver.wait(until.elementLocated(By.css('.content-loaded')), 5000);\n \n // Assert condition\n const header = await driver.findElement(By.css('.header'));\n const text = await header.getText();\n console.assert(text.includes('Expected Text'), 'Header text verification failed');\n } finally {\n // Always close the browser\n await driver.quit();\n }\n}\n\nrunTest();\n\n### Data Extraction\n\n## herd-extract\n\nCode (javascript):\n// JavaScript\nimport { HerdClient } from '@monitoro/herd';\n\nasync function extractData() {\n const client = new HerdClient('your-token');\n await client.initialize();\n const devices = await client.listDevices();\n const device = devices[0];\n \n const page = await device.newPage();\n await page.goto('https://example.com/products');\n \n // Extract product data with a single call\n const products = await page.extract({\n items: {\n _$r: '.product-card', // Repeat for each product card\n name: '.product-name',\n price: '.product-price',\n rating: '.product-rating',\n inStock: '.stock-status'\n }\n });\n \n console.log(products.items);\n await client.close();\n}\n\nextractData();\n\n## selenium-extract\n\nCode (javascript):\n// JavaScript\nimport { Builder, By } from 'selenium-webdriver';\n\nasync function extractData() {\n const driver = await new Builder()\n .forBrowser('chrome')\n .build();\n \n try {\n await driver.get('https://example.com/products');\n \n // Extract product data with multiple queries\n const productElements = await driver.findElements(By.css('.product-card'));\n \n const products = [];\n for (const element of productElements) {\n const name = await element.findElement(By.css('.product-name')).getText();\n const price = await element.findElement(By.css('.product-price')).getText();\n \n let rating = '';\n try {\n rating = await element.findElement(By.css('.product-rating')).getText();\n } catch (e) {\n // Element might not exist\n rating = 'N/A';\n }\n \n let inStock = false;\n try {\n const stockText = await element.findElement(By.css('.stock-status')).getText();\n inStock = stockText.includes('In Stock');\n } catch (e) {\n // Element might not exist\n }\n \n products.push({ name, price, rating, inStock });\n }\n \n console.log(products);\n } finally {\n await driver.quit();\n }\n}\n\nextractData();\n\n## Migration Guide: From Selenium to Herd\n\nTransitioning from Selenium to Herd is straightforward. Here's a guide to help you migrate your existing code:\n\n### 1. Installation\n\n1. Install the Herd SDK:\n \nCode (bash):\n # JavaScript\n npm install @monitoro/herd\n \n # Python\n pip install herd-client\n\n2. Install the Herd browser extension in your preferred browser\n\n3. Register your browser as a device in the Herd dashboard\n\n### 2. Code Migration\n\n| Selenium | Herd | Notes |\n| --- | --- | --- |\n| `new Builder().forBrowser().build()` | `new HerdClient(apiUrl, token)` `await client.initialize()` `const devices = await client.listDevices()` `const device = devices[0]` | Herd connects to your existing browser |\n| `driver.get(url)` | `await page.goto(url)` | Similar syntax |\n| `driver.findElement(By.css(selector))` | `await page.$(selector)` | Herd uses CSS selectors directly |\n| `element.sendKeys(text)` | `await element.type(text)` | Different method name |\n| `element.click()` | `await element.click()` | Identical usage |\n| `driver.wait(until.elementLocated())` | `await page.waitForSelector(selector)` | Similar functionality |\n| `driver.quit()` | `await client.close()` | Herd just disconnects, browser stays open |\n\n### 3. Handling Multiple Browsers\n\n**Selenium:**\n\nCode (javascript):\nconst chrome = await new Builder().forBrowser('chrome').build();\nconst firefox = await new Builder().forBrowser('firefox').build();\n\n**Herd:**\n\nCode (javascript):\n// Connect to different browsers that are registered as devices\nconst chromiumDevice = devices.find(d =\u003e d.name === 'Chrome Browser');\n\n## Why Choose Herd Over Selenium?\n\n### 1. No WebDriver Headaches\n\nHerd eliminates the need for WebDrivers, solving the most common Selenium pain points:\n- No driver version compatibility issues\n- No driver installation or updates needed\n- No broken tests due to browser updates\n\n### 2. Use Existing Authentication\n\nWith Herd, you can automate tasks in your already authenticated browser:\n- No need to write and maintain authentication code\n- Access to sites requiring complex authentication\n- Use existing cookies, local storage, and sessions\n\n### 3. Simplified Setup and Maintenance\n\nHerd significantly reduces the overhead of browser automation:\n- No complex CI/CD configuration\n- No driver path management\n- No browser version tracking\n\n### 4. Intuitive API for Modern Development\n\nHerd provides:\n- Clean, Promise-based API\n- Powerful data extraction capabilities\n- Better debugging experience (view automation in your browser)\n\n## Get Started with Herd Today\n\nReady to try a more efficient alternative to Selenium? Get started with Herd:\n\n1. [Create a Herd account](/register)\n2. [Install the browser extension](/docs/installation)\n3. [Connect your browser](/docs/connect-your-browser)\n4. [Run your first automation](/docs/automation-basics)\n\nDiscover how Herd can simplify your browser automation workflows while eliminating the most common frustrations of working with Selenium.\n\n================================================================================\n\nDocument: Automation Basics\nURL: https://herd.garden/docs/automation-basics\n\n# Automation Basics\n\nWelcome to Monitoro Herd! This guide will walk you through creating your first browser automation step-by-step. We'll start with the basics and gradually build up to more complex examples, explaining each concept along the way.\n\n## javascript\n\n## JavaScript SDK\n\n### Setting Up Your Environment\n\nBefore writing any code, you'll need to set up your JavaScript environment and install the Herd SDK:\n\n1. Make sure you have Node.js installed (version 14 or higher recommended)\n2. Create a new project directory\n3. Install the SDK using npm:\n\nCode (bash):\nnpm install @monitoro/herd\n\n### Initializing the Client\n\nThe first step in any automation is to initialize the Herd client with your API credentials:\n\nCode (javascript):\n// Import the Herd client\nimport { HerdClient } from '@monitoro/herd';\n\n// Initialize the client with your API URL and token\nconst client = new HerdClient('your-token');\n\n// Always initialize the client before using it\nawait client.initialize();\nNote: **Note:** Replace the token with your actual Herd API token from your dashboard.\n\n### Connecting to a Device\n\nAfter initializing the client, you need to connect to a device (browser) that will perform the automation:\n\nCode (javascript):\n// Get a list of available devices\nconst devices = await client.listDevices();\n\n// Connect to the first available device\nconst device = devices[0];\n\nconsole.log(`Connected to device: ${device.id}`);\n\nThis code retrieves all devices registered to your account and connects to the first one. In a production environment, you might want to select a specific device based on its properties or availability.\n\n### Creating a Page and Navigating\n\nNow that you're connected to a device, you can create a new browser page and navigate to a website:\n\nCode (javascript):\n// Create a new page in the browser\nconst page = await device.newPage();\n\n// Navigate to a website\nawait page.goto('https://example.com');\n\nconsole.log('Successfully navigated to example.com');\n\nThe `goto` method loads the specified URL and waits for the page to load. By default, it waits until the page's `load` event is fired, but you can customize this behavior with options.\n\n### Extracting Basic Information\n\nOne of the most common automation tasks is extracting information from web pages. Here's how to extract basic elements:\n\nCode (javascript):\n// Extract content using CSS selectors\nconst content = await page.extract({\n title: 'h1', // Extracts the main heading\n description: 'p', // Extracts the first paragraph\n link: 'a' // Extracts the first link text\n});\n\n// Display the extracted content\nconsole.log('Extracted content:');\nconsole.log(`Title: ${content.title}`);\nconsole.log(`Description: ${content.description}`);\nconsole.log(`Link: ${content.link}`);\n\nThe `extract` method uses CSS selectors to find elements on the page and extract their text content. This is a powerful way to scrape structured data from websites.\n\n### Proper Resource Management\n\nAlways remember to close resources when you're done with them to prevent memory leaks:\n\nCode (javascript):\n// Close the page when done\nawait page.close();\n\n// Close the client connection\nawait client.close();\n\n### Putting It All Together\n\nHere's a complete example that combines all the steps above into a single function:\n\nCode (javascript):\nimport { HerdClient } from '@monitoro/herd';\n\nasync function runBasicAutomation() {\n const client = new HerdClient('your-token');\n \n try {\n // Initialize the client\n await client.initialize();\n console.log('Client initialized successfully');\n \n // Get the first available device\n const devices = await client.listDevices();\n if (devices.length === 0) {\n throw new Error('No devices available');\n }\n const device = devices[0];\n console.log(`Connected to device: ${device.id}`);\n \n // Create a new page\n const page = await device.newPage();\n console.log('New page created');\n \n // Navigate to a website\n console.log('Navigating to example.com...');\n await page.goto('https://example.com');\n console.log('Navigation complete');\n \n // Extract content\n console.log('Extracting content...');\n const content = await page.extract({\n title: 'h1',\n description: 'p',\n link: 'a'\n });\n \n // Display the extracted content\n console.log('\\nExtracted content:');\n console.log(`Title: ${content.title}`);\n console.log(`Description: ${content.description}`);\n console.log(`Link: ${content.link}`);\n \n } catch (error) {\n console.error('Error during automation:', error);\n } finally {\n // Always close the client when done\n console.log('Closing client connection...');\n await client.close();\n console.log('Client connection closed');\n }\n}\n\n// Run the automation\nrunBasicAutomation();\n\n### Interacting with Web Pages\n\nNow let's explore how to interact with elements on a page. This includes clicking buttons, typing text, and handling forms.\n\n#### Finding Elements\n\nBefore interacting with an element, you need to find it on the page:\n\nCode (javascript):\n// Find an element using a CSS selector\nconst searchBox = await page.$('input[name=\"q\"]');\n\n// Check if the element was found\nif (searchBox) {\n console.log('Search box found');\n} else {\n console.log('Search box not found');\n}\n\nThe ` herd.garden MCP | Top Sites | DialtoneApp

MCP Explorer

Herd - Browser Superpowers & MCP Servers for AI Agents

DialtoneApp is using the stored discovery files and saved MCP scan metadata for this domain to look for an MCP endpoint, verify the handshake, and turn the result into a readable chat.

DialtoneApp found a lead, but the endpoint did not complete a usable MCP handshake.

idle
Visit site

Discovered endpoint

No endpoint found in stored discovery content

Server info

No successful initialize result yet.

Live Chat

MCP conversation

Statusinitialize handshake...

DialtoneApp could not start a live chat with this MCP server.

method returns the first element that matches the CSS selector, or `null` if no element is found.\n\n#### Typing Text\n\nTo type text into an input field:\n\nCode (javascript):\n// Type text into an input field\nawait page.type('input[name=\"q\"]', 'Monitoro Herd automation');\nconsole.log('Text entered into search box');\n\nThe `type` method finds the element using the CSS selector and simulates typing the specified text.\n\n#### Clicking Elements\n\nTo click a button or link:\n\nCode (javascript):\n// Click a button\nawait page.click('input[type=\"submit\"]');\nconsole.log('Search button clicked');\n\nBy default, the `click` method just clicks the element. If you want to wait for navigation to complete after clicking:\n\nCode (javascript):\n// Click and wait for navigation\nawait page.click('input[type=\"submit\"]', { \n waitForNavigation: 'networkidle2' \n});\nconsole.log('Search button clicked and navigation completed');\n\nThe `networkidle2` option waits until there are no more than 2 network connections for at least 500ms.\n\n#### Waiting for Elements\n\nSometimes you need to wait for elements to appear on the page:\n\nCode (javascript):\n// Wait for an element to appear\nawait page.waitForSelector('#search');\nconsole.log('Search results have loaded');\n\nThis is useful when dealing with dynamic content that loads after the initial page load.\n\n#### Search Engine Example\n\nLet's put these concepts together in a search engine example:\n\nCode (javascript):\nasync function searchExample() {\n const client = new HerdClient('your-token');\n \n try {\n await client.initialize();\n const devices = await client.listDevices();\n const device = devices[0];\n const page = await device.newPage();\n \n // Navigate to a search engine\n console.log('Navigating to Google...');\n await page.goto('https://www.google.com');\n \n // Type in the search box\n console.log('Entering search query...');\n await page.type('input[name=\"q\"]', 'Monitoro Herd automation');\n \n // Submit the search form and wait for results\n console.log('Submitting search...');\n await page.click('input[type=\"submit\"]', { \n waitForNavigation: 'networkidle2' \n });\n \n // Wait for results to load completely\n console.log('Waiting for search results...');\n await page.waitForSelector('#search');\n \n // Extract search result titles\n console.log('Extracting search results...');\n const searchResults = await page.extract({\n titles: {\n _$r: '#search .g h3', // _$r extracts multiple elements\n text: ':root' // For each match, get its text\n }\n });\n \n // Display the search result titles\n console.log('\\nSearch Results:');\n searchResults.titles.forEach((result, index) =\u003e {\n console.log(`${index + 1}. ${result.text}`);\n });\n \n } catch (error) {\n console.error('Error:', error);\n } finally {\n await client.close();\n }\n}\n\n## python\n\n## Python SDK\n\n### Setting Up Your Environment\n\nBefore writing any code, you'll need to set up your Python environment and install the Herd SDK:\n\n1. Make sure you have Python 3.8+ installed\n2. Create a virtual environment (recommended)\n3. Install the SDK using pip:\n\nCode (bash):\npip install monitoro-herd\n\n### Initializing the Client\n\nThe first step in any automation is to initialize the Herd client with your API credentials:\n\nCode (python):\n# Import the Herd client\nfrom monitoro_herd import HerdClient\n\n# Initialize the client with your API URL and token\nclient = HerdClient('your-token')\n\n# Always initialize the client before using it\nclient.initialize()\nNote: **Note:** Replace the token with your actual Herd API token from your dashboard.\n\n### Connecting to a Device\n\nNext, connect to a device that will run your automation:\n\nCode (python):\n# Get available devices\ndevices = await client.list_devices()\n\n# Connect to the first device\ndevice = devices[0]\n\nprint(f\"Connected to device: {device.id}\")\n\n### Creating a Page and Navigating\n\nNow create a browser page and navigate to a website:\n\nCode (python):\n# Create a new page\npage = await device.new_page()\n\n# Navigate to a website\nawait page.goto(\"https://example.com\")\n\nprint(\"Successfully navigated to example.com\")\n\n### Extracting Basic Information\n\nExtract information from the page using CSS selectors:\n\nCode (python):\n# Extract basic information\ndata = await page.extract({\n \"title\": \"h1\", # Main heading\n \"description\": \"p\", # First paragraph\n \"link\": \"a\" # First link text\n})\n\n# Display the extracted data\nprint(\"Extracted data:\")\nprint(f\"Title: {data['title']}\")\nprint(f\"Description: {data['description']}\")\nprint(f\"Link: {data['link']}\")\n\n### Resource Management\n\nAlways close resources when you're done:\n\nCode (python):\n# Close the page\nawait page.close()\n\n# Close the client\nawait client.close()\n\n### Complete Basic Example\n\nHere's a complete example putting all these concepts together:\n\nCode (python):\nimport asyncio\nfrom monitoro_herd import HerdClient\n\nasync def basic_extraction():\n # Initialize the client\n client = HerdClient(\"your-token\")\n \n try:\n # Initialize the connection\n await client.initialize()\n print(\"Client initialized successfully\")\n \n # Get the first available device\n devices = await client.list_devices()\n if not devices:\n raise Exception(\"No devices available\")\n device = devices[0]\n print(f\"Connected to device: {device.id}\")\n \n # Create a new page\n page = await device.new_page()\n print(\"New page created\")\n \n # Navigate to a website\n print(\"Navigating to example.com...\")\n await page.goto(\"https://example.com\")\n print(\"Navigation complete\")\n \n # Extract data using simple selectors\n print(\"Extracting content...\")\n data = await page.extract({\n \"title\": \"h1\",\n \"description\": \"p\",\n \"link\": \"a\"\n })\n \n # Display the extracted data\n print(\"\\nExtracted data:\")\n print(f\"Title: {data['title']}\")\n print(f\"Description: {data['description']}\")\n print(f\"Link: {data['link']}\")\n \n except Exception as e:\n print(f\"Error during automation: {e}\")\n finally:\n # Always close resources\n print(\"Closing client connection...\")\n await client.close()\n print(\"Client connection closed\")\n\n# Run the async function\nasyncio.run(basic_extraction())\n\n### Working with Lists and Structured Data\n\nOne of the most powerful features of Herd is the ability to extract structured data from lists of elements. This is perfect for scraping search results, product listings, or article collections.\n\n#### The `_$r` Selector\n\nTo extract multiple elements that match a pattern, use the `_$r` (repeat) selector:\n\nCode (python):\n# Extract a list of items\ndata = await page.extract({\n \"items\": {\n \"_$r\": \".item\", # Find all elements with class \"item\"\n \"name\": \".item-name\", # For each item, get the name\n \"price\": \".price\" # For each item, get the price\n }\n})\n\n# Access the extracted items\nfor item in data[\"items\"]:\n print(f\"Name: {item['name']}, Price: {item['price']}\")\n\nThe `_$r` selector tells Herd to find all elements matching the selector and extract the specified properties for each one.\n\n#### Extracting Attributes\n\nSometimes you need to extract an attribute rather than the text content:\n\nCode (python):\n# Extract links and their href attributes\ndata = await page.extract({\n \"links\": {\n \"_$r\": \"a\", # Find all links\n \"text\": \":root\", # Get the link text\n \"url\": {\n \"_$\": \":root\", # Reference the same element\n \"attribute\": \"href\" # Get its href attribute\n }\n }\n})\n\n# Display the links\nfor link in data[\"links\"]:\n print(f\"Link: {link['text']} -\u003e {link['url']}\")\n\n#### Hacker News Example\n\nLet's put these concepts together to scrape stories from Hacker News:\n\nCode (python):\nimport asyncio\nfrom monitoro_herd import HerdClient\n\nasync def scrape_hacker_news():\n client = HerdClient(\"your-token\")\n \n try:\n await client.initialize()\n devices = await client.list_devices()\n device = devices[0]\n page = await device.new_page()\n \n # Navigate to Hacker News\n print(\"Navigating to Hacker News...\")\n await page.goto(\"https://news.ycombinator.com\")\n \n # Extract stories and their metadata\n print(\"Extracting stories...\")\n data = await page.extract({\n # Extract the story elements\n \"stories\": {\n \"_$r\": \".athing\", # Each story row\n \"title\": \".titleline \u003e a\", # Story title\n \"site\": \".sitestr\", # Source website\n \"link\": {\n \"_$\": \".titleline \u003e a\", # Story link\n \"attribute\": \"href\" # Get the URL\n }\n },\n # Extract the metadata (points, author, etc.)\n \"metadata\": {\n \"_$r\": \".subline\", # Metadata rows\n \"points\": \".score\", # Points count\n \"author\": \".hnuser\", # Author username\n \"time\": \".age\" # Submission time\n }\n })\n \n # Combine stories with their metadata\n # (They're in separate lists but in the same order)\n combined_stories = list(zip(data[\"stories\"], data[\"metadata\"]))\n \n # Display the first 3 stories\n print(f\"\\nExtracted {len(combined_stories)} stories:\")\n for i, (story, meta) in enumerate(combined_stories[:3]):\n print(f\"\\nStory {i+1}:\")\n print(f\"Title: {story['title']}\")\n if \"site\" in story:\n print(f\"Site: {story['site']}\")\n print(f\"Link: {story['link']}\")\n if \"points\" in meta:\n print(f\"Points: {meta['points']}\")\n if \"author\" in meta:\n print(f\"Author: {meta['author']}\")\n if \"time\" in meta:\n print(f\"Posted: {meta['time']}\")\n \n finally:\n await page.close()\n await client.close()\n\n# Run the function\nasyncio.run(scrape_hacker_news())\n\n## Tips for Successful Automation\n\n1. **Start Simple**: Begin with basic extractions before moving to complex interactions\n2. **Use Appropriate Selectors**: Learn CSS selectors to target elements precisely\n3. **Handle Errors**: Always include try/catch (JavaScript) or try/except (Python) blocks\n4. **Close Resources**: Always close pages and clients when done to avoid resource leaks\n5. **Test Incrementally**: Build your automation step by step, testing each part\n6. **Add Delays When Needed**: For dynamic content, use `waitForSelector` or similar methods\n7. **Debug with Screenshots**: Take screenshots during automation to see what's happening\n\n## Next Steps\n\nNow that you've created your first automation, you can:\n\n- Explore more complex selectors and extraction patterns\n- Learn how to handle authentication and login flows\n- Set up scheduled automations for regular data collection\n- Integrate with your existing systems via APIs\n\n================================================================================\n\nDocument: Connect Your Browser\nURL: https://herd.garden/docs/connect-your-browser\n\n# Connect your Browser to Herd\n\nAfter installing the Herd extension, you need to connect your browser to your account. This guide explains how to establish and manage connections between your browsers and the Herd platform. You can connect multiple browsers to the same account. Learn more about [managing multiple devices](/docs/device-management).\n\n## Device Registration\n\nBefore you can connect a browser, you need to register the device in your Herd dashboard:\n\n1. Log in to your Herd account\n2. Navigate to the \"Devices\" section\n3. Click \"Register New Device\" \n4. Enter a descriptive name for the device (e.g., \"Work Laptop - Chrome\")\n5. Choose appropriate tags if you're organizing devices into groups\n6. Click \"Create Registration\"\n7. Copy the registration code that appears (you'll need this to connect the browser)\n\n## Connecting Your Browser\n\nOnce you have a registration code, follow these steps to connect your browser:\n\n1. Make sure the Herd extension is installed in your browser\n2. Click the Herd icon in your browser toolbar\n3. Select \"Connect Browser\" or \"Register Device\" from the menu\n4. Paste the registration code into the field\n5. Click \"Connect\"\n6. You should see a confirmation message indicating the browser is now connected\n\n## Managing Active Connections\n\nYou can view and manage all your connected browsers from the Herd dashboard:\n\n### Viewing Connected Devices\n\n1. Log in to your Herd account\n2. Navigate to the \"Devices\" section\n3. The \"Active Connections\" tab shows all currently connected browsers\n4. Each connection displays information such as:\n - Device name\n - Browser type and version\n - Connection status\n - Last activity timestamp\n\n### Remote Actions\n\nOnce a browser is connected, you can perform various remote actions:\n\n1. **Remote Control**: Initiate a remote control session to view and interact with the browser\n2. **Capture Screenshot**: Take a snapshot of the current browser window\n3. **Tab Management**: View, open, close, or navigate tabs\n4. **Bookmark Management**: View or modify the browser's bookmarks\n5. **History Access**: View browsing history (if permissions allow)\n\n## Connection Troubleshooting\n\nIf you're having trouble connecting your browser to Herd, try these solutions:\n\n### Connection Failures\n\n* Verify that the registration code is correct and hasn't expired\n* Make sure the browser is online and has a stable internet connection\n* Check that the Herd extension is properly installed and enabled\n* Try restarting your browser\n\n### Dropped Connections\n\n* Check your network stability\n* Ensure the browser hasn't entered sleep mode\n* Verify that the extension hasn't been disabled\n* Check if a browser update has affected the extension\n\n### Reconnecting\n\nIf a connection is lost, you can easily reconnect:\n\n1. Click the Herd icon in your browser toolbar\n2. If the connection status shows \"Disconnected,\" click \"Reconnect\"\n3. If prompted, enter your registration code again\n4. Wait for the connection to be reestablished\n\n## Connection Security\n\nAll connections between your browser and the Herd platform are secured with end-to-end encryption. Your data remains private and protected throughout the connection process.\n\nFor more information on security features, see our [Security \u0026 Privacy](security-privacy) documentation.\n\n================================================================================\n\nDocument: Data Extraction\nURL: https://herd.garden/docs/data-extraction\n\n# Data Extraction\n\nWelcome to Monitoro Herd's powerful data extraction system! This guide will walk you through how to extract structured data from web pages using our intuitive selector system and transformation pipelines.\n\n## Understanding Selectors\n\nHerd provides a flexible and powerful way to extract data from web pages using a declarative JSON-based selector system.\n\n### Basic Extraction\n\n## javascript\n\nThe simplest form of extraction uses CSS selectors to target elements:\n\nCode (javascript):\n// Extract basic text content\nconst data = await page.extract({\n title: 'h1', // Extracts the main heading\n description: 'p', // Extracts the first paragraph\n link: 'a' // Extracts the first link text\n});\n\nconsole.log(data.title); // \"Welcome to Our Website\"\nconsole.log(data.description); // \"This is our homepage.\"\n\n## python\n\nThe simplest form of extraction uses CSS selectors to target elements:\n\nCode (python):\n# Extract basic text content\ndata = await page.extract({\n \"title\": \"h1\", # Extracts the main heading\n \"description\": \"p\", # Extracts the first paragraph\n \"link\": \"a\" # Extracts the first link text\n})\n\nprint(data[\"title\"]) # \"Welcome to Our Website\"\nprint(data[\"description\"]) # \"This is our homepage.\"\n\n### Advanced Selector Syntax\n\n## javascript\n\nFor more complex extraction needs, use the expanded object syntax:\n\nCode (javascript):\nconst data = await page.extract({\n title: {\n _$: 'h1', // CSS selector\n attribute: 'id' // Extract the ID attribute instead of text\n },\n price: {\n _$: '.price', // Target price element\n pipes: ['parseNumber'] // Apply transformation\n }\n});\n\n## python\n\nFor more complex extraction needs, use the expanded object syntax:\n\nCode (python):\ndata = await page.extract({\n \"title\": {\n \"_$\": \"h1\", # CSS selector\n \"attribute\": \"id\" # Extract the ID attribute instead of text\n },\n \"price\": {\n \"_$\": \".price\", # Target price element\n \"pipes\": [\"parseNumber\"] # Apply transformation\n }\n})\n\n### Extracting Lists of Items\n\n## javascript\n\nTo extract multiple elements that match a pattern, use the `_$r` (repeat) selector:\n\nCode (javascript):\nconst data = await page.extract({\n items: {\n _$r: '.item', // Find all elements with class \"item\"\n title: 'h2', // For each item, get the title\n price: '.price', // For each item, get the price\n date: 'time' // For each item, get the date\n }\n});\n\n// Access the extracted items\ndata.items.forEach(item =\u003e {\n console.log(`${item.title}: ${item.price}, Posted: ${item.date}`);\n});\n\n## python\n\nTo extract multiple elements that match a pattern, use the `_$r` (repeat) selector:\n\nCode (python):\ndata = await page.extract({\n \"items\": {\n \"_$r\": \".item\", # Find all elements with class \"item\"\n \"title\": \"h2\", # For each item, get the title\n \"price\": \".price\", # For each item, get the price\n \"date\": \"time\" # For each item, get the date\n }\n})\n\n# Access the extracted items\nfor item in data[\"items\"]:\n print(f\"{item['title']}: {item['price']}, Posted: {item['date']}\")\n\n### Nested Extraction\n\n## javascript\n\nYou can nest selectors to extract hierarchical data:\n\nCode (javascript):\nconst data = await page.extract({\n product: {\n name: '.product-name',\n details: {\n _$: '.product-details',\n specs: {\n _$r: '.spec-item',\n label: '.spec-label',\n value: '.spec-value'\n }\n }\n }\n});\n\n## python\n\nYou can nest selectors to extract hierarchical data:\n\nCode (python):\ndata = await page.extract({\n \"product\": {\n \"name\": \".product-name\",\n \"details\": {\n \"_$\": \".product-details\",\n \"specs\": {\n \"_$r\": \".spec-item\",\n \"label\": \".spec-label\",\n \"value\": \".spec-value\"\n }\n }\n }\n})\n\n## Special Selectors\n\nHerd provides special selectors to handle various extraction scenarios:\n\n### Root Selector (`:root`)\n\nThe `:root` selector refers to the current element in context:\n\n## javascript\n\nCode (javascript):\nconst data = await page.extract({\n items: {\n _$r: '.item',\n someElement: ':root', // Extract text of the .item element itself\n classes: {\n _$: ':root',\n attribute: 'class' // Extract class attribute of the same element\n }\n }\n});\n\n## python\n\nCode (python):\ndata = await page.extract({\n \"items\": {\n \"_$r\": \".item\",\n \"someElement\": \":root\", # Extract text of the .item element itself\n \"classes\": {\n \"_$\": \":root\",\n \"attribute\": \"class\" # Extract class attribute of the same element\n }\n }\n})\n\n### Property Extraction\n\nYou can extract JavaScript properties from elements:\n\n## javascript\n\nCode (javascript):\nconst data = await page.extract({\n dimensions: {\n _$: '.box',\n property: 'getBoundingClientRect' // Get element dimensions\n },\n html: {\n _$: '.content',\n property: 'innerHTML' // Get inner HTML\n }\n});\n\n## python\n\nCode (python):\ndata = await page.extract({\n \"dimensions\": {\n \"_$\": \".box\",\n \"property\": \"getBoundingClientRect\" # Get element dimensions\n },\n \"html\": {\n \"_$\": \".content\",\n \"property\": \"innerHTML\" # Get inner HTML\n }\n})\n\n## Transformation Pipelines\n\nHerd includes powerful transformation pipelines to process extracted data:\n\n### Available Transformations\n\n| Pipe | Description | Example Input | Example Output |\n|------|-------------|--------------|----------------|\n| `trim` | Removes whitespace from start/end | `\" Hello \"` | `\"Hello\"` |\n| `toLowerCase` | Converts text to lowercase | `\"HELLO\"` | `\"hello\"` |\n| `toUpperCase` | Converts text to uppercase | `\"hello\"` | `\"HELLO\"` |\n| `parseNumber` | Extracts numbers from text | `\"$1,2K.45\"` | `1200.45` |\n| `parseDate` | Converts text to date | `\"2024-01-15\"` | `\"2024-01-15T00:00:00.000Z\"` |\n| `parseDateTime` | Converts text to datetime | `\"2024-01-15T12:00:00Z\"` | `\"2024-01-15T12:00:00.000Z\"` |\n\n### Using Transformations\n\nApply transformations using the `pipes` property:\n\n## javascript\n\nCode (javascript):\nconst data = await page.extract({\n price: {\n _$: '.price',\n pipes: ['parseNumber'] // Convert \"$1,234.56\" to 1234.56\n },\n title: {\n _$: 'h1',\n pipes: ['trim', 'toLowerCase'] // Apply multiple transformations\n }\n});\n\n## python\n\nCode (python):\ndata = await page.extract({\n \"price\": {\n \"_$\": \".price\",\n \"pipes\": [\"parseNumber\"] # Convert \"$1,234.56\" to 1234.56\n },\n \"title\": {\n \"_$\": \"h1\",\n \"pipes\": [\"trim\", \"toLowerCase\"] # Apply multiple transformations\n }\n})\n\n### Handling Currency and Large Numbers\n\nThe `parseNumber` transformation handles various formats:\n\n## javascript\n\nCode (javascript):\nconst data = await page.extract({\n price1: {\n _$: '.price-1', // Contains \"$1,234.56\"\n pipes: ['parseNumber'] // Result: 1234.56\n },\n price2: {\n _$: '.price-2', // Contains \"$1.5M\"\n pipes: ['parseNumber'] // Result: 1500000\n },\n price3: {\n _$: '.price-3', // Contains \"1.5T€\"\n pipes: ['parseNumber'] // Result: 1500000000000\n }\n});\n\n## python\n\nCode (python):\ndata = await page.extract({\n \"price1\": {\n \"_$\": \".price-1\", # Contains \"$1,234.56\"\n \"pipes\": [\"parseNumber\"] # Result: 1234.56\n },\n \"price2\": {\n \"_$\": \".price-2\", # Contains \"$1.5M\"\n \"pipes\": [\"parseNumber\"] # Result: 1500000\n },\n \"price3\": {\n \"_$\": \".price-3\", # Contains \"1.5T€\"\n \"pipes\": [\"parseNumber\"] # Result: 1500000000000\n }\n})\n\n## Real-World Examples\n\nLet's look at some practical examples of data extraction:\n\n### E-commerce Product Listing\n\nExtract products from a search results page:\n\n## javascript\n\nCode (javascript):\nconst searchResults = await page.extract({\n products: {\n _$r: '[data-component-type=\"s-search-result\"]',\n title: {\n _$: 'h2 .a-link-normal',\n pipes: ['trim']\n },\n price: {\n _$: '.a-price .a-offscreen',\n pipes: ['parseNumber']\n },\n rating: {\n _$: '.a-icon-star-small .a-icon-alt',\n pipes: ['trim']\n },\n reviews: {\n _$: '.a-size-base.s-underline-text',\n pipes: ['trim']\n }\n }\n});\n\n## python\n\nCode (python):\nsearchResults = await page.extract({\n \"products\": {\n \"_$r\": '[data-component-type=\"s-search-result\"]',\n \"title\": {\n \"_$\": \"h2 .a-link-normal\",\n \"pipes\": [\"trim\"]\n },\n \"price\": {\n \"_$\": \".a-price .a-offscreen\",\n \"pipes\": [\"parseNumber\"]\n },\n \"rating\": {\n \"_$\": \".a-icon-star-small .a-icon-alt\",\n \"pipes\": [\"trim\"]\n },\n \"reviews\": {\n \"_$\": \".a-size-base.s-underline-text\",\n \"pipes\": [\"trim\"]\n }\n }\n})\n\n### News Article List\n\nExtract articles from a news site:\n\n## javascript\n\nCode (javascript):\nconst articles = await page.extract({\n items: {\n _$r: '.item',\n title: {\n _$: 'h2',\n pipes: ['trim', 'toLowerCase']\n },\n price: {\n _$: '.price',\n pipes: ['parseNumber']\n },\n date: {\n _$: 'time',\n pipes: ['parseDate']\n }\n }\n});\n\n## python\n\nCode (python):\narticles = await page.extract({\n \"items\": {\n \"_$r\": \".item\",\n \"title\": {\n \"_$\": \"h2\",\n \"pipes\": [\"trim\", \"toLowerCase\"]\n },\n \"price\": {\n \"_$\": \".price\",\n \"pipes\": [\"parseNumber\"]\n },\n \"date\": {\n \"_$\": \"time\",\n \"pipes\": [\"parseDate\"]\n }\n }\n})\n\n## Advanced Techniques\n\n### Handling Dynamic Content\n\nFor dynamic content that loads after the page is ready:\n\n## javascript\n\nCode (javascript):\n// Wait for dynamic content to load\nawait page.waitForElement('#dynamic span');\n\n// Then extract the content\nconst data = await page.extract({\n content: '#dynamic span'\n});\n\n## python\n\nCode (python):\n# Wait for dynamic content to load\nawait page.waitForElement('#dynamic span')\n\n# Then extract the content\ndata = await page.extract({\n \"content\": \"#dynamic span\"\n})\n\n### Extracting Page Metadata\n\nExtract information about the page itself:\n\n## javascript\n\nCode (javascript):\nconst pageInfo = await page.extract({\n title: 'title',\n metaDescription: 'meta[name=\"description\"]',\n canonicalUrl: {\n _$: 'link[rel=\"canonical\"]',\n attribute: 'href'\n }\n});\n\n## python\n\nCode (python):\npageInfo = await page.extract({\n \"title\": \"title\",\n \"metaDescription\": 'meta[name=\"description\"]',\n \"canonicalUrl\": {\n \"_$\": 'link[rel=\"canonical\"]',\n \"attribute\": \"href\"\n }\n})\n\n## Tips for Effective Extraction\n\n1. **Use Specific Selectors**: The more specific your CSS selectors, the more reliable your extraction\n2. **Test Incrementally**: Build your extraction schema step by step, testing each part\n3. **Handle Missing Data**: Always account for elements that might not exist on the page\n4. **Apply Appropriate Transformations**: Use pipes to clean and format data as needed\n5. **Combine with Interactions**: For complex sites, interact with the page before extraction\n\n## Next Steps\n\nNow that you understand Herd's data extraction system, you can:\n\n- Create complex extraction schemas for any website\n- Transform raw data into structured, usable formats\n- Build powerful automations that collect and process web data\n\n================================================================================\n\nDocument: Device Management\nURL: https://herd.garden/docs/device-management\n\n# Device Management\n\nManaging your devices in Herd is simple and intuitive. This guide will walk you through the various actions you can perform on the Devices page.\n\n## Understanding the Devices Page\n\nThe Devices page is your central hub for managing all browsers and headless devices connected to your Herd account. Here you can:\n\n- View all your connected devices\n- Register new devices\n- Access device registration URLs\n- Delete devices you no longer need\n\n## Viewing Your Devices\n\nWhen you visit the Devices page, you'll see a list of all your registered devices. For each device, you can view:\n\n- Device name\n- Status (active or inactive)\n- Device ID\n- Device type (browser or headless)\n- Last active timestamp\n\nDevices with an active status will display a pulsing green indicator, while inactive devices will show a gray status indicator.\n\n## Registering a New Device\n\nTo add a new device to your Herd account:\n\n1. Click the **Register New Device** button at the top of the Devices page\n2. Enter a name for your device (or use the suggested name)\n3. Select the device type:\n - **Browser**: For devices with a visual interface such as your own browser\n - **Headless**: For devices running in headless mode (for docker and kubernetes deployments)\n4. Click **Register Device**\n5. A registration URL will be generated - use this URL to connect your device to Herd\n\nThe registration URL will be stored locally in your browser, allowing you to access it again later if needed.\n\n## Accessing Registration URLs\n\nIf you need to access a previously generated registration URL:\n\n1. Find the device in your devices list\n2. Look for the \"Registration URL available\" indicator\n3. Click the **View URL** button\n4. The registration URL will be displayed in a modal window\n\nThis is particularly useful if you need to reconnect a device or share the registration link with team members.\n\n## Deleting a Device\n\nTo remove a device from your Herd account:\n\n1. Find the device you want to delete in your devices list\n2. Click the **Delete** button for that device\n3. Confirm the deletion in the confirmation dialog\n\nPlease note that deleting a device is permanent and cannot be undone. The device will be removed from your account, and any stored registration URLs for that device will be cleared from your local storage.\n\n## Device Status\n\nDevices in Herd can have different statuses:\n\n- **Active**: The device is currently connected and ready to use\n- **Inactive**: The device is registered but not currently connected\n\nAn active device can be used immediately for automation tasks, while inactive devices need to be reconnected before use.\n\n## Best Practices\n\nHere are some tips for effective device management:\n\n- Use descriptive names for your devices to easily identify them\n- Regularly clean up unused devices to keep your dashboard organized\n- Store registration URLs securely if you plan to share them with team members\n- Check the \"Last Active\" timestamp to identify devices that haven't been used recently\n\nBy following these guidelines, you'll be able to maintain an organized and efficient device management system in Herd.\n\n================================================================================\n\nDocument: Getting Started\nURL: https://herd.garden/docs/getting-started\n\n# Getting Started with Herd\n\nThis guide will help you get up and running with Herd quickly to run your first trail.\n\n## What is Herd?\n\nHerd connects AI Agents to websites using your own browser credentials. It enables you to:\n\n- **Run Trails** - pre-built automations for specific websites and tasks\n- **Extract data and interact with websites** using your logged-in browser sessions\n- **Interact with web pages** through AI Agents like OpenAI's ChatGPT and Anthropic's Claude\n\n## Quick Start\n\n### 1. Install the Browser Extension\n\n Chrome\n\n Edge\n\n Brave\n\n### 2. Register Your Browser\n\nAfter installing the extension:\n\n1. Click the Herd icon in your browser toolbar\n2. Sign in with your Herd account (or create one)\n3. Name your device and register it\n\n![Browser Registration](https://herd.garden/register-device.png)\n\n### 3. Install the Herd SDK\n\nInstall the Herd SDK using npm:\n\n## npm\n\nCode (bash):\nnpm install -g @monitoro/herd\n\n## yarn\n\nCode (bash):\nyarn global add @monitoro/herd\n\n## pnpm\n\nCode (bash):\npnpm add -g @monitoro/herd\n\n### 4. Run Your First Trail\n\nThe browser trail provides core functionality for navigating and extracting data from any website. Run this command to test it out:\n\nCode (bash):\nherd trail run @herd/browser -a markdown -p '{\"url\": \"https://example.com\"}'\n\nThat's it! Add it to your MCP config to use it in your AI agents like in this example. Note, you can add as many trails as you want to your MCP config:\n\nCode (json):\n{\n \"mcpServers\": {\n \"browser\": {\n \"command\": \"herd\",\n \"args\": [\n \"trail\",\n \"server\",\n \"@herd/browser\"\n ]\n }\n }\n}\n\n## For Developers\n\nYou can also automate your browser with the Herd SDK. Connect to it with your AI agents or code:\n\n## javascript\n\nCode (javascript):\n// Connect to your Herd device\nconst client = new HerdClient('your-token');\nawait client.initialize();\nconst devices = await client.listDevices();\nconst device = devices[0];\n\n// Create a new page and navigate\nconst page = await device.newPage();\nawait page.goto(\"https://example.com\");\n\n// Extract data using simple selectors\nconst data = await page.extract({\n title: \"h1\",\n description: \"p\",\n link: \"a\"\n});\n\nconsole.log(\"Extracted data:\", data);\n\n## python\n\nCode (python):\nfrom monitoro_herd import HerdClient\n\n# Connect to your Herd device\nclient = HerdClient(\"your-token\")\nawait client.initialize()\ndevices = await client.list_devices()\ndevice = devices[0]\n\n# Create a new page and navigate\npage = await device.new_page()\nawait page.goto(\"https://example.com\")\n\n# Extract data using simple selectors\ndata = await page.extract({\n \"title\": \"h1\",\n \"description\": \"p\",\n \"link\": \"a\"\n})\n\nprint(\"Extracted data:\", data)\n\n## What's Next?\n\nNow that you've run your first trail, you can:\n\n- [Explore available trails](/trails) - Browse pre-built trails for various websites\n- [Learn about data extraction](/docs/data-extraction) - Extract structured data from web pages\n- [Create your own trail](/docs/trails-automations) - Build and share your own custom trails\n\n## Need Help?\n\nIf you encounter any issues during setup:\n\n- Make sure your browser extension is correctly installed and you're signed in\n- Check that your device is registered in the [device dashboard](/devices)\n- Visit our [troubleshooting guide](/docs/troubleshooting) for common solutions\n\n.browser-btn {\n display: inline-flex;\n align-items: center;\n padding: 0.2rem 1rem;\n background-color: #1f2937;\n color: white;\n border-radius: 0.375rem;\n font-size: 1.2rem;\n font-weight: 500;\n text-decoration: none;\n border: 1px solid rgba(255, 255, 255, 0.1);\n}\n\n.browser-btn:hover {\n background-color: #374151;\n}\n\n================================================================================\n\nDocument: Installation\nURL: https://herd.garden/docs/installation\n\n# Installing the Herd Extension\n\nThe Herd extension is the client component that allows your browser to be remotely managed. This guide provides detailed instructions for installing the extension on different browsers.\n\n## Chrome Installation\n\nThe Herd extension is primarily designed for Google Chrome and Chromium-based browsers. Follow these steps to install:\n\n### Standard Installation\n\n1. Download the Herd extension from your dashboard by clicking \"Download Herd\" in the navigation bar\n2. Open Chrome and navigate to `chrome://extensions`\n3. Enable \"Developer mode\" by toggling the switch in the top-right corner\n4. Drag and drop the downloaded `herd-latest.zip` file onto the extensions page\n5. Chrome will automatically install the extension\n\n### Verifying Installation\n\nAfter installation, you should see the Herd extension in your extensions list. To verify it's working correctly:\n\n1. Look for the Herd icon in your browser toolbar\n2. If it's not visible, click the puzzle piece icon to see all extensions and pin the Herd extension\n3. The icon should be colored, indicating it's ready to be connected\n\n## Installation on Other Browsers\n\nWhile Herd works best with Chrome, it's also compatible with other Chromium-based browsers:\n\n### Microsoft Edge\n\n1. Download the Herd extension zip file\n2. Open Edge and navigate to `edge://extensions`\n3. Enable \"Developer mode\" using the toggle in the left sidebar\n4. Drag and drop the `herd-latest.zip` file onto the extensions page\n5. Follow the prompts to complete installation\n\n### Brave Browser\n\n1. Download the Herd extension zip file\n2. Open Brave and navigate to `brave://extensions`\n3. Enable \"Developer mode\" in the top-right corner\n4. Drag and drop the `herd-latest.zip` file onto the extensions page\n5. Confirm the installation when prompted\n\n## Enterprise Deployment\n\nFor enterprise environments, you may want to deploy the Herd extension to multiple browsers. Here are some options:\n\n### Chrome Enterprise Policy\n\nYou can use Chrome Enterprise policies to automatically install and configure the Herd extension:\n\n1. Extract the Herd extension zip file to a network location accessible to all users\n2. Configure a policy to force-install extensions from a local path\n3. Set up the appropriate extension settings via policy\n\n### Manual Distribution\n\nFor smaller teams, you can manually distribute the extension:\n\n1. Download the extension once\n2. Share the zip file with team members\n3. Provide them with instructions for installation\n4. Create device registrations for each team member in your Herd dashboard\n\n## Troubleshooting Installation Issues\n\nIf you encounter issues during installation, try these solutions:\n\n### Extension Won't Install\n\n* Make sure Developer mode is enabled in your browser's extensions page\n* Check that you're using a supported browser (Chrome, Edge, Brave)\n* Verify that the zip file wasn't corrupted during download (try re-downloading)\n* Make sure you're dragging the zip file itself, not an extracted folder\n\n### Extension Installed But Not Working\n\n* Check if the extension is enabled in your browser\n* Try restarting your browser\n* Verify that you've completed the device registration process\n* Check your browser's console for any error messages\n\nFor more troubleshooting tips, see our [Troubleshooting](troubleshooting) guide.\n\n================================================================================\n\nDocument: Reference Device\nURL: https://herd.garden/docs/reference-device\n\n# Device\n\nThe Device class represents a connected browser or device in the Herd platform. It provides methods for managing pages, handling events, and controlling the device's lifecycle. \n\nEach Device instance gives you full control over a browser, allowing you to create and manage pages (tabs), handle various browser events, and automate browser interactions.\n\n## javascript\n\nYou can obtain a Device instance either by calling `client.listDevices()` to get all available devices, or `client.getDevice(deviceId)` to get a specific device by its ID.\n\n## Properties\n\n### deviceId\nThe unique identifier for the device in the Herd platform. This is an internal ID that uniquely identifies the device in our system and is automatically generated when the device is registered.\n\n### type\nThe type of device, which indicates its capabilities and behavior. Currently supported types include:\n- 'browser': A browser instance that can be automated\n- 'headless': A headless browser instance running on docker or kubernetes\n\n### name\nAn optional display name for the device. This can be used to give the device a human-readable label for easier identification in your application or the Herd dashboard.\n\n### status\nThe current status of the device. Possible values include:\n- 'online': The device is connected and ready to receive commands\n- 'offline': The device is not currently connected\n- 'busy': The device is processing a command\n- 'error': The device encountered an error\n\n### lastActive\nA timestamp indicating when the device was last active. This is automatically updated whenever the device performs an action or responds to a command. The value is a JavaScript Date object.\n\n## Methods\n\n### newPage()\nCreates a new page (tab) in the device.\n\nCode (javascript):\n// Create a new page\nconst page = await device.newPage();\nconsole.log('New page created:', page.id);\n\n### listPages()\nReturns a list of all pages (tabs) currently open in the device.\n\nCode (javascript):\n// List all open pages\nconst pages = await device.listPages();\npages.forEach(page =\u003e {\n console.log(`Page ${page.id}: ${page.url}`);\n});\n\n### getPage(pageId)\nGets a specific page by ID.\n\nCode (javascript):\n// Get a specific page\nconst page = await device.getPage(123);\nconsole.log('Current URL:', page.url);\n\n### onEvent(callback)\nSubscribes to all events from the device. Returns an unsubscribe function.\n\nCode (javascript):\n// Subscribe to all device events\nconst unsubscribe = device.onEvent((event) =\u003e {\n console.log('Device event:', event);\n});\n\n// Later: stop listening to events\nunsubscribe();\n\n### on(eventName, callback)\nSubscribes to a specific event from the device. Returns the device instance for chaining.\n\nCode (javascript):\n// Subscribe to specific events\ndevice.on('navigation', (event) =\u003e {\n console.log('Navigation occurred:', event);\n}).on('console', (event) =\u003e {\n console.log('Console message:', event);\n});\n\n### close()\nCloses the device and cleans up resources. This will close all pages and remove event listeners.\n\nCode (javascript):\n// Close the device and cleanup\nawait device.close();\n\n## Example Usage\n\nHere's a complete example showing how to use the Device class:\n\nCode (javascript):\nimport { HerdClient } from '@monitoro/herd';\n\nasync function main() {\n const client = new HerdClient({\n token: 'your-auth-token'\n });\n \n await client.initialize();\n \n // Get a device\n const device = await client.getDevice('my-browser');\n \n // Create a new page and navigate\n const page = await device.newPage();\n await page.goto('https://example.com');\n \n // Listen for navigation events\n device.on('navigation', (event) =\u003e {\n console.log('Page navigated:', event.url);\n });\n \n // List all pages\n const pages = await device.listPages();\n console.log(`Device has ${pages.length} pages open`);\n \n // Cleanup when done\n await device.close();\n await client.close();\n}\n\nmain().catch(console.error);\n\n## python\n\nYou can obtain a Device instance either by calling `client.list_devices()` to get all available devices, or `client.get_device(device_id)` to get a specific device by its ID.\n\n## Properties\n\n### device_id\nThe unique identifier for the device in the Herd platform. This is an internal ID that uniquely identifies the device in our system and is automatically generated when the device is registered.\n\n### type\nThe type of device, which indicates its capabilities and behavior. Currently supported types include:\n- 'browser': A browser instance that can be automated\n- 'headless': A headless browser instance running on docker or kubernetes\n\n### name\nAn optional display name for the device. This can be used to give the device a human-readable label for easier identification in your application or the Herd dashboard.\n\n### status\nThe current status of the device. Possible values include:\n- 'online': The device is connected and ready to receive commands\n- 'offline': The device is not currently connected\n- 'busy': The device is processing a command\n- 'error': The device encountered an error\n\n### last_active\nA timestamp indicating when the device was last active. This is automatically updated whenever the device performs an action or responds to a command. The value is a Python datetime object.\n\n## Methods\n\n### new_page()\nCreates a new page (tab) in the device.\n\nCode (python):\n# Create a new page\npage = await device.new_page()\nprint(f\"New page created: {page.id}\")\n\n### list_pages()\nReturns a list of all pages (tabs) currently open in the device.\n\nCode (python):\n# List all open pages\npages = await device.list_pages()\nfor page in pages:\n print(f\"Page {page.id}: {page.url}\")\n\n### get_page(page_id)\nGets a specific page by ID.\n\nCode (python):\n# Get a specific page\npage = await device.get_page(123)\nprint(f\"Current URL: {page.url}\")\n\n### on_event(callback)\nSubscribes to all events from the device. Returns an unsubscribe function.\n\nCode (python):\n# Subscribe to all device events\ndef handle_event(event):\n print(\"Device event:\", event)\n\nunsubscribe = device.on_event(handle_event)\n\n# Later: stop listening to events\nunsubscribe()\n\n### on(event_name, callback)\nSubscribes to a specific event from the device. Returns the device instance for chaining.\n\nCode (python):\n# Subscribe to specific events\ndef handle_navigation(event):\n print(\"Navigation occurred:\", event)\n\ndef handle_console(event):\n print(\"Console message:\", event)\n\ndevice.on(\"navigation\", handle_navigation)\\\n .on(\"console\", handle_console)\n\n### close()\nCloses the device and cleans up resources. This will close all pages and remove event listeners.\n\nCode (python):\n# Close the device and cleanup\nawait device.close()\n\n## Example Usage\n\nHere's a complete example showing how to use the Device class:\n\nCode (python):\nfrom monitoro_herd import HerdClient\n\nasync def main():\n client = HerdClient(\n token=\"your-auth-token\"\n )\n \n await client.initialize()\n \n # Get a device\n device = await client.get_device(\"my-browser\")\n \n # Create a new page and navigate\n page = await device.new_page()\n await page.goto(\"https://example.com\")\n \n # Listen for navigation events\n def handle_navigation(event):\n print(\"Page navigated:\", event[\"url\"])\n \n device.on(\"navigation\", handle_navigation)\n \n # List all pages\n pages = await device.list_pages()\n print(f\"Device has {len(pages)} pages open\")\n \n # Cleanup when done\n await device.close()\n await client.close()\n\n# Run the async function\nimport asyncio\nasyncio.run(main())\n\n================================================================================\n\nDocument: Reference Herd Client\nURL: https://herd.garden/docs/reference-herd-client\n\n# HerdClient\n\nThe HerdClient is the main entry point for interacting with the Herd platform. It provides methods for managing devices, pages, and executing browser automation commands.\n\n## javascript\n\n## Installation\n\nCode (bash):\nnpm install @monitoro/herd\n# or\nyarn add @monitoro/herd\n\n## Usage\n\nCode (javascript):\nimport { HerdClient } from '@monitoro/herd';\n\n// Create a client instance\nconst client = new HerdClient({\n token: 'your-auth-token' // Get your token at herd.garden\n});\n\n// Initialize the client\nawait client.initialize();\n\n## Methods\n\n### initialize()\nInitializes the client by establishing connections to the Herd platform. Must be called before using other methods.\n\nCode (javascript):\nawait client.initialize();\n\n### listDevices()\nReturns a list of all available devices.\n\nCode (javascript):\nconst devices = await client.listDevices();\nconsole.log('Available devices:', devices);\n\n### getDevice(deviceId)\nGets a specific device by ID.\n\nCode (javascript):\nconst device = await client.getDevice('device-123');\n\n### registerDevice(options)\nRegisters a new device with the platform.\n\nCode (javascript):\nconst device = await client.registerDevice({\n deviceId: 'my-device',\n type: 'browser',\n name: 'My Test Browser'\n});\n\n### sendCommand(deviceId, command, params)\nSends a command to a specific device. This is a low-level method that allows you to send arbitrary commands to a device and is not recommended for most use cases.\n\nCode (javascript):\nconst result = await client.sendCommand('device-123', 'Page.click', {\n selector: '#submit-button'\n});\n\n### subscribeToDeviceEvents(deviceId, callback)\nSubscribes to all events from a device.\n\nCode (javascript):\nconst unsubscribe = client.subscribeToDeviceEvents('device-123', (event) =\u003e {\n console.log('Device event:', event);\n});\n\n// Later: unsubscribe to stop receiving events\nunsubscribe();\n\n### subscribeToDeviceEvent(deviceId, eventName, callback) \nSubscribes to a specific event from a device.\n\nCode (javascript):\nconst unsubscribe = client.subscribeToDeviceEvent('device-123', 'navigation', (event) =\u003e {\n console.log('Navigation event:', event);\n});\n\n### close()\nCloses the client and cleans up resources.\n\nCode (javascript):\nawait client.close();\n\n## python\n\n## Installation\n\nCode (bash):\npip install monitoro-herd\n\n## Usage\n\nCode (python):\nfrom monitoro_herd import HerdClient\n\n# Create a client instance\nclient = HerdClient(\n token='your-auth-token' # Get your token at herd.garden\n)\n\n# Initialize the client\nawait client.initialize()\n\n## Methods\n\n### initialize()\nInitializes the client by establishing connections to the Herd platform. Must be called before using other methods.\n\nCode (python):\nawait client.initialize()\n\n### list_devices()\nReturns a list of all available devices.\n\nCode (python):\ndevices = await client.list_devices()\nprint('Available devices:', devices)\n\n### get_device(device_id)\nGets a specific device by ID.\n\nCode (python):\ndevice = await client.get_device('device-123')\n\n### register_device(device_id, device_type, name)\nRegisters a new device with the platform.\n\nCode (python):\ndevice = await client.register_device(\n device_id='my-device',\n device_type='browser',\n name='My Test Browser'\n)\n\n### send_command(device_id, command, payload)\nSends a command to a specific device.\n\nCode (python):\nresult = await client.send_command(\n 'device-123',\n 'Page.click',\n {'selector': '#submit-button'}\n)\n\n### subscribe_to_device_events(device_id, callback)\nSubscribes to all events from a device.\n\nCode (python):\ndef handle_event(event):\n print('Device event:', event)\n\nunsubscribe = client.subscribe_to_device_events('device-123', handle_event)\n\n# Later: unsubscribe to stop receiving events\nunsubscribe()\n\n### subscribe_to_device_event(device_id, event_name, callback)\nSubscribes to a specific event from a device.\n\nCode (python):\ndef handle_navigation(event):\n print('Navigation event:', event)\n\nunsubscribe = client.subscribe_to_device_event('device-123', 'navigation', handle_navigation)\n\n### close()\nCloses the client and cleans up resources.\n\nCode (python):\nawait client.close()\n\n================================================================================\n\nDocument: Reference Mcp Server\nURL: https://herd.garden/docs/reference-mcp-server\n\n# MCP Server\n\nHerd's MCP Server allows you to securely expose web applications to Large Language Models (LLMs) through local browser automation. Using the Model Context Protocol (MCP), you can create a secure bridge between AI models and your favorite websites without sharing credentials or running browsers in the cloud.\n\n## Key Benefits\n\n- **Secure Access**: Your browser runs locally, keeping your credentials and cookies secure\n- **Privacy First**: No need to share sensitive data or tokens with third-party services\n- **Native Experience**: Interact with web apps through your actual browser, maintaining all your preferences and login state\n- **Universal Compatibility**: Works with any web application without needing API access\n- **Custom Tools**: Create tailored tools that encapsulate complex web interactions\n\n## javascript\n\n## Installation\n\nCode (bash):\nnpm install @monitoro/herd\n\n## Basic Setup\n\nHere's how to create an MCP server that exposes web application functionality:\n\nCode (javascript):\nimport { HerdMcpServer } from '@monitoro/herd';\n\nconst server = new HerdMcpServer({\n info: {\n name: \"gmail-assistant\",\n version: \"1.0.0\",\n description: \"Gmail automation tools for LLMs\"\n },\n transport: {\n type: \"sse\",\n port: 3000\n },\n herd: {\n token: \"your-herd-token\" // Get from herd.garden\n }\n});\n\n// Start the server\nawait server.start();\n\n## Creating Web App Tools\n\nTools encapsulate web application functionality for LLMs. Here are some examples:\n\nCode (javascript):\n// Gmail: Compose new email\nserver.tool({\n name: \"composeEmail\",\n description: \"Compose and send a new email\",\n schema: {\n to: z.string().email(),\n subject: z.string(),\n body: z.string()\n }\n}, async ({ to, subject, body }, devices) =\u003e {\n const device = devices[0];\n const page = await device.newPage();\n \n // Navigate to Gmail compose\n await page.goto('https://mail.google.com/mail/u/0/#compose');\n \n // Fill out the email form\n await page.type('input[aria-label=\"To\"]', to);\n await page.type('input[aria-label=\"Subject\"]', subject);\n await page.type('div[aria-label=\"Message Body\"]', body);\n \n // Send the email\n await page.click('div[aria-label=\"Send\"]');\n \n return { success: true };\n});\n\n// Twitter: Post a tweet\nserver.tool({\n name: \"postTweet\",\n description: \"Post a new tweet\",\n schema: {\n content: z.string().max(280)\n }\n}, async ({ content }, devices) =\u003e {\n const device = devices[0];\n const page = await device.newPage();\n \n await page.goto('https://twitter.com/compose/tweet');\n await page.type('div[aria-label=\"Tweet text\"]', content);\n await page.click('div[data-testid=\"tweetButton\"]');\n \n return { success: true };\n});\n\n## Creating Web App Resources\n\nResources provide structured data from web applications:\n\nCode (javascript):\n// Gmail: Unread emails\nserver.resource({\n name: \"unreadEmails\",\n uriOrTemplate: \"gmail/unread\",\n}, async (devices) =\u003e {\n const device = devices[0];\n const page = await device.newPage();\n \n await page.goto('https://mail.google.com/mail/u/0/#inbox');\n \n // Extract unread email information\n const emails = await page.evaluate(() =\u003e {\n return Array.from(document.querySelectorAll('tr.unread'))\n .map(row =\u003e ({\n sender: row.querySelector('.sender').textContent,\n subject: row.querySelector('.subject').textContent,\n date: row.querySelector('.date').textContent\n }));\n });\n \n return { emails };\n});\n\n// LinkedIn: Profile Information\nserver.resource({\n name: \"linkedinProfile\",\n uriOrTemplate: \"linkedin/profile/{username}\",\n}, async ({ username }, devices) =\u003e {\n const device = devices[0];\n const page = await device.newPage();\n \n await page.goto(`https://www.linkedin.com/in/${username}`);\n \n // Extract profile information\n return await page.extract({\n name: 'h1',\n headline: '.headline',\n about: '.about-section p',\n experience: '.experience-section li'\n });\n});\n\n## Complete Example: Twitter Assistant\n\nHere's a complete example showing how to create an MCP server that provides Twitter automation capabilities to LLMs:\n\nCode (javascript):\nimport { HerdMcpServer } from '@monitoro/herd';\nimport { z } from 'zod';\n\nconst server = new HerdMcpServer({\n info: {\n name: \"twitter-assistant\",\n version: \"1.0.0\",\n description: \"Twitter automation for LLMs\"\n },\n transport: {\n type: \"sse\",\n port: 3000\n },\n herd: {\n token: process.env.HERD_TOKEN\n }\n});\n\n// Post a tweet\nserver.tool({\n name: \"postTweet\",\n description: \"Post a new tweet\",\n schema: {\n content: z.string().max(280)\n }\n}, async ({ content }, devices) =\u003e {\n const device = devices[0];\n const page = await device.newPage();\n await page.goto('https://twitter.com/compose/tweet');\n await page.type('div[aria-label=\"Tweet text\"]', content);\n await page.click('div[data-testid=\"tweetButton\"]');\n return { success: true };\n});\n\n// Get timeline\nserver.resource({\n name: \"timeline\",\n uriOrTemplate: \"twitter/timeline\",\n}, async (devices) =\u003e {\n const device = devices[0];\n const page = await device.newPage();\n await page.goto('https://twitter.com/home');\n \n return await page.extract({\n tweets: {\n _$r: 'article[data-testid=\"tweet\"]',\n author: '[data-testid=\"User-Name\"]',\n content: '[data-testid=\"tweetText\"]',\n stats: {\n likes: '[data-testid=\"like\"]',\n retweets: '[data-testid=\"retweet\"]'\n }\n }\n });\n});\n\n// Like a tweet\nserver.tool({\n name: \"likeTweet\",\n description: \"Like a tweet by its URL\",\n schema: {\n tweetUrl: z.string().url()\n }\n}, async ({ tweetUrl }, devices) =\u003e {\n const device = devices[0];\n const page = await device.newPage();\n await page.goto(tweetUrl);\n await page.click('div[data-testid=\"like\"]');\n return { success: true };\n});\n\n// Start the server\nserver.start().then(() =\u003e {\n console.log('Twitter Assistant ready!');\n}).catch(console.error);\n\nThis setup allows LLMs to:\n1. Post tweets\n2. Read the timeline\n3. Like tweets\n4. All through your local browser, maintaining your security and privacy\n\n## python\n\nThe MCP Server implementation is currently only available in JavaScript. Python support is coming soon!\n\nIn the meantime, you can:\n1. Use the JavaScript implementation to create your MCP server\n2. Connect to it from Python using standard MCP client libraries\n3. Use the Python Herd SDK for direct browser automation without MCP\n\nExample of direct web automation with Python:\n\nCode (python):\nfrom monitoro_herd import HerdClient\n\nasync def main():\n client = HerdClient(token=\"your-token\")\n await client.initialize()\n \n device = await client.get_device(\"my-browser\")\n page = await device.new_page()\n \n # Navigate to Twitter\n await page.goto(\"https://twitter.com\")\n \n # Extract timeline content\n content = await page.extract({\n \"tweets\": {\n \"_$r\": \"article[data-testid='tweet']\",\n \"author\": \"[data-testid='User-Name']\",\n \"content\": \"[data-testid='tweetText']\"\n }\n })\n print(\"Timeline:\", content)\n\n# Run the async function\nimport asyncio\nasyncio.run(main())\n\nStay tuned for native Python MCP support!\n\nRead more about Model Context Protocol in the MCP official documentation.\n\n================================================================================\n\nDocument: Reference Node\nURL: https://herd.garden/docs/reference-node\n\n# Node\n\nThe Node class provides a DOM-like API for interacting with elements on a page. It allows you to inspect and manipulate elements using familiar DOM methods and properties.\n\n## javascript\n\nYou can obtain Node instances through Page query methods:\n- `page.querySelector(selector)` - Find first matching element\n- `page.querySelectorAll(selector)` - Find all matching elements\n- `node.querySelector(selector)` - Find first matching element within this node\n- `node.querySelectorAll(selector)` - Find all matching elements within this node\n\n## Properties\n\n### nodeType\nThe type of node (1 for Element, 3 for Text).\n\n### nodeName\nThe name of the node (tag name for elements, '#text' for text nodes).\n\n### nodeValue\nThe text content for text nodes, null for elements.\n\n### tagName\nThe tag name of the element (empty string for non-elements).\n\n### textContent\nThe text content of the node and its descendants.\n\n### innerHTML\nThe HTML content inside the element.\n\n### childNodes\nArray of child nodes.\n\n### firstChild\nThe first child node, or null if none exists.\n\n### lastChild\nThe last child node, or null if none exists.\n\n## Methods\n\n### getAttribute(name)\nGets the value of an attribute.\n\nCode (javascript):\nconst href = element.getAttribute('href');\n\n### hasAttribute(name)\nChecks if an attribute exists.\n\nCode (javascript):\nif (element.hasAttribute('disabled')) {\n console.log('Element is disabled');\n}\n\n### click([options])\nClicks the element.\n\nCode (javascript):\n// Simple click\nawait element.click();\n\n// Click with navigation wait\nawait element.click({ waitForNavigation: 'networkidle2' });\n\n### type(text[, options])\nTypes text into the element.\n\nCode (javascript):\nawait element.type('Hello world');\n\n### focus([options])\nFocuses the element.\n\nCode (javascript):\nawait element.focus();\n\n### blur([options])\nRemoves focus from the element.\n\nCode (javascript):\nawait element.blur();\n\n### hover([options])\nHovers over the element.\n\nCode (javascript):\nawait element.hover();\n\n### scrollIntoView([options])\nScrolls the element into view.\n\nCode (javascript):\nawait element.scrollIntoView();\n\n### setValue(value[, options])\nSets the value of a form element.\n\nCode (javascript):\n// Set input value\nawait element.setValue('test@example.com');\n\n// Set checkbox\nawait element.setValue(true);\n\n### dispatchEvent(eventName[, detail, options])\nDispatches an event on the element.\n\nCode (javascript):\nawait element.dispatchEvent('click');\n\n### dragTo(target[, options])\nDrags this element to another element or selector.\n\nCode (javascript):\n// Drag to another element\nconst target = await page.querySelector('.dropzone');\nawait element.dragTo(target);\n\n// Drag to selector\nawait element.dragTo('.dropzone');\n\n### querySelector(selector)\nFinds the first matching element within this node.\n\nCode (javascript):\nconst child = await element.querySelector('.child');\n\n### querySelectorAll(selector)\nFinds all matching elements within this node.\n\nCode (javascript):\nconst children = await element.querySelectorAll('.child');\n\n### getBoundingClientRect()\nGets the element's position and size.\n\nCode (javascript):\nconst rect = element.getBoundingClientRect();\nconsole.log(rect.x, rect.y, rect.width, rect.height);\n\n## Example Usage\n\nHere's a complete example showing how to use the Node class:\n\nCode (javascript):\n// Get a form element\nconst form = await page.querySelector('form');\n\n// Fill out form fields\nconst emailInput = await form.querySelector('input[type=\"email\"]');\nawait emailInput.type('test@example.com');\n\n// Check a checkbox\nconst checkbox = await form.querySelector('.terms-checkbox');\nawait checkbox.setValue(true);\n\n// Get form dimensions\nconst rect = form.getBoundingClientRect();\nconsole.log('Form size:', rect.width, rect.height);\n\n// Submit the form\nconst submitButton = await form.querySelector('button[type=\"submit\"]');\nawait submitButton.click({ waitForNavigation: 'networkidle2' });\n\n## python\n\nThe Node API is currently only available in JavaScript. In Python, you should use the Page methods directly with CSS selectors to interact with elements:\n\nCode (python):\n# Instead of:\n# element = await page.querySelector('.button')\n# await element.click()\n\n# Use:\nawait page.click('.button')\n\n# Instead of:\n# element = await page.querySelector('input')\n# await element.type('Hello')\n\n# Use:\nawait page.type('input', 'Hello')\n\nThis provides the same functionality but with a slightly different API. The Node API will be available in Python in a future release.\n\n================================================================================\n\nDocument: Reference Page\nURL: https://herd.garden/docs/reference-page\n\n# Page\n\nThe Page class represents a browser tab or page in the Herd platform. It provides methods for navigating, interacting with elements, handling events, and automating browser actions.\n\n## javascript\n\nYou can obtain a Page instance using any of these Device methods:\n- `device.newPage()` - Create a new page\n- `device.listPages()` - Get all pages\n- `device.getPage(pageId)` - Get a specific page by ID\n\n## Properties\n\n### id\nThe unique identifier for the page (tab) in the browser. This is a number that uniquely identifies the tab.\n\n### url\nThe current URL of the page. Returns an empty string if the page hasn't loaded any URL yet.\n\n### title\nThe current title of the page. Returns an empty string if the page hasn't loaded or has no title.\n\n### active\nWhether this page is currently the active tab in the browser window.\n\n## Methods\n\n### goto(url[, options])\nNavigates the page to the specified URL.\n\nCode (javascript):\n// Navigate to a URL and wait for network to be idle\nawait page.goto('https://example.com');\n\n// Navigate with custom options\nawait page.goto('https://example.com', {\n waitForNavigation: 'load' // Wait for load event instead of network idle\n});\n\nOptions:\n- `waitForNavigation`: When to consider navigation complete\n - `'load'`: Wait for load event\n - `'domcontentloaded'`: Wait for DOMContentLoaded event\n - `'networkidle0'`: Wait for network to be idle (0 connections for 500ms)\n - `'networkidle2'`: Wait for network to be idle (≤ 2 connections for 500ms)\n\n### querySelector(selector)\nFinds the first element matching the CSS selector. Returns a Node object that can be used for further interactions.\n\nCode (javascript):\nconst element = await page.querySelector('.submit-button');\nif (element) {\n console.log('Element found:', element.textContent);\n}\n\n### querySelectorAll(selector)\nFinds all elements matching the CSS selector. Returns an array of Node objects.\n\nCode (javascript):\nconst elements = await page.querySelectorAll('li.item');\nfor (const element of elements) {\n console.log('Item text:', element.textContent);\n}\n\n### dom()\nReturns the DOM of the page as a JSDOM object for ultimate flexibility. This is useful for extracting data by walking the DOM and finding elements manually.\nNote: This is a read-only view of the DOM, so you cannot trigger events or modify the DOM this way.\n\nCode (javascript):\nconst dom = await page.dom();\nconsole.log(dom);\n\n### waitForElement(selector[, options])\nWaits for an element matching the selector to appear or disappear on the page.\n\nCode (javascript):\n// Wait for an element to be visible\nawait page.waitForElement('#loading-indicator'); // or the following which is equivalent \nawait page.waitForElement('#loading-indicator', { state: 'visible' });\n\n// Wait for an element to be hidden\nawait page.waitForElement('#loading-indicator', { state: 'hidden' });\n\n// Wait for an element to be attached to DOM\nawait page.waitForElement('.dynamic-content', { state: 'attached' });\n\n// Wait for an element to be detached from DOM\nawait page.waitForElement('.old-content', { state: 'detached' });\n\n// Wait with timeout\nawait page.waitForElement('.dynamic-content', { \n state: 'visible',\n timeout: 10000 // 10 seconds (default is 5 seconds)\n});\n\nOptions:\n- `state`: The state to wait for\n - `'visible'`: Wait for element to be visible (default)\n - `'hidden'`: Wait for element to be hidden\n - `'attached'`: Wait for element to be attached to DOM\n - `'detached'`: Wait for element to be detached from DOM\n- `timeout`: Maximum time to wait in milliseconds (default: 5000)\n\n### waitForSelector(selector[, options])\nAlias for waitForElement. Waits for an element matching the selector to appear or disappear.\n\nCode (javascript):\n// Wait for a selector to be visible\nawait page.waitForSelector('.search-results', { state: 'visible' });\n\n// Wait for a selector to be detached\nawait page.waitForSelector('.loading-spinner', { state: 'detached', timeout: 5000 });\n\n### waitForNavigation(condition)\nWaits for navigation to complete based on the specified condition.\n\nCode (javascript):\n// Wait for page load event\nawait page.waitForNavigation('load');\n\n// Wait for network to be idle\nawait page.waitForNavigation('networkidle0');\n\n// Wait for URL change\nawait page.waitForNavigation('change');\n\nConditions:\n- `'load'`: Wait for load event\n- `'domcontentloaded'`: Wait for DOMContentLoaded event\n- `'change'`: Wait for URL change\n- `'networkidle0'`: Wait for network to be idle (0 connections for 500ms)\n- `'networkidle2'`: Wait for network to be idle (≤ 2 connections for 500ms)\n\n### click(selector[, options])\nClicks an element that matches the selector.\n\nCode (javascript):\n// Simple click\nawait page.click('#submit-button');\n\n// Click with navigation wait\nawait page.click('a.link', { waitForNavigation: 'networkidle2' });\n\n### type(selector, text[, options])\nTypes text into an input element.\n\nCode (javascript):\n// Type into an input field\nawait page.type('#search-input', 'search query');\n\n// Type with navigation wait (for auto-submit forms)\nawait page.type('#search-input', 'search query', {\n waitForNavigation: 'networkidle2'\n});\n\n### focus(selector[, options])\nFocuses an element on the page.\n\nCode (javascript):\nawait page.focus('#email-input');\n\n### hover(selector[, options])\nHovers over an element.\n\nCode (javascript):\nawait page.hover('.dropdown-trigger');\n\n### press(key[, options])\nPresses a keyboard key.\n\nCode (javascript):\nawait page.press('Enter');\n\n### scroll(x, y[, options])\nScrolls the page by the specified amount.\n\nCode (javascript):\n// Scroll down 500 pixels\nawait page.scroll(0, 500);\n\n### scrollIntoView(selector[, options])\nScrolls an element into view.\n\nCode (javascript):\nawait page.scrollIntoView('#bottom-content');\n\n### back([options])\nNavigates back in the browser history.\n\nCode (javascript):\nawait page.back({ waitForNavigation: 'networkidle2' });\n\n### forward([options])\nNavigates forward in the browser history.\n\nCode (javascript):\nawait page.forward({ waitForNavigation: 'networkidle2' });\n\n### reload([options])\nReloads the current page.\n\nCode (javascript):\nawait page.reload({ waitForNavigation: 'networkidle2' });\n\n### activate()\nActivates the page (makes it the active tab).\n\nCode (javascript):\nawait page.activate();\n\n### close()\nCloses the page (tab).\n\nCode (javascript):\nawait page.close();\n\n## Example Usage\n\nHere's a complete example showing how to use the Page class:\n\nCode (javascript):\n// Create a new page\nconst page = await device.newPage();\n\n// Navigate to a URL\nawait page.goto('https://example.com');\n\n// Fill out a form\nawait page.type('#username', 'myuser');\nawait page.type('#password', 'mypass');\nawait page.click('#submit-button', { waitForNavigation: 'networkidle2' });\n\n// Extract some data\nconst title = await page.evaluate(() =\u003e document.title);\nconsole.log('Page title:', title);\n\n// Close the page when done\nawait page.close();\n\n## python\n\nYou can obtain a Page instance using any of these Device methods:\n- `device.new_page()` - Create a new page\n- `device.list_pages()` - Get all pages\n- `device.get_page(page_id)` - Get a specific page by ID\n\n## Properties\n\n### id\nThe unique identifier for the page (tab) in the browser. This is a number that uniquely identifies the tab.\n\n### url\nThe current URL of the page. Returns an empty string if the page hasn't loaded any URL yet.\n\n### title\nThe current title of the page. Returns an empty string if the page hasn't loaded or has no title.\n\n### active\nWhether this page is currently the active tab in the browser window.\n\n## Methods\n\n### goto(url[, options])\nNavigates the page to the specified URL.\n\nCode (python):\n# Navigate to a URL and wait for network to be idle\nawait page.goto(\"https://example.com\")\n\n# Navigate with custom options\nawait page.goto(\"https://example.com\", {\n \"waitForNavigation\": \"load\" # Wait for load event instead of network idle\n})\n\nOptions:\n- `waitForNavigation`: When to consider navigation complete\n - `'load'`: Wait for load event\n - `'domcontentloaded'`: Wait for DOMContentLoaded event\n - `'networkidle0'`: Wait for network to be idle (0 connections for 500ms)\n - `'networkidle2'`: Wait for network to be idle (≤ 2 connections for 500ms)\n\n### querySelector(selector) / Q(selector)\nFinds the first element matching the CSS selector. Returns a dictionary representing the element.\n\nCode (python):\n# Using querySelector (alias for Q)\nelement = await page.querySelector(\".submit-button\")\nif element:\n print(\"Element found:\", element)\n\n# Using Q directly\nelement = await page.Q(\".submit-button\")\n\n### querySelectorAll(selector) / QQ(selector)\nFinds all elements matching the CSS selector. Returns a list of dictionaries representing the elements.\n\nCode (python):\n# Using querySelectorAll (alias for QQ)\nelements = await page.querySelectorAll(\"li.item\")\nfor element in elements:\n print(\"Item:\", element)\n\n# Using QQ directly\nelements = await page.QQ(\"li.item\")\n\n### click(selector[, options])\nClicks an element that matches the selector.\n\nCode (python):\n# Simple click\nawait page.click(\"#submit-button\")\n\n# Click with navigation wait\nawait page.click(\"a.link\", {\"waitForNavigation\": \"networkidle2\"})\n\n### type(selector, text[, options])\nTypes text into an input element.\n\nCode (python):\n# Type into an input field\nawait page.type(\"#search-input\", \"search query\")\n\n# Type with navigation wait (for auto-submit forms)\nawait page.type(\"#search-input\", \"search query\", {\n \"waitForNavigation\": \"networkidle2\"\n})\n\n### focus(selector[, options])\nFocuses an element on the page.\n\nCode (python):\nawait page.focus(\"#email-input\")\n\n### hover(selector[, options])\nHovers over an element.\n\nCode (python):\nawait page.hover(\".dropdown-trigger\")\n\n### scroll(x, y[, options])\nScrolls the page by the specified amount.\n\nCode (python):\n# Scroll down 500 pixels\nawait page.scroll(0, 500)\n\n### scroll_into_view(selector[, options])\nScrolls an element into view.\n\nCode (python):\nawait page.scroll_into_view(\"#bottom-content\")\n\n### set_value(selector, value[, options])\nSets the value of a form element directly.\n\nCode (python):\n# Set input value\nawait page.set_value(\"#quantity\", \"5\")\n\n# Set checkbox\nawait page.set_value(\"#agree\", True)\n\n### dispatch_event(selector, event_name[, detail, options])\nDispatches a DOM event on an element.\n\nCode (python):\nawait page.dispatch_event(\"#my-button\", \"click\")\n\n### close()\nCloses the page (tab).\n\nCode (python):\nawait page.close()\n\n## Example Usage\n\nHere's a complete example showing how to use the Page class:\n\nCode (python):\n# Create a new page\npage = await device.new_page()\n\n# Navigate to a URL\nawait page.goto(\"https://example.com\")\n\n# Fill out a form\nawait page.type(\"#username\", \"myuser\")\nawait page.type(\"#password\", \"mypass\")\nawait page.click(\"#submit-button\", {\"waitForNavigation\": \"networkidle2\"})\n\n# Extract some data using the extract method\ndata = await page.extract({\n \"title\": \"h1\",\n \"description\": \".description\"\n})\nprint(\"Extracted data:\", data)\n\n# Close the page when done\nawait page.close()\n\n================================================================================\n\nDocument: Security Privacy\nURL: https://herd.garden/docs/security-privacy\n\n# Security \u0026 Privacy in Herd\n\nAt Herd, we take security and privacy seriously. This guide outlines the security features built into the platform and the privacy measures we take to protect your data.\n\n## Security Architecture\n\nHerd is built with security at its core:\n\n### End-to-End Encryption\n\nAll communication between your browsers and the Herd platform is encrypted end-to-end:\n\n- **Transport Layer Security (TLS)**: All API requests and responses use TLS 1.3.\n- **WebRTC Encryption**: Remote control sessions use WebRTC with DTLS-SRTP encryption.\n- **Secure WebSockets**: Real-time communication uses encrypted WebSocket connections.\n- **NATS**: All communication between your browser and the code you run is encrypted end-to-end using a NATS-based PKI infrastructure and we do not intercept or inspect the content of the communication.\n\n### Authentication \u0026 Authorization\n\nMultiple layers of security protect access to your account and devices:\n\n- **Multi-factor Authentication**: Optional 2FA for account access\n- **Device Registration Codes**: One-time codes for connecting browsers\n- **Session Management**: Automatic timeouts and the ability to revoke sessions\n- **Role-Based Access Control**: Granular permissions for team members\n\n### Infrastructure Security\n\nOur platform infrastructure implements industry best practices:\n\n- **Regular Security Audits**: Third-party security audits of our systems\n- **Vulnerability Scanning**: Continuous monitoring for vulnerabilities\n- **Secure Development**: Strict coding practices and security reviews\n- **Cloud Security**: Leveraging secure cloud infrastructure with isolation between customers\n\n## Privacy Features\n\nHerd includes several features to protect your privacy:\n\n### Data Minimization\n\nWe only collect the data necessary for the platform to function:\n\n- **Selective Access**: You control which browsers are connected and which data is shared\n- **No Unnecessary Telemetry**: Limited data collection focused on service performance\n- **Automatic Data Expiry**: Logs and temporary data are automatically purged after set periods\n\n### User Control\n\nYou maintain control over your data and connections:\n\n- **Connection Visibility**: Clear indicators when a browser is being monitored or controlled\n- **Permission Prompts**: Optional prompts before remote control is initiated\n- **Incognito Mode Handling**: Special handling of private browsing sessions\n- **Activity Logs**: Transparent logs of all remote control activities\n\n### Compliance Features\n\nFor organizations with specific compliance requirements:\n\n- **Data Residency Options**: Select where your data is stored (Enterprise plan)\n- **Compliance Reporting**: Generate reports for audit purposes\n- **Custom Retention Policies**: Set data retention periods to match your policies\n- **Privacy Mode**: Additional restrictions for sensitive environments\n\n## Security Best Practices\n\nTo maximize security when using Herd:\n\n### Account Security\n\n- Use strong, unique passwords for your Herd account\n- Enable two-factor authentication\n- Regularly review active sessions and revoke any suspicious ones\n- Limit account access to necessary team members only\n\n### Device Security\n\n- Register devices with descriptive names for easy identification\n- Regularly review connected devices and remove unused ones\n- Use device tagging to organize and manage access\n- Consider network-level restrictions for sensitive devices\n\n### Remote Control Security\n\n- Always end remote control sessions when not in use\n- Use view-only mode when full control isn't necessary\n- Be cautious about what information is visible during remote sessions\n- Consider scheduling remote sessions in advance when possible\n\n## Privacy Policy\n\nHerd's formal privacy policy can be found at [monitoro.co/privacy](https://monitoro.co/privacy). Key points include:\n\n- We do not sell your data to third parties\n- We only process your data to provide the Herd service\n- You retain ownership of all content viewed or managed through Herd\n- We implement strong security measures to protect your data\n- We are transparent about any data breaches or security incidents\n\n## Security Updates\n\nWe continuously improve our security and privacy features:\n\n- Security updates are automatically applied to the platform\n- Extension updates are released regularly with security improvements\n- Follow our blog for detailed information on security enhancements\n\n## Reporting Security Issues\n\nIf you discover a security vulnerability, please report it responsibly:\n\n1. Email security@herd.garden with details of the vulnerability\n2. Include steps to reproduce if possible\n3. Allow time for us to address the issue before public disclosure\n\nNote: We unfortunately do not offer a bug bounty program as we are a small team and our resources are limited. We do appreciate your responsible disclosure and help keeping the internet a safer place.\n\n================================================================================\n\nDocument: Trails Automations\nURL: https://herd.garden/docs/trails-automations\n\n# Trails\n\nTrails in the Herd platform are packaged automations that perform specific tasks such as extracting data or submitting forms. They make it easy to reuse automation logic across different projects and achieve high reliability through a solid and accessible testing process.\n\n## javascript\n\nTrails are fully supported in the JavaScript SDK.\n\n## Creating a Trail\n\nA trail defines the following components:\n\n- **urls.ts**: Exports an array of URL definitions\n- **selectors.ts**: Exports an array of selector configurations\n- **actions.ts**: Exports action classes that implement the `TrailAction` interface\n\nA trail at the minimum has the following structure:\n\nCode:\ngoogle-search/\n urls.ts\n selectors.ts\n actions.ts\n package.json\n\nThe `package.json` file defines the trail and its dependencies:\n\nCode (json):\n{\n \"name\": \"google-search\",\n \"description\": \"Search google for webpages.\",\n \"version\": \"1.0.0\",\n \"dependencies\": {\n \"@monitoro/herd\": \"latest\"\n }\n}\n\nYou should never run the .ts files manually. Instead, use the Herd CLI to run, test, debug, and publish trails. Make sure to use version control to manage your trails, as publishing submits a built version to the Herd registry, not the source code.\n\n## Trail Implementation Guide\n\n### Step 1: Set up the trail structure\n\nCreate a new directory for your trail with the following files:\n\nCode:\nmy-trail/\n urls.ts\n selectors.ts\n actions.ts\n package.json\n\nAdd the basic package.json configuration:\n\nCode (json):\n{\n \"name\": \"my-trail\",\n \"version\": \"1.0.0\",\n \"dependencies\": {\n \"@monitoro/herd\": \"latest\"\n }\n}\n\n### Step 2: Define URLs\n\nIn `urls.ts`, export an array of URL definitions that your trail will interact with:\n\nCode (typescript):\nexport default [{\n \"id\": \"my-url\",\n \"template\": \"https://example.com/path?param1={param1}\u0026param2={param2}\",\n \"description\": \"Description of what this URL represents\",\n \"examples\": [\n { \"param1\": \"value1\", \"param2\": \"value2\" }\n ],\n \"params\": {\n \"param1\": {\n \"type\": \"string\",\n \"required\": true,\n \"description\": \"Description of param1\"\n },\n \"param2\": {\n \"type\": \"string\",\n \"required\": false,\n \"default\": \"defaultValue\",\n \"description\": \"Description of param2\"\n }\n }\n}];\n\n### Step 3: Define Selectors\n\nIn `selectors.ts`, export an array of selector configurations that define how to extract data from web pages:\n\nCode (typescript):\nexport default [{\n \"id\": \"my-selector\",\n \"value\": {\n \"dataKey\": {\n \"_$r\": \"#main-container\",\n \"title\": { \"_$\": \".title\" },\n \"description\": { \"_$\": \".description\" },\n \"link\": { \"_$\": \"a.link\", \"attribute\": \"href\" }\n }\n },\n \"description\": \"Selector for extracting specific data\",\n \"examples\": [\n {\n \"urlId\": \"my-url\",\n \"urlParams\": { \"param1\": \"value1\", \"param2\": \"value2\" }\n }\n ]\n}];\n\n### Step 4: Implement Actions\n\nIn `actions.ts`, define one or more action classes that implement the `TrailAction` interface:\n\nCode (typescript):\nimport type { Device, TrailAction, TrailActionManifest, TrailRunResources } from \"@monitoro/herd\";\n\nexport class MyTrailAction implements TrailAction {\n manifest: TrailActionManifest = {\n name: \"my-action\",\n description: \"Description of what this action does\",\n params: {\n param1: {\n type: \"string\",\n description: \"Description of param1\"\n },\n param2: {\n type: \"number\",\n description: \"Description of param2\",\n default: 10\n }\n },\n result: {\n type: \"array\",\n description: \"Description of the result\",\n items: {\n type: \"object\",\n properties: {\n property1: { type: \"string\" },\n property2: { type: \"number\" }\n }\n }\n },\n examples: [\n {\n \"param1\": \"value1\",\n \"param2\": 10\n }\n ]\n }\n\n async test(device: Device, params: Record, resources: TrailRunResources) {\n try {\n const result = await this.run(device, params, resources);\n // Validate result\n if (!result || result.length === 0) {\n return { status: \"error\", message: \"No results found\", result: [] };\n }\n return { status: \"success\", result };\n } catch (e) {\n return { status: \"error\", message: `Error: ${e}`, result: null };\n }\n }\n\n async run(device: Device, params: Record, resources: TrailRunResources) {\n const { param1, param2 } = params;\n const page = await device.newPage();\n \n try {\n // Navigate to URL using the URL template from urls.ts\n await page.goto(resources.url('my-url', { param1, param2 }));\n \n // Extract data using selectors from selectors.ts\n const extracted = await page.extract(resources.selector('my-selector'));\n \n // Process extracted data\n const results = (extracted as any)?.dataKey || [];\n \n // Return processed results\n return results;\n } finally {\n await page.close();\n }\n }\n}\n\n### Step 5: Testing and debugging\n\nUse the Herd CLI to test your trail locally (make sure to define test cases as `examples` in the trail action manifest):\n\nCode (bash):\nherd trail test --action my-trail\n\nYou can also watch for changes in the trail and re-run tests:\n\nCode (bash):\nherd trail test --action my-trail --watch # or herd trail test -a my-trail -w\n\nAnd you can also test selectors only:\n\nCode (bash):\nherd trail test --selector my-selector\n\nAnd watch for changes in the selectors:\n\nCode (bash):\nherd trail test --selector my-selector --watch # or herd trail test -s my-selector -w\n\n### Step 6: Publish your trail\n\nPublishing is coming soon!\n\n### Best Practices\n\n1. **Error handling**: Implement robust error handling in your actions to handle network issues, missing elements, etc.\n2. **Performance**: Minimize page loads and extract as much data as possible from each page.\n3. **Maintainability**: Use descriptive names and add comments to make your trail easier to maintain.\n4. **Testing**: Test your trail with different parameters to ensure it works in various scenarios.\n5. **Versioning**: Increment your trail's version in package.json when making changes.\n\n## python\n\nTrails support for Python SDK is coming soon. Stay tuned for updates!\n\nIn the meantime, you can use the JavaScript SDK to create trails and then use the Herd CLI to publish them.\n\n================================================================================\n\nDocument: Troubleshooting\nURL: https://herd.garden/docs/troubleshooting\n\n# Troubleshooting Herd\n\nThis guide provides solutions for common issues you might encounter when using the Herd platform. If you can't find a solution here, please contact our [support team](/docs/getting-started) for further assistance.\n\n## Installation Issues\n\n### Extension Won't Install\n\n**Problem**: You can't install the Herd extension in your browser.\n\n**Solutions**:\n- Verify Developer mode is enabled in your browser's extensions page\n- Make sure you're using a supported browser (Chrome, Edge, Brave)\n- Try downloading the extension file again as it might be corrupted\n- Check if your browser has restrictions on installing third-party extensions\n- Try installing from a different browser profile\n\nIf all else fails, follow the [Getting Started guide](/docs/getting-started), or [contact](mailto:support@herd.garden) us for help with as much context about the issue as you can share.\n\n### Extension Shows as Corrupted\n\n**Problem**: Your browser shows the Herd extension as corrupted or invalid.\n\n**Solutions**:\n- Re-download the extension package\n- Clear your browser cache before installing\n- Try extracting the zip file and loading it as an unpacked extension\n- Verify that you have the latest version of the extension\n\nIf all else fails, follow the [Getting Started guide](/docs/getting-started), or [contact](mailto:support@herd.garden) us for help with as much context about the issue as you can share.\n\n## Connection Issues\n\n### Browser is disconnected or no browser registered\n\n**Problem**: Running a trail or calling any Herd command throws an exception \"Browser is disconnected or no browser registered\".\nThis means that your browser is not connected to the Herd platform \n\n**Solutions**:\n- First ensure that you have installed Herd and registered your browser following our [Getting Started guide](/docs/getting-started).\n- Check the internet connection of the computer where you installed Herd extension\n- Go to `chrome://extensions` and click the reload icon next to Herd extension\n- If that doesn't work, delete the extension and reconnect it again from scratch, following our [Getting Started guide](/docs/getting-started).\n\nIf nothing works, [contact](mailto:support@herd.garden) us for additional help with as much context about the issue as you can share.\n\n### Can't Connect to Herd Server\n\n**Problem**: The extension is installed but can't connect to the Herd server.\n\n**Solutions**:\n- Check your internet connection\n- Verify that the registration code is correct and hasn't expired\n- Make sure your firewall isn't blocking the connection\n- Try disabling any VPN or proxy services temporarily\n- Check if your organization blocks WebSocket connections\n\n### Connection Keeps Dropping\n\n**Problem**: The connection between your browser and Herd keeps disconnecting.\n\n**Solutions**:\n- Check for network stability issues\n- Make sure your computer isn't going to sleep\n- Verify that the browser is allowed to run in the background\n- Check if other extensions are conflicting with Herd\n- Try connecting using a wired network connection if possible\n\n## Remote Control Issues\n\n### Black Screen During Remote Control\n\n**Problem**: You see a black screen when trying to view a remote browser.\n\n**Solutions**:\n- Check if the remote device is in sleep mode or locked\n- Refresh the remote control session\n- Make sure the remote browser tab is active (not minimized)\n- Try restarting the remote browser\n- Verify that the remote device has granted screen sharing permissions\n\n### High Latency in Remote Control\n\n**Problem**: There's significant lag when controlling a remote browser.\n\n**Solutions**:\n- Lower the streaming quality in the remote control settings\n- Check network conditions on both ends\n- Close unnecessary tabs and applications on both devices\n- Make sure no bandwidth-heavy activities are running (like video streaming)\n- Try using a wired connection if possible\n\n### Input Not Registering\n\n**Problem**: Mouse clicks or keyboard input aren't registering on the remote browser.\n\n**Solutions**:\n- Check if you're in \"View Only\" mode and switch to \"Control\" mode\n- Try clicking on the remote view area to ensure it has focus\n- Refresh the remote control session\n- Check if the remote browser is responding to local inputs\n- Try restarting the remote control session\n\n## Account and Management Issues\n\n### Device Not Appearing in Dashboard\n\n**Problem**: A connected device isn't showing up in your Herd dashboard.\n\n**Solutions**:\n- Verify the device is properly connected\n- Refresh the dashboard page\n- Check if the device is registered under a different account\n- Make sure the extension is enabled and running\n- Try reconnecting the device using a new registration code\n\n### Can't Create New Device Registration\n\n**Problem**: You're unable to create a new device registration.\n\n**Solutions**:\n- Check if you've reached your plan's device limit\n- Verify that you have the necessary permissions in your account\n- Try using a different browser to access the dashboard\n- Clear your browser cache and cookies\n- Check if your account has any restrictions\n\n## System Requirements\n\nIf you're experiencing persistent issues, verify that your system meets these minimum requirements:\n\n- **Browser**: Chrome 70+, Edge 79+, or Brave 1.0+\n- **Operating System**: Windows 10+, macOS 10.14+, or Ubuntu 18.04+\n- **RAM**: 4GB minimum (8GB recommended)\n- **Network**: Stable internet connection with at least 1Mbps upload/download\n- **Processor**: Dual-core processor at 2GHz or higher\n\n## Contacting Support\n\nIf you've tried the solutions above and are still experiencing issues, please contact our support team:\n\n1. Email: support@herd.garden\n2. In-app: Click the \"Help\" icon in the dashboard footer\n3. Documentation: Check for updated troubleshooting guides on our website\n\nWhen contacting support, please include:\n- Your browser type and version\n- Your operating system\n- A description of the issue\n- Any error messages you've received\n- Steps you've already taken to resolve the issue\n\n================================================================================\n","robots_txt_url":"/api/v1/crawler/top-sites/herd.garden/robots.txt","llms_txt_url":"/api/v1/crawler/top-sites/herd.garden/llms.txt","llms_full_txt_url":"/api/v1/crawler/top-sites/herd.garden/llms-full.txt"}}