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

junso-browser

v0.3.0

Published

Standalone CloakBrowser host — runs fingerprinted stealth-Chromium per profile, exposes each over a token-authed CDP gateway + interactive viewport, and ships an optional MCP server so any client gets browser tools with no local browser.

Readme

junso-browser

A standalone CloakBrowser host. It runs one stealth-Chromium instance per profile, each with its own deterministic device fingerprint + proxy + cookies, and exposes each over a token-authed CDP gateway. Drive it with anything that speaks CDP — agent-browser, Playwright, Puppeteer — via --cdp.

Built for Jun: Jun keeps its whole tool surface and just connects to a junso-browser profile's CDP URL, gaining real per-profile device identities (the gap a shared local browser can't close).

Why

A fresh profile gives new cookies + storage; a proxy gives a new IP — but on one machine every profile shares the same device fingerprint (canvas/WebGL/timezone/fonts/TLS), so a site can still link them. CloakBrowser derives a full, consistent fingerprint from a seed (same seed = same device; different seed = different device). junso-browser runs one seeded instance per profile and hands out its CDP endpoint.

How it works

client (agent-browser --cdp wss://host/cdp/<profile>?token=…)
        │  WS relay + /json discovery (token-authed gateway)
        ▼
junso-browser ──spawn──> cloak chrome  --fingerprint=<seed> --fingerprint-platform=…
                          --proxy-server=…  --user-data-dir=…  --remote-debugging-port=0
  • No Playwright at runtime — the cloak binary applies the fingerprint from --fingerprint* flags (--headless=new); junso-browser is a thin process supervisor that spawns it and proxies CDP.
  • The cloak CDP binds 127.0.0.1 with no auth; junso-browser is the only thing that touches it and fronts it with a token gateway.

Install & run

bun i -g junso-browser                                    # installs the host + MCP bins
JUNSO_BROWSER_TOKEN=secret JUNSO_BROWSER_HOST=0.0.0.0 junso-browser   # start the host (:8790)

The CloakBrowser binary (~200 MB) auto-downloads on first run (opt out: JUNSO_BROWSER_NO_CLOAK_DOWNLOAD=true). Runs on Bun (Bun.serve/Bun.spawn).

The package ships two bins: junso-browser (the host) and junso-browser-mcp (the optional MCP — see below). From source: bun install then bun run start / bun run mcp. Build: bun run build (bundle dist/) or bun run build:binary (single compiled binary).

Config (env)

| Var | Default | | |---|---|---| | JUNSO_BROWSER_PORT | 8790 | listen port | | JUNSO_BROWSER_HOST | 127.0.0.1 | listen addr (token required if non-loopback) | | JUNSO_BROWSER_TOKEN | — | shared secret for API + CDP | | JUNSO_BROWSER_DATA_DIR | ~/.junso-browser | profiles.json + user-data dirs | | JUNSO_BROWSER_MAX_INSTANCES | 3 | concurrent cloak instances (LRU-reaped) | | JUNSO_BROWSER_IDLE_MS | 1800000 | reap an instance after this idle | | JUNSO_BROWSER_PUBLIC_URL | — | base for handed-out CDP URLs (e.g. wss://browser.example.com) | | JUNSO_BROWSER_CLOAK_PATH | auto | override the binary path |

API

All /api/* require the token (Authorization: Bearer …, x-junso-token, or ?token=).

| Method | Path | | |---|---|---| | GET | /health | status + cloak presence + running count | | GET | /api/profiles | list (proxy masked) + running flag | | POST | /api/profiles | { name, fingerprint?: { seed?, platform?, timezone?, locale? }, proxy? } | | GET/PATCH/DELETE | /api/profiles/:id | read / update (incl. rotateSeed) / delete | | POST | /api/profiles/:id/launch | start instance → { cdpUrl } | | POST | /api/profiles/:id/stop | stop instance | | GET | /api/profiles/:id/cdp | { cdpUrl } (auto-launches on connect) | | WS/GET | /cdp/:id | CDP gateway (ws relay + /cdp/:id/json* discovery) |

Interactive viewport

Open http://<host>:8790/view/<profile>?token=<token> in any browser to watch and take control of a profile's browser — live screencast + your mouse/keyboard forwarded over CDP. Use it for human-in-the-loop steps (log in by hand, solve a CAPTCHA, check something) on the same fingerprinted profile the agent drives via MCP. CDP supports multiple clients, so the viewport and the MCP coexist. (Frames stream on repaint — a static page shows one frame until you interact.)

Optional browser MCP

junso-browser ships the same browser MCP over two transports — the way to give any MCP client (Jun, Codex, Claude Desktop) browser tools without a local browser. It drives profiles through the host's CDP gateway, so it works whether junso-browser is local or remote.

HTTP transport — no local install (recommended)

Since 0.3.0 the host exposes the MCP at POST /mcp (Streamable HTTP), so a client adds junso-browser as a plain URL with nothing installed locally. Token-authed via Authorization: Bearer <token> (also accepts x-junso-token: <token> or ?token=<token> in the URL).

The auth header config differs per client — this is the #1 setup gotcha:

Claude Code (.mcp.json at the repo root, or ~/.claude.json for user scope) — uses a headers object:

{
  "mcpServers": {
    "junso-browser": {
      "type": "http",
      "url": "http://<host>:8790/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}

Codex / Jun (config.toml) — uses http_headers (NOT headers — Codex silently ignores a headers block, connects with no auth, gets a 401, and hangs at startupState:"starting"):

# may also require this at the top level on some builds:
experimental_use_rmcp_client = true

[mcp_servers.junso-browser]
url = "http://<host>:8790/mcp"

[mcp_servers.junso-browser.http_headers]
Authorization = "Bearer <token>"

Codex also supports bearer_token_env_var = "MY_TOKEN_ENV" and env_http_headers to source the token/headers from env vars instead of hardcoding.

Common to all clients:

  • Append ?profile=<id> to the URL to set the session's initial profile (else default, auto-created).
  • Behind the Caddy TLS front, use https://<host>:8443/mcp (or whatever subdomain reverse-proxies to 127.0.0.1:8790).
  • The MCP runs in-process on the host, one isolated server per session (Mcp-Session-Id header), idle-reaped after 10 min. A plain GET /mcp405 (the bridge is request/response only, no server→client SSE) — that's expected, not an error.
  • browser_solve_captcha's 2captcha key is supplied by the connector: x-twocaptcha-key header or ?twocaptcha= (keeps the secret with the client).

Troubleshooting: if the client hangs on "loading" / shows a "Connect"/OAuth button / sits at authStatus:"unsupported", the token isn't reaching the host (wrong header key — see Codex http_headers above) → it 401s and never completes the handshake. Verify the host independently with a raw POST:

curl -i -X POST http://<host>:8790/mcp \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

A working host returns 200 with an mcp-session-id header and serverInfo: junso-browser. If curl works but the client doesn't, the problem is the client's auth-key syntax, not the host. Also confirm you're hitting an actual junso-browser host: GET /health returns JSON {"name":"junso-browser",…} — if it returns HTML, that hostname is serving a different app (e.g. the Jun web UI) and junso-browser isn't routed there.

stdio transport — junso-browser-mcp bin

Add it to a client's MCP config (once junso-browser is installed globally):

{
  "command": "junso-browser-mcp",
  "env": { "JUNSO_BROWSER_URL": "https://browser.example.com:8790", "JUNSO_BROWSER_TOKEN": "secret", "JUNSO_BROWSER_PROFILE": "default" }
}

(or "command": "bunx", "args": ["-y", "junso-browser-mcp"] without a global install).

Tools

Drive: browser_status, browser_navigate, browser_snapshot, browser_click, browser_fill, browser_type, browser_press, browser_select, browser_scroll, browser_wait, browser_read, browser_eval, browser_screenshot, browser_exec. Identity: browser_list_profiles, browser_switch_profile, browser_create_profile. Proxy/IP: browser_get_proxy, browser_set_proxy, browser_exit_ip, browser_rotate_proxy, browser_probe_ip. CAPTCHA: browser_solve_captcha.

The active profile (env JUNSO_BROWSER_PROFILE for stdio / ?profile= for HTTP, default default) is auto-created on the host if missing; browser_switch_profile changes identity at runtime. browser_exit_ip returns the live exit IP + geo health (kind, tzMismatch); browser_rotate_proxy draws a fresh sticky IP and re-syncs the fingerprint — the host carries standing login instructions so clients follow the residential-IP → health-check → one-attempt-per-IP recipe automatically.

Status

Phase 1 MVP — verified e2e (profile → fingerprinted cloak → external CDP client sees the configured fingerprint, deterministic per seed). Next: Jun integration (opt-in JUN_BROWSER_CDP backend), single-binary deploy, optional fingerprint personas.