原始内容
pi-sub
Unofficial continuation of
marckrenn/pi-sub. This fork keeps the upstream MIT license and attribution, but publishes packages under the@eiei114npm scope.
Monorepo for the sub-* extension ecosystem: a shared usage core (sub-core), UI clients (like sub-bar), and headless consumers that subscribe to usage updates.
About this fork
This fork exists because the upstream repo has been quiet while a few maintenance issues and PRs were waiting. The goal is to keep pi-sub usable for current Pi users, not to present this fork as the upstream project.
Initial changes in this fork:
- Package scope changed from
@marckrenn/*to@eiei114/*for npm publishing. - Install docs updated for the
@eiei114packages. - Cache writes are more robust on Windows by retrying transient rename failures and cleaning up temp files.
- Cache lock ownership is safer so one process does not release another process's lock.
turn_endandtool_resultrefreshes now respect the cache TTL instead of forcing network requests every turn.
Overview
- sub-core: fetches usage + status, manages cache/locks, owns provider selection, and emits updates via
pi.events. - sub-bar: UI widget that renders the current usage state above the editor.
- sub-status: compact status-line client that renders the current usage state via
ctx.ui.setStatus(...). - sub-shared: shared types + event contract (published to npm as
@eiei114/pi-sub-shared).
sub-core can power multiple sub-* extensions at once, including rich UI clients like sub-bar and compact/headless-friendly clients like sub-status.
Packages
| Package | Version | Downloads | Description |
|---|---|---|---|
@eiei114/pi-sub-core |
Shared fetch/cache core (pi extension). | ||
@eiei114/pi-sub-bar |
Rich widget display client (pi extension). | ||
@eiei114/pi-sub-status |
Compact status-line display client (pi extension). | ||
@eiei114/pi-sub-shared |
Shared types + event contract (npm package). |
Ideas / planned sub-* extensions
| Package | Description |
|---|---|
pi-sub-compare |
Usage comparison chart across multiple providers. |
pi-sub-model-switcher |
Auto model/provider switching when reaching a usage threshold. |
pi-sub-account-switcher |
Cycle between multiple subscriptions at usage thresholds. |
If you’d like to work on these, PRs or standalone packages are welcome.
Requirements
- Node.js >= 20 (see
.nvmrc) - npm (bundled with Node)
Pi package manager
You can install the packages via pi install:
pi install npm:@eiei114/pi-sub-core
pi install npm:@eiei114/pi-sub-bar
pi install npm:@eiei114/pi-sub-status
sub-bar remains the default rich UI path. sub-status is an explicit opt-in compact client and can be installed alongside sub-bar when you want both the widget and a status-line summary.
Quick Start (manual install)
git clone https://github.com/eiei114/pi-sub.git
# Enable the shared core plus one or both display clients
ln -s /path/to/pi-sub/packages/sub-core ~/.pi/agent/extensions/sub-core
ln -s /path/to/pi-sub/packages/sub-bar ~/.pi/agent/extensions/sub-bar
ln -s /path/to/pi-sub/packages/sub-status ~/.pi/agent/extensions/sub-status
Alternative (no symlink): add the core plus whichever clients you want to ~/.pi/agent/settings.json:
{
"extensions": [
"/path/to/pi-sub/packages/sub-core/index.ts",
"/path/to/pi-sub/packages/sub-bar/index.ts",
"/path/to/pi-sub/packages/sub-status/index.ts"
]
}
sub-sharedis an npm dependency and is pulled automatically.sub-barandsub-statusare both optional clients on top ofsub-core.
Communication model (core ↔ clients)
sub-core is the source of truth. It emits updates and accepts requests/actions over pi.events.
Rendering good practice (snappy UI)
To keep UI clients responsive (like sub-bar), prefer this sequence when a model or session changes:
- Render cached state immediately (even if stale).
- Fetch fresh usage in the background.
- Re-render when new data arrives.
Why: awaiting fetches inside pi.on("session_start") / pi.on("model_select") blocks other extension handlers, so UI renders can lag behind network calls. In sub-core we use a non-blocking refresh (void refresh(...)) and allow stale cache (allowStaleCache: true) so cached usage is emitted before the forced fetch finishes. UI clients should listen for sub-core:update-current and render whenever state changes.
Broadcasts
sub-core:ready→{ state, settings }(first load)sub-core:update-current→{ state }(cache hit or fresh fetch)sub-core:update-all→{ state }(cached entries + current provider)sub-core:settings:updated→{ settings }
Requests (pull)
sub-core:request→{ reply, includeSettings? }sub-core:request→{ type: "entries", reply, force? }
Actions (mutate core state)
sub-core:settings:patch→{ patch }(persists core settings)sub-core:action→{ type: "refresh" | "cycleProvider", force? }
UI extensions like sub-bar and compact clients like sub-status listen for updates and render the current provider state in their own format.
Settings & Cache
Settings live in the agent directory to survive updates (legacy extension settings.json files are migrated on first run when present, and removed after successful migration). Cache/lock files live under ~/.pi/agent/cache/sub-core; legacy cache/lock files next to the sub-core extension entry or in the agent root are migrated and removed on first run.
- sub-core settings:
~/.pi/agent/pi-sub-core-settings.json - sub-bar settings:
~/.pi/agent/pi-sub-bar-settings.json - cache:
~/.pi/agent/cache/sub-core/cache.json - lock:
~/.pi/agent/cache/sub-core/cache.lock
Adding a Provider (summary)
You must update both sub-core (fetch layer) and sub-bar (display/UI).
sub-core
- Add provider name to
packages/sub-core/src/types.ts. - Implement fetcher in
packages/sub-core/src/providers/impl/<provider>.ts. - Register provider in
packages/sub-core/src/providers/registry.ts. - Add detection + status config in
packages/sub-core/src/providers/metadata.ts. - Add settings defaults in
packages/sub-core/src/settings-types.ts.
sub-bar
- Add provider name to
packages/sub-bar/src/types.ts. - Add display metadata in
packages/sub-bar/src/providers/metadata.ts. - Add window visibility rules in
packages/sub-bar/src/providers/windows.ts. - Add extras (if needed) in
packages/sub-bar/src/providers/extras.ts. - Add settings UI + defaults in
packages/sub-bar/src/providers/settings.tsandpackages/sub-bar/src/settings-types.ts.
Development
Developer guide (common workflows)
Add a new provider (core + UI)
- sub-core owns fetching, caching, status lookup, provider detection, and emits update events.
- sub-bar owns display rules, formatting, per-provider UI settings, and visibility of windows/extras.
- If you add new shared types or provider metadata used by multiple packages, update sub-shared and re-export.
Add a new feature: core vs sub-*
Use this rule of thumb when deciding where a feature lives:
Put it in sub-core when:
- It affects data fetching, provider detection/selection, or status polling.
- It changes event contracts (
sub-core:*events) or tools (sub_get_usage,sub_get_all_usage). - It introduces shared settings that should affect all clients.
- It requires cache/lock behavior or cross-window coordination.
Put it in sub- when:*
- It is presentation-only (formatting, layout, colors, widget behavior).
- It is UI-only settings (visibility toggles, label text, window ordering in display).
- It targets a single client (e.g. sub-bar specific display change).
If both layers need it:
- Add data and settings in sub-core (and
sub-sharedtypes), then consume in sub-bar. - Update docs and tests for the shared contract.
Example decisions
- New API field or rate window data → sub-core (fetch + cache), then surface in sub-bar.
- New bar style or status icon pack → sub-bar only.
- New provider enablement behavior → sub-core (and sub-bar UI can forward settings).
Dev setup
npm install
Common commands:
npm run check— typecheck all workspacesnpm run test— run workspace tests (sub-bar + sub-core + sub-status)npm run lint/npm run lint:fix— lint TypeScriptnpm run format— format with Prettiernpm run verify— run check + test + lint
Watch mode:
npm run check:watch -w @eiei114/pi-sub-core
npm run check:watch -w @eiei114/pi-sub-bar
npm run check:watch -w @eiei114/pi-sub-status
npm run check:watch -w @eiei114/pi-sub-shared
npm run test:watch -w @eiei114/pi-sub-bar
npm run test:watch -w @eiei114/pi-sub-status
Workspace-specific commands:
npm run check -w @eiei114/pi-sub-core
npm run check -w @eiei114/pi-sub-bar
npm run check -w @eiei114/pi-sub-status
npm run check -w @eiei114/pi-sub-shared
npm run test -w @eiei114/pi-sub-core
npm run test -w @eiei114/pi-sub-bar
npm run test -w @eiei114/pi-sub-status