<!--
hoody-browser Subskill (sdk)
Auto-generated by Hoody Skills Generator
Generated: 2026-06-20T00:04:02.027Z
Model: mimo-v2.5-pro + fixer:mimo-v2.5-pro
Mode: sdk


Tokens: 7465

DO NOT EDIT MANUALLY - Changes will be overwritten on next generation
-->

# hoody-browser Subskill

## Overview

### Service Purpose
hoody-browser provides **browser automation accessible via HTTP** — a headless Chrome service controlled through a REST API. This enables programmatic web interaction without direct browser management, ideal for AI agents and automated workflows.

### When to Use
- **Web scraping**: Extract content from dynamic websites
- **Form automation**: Fill and submit web forms
- **Screenshot generation**: Capture visual evidence of web states
- **PDF creation**: Convert web pages to PDF documents
- **Session management**: Maintain browser state across operations
- **Debugging & monitoring**: Capture console logs and network traffic

### Hoody Philosophy Alignment
Browser automation as a managed service removes complexity from agent tasks. The HTTP API pattern ensures consistent access across environments while maintaining isolation between instances.

## Common Workflows

### Browser Instance Management

#### Create and Verify Browser Instance
```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: 'TOKEN' })

// Create browser with custom configuration
const browser = await client.browser.instances.start({
  id: 'scraper-001',
  proxy: {
    server: 'proxy.example.com:8080',
    username: 'proxyUser',
    password: 'proxyPass',
    bypass: 'localhost'
  },
  viewport: { width: 1920, height: 1080 },
  autoStart: false,
  sessionName: 'Web Scraping Session',
  timezone: 'America/New_York',
  locale: 'en-US',
  userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
  screenResolution: '1920x1080'
})

// Verify instance creation
const metadata = await client.browser.introspection.getMetadata({ browser_id: 'scraper-001' })
console.log('Browser ready:', metadata.browser.name, metadata.browser.version)
```

#### Restart with New Configuration
```
// Restart browser with updated settings
await client.browser.instances.restart('scraper-001', {
  proxy: {
    server: 'new-proxy.example.com:9090',
    username: 'newUser',
    password: 'newPass'
  },
  sessionName: 'Updated Session',
  timezone: 'Europe/London',
  locale: 'en-GB'
})

// Confirm restart completed
const status = await client.browser.introspection.getMetadata({ browser_id: 'scraper-001' })
console.log('Session name:', status.sessionName)
```

### Web Navigation & Content Extraction

#### Navigate and Extract Page Content
```
// Navigate to target URL
const browseResult = await client.browser.interaction.browse(
  'scraper-001',
  true,
  'https://example.com/products'
)

// Extract full HTML content
const html = await client.browser.page.getHtml('scraper-001', browseResult.tabId)

// Extract visible text only
const text = await client.browser.page.getText('scraper-001', browseResult.tabId)

console.log('HTML length:', html.length)
console.log('Text content:', text.substring(0, 200) + '...')
```

#### Execute JavaScript on Page
```
// Navigate to page
await client.browser.interaction.browse('scraper-001', true, 'https://example.com/dashboard')

// Execute JavaScript to extract data
const result = await client.browser.interaction.evalPost('scraper-001', true, {
  script: `
    const items = document.querySelectorAll('.product-item');
    return Array.from(items).map(item => ({
      name: item.querySelector('.name').textContent,
      price: item.querySelector('.price').textContent
    }));
  `
})

console.log('Extracted products:', result)
```

### Visual Capture & Document Generation

#### Capture Screenshots with Different Formats
```
// Full page PNG screenshot
const pngScreenshot = await client.browser.interaction.takeScreenshot(
  'scraper-001',
  true,
  'https://example.com/report',
  undefined,
  undefined,
  undefined,
  'png',
  undefined,
  true
)

// JPEG with quality setting
const jpegScreenshot = await client.browser.interaction.takeScreenshot(
  'scraper-001',
  true,
  'https://example.com/dashboard',
  undefined,
  undefined,
  undefined,
  'jpeg',
  80,
  false
)

console.log('PNG size:', pngScreenshot.length)
console.log('JPEG size:', jpegScreenshot.length)
```

#### Generate PDF Reports
```
// Generate PDF with custom margins
const pdf = await client.browser.page.exportPdf(
  'scraper-001',
  undefined,
  true,
  'https://example.com/report',
  'A4',
  false,
  true,
  '2cm'
)

// Generate landscape PDF for wide tables
const landscapePdf = await client.browser.page.exportPdf(
  'scraper-001',
  undefined,
  true,
  'https://example.com/spreadsheet',
  'A4',
  true,
  true,
  '1cm'
)

console.log('Portrait PDF size:', pdf.length)
console.log('Landscape PDF size:', landscapePdf.length)
```

## Advanced Operations

### Multi-Tab Automation with State Verification

