@victor-software-house/pi-multicodex 2.0.8 → 2.0.9

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/account-manager.ts +68 -0
  2. package/package.json +2 -1
@@ -2,6 +2,7 @@ import {
2
2
  type OAuthCredentials,
3
3
  refreshOpenAICodexToken,
4
4
  } from "@mariozechner/pi-ai/oauth";
5
+ import { AuthStorage } from "@mariozechner/pi-coding-agent";
5
6
  import { normalizeUnknownError } from "pi-provider-utils/streams";
6
7
  import { loadImportedOpenAICodexAuth } from "./auth";
7
8
  import { isAccountAvailable, pickBestAccount } from "./selection";
@@ -343,6 +344,12 @@ export class AccountManager {
343
344
  return account.accessToken;
344
345
  }
345
346
 
347
+ // For the imported pi account, delegate to AuthStorage so we share pi's
348
+ // file lock and never race with pi's own refresh path.
349
+ if (account.importSource === "pi-openai-codex") {
350
+ return this.ensureValidTokenForImportedAccount(account);
351
+ }
352
+
346
353
  const inflight = this.refreshPromises.get(account.email);
347
354
  if (inflight) {
348
355
  return inflight;
@@ -370,4 +377,65 @@ export class AccountManager {
370
377
  this.refreshPromises.set(account.email, promise);
371
378
  return promise;
372
379
  }
380
+
381
+ /**
382
+ * Refresh path for the imported pi account.
383
+ *
384
+ * Uses AuthStorage so our refresh is serialised by the same file lock that
385
+ * pi's own credential refresh uses. This prevents "refresh_token_reused"
386
+ * errors caused by pi and multicodex both refreshing the same token
387
+ * simultaneously.
388
+ */
389
+ private async ensureValidTokenForImportedAccount(
390
+ account: Account,
391
+ ): Promise<string> {
392
+ // Check if pi already refreshed since our last sync.
393
+ const latest = await loadImportedOpenAICodexAuth();
394
+ if (latest && Date.now() < latest.credentials.expires - 5 * 60 * 1000) {
395
+ account.accessToken = latest.credentials.access;
396
+ account.refreshToken = latest.credentials.refresh;
397
+ account.expiresAt = latest.credentials.expires;
398
+ account.importFingerprint = latest.fingerprint;
399
+ const accountId =
400
+ typeof latest.credentials.accountId === "string"
401
+ ? latest.credentials.accountId
402
+ : undefined;
403
+ if (accountId) {
404
+ account.accountId = accountId;
405
+ }
406
+ this.save();
407
+ this.notifyStateChanged();
408
+ return account.accessToken;
409
+ }
410
+
411
+ // Both our copy and auth.json are expired — let AuthStorage refresh with
412
+ // its file lock so only one caller (us or pi) fires the API call.
413
+ const authStorage = AuthStorage.create();
414
+ const apiKey = await authStorage.getApiKey("openai-codex");
415
+ if (!apiKey) {
416
+ throw new Error(
417
+ "OpenAI Codex: token refresh failed — please re-authenticate with /login",
418
+ );
419
+ }
420
+
421
+ // Read the refreshed tokens back from auth.json.
422
+ const refreshed = await loadImportedOpenAICodexAuth();
423
+ if (refreshed) {
424
+ account.accessToken = refreshed.credentials.access;
425
+ account.refreshToken = refreshed.credentials.refresh;
426
+ account.expiresAt = refreshed.credentials.expires;
427
+ account.importFingerprint = refreshed.fingerprint;
428
+ const accountId =
429
+ typeof refreshed.credentials.accountId === "string"
430
+ ? refreshed.credentials.accountId
431
+ : undefined;
432
+ if (accountId) {
433
+ account.accountId = accountId;
434
+ }
435
+ this.save();
436
+ this.notifyStateChanged();
437
+ }
438
+
439
+ return apiKey;
440
+ }
373
441
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@victor-software-house/pi-multicodex",
3
- "version": "2.0.8",
3
+ "version": "2.0.9",
4
4
  "description": "Codex account rotation extension for pi",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -89,6 +89,7 @@
89
89
  "@semantic-release/release-notes-generator": "^14.1.0",
90
90
  "@types/node": "^25.5.0",
91
91
  "@typescript/native-preview": "7.0.0-dev.20260314.1",
92
+ "conventional-changelog-conventionalcommits": "^9.3.0",
92
93
  "semantic-release": "^25.0.3",
93
94
  "typescript": "^5.9.3",
94
95
  "vitest": "^4.1.0"