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

@neolifehealth/cli

v0.0.2

Published

neolife developer CLI — the human-in-terminal front door to the fulfillment rail (login, keys, orders, catalog, signed webhook testing).

Downloads

337

Readme

@neolifehealth/cli

The neolife CLI — the human-in-terminal front door to the neolife fulfillment rail. A clinic's developer uses it to pair a key, mint machine keys, list orders, browse the catalog, and (the headline features) stream live events to a local receiver and fire a correctly signed test event at one — no relay service, public URL, or tunnel needed.

It is the terminal complement to the agent MCP surface (@neolifehealth/mcp) and the SDKs (@neolifehealth/sdk, Python). All three talk to the same HTTP API and inherit the same non-negotiable compliance invariants:

  • Default mode is SANDBOX. Sandbox keys (nk_sandbox_… / rk_sandbox_…) resolve only to synthetic data. The CLI never operates in live by accident and prints a persistent SANDBOX / LIVE banner on every command.
  • There is no --submit. Ever. orders create produces a draft only. An unapproved clinical order can only reach a pharmacy after a licensed provider approves it, and that gate is enforced server-side. A terminal is never a compliance backdoor.
  • No PHI touches the CLI. Webhook payloads are PHI-free by contract (ids, status, shipping metadata, reason codes — never a name, address, DOB, drug, or intake answer).

Contents

Install

# From the monorepo (recommended while unpublished):
pnpm --filter @neolifehealth/cli build
node packages/cli/dist/index.js --help

# Or, once published:
npm i -g @neolifehealth/cli
neolife --help

The package declares a neolife bin (dist/index.js), so a global install puts neolife on your PATH. All examples below use neolife; substitute node packages/cli/dist/index.js when running from a local build. Requires Node 18+ (native fetch and async-iterable response bodies).

Quickstart

# 1. Log in with a sandbox key (mint one at POST /developer/keys or `neolife keys create`).
#    Verifies the key against GET /developer/keys and stores it in ~/.neolife/config.json.
neolife login --key nk_sandbox_xxxxxxxxxxxxx
#    …or pipe it:   echo nk_sandbox_xxx | neolife login

# 2. See where you're pointed (masked key, mode, base URL, webhook secret).
neolife config

# 3. Mint another key.
neolife keys create --name "shopify-integration" --mode sandbox --type secret
neolife keys list

# 4. Look at orders and the catalog.
neolife orders list
neolife orders get ord_123 --json
neolife catalog list

# 5. Draft (never submit) an order.
neolife orders create --items '[{"productId":"prod_x","quantity":1}]'

# 6a. Stream this tenant's live events to a local receiver — like `stripe listen`.
neolife listen --forward-to http://localhost:3000/webhooks

# 6b. Or fire ONE correctly-signed test event at your receiver — no relay needed.
neolife trigger order.shipped \
  --forward-to http://localhost:3000/webhooks \
  --secret whsec_your_signing_secret

How it fits together

flowchart LR
  subgraph terminal["your terminal"]
    CLI["neolife CLI"]
  end
  subgraph api["neolife API (default https://api.neolife.health)"]
    KEYS["/developer/keys"]
    ORD["/orders"]
    PROD["/products"]
    SSE["/developer/events/stream (SSE)"]
  end
  RECV["your local webhook\nreceiver"]

  CLI -- "Bearer nk_… key" --> KEYS
  CLI -- "Bearer nk_… key" --> ORD
  CLI -- "Bearer nk_… key" --> PROD
  CLI -- "listen: GET SSE" --> SSE
  SSE -- "PHI-free event frames" --> CLI
  CLI -- "trigger / listen:\nStandard-Webhooks-signed POST" --> RECV

login, keys, orders, and catalog are ordinary authenticated REST calls (Authorization: Bearer <key>). listen opens the server's SSE stream and re-signs each event before forwarding it; trigger skips the server entirely and signs a synthetic event locally. Both signing paths produce byte-for-byte identical output to the server signer, so an event tested locally verifies against the same receiver code a production event would.

Commands

