Skill: ktpriyatham/browser-ladder (version 1.0.0)

--- name: browser-ladder version: 1.0.0 description: Climb the browser ladder — start free, escalate only when needed. L1 (fetch) → L2 (local Playwright) → L3 (BrowserCat) → L4 (Browserless.io for CAPTCHA/bot bypass). metadata: clawdbot: emoji: "🪜" requires: bins: - node - docker env: - name: BROWSERCAT_API_KEY description: BrowserCat API key (free tier) - get at https://browsercat.com required: false - name: BROWSERLESS_TOKEN description: Browserless.io token ($10/mo) - get at https://browserless.io required: false --- # Browser Ladder 🪜 Climb from free to paid only when you need to. ## Quick Setup Run the setup script after installation: ```bash ./skills/browser-ladder/scripts/setup.sh ``` Or manually add to your `.env`: ```bash # Optional - only needed for Rungs 3-4 BROWSERCAT_API_KEY=your-key # Free: https://browsercat.com BROWSERLESS_TOKEN=your-token # Paid: https://browserless.io ``` ## The Ladder ``` ┌─────────────────────────────────────────────┐ │ 🪜 Rung 4: Browserless.io (Cloud Paid) │ │ • CAPTCHA solving, bot detection bypass │ │ • Cost: $10+/mo │ │ • Requires: BROWSERLESS_TOKEN │ ├─────────────────────────────────────────────┤ │ 🪜 Rung 3: BrowserCat (Cloud Free) │ │ • When local Docker fails │ │ • Cost: FREE (limited) │ │ • Requires: BROWSERCAT_API_KEY │ ├─────────────────────────────────────────────┤ │ 🪜 Rung 2: Playwright Docker (Local) │ │ • JavaScript rendering, screenshots │ │ • Cost: FREE (CPU only) │ │ • Requires: Docker installed │ ├─────────────────────────────────────────────┤ │ 🪜 Rung 1: web_fetch (No browser) │ │ • Static pages, APIs, simple HTML │ │ • Cost: FREE │ │ • Requires: Nothing │ └─────────────────────────────────────────────┘ Start at the bottom. Climb only when needed. ``` ## When to Climb | Situation | Rung | Why | |-----------|------|-----| | Static HTML, APIs | 1 | No JS needed | | React/Vue/SPA apps | 2 | JS rendering | | Docker unavailable | 3 | Cloud fallback | | CAPTCHA/Cloudflare | 4 | Bot bypass needed | | OAuth/MFA flows | 4 | Complex auth | ## Decision Flow ``` Need to access a URL │ ▼ Static content? ──YES──▶ Rung 1 (web_fetch) │ NO ▼ JS rendering only? ──YES──▶ Rung 2 (Playwright Docker) │ NO │ │ Success? ──NO──▶ Rung 3 ▼ │ YES CAPTCHA/bot detection? ────────────────────▶ DONE │ YES ▼ Rung 4 (Browserless.io) ──▶ DONE ``` ## Usage Examples ### Rung 1: Static content ```javascript // Built into Clawdbot const content = await web_fetch("https://example.com"); ``` ### Rung 2: JS-rendered page ```bash docker run --rm -v /tmp:/output mcr.microsoft.com/playwright:v1.58.0-jammy \ npx playwright screenshot https://spa-app.com /output/shot.png ``` ### Rung 3: Cloud browser (BrowserCat) ```javascript const { chromium } = require('playwright'); const browser = await chromium.connect('wss://api.browsercat.com/connect', { headers: { 'Api-Key': process.env.BROWSERCAT_API_KEY } }); ``` ### Rung 4: CAPTCHA bypass (Browserless) ```javascript const { chromium } = require('playwright'); const browser = await chromium.connectOverCDP( `wss://production-sfo.browserless.io?[REDACTED_SECRET]` ); // CAPTCHA handled automatically ``` ## Cost Optimization 1. **Start low** — Always try Rung 1 first 2. **Cache results** — Don't re-fetch unnecessarily 3. **Batch requests** — One browser session for multiple pages 4. **Check success** — Only climb if lower rung fails ## Get Your Keys | Service | Cost | Sign Up | |---------|------|---------| | BrowserCat | Free tier | https://browsercat.com | | Browserless.io | $10+/mo | https://browserless.io | Both are optional — Rungs 1-2 work without any API keys.

