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

chrome-cdp-cli

v2.1.4

Published

Browser automation CLI via Chrome DevTools Protocol. Designed for developers and AI assistants - combines dedicated commands for common tasks with flexible JavaScript execution for complex scenarios. Features: element interaction, screenshots, DOM snapsho

Readme

CDP — Chrome DevTools Protocol CLI

A command-line tool for browser automation via Chrome DevTools Protocol. Designed for developers and AI assistants who need reliable, scriptable browser control with dedicated commands for common tasks and unlimited flexibility through JavaScript execution.

npm install -g chrome-cdp-cli
cdp eval "document.title"

Why This Tool Exists

The honest story: I started using chrome-devtools-mcp like everyone else. It worked great… until it didn't. One day it just stopped working — Cursor showed 26 tools available, everything looked normal, but every tool call threw errors. Classic black box problem: you can't debug what you can't see inside.

Meanwhile, Anthropic introduced the SKILL concept, which makes much more sense for how LLMs work. And what pairs perfectly with Skills? Good old-fashioned command-line tools. They're debuggable, composable, and you can actually see what's happening when things go wrong.

The result: A tool that's both powerful enough for complex automation and simple enough that you (and your AI assistant) can use it without pulling your hair out.


Installation

# Install globally
npm install -g chrome-cdp-cli

# Use without installing
npx chrome-cdp-cli eval "document.title"

After installation, both cdp and chrome-cdp-cli are available as entry points:

cdp eval "document.title"
chrome-cdp-cli eval "document.title"   # backward-compatible alias

Prerequisites

  • Node.js 18.0.0 or higher
  • Chrome with remote debugging enabled

Starting Chrome with Remote Debugging

From Chrome 136, --user-data-dir is required alongside --remote-debugging-port. See Chrome's announcement.

# Standard launch (use a dedicated profile directory)
chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug

# Headless
chrome --headless --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug

# macOS full path
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug &

Quick Start

# Execute JavaScript in the browser
cdp eval "document.title"
cdp eval "window.location.href = 'https://example.com'"

# Take a screenshot (saves to file)
cdp screenshot --filename page.png

# Screenshot to stdout (pipe to another tool)
cdp screenshot | feh -

# Capture DOM snapshot
cdp dom --filename dom.txt

# Follow console logs in real time (like tail -f)
cdp log -f

# Follow network requests in real time
cdp network -f

# Element interactions
cdp click "#submit-button"
cdp fill "#email" "[email protected]"
cdp hover ".menu-item"

Global Options

All commands accept these options, which can appear before or after the command name:

| Option | Short | Default | Description | |--------|-------|---------|-------------| | --host | -h | localhost | Chrome host | | --port | -p | 9222 | DevTools port | | --format | -f | text | Output format: json or text | | --timeout | -t | 30000 | Command timeout (ms) | | --target-index | -i | — | Select Chrome page by index (1-based) | | --verbose | -v | false | Enable verbose logging | | --quiet | -q | false | Suppress non-essential output | | --debug | -d | false | Enable debug logging | | --config | -c | — | Configuration file path |

Selecting a Chrome Page

When multiple Chrome pages are open, CDP shows an interactive selector:

Select a Chrome page  (↑↓ navigate, Enter select, q quit)
──────────────────────────────────────────────────────
 ❯ [1] My App
       http://localhost:3000/
   [2] GitHub
       https://github.com/
──────────────────────────────────────────────────────
Tip: skip this prompt with  cdp -i <number> <command>
     or close other tabs until only one remains.

Use arrow keys to navigate, Enter to select, q/ESC/Ctrl+C to cancel (exits cleanly with code 0).

To skip the prompt, pass -i before or after the command:

cdp -i 1 eval "document.title"
cdp eval "document.title" -i 1    # also works

Command Reference

eval — JavaScript Execution

Execute any JavaScript in the browser context.

