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

phantomwright-cli

v2.3.0

Published

CLI for undetected browser automation with human simulation and captcha solving

Readme

phantomwright-cli

Undetected browser automation CLI — daemon-based sessions and deterministic browser operations

npm License: MIT

phantomwright-cli exposes phantomwright as a composable shell command for scripted, deterministic browser automation.

Package changelog: CHANGELOG.md


Installation

npm install -g phantomwright-cli
npx phantomwright-driver install --with-deps chromium

Quick start

# Open a URL
phantomwright-cli open https://example.com

# Click an element (use snapshot to get ref IDs)
phantomwright-cli snapshot
phantomwright-cli click e5

# Fill a form field and submit
phantomwright-cli fill e8 "[email protected]"
phantomwright-cli press Enter

# Stop the session when done
phantomwright-cli session-stop

Global options

These apply to all commands:

| Option | Description | Default | |---|---|---| | -s, --session <name> | Session name (isolated browser daemon) | default | | --json | Output results as JSON | off | | --headless | Run browser headlessly | off | | --channel <channel> | Browser channel: chrome or msedge | msedge | | --locale <locale> | Browser locale (e.g. en-US, ja-JP) | en-US | | --persistent | Use persistent browser context (reuse login state) | off | | --persistent-fresh | Persistent context with a clean fresh profile | off | | --cdp-endpoint <url> | Connect to an existing Chromium over CDP instead of launching a local browser | off | | --cdp-header <header...> | Header sent on CDP connect (Name: value), repeatable. Requires --cdp-endpoint. | none | | --cdp-timeout <ms> | Timeout for the CDP connect handshake (0 disables). Requires --cdp-endpoint. | 30000 | | --route-header-url <pattern...> | Playwright URL pattern whose matching page requests receive route headers, repeatable | none | | --route-header <header...> | Header added to requests matching --route-header-url (Name: value), repeatable | none | | --route-header-file <header...> | Header added to requests matching --route-header-url, with value read from file (Name: path), repeatable | none | | --route-header-file-env <header...> | Header added to requests matching --route-header-url, with value read from file path stored in an env var (Name: ENV_VAR), repeatable | none | | --viewport <WxH> | Browser viewport size, e.g. 1920x1080. Rejected in --cdp-endpoint mode (remote window owns its size). | 1366x768 |


Command reference

Navigation

| Command | Description | |---|---| | open <url> | Open URL, launch browser daemon if needed. --no-snapshot skips the returned a11y tree, --timeout <ms> overrides the default 30 s navigation timeout. | | navigate <url> | Alias for open | | goto <url> | Alias for open | | go-back | Navigate back | | go-forward | Navigate forward | | reload | Reload current page |

Interaction (ref-based)

Refs are obtained from snapshot output (e.g. e5, e12).

| Command | Description | |---|---| | snapshot | Get accessibility tree with ref IDs | | click <ref> | Click element | | dblclick <ref> | Double-click element | | hover <ref> | Hover over element | | fill <ref> <text> | Clear and fill an input field | | type <text> | Type into currently focused element | | select <ref> <value> | Select a dropdown option | | check <ref> | Check a checkbox or radio button | | uncheck <ref> | Uncheck a checkbox | | drag <from-ref> <to-ref> | Drag element to another | | press <key> | Press a key (Enter, Tab, Escape, …) | | scroll [direction] | Scroll up or down (default: down) with --by <pixels> |

Read page content

| Command | Description | |---|---| | get-text <ref> | Get text content of an element | | get-attribute <ref> <attr> | Get an attribute value of an element | | eval <code> [ref] | Execute JavaScript (optionally on a scoped element) | | evaluate <script> [ref] | Deprecated alias for eval (prints a note to stderr; prefer eval) |

Wait

