@bacnh85/pi-sub 0.1.5 → 0.1.6
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/index.ts +35 -15
- package/package.json +2 -1
package/index.ts
CHANGED
|
@@ -80,8 +80,10 @@ interface SubscriptionProviderAdapter {
|
|
|
80
80
|
interface State {
|
|
81
81
|
model?: ModelLike;
|
|
82
82
|
adapter?: SubscriptionProviderAdapter;
|
|
83
|
+
adapterId?: string;
|
|
83
84
|
snapshot?: SubscriptionUsageSnapshot;
|
|
84
85
|
lastRefreshAt: number;
|
|
86
|
+
refreshGeneration: number;
|
|
85
87
|
inFlight?: Promise<SubscriptionUsageSnapshot>;
|
|
86
88
|
refreshTimer?: NodeJS.Timeout;
|
|
87
89
|
debounceTimer?: NodeJS.Timeout;
|
|
@@ -132,6 +134,14 @@ function accountFromPiAuth(entry: PiAuthEntry): SubscriptionAccountSnapshot {
|
|
|
132
134
|
};
|
|
133
135
|
}
|
|
134
136
|
|
|
137
|
+
function getCodexAccountId(entry: PiAuthEntry | undefined): string | undefined {
|
|
138
|
+
if (!entry) return undefined;
|
|
139
|
+
if (typeof entry.accountId === "string" && entry.accountId.length > 0) return entry.accountId;
|
|
140
|
+
const claims = decodeJwtPayload(entry.access);
|
|
141
|
+
const auth = claims?.["https://api.openai.com/auth"];
|
|
142
|
+
return typeof auth?.chatgpt_account_id === "string" ? auth.chatgpt_account_id : undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
135
145
|
function planLabel(plan: string | undefined): string | undefined {
|
|
136
146
|
if (!plan) return undefined;
|
|
137
147
|
const normalized = plan.toLowerCase().replace(/[_-]+/g, " ");
|
|
@@ -239,11 +249,12 @@ function parseOpcodeUsageResponse(body: unknown): UsageApiSnapshot | undefined {
|
|
|
239
249
|
};
|
|
240
250
|
}
|
|
241
251
|
|
|
242
|
-
async function readPiCodexAuth(): Promise<PiAuthEntry> {
|
|
252
|
+
async function readPiCodexAuth(): Promise<PiAuthEntry & { accountId: string }> {
|
|
243
253
|
const auth = await readJsonFile<PiAuthFile>(piAuthPath());
|
|
244
254
|
const entry = auth[CODEX_PROVIDER];
|
|
245
|
-
|
|
246
|
-
|
|
255
|
+
const accountId = getCodexAccountId(entry!);
|
|
256
|
+
if (!entry?.access || !accountId) throw new Error("Missing openai-codex OAuth entry in Pi auth");
|
|
257
|
+
return { ...entry, accountId };
|
|
247
258
|
}
|
|
248
259
|
|
|
249
260
|
async function readOpenCodeGoAuth(): Promise<{ key: string }> {
|
|
@@ -260,7 +271,7 @@ async function fetchUsageFromPiAuth(entry: PiAuthEntry, signal?: AbortSignal): P
|
|
|
260
271
|
headers: {
|
|
261
272
|
Accept: "application/json",
|
|
262
273
|
Authorization: `Bearer ${entry.access}`,
|
|
263
|
-
"ChatGPT-Account-Id": entry.accountId
|
|
274
|
+
"ChatGPT-Account-Id": getCodexAccountId(entry) ?? entry.accountId,
|
|
264
275
|
"User-Agent": "pi-sub/0.1.0",
|
|
265
276
|
},
|
|
266
277
|
signal: combinedSignal,
|
|
@@ -306,17 +317,12 @@ async function fetchCodexUsage(signal?: AbortSignal): Promise<SubscriptionUsageS
|
|
|
306
317
|
async function fetchOpenCodeGoUsage(signal?: AbortSignal): Promise<SubscriptionUsageSnapshot> {
|
|
307
318
|
try {
|
|
308
319
|
await readOpenCodeGoAuth();
|
|
309
|
-
const account: SubscriptionAccountSnapshot = {
|
|
310
|
-
isActive: true,
|
|
311
|
-
accountLabel: "OpenCode Go",
|
|
312
|
-
lastActivity: "Now",
|
|
313
|
-
};
|
|
314
320
|
return {
|
|
315
321
|
providerId: OPC_PROVIDER,
|
|
316
322
|
providerDisplayName: "OpenCode Go",
|
|
317
|
-
accounts: [
|
|
318
|
-
activeAccount: account,
|
|
323
|
+
accounts: [],
|
|
319
324
|
fetchedAt: Date.now(),
|
|
325
|
+
error: "OpenCode Go usage tracking not yet implemented",
|
|
320
326
|
};
|
|
321
327
|
} catch (error) {
|
|
322
328
|
return {
|
|
@@ -442,11 +448,21 @@ function stopTimer(state: State): void {
|
|
|
442
448
|
}
|
|
443
449
|
|
|
444
450
|
function updateActiveAdapter(ctx: ExtensionContext, state: State, model: ModelLike): void {
|
|
451
|
+
const nextAdapter = supportedAdapter(model);
|
|
452
|
+
const adapterChanged = state.adapterId !== nextAdapter?.id;
|
|
453
|
+
|
|
445
454
|
state.model = model;
|
|
446
|
-
state.adapter =
|
|
447
|
-
|
|
455
|
+
state.adapter = nextAdapter;
|
|
456
|
+
state.adapterId = nextAdapter?.id;
|
|
457
|
+
|
|
458
|
+
if (adapterChanged) {
|
|
448
459
|
state.snapshot = undefined;
|
|
449
460
|
state.lastRefreshAt = 0;
|
|
461
|
+
state.inFlight = undefined;
|
|
462
|
+
state.refreshGeneration++;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (!state.adapter) {
|
|
450
466
|
stopTimer(state);
|
|
451
467
|
}
|
|
452
468
|
renderSubscriptionLine(ctx, state);
|
|
@@ -461,15 +477,19 @@ async function refreshUsage(ctx: ExtensionContext, state: State, force: boolean)
|
|
|
461
477
|
}
|
|
462
478
|
if (!force && state.snapshot && Date.now() - state.lastRefreshAt < REFRESH_TTL_MS) return state.snapshot;
|
|
463
479
|
if (state.inFlight) return state.inFlight;
|
|
480
|
+
const generation = state.refreshGeneration;
|
|
464
481
|
renderSubscriptionLine(ctx, state);
|
|
465
482
|
state.inFlight = adapter.fetchUsage(ctx.signal).then((snapshot) => {
|
|
483
|
+
if (state.refreshGeneration !== generation) return snapshot;
|
|
466
484
|
snapshot.cost = aggregateSessionCost(ctx);
|
|
467
485
|
state.snapshot = snapshot;
|
|
468
486
|
state.lastRefreshAt = Date.now();
|
|
469
487
|
renderSubscriptionLine(ctx, state);
|
|
470
488
|
return snapshot;
|
|
471
489
|
}).finally(() => {
|
|
472
|
-
state.
|
|
490
|
+
if (state.refreshGeneration === generation) {
|
|
491
|
+
state.inFlight = undefined;
|
|
492
|
+
}
|
|
473
493
|
});
|
|
474
494
|
return state.inFlight;
|
|
475
495
|
}
|
|
@@ -529,7 +549,7 @@ function buildDetails(snapshot: SubscriptionUsageSnapshot | undefined, state: St
|
|
|
529
549
|
}
|
|
530
550
|
|
|
531
551
|
export default function (pi: ExtensionAPI) {
|
|
532
|
-
const state: State = { lastRefreshAt: 0 };
|
|
552
|
+
const state: State = { lastRefreshAt: 0, refreshGeneration: 0 };
|
|
533
553
|
|
|
534
554
|
pi.registerMessageRenderer(MESSAGE_TYPE, (message, _options, theme) => {
|
|
535
555
|
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bacnh85/pi-sub",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Pi extension showing subscription usage for supported model providers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"./index.ts"
|
|
34
34
|
]
|
|
35
35
|
},
|
|
36
|
+
"engines": { "node": ">=20.3.0" },
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"@earendil-works/pi-coding-agent": "*",
|
|
38
39
|
"@earendil-works/pi-tui": "*"
|