@bacnh85/pi-sub 0.1.9 → 0.1.11
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/README.md +2 -6
- package/{index.ts → extensions/index.ts} +10 -73
- package/extensions/package.json +3 -0
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -15,13 +15,9 @@ pi install npm:@bacnh85/pi-sub
|
|
|
15
15
|
From this repository checkout:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
|
|
19
|
-
npm install
|
|
20
|
-
cd ../..
|
|
21
|
-
|
|
22
|
-
pi install ./extensions/pi-sub
|
|
18
|
+
pi install ./pi-sub
|
|
23
19
|
# or test directly
|
|
24
|
-
pi -e ./
|
|
20
|
+
pi -e ./pi-sub
|
|
25
21
|
```
|
|
26
22
|
|
|
27
23
|
## What it shows
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { Box, Text } from "@earendil-works/pi-tui";
|
|
3
2
|
import { createHash } from "node:crypto";
|
|
4
3
|
import fs from "node:fs/promises";
|
|
5
4
|
import os from "node:os";
|
|
@@ -20,7 +19,6 @@ type ModelLike = { provider?: string; id?: string } | undefined;
|
|
|
20
19
|
|
|
21
20
|
type UsageApiWindow = {
|
|
22
21
|
used_percent?: number;
|
|
23
|
-
limit_window_seconds?: number;
|
|
24
22
|
reset_at?: number;
|
|
25
23
|
};
|
|
26
24
|
|
|
@@ -46,14 +44,10 @@ type PiAuthEntry = {
|
|
|
46
44
|
};
|
|
47
45
|
|
|
48
46
|
interface UsageWindow {
|
|
49
|
-
used?: number;
|
|
50
|
-
limit?: number;
|
|
51
47
|
percent?: number;
|
|
52
48
|
remaining?: number;
|
|
53
49
|
remainingLabel?: string;
|
|
54
50
|
resetLabel?: string;
|
|
55
|
-
resetsAt?: string | number | Date;
|
|
56
|
-
label?: string;
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
interface SubscriptionAccountSnapshot {
|
|
@@ -63,12 +57,10 @@ interface SubscriptionAccountSnapshot {
|
|
|
63
57
|
plan?: string;
|
|
64
58
|
fiveHour?: UsageWindow;
|
|
65
59
|
weekly?: UsageWindow;
|
|
66
|
-
monthly?: UsageWindow;
|
|
67
60
|
lastActivity?: string;
|
|
68
61
|
}
|
|
69
62
|
|
|
70
63
|
interface SubscriptionUsageSnapshot {
|
|
71
|
-
providerId: string;
|
|
72
64
|
providerDisplayName: string;
|
|
73
65
|
accounts: SubscriptionAccountSnapshot[];
|
|
74
66
|
activeAccount?: SubscriptionAccountSnapshot;
|
|
@@ -77,12 +69,11 @@ interface SubscriptionUsageSnapshot {
|
|
|
77
69
|
cost?: number;
|
|
78
70
|
}
|
|
79
71
|
|
|
80
|
-
|
|
72
|
+
type SubscriptionProviderAdapter = {
|
|
81
73
|
id: string;
|
|
82
74
|
displayName: string;
|
|
83
|
-
isModelSupported(model: ModelLike): boolean;
|
|
84
75
|
fetchUsage(signal?: AbortSignal): Promise<SubscriptionUsageSnapshot>;
|
|
85
|
-
}
|
|
76
|
+
};
|
|
86
77
|
|
|
87
78
|
interface State {
|
|
88
79
|
model?: ModelLike;
|
|
@@ -246,8 +237,6 @@ function usageWindowFromApi(window: UsageApiWindow | undefined): UsageWindow | u
|
|
|
246
237
|
remaining,
|
|
247
238
|
remainingLabel,
|
|
248
239
|
resetLabel,
|
|
249
|
-
resetsAt: window.reset_at,
|
|
250
|
-
label: remainingLabel ? `${remaining}% (${remainingLabel})` : `${remaining}%`,
|
|
251
240
|
};
|
|
252
241
|
}
|
|
253
242
|
|
|
@@ -270,7 +259,6 @@ function parseUsageResponse(body: unknown): UsageApiSnapshot | undefined {
|
|
|
270
259
|
if (!window || typeof window !== "object" || typeof window.used_percent !== "number") return undefined;
|
|
271
260
|
return {
|
|
272
261
|
used_percent: window.used_percent,
|
|
273
|
-
limit_window_seconds: typeof window.limit_window_seconds === "number" ? window.limit_window_seconds : undefined,
|
|
274
262
|
reset_at: typeof window.reset_at === "number" ? window.reset_at : undefined,
|
|
275
263
|
};
|
|
276
264
|
};
|
|
@@ -289,13 +277,11 @@ async function readPiCodexAuth(): Promise<PiAuthEntry & { accountId: string }> {
|
|
|
289
277
|
return { ...entry, accountId };
|
|
290
278
|
}
|
|
291
279
|
|
|
292
|
-
async function readOpenCodeGoAuth(): Promise<
|
|
280
|
+
async function readOpenCodeGoAuth(): Promise<SubscriptionAccountSnapshot> {
|
|
293
281
|
const auth = await readJsonFile<PiAuthFile>(piAuthPath());
|
|
294
282
|
const entry = auth[OPC_PROVIDER];
|
|
295
283
|
if (!entry?.key && !entry?.accountId) throw new Error("Missing opencode-go API key or accountId in Pi auth");
|
|
296
|
-
|
|
297
|
-
if (typeof entry.key === "string") return { key: entry.key, account };
|
|
298
|
-
return { accountId: typeof entry.accountId === "string" ? entry.accountId : undefined, account };
|
|
284
|
+
return authAccountSnapshot("OpenCode Go", entry, { plan: "Go" });
|
|
299
285
|
}
|
|
300
286
|
|
|
301
287
|
async function readZaiAuth(): Promise<{ key: string; account: SubscriptionAccountSnapshot }> {
|
|
@@ -341,7 +327,6 @@ async function fetchCodexUsage(signal?: AbortSignal): Promise<SubscriptionUsageS
|
|
|
341
327
|
const usage = await fetchUsageFromPiAuth(entry, signal);
|
|
342
328
|
activeAccount = mergeUsageIntoAccount(activeAccount, usage);
|
|
343
329
|
return {
|
|
344
|
-
providerId: CODEX_PROVIDER,
|
|
345
330
|
providerDisplayName: "Codex",
|
|
346
331
|
accounts: [activeAccount],
|
|
347
332
|
activeAccount,
|
|
@@ -349,7 +334,6 @@ async function fetchCodexUsage(signal?: AbortSignal): Promise<SubscriptionUsageS
|
|
|
349
334
|
};
|
|
350
335
|
} catch (error) {
|
|
351
336
|
return {
|
|
352
|
-
providerId: CODEX_PROVIDER,
|
|
353
337
|
providerDisplayName: "Codex",
|
|
354
338
|
accounts: [],
|
|
355
339
|
fetchedAt: Date.now(),
|
|
@@ -360,9 +344,8 @@ async function fetchCodexUsage(signal?: AbortSignal): Promise<SubscriptionUsageS
|
|
|
360
344
|
|
|
361
345
|
async function fetchOpenCodeGoUsage(_signal?: AbortSignal): Promise<SubscriptionUsageSnapshot> {
|
|
362
346
|
try {
|
|
363
|
-
const
|
|
347
|
+
const account = await readOpenCodeGoAuth();
|
|
364
348
|
return {
|
|
365
|
-
providerId: OPC_PROVIDER,
|
|
366
349
|
providerDisplayName: "OpenCode Go",
|
|
367
350
|
accounts: [account],
|
|
368
351
|
activeAccount: account,
|
|
@@ -370,7 +353,6 @@ async function fetchOpenCodeGoUsage(_signal?: AbortSignal): Promise<Subscription
|
|
|
370
353
|
};
|
|
371
354
|
} catch (error) {
|
|
372
355
|
return {
|
|
373
|
-
providerId: OPC_PROVIDER,
|
|
374
356
|
providerDisplayName: "OpenCode Go",
|
|
375
357
|
accounts: [],
|
|
376
358
|
fetchedAt: Date.now(),
|
|
@@ -418,8 +400,6 @@ function zaiLimitToUsageWindow(limit: ZaiLimitEntry): UsageWindow | undefined {
|
|
|
418
400
|
remaining,
|
|
419
401
|
remainingLabel,
|
|
420
402
|
resetLabel,
|
|
421
|
-
resetsAt: limit.nextResetTime,
|
|
422
|
-
label: remainingLabel ? `${remaining}% (${remainingLabel})` : `${remaining}%`,
|
|
423
403
|
};
|
|
424
404
|
}
|
|
425
405
|
|
|
@@ -473,7 +453,6 @@ async function fetchZaiUsage(signal?: AbortSignal): Promise<SubscriptionUsageSna
|
|
|
473
453
|
};
|
|
474
454
|
|
|
475
455
|
return {
|
|
476
|
-
providerId: ZAI_PROVIDER,
|
|
477
456
|
providerDisplayName: "Z.ai",
|
|
478
457
|
accounts: [account],
|
|
479
458
|
activeAccount: account,
|
|
@@ -481,7 +460,6 @@ async function fetchZaiUsage(signal?: AbortSignal): Promise<SubscriptionUsageSna
|
|
|
481
460
|
};
|
|
482
461
|
} catch (error) {
|
|
483
462
|
return {
|
|
484
|
-
providerId: ZAI_PROVIDER,
|
|
485
463
|
providerDisplayName: "Z.ai",
|
|
486
464
|
accounts: [],
|
|
487
465
|
fetchedAt: Date.now(),
|
|
@@ -490,31 +468,11 @@ async function fetchZaiUsage(signal?: AbortSignal): Promise<SubscriptionUsageSna
|
|
|
490
468
|
}
|
|
491
469
|
}
|
|
492
470
|
|
|
493
|
-
const codexAdapter: SubscriptionProviderAdapter = {
|
|
494
|
-
id: CODEX_PROVIDER,
|
|
495
|
-
displayName: "Codex",
|
|
496
|
-
isModelSupported: isCodexModel,
|
|
497
|
-
fetchUsage: fetchCodexUsage,
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
const openCodeGoAdapter: SubscriptionProviderAdapter = {
|
|
501
|
-
id: OPC_PROVIDER,
|
|
502
|
-
displayName: "OpenCode Go",
|
|
503
|
-
isModelSupported: isOpenCodeGoModel,
|
|
504
|
-
fetchUsage: fetchOpenCodeGoUsage,
|
|
505
|
-
};
|
|
506
|
-
|
|
507
|
-
const zaiAdapter: SubscriptionProviderAdapter = {
|
|
508
|
-
id: ZAI_PROVIDER,
|
|
509
|
-
displayName: "Z.ai",
|
|
510
|
-
isModelSupported: isZaiModel,
|
|
511
|
-
fetchUsage: fetchZaiUsage,
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
const adapters = [codexAdapter, openCodeGoAdapter, zaiAdapter];
|
|
515
|
-
|
|
516
471
|
function supportedAdapter(model: ModelLike): SubscriptionProviderAdapter | undefined {
|
|
517
|
-
|
|
472
|
+
if (isCodexModel(model)) return { id: CODEX_PROVIDER, displayName: "Codex", fetchUsage: fetchCodexUsage };
|
|
473
|
+
if (isOpenCodeGoModel(model)) return { id: OPC_PROVIDER, displayName: "OpenCode Go", fetchUsage: fetchOpenCodeGoUsage };
|
|
474
|
+
if (isZaiModel(model)) return { id: ZAI_PROVIDER, displayName: "Z.ai", fetchUsage: fetchZaiUsage };
|
|
475
|
+
return undefined;
|
|
518
476
|
}
|
|
519
477
|
|
|
520
478
|
function formatRemaining(window: UsageWindow | undefined): string {
|
|
@@ -525,20 +483,10 @@ function formatRemaining(window: UsageWindow | undefined): string {
|
|
|
525
483
|
return "?";
|
|
526
484
|
}
|
|
527
485
|
|
|
528
|
-
function formatWindow(window: UsageWindow | undefined, _compact = true): string {
|
|
529
|
-
if (!window) return "?";
|
|
530
|
-
if (window.percent !== undefined && window.resetLabel) return `${window.percent}% (${window.resetLabel})`;
|
|
531
|
-
if (window.label) return window.label;
|
|
532
|
-
if (window.percent !== undefined) return `${window.percent}%`;
|
|
533
|
-
if (window.used !== undefined && window.limit !== undefined) return `${window.used}/${window.limit}`;
|
|
534
|
-
return "?";
|
|
535
|
-
}
|
|
536
|
-
|
|
537
486
|
function minRemaining(account: SubscriptionAccountSnapshot | undefined): number {
|
|
538
487
|
const values: number[] = [];
|
|
539
488
|
if (account?.fiveHour?.remaining !== undefined) values.push(account.fiveHour.remaining);
|
|
540
489
|
if (account?.weekly?.remaining !== undefined) values.push(account.weekly.remaining);
|
|
541
|
-
if (account?.monthly?.remaining !== undefined) values.push(account.monthly.remaining);
|
|
542
490
|
if (values.length === 0) return 100;
|
|
543
491
|
return Math.min(...values);
|
|
544
492
|
}
|
|
@@ -548,7 +496,6 @@ function windowSegments(account: SubscriptionAccountSnapshot | undefined): strin
|
|
|
548
496
|
const segments: string[] = [];
|
|
549
497
|
if (account.fiveHour) segments.push(`R:${formatRemaining(account.fiveHour)}`);
|
|
550
498
|
if (account.weekly) segments.push(`W:${formatRemaining(account.weekly)}`);
|
|
551
|
-
if (account.monthly) segments.push(`M:${formatRemaining(account.monthly)}`);
|
|
552
499
|
return segments;
|
|
553
500
|
}
|
|
554
501
|
|
|
@@ -688,12 +635,8 @@ function buildDetails(snapshot: SubscriptionUsageSnapshot | undefined, state: St
|
|
|
688
635
|
|
|
689
636
|
const hasFiveHour = snapshot.accounts.some((a) => a.fiveHour);
|
|
690
637
|
const hasWeekly = snapshot.accounts.some((a) => a.weekly);
|
|
691
|
-
const hasMonthly = snapshot.accounts.some((a) => a.monthly);
|
|
692
|
-
|
|
693
638
|
if (hasFiveHour) columns.push({ key: "five", label: "ROLLING", get: (a) => formatRemaining(a.fiveHour) });
|
|
694
639
|
if (hasWeekly) columns.push({ key: "weekly", label: "WEEKLY", get: (a) => formatRemaining(a.weekly) });
|
|
695
|
-
if (hasMonthly) columns.push({ key: "monthly", label: "MONTHLY", get: (a) => formatRemaining(a.monthly) });
|
|
696
|
-
|
|
697
640
|
const rows = snapshot.accounts.map((account) => ({
|
|
698
641
|
active: account.isActive ? "*" : " ",
|
|
699
642
|
snapshot: account,
|
|
@@ -714,7 +657,7 @@ function buildDetails(snapshot: SubscriptionUsageSnapshot | undefined, state: St
|
|
|
714
657
|
|
|
715
658
|
const costLine = snapshot.cost !== undefined ? `\nSession cost: $${snapshot.cost.toFixed(2)}` : "";
|
|
716
659
|
const lines = [`Provider: ${snapshot.providerDisplayName} · Model: ${state.model?.id ?? "unknown-model"} · Fetched: ${new Date(snapshot.fetchedAt).toLocaleTimeString()}${costLine}`, "", header, sep, ...body];
|
|
717
|
-
if (!hasFiveHour && !hasWeekly
|
|
660
|
+
if (!hasFiveHour && !hasWeekly) {
|
|
718
661
|
lines.push("", `${snapshot.providerDisplayName} does not expose usage windows.`);
|
|
719
662
|
}
|
|
720
663
|
return lines.join("\n");
|
|
@@ -723,12 +666,6 @@ function buildDetails(snapshot: SubscriptionUsageSnapshot | undefined, state: St
|
|
|
723
666
|
export default function (pi: ExtensionAPI) {
|
|
724
667
|
const state: State = { lastRefreshAt: 0, refreshGeneration: 0 };
|
|
725
668
|
|
|
726
|
-
pi.registerMessageRenderer(MESSAGE_TYPE, (message, _options, theme) => {
|
|
727
|
-
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
|
728
|
-
box.addChild(new Text(String(message.content ?? ""), 0, 0));
|
|
729
|
-
return box;
|
|
730
|
-
});
|
|
731
|
-
|
|
732
669
|
pi.on("session_start", async (_event, ctx) => {
|
|
733
670
|
updateActiveAdapter(ctx, state, ctx.model);
|
|
734
671
|
if (state.adapter) void refreshUsage(ctx, state, true);
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bacnh85/pi-sub",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Pi extension showing subscription usage for supported model providers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
|
-
"homepage": "https://github.com/bacnh85/
|
|
10
|
+
"homepage": "https://github.com/bacnh85/pi-extensions#readme",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "git+https://github.com/bacnh85/
|
|
14
|
-
"directory": "
|
|
13
|
+
"url": "git+https://github.com/bacnh85/pi-extensions.git",
|
|
14
|
+
"directory": "pi-sub"
|
|
15
15
|
},
|
|
16
16
|
"bugs": {
|
|
17
|
-
"url": "https://github.com/bacnh85/
|
|
17
|
+
"url": "https://github.com/bacnh85/pi-extensions/issues"
|
|
18
18
|
},
|
|
19
19
|
"keywords": [
|
|
20
20
|
"pi-package",
|
|
@@ -26,18 +26,18 @@
|
|
|
26
26
|
],
|
|
27
27
|
"files": [
|
|
28
28
|
"README.md",
|
|
29
|
-
"index.ts"
|
|
29
|
+
"extensions/index.ts",
|
|
30
|
+
"extensions/package.json"
|
|
30
31
|
],
|
|
31
32
|
"pi": {
|
|
32
33
|
"extensions": [
|
|
33
|
-
"./index.ts"
|
|
34
|
+
"./extensions/index.ts"
|
|
34
35
|
]
|
|
35
36
|
},
|
|
36
37
|
"engines": {
|
|
37
38
|
"node": ">=20.3.0"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
|
-
"@earendil-works/pi-coding-agent": "*"
|
|
41
|
-
"@earendil-works/pi-tui": "*"
|
|
41
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
42
42
|
}
|
|
43
43
|
}
|