| Command | What it does | |---|---| | neolife login [--key <k>] | Store an API key (flag, stdin, or global --key); verify it against GET /developer/keys; warn loudly on a LIVE key. --profile <name> picks the profile to store under (default default). | | neolife config | Show the current profile — masked key, mode, base URL, webhook secret, config path, and the list of known profiles. | | neolife keys list | List the tenant's machine keys over /developer/keys. --json. | | neolife keys create | Mint a key. --name (required), --mode sandbox\|live (default sandbox), --type secret\|restricted (default secret), --scopes <a,b> (restricted only). Plaintext key is shown once. | | neolife keys revoke <id> | Revoke a key by id. | | neolife orders list | List orders. --status, cursor pagination --limit / --starting-after, --json. | | neolife orders get <id> | Fetch one order. --json for the full object. | | neolife orders create | Draft only — no --submit, by design. --items <json> or --file <path>. | | neolife catalog list | List products over GET /products. --json. | | neolife trigger <event> --forward-to <url> | Fire one signed test event from the PHI-free catalog at a local receiver. --secret whsec_…, --data <json> to override the sample payload, --json. Rejects unknown events. | | neolife listen --forward-to <url> | Stream this tenant's live events over SSE and forward each, Standard-Webhooks-signed, to a receiver (stripe listen-style). --secret whsec_… (else generated), --events <comma,list>, --json. | | neolife open [orders\|docs] | Open the ops console (/orders) or the docs site in your browser. | | neolife --version / --help | Version and help. |