cdp eval "document.title"
cdp eval "window.location.href"
cdp eval --file script.js
cdp eval "await fetch('/api').then(r => r.json())"
cdp eval "await new Promise(r => setTimeout(r, 5000))" --timeout 10000

screenshot — Take Screenshots

# Save to file
cdp screenshot --filename page.png
cdp screenshot --filename page.jpg --image-format jpeg --quality 85
cdp screenshot --full-page --filename fullpage.png
cdp screenshot --width 1920 --height 1080 --filename custom.png

# Output raw binary to stdout (pipe to another tool)
cdp screenshot > page.png
cdp screenshot | open -f -a Preview        # macOS
cdp screenshot | display                   # Linux (ImageMagick)
cdp -i 2 screenshot | feh -               # second tab, pipe to feh

Note: --image-format controls the image encoding (png/jpeg). The global --format flag (json/text) does not apply to screenshot output.

Warning: Running cdp screenshot without --filename in a terminal will print binary data to your terminal. The CLI detects this and shows an error instead.

dom — DOM Snapshot (alias: snapshot)

Capture a full DOM snapshot with layout information.

cdp dom --filename dom.txt
cdp dom --filename dom.json --format json
cdp snapshot --filename dom.txt    # alias

log — Follow Console Messages (alias: console)

Stream console messages in real time directly from the browser. This command runs until you press Ctrl+C.

# Follow all console messages
cdp log -f

# Filter by message type
cdp log -f --types error,warn

# Filter by text pattern (regex)
cdp log -f --text-pattern "api|fetch"

# Output as JSON
cdp log -f --format json

Note: log streams events directly via CDP — no background process required. Since it follows live events, historical messages (before the command started) are not shown.

Filter options:

| Option | Description | |--------|-------------| | --types <list> | Comma-separated types: log,info,warn,error,debug | | --text-pattern <regex> | Filter messages matching this pattern |

network — Follow Network Requests (alias: net)

Stream network requests in real time directly from the browser. Runs until Ctrl+C.

# Follow all requests
cdp network -f

# Filter by HTTP method
cdp network -f --methods POST,PUT

# Filter by URL pattern (regex)
cdp network -f --url-pattern "api|graphql"

# Filter by status code
cdp network -f --status-codes 200,201,404

# Combine filters
cdp network -f --methods POST --url-pattern "/api" --status-codes 200,201

# JSON output
cdp network -f --format json

Filter options:

| Option | Description | |--------|-------------| | --methods <list> | Comma-separated HTTP methods: GET,POST,PUT,DELETE,… | | --url-pattern <regex> | Filter URLs matching this pattern | | --status-codes <list> | Comma-separated status codes: 200,404,500 |

click — Click an Element

cdp click "#submit-button"
cdp click ".slow-button" --timeout 10000
cdp click "#optional" --no-wait

hover — Hover over an Element

cdp hover "#menu-item"
cdp hover ".dropdown-trigger"

fill — Fill a Form Field

Works with text inputs, email inputs, password inputs, textareas, and <select> dropdowns (matches by value or visible text).

cdp fill "#username" "[email protected]"
cdp fill "input[type='password']" "secret123"
cdp fill "#country" "United States"      # select dropdown by text
cdp fill "#country" "US"                 # select dropdown by value
cdp fill "#notes" " - extra" --no-clear  # append, don't clear first

fill_form — Batch Form Fill

# Inline JSON
cdp fill_form --fields '[
  {"selector":"#firstName","value":"John"},
  {"selector":"#lastName","value":"Doe"},
  {"selector":"#email","value":"[email protected]"}
]'

# From JSON file
cdp fill_form --fields-file form-data.json

# Stop on first error (default: continue on error)
cdp fill_form --fields '[...]' --stop-on-error

drag — Drag and Drop

cdp drag "#draggable-item" "#drop-zone"
cdp drag ".file-item" ".upload-area" --timeout 10000

press_key — Keyboard Input

