@sreetej510/pi-usage 0.1.1 → 0.1.2

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/src/types.ts DELETED
@@ -1,228 +0,0 @@
1
- import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
-
3
- export type CommandArgumentCompletion = {
4
- value: string;
5
- label: string;
6
- description?: string;
7
- };
8
-
9
- export type UsageSource = "pi-auth" | "codex-app-server" | "anthropic-oauth";
10
- export type PiModel = NonNullable<ExtensionContext["model"]>;
11
- export type ProviderUsageModel = Pick<PiModel, "id" | "name" | "provider">;
12
-
13
- export type QueryUsageOptions = {
14
- clearStatusline: boolean;
15
- consumeBankedReset: boolean;
16
- consumeBankedResetId?: string;
17
- listBankedResets: boolean;
18
- raw: boolean;
19
- refresh: boolean;
20
- statusline: boolean;
21
- timeoutMs: number;
22
- };
23
-
24
- export type CachedReport = {
25
- createdAt: number;
26
- report: UsageReport;
27
- };
28
-
29
- export type QueryUsageResult = { ok: true; report: UsageReport } | { ok: false; errors: UsageQueryError[] };
30
-
31
- export type UsageQueryError = {
32
- source: UsageSource;
33
- message: string;
34
- cause?: unknown;
35
- };
36
-
37
- export type CodexUsageReport = {
38
- provider: "codex";
39
- source: "pi-auth" | "codex-app-server";
40
- capturedAt: number;
41
- planType?: string;
42
- bankedResetsAvailable?: number;
43
- snapshots: NormalizedRateLimitSnapshot[];
44
- };
45
-
46
- export type AnthropicUsageReport = {
47
- provider: "anthropic";
48
- source: "anthropic-oauth";
49
- capturedAt: number;
50
- summaryLines: string[];
51
- statusline: string;
52
- };
53
-
54
- export type UsageReport = CodexUsageReport | AnthropicUsageReport;
55
-
56
- export type NormalizedRateLimitSnapshot = {
57
- limitId: string;
58
- limitName?: string;
59
- primary?: NormalizedRateLimitWindow;
60
- secondary?: NormalizedRateLimitWindow;
61
- credits?: NormalizedCredits;
62
- };
63
-
64
- export type NormalizedRateLimitWindow = {
65
- usedPercent: number;
66
- windowMinutes?: number;
67
- resetsAt?: number;
68
- };
69
-
70
- export type NormalizedCredits = {
71
- hasCredits: boolean;
72
- unlimited: boolean;
73
- balance?: string;
74
- };
75
-
76
- export type RateLimitStatusPayload = {
77
- plan_type?: unknown;
78
- rate_limit?: unknown;
79
- additional_rate_limits?: unknown;
80
- credits?: unknown;
81
- rate_limit_reset_credits?: unknown;
82
- };
83
-
84
- export type BackendRateLimitDetails = {
85
- primary_window?: unknown;
86
- secondary_window?: unknown;
87
- };
88
-
89
- export type BackendWindowSnapshot = {
90
- used_percent?: unknown;
91
- limit_window_seconds?: unknown;
92
- reset_at?: unknown;
93
- };
94
-
95
- export type BackendAdditionalRateLimit = {
96
- limit_name?: unknown;
97
- metered_feature?: unknown;
98
- rate_limit?: unknown;
99
- };
100
-
101
- export type BackendCreditsSnapshot = {
102
- has_credits?: unknown;
103
- unlimited?: unknown;
104
- balance?: unknown;
105
- };
106
-
107
- export type BackendRateLimitResetCredits = {
108
- available_count?: unknown;
109
- availableCount?: unknown;
110
- };
111
-
112
- export type AppServerRateLimitResponse = {
113
- rateLimits?: unknown;
114
- rateLimitsByLimitId?: unknown;
115
- rateLimitResetCredits?: unknown;
116
- };
117
-
118
- export type AppServerRateLimitSnapshot = {
119
- limitId?: unknown;
120
- limitName?: unknown;
121
- primary?: unknown;
122
- secondary?: unknown;
123
- credits?: unknown;
124
- planType?: unknown;
125
- };
126
-
127
- export type AppServerWindowSnapshot = {
128
- usedPercent?: unknown;
129
- windowDurationMins?: unknown;
130
- resetsAt?: unknown;
131
- };
132
-
133
- export type AppServerCreditsSnapshot = {
134
- hasCredits?: unknown;
135
- unlimited?: unknown;
136
- balance?: unknown;
137
- };
138
-
139
- export type AppServerRateLimitResetCredits = {
140
- availableCount?: unknown;
141
- available_count?: unknown;
142
- };
143
-
144
- export type RpcResponse = {
145
- id?: unknown;
146
- result?: unknown;
147
- error?: { message?: unknown; code?: unknown };
148
- };
149
-
150
- export type PendingRpc = {
151
- resolve: (value: unknown) => void;
152
- reject: (error: Error) => void;
153
- };
154
-
155
- export type AnthropicOAuthWindow = {
156
- utilization?: unknown;
157
- resets_at?: unknown;
158
- limit_dollars?: unknown;
159
- used_dollars?: unknown;
160
- };
161
-
162
- export type AnthropicExtraUsage = {
163
- is_enabled?: unknown;
164
- used_credits?: unknown;
165
- monthly_limit?: unknown;
166
- currency?: unknown;
167
- utilization?: unknown;
168
- reset_at?: unknown;
169
- resets_at?: unknown;
170
- };
171
-
172
- export type AnthropicOAuthUsagePayload = Record<string, unknown> & {
173
- five_hour?: unknown;
174
- seven_day?: unknown;
175
- extra_usage?: unknown;
176
- };
177
-
178
- export type CodexResetCreditPayload = {
179
- available_count?: unknown;
180
- availableCount?: unknown;
181
- credits?: unknown;
182
- };
183
-
184
- export type CodexResetCreditRowPayload = {
185
- id?: unknown;
186
- reset_type?: unknown;
187
- resetType?: unknown;
188
- status?: unknown;
189
- granted_at?: unknown;
190
- grantedAt?: unknown;
191
- expires_at?: unknown;
192
- expiresAt?: unknown;
193
- };
194
-
195
- export type CodexResetCredit = {
196
- id: string;
197
- resetType?: string;
198
- status?: string;
199
- grantedAt?: string;
200
- expiresAt?: string;
201
- };
202
-
203
- export type CodexResetCreditList = {
204
- availableCount: number;
205
- credits: CodexResetCredit[];
206
- };
207
-
208
- export type UsageProviderKey = "codex" | "anthropic";
209
-
210
- export type SharedCacheEntry = { createdAt: number; report: UsageReport };
211
-
212
- export type SharedUsageCache = {
213
- version: number;
214
- entries: Partial<Record<UsageProviderKey, SharedCacheEntry>>;
215
- backoffUntil?: Partial<Record<UsageProviderKey, number>>;
216
- };
217
-
218
- export type FooterTheme = {
219
- fg: (color: string, text: string) => string;
220
- bold: (text: string) => string;
221
- };
222
- export type FooterTui = { requestRender: () => void };
223
- export type FooterDataView = {
224
- getGitBranch: () => string | null;
225
- getExtensionStatuses: () => ReadonlyMap<string, string>;
226
- getAvailableProviderCount: () => number;
227
- onBranchChange: (callback: () => void) => () => void;
228
- };
package/src/utils.ts DELETED
@@ -1,144 +0,0 @@
1
- import { MAX_ERROR_BODY_CHARS } from "./constants.js";
2
-
3
- export function delay(ms: number): Promise<void> {
4
- return new Promise((resolve) => {
5
- const timer = setTimeout(resolve, ms);
6
- timer.unref?.();
7
- });
8
- }
9
-
10
- export function errorMessage(error: unknown): string {
11
- return error instanceof Error ? error.message : String(error);
12
- }
13
-
14
- export function parseJsonObject(text: string, description: string): Record<string, unknown> {
15
- let parsed: unknown;
16
- try {
17
- parsed = JSON.parse(text) as unknown;
18
- } catch (error) {
19
- throw new Error(`${description} was not valid JSON: ${errorMessage(error)}`);
20
- }
21
- return assertObject(parsed, description);
22
- }
23
-
24
- export function assertObject(value: unknown, description: string): Record<string, unknown> {
25
- if (!value || typeof value !== "object" || Array.isArray(value)) {
26
- throw new Error(`${description} was not an object.`);
27
- }
28
- return value as Record<string, unknown>;
29
- }
30
-
31
- export function asString(value: unknown): string | undefined {
32
- return typeof value === "string" ? value : undefined;
33
- }
34
-
35
- export function asNumber(value: unknown): number | undefined {
36
- if (typeof value === "number" && Number.isFinite(value)) return value;
37
- if (typeof value === "string" && value.trim()) {
38
- const parsed = Number(value);
39
- return Number.isFinite(parsed) ? parsed : undefined;
40
- }
41
- return undefined;
42
- }
43
-
44
- export function asBoolean(value: unknown): boolean | undefined {
45
- return typeof value === "boolean" ? value : undefined;
46
- }
47
-
48
- export function hasHeader(headers: Record<string, string>, name: string): boolean {
49
- return Object.keys(headers).some((key) => key.toLowerCase() === name.toLowerCase());
50
- }
51
-
52
- export function redactErrorBody(body: string): string {
53
- return truncateEnd(
54
- body
55
- .replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer <redacted>")
56
- .replace(/"access_token"\s*:\s*"[^"]+"/gi, '"access_token":"<redacted>"')
57
- .trim(),
58
- MAX_ERROR_BODY_CHARS,
59
- );
60
- }
61
-
62
- export function truncateEnd(value: string, maxChars: number): string {
63
- if (value.length <= maxChars) return value;
64
- return `${value.slice(0, maxChars - 1)}…`;
65
- }
66
-
67
- export function prettyJson(text: string): string {
68
- try {
69
- return JSON.stringify(JSON.parse(text), null, 2);
70
- } catch {
71
- return text.slice(0, MAX_ERROR_BODY_CHARS * 4);
72
- }
73
- }
74
-
75
- export function formatAgeShort(ms: number): string {
76
- const minutes = Math.round(ms / 60_000);
77
- if (minutes < 60) return `${Math.max(1, minutes)}m`;
78
- const hours = Math.floor(minutes / 60);
79
- if (hours < 24) return `${hours}h`;
80
- return `${Math.floor(hours / 24)}d`;
81
- }
82
-
83
- export function clampPercent(value: number): number {
84
- if (!Number.isFinite(value)) return 0;
85
- return Math.min(100, Math.max(0, value));
86
- }
87
-
88
- export function formatNumber(value: number, fallback: string): string {
89
- if (!Number.isFinite(value)) return fallback;
90
- return new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format(value);
91
- }
92
-
93
- const CURRENCY_SYMBOLS: Record<string, string> = {
94
- USD: "$",
95
- CAD: "$",
96
- AUD: "$",
97
- EUR: "\u20ac",
98
- GBP: "\u00a3",
99
- JPY: "\u00a5",
100
- };
101
-
102
- /** Consistent money formatting shared by enterprise windows and extra usage:
103
- * a currency symbol (or code) prefix with fixed decimal places. `decimals`
104
- * defaults to 2 for the full report; pass 0 for compact statusline amounts. */
105
- export function formatCurrencyAmount(amountMajorUnits: number, currencyCode: string, decimals = 2): string {
106
- if (!Number.isFinite(amountMajorUnits)) return String(amountMajorUnits);
107
- const formatted = new Intl.NumberFormat(undefined, {
108
- minimumFractionDigits: decimals,
109
- maximumFractionDigits: decimals,
110
- }).format(amountMajorUnits);
111
- const symbol = CURRENCY_SYMBOLS[currencyCode.toUpperCase()];
112
- return symbol ? `${symbol}${formatted}` : `${currencyCode} ${formatted}`;
113
- }
114
-
115
- export function compactLimitLabel(label: string): string {
116
- const normalized = label.replace(/[_-]+/g, " ").trim();
117
- const codexVariant = normalized.match(/\bcodex\s+(.+)$/i)?.[1]?.trim();
118
- const compact = codexVariant || normalized;
119
- return compact.toLowerCase().replace(/\s+/g, " ");
120
- }
121
-
122
- export function normalizedUsageKey(value: string | undefined): string | undefined {
123
- const key = value
124
- ?.toLowerCase()
125
- .replace(/[^a-z0-9]+/g, "-")
126
- .replace(/^-+|-+$/g, "");
127
- return key || undefined;
128
- }
129
-
130
- export function addNormalizedUsageKey(keys: Set<string>, value: string | undefined): void {
131
- const key = normalizedUsageKey(value);
132
- if (key) keys.add(key);
133
- }
134
-
135
- export function normalizedKeyHasToken(key: string, token: string): boolean {
136
- return key === token || key.startsWith(`${token}-`) || key.endsWith(`-${token}`) || key.includes(`-${token}-`);
137
- }
138
-
139
- export function sanitizeStatusText(text: string): string {
140
- return text
141
- .replace(/[\r\n\t]/g, " ")
142
- .replace(/ +/g, " ")
143
- .trim();
144
- }