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

@nebulr-group/bridge-cli

v0.6.4

Published

The Bridge CLI: set up and manage sign-in, roles, plans, feature flags and API tokens from your terminal or AI coding agent.

Readme

The Bridge CLI

@nebulr-group/bridge-cli sets up and manages your Bridge app from the terminal: apps, sign-in methods, roles, plans and prices, feature flags and API tokens. Built for AI coding agents (Claude Code, Cursor, Copilot) as much as for people.

The Bridge is a hosted backend for SaaS apps. It gives you sign-in (passwords, magic links, passkeys, social login and SSO), multi-tenant workspaces with roles, Stripe subscriptions with plan limits, and feature flags, all managed from one dashboard. Your AI coding assistant can set it up for you through the Bridge MCP server.

Installation

npm install -g @nebulr-group/bridge-cli

Or run directly with npx:

npx @nebulr-group/bridge-cli tenant list

Authentication

bridge-cli supports two authentication paths.

1. Interactive (recommended for humans + AI agents) — bridge auth login

bridge auth login

Opens your default browser, runs through a PKCE-secured loopback flow (RFC 8252), and stores a 10-day token at ~/.config/bridge/credentials.json (mode 0600). After that, every subsequent bridge command picks the token up automatically — no env vars needed.

bridge auth status   # show every stored app, and which one is active
bridge auth logout   # revoke the active token and remove it locally

One login, every app of the workspace

By default the consent screen preselects Every app in <workspace>, so one login covers all your apps — the same way the MCP server does. The app you pin at login (--app) is the home app; switch between the others locally:

bridge auth login                         # Logged in as [email protected]. Workspace: every app (home app: Acme)
bridge app list                           # every app, marking home and current
bridge app use "Acme Stage"               # the default for every command, no re-login
bridge app create --name "Acme Prod" --use
bridge --app 66f0c0ffee0000000000abcd role list   # one command on another app

Precedence: --app > bridge app use > the home app. --profile still picks which stored login; --app picks the app within it. Acting on a non-home app prints bridge: Acting on app <name> (<id>) to stderr. Per-app tokens live only in memory for that one command and are never written to disk.

bridge auth login --single-app asks for a login that covers only the home app (the user can still change the choice on the consent screen). On such a login, --app / bridge app use for another app fail and tell you to log in again. BRIDGE_API_KEY is always bound to its one app.

Several apps at once

The credentials file holds one credential per app, so you can log in to your local, stage and prod apps and move between them without a browser round-trip — the tokens are already on disk with days left on them.

bridge auth login --label northwhistle-local     # once per app
bridge auth login --label northwhistle-prod

bridge auth status                               # lists both, marks the active one
bridge auth use northwhistle-local               # switch the default, offline
bridge --profile northwhistle-prod app get       # target ONE command, default untouched

A profile is selected by its --label, its app id, or its app name, in that order. An ambiguous name is an error rather than a guess. BRIDGE_PROFILE does the same thing as --profile for a whole shell session.

Prefer --profile over bridge auth use in scripts and long-running agent sessions: use mutates state another process may be reading, --profile does not.

Every command prints one line to stderr naming the app that answered:

bridge: northwhistle-prod (606b4416fabdc800087d09ec) · https://api.thebridge.dev · via saved default

stdout stays pure JSON, so this does not disturb anything parsing it. Silence it with BRIDGE_NO_BANNER=true if you are already certain which app you are on.

bridge auth logout removes the active app only. Use --profile <name> for a specific one, or --all to clear the file.

Existing single-app credentials files keep working and are upgraded in place the next time something writes.

bridge auth login flags

| Flag | Description | |------|-------------| | --app <id\|name> | Pin the home app, skipping the picker on the consent screen. | | --single-app | Preselect "only this app" instead of "every app in the workspace" on the consent screen. | | --label <text> | Friendly label stored on the token (default: bridge-cli). Useful when listing CLI tokens at app.thebridge.dev/keys. | | --no-browser | Print the authorization URL instead of opening a browser. Use this on headless boxes or over SSH. |

When the token expires (10 days), the next command fails with a friendly Token expired. Run bridge auth login to re-authenticate. message.