path: scripts/setup.sh
content: #!/bin/bash
# Browser Ladder Setup Script
# Prompts for optional API keys and adds them to .env

set -e

ENV_FILE="${CLAWDBOT_WORKSPACE:-.}/.env"

echo "🪜 Browser Ladder Setup"
echo "========================"
echo ""
echo "This skill works without any API keys (Rungs 1-2)."
echo "Add keys below to enable cloud browsers (Rungs 3-4)."
echo ""

# Rung 3: BrowserCat (free)
echo "--- Rung 3: BrowserCat (FREE) ---"
echo "Get a free API key at: https://browsercat.com"
read -p "BrowserCat API Key (or press Enter to skip): " BROWSERCAT_KEY

if [ -n "$BROWSERCAT_KEY" ]; then
  # Remove existing key if present
  grep -v "^BROWSERCAT_API_KEY=" "$ENV_FILE" > "$ENV_FILE.tmp" 2>/dev/null || true
  mv "$ENV_FILE.tmp" "$ENV_FILE" 2>/dev/null || true
  echo "BROWSERCAT_API_KEY=$BROWSERCAT_KEY" >> "$ENV_FILE"
  echo "✅ BrowserCat key saved"
else
  echo "⏭️  Skipped (Rung 3 disabled)"
fi

echo ""

# Rung 4: Browserless.io (paid)
echo "--- Rung 4: Browserless.io (\$10+/mo) ---"
echo "Get a token at: https://browserless.io"
echo "Enables: CAPTCHA solving, bot detection bypass"
read -p "Browserless Token (or press Enter to skip): " BROWSERLESS_KEY

if [ -n "$BROWSERLESS_KEY" ]; then
  grep -v "^BROWSERLESS_TOKEN=" "$ENV_FILE" > "$ENV_FILE.tmp" 2>/dev/null || true
  mv "$ENV_FILE.tmp" "$ENV_FILE" 2>/dev/null || true
  echo "BROWSERLESS_TOKEN=$BROWSERLESS_KEY" >> "$ENV_FILE"
  echo "✅ Browserless token saved"
else
  echo "⏭️  Skipped (Rung 4 disabled)"
fi

echo ""
echo "🪜 Setup complete!"
echo ""
echo "Available rungs:"
echo "  ✅ Rung 1: web_fetch (always available)"
echo "  ✅ Rung 2: Playwright Docker (requires: docker)"
[ -n "$BROWSERCAT_KEY" ] && echo "  ✅ Rung 3: BrowserCat" || echo "  ⬜ Rung 3: BrowserCat (no key)"
[ -n "$BROWSERLESS_KEY" ] && echo "  ✅ Rung 4: Browserless.io" || echo "  ⬜ Rung 4: Browserless.io (no key)"


path: scripts/browse.sh
content: #!/bin/bash
# browse.sh - Hierarchical browser with fallback chain
# Usage: browse.sh <url> [options]
#
# Options:
#   --screenshot <path>   Take screenshot
#   --pdf <path>          Generate PDF  
#   --html                Output page HTML
#   --level <1-4>         Force specific level
#   --verbose             Show which level was used

set -euo pipefail

URL="${1:-}"
SCREENSHOT=""
PDF=""
HTML=false
FORCE_LEVEL=""
VERBOSE=false

# Parse args
shift || true
while [[ $# -gt 0 ]]; do
  case $1 in
    --screenshot) SCREENSHOT="$2"; shift 2 ;;
    --pdf) PDF="$2"; shift 2 ;;
    --html) HTML=true; shift ;;
    --level) FORCE_LEVEL="$2"; shift 2 ;;
    --verbose) VERBOSE=true; shift ;;
    *) echo "Unknown option: $1"; exit 1 ;;
  esac
done

if [[ -z "$URL" ]]; then
  echo "Usage: browse.sh <url> [--screenshot path] [--pdf path] [--html] [--level 1-4]"
  exit 1
fi

log() {
  if $VERBOSE; then echo "[browse] $1" >&2; fi
}

