Skill: ricardodantas/podman-browser (version 1.2.1)

# Podman Browser Skill Headless browser automation using Podman + Playwright for scraping JavaScript-rendered pages. ## Requirements - Podman 5.x+ installed and running - Node.js 18+ (for running the CLI) - Internet connection (first run pulls ~1.5GB container image) ## Installation Create a symlink for easy access: ```bash chmod +x browse.js ln -sf "$(pwd)/browse.js" ~/.local/bin/podman-browse ``` First run will pull the Playwright container image (~1.5GB). ## Commands ### `podman-browse` (or `./browse.js`) Fetch a JavaScript-rendered page and return its text content. ```bash podman-browse "https://example.com" ``` **Options:** - `--html` - Return raw HTML instead of text - `--wait <ms>` - Wait for additional time after load (default: 2000ms) - `--selector <css>` - Wait for specific element before capturing - `-h, --help` - Show help **Examples:** ```bash # Get rendered text content from Hacker News podman-browse "https://news.ycombinator.com" # Get raw HTML podman-browse --html "https://news.ycombinator.com" # Wait for specific element podman-browse --selector ".itemlist" "https://news.ycombinator.com" # Extra wait time for slow pages podman-browse --wait 5000 "https://news.ycombinator.com/newest" ``` ## How It Works 1. Runs Microsoft's official Playwright container via Podman 2. Uses Chromium in headless mode 3. Waits for JavaScript to render (networkidle + custom wait) 4. Returns text or HTML content ## Container Image Uses `mcr.microsoft.com/playwright:v1.50.0-noble` with `playwright@1.50.0` npm package (versions must match). ## Files - `browse.js` - Self-contained Node.js CLI (handles args + spawns podman) - `SKILL.md` - This documentation ## Notes - First run will pull the container image (~1.5GB) - Uses `--ipc=host` for Chromium stability - Uses `--init` to handle zombie processes - Sandbox disabled when running as root (fine for trusted sites) - Each run starts a fresh container (clean but takes ~10-15s)

path: browse.js
content: #!/usr/bin/env node
// podman-browse - Headless browser automation using Podman + Playwright
// Fetches JavaScript-rendered pages and returns text or HTML content

const { spawn } = require('child_process');
const path = require('path');

const IMAGE = 'mcr.microsoft.com/playwright:v1.50.0-noble';
const PLAYWRIGHT_VERSION = '1.50.0';

// Defaults
let waitMs = 2000;
let outputMode = 'text';
let selector = '';
let url = '';

// Parse arguments
const args = process.argv.slice(2);
for (let i = 0; i < args.length; i++) {
    switch (args[i]) {
        case '--html':
            outputMode = 'html';
            break;
        case '--wait':
            waitMs = parseInt(args[++i], 10);
            break;
        case '--selector':
            selector = args[++i];
            break;
        case '-h':
        case '--help':
            console.log(`Usage: browse.js [OPTIONS] URL

Fetch a JavaScript-rendered page using Playwright in Podman.

Options:
  --html              Return raw HTML instead of text
  --wait <ms>         Wait time after load (default: 2000ms)
  --selector <css>    Wait for specific element before capturing
  -h, --help          Show this help

Examples:
  ./browse.js "https://example.com"
  ./browse.js --html "https://example.com"
  ./browse.js --selector ".content" --wait 5000 "https://example.com"`);
            process.exit(0);
        default:
            if (!args[i].startsWith('-')) {
                url = args[i];
            } else {
                console.error(`Unknown option: ${args[i]}`);
                process.exit(1);
            }
    }
}

if (!url) {
    console.error('Error: URL is required');
    console.error('Usage: browse.js [OPTIONS] URL');
    process.exit(1);
}

// The actual Playwright script to run inside the container
const playwrightScript = `
const { chromium } = require('playwright');

(async () => {
    const url = process.env.TARGET_URL;
    const waitMs = parseInt(process.env.WAIT_MS || '2000', 10);
    const outputMode = process.env.OUTPUT_MODE || 'text';
    const selector = process.env.SELECTOR || '';

    const browser = await chromium.launch({
        headless: true,
        args: ['--no-sandbox', '--disable-setuid-sandbox']
    });

    try {
        const context = await browser.newContext({
            userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
        });
        const page = await context.newPage();

        await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });

        if (selector) {
            try {
                await page.waitForSelector(selector, { timeout: 10000 });
            } catch (e) {
                console.error('Warning: selector not found within timeout');
            }
        }

        await page.waitForTimeout(waitMs);

        if (outputMode === 'html') {
            const html = await page.content();
            console.log(html);
        } else {
            const text = await page.evaluate(() => {
                const scripts = document.querySelectorAll('script, style, noscript');
                scripts.forEach(el => el.remove());
                return document.body.innerText;
            });
            console.log(text);
        }
    } catch (error) {
        console.error('Error:', error.message);
        process.exit(1);
    } finally {
        await browser.close();
    }
})();
`;