The credentials file location honors XDG_CONFIG_HOME:

$XDG_CONFIG_HOME/bridge/credentials.json   # if XDG_CONFIG_HOME is set
~/.config/bridge/credentials.json          # otherwise

2. Service-account / CI — BRIDGE_API_KEY

For non-interactive contexts (CI/CD pipelines, Docker images, headless agents) set the API key directly in the environment:

export BRIDGE_API_KEY=<your-api-token>

If you have logged in via bridge auth login, the credentials file always wins — a fresh login takes effect immediately, even when BRIDGE_API_KEY is still exported in your shell. BRIDGE_API_KEY is only used when no credentials file is present (the typical CI runner shape). To switch back to env-var auth on a developer machine, run bridge auth logout --all first.

Because that trips people up, the CLI now says so at the moment it happens: with both present you get a line on stderr telling you BRIDGE_API_KEY is set and being ignored, and what to do instead. To point the CLI at a different app, use --profile, not the env vars.

BRIDGE_APP_ID has no effect on any path. The app is carried by the credential itself — a login token and an API key are both already scoped to one app, so there is nothing for it to change. It is called out on stderr whenever it is set, rather than being quietly ignored.

Optional configuration (applies to both auth paths):

export BRIDGE_BASE_URL=https://api.thebridge.dev   # default
export BRIDGE_TENANT_ID=<tenant-id>                 # for user commands
export BRIDGE_DEBUG=true                            # enable debug logging

3. Targeting a local stack

Two exports, then log in as usual — the browser flow works against a local stack the same way it does against production:

export BRIDGE_BASE_URL=http://localhost:3200        # bridge-api
export BRIDGE_AUTH_BASE_URL=http://localhost:3091   # consent screen (cloud-views)

bridge auth login

BRIDGE_BASE_URL is where commands send their API calls; BRIDGE_AUTH_BASE_URL is where bridge auth login opens the consent screen. Set the second one too, or you will authenticate against production and then issue commands against your local API with a token it does not accept.

There is deliberately no config file to source (TBP-121). A sourceable file mixed CLI settings with two demo apps' settings, pinned an undocumented app id, and kept a long-lived API key on disk in plaintext; two exports cover the same ground without any of that. For CI, use BRIDGE_API_KEY as described above.

One more, for working on the integration guides themselves:

export BRIDGE_GUIDE_LOCAL_DIR=/path/to/thebridge-platform/bridge-plugins

bridge guide then reads prompts from that directory instead of fetching them from GitHub (or the copies bundled with the CLI), so edits show up without a release.

Usage

All output is JSON by default for management commands. AI agents parse it directly; humans can pipe through jq. The bridge auth status and bridge auth login commands print human-readable text, since their primary audience is a human in a terminal.

bridge <command> <subcommand> [options]

Addressing a resource

Every command that acts on a single resource takes either its opaque id or its human-readable identifier — you never have to run a list first just to look up an id.

| Noun | Human-readable option | Id option | |---|---|---| | flag | --key <key> (or a positional <key> on get / eval / schedule) | --id | | role | --key <key> (or a positional <idOrKey> on get) | --id | | plan | positional <key> — plans are key-native, no id anywhere | — | | tenant | --name <name> | --id | | user | --email <email> | --user-id | | token | --name <name> | --id |

Rules, the same for every noun:

  • Pass exactly one. Both together, or neither, is an error.
  • An identifier that matches nothing fails and names the list command to run.
  • An identifier that matches more than one resource fails, lists the candidates, and changes nothing. The CLI never picks one for you — pass --id to disambiguate.

--name and --email exist because tenants, users and tokens have no key field. Of those, only a user's email is unique; tenant and token names are free text, which is exactly why the ambiguity check is a hard failure.

Auth (interactive credentials)

bridge auth login                # default: open browser, complete PKCE flow
bridge auth login --app acme     # pin to a specific app
bridge auth login --label "work laptop"
bridge auth login --no-browser   # print the URL (headless / SSH)
bridge auth status               # show every stored app, active one marked
bridge auth use <label|app-id>   # switch the default app, no browser
bridge auth logout               # revoke + remove the active app
bridge auth logout --all         # ...or every stored app
bridge --profile <label> <cmd>   # target one stored login for one command
bridge app list                  # apps this login covers (home + current marked)
bridge app use <id|name>         # switch app within a workspace login, no browser
bridge app create --name <n> [--callback-url <url>] [--use]
bridge --app <id|name> <cmd>     # one command on another app of the workspace

