@narumitw/pi-usage 0.60.10 → 0.60.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-usage",
3
- "version": "0.60.10",
3
+ "version": "0.60.11",
4
4
  "description": "Pi extension that shows current-account usage and DeepSeek API balance for supported providers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -101,11 +101,18 @@ export async function resolveCodexResetAuth(
101
101
  const expectedModel = `${model.provider}/${model.id}`;
102
102
  const adapter = adapterForProvider("openai-codex");
103
103
  if (!adapter) throw new Error("OpenAI Codex usage support is unavailable.");
104
+ if (candidateReader?.waitUntilReady && !(await candidateReader.waitUntilReady(ctx, "openai-codex"))) {
105
+ throw new Error("OpenAI Codex OAuth credential readiness failed closed.");
106
+ }
104
107
  const auth = await resolveUsageAuth(ctx, adapter, salt, credentialReader);
105
108
  if (`${ctx.model?.provider}/${ctx.model?.id}` !== expectedModel) {
106
109
  throw new Error("The current model changed while resolving Codex reset authentication.");
107
110
  }
108
111
  if (!auth) throw new Error("No runtime credential is configured for OpenAI Codex.");
112
+ const offered = candidateReader
113
+ ? await candidateReader(ctx, "openai-codex")
114
+ : fallbackOAuthCredentialCandidates("openai-codex", credentialReader);
115
+ if (!offered.ok) throw new Error("OpenAI Codex OAuth credential discovery failed closed.");
109
116
 
110
117
  const resolvedAccess = bearerToken(headerValue(auth.headers, "Authorization")) ?? auth.apiKey;
111
118
  if (!resolvedAccess) throw new Error("OpenAI Codex OAuth credentials were incomplete.");
