npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cvr/browser

v1.0.0

Published

Headless browser automation CLI for AI agents

Readme

Browser CLI

Headless browser automation CLI for AI agents. Built with Effect and Playwright.

Inspired by vercel-labs/browser.

Features

  • Daemon architecture - Background process manages browser lifecycle
  • Unix socket IPC - Fast, reliable communication between CLI and daemon
  • Session isolation - Run multiple browser instances in parallel
  • Accessibility snapshots - Get semantic page structure with element refs
  • Chrome profile support - Reuse existing cookies and auth state
  • Interactive picker - Let users click to select elements in headed mode

Installation

# Clone and install
git clone https://github.com/your-username/browser.git
cd browser
bun install

# Build binary
bun run build

# Add to PATH (or use directly)
export PATH="$PWD/bin:$PATH"

Quick Start

browser open https://example.com    # Navigate (auto-starts daemon)
browser snapshot                     # Get accessibility tree
browser click "button[Submit]"       # Click element
browser fill "#email" "[email protected]" # Fill input
browser screenshot                   # Take screenshot
browser close                        # Close browser and daemon

Commands

Navigation

| Command | Description | |---------|-------------| | browser open <url> | Navigate to URL (auto-starts daemon) | | browser open --headed <url> | Navigate with visible browser window | | browser open --profile true <url> | Use default Chrome profile | | browser back | Go back in history | | browser forward | Go forward in history | | browser reload | Reload current page |

Page Content

| Command | Description | |---------|-------------| | browser snapshot | Get page accessibility tree with refs | | browser snapshot -i | Interactive elements only | | browser snapshot -c | Compact mode (no empty elements) | | browser screenshot [path] | Take screenshot | | browser screenshot -f | Full page screenshot | | browser content | Get page HTML | | browser get url | Get current URL | | browser get title | Get page title | | browser get text <selector> | Get element text |

Actions

| Command | Description | |---------|-------------| | browser click <selector> | Click element (supports @refs) | | browser fill <selector> <value> | Fill input field | | browser type <selector> <text> | Type text character by character | | browser hover <selector> | Hover over element | | browser check <selector> | Check checkbox | | browser uncheck <selector> | Uncheck checkbox | | browser press <key> | Press keyboard key | | browser select <selector> <value> | Select dropdown option | | browser scroll down | Scroll page down |

Tabs

| Command | Description | |---------|-------------| | browser tab list | List all tabs | | browser tab new | Open new tab | | browser tab switch <index> | Switch to tab by index | | browser tab close [index] | Close tab |

Debug

| Command | Description | |---------|-------------| | browser console | Get console messages | | browser errors | Get page errors | | browser eval <script> | Evaluate JavaScript | | browser wait <selector> | Wait for element |

Interactive

| Command | Description | |---------|-------------| | browser pick <message> | Interactive element picker |

Session Management

| Command | Description | |---------|-------------| | browser close | Close browser and daemon | | browser -s <name> <cmd> | Use named session |

Using Element Refs

After snapshot, elements have refs like [ref=e1]. Use them directly with @:

browser snapshot
# Output:
# - button "Submit" [ref=e1]
# - link "Home" [ref=e2]

browser click @e1    # Click the Submit button
browser hover @e2    # Hover over Home link

Chrome Profile Support

Reuse your existing Chrome cookies and authentication:

# Use default Chrome profile
browser open --profile true --headed https://github.com

# Use custom profile path
browser open --profile "/path/to/profile" https://github.com

Sessions

Run multiple browser instances in parallel:

# Terminal 1
browser -s session1 open https://site1.com

# Terminal 2
browser -s session2 open https://site2.com

Architecture

┌─────────────────────────────────────────────────────────┐
│  CLI (src/cli.ts)                                        │
│  - Parses args via @effect/cli                           │
│  - Uses DaemonManager for daemon lifecycle               │
│  - Sends commands via Unix socket                        │
└────────────────────────────┬────────────────────────────┘
                             │
        ┌────────────────────┴────────────────────┐
        ▼                                         ▼
┌───────────────────┐                    ┌───────────────┐
│  DaemonManager    │                    │  Unix Socket  │
│  (spawn/check)    │                    │  (IPC)        │
└───────────────────┘                    └───────┬───────┘
                                                 │
                                          [Daemon Process]
                                                 │
                                                 ▼
                             ┌─────────────────────────────┐
                             │  server.ts                   │
                             │  - ManagedRuntime            │
                             │  - Command dispatch          │
                             └──────────────┬──────────────┘
                                            │
                                            ▼
                             ┌─────────────────────────────┐
                             │  PlaywrightService          │
                             │  - executeCommand(cmd)      │
                             │  - State via Effect Ref     │
                             │  - 65+ command handlers     │
                             └─────────────────────────────┘

Development

# Run from source
bun run src/cli.ts open https://example.com

# Type check
bun run typecheck

# Run tests
bun run test

# Build binary
bun run build

Testing

Tests use @effect/vitest with mock layers:

import { describe, expect, it } from '@effect/vitest'
import { Effect } from 'effect'
import { PlaywrightService } from '../src/services/PlaywrightService.js'

describe('PlaywrightService', () => {
  it.effect('navigate sends URL', () => {
    const recorder = createRecorder(new Map([
      ['navigate', { url: 'https://example.com', title: 'Example' }]
    ]))

    return Effect.gen(function* () {
      const playwright = yield* PlaywrightService
      const response = yield* playwright.executeCommand({
        id: 'test-1',
        action: 'navigate',
        url: 'https://example.com',
      })

      expect(response.success).toBe(true)
    }).pipe(Effect.provide(PlaywrightService.Test(recorder.handler)))
  })
})

Tech Stack

Credits

  • vercel-labs/browser - Original inspiration for browser automation CLI design
  • Effect - Excellent TypeScript library for building robust applications
  • Playwright - Reliable browser automation

License

MIT