cdp press_key "Enter"
cdp press_key "Escape"
cdp press_key "Tab"
cdp press_key "a" --modifiers Ctrl          # Ctrl+A
cdp press_key "s" --modifiers Ctrl          # Ctrl+S
cdp press_key "c" --modifiers Ctrl,Shift    # Ctrl+Shift+C
cdp press_key "ArrowDown" --selector "#dropdown"

upload_file — File Upload

cdp upload_file "input[type='file']" "./document.pdf"
cdp upload_file "#file-input" "/path/to/image.jpg"

wait_for — Wait for Element State

cdp wait_for "#loading-spinner"              # exists
cdp wait_for "#modal" --condition visible
cdp wait_for "#loading" --condition hidden
cdp wait_for "#submit-btn" --condition enabled
cdp wait_for "#processing" --condition disabled
cdp wait_for "#slow-element" --timeout 30000

handle_dialog — Handle Browser Dialogs

cdp handle_dialog accept
cdp handle_dialog dismiss
cdp handle_dialog accept --text "John Doe"   # prompt with text
cdp handle_dialog accept --timeout 10000

install_cursor_command — Install Cursor Integration

cdp install_cursor_command
cdp install_cursor_command --force
cdp install_cursor_command --target-directory /custom/.cursor/commands

install_claude_skill — Install Claude Skill

cdp install_claude_skill                              # project scope
cdp install_claude_skill --skill-type personal        # user home scope
cdp install_claude_skill --include-examples --include-references

The Power of eval

eval is the most powerful command — it runs any JavaScript in the browser, making virtually any automation task possible without waiting for dedicated command implementations.

# Navigation
cdp eval "window.location.href = 'https://example.com'"
cdp eval "window.history.back()"
cdp eval "window.location.reload()"

# Content extraction
cdp eval "document.title"
cdp eval "Array.from(document.querySelectorAll('a')).map(a => a.href)"
cdp eval "Array.from(document.querySelectorAll('.product')).map(p => ({
  name: p.querySelector('.name').textContent,
  price: p.querySelector('.price').textContent
}))"

# Wait for dynamic content
cdp eval "new Promise(resolve => {
  const check = () => {
    const el = document.querySelector('#dynamic-content');
    if (el) resolve(el.textContent);
    else setTimeout(check, 100);
  };
  check();
})"

# Make HTTP requests from browser context
cdp eval "fetch('/api/data').then(r => r.json())"
cdp eval "fetch('/api/users', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({name: 'John'})
}).then(r => r.json())"

# Performance data
cdp eval "performance.getEntriesByType('navigation')"
cdp eval "performance.getEntriesByType('resource').length"

# Browser info
cdp eval "navigator.userAgent"
cdp eval "{width: window.innerWidth, height: window.innerHeight}"

Configuration

Config File

Create .chrome-cdp-cli.yaml in your project root or home directory:

host: localhost
port: 9222
timeout: 30000
outputFormat: text
verbose: false

profiles:
  development:
    debug: true
    verbose: true
  production:
    quiet: true
    outputFormat: json

aliases:
  ss: screenshot
  js: eval

Environment Variables

export CHROME_CDP_CLI_HOST=localhost
export CHROME_CDP_CLI_PORT=9222
export CHROME_CDP_CLI_TIMEOUT=30000
export CHROME_CDP_CLI_VERBOSE=true

Precedence

  1. Command-line arguments (highest)
  2. Environment variables
  3. Config file values
  4. Built-in defaults (lowest)

Help System

cdp help                        # all commands, categorized
cdp help eval                   # command-specific help with examples
cdp help screenshot
cdp help log
cdp help network
cdp help topic configuration
cdp help topic selectors
cdp help topic automation

Scripting & Automation

Shell Pipelines

# Screenshot to file via redirect
cdp screenshot > page.png

# Get page title as plain text
cdp eval "document.title" --format text

# Extract data as JSON for processing
cdp eval "Array.from(document.links).map(l => l.href)" --format json | jq '.[]'