#### Sequential Tab Operations
```
async function scrapeMultiplePages(browserId: string, urls: string[]) {
  const results = []
  
  for (const url of urls) {
    // Open new tab for each URL
    const tab = await client.browser.interaction.browse(browserId, true, url)
    
    // Verify navigation completed
    const tabs = await client.browser.introspection.listTabs(browserId)
    const activeTab = tabs.find(t => t.id === tab.tabId)
    
    if (activeTab?.status !== 'loaded') {
      console.warn(`Navigation incomplete for ${url}`)
      continue
    }
    
    // Extract content
    const content = await client.browser.page.getText(browserId, tab.tabId)
    results.push({ url, content: content.substring(0, 500) })
    
    // Close tab to free resources
    await client.browser.introspection.closeTab(browserId, true, tab.tabId)
  }
  
  return results
}
```

#### Error Recovery Pattern
```
async function resilientScrape(browserId: string, url: string, retries = 3) {
  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      // Check browser health before attempt
      if (attempt > 1) {
        const health = await client.browser.health.check()
        if (health.status !== 'healthy') {
          await client.browser.instances.restart(browserId)
        }
      }
      
      // Attempt navigation
      const result = await client.browser.interaction.browse(browserId, true, url)
      
      // Verify page loaded successfully
      const html = await client.browser.page.getHtml(browserId, result.tabId)
      if (!html.includes('<body>')) {
        throw new Error('Page failed to load properly')
      }
      
      return html
      
    } catch (error) {
      console.error(`Attempt ${attempt} failed:`, error.message)
      
      if (attempt === retries) {
        // Final attempt - capture screenshot for debugging
        await client.browser.interaction.takeScreenshot(
          browserId,
          true,
          url,
          undefined,
          undefined,
          undefined,
          'png',
          undefined,
          true
        )
        throw error
      }
      
      // Exponential backoff
      await new Promise(resolve => setTimeout(resolve, 1000 * attempt))
    }
  }
}
```

### Session Management & Debugging

#### Authentication Flow with Cookies
```
async function authenticatedScrape(browserId: string) {
  // Navigate to login page
  await client.browser.interaction.browse(browserId, true, 'https://example.com/login')
  
  // Fill and submit login form
  await client.browser.interaction.evalPost(browserId, true, {
    script: `
      document.querySelector('#username').value = 'user';
      document.querySelector('#password').value = 'pass';
      document.querySelector('#login-form').submit();
    `
  })
  
  // Wait for navigation to complete
  await new Promise(resolve => setTimeout(resolve, 2000))
  
  // Capture session cookies
  const cookies = await client.browser.cookies.get(browserId, true, 'https://example.com')
  
  // Verify authentication succeeded
  if (!cookies.some(c => c.name.includes('session'))) {
    throw new Error('Login failed - no session cookie found')
  }
  
  return cookies
}
```

#### Performance Monitoring
```
async function monitorPerformance(browserId: string, url: string) {
  // Clear previous logs
  await client.browser.debugging.getConsoleLogs(browserId, undefined, true, undefined, undefined, true)
  await client.browser.debugging.getNetworkLogs(browserId, undefined, true, undefined, true)
  
  // Navigate and monitor
  await client.browser.interaction.browse(browserId, true, url)
  
  // Collect performance metrics
  const consoleLogs = await client.browser.debugging.getConsoleLogs(browserId)
  const networkLogs = await client.browser.debugging.getNetworkLogs(browserId)
  
  // Analyze results
  const errors = consoleLogs.filter(log => log.type === 'error')
  const slowRequests = networkLogs.filter(req => req.duration > 1000)
  
  return {
    consoleErrors: errors.length,
    slowRequests: slowRequests.length,
    totalRequests: networkLogs.length,
    totalConsoleMessages: consoleLogs.length
  }
}
```

## Quick Reference

### Most Common Endpoints
1. **Create Instance**: `POST /api/v1/browser/start` → `client.browser.instances.start()`
2. **Navigate**: `GET /api/v1/browser/browse` → `client.browser.interaction.browse()`
3. **Extract Text**: `GET /api/v1/browser/text` → `client.browser.page.getText()`
4. **Take Screenshot**: `GET /api/v1/browser/screenshot` → `client.browser.interaction.takeScreenshot()`
5. **Execute JS**: `POST /api/v1/browser/eval` → `client.browser.interaction.evalPost()`
6. **Get HTML**: `GET /api/v1/browser/html` → `client.browser.page.getHtml()`
7. **Manage Cookies**: `GET/POST/DELETE /api/v1/browser/cookies` → `client.browser.cookies.*`

### Essential Parameters
- **browser_id**: Unique identifier for browser instance (required for all instance operations)
- **url**: Target URL for navigation (required for browse, optional for screenshot)
- **tabId**: Specific tab identifier (for multi-tab operations)
- **start**: Auto-start browser if not running (default: false in SDK)

### Typical Response Formats
- **Instance Metadata**: Browser version, session info, viewport details
- **Navigation Result**: Tab ID, load status, final URL
- **Content Extraction**: Raw HTML/text string or structured data
- **Screenshot**: Base64-encoded image data (PNG/JPEG)
- **Cookie Array**: Name, value, domain, path, expiration

### Important Notes
- **Path Convention**: Use exactly `/api/v1/browser/*` paths from endpoint inventory
- **Base URL**: Service-specific URL pattern, not api.hoody.com
- **Authentication**: Handled by HoodyClient initialization, not in endpoint calls
- **Resource Management**: Always stop or shutdown browsers when done to free resources