@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.
Files changed (2) hide show
  1. package/index.ts +35 -15
  2. 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
- if (!entry?.access || !entry.accountId) throw new Error("Missing openai-codex OAuth entry in Pi auth");
246
- return entry;
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: [account],
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 = supportedAdapter(model);
447
- if (!state.adapter) {
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.inFlight = undefined;
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.5",
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": "*"