| Command | Description | |---|---| | wait <ref> | Wait for element to be visible (add --hidden to wait for hidden, --timeout <ms>) | | wait-for-timeout <ms> | Sleep for fixed milliseconds (prints a note to stderr; prefer condition-based waits like wait-for-idle) | | wait-for-url <pattern> | Wait for URL to match a pattern (--timeout <ms>) | | wait-for-navigation | Wait for current/in-flight navigation to reach domcontentloaded (--timeout <ms>) | | wait-for-load | Wait for page load (--state load\|domcontentloaded\|networkidle, --timeout <ms>) | | wait-for-idle | Wait for network idle (--timeout <ms>) |

Output

| Command | Description | |---|---| | screenshot [path] | Take a screenshot. Output path may be a positional (screenshot out.png) or -o file.png / --path file.png. --full-page for whole document; --ref <ref> captures just that element (mutually exclusive with --full-page). | | pdf [path] | Export to PDF — headless only. Output path may be a positional (pdf out.pdf) or -o / --path. --format A4, --landscape. |

Tab management

| Command | Description | |---|---| | tab-list | List all open tabs | | tab-new [url] | Open a new tab. --timeout <ms> overrides the default 30 s navigation timeout when [url] is given. | | tab-close [index] | Close a tab | | tab-select <index> | Switch to a tab |

Special

| Command | Description | |---|---| | clear-overlays | Remove common overlays (cookie banners, modals, popups) |

Composite

| Command | Description | |---|---| | search <ref> <text...> | fill + Enter + wait-for-idle in one step | | batch <commands...> | Run multiple CLI commands in sequence, separated by \|\|\| (supports quoted arguments) |

Session management

| Command | Description | |---|---| | session-list | List all sessions | | session-stop [name] | Stop a specific session (default: current) | | session-stop-all | Stop all sessions | | session-delete [name] | Delete session and its user data |

Browser profile management

| Command | Description | |---|---| | profile-list | List available browser profiles (--browser msedge\|chrome) | | profile-select <index> | Copy a system profile into the session | | profile-info | Show current session's profile |

Sessions and daemon

Every command operates through a per-session browser daemon. The daemon is started automatically on the first command and keeps the browser open for subsequent commands.

# Two isolated sessions
phantomwright-cli -s session-a open https://gmail.com
phantomwright-cli -s session-b open https://outlook.com

# Stop a specific session
phantomwright-cli session-stop session-a

# Stop all
phantomwright-cli session-stop-all

Request header routes

Use --route-header-url with one or more route header flags to register a Playwright browserContext.route(...) handler before the command runs. Matching page requests are continued with the given headers merged into the original request headers. For a true HTTP 302 login flow, match the first-hop URL that produces the redirect; in the default stealth mode, the redirected target request receives the continued headers.

Route header handlers are active only for the current CLI command and are cleared when that command completes (whether it succeeds or errors — a retry that needs the headers must pass the flags again). After a successful login/bootstrap command, normal follow-up actions usually rely on the browser's session cookies and should not repeat the secret header. If a later command is expected to trigger the hosted SSO redirect again, pass the route header flags on that command too.

For agent workflows and other secret-bearing headers, prefer --route-header-file-env. The agent passes only an environment variable name; the CLI resolves that variable to a file path and reads the header value locally, so the token does not appear in the prompt, command line, or shell history.

phantomwright-cli `
  --route-header-url "https://msvacation.microsoft.com/**" `
  --route-header-url "https://login.microsoftonline.com/**/oauth2/v2.0/authorize*" `
  --route-header-file-env "x-ms-HostedSsoCredential: HOSTED_SSO_TOKEN_FILE" `
  open "https://msvacation.microsoft.com"

Passing both URL patterns covers the two common login shapes: an HTTP 302 from the app to AAD (the first-hop app URL triggers the route), and a later JavaScript/link/form navigation directly to AAD (the login URL triggers the route). All --route-header-url patterns share the same set of --route-header / --route-header-file / --route-header-file-env values; flag order does not group a URL with a specific header.

