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

pi-usage-hub

v1.1.3

Published

Usage hub for pi — provider quota/balance registry with cache, /usage-hub panel, and pull API

Downloads

636

Readme

pi-usage-hub

Track provider quotas and balances in pi — /usage-hub panel, session stats, and a pull API for footer integration.

npm version License: MIT

Why

Checking separate dashboards for DeepSeek, NewAPI relays, OpenAI Codex, xAI, Kiro, OpenCode Go, and ARK breaks flow. pi-usage-hub brings their quotas and balances into one TUI panel while keeping footer integration optional and pull-based.

Install

pi install npm:pi-usage-hub

Commands

| Command | Description | |---------|-------------| | /usage-hub | Show quotas and balances for detected providers | | /usage-hub session | Show local session token and cost stats | | /usage-hub login <name> | Open browser login for a cookie-based provider |

Inside the panel, Tab switches between Quota and Session. In the Quota view, ↑↓ scrolls when content overflows.

pi-usage-hub quota panel

pi-usage-hub session panel

Configuration

Create ~/.pi/agent/pi-usage-hub.json. Provider order is preserved in the panel.

{
  "providers": [
    {
      "type": "deepseek",
      "apiKey": "sk-..."
    },
    {
      "name": "xh",
      "type": "newapi",
      "matchProviders": ["xh-cc", "xh-glm"],
      "host": "https://example.com",
      "token": "...",
      "userId": "1"
    },
    {
      "name": "ocg",
      "type": "opencode-go",
      "workspaceId": "wrk_...",
      "matchProviders": ["opencode-go"]
    },
    { "type": "ark" },
    {
      "type": "xai",
      "matchProviders": ["xai-auth", "xai", "grok-cli"]
    },
    { "type": "kiro" },
    { "type": "kimi-coding" },
    { "type": "zai" },
    { "type": "codex" }
  ]
}

Common fields

| Field | Required | Description | |-------|----------|-------------| | type | yes | Built-in provider factory | | name | no | Instance key; defaults to type, then type-2, type-3, and so on | | matchProviders | no | Additional model.provider values mapped to this entry | | shortLabel / label | no | Override the footer label or panel title | | hidden | no | Exclude from panel and footer; /usage-hub login still works | | disabled | no | Do not register at all; on untyped entries, refuses that provider's registration |

Provider types

| Type | Credentials | Notes | Website | |------|-------------|-------|---------| | deepseek | apiKey | Account balance | deepseek | | newapi | host, token, userId | Balance and today's spend; supports multiple instances | newapi | | opencode-go | workspaceId; optional auth | Go plan five-hour/weekly/monthly spend; uses the configured console session cookie or macOS Chrome | opencode | | ark | optional cookie, csrfToken | Uses the configured Cookie header or macOS Chrome | 火山引擎 | | xai | — | Reads auth.json or ~/.grok/auth.json | x.ai/grok | | kiro | — | Reads the Kiro OAuth entry from auth.json | kiro.dev | | kimi-coding | optional apiKey | 5h rolling window + weekly quota; falls back to the kimi-coding entry in auth.json (Kimi Code key, not the open-platform key) | kimi.com/code | | zai | optional apiKey | 5h window, weekly, and monthly search quota; falls back to the zai entry in auth.json | z.ai | | codex | — | Codex rolling and 7-day usage windows; reads the openai-codex OAuth entry in auth.json | OpenAI Codex |

For NewAPI, token is the system access token (not a chat sk- token), and userId is sent as New-Api-User. See Authentication and Generate access token.

For ARK and OpenCode Go, configured cookie / auth values take priority over Chrome. Manual credentials disable /usage-hub login for that entry; replace them when they expire. Automatic Chrome cookie reading is macOS only.

Related auth packages

These are companion packages, not npm peer dependencies:

| Type | Companion | Purpose | |------|-----------|---------| | kiro | pi-provider-kiro-dev | Provides /login kiro, models, and the auth.json entry |