Auth Configuration (app-level — separate from auth login)

bridge auth config
bridge auth mfa --enabled true
bridge auth password-policy --access-token-ttl 3600

App

bridge app get
bridge app update --name "My App" --mfa-enabled true

Tenants

bridge tenant list
bridge tenant get --name "Acme Corp"              # or --id <tenant-id>
bridge tenant create --owner-email [email protected] --name "Acme Corp" --plan enterprise
bridge tenant update --name "Acme Corp" --locale sv
bridge tenant update --name "Acme Corp" --new-name "Acme Corp Updated"
bridge tenant delete --name "Acme Corp"           # or --id <tenant-id>

Tenants have no key field, so they are addressed by --name. Names are not enforced unique — if two tenants share one, the command fails and lists both rather than guessing. Use --id to disambiguate.

tenant update --name addresses the tenant when --id is absent; passed alongside --id it keeps its original meaning and renames the tenant. --new-name always renames, whichever way you addressed it.

Users

Requires tenant context via --tenant-id or BRIDGE_TENANT_ID.

bridge user list --tenant-id <tenant-id>
bridge user get --email [email protected] --tenant-id <tenant-id>      # or --user-id <user-id>
bridge user invite --email [email protected] --role ADMIN --tenant-id <tenant-id>
bridge user update --email [email protected] --role OWNER --tenant-id <tenant-id>
bridge user remove --email [email protected] --tenant-id <tenant-id>

Users have no key field either; --email is the human-readable address and is unique within a tenant (matched case-insensitively).

Access Roles

bridge role list
bridge role get ADMIN
bridge role create --name Editor --key editor --privileges READ,WRITE
bridge role update --key editor --privileges READ,WRITE,DELETE     # or --id <role-id>
bridge role delete --key editor                                    # or --id <role-id>

Feature Flags

bridge flag list
bridge flag create --key dark-mode --description "Dark mode UI" --enabled
bridge flag update --key dark-mode --state on                      # or --id <flag-id>
bridge flag update --key dark-mode --new-key dark-theme            # rename
bridge flag toggle --key dark-mode --enabled true                  # or --id <flag-id>
bridge flag delete --key dark-mode                                 # or --id <flag-id>

flag update --key addresses the flag when --id is absent; passed alongside --id it keeps its original meaning and renames the flag. --new-key always renames, whichever way you addressed it.

Branding

bridge branding get
bridge branding update --bg-color "#ffffff" --text-color "#000000"

Subscription Plans

bridge plan list
# A plan needs at least one price — `create` takes the first one.
# A free tier is a zero-amount price: --amount 0
bridge plan create --key pro --name "Pro Plan" --amount 29 --interval month
bridge plan update --key pro --name "Pro Plan v2"

# Prices — a plan can have several (one per currency + interval). Idempotent.
bridge plan price set pro --amount 290 --currency usd --interval year
# Removing the last price is refused: a priceless plan can't be assigned.
bridge plan price rm  pro --interval year

# Usage quotas (hard | metered caps)
# hard = block at the cap; metered = bill overage per unit (needs --price-amount)
bridge plan quota set pro --metric ai_completions --limit 1000 --policy hard
# Metered: first 1000 free, then $0.002/unit (currency from the plan's price)
bridge plan quota set pro --metric ai_completions --limit 1000 --policy metered --price-amount 0.002
# Pure per-unit (billed from unit 1)
bridge plan quota set pro --metric api_calls --limit 0 --policy metered --price-amount 0.01 --price-currency usd
bridge plan quota rm  pro --metric ai_completions

# Included on/off features — each is `bridge:billing.entitlement.<key>` in flag rules
bridge plan feature add    pro analytics --name "Analytics"
bridge plan feature list   pro
bridge plan feature remove pro analytics
bridge plan create --key team --name Team --amount 0 --interval month --features "analytics,sso:Single sign-on"

