@ohgodtamit/pi-usage 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/LICENSE +22 -0
- package/README.md +403 -0
- package/THIRD_PARTY_NOTICES.md +54 -0
- package/dist/aggregate.d.ts +392 -0
- package/dist/aggregate.d.ts.map +1 -0
- package/dist/cache.d.ts +27 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/format.d.ts +46 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/freshness.d.ts +42 -0
- package/dist/freshness.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/mascot.d.ts +35 -0
- package/dist/mascot.d.ts.map +1 -0
- package/dist/prices.d.ts +24 -0
- package/dist/prices.d.ts.map +1 -0
- package/dist/provider.d.ts +147 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/view.d.ts +207 -0
- package/dist/view.d.ts.map +1 -0
- package/dist/zai.d.ts +57 -0
- package/dist/zai.d.ts.map +1 -0
- package/package.json +60 -0
- package/src/aggregate.ts +1838 -0
- package/src/cache.ts +100 -0
- package/src/config.ts +93 -0
- package/src/format.ts +166 -0
- package/src/freshness.ts +55 -0
- package/src/index.ts +642 -0
- package/src/mascot.ts +227 -0
- package/src/prices.ts +63 -0
- package/src/provider.ts +704 -0
- package/src/view.ts +2585 -0
- package/src/zai.ts +101 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prices.d.ts","sourceRoot":"","sources":["../src/prices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAuC3D,CAAC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active-provider detection and live quota fetching.
|
|
3
|
+
*
|
|
4
|
+
* The usage panel surfaces TWO independent signals about your active provider:
|
|
5
|
+
*
|
|
6
|
+
* 1. Live money quota — fetched from the provider's billing API when it has
|
|
7
|
+
* one (OpenRouter credits, OpenAI costs). This is the
|
|
8
|
+
* provider's own view of your account.
|
|
9
|
+
* 2. Rate-limit headers — captured from every provider HTTP response via the
|
|
10
|
+
* `after_provider_response` event. These are universal
|
|
11
|
+
* (Anthropic, OpenAI, OpenRouter, Google, … all return
|
|
12
|
+
* them) and reflect the live per-window limits applied
|
|
13
|
+
* to your current API key.
|
|
14
|
+
*
|
|
15
|
+
* API keys are resolved through the session's model registry, the same path pi
|
|
16
|
+
* uses for request-time credentials and OAuth refresh.
|
|
17
|
+
*/
|
|
18
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
19
|
+
import { type ModelRegistry } from "@earendil-works/pi-coding-agent";
|
|
20
|
+
/** Resolve a provider's current request credential, including refreshed OAuth. */
|
|
21
|
+
export declare function resolveApiKey(modelRegistry: ModelRegistry, provider: string): Promise<string | undefined>;
|
|
22
|
+
/** Check whether pi can currently resolve authentication for a provider. */
|
|
23
|
+
export declare function hasProviderKey(modelRegistry: ModelRegistry, provider: string): Promise<boolean>;
|
|
24
|
+
export interface ActiveProvider {
|
|
25
|
+
provider: string;
|
|
26
|
+
modelId: string;
|
|
27
|
+
baseUrl: string;
|
|
28
|
+
api: string;
|
|
29
|
+
/** True when an API key for this provider is resolvable from the environment. */
|
|
30
|
+
hasKey: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** Detect the currently active provider/model from the session context. */
|
|
33
|
+
export declare function detectActiveProvider(modelRegistry: ModelRegistry, model: Model<Api> | undefined): Promise<ActiveProvider | null>;
|
|
34
|
+
export interface RateLimitWindow {
|
|
35
|
+
/** "requests" | "tokens" | "input-tokens" | "output-tokens" */
|
|
36
|
+
resource: string;
|
|
37
|
+
/** Approximate window label, e.g. "tokens/min". Heuristic per provider tier. */
|
|
38
|
+
window: string;
|
|
39
|
+
limit: number;
|
|
40
|
+
remaining: number;
|
|
41
|
+
/** Epoch ms when the window resets, or 0 if unknown. */
|
|
42
|
+
resetMs: number;
|
|
43
|
+
}
|
|
44
|
+
export interface ProviderQuota {
|
|
45
|
+
active: ActiveProvider | null;
|
|
46
|
+
fetchedAt: number;
|
|
47
|
+
/** Live account credits (OpenRouter). Undefined when not applicable/available. */
|
|
48
|
+
credits?: {
|
|
49
|
+
total: number;
|
|
50
|
+
used: number;
|
|
51
|
+
remaining: number;
|
|
52
|
+
};
|
|
53
|
+
/** Live provider spend in USD (OpenAI organization/costs API). Best-effort. */
|
|
54
|
+
spend5h?: number;
|
|
55
|
+
spend7d?: number;
|
|
56
|
+
monthlyLimit?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Provider-native plan quotas (ZAI GLM coding plans, OpenAI Codex subscription):
|
|
59
|
+
* session (5h) and weekly (7d) windows reported directly by the upstream as a
|
|
60
|
+
* used percentage with a live reset countdown. These replace the session-derived bars.
|
|
61
|
+
*/
|
|
62
|
+
planQuota?: {
|
|
63
|
+
plan: string;
|
|
64
|
+
session5h?: {
|
|
65
|
+
usedPct: number;
|
|
66
|
+
resetMs: number;
|
|
67
|
+
};
|
|
68
|
+
weekly?: {
|
|
69
|
+
usedPct: number;
|
|
70
|
+
resetMs: number;
|
|
71
|
+
};
|
|
72
|
+
webSearches?: {
|
|
73
|
+
used: number;
|
|
74
|
+
limit: number;
|
|
75
|
+
resetMs: number;
|
|
76
|
+
};
|
|
77
|
+
/** Purchased credits balance (OpenAI Codex), when reported. */
|
|
78
|
+
credits?: {
|
|
79
|
+
balance: number;
|
|
80
|
+
unlimited: boolean;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
/** Rate-limit windows captured from the most recent provider response. */
|
|
84
|
+
rateLimits: RateLimitWindow[];
|
|
85
|
+
/** "live" if a billing API responded, "headers" if only rate-limit headers, "none" otherwise. */
|
|
86
|
+
source: "live" | "headers" | "none";
|
|
87
|
+
/** Human hints (e.g. why live quota is unavailable). */
|
|
88
|
+
notes: string[];
|
|
89
|
+
error?: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Parse provider rate-limit headers into structured windows.
|
|
93
|
+
*
|
|
94
|
+
* Recognizes three conventions and de-dupes by resource:
|
|
95
|
+
* - Anthropic: `anthropic-ratelimit-{resource}-{limit|remaining|reset}`
|
|
96
|
+
* - OpenAI: `x-ratelimit-{limit|remaining|reset}-{resource}`
|
|
97
|
+
* - Generic: `x-ratelimit-{limit|remaining|reset}` (older/simpler APIs)
|
|
98
|
+
*
|
|
99
|
+
* Pi lowercases all header keys, so matching is case-insensitive by contract.
|
|
100
|
+
*/
|
|
101
|
+
export declare function parseRateLimits(headers: Record<string, string>, now?: number): RateLimitWindow[];
|
|
102
|
+
/**
|
|
103
|
+
* Parse a reset value into an epoch-ms timestamp.
|
|
104
|
+
*
|
|
105
|
+
* Handles ISO-8601 timestamps ("2024-01-01T12:00:00Z"), OpenAI-style durations
|
|
106
|
+
* ("6m0s", "500ms", "2h"), and bare-seconds integers.
|
|
107
|
+
*/
|
|
108
|
+
export declare function parseReset(value: string | undefined, now: number): number;
|
|
109
|
+
/**
|
|
110
|
+
* Parse OpenAI Codex subscription quota from response headers captured via
|
|
111
|
+
* `after_provider_response`. This is the reliable path: the headers come fresh
|
|
112
|
+
* from pi's own authenticated Codex request, so there is no token/refresh
|
|
113
|
+
* management (unlike the `/wham/usage` REST endpoint, whose OAuth token in
|
|
114
|
+
* `~/.codex/auth.json` is frequently stale/rotated).
|
|
115
|
+
*
|
|
116
|
+
* Header families (authoritative: openai/codex rate_limits.rs):
|
|
117
|
+
* x-codex-primary-used-percent — 5h rolling window used % (0-100)
|
|
118
|
+
* x-codex-primary-reset-at — unix SECONDS of next reset
|
|
119
|
+
* x-codex-secondary-used-percent — 7-day rolling window used %
|
|
120
|
+
* x-codex-secondary-reset-at — unix SECONDS
|
|
121
|
+
* x-codex-credits-has-credits / -unlimited / -balance — purchased credits
|
|
122
|
+
* x-codex-limit-name — plan/limit display name
|
|
123
|
+
*
|
|
124
|
+
* Returns the planQuota shape (shared with ZAI) when any Codex window is present.
|
|
125
|
+
*/
|
|
126
|
+
export declare function parseCodexQuota(headers: Record<string, string>): NonNullable<ProviderQuota["planQuota"]> | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* Fetch OpenAI Codex subscription quota from the REST endpoint using pi's
|
|
129
|
+
* request-time credential resolver, which refreshes OAuth before returning.
|
|
130
|
+
*/
|
|
131
|
+
export declare function fetchCodexQuota(modelRegistry: ModelRegistry, provider?: string, signal?: AbortSignal): Promise<{
|
|
132
|
+
quota: NonNullable<ProviderQuota["planQuota"]>;
|
|
133
|
+
error?: string;
|
|
134
|
+
} | {
|
|
135
|
+
quota: undefined;
|
|
136
|
+
error: string;
|
|
137
|
+
} | undefined>;
|
|
138
|
+
/**
|
|
139
|
+
* Build a full provider quota snapshot: merge already-captured rate-limit
|
|
140
|
+
* headers with a fresh live fetch from the provider's billing API (if any).
|
|
141
|
+
*
|
|
142
|
+
* `capturedRateLimits` comes from the `after_provider_response` event in the
|
|
143
|
+
* orchestrator (index.ts), so it reflects the most recent real request made by
|
|
144
|
+
* the active provider.
|
|
145
|
+
*/
|
|
146
|
+
export declare function fetchProviderQuota(modelRegistry: ModelRegistry, active: ActiveProvider | null, capturedRateLimits: RateLimitWindow[], capturedHeaders?: Record<string, string>, signal?: AbortSignal): Promise<ProviderQuota>;
|
|
147
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
package/dist/view.d.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive usage panel TUI component.
|
|
3
|
+
*
|
|
4
|
+
* Rendered via ctx.ui.custom(). Mirrors Claude Code's `/usage` screen:
|
|
5
|
+
* always-visible 5-hour and weekly quota bars, a selectable time window, and
|
|
6
|
+
* independent-characteristic breakdowns by model / skill / plugin / tool /
|
|
7
|
+
* project. Supports vertical scrolling for small terminals.
|
|
8
|
+
*/
|
|
9
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import type { TUI } from "@earendil-works/pi-tui";
|
|
11
|
+
import { type AttributionMaps, type Report, type StatsRange, type WindowKey } from "./aggregate.ts";
|
|
12
|
+
import { type ViewKey } from "./mascot.ts";
|
|
13
|
+
import type { ProviderQuota } from "./provider.ts";
|
|
14
|
+
export type { ViewKey } from "./mascot.ts";
|
|
15
|
+
/** Sort field for the Models table. */
|
|
16
|
+
type SortKey = "value" | "name";
|
|
17
|
+
/** Sort field + direction for the Daily table. */
|
|
18
|
+
type DailySortField = "tokens" | "cost" | "date";
|
|
19
|
+
/** Semantic controls shared by terminal key handling and portable UI clients. */
|
|
20
|
+
export type UsageAction = {
|
|
21
|
+
type: "view";
|
|
22
|
+
view: ViewKey;
|
|
23
|
+
} | {
|
|
24
|
+
type: "window";
|
|
25
|
+
window: WindowKey;
|
|
26
|
+
} | {
|
|
27
|
+
type: "modelSort";
|
|
28
|
+
sort: SortKey;
|
|
29
|
+
} | {
|
|
30
|
+
type: "dailySort";
|
|
31
|
+
sort: DailySortField;
|
|
32
|
+
} | {
|
|
33
|
+
type: "statsRange";
|
|
34
|
+
range: StatsRange;
|
|
35
|
+
} | {
|
|
36
|
+
type: "providerSort";
|
|
37
|
+
sort: SortKey;
|
|
38
|
+
} | {
|
|
39
|
+
type: "wrappedYear";
|
|
40
|
+
year: number;
|
|
41
|
+
} | {
|
|
42
|
+
type: "wrappedYearDelta";
|
|
43
|
+
delta: number;
|
|
44
|
+
} | {
|
|
45
|
+
type: "refresh";
|
|
46
|
+
} | {
|
|
47
|
+
type: "configure";
|
|
48
|
+
} | {
|
|
49
|
+
type: "close";
|
|
50
|
+
};
|
|
51
|
+
export interface UsageViewDeps {
|
|
52
|
+
theme: Theme;
|
|
53
|
+
tui: TUI | undefined;
|
|
54
|
+
maps: AttributionMaps;
|
|
55
|
+
home: string;
|
|
56
|
+
getConfig: () => {
|
|
57
|
+
fiveHourLimit?: number;
|
|
58
|
+
weeklyLimit?: number;
|
|
59
|
+
fiveHourTokenLimit?: number;
|
|
60
|
+
weeklyTokenLimit?: number;
|
|
61
|
+
};
|
|
62
|
+
onClose: () => void;
|
|
63
|
+
onRefresh: () => void;
|
|
64
|
+
onConfigure: () => void;
|
|
65
|
+
}
|
|
66
|
+
export declare class UsageView {
|
|
67
|
+
private readonly deps;
|
|
68
|
+
private portableRendering;
|
|
69
|
+
private state;
|
|
70
|
+
constructor(deps: UsageViewDeps);
|
|
71
|
+
/** Set the initial view (used by /usage-models, /usage-daily, … shortcuts). */
|
|
72
|
+
setInitialView(view: ViewKey): void;
|
|
73
|
+
get activeView(): ViewKey;
|
|
74
|
+
get wrappedYears(): number[];
|
|
75
|
+
/** Apply a UI-independent dashboard action. */
|
|
76
|
+
applyAction(action: UsageAction): void;
|
|
77
|
+
/** Re-bind the TUI/theme/close callback once pi's custom() factory runs. */
|
|
78
|
+
bind(tui: TUI, theme: Theme, onClose: () => void): void;
|
|
79
|
+
setReport(report: Report): void;
|
|
80
|
+
setScanning(loaded: number, total: number): void;
|
|
81
|
+
setError(message: string): void;
|
|
82
|
+
setProviderQuota(quota: ProviderQuota): void;
|
|
83
|
+
handleInput(data: string): void;
|
|
84
|
+
render(width: number): string[];
|
|
85
|
+
/** Render all lines without terminal viewport assumptions or key-based instructions. */
|
|
86
|
+
renderPortable(width: number): string[];
|
|
87
|
+
/** Final width clamp so one long line can never break the TUI layout. */
|
|
88
|
+
private clampLine;
|
|
89
|
+
invalidate(): void;
|
|
90
|
+
private availableHeight;
|
|
91
|
+
private setWindow;
|
|
92
|
+
private setView;
|
|
93
|
+
private cycleView;
|
|
94
|
+
private setStatsRange;
|
|
95
|
+
/** Set the Daily sort field; pressing the same field again flips direction. */
|
|
96
|
+
private setDailySort;
|
|
97
|
+
private cycleWrappedYear;
|
|
98
|
+
private scrollBy;
|
|
99
|
+
private scrollTo;
|
|
100
|
+
private clampScroll;
|
|
101
|
+
private buildLines;
|
|
102
|
+
/**
|
|
103
|
+
* Subscription-aware breakdown unit: token-priced providers (Codex, ZAI
|
|
104
|
+
* plans) always show tokens; otherwise USD when the window has real cost.
|
|
105
|
+
*/
|
|
106
|
+
private unitForWindow;
|
|
107
|
+
private renderOverview;
|
|
108
|
+
/** Render the always-on quota bars (plan quota or session-derived budget). */
|
|
109
|
+
private renderQuotaBlock;
|
|
110
|
+
private renderModels;
|
|
111
|
+
/**
|
|
112
|
+
* Models table styled like the Skills section (name · % · bar · value), with
|
|
113
|
+
* an extra column for the average generation speed (estimated tok/s).
|
|
114
|
+
*/
|
|
115
|
+
private appendModelTable;
|
|
116
|
+
private renderDelegation;
|
|
117
|
+
private windowDuration;
|
|
118
|
+
private zeroBucket;
|
|
119
|
+
private addUsageToBucket;
|
|
120
|
+
private renderDaily;
|
|
121
|
+
private renderHourly;
|
|
122
|
+
private renderProviders;
|
|
123
|
+
private renderWrapped;
|
|
124
|
+
/** Hairline section label — matches Stats/Daily report rhythm. */
|
|
125
|
+
private wrappedSectionHeader;
|
|
126
|
+
/** Pi-chan footer card — character accent, professional tone. */
|
|
127
|
+
private wrappedInsightBox;
|
|
128
|
+
/** Side-by-side mascot + content when the terminal is wide enough. */
|
|
129
|
+
private appendMascotBlock;
|
|
130
|
+
private wrappedBannerLine;
|
|
131
|
+
private appendWrappedHero;
|
|
132
|
+
private appendWrappedMonthly;
|
|
133
|
+
/**
|
|
134
|
+
* Claude Code / Stats-style vertical month columns: graded blocks, month
|
|
135
|
+
* labels, Less→More legend, and a peak-month callout.
|
|
136
|
+
*/
|
|
137
|
+
private appendWrappedMonthlyHeatmap;
|
|
138
|
+
/** Narrow-terminal fallback: horizontal share bars (same geometry as Rankings). */
|
|
139
|
+
private appendWrappedMonthlyRows;
|
|
140
|
+
private appendWrappedHighlights;
|
|
141
|
+
private appendWrappedTops;
|
|
142
|
+
private renderStats;
|
|
143
|
+
/** Interactive range selector for the Stats view (All / 7d / 30d). */
|
|
144
|
+
private statsRangeLine;
|
|
145
|
+
/** Render stat pairs in two aligned columns. */
|
|
146
|
+
private appendStatGrid;
|
|
147
|
+
/** A playful one-liner comparing total usage to a familiar reference. */
|
|
148
|
+
private statsFunFact;
|
|
149
|
+
/**
|
|
150
|
+
* GitHub-style contribution heatmap: a month-label header row, then 7 day
|
|
151
|
+
* rows (Sun..Sat) of graded square cells, then a Less→More legend.
|
|
152
|
+
*/
|
|
153
|
+
private heatmapColor;
|
|
154
|
+
/** Map a 0–1 usage ratio to heatmap intensity (matches Stats view). */
|
|
155
|
+
private heatmapLevel;
|
|
156
|
+
private appendContribGraph;
|
|
157
|
+
/** Sparkline of the last 30 active-window days (Overview trend strip). */
|
|
158
|
+
private appendTrendSparkline;
|
|
159
|
+
private portableMenuLines;
|
|
160
|
+
private menuLines;
|
|
161
|
+
private titleLineRaw;
|
|
162
|
+
private windowTabs;
|
|
163
|
+
private subheaderLine;
|
|
164
|
+
private quotaLine;
|
|
165
|
+
/**
|
|
166
|
+
* Render an upstream-reported percentage quota bar (e.g. ZAI 5h/weekly).
|
|
167
|
+
* The provider only exposes `usedPct` (0-100) + a reset countdown, so the bar
|
|
168
|
+
* shows used%, remaining%, and when the window resets.
|
|
169
|
+
*/
|
|
170
|
+
private percentLine;
|
|
171
|
+
/**
|
|
172
|
+
* Build a context-aware hint explaining why the subscription quota isn't
|
|
173
|
+
* shown yet. Subscriptions (OpenAI Codex, ZAI coding plans) get their quota
|
|
174
|
+
* from the upstream — the panel must never suggest `/usage-config` for
|
|
175
|
+
* these, because that would be wrong (it would just aggregate session
|
|
176
|
+
* history, not the real plan quota).
|
|
177
|
+
*/
|
|
178
|
+
private buildSubscriptionHint;
|
|
179
|
+
private appendTokenComposition;
|
|
180
|
+
private statsLine;
|
|
181
|
+
private topConsumer;
|
|
182
|
+
/** Render the active-provider banner + live quota + rate-limit windows. */
|
|
183
|
+
private appendProviderSection;
|
|
184
|
+
/** Compact single-line bar: `[label] ██████░░░░ right` */
|
|
185
|
+
private miniBar;
|
|
186
|
+
/**
|
|
187
|
+
* One aligned table-header row: the section title fills the label column and
|
|
188
|
+
* the column labels (`%`, value unit, optional extra) sit directly above
|
|
189
|
+
* their data columns. Keeps every breakdown section visually consistent.
|
|
190
|
+
*/
|
|
191
|
+
private tableHeader;
|
|
192
|
+
private appendToolsSection;
|
|
193
|
+
private appendSection;
|
|
194
|
+
/**
|
|
195
|
+
* Plugin usage section: ranks plugins by their attributed usage and shows the
|
|
196
|
+
* specific skills/tools that drove each one, plus the "core" remainder
|
|
197
|
+
* (turns with only builtin tools and no skill — i.e. plain pi usage).
|
|
198
|
+
*
|
|
199
|
+
* Plugins are independent characteristics: a single turn can credit several
|
|
200
|
+
* plugins, so the percentages need not sum to 100. The core line is the
|
|
201
|
+
* complement (turns attributed to NO plugin).
|
|
202
|
+
*/
|
|
203
|
+
private appendPluginUsageSection;
|
|
204
|
+
private portableFooterLine;
|
|
205
|
+
private footerLine;
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"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"}
|
package/dist/zai.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure ZAI (Zhipu / GLM coding plans) quota-limit classification.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from provider.ts into its own module so it has ZERO pi / pi-ai
|
|
5
|
+
* runtime dependencies and is unit-testable without network or auth. The
|
|
6
|
+
* network layer (provider.ts) imports `classifyZaiLimits` from here.
|
|
7
|
+
*
|
|
8
|
+
* The return shape is structurally compatible with the `planQuota` field on
|
|
9
|
+
* provider.ts's `ProviderQuota` (a subset: ZAI never reports purchased
|
|
10
|
+
* credits, only the 5h/weekly windows + web searches), so it can be returned
|
|
11
|
+
* directly from `fetchZaiPlanQuota`.
|
|
12
|
+
*/
|
|
13
|
+
/** A single quota window in ZAI's /quota/limit response. */
|
|
14
|
+
export interface ZaiQuotaLimit {
|
|
15
|
+
type?: string;
|
|
16
|
+
unit?: number;
|
|
17
|
+
number?: number;
|
|
18
|
+
usage?: number;
|
|
19
|
+
currentValue?: number;
|
|
20
|
+
remaining?: number;
|
|
21
|
+
percentage?: number;
|
|
22
|
+
nextResetTime?: number;
|
|
23
|
+
}
|
|
24
|
+
/** Provider-native plan quota fragment produced from a ZAI limits array. */
|
|
25
|
+
export interface ZaiPlanQuota {
|
|
26
|
+
plan: string;
|
|
27
|
+
session5h?: {
|
|
28
|
+
usedPct: number;
|
|
29
|
+
resetMs: number;
|
|
30
|
+
};
|
|
31
|
+
weekly?: {
|
|
32
|
+
usedPct: number;
|
|
33
|
+
resetMs: number;
|
|
34
|
+
};
|
|
35
|
+
webSearches?: {
|
|
36
|
+
used: number;
|
|
37
|
+
limit: number;
|
|
38
|
+
resetMs: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Classify a ZAI /quota/limit `limits[]` into the planQuota shape.
|
|
43
|
+
*
|
|
44
|
+
* PURE function (no network). Detection strategy (robust across ZAI plan
|
|
45
|
+
* tiers and regions):
|
|
46
|
+
* 1. Prefer the documented exact identifiers verified on the "max" plan:
|
|
47
|
+
* session 5h = (unit 3, number 5), weekly 7d = (unit 6, number 1).
|
|
48
|
+
* 2. Fall back to positional pairing: ZAI only ever reports these two
|
|
49
|
+
* token windows, so the window that is NOT the session is the weekly.
|
|
50
|
+
* This keeps weekly resolving when other plans/regions encode the window
|
|
51
|
+
* with different unit/number values — the production bug where only the
|
|
52
|
+
* 5h bar appeared for non-"max" accounts.
|
|
53
|
+
* 3. When neither exact code matches, assign by reset-window length: the
|
|
54
|
+
* shorter countdown maps to the 5h session, the longer to weekly.
|
|
55
|
+
*/
|
|
56
|
+
export declare function classifyZaiLimits(limits: ZaiQuotaLimit[], level: string): ZaiPlanQuota | undefined;
|
|
57
|
+
//# sourceMappingURL=zai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zai.d.ts","sourceRoot":"","sources":["../src/zai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAChE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,aAAa,EAAE,EACvB,KAAK,EAAE,MAAM,GACZ,YAAY,GAAG,SAAS,CAiD1B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ohgodtamit/pi-usage",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Usage dashboards, attribution, and live provider quotas for Pi",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/GodTamIt/pi-mono.git",
|
|
8
|
+
"directory": "packages/pi-usage"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/GodTamIt/pi-mono/tree/master/packages/pi-usage#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/GodTamIt/pi-mono/issues"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"pi-package"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22.22.2"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"src",
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"THIRD_PARTY_NOTICES.md"
|
|
29
|
+
],
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"import": "./src/index.ts"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"pi": {
|
|
37
|
+
"extensions": [
|
|
38
|
+
"./src/index.ts"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"typecheck": "tsc --noEmit --declaration false --declarationMap false",
|
|
43
|
+
"unit": "vitest run",
|
|
44
|
+
"test": "npm run unit",
|
|
45
|
+
"declarations": "node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.declarations.json",
|
|
46
|
+
"prepack": "npm run declarations",
|
|
47
|
+
"pack:inspect": "node ./scripts/inspect-package.mjs",
|
|
48
|
+
"smoke:installed": "node ./test/installed/smoke.mjs"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@earendil-works/pi-coding-agent": ">=0.84.3 <0.85.0",
|
|
52
|
+
"@earendil-works/pi-ai": ">=0.84.3 <0.85.0",
|
|
53
|
+
"@earendil-works/pi-tui": ">=0.84.3 <0.85.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@earendil-works/pi-coding-agent": "0.84.3",
|
|
57
|
+
"@earendil-works/pi-ai": "0.84.3",
|
|
58
|
+
"@earendil-works/pi-tui": "0.84.3"
|
|
59
|
+
}
|
|
60
|
+
}
|