pi-usage-hub only reads those credentials; it does not run their OAuth flows.

Built-in providers cover the integrations used and smoke-tested by the author. To support another provider, register a custom provider from an extension, or fork the package and submit a PR.

Add a custom provider

See examples/custom-provider.ts for a self-contained extension that implements and registers a provider. Copy it into ~/.pi/agent/extensions/, then adapt the endpoint, credentials, response shape, and labels.

pi.events.on("pi-usage-hub:ready", (hub) => hub.register(myProvider));
pi.events.emit("pi-usage-hub:register", myProvider);
pi.events.emit("pi-usage-hub:unregister", { key: "my-relay" });

Re-registering the same key overwrites (registration is idempotent), so the ready-listener + emit combination above never duplicates a provider.

Configure a custom provider from JSON

providers entries without type target an externally-registered provider by name. Meta fields override the provider's own values at register time; disabled: true refuses registration; all other fields pass through untouched to hub.getProviderConfig(name), so the extension can read its credentials from JSON instead of env vars:

{
  "providers": [
    { "type": "kimi-coding" },
    {
      "name": "qianwen",
      "shortLabel": "QW",
      "hidden": false,
      "cookie": "login_qianwenai_ticket=..."
    }
  ]
}
pi.events.on("pi-usage-hub:ready", (hub) => {
  const cfg = hub.getProviderConfig("qianwen"); // { shortLabel, hidden, cookie, ... }
  const cookie = typeof cfg?.cookie === "string" ? cfg.cookie : undefined;
});

Untyped entries do not affect built-in name allocation; two untyped entries with the same name resolve last-write-wins. An untyped entry whose name matches a built-in entry also applies to it, so { "name": "kimi-coding", "hidden": true } hides the built-in without editing its own entry.

Register a built-in from an extension

hub.registerBuiltin(type, cfg?) instantiates any built-in factory with your own parameters (e.g. credentials obtained at runtime) and registers it, allocating type, type-2, … on key clash. Returns the registered key, or null for unknown types or disabled: true.

Pull API

The hub caches results for 60 seconds and deduplicates concurrent requests. It never pushes footer text: consumers start refreshes without blocking lifecycle events, read the cached summary, and re-render when notified.

| API | Role | |-----|------| | pi-usage-hub:ready | Provides the hub; also emitted on session_start | | hub.refresh({ model?, force? }) | Refreshes the matching provider and returns its summary | | hub.getSummary(model?) | Synchronously reads the cached one-line summary | | pi-usage-hub:updated | Signals { key, summary } after a cache update |

Footer example

footer integration

type UsageHub = {
  getSummary(model?: { provider?: string }): string | null;
  refresh(opts?: {
    model?: { provider?: string };
    force?: boolean;
  }): Promise<string | null>;
};

let usageHub: UsageHub | null = null;
let requestRender: (() => void) | null = null;

const offReady = pi.events.on("pi-usage-hub:ready", (hub: UsageHub) => {
  usageHub = hub;
  requestRender?.();
});

const offUpdated = pi.events.on("pi-usage-hub:updated", () => {
  requestRender?.();
});

// Pi awaits lifecycle handlers; refresh in the background and re-render on pi-usage-hub:updated.
pi.on("session_start", (_event, ctx) => {
  void usageHub?.refresh({ model: ctx.model, force: true });
});
pi.on("model_select", (event) => {
  void usageHub?.refresh({ model: event.model, force: true });
});
pi.on("agent_end", (_event, ctx) => {
  void usageHub?.refresh({ model: ctx.model, force: true });
});

// Inside footer render():
// const usageText = usageHub?.getSummary(ctx.model);
// "XAI 87% · ↻ 3d 16h"

pi.on("session_shutdown", async () => {
  offReady();
  offUpdated();
  usageHub = null;
});

License

MIT