# Level 1: web_fetch (via curl)
try_level1() {
  log "Trying Level 1: web_fetch (curl)"
  if $HTML; then
    curl -sL "$URL" 2>/dev/null && return 0
  elif [[ -n "$SCREENSHOT" ]] || [[ -n "$PDF" ]]; then
    # Can't do screenshots/PDFs with curl
    return 1
  else
    # Just check if page loads
    curl -sL -o /dev/null -w "%{http_code}" "$URL" 2>/dev/null | grep -q "200" && return 0
  fi
  return 1
}

# Level 2: Playwright Docker
try_level2() {
  log "Trying Level 2: Playwright Docker"
  
  if ! command -v docker &>/dev/null; then
    log "Docker not available"
    return 1
  fi
  
  # Check if Docker is running
  if ! docker info &>/dev/null; then
    log "Docker daemon not running"
    return 1
  fi
  
  if [[ -n "$SCREENSHOT" ]]; then
    local OUTPUT_DIR=$(dirname "$SCREENSHOT")
    local OUTPUT_FILE=$(basename "$SCREENSHOT")
    mkdir -p "$OUTPUT_DIR"
    
    docker run --rm -v "$OUTPUT_DIR:/output" \
      mcr.microsoft.com/playwright:v1.58.0-jammy \
      npx playwright screenshot "$URL" "/output/$OUTPUT_FILE" 2>/dev/null && return 0
      
  elif [[ -n "$PDF" ]]; then
    local OUTPUT_DIR=$(dirname "$PDF")
    local OUTPUT_FILE=$(basename "$PDF")
    mkdir -p "$OUTPUT_DIR"
    
    docker run --rm -v "$OUTPUT_DIR:/output" \
      mcr.microsoft.com/playwright:v1.58.0-jammy \
      npx playwright pdf "$URL" "/output/$OUTPUT_FILE" 2>/dev/null && return 0
      
  elif $HTML; then
    # For HTML, we need a script since npx playwright doesn't have a content command
    docker run --rm mcr.microsoft.com/playwright:v1.58.0-jammy \
      npx -y playwright-cli-extra content "$URL" 2>/dev/null && return 0
    
    # Fallback: just use curl if we got here for HTML
    return 1
  fi
  
  return 1
}

# Level 3: BrowserCat (free cloud)
try_level3() {
  log "Trying Level 3: BrowserCat"
  
  if [[ -z "${BROWSERCAT_API_KEY:-}" ]]; then
    log "BROWSERCAT_API_KEY not set"
    return 1
  fi
  
  # TODO: Implement BrowserCat API
  return 1
}

# Level 4: Browserless.io (paid)
try_level4() {
  log "Trying Level 4: Browserless.io"
  
  if [[ -z "${BROWSERLESS_TOKEN:-}" ]]; then
    log "BROWSERLESS_TOKEN not set"
    return 1
  fi
  
  if [[ -n "$SCREENSHOT" ]]; then
    curl -s -X POST "https://production-sfo.browserless.io/screenshot?[REDACTED_SECRET]" \
      -H "Content-Type: application/json" \
      -d "{\"url\": \"$URL\", \"options\": {\"fullPage\": true}}" \
      -o "$SCREENSHOT" 2>/dev/null && return 0
  elif [[ -n "$PDF" ]]; then
    curl -s -X POST "https://production-sfo.browserless.io/pdf?[REDACTED_SECRET]" \
      -H "Content-Type: application/json" \
      -d "{\"url\": \"$URL\"}" \
      -o "$PDF" 2>/dev/null && return 0
  elif $HTML; then
    curl -s -X POST "https://production-sfo.browserless.io/content?[REDACTED_SECRET]" \
      -H "Content-Type: application/json" \
      -d "{\"url\": \"$URL\"}" 2>/dev/null && return 0
  fi
  
  return 1
}

# Main execution
main() {
  local LEVELS=(1 2 3 4)
  
  # If level forced, only try that one
  if [[ -n "$FORCE_LEVEL" ]]; then
    LEVELS=("$FORCE_LEVEL")
  fi
  
  for level in "${LEVELS[@]}"; do
    case $level in
      1) try_level1 && { log "Success at Level 1"; exit 0; } ;;
      2) try_level2 && { log "Success at Level 2"; exit 0; } ;;
      3) try_level3 && { log "Success at Level 3"; exit 0; } ;;
      4) try_level4 && { log "Success at Level 4"; exit 0; } ;;
    esac
  done
  
  echo "All levels failed for URL: $URL" >&2
  exit 1
}

main


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)