// Run Playwright in Podman container
const podmanArgs = [
    'run', '--rm', '-i',
    '--ipc=host',
    '--init',
    '-e', `TARGET_URL=${url}`,
    '-e', `WAIT_MS=${waitMs}`,
    '-e', `OUTPUT_MODE=${outputMode}`,
    '-e', `SELECTOR=${selector}`,
    IMAGE,
    '/bin/bash', '-c',
    `cd /tmp && npm init -y >/dev/null 2>&1 && npm install playwright@${PLAYWRIGHT_VERSION} >/dev/null 2>&1 && node -e '${playwrightScript.replace(/'/g, "'\\''")}'`
];

const proc = spawn('podman', podmanArgs, { stdio: ['inherit', 'inherit', 'inherit'] });

proc.on('close', (code) => {
    process.exit(code || 0);
});

proc.on('error', (err) => {
    console.error(`Failed to start podman: ${err.message}`);
    process.exit(1);
});


path: README.md
content: # podman-browser

Headless browser automation using Podman + Playwright for scraping JavaScript-rendered pages.

## What it does

This skill runs a headless Chromium browser inside a Podman container using Microsoft's official Playwright image. It's useful for:

- Scraping pages that require JavaScript to render content
- Fetching dynamically loaded data (SPAs, React apps, etc.)
- Automated browser tasks without installing browser dependencies locally

## Prerequisites

- **Podman 5.x+** installed and running
- **Node.js 18+** (for running the CLI)
- Internet connection (first run pulls ~1.5GB container image)

## Installation

### For OpenClaw users

Copy the skill folder to your OpenClaw skills directory:

```bash
cp -r podman-browser ~/.openclaw/workspace/skills/
```

Create a symlink to make it available in your PATH:

```bash
ln -sf ~/.openclaw/workspace/skills/podman-browser/browse.js ~/.local/bin/podman-browse
chmod +x ~/.openclaw/workspace/skills/podman-browser/browse.js
```

### Standalone usage

Clone this repo and add to your PATH:

```bash
git clone https://github.com/ricardodantas/podman-browser.git
cd podman-browser
chmod +x browse.js
ln -sf "$(pwd)/browse.js" ~/.local/bin/podman-browse
```

## Usage

### Basic usage

```bash
# Get rendered text content from a page
podman-browse "https://example.com"

# Or run directly
./browse.js "https://example.com"
```

### Options

| Option | Description |
|--------|-------------|
| `--html` | Return raw HTML instead of text |
| `--wait <ms>` | Wait time after load (default: 2000ms) |
| `--selector <css>` | Wait for specific CSS selector before capturing |
| `-h, --help` | Show help |

### Examples

```bash
# Get rendered text content from Hacker News
podman-browse "https://news.ycombinator.com"

# Get raw HTML
podman-browse --html "https://news.ycombinator.com"

# Wait for specific element
podman-browse --selector ".itemlist" "https://news.ycombinator.com"

# Extra wait time for slow pages
podman-browse --wait 5000 "https://news.ycombinator.com/newest"
```

## How it works

1. Launches Microsoft's official Playwright container via Podman
2. Uses Chromium in headless mode with a realistic user agent
3. Navigates to the URL and waits for network idle
4. Optionally waits for a specific CSS selector
5. Applies additional wait time for JavaScript to settle
6. Returns text content (or HTML with `--html` flag)

## Files

| File | Description |
|------|-------------|
| `browse.js` | Self-contained Node.js CLI (handles args + spawns podman) |
| `SKILL.md` | OpenClaw skill documentation |

## Container Image

Uses `mcr.microsoft.com/playwright:v1.50.0-noble` with `playwright@1.50.0` npm package.

**Note**: The Playwright npm package version must match the container image version.

## Performance Notes

- First run pulls the container image (~1.5GB)
- Each run starts a fresh container (clean but takes ~10-15s)
- Uses `--ipc=host` for Chromium stability
- Uses `--init` to handle zombie processes

## License

GPL-3.0 License - see [LICENSE](LICENSE) file.

## Contributing

Contributions welcome! Please open an issue or PR.

## Author

Ricardo Dantas ([@ricardodantas](https://github.com/ricardodantas))


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