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

otterkit

v0.47.0

Published

OtterKit CLI - provision and connect tunnels for AI agents

Readme

otterkit

Instant tunnels and webhooks for AI agents. Pay per use with prepaid credits.

Quick Start

# One-time: log in (opens your browser)
npx otterkit login

# Create a tunnel
npx otterkit tunnel 3000

Buy credits at console.otterkit.com. Once you've logged in, agents on the same machine provision automatically using your credits.

Commands

Tunnel

Expose a local port to the internet.

npx otterkit tunnel <port>                    # Foreground (1 credit/hr)
npx otterkit tunnel <port> --log              # Also capture every request to a JSONL log
npx otterkit tunnel <port> --daemon           # Background, auto-stops after 24h
npx otterkit tunnel <port> --daemon --ttl 3d  # Background, auto-stops after 3 days
npx otterkit tunnel <port> --subdomain myapp  # Stable URL: https://myapp.otterkit.app
npx otterkit tunnel <port> --auth user:pass   # Require HTTP Basic auth on the public URL

--auth is enforced by the CLI on your machine, before anything reaches your local server - unauthenticated requests get a 401 and are never forwarded. Credentials are not sent to or stored by OtterKit's servers.

Stable URLs

By default each tunnel gets a fresh random URL (https://tunnel-a1b2c3d4.otterkit.app). Pass --subdomain <name> to get a stable, reserved URL instead. The name is claimed to your account the first time you use it and reused on every run after that - so the same public URL (https://myapp.otterkit.app) always points at your tunnel, across machines and restarts. It works on tunnel and webhook, foreground and --daemon.

Holding a name is free - you still only pay the normal 1 credit/hr while a tunnel is connected. If another account already owns the name you'll get a "taken" error and no credit is charged; pick another name or omit the flag for an auto name.

npx otterkit subdomains                 # List your reserved subdomains
npx otterkit subdomains reserve myapp   # Reserve a name without starting a tunnel
npx otterkit subdomains release myapp   # Release a name so it can be reused

Project Config (otterkit up)

Define named tunnel profiles in an otterkit.toml and bring them all up with one command. Every profile runs as a background daemon, so status, inspect, and stop work as usual.

# otterkit.toml
[tunnels.web]
port = 3000
subdomain = "myapp"          # stable URL (optional)
auth = "admin:s3cret"        # HTTP Basic auth (optional)
log = true                   # capture requests (optional)
ttl = "8h"                   # auto-stop (optional, default 24h)

[tunnels.hooks]
webhook = true               # capture-only endpoint, no local server
subdomain = "myapp-hooks"
respond = 200                # custom auto-response (optional)
respond_body = '{"ok":true}'
npx otterkit up                  # Start everything (skips profiles already running)
npx otterkit up --json           # Machine-readable results
npx otterkit down                # Stop the daemons started from the config
npx otterkit up --config ./deploy/otterkit.toml

Webhook

Capture incoming HTTP requests without a local server. Every request is saved to ~/.otterkit/requests/<subdomain>.jsonl so nothing is lost when the terminal closes.

npx otterkit webhook                           # Foreground (1 credit/hr)
npx otterkit webhook --daemon --ttl 4h         # Background, auto-stops after 4h

Some providers require a specific response before they deliver events (challenge echoes, strict 2xx checks). Customize the auto-response with --respond / --respond-body (default: 200 {"received":true}):

npx otterkit webhook --respond 204
npx otterkit webhook --respond 200 --respond-body '{"challenge":"accepted"}'

With --standby, the endpoint stays live while you're disconnected (laptop closed, daemon killed): the server answers each request with your auto-response and buffers up to 200 captures (64 KB bodies), replaying them into your local log on reconnect - providers never see downtime. Billing continues while disconnected (still capped at 10 credits/day; the session still auto-stops at its --ttl).

npx otterkit webhook --standby
npx otterkit webhook --daemon --standby --respond 204

Inspect

View requests captured by webhook or tunnel --log.

npx otterkit inspect <subdomain>               # Pretty-print last 20 requests
npx otterkit inspect <subdomain> --json        # Raw JSONL (pipe-friendly)
npx otterkit inspect <subdomain> --last 50     # Show last 50 requests
npx otterkit inspect <subdomain> --follow      # Live-tail new requests (Ctrl+C to stop)
npx otterkit inspect <subdomain> --method POST --status 5xx --path /webhook
npx otterkit inspect <subdomain> --har > session.har   # HAR 1.2 for devtools/HAR viewers

Filters (--method, --status, --path) combine and work with --json, --follow, and --har. --status accepts an exact code (500) or a class (5xx).

Replay

Re-send a captured request to your local server - same as the desktop app's replay, straight to the target with no tunnel round-trip. Great for re-testing webhook handlers after a fix without waiting for the provider to fire again. The replayed exchange is appended to the capture log, so it shows up in otterkit inspect.

The target defaults to the running daemon's host:port for that subdomain; pass --target for webhook captures or stopped tunnels.

npx otterkit replay <subdomain>                          # Re-send the latest request
npx otterkit replay <subdomain> --index 3                # Re-send request #3 (1 = oldest, -1 = latest)
npx otterkit replay <subdomain> --target 127.0.0.1:3000  # Explicit local target
npx otterkit replay <subdomain> --json                   # Machine-readable response (base64 body)

# Tweak the request before re-sending
npx otterkit replay <subdomain> --method PUT --path /v2/hook \
  -H "X-Debug: 1" --body '{"event":"payment.failed"}'

# Edit individual JSON body fields (repeatable; numbers/booleans parse as JSON)
npx otterkit replay <subdomain> --set data.amount=999 --set data.livemode=false

Capture logs record the full exchange - response status, headers, and body - so inspect --json and --har exports carry both sides of every request.

Await

Block until a matching request arrives - the scriptable primitive for CI and agents: "trigger the event, await its arrival, assert on the payload." By default it keeps streaming matches (your session keeps running either way - await never stops the tunnel it watches). Pass --count for a deterministic exit.

npx otterkit await <subdomain>                            # Stream every request (Ctrl+C to stop)
npx otterkit await <subdomain> --count 1 --timeout 120s   # CI: exit 0 on arrival, 2 on timeout
npx otterkit await <subdomain> --method POST --path /stripe --json

Exit codes: 0 when --count is satisfied (or on Ctrl+C), 2 when --timeout expires unmet. Filters (--method, --status, --path) match inspect's.

Continue a /try session

Started on otterkit.com/try? The page shows a claim command that moves the session into your terminal - same public URL, and everything already captured on the page is imported into your local log:

npx otterkit webhook --claim <code>   # code comes from the /try page

Send & Verify Events

Develop a webhook handler with no provider account, dashboard, or real event. send fires a correctly-signed synthetic event at your local handler; verify checks whether captured requests carry a valid signature. Both run locally - the secret never leaves your machine. Schemes: Stripe, GitHub, Shopify, Slack.

# Fire a signed event straight at your handler (passes its signature check)
npx otterkit send stripe:payment_intent.succeeded 127.0.0.1:3000/webhooks/stripe --secret whsec_...
npx otterkit send github:push 127.0.0.1:3000/hooks --secret ghs_...
npx otterkit send --list                              # all providers + events

# "Is my verification wrong, or the payload?" - answered against captured requests
npx otterkit verify myhooks stripe --secret whsec_...

# Replay a captured request with an edited body, re-signed so it still validates
npx otterkit replay myhooks --set data.object.amount=999 --resign stripe --secret whsec_...

MCP Server (for AI agents)

otterkit mcp runs a Model Context Protocol server over stdio - Claude Code, Cursor, and any MCP client get tunnels, webhooks, capture logs, await, and replay as native tools, authenticated by the same otterkit login.

# Claude Code
claude mcp add otterkit -- npx otterkit mcp
// Cursor & friends (.cursor/mcp.json)
{ "mcpServers": { "otterkit": { "command": "npx", "args": ["otterkit", "mcp"] } } }

Tools: webhook_create, tunnel_create, sessions_list, session_stop, requests_list, request_await, request_replay, event_send, request_verify, try_claim, account_status.

Account

npx otterkit login                             # Log in via browser
npx otterkit whoami                            # Show account + balance
npx otterkit balance                           # Show credit balance
npx otterkit logout                            # Remove the saved token

Manage Daemons

npx otterkit status                            # List running daemons
npx otterkit stop <subdomain>                  # Stop a daemon

JSON Output (for agents & scripts)

Every read/provision command takes --json and prints one machine-readable result:

npx otterkit tunnel 3000 --daemon --json   # {"subdomain":..., "publicUrl":..., "pid":...}
npx otterkit webhook --daemon --json
npx otterkit up --json
npx otterkit status --json
npx otterkit subdomains list --json
npx otterkit inspect <subdomain> --json
npx otterkit replay <subdomain> --json
npx otterkit whoami --json
npx otterkit balance --json

Errors in --json mode are also JSON ({"error":"insufficient_credits", ...}) with exit code 1.

Options

| Flag | Description | Default | | -------------------- | ------------------------------------------------------- | ------------------- | | --host <host> | Local host to forward to | 127.0.0.1 | | --log | Capture requests to a JSONL log (tunnel only) | off | | --daemon | Run in background | off | | --ttl <duration> | Auto-stop after duration, e.g. 4h, 3d (max 7d) | 24h | | --subdomain <name> | Use a stable reserved URL (claimed on first use) | auto | | --auth <user:pass> | Require HTTP Basic auth (tunnel only, enforced locally) | off | | --respond <status> | Webhook auto-response status (webhook only) | 200 | | --respond-body <d> | Webhook auto-response body (webhook only) | {"received":true} | | --claim <code> | Continue a /try page session (webhook only) | - | | --set <path=value> | Edit a JSON body field before replaying (repeatable) | - | | --count <n> | await: exit 0 after N matches | stream forever | | --timeout <dur> | await: give up after e.g. 30s, 5m (exit 2) | none | | --json | Machine-readable output (daemon/read commands) | off |

Pricing

Metered by connected time. 1 credit = $0.01. Buy prepaid credits in the console.

| What | Cost | | -------------------- | ------------------------------------------ | | Per connected hour | 1 credit ($0.01), first hour up front | | Daily cap per tunnel | 10 credits ($0.10) - hours beyond are free | | While disconnected | Free - billing pauses | | Auto-stop | Default 24h, --ttl up to 7d |

A forgotten tunnel bills at most the daily cap, then auto-stops at its TTL.

How It Works

  1. otterkit login runs a browser device-flow and saves an API token to ~/.otterkit/credentials.json.
  2. The CLI sends that token when provisioning; the server debits your credit balance.
  3. Server provisions a public URL like https://tunnel-a1b2c3d4.otterkit.app (or your reserved --subdomain name, if given).
  4. CLI connects via WebSocket and forwards traffic to your local port.

For headless/CI, set OTTERKIT_TOKEN (create a token at console.otterkit.com) instead of otterkit login.

Links

License

MIT