@@ -113,10 +120,6 @@ export async function resolveCodexResetAuth(
113
120
  if (!resolvedAccountId) {
114
121
  throw new Error("The active OpenAI Codex access token did not contain a valid account ID.");
115
122
  }
116
- const offered = candidateReader
117
- ? candidateReader(ctx, "openai-codex")
118
- : fallbackOAuthCredentialCandidates("openai-codex", credentialReader);
119
- if (!offered.ok) throw new Error("OpenAI Codex OAuth credential discovery failed closed.");
120
123
  const { accountId, storedAccess } = selectCodexResetCredential(
121
124
  offered.candidates,
122
125
  resolvedAccess,
@@ -2,6 +2,7 @@ import type { OAuthCredential } from "@earendil-works/pi-ai";
2
2
  import { type ExtensionAPI, type ExtensionContext, readStoredCredential } from "@earendil-works/pi-coding-agent";
3
3
 
4
4
  export const OAUTH_CREDENTIAL_SOURCE_CHANNEL = "oauth:credential-source:v1";
5
+ export const OAUTH_CREDENTIAL_READINESS_CHANNEL = "oauth:credential-readiness:v1";
5
6
 
6
7
  export type StoredCredentialReader = (providerId: string) => unknown;
7
8
 
@@ -9,13 +10,19 @@ export type OAuthCredentialCandidates =
9
10
  | { ok: true; candidates: readonly OAuthCredential[]; offeredCount?: number }
10
11
  | { ok: false };
11
12
 
12
- export type OAuthCredentialCandidateReader = (ctx: ExtensionContext, providerId: string) => OAuthCredentialCandidates;
13
+ export type OAuthCredentialCandidateReader = {
14
+ (ctx: ExtensionContext, providerId: string): OAuthCredentialCandidates | Promise<OAuthCredentialCandidates>;
15
+ waitUntilReady?(ctx: ExtensionContext, providerId: string): Promise<boolean>;
16
+ };
13
17
 
14
18
  export function createOAuthCredentialCandidateReader(
15
19
  pi: ExtensionAPI,
16
20
  credentialReader: StoredCredentialReader = readStoredCredential,
17
21
  ): OAuthCredentialCandidateReader {
18
- return (ctx, providerId) => collectOAuthCredentialCandidates(pi, ctx, providerId, credentialReader);
22
+ const reader: OAuthCredentialCandidateReader = (ctx, providerId) =>
23
+ collectOAuthCredentialCandidates(pi, ctx, providerId, credentialReader);
24
+ reader.waitUntilReady = (ctx, providerId) => waitForOAuthCredentialSources(pi, ctx, providerId);
25
+ return reader;
19
26
  }
20
27
 
21
28
  export function collectOAuthCredentialCandidates(
@@ -52,6 +59,36 @@ export function collectOAuthCredentialCandidates(
52
59
  return { ok: true, candidates, offeredCount };
53
60
  }
54
61
 
62
+ async function waitForOAuthCredentialSources(
63
+ pi: Pick<ExtensionAPI, "events">,
64
+ ctx: ExtensionContext,
65
+ providerId: string,
66
+ ): Promise<boolean> {
67
+ const pending: Promise<unknown>[] = [];
68
+ let collecting = true;
69
+ const request = Object.freeze({
70
+ session: ctx.sessionManager,
71
+ provider: providerId,
72
+ waitUntil(value: unknown) {
73
+ if (!collecting || !(value instanceof Promise)) return;
74
+ pending.push(value);
75
+ },
76
+ });
77
+ try {
78
+ pi.events.emit(OAUTH_CREDENTIAL_READINESS_CHANNEL, request);
79
+ } catch {
80
+ return false;
81
+ } finally {
82
+ collecting = false;
83
+ }
84
+ try {
85
+ await Promise.all(pending);
86
+ return true;
87
+ } catch {
88
+ return false;
89
+ }
90
+ }
91
+
55
92
  export function fallbackOAuthCredentialCandidates(
56
93
  providerId: string,
57
94
  credentialReader: StoredCredentialReader,
package/src/query.ts CHANGED
@@ -343,6 +343,13 @@ export async function resolveUsageAuth(
343
343
 
344
344
  const model = candidateModels(ctx, adapter.id).find((candidate) => hasOfficialOrigin(candidate, adapter.id));
345
345
  if (!model) return undefined;
346
+ if (
347
+ ["github-copilot", "xai"].includes(adapter.id) &&
348
+ candidateReader?.waitUntilReady &&
349
+ !(await candidateReader.waitUntilReady(ctx, adapter.id))
350
+ ) {
351
+ throw new Error(`${adapter.displayName} OAuth credential readiness failed closed.`);
352
+ }
346
353
  // SAFETY: Pi exposes the required auth methods at runtime, and checks below narrow them before use.
347
354
  const registry = ctx.modelRegistry as unknown as UsageAuthRegistry;
348
355
  const provider = registry.getProvider?.(adapter.id);
@@ -416,7 +423,7 @@ export async function resolveUsageAuth(
416
423
  };
417
424
  if (adapter.id === "github-copilot") {
418
425
  const offered = candidateReader
419
- ? candidateReader(ctx, adapter.id)
426
+ ? await candidateReader(ctx, adapter.id)
420
427
  : fallbackOAuthCredentialCandidates(adapter.id, credentialReader);
421
428
  if (!offered.ok) {
422
429
  throw new Error("GitHub Copilot OAuth credential discovery failed closed.");
@@ -425,7 +432,7 @@ export async function resolveUsageAuth(
425
432
  }
426
433
  if (adapter.id === "xai") {
427
434
  const offered = candidateReader
428
- ? candidateReader(ctx, adapter.id)
435
+ ? await candidateReader(ctx, adapter.id)
429
436
  : fallbackOAuthCredentialCandidates(adapter.id, credentialReader);
430
437
  if (!offered.ok) throw new Error("xAI OAuth credential discovery failed closed.");
431
438
  return finalize(resolveXaiUsageAuth(auth, model, salt, offered.candidates));