Global flags

  • -p, --profile <name> — config profile to use (default: current profile).
  • --base-url <url> — override the API base URL for one call (default https://api.neolife.health; use http://localhost:4000 for a local stack).
  • --key <key> — override the API key for one call (nk_sandbox_… / nk_live_… / rk_…).

Key format

Keys are Stripe-style and self-describing — the CLI reads the mode straight from the prefix, so the SANDBOX / LIVE banner never depends on a network round-trip:

| Prefix | Type | Mode | |---|---|---| | nk_sandbox_… | secret | sandbox (synthetic data only) | | nk_live_… | secret | live (real, PHI-scoped tenant) | | rk_sandbox_… | restricted | sandbox | | rk_live_… | restricted | live |

trigger — signed local webhook testing

neolife trigger builds the exact event envelope the server emits — { id, type, createdAt, data } — and signs it per Standard Webhooks:

signature = "v1," + base64( HMAC-SHA256( whsecBytes, `${id}.${timestamp}.${payload}` ) )

webhook-id:        msg_…            (stable across retries — receiver dedupe key)
webhook-timestamp: <unix seconds>   (receivers reject outside ±5 min → replay protection)
webhook-signature: v1,<sig>         (space-separated list allows key rotation)

The signing secret is whsec_<base64>; the base64 part is the raw HMAC key. This byte-for-byte matches the server signer at apps/api/src/modules/dev-webhooks/webhook-signer.ts, so an event fired locally verifies against the same receiver code a production event would. Only events in the PHI-free catalog are accepted:

intake.submission.created  intake.certificate.issued  intake.review.approved
intake.review.rejected     order.routed  order.submitted  order.accepted
order.shipped              order.delivered  order.rejected  refill.followup_required

Each event ships with a representative PHI-free sample payload (ids, status, shipping metadata, reason codes only). Override it with --data '<json>' — but keep it PHI-free. The signing secret comes from --secret whsec_… or the profile's webhookSecret in ~/.neolife/config.json.

listen — stream live events to your receiver

neolife listen is the stripe listen of the fulfillment rail. Where trigger fires one synthetic event, listen opens the server's live event stream and forwards every real event your tenant produces to a local receiver — so you can exercise a real webhook handler end-to-end without a public URL or a tunnel.

neolife listen \
  --forward-to http://localhost:3000/webhooks \
  --secret whsec_your_signing_secret \
  --events order.shipped,order.delivered   # optional filter; default: all events

How it works:

  1. Stream. It opens GET {baseUrl}/developer/events/stream (Server-Sent Events) with your stored key as Authorization: Bearer <key>. Each data: frame is one PHI-free event { id, type, createdAt, data }. A tiny built-in SSE parser handles multi-line data: frames and ignores : keepalive comments; the connection stays open.
  2. Filter. If --events is given, events whose type isn't in the list are skipped (each value is validated against the PHI-free catalog, so a typo fails loudly before the stream opens).
  3. Sign. Each forwarded event is re-signed with the same Standard-Webhooks signer as trigger (webhook-id / webhook-timestamp / webhook-signature: v1,<base64 HMAC>), so it verifies against the identical receiver code a production event would.
  4. Forward. It POSTs the signed envelope to --forward-to (default http://localhost:3000/webhooks) and prints one line per event: timestamp, event type, → the forward URL, and the receiver's HTTP status. A receiver connection error is reported without crashing the stream. --json prints one JSON object per event instead.

No --secret? Like stripe listen, it generates a whsec_… secret and prints it up front — configure your receiver to verify with that value:

Signing with a generated secret: whsec_… — configure your receiver to verify with this

Robustness. If the stream drops it reconnects with capped backoff (1s → 2s → 5s → 10s → 15s); a successful reconnect resets the counter, so a long-lived stream survives transient drops. After a run of consecutive failures with no successful read it gives up with a clear message. 401/403 is treated as terminal (a bad or unpermitted key won't fix itself). Ctrl-C exits cleanly. A persistent SANDBOX / LIVE banner prints like every other command, with a loud extra warning when you listen on a LIVE key.

Like the rest of the CLI, listen handles no PHI — the streamed events are PHI-free by contract.

Config & profiles

Config is stored at ~/.neolife/config.json (dir 0700, file 0600 — it holds a secret key and is never world-readable), one entry per named profile:

{
  "currentProfile": "default",
  "profiles": {
    "default": {
      "apiKey": "nk_sandbox_…",
      "baseUrl": "https://api.neolife.health",
      "webhookSecret": "whsec_…"
    }
  }
}

Keys and secrets are always masked when displayed (self-describing prefix + last 4). Profiles let a dev hold a sandbox key and (rarely) a live key side by side and switch per-command with -p/--profile:

neolife login --key nk_sandbox_xxx --profile default
neolife login --key nk_live_xxx    --profile prod     # stored, but never the default
neolife orders list --profile prod                    # explicit opt-in to live

If no config file exists yet, the CLI falls back to a default profile pointed at https://api.neolife.health — so --base-url / --key work even before you log in.

Errors & exit codes

API failures surface the server error envelope ({ error: { type, code, message, request_id }, message }, see docs/prd/DEVELOPER-PLATFORM.md) with the request_id for support. Common cases:

  • No key configuredRun neolife login first (or pass --key).
  • Unreachable APICould not reach the neolife API at <url> — is it running?
  • Unknown webhook event (trigger / listen --events) → fails loudly, listing the valid catalog.
  • trigger receiver returns non-2xx → exit code 1 and the receiver's response body is printed.
  • listen stream 401/403 → terminal error (won't retry); other drops reconnect with backoff.

Relationship to the MCP and SDKs

The CLI is one of several clients over the same fulfillment API, each for a different consumer:

| Surface | Package | For | |---|---|---| | CLI (this) | @neolifehealth/cli | A human in a terminal — pair a key, inspect orders, test webhooks. | | MCP | @neolifehealth/mcp | An AI agent — the same read/draft operations exposed as MCP tools. | | Node SDK | @neolifehealth/sdk | Application code — typed client + webhook verification. | | Python SDK | (packages/sdk-python) | Application code in Python. |

All four enforce the same invariants: sandbox-by-default, provider-approval-gated submission (no client can auto-submit a clinical order), and PHI-free webhooks. The CLI is deliberately decoupled — it ships a self-contained copy of the key-prefix parser and the Standard-Webhooks signer rather than importing @neolifehealth/sdk, so the published package has zero workspace build coupling. The signer is kept a byte-for-byte mirror of apps/api/src/modules/dev-webhooks/webhook-signer.ts so signatures are interchangeable across surfaces.

See docs/prd/DEVELOPER-PLATFORM.md for the developer-platform roadmap and the API's error/webhook contracts.