# Capture logs from a specific tab
cdp -i 2 log -f --types error 2>&1 | tee errors.log

Scripting with Multiple Commands

#!/bin/bash
# Navigate and capture
cdp eval "window.location.href = 'https://example.com'"
sleep 1
cdp screenshot --filename after-nav.png
cdp dom --filename after-nav.txt

Exit Codes

| Code | Meaning | |------|---------| | 0 | Success (including user-cancelled interactive selector) | | 1 | General error | | 3 | Chrome connection error | | 8 | Invalid arguments |


Troubleshooting

Chrome Won't Enable Remote Debugging

Most common cause: missing --user-data-dir.

# Wrong (Chrome 136+ will ignore this)
chrome --remote-debugging-port=9222

# Correct
chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug

See Chrome's announcement for details.

Connection Refused

  • Verify Chrome is running with the correct flags
  • Check the port matches (--port option, default 9222)
  • Ensure no firewall is blocking the port

Multiple Pages / Tab Selector Appears

If you have multiple Chrome tabs open, CDP shows an interactive selector. To avoid it:

cdp -i 1 eval "document.title"   # select tab by index

Or close unneeded tabs, leaving only the one you want to automate.

Command Timeout

cdp eval "await longOperation()" --timeout 60000

Element Not Found

cdp wait_for "#dynamic-element" --timeout 10000
cdp click "#dynamic-element"

Debug Mode

cdp --debug eval "document.title"
cdp --verbose screenshot --filename page.png

Development

git clone https://github.com/nicoster/chrome-devtools-cli.git
cd chrome-devtools-cli
npm install

# Development run
npm run dev -- eval "document.title"

# Build
npm run build         # development (with source maps)
npm run build:prod    # production (optimized)
npm run build:watch   # watch mode

# Test
npm test
npm run test:watch
npm run test:ci       # CI mode with coverage

# Lint
npm run lint
npm run lint:fix

# Verify everything
npm run verify

Project Structure

chrome-devtools-cli/
├── src/
│   ├── cli/              # ArgumentParser, CommandRouter, HelpSystem, CLIApplication
│   ├── client/           # CDP client
│   ├── connection/       # Connection management & target discovery
│   ├── handlers/         # One file per command
│   ├── monitors/         # ConsoleMonitor, NetworkMonitor (real-time event streams)
│   ├── proxy/            # Background proxy server (used by IDE integrations)
│   ├── config/           # Configuration management
│   └── index.ts          # Entry point
├── dist/                 # Compiled output
└── package.json

Changelog

v2.1.0

  • cdp alias: cdp is now the primary command name (chrome-cdp-cli still works)
  • log command: renamed from console (console kept as alias); follow-only real-time streaming via CDP, no background proxy required
  • network command: follow-only real-time streaming; filter flags (--methods, --url-pattern, --status-codes) are now top-level instead of nested under --filter
  • dom command: renamed from snapshot (snapshot kept as alias)
  • screenshot binary output: omit --filename to write raw PNG/JPEG bytes to stdout for piping; --format renamed to --image-format to avoid conflict with the global --format
  • Interactive target selector: when multiple Chrome pages are open, an arrow-key menu lets you pick one; pass -i <n> to skip it
  • -i global option: --target-index / -i now works before or after the command name
  • Removed restart command: proxy-based log collection has been removed entirely
  • Better error messages: connection errors now include the --user-data-dir tip and a link to Chrome's announcement

v2.0.x

  • Enhanced argument parser with schema validation
  • Comprehensive help system with contextual assistance
  • Configuration management with YAML/JSON support and profiles
  • Standardized output formatting (JSON/text)
  • Full element interaction suite: click, hover, fill, drag, press_key, upload_file, wait_for, handle_dialog
  • Batch form filling with fill_form
  • IDE integrations: Cursor commands, Claude skills

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature-name
  3. Add tests for your changes
  4. Run npm run verify
  5. Commit and open a pull request

License

MIT — see LICENSE.

Support