API Tokens

bridge token list
bridge token create --name "CI Token" --privileges USER_READ,TENANT_READ
bridge token revoke --name "CI Token"             # or --id <token-id>

Token names are free text and not enforced unique — a duplicate name fails and lists the candidates instead of revoking one at random.

Events

bridge event list
bridge event list --type USER_CREATED --since 24h --limit 50

Setup Workflows (multi-step)

bridge setup sso --provider google --client-id <id> --client-secret <secret>
bridge setup sso --provider azure --client-id <id> --client-secret <secret> --tenant-id <entra-directory-id>
bridge setup payments --stripe-key sk_test_xxx
bridge setup communication --from-address [email protected] --from-name "Acme"

setup sso supports google, github, linkedin, facebook and azure (azure also needs the Entra ID Directory (tenant) ID). SAML and OIDC are not supported by the Bridge API; set them up in the Bridge admin UI. The command prints the callback URL to register in the provider's console.

setup communication sets only the sender name and address. Bridge sends all email through its own provider, so there is no provider or API key to configure. A new sender address is verified by email before Bridge uses it.

Info (context for AI agents)

bridge info app
bridge info auth-config
bridge info flags
bridge info plans
bridge info roles

Integration Guides

Three master prompts orchestrate the per-framework guides:

bridge guide              # Bridge Auth master prompt (auth, RBAC, tenants)
bridge guide flags        # Feature Flags 2.0 master prompt
bridge guide billing      # Billing 2.0 master prompt (subscriptions, quotas, webhooks)

One page explains the mechanisms all of them build on — the server decides limits and the client decorates, a POST increments the limit, counter vs gauge, the three ways to show a limit in the UI, and the four levels of customising Bridge's pages:

bridge guide mechanisms

Per-framework guides:

bridge guide list
bridge guide <framework>                    # auth guide (svelte | react | nextjs | angular | nestjs | express)
bridge guide <framework> sdk-auth           # in-app auth UI (frontend frameworks only)
bridge guide flags --framework <name>       # flags-specific guide
bridge guide billing --framework <name>     # billing-specific guide
bridge guide custom                         # REST API fallback for any language

bridge guide flags and bridge guide billing auto-detect the framework from package.json when --framework is omitted — they fall back to the generic master prompt if no framework signal is found.

Output Format

Success:

{
  "success": true,
  "data": { ... }
}

Error:

{
  "success": false,
  "error": {
    "code": "TENANT_HAS_ACTIVE_SUBSCRIPTION",
    "message": "Cannot delete tenant with active subscription.",
    "details": { ... }
  }
}

bridge auth login, bridge auth logout, and bridge auth status print plain text (they're the only commands aimed primarily at humans).

Exit Codes

| Code | Meaning | |------|---------| | 0 | Success | | 1 | Client error (4xx), or bridge auth login failed/cancelled | | 2 | Server error (5xx) | | 3 | Configuration error (missing API key, expired credentials, etc.) |

Security notes

  • bridge auth login uses RFC 8252 loopback PKCE — the loopback URL is always http://127.0.0.1:<random-port>/callback (never localhost, to avoid DNS spoofing).
  • The loopback HTTP server handles a single request, then closes — no port stays bound.
  • The CSRF state parameter is verified on the callback before the code is exchanged.
  • The credentials file is written with mode 0600 (owner-only). Its parent directory is created with mode 0700.
  • The CLI never logs the JWT or the PKCE code_verifier.

Development

# Install dependencies
npm install

# Build
npm run build

# Run locally
node bridge-cli/dist/bin.js --help

# Test
npm test

Learn more

Other Bridge packages

| Package | For | |---|---| | @nebulr-group/bridge-svelte | SvelteKit | | @nebulr-group/bridge-react | React | | @nebulr-group/bridge-nextjs | Next.js | | @nebulr-group/bridge-angular | Angular | | @nebulr-group/bridge-nestjs | NestJS | | @nebulr-group/bridge-express | Express | | @nebulr-group/bridge-auth-core | Any JavaScript app (core) |

License

MIT © Nebulr. Built by The Bridge.