@alexanderfortin/pi-usage-lib
Shared library for Pi usage monitoring extensions
Package details
Install @alexanderfortin/pi-usage-lib from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@alexanderfortin/pi-usage-lib- Package
@alexanderfortin/pi-usage-lib- Version
0.2.0- Published
- Jun 18, 2026
- Downloads
- 1,143/mo · 494/wk
- Author
- alexanderfortin
- License
- MIT
- Types
- extension
- Size
- 51.2 KB
- Dependencies
- 1 dependency · 2 peers
Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-usage-lib
Shared library for building Pi coding agent usage-monitoring extensions.
It absorbs all the boilerplate that most *-usage extension needs — Pi event registration, provider matching, API fetching with sandbox-aware auth, response caching, error handling, and themed footer rendering — so that each extension reduces to a single config object with two callbacks.
What's Included
| Module | Exports | Purpose |
|---|---|---|
pi-usage-lib |
createUsageExtension(), UsageCache, UsageError, buildAuthHeaders(), safeFetch(), safeParseJson() |
Factory + building blocks |
pi-usage-lib/datetime |
formatInstantFromEpochMs(), formatTimeRemainingFromEpochMs() |
Temporal date/time helpers |
Install
bun add @alexanderfortin/pi-usage-lib
Quick Start
import { createUsageExtension, buildAuthHeaders, safeFetch, safeParseJson } from "@alexanderfortin/pi-usage-lib"
export default createUsageExtension({
providerPrefix: "myprovider",
statusKey: "myprovider-usage",
label: "MyProvider",
async fetchUsage(modelRegistry) {
const headers = await buildAuthHeaders(modelRegistry, "myprovider")
const response = await safeFetch("https://api.myprovider.com/usage", { headers })
const data = await safeParseJson(response)
return { balance: data.balance }
},
renderStatus(data, theme) {
return theme.fg("muted", "MyProvider:") + theme.fg("accent", `$${data.balance.toFixed(2)}`)
},
})
That's it — one file, no other source files needed. The factory registers all Pi events, manages caching, handles errors, and shows a themed footer automatically.
API Reference
createUsageExtension<TData>(config)
Creates a Pi extension function from a configuration object.
interface UsageExtensionConfig<TData> {
providerPrefix: string // e.g. "zai", "deepseek"
statusKey: string // e.g. "zai-usage"
label: string // e.g. "MyProvider"
cooldownMs?: number // cache TTL (default: 30_000)
fetchUsage: FetchUsageFn<TData>
renderStatus: RenderStatusFn<TData>
renderError?: RenderErrorFn // optional, defaults to themed <err:code> display
}
The returned function is a valid Pi extension — pass it as the default export or register it via pi.extensions.
buildAuthHeaders(modelRegistry, providerName, extra?)
Builds authenticated fetch headers using a 3-way sandbox-aware strategy:
- Real key → sends
Authorization: Bearer <key> "proxy-managed"sentinel → skips auth header (Docker Sandbox proxy handles it)- No key → skips auth header (API returns 401 → shown as
<err:http401>)
Always sets Accept-Encoding: identity to work around Pi v0.75.0's undici gzip decompression issue.
safeFetch(url, init?)
Wraps fetch() with structured error handling:
- Network errors (DNS, timeout, proxy) → throws
UsageErrorwith code"fetch" - HTTP errors (4xx, 5xx) → throws
UsageErrorwith code"http{status}"
safeParseJson<T>(response)
Parses response.json() with error handling:
- Empty or malformed body → throws
UsageErrorwith code"badjson"
UsageError
class UsageError extends Error {
readonly name = "UsageError"
readonly code: string // e.g. "fetch", "http401", "badjson", custom codes
}
Used by safeFetch, safeParseJson, and the default error renderer. Extensions can throw UsageError from their fetchUsage with custom codes — the default renderError will display them as <err:code> in the footer.
UsageCache<TData>
The generic cache class used internally by createUsageExtension. Available for advanced use cases where you need direct control over the cache lifecycle.
Color Thresholds (Configurable)
The library highlights usage values with color when they approach or exceed limits:
- Percentage-based (e.g. Z.ai usage): warning above 80%, critical at 90%+
- Credit-based (e.g. DeepSeek balance): warning below $5, critical at $1 or less
Overriding thresholds via settings file
You can override any or all of the default thresholds by creating a JSON file at ~/.pi/agent/usage-lib.json:
{
"thresholds": {
"percentage": { "warning": 70, "critical": 85 },
"credit": { "warning": 5, "critical": 2 }
}
}
All keys are optional — only the ones you specify are overridden; the rest keep their defaults. Unknown keys and non-numeric values are silently ignored. The file is read once per session and cached.
Overriding thresholds programmatically
The colorForPercentage() and colorForCredit() helpers accept an optional thresholds parameter:
import { colorForPercentage, type ColorThresholds } from "@alexanderfortin/pi-usage-lib"
const custom: ColorThresholds = {
percentage: { warning: 60, critical: 75 },
credit: { warning: 5, critical: 1 },
}
// In your renderStatus callback:
const color = colorForPercentage(data.percentage, theme, custom)
When no thresholds argument is passed, the functions fall back to the values loaded from the settings file (or the built-in defaults).
Color threshold API
| Export | Description |
|---|---|
ColorThresholds |
Type describing the threshold structure |
DEFAULT_COLOR_THRESHOLDS |
Built-in default thresholds |
loadColorThresholds() |
Load & cache thresholds from the settings file |
mergeThresholds(defaults, overrides) |
Merge partial overrides into defaults |
getSettingsFilePath() |
Resolve the path to ~/.pi/agent/usage-lib.json |
Error Display (Default Behavior)
By default, errors are shown in the footer using the following pattern:
MyProvider:<err:http401>
MyProvider:<err:fetch>
MyProvider:<err:badjson>
No console.error is called — the footer is the error channel. To customize, provide a renderError callback. Return undefined to clear the footer instead.
Date/Time Utilities (pi-usage-lib/datetime)
import { formatInstantFromEpochMs, formatTimeRemainingFromEpochMs } from "@alexanderfortin/pi-usage-lib/datetime"
formatInstantFromEpochMs(1705318245000) // "Mon, 15 Jan 2024, 14:30:45 GMT"
formatTimeRemainingFromEpochMs(Date.now() + 3665000) // "1h 1m 5s"
License
See LICENSE