@victor-software-house/pi-multicodex 2.1.3 → 2.1.5

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.
@@ -313,6 +313,42 @@ export class AccountManager {
313
313
  );
314
314
  }
315
315
 
316
+ private getLinkedImportedAccount(): Account | undefined {
317
+ return this.data.accounts.find(
318
+ (account) =>
319
+ account.importSource === "pi-openai-codex" &&
320
+ account.importMode !== "synthetic",
321
+ );
322
+ }
323
+
324
+ private getSyntheticImportedAccount(): Account | undefined {
325
+ return this.data.accounts.find(
326
+ (account) =>
327
+ account.importSource === "pi-openai-codex" &&
328
+ account.importMode === "synthetic",
329
+ );
330
+ }
331
+
332
+ private findManagedImportedTarget(imported: {
333
+ identifier: string;
334
+ credentials: OAuthCredentials;
335
+ }): Account | undefined {
336
+ const byRefreshToken = this.data.accounts.find(
337
+ (account) =>
338
+ account.importMode !== "synthetic" &&
339
+ account.refreshToken === imported.credentials.refresh,
340
+ );
341
+ if (byRefreshToken) {
342
+ return byRefreshToken;
343
+ }
344
+
345
+ return this.data.accounts.find(
346
+ (account) =>
347
+ account.importMode !== "synthetic" &&
348
+ account.email === imported.identifier,
349
+ );
350
+ }
351
+
316
352
  private clearImportedLink(account: Account): boolean {
317
353
  let changed = false;
318
354
  if (account.importSource) {
@@ -334,15 +370,18 @@ export class AccountManager {
334
370
  const imported = await loadImportedOpenAICodexAuth();
335
371
  if (!imported) return false;
336
372
 
337
- const existingImported = this.getImportedAccount();
338
- if (existingImported?.importFingerprint === imported.fingerprint) {
373
+ const linkedImported = this.getLinkedImportedAccount();
374
+ const syntheticImported = this.getSyntheticImportedAccount();
375
+ const currentImported = linkedImported ?? syntheticImported;
376
+ if (
377
+ currentImported?.importFingerprint === imported.fingerprint &&
378
+ (currentImported.importMode !== "synthetic" ||
379
+ currentImported.email === imported.identifier)
380
+ ) {
339
381
  return false;
340
382
  }
341
383
 
342
- const matchingAccount = this.findAccountByRefreshToken(
343
- imported.credentials.refresh,
344
- existingImported?.email,
345
- );
384
+ const matchingAccount = this.findManagedImportedTarget(imported);
346
385
  if (matchingAccount) {
347
386
  let changed = this.applyCredentials(
348
387
  matchingAccount,
@@ -353,12 +392,11 @@ export class AccountManager {
353
392
  importFingerprint: imported.fingerprint,
354
393
  },
355
394
  );
356
- if (existingImported && existingImported !== matchingAccount) {
357
- if (existingImported.importMode === "synthetic") {
358
- changed = this.removeAccountRecord(existingImported) || changed;
359
- } else {
360
- changed = this.clearImportedLink(existingImported) || changed;
361
- }
395
+ if (linkedImported && linkedImported !== matchingAccount) {
396
+ changed = this.clearImportedLink(linkedImported) || changed;
397
+ }
398
+ if (syntheticImported) {
399
+ changed = this.removeAccountRecord(syntheticImported) || changed;
362
400
  }
363
401
  if (changed) {
364
402
  this.save();
@@ -367,17 +405,24 @@ export class AccountManager {
367
405
  return changed;
368
406
  }
369
407
 
370
- if (existingImported?.importMode === "synthetic") {
371
- const target = this.getAccount(imported.identifier);
408
+ if (linkedImported) {
409
+ const changed = this.clearImportedLink(linkedImported);
410
+ if (changed) {
411
+ this.save();
412
+ this.notifyStateChanged();
413
+ }
414
+ }
415
+
416
+ if (syntheticImported) {
372
417
  let changed = false;
373
- if (!target && existingImported.email !== imported.identifier) {
418
+ if (syntheticImported.email !== imported.identifier) {
374
419
  changed = this.updateAccountEmail(
375
- existingImported,
420
+ syntheticImported,
376
421
  imported.identifier,
377
422
  );
378
423
  }
379
424
  changed =
380
- this.applyCredentials(existingImported, imported.credentials, {
425
+ this.applyCredentials(syntheticImported, imported.credentials, {
381
426
  importSource: "pi-openai-codex",
382
427
  importMode: "synthetic",
383
428
  importFingerprint: imported.fingerprint,
@@ -389,14 +434,6 @@ export class AccountManager {
389
434
  return changed;
390
435
  }
391
436
 
392
- if (existingImported) {
393
- const changed = this.clearImportedLink(existingImported);
394
- if (changed) {
395
- this.save();
396
- this.notifyStateChanged();
397
- }
398
- }
399
-
400
437
  this.addOrUpdateAccount(imported.identifier, imported.credentials, {
401
438
  importSource: "pi-openai-codex",
402
439
  importMode: "synthetic",
package/auth.ts CHANGED
@@ -45,7 +45,44 @@ function getRequiredString(
45
45
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
46
46
  }
47
47
 
48
- function createImportedIdentifier(accountId: string): string {
48
+ function decodeJwtPayload(token: string): Record<string, unknown> | undefined {
49
+ const parts = token.split(".");
50
+ if (parts.length !== 3) return undefined;
51
+ const payload = parts[1];
52
+ if (!payload) return undefined;
53
+ try {
54
+ const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
55
+ const padded = normalized.padEnd(
56
+ normalized.length + ((4 - (normalized.length % 4)) % 4),
57
+ "=",
58
+ );
59
+ const decoded = Buffer.from(padded, "base64").toString("utf8");
60
+ const parsed = JSON.parse(decoded) as unknown;
61
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
62
+ return undefined;
63
+ }
64
+ return parsed as Record<string, unknown>;
65
+ } catch {
66
+ return undefined;
67
+ }
68
+ }
69
+
70
+ function getProfileEmail(accessToken: string): string | undefined {
71
+ const payload = decodeJwtPayload(accessToken);
72
+ const profile = payload?.["https://api.openai.com/profile"];
73
+ if (!profile || typeof profile !== "object" || Array.isArray(profile)) {
74
+ return undefined;
75
+ }
76
+ const email = (profile as Record<string, unknown>).email;
77
+ return typeof email === "string" && email.trim() ? email.trim() : undefined;
78
+ }
79
+
80
+ function createImportedIdentifier(
81
+ accessToken: string,
82
+ accountId: string,
83
+ ): string {
84
+ const email = getProfileEmail(accessToken);
85
+ if (email) return email;
49
86
  return `${IMPORTED_ACCOUNT_PREFIX} ${accountId.slice(0, 8)}`;
50
87
  }
51
88
 
@@ -84,7 +121,7 @@ export function parseImportedOpenAICodexAuth(
84
121
  accountId,
85
122
  };
86
123
  return {
87
- identifier: createImportedIdentifier(accountId ?? "default"),
124
+ identifier: createImportedIdentifier(access, accountId ?? "default"),
88
125
  fingerprint: createFingerprint({ access, refresh, expires, accountId }),
89
126
  credentials,
90
127
  };
package/commands.ts CHANGED
@@ -99,7 +99,11 @@ function formatAccountStatusLine(
99
99
  const quotaHit =
100
100
  account.quotaExhaustedUntil && account.quotaExhaustedUntil > Date.now();
101
101
  const untouched = isUsageUntouched(usage) ? "untouched" : null;
102
- const imported = account.importSource ? "linked-auth" : null;
102
+ const imported = account.importSource
103
+ ? account.importMode === "synthetic"
104
+ ? "pi auth only"
105
+ : "pi auth"
106
+ : null;
103
107
  const reauth = account.needsReauth ? "needs reauth" : null;
104
108
  const tags = [
105
109
  active?.email === account.email ? "active" : null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@victor-software-house/pi-multicodex",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Codex account rotation extension for pi",
5
5
  "license": "MIT",
6
6
  "type": "module",