If you already have a literal non-secret value, use --route-header "Name: value". If you have a direct file path instead of an env var, use --route-header-file "Name: path". Relative paths (including those resolved from --route-header-file-env) are resolved against the directory where you run the CLI. File values have a single trailing newline (\n or \r\n) stripped; any other whitespace is preserved.

--route-header affects browser page requests. --cdp-header is different: it only affects the HTTP handshake used to connect to a remote CDP browser endpoint.

If you need a non-redirect destination server to observe the injected header on the same request, use --no-stealth; the stealth plugin changes first-hop header visibility while preserving the redirect login behavior above.

In --cdp-endpoint mode, route headers attach to the first browser context exposed by the remote browser at daemon connect time. Pages opened in other contexts of the same remote browser will not see the injected headers.


Remote browser via CDP

Use --cdp-endpoint <url> to attach to a Chromium that's already running (on this machine or another host) instead of letting the daemon launch one. The local daemon and session model are unchanged — only the browser is remote.

Start Chromium with CDP exposed on the host that owns the browser:

# On the host where the browser will run
msedge --remote-debugging-port=9222 --user-data-dir=/tmp/cdp-test about:blank

Then attach from any host (use an SSH tunnel for cross-host so the debug port stays off the public internet):

# From another machine: forward the remote CDP port over SSH first
ssh -L 9222:localhost:9222 user@remote-host

# Then drive the remote browser through the tunnel
phantomwright-cli --cdp-endpoint http://localhost:9222 open https://example.com
phantomwright-cli --cdp-endpoint http://localhost:9222 snapshot

For CDP endpoints sitting behind an auth proxy (Browserless, internal gateway, etc.), pass headers and/or raise the connect timeout:

# With auth header (repeat --cdp-header for multiple)
phantomwright-cli \
  --cdp-endpoint https://browser.example.com \
  --cdp-header "Authorization: Bearer $TOKEN" \
  --cdp-header "X-Tenant: acme" \
  open https://example.com

# Custom connect timeout (default 30000 ms, 0 = no timeout)
phantomwright-cli --cdp-endpoint http://localhost:9222 --cdp-timeout 60000 snapshot

--cdp-header and --cdp-timeout only apply in CDP mode — passing them without --cdp-endpoint is rejected rather than silently ignored.

Note for enterprise-managed browsers (Microsoft Edge with SSO / Purview)

When attaching to an enterprise-managed Edge for the first time on a fresh --user-data-dir, Edge may open a "Welcome to Microsoft Edge" sync-confirmation page (edge://welcome-new-device/), and your corporate account's SSO / compliance extensions (e.g. Microsoft Purview) may load in the background. phantomwright-cli will not close, dismiss, or interact with these pages — they belong to your browser session and may carry company policy. You can ignore them, or click Yes, continue / No, sign me out at your discretion; either choice is safe for automation.

If you want a clean automation session without these prompts, pre-warm the --user-data-dir (launch Edge once, complete the welcome flow, close it) before pointing phantomwright-cli at the CDP port.

Restrictions in CDP mode:

  • --persistent and --persistent-fresh are rejected — the remote browser manages its own user-data-dir.
  • profile-list, profile-select, and profile-info are disabled — they operate on a locally copied profile that does not exist for a remote browser.
  • When the remote browser exits or the connection drops, the daemon shuts down (same behavior as a local browser crash).

Security note: never expose --remote-debugging-address=0.0.0.0 directly on the public internet — anyone who reaches the port gets full control of the browser. Prefer SSH tunnels or a private network.


Use --persistent to carry login state across commands. On first use the CLI will prompt you to pick a system browser profile to copy.

# First run — prompts for profile selection
phantomwright-cli --persistent open https://github.com

# Subsequent runs — uses same login state
phantomwright-cli --persistent open https://github.com/notifications

# Fresh persistent context (no login state, but stable across calls)
phantomwright-cli --persistent-fresh open https://example.com

JSON output

Add --json to any command to get machine-readable output:

phantomwright-cli --json snapshot
phantomwright-cli --json open https://example.com

License

MIT