@victor-software-house/pi-multicodex 2.0.9 → 2.0.10
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/account-manager.ts +34 -3
- package/commands.ts +16 -6
- package/extension.ts +4 -2
- package/hooks.ts +27 -4
- package/package.json +1 -1
- package/selection.ts +1 -0
- package/storage.ts +1 -0
package/account-manager.ts
CHANGED
|
@@ -80,6 +80,7 @@ export class AccountManager {
|
|
|
80
80
|
existing.expiresAt = creds.expires;
|
|
81
81
|
existing.importSource = options?.importSource;
|
|
82
82
|
existing.importFingerprint = options?.importFingerprint;
|
|
83
|
+
existing.needsReauth = undefined;
|
|
83
84
|
if (accountId) {
|
|
84
85
|
existing.accountId = accountId;
|
|
85
86
|
}
|
|
@@ -231,10 +232,22 @@ export class AccountManager {
|
|
|
231
232
|
return this.usageCache.get(email);
|
|
232
233
|
}
|
|
233
234
|
|
|
235
|
+
getAccountsNeedingReauth(): Account[] {
|
|
236
|
+
return this.data.accounts.filter((a) => a.needsReauth);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private markNeedsReauth(account: Account): void {
|
|
240
|
+
account.needsReauth = true;
|
|
241
|
+
this.save();
|
|
242
|
+
this.notifyStateChanged();
|
|
243
|
+
}
|
|
244
|
+
|
|
234
245
|
async refreshUsageForAccount(
|
|
235
246
|
account: Account,
|
|
236
247
|
options?: { force?: boolean; signal?: AbortSignal },
|
|
237
248
|
): Promise<CodexUsageSnapshot | undefined> {
|
|
249
|
+
if (account.needsReauth) return this.usageCache.get(account.email);
|
|
250
|
+
|
|
238
251
|
const cached = this.usageCache.get(account.email);
|
|
239
252
|
const now = Date.now();
|
|
240
253
|
if (
|
|
@@ -340,6 +353,15 @@ export class AccountManager {
|
|
|
340
353
|
}
|
|
341
354
|
|
|
342
355
|
async ensureValidToken(account: Account): Promise<string> {
|
|
356
|
+
if (account.needsReauth) {
|
|
357
|
+
const hint = account.importSource
|
|
358
|
+
? "/login openai-codex"
|
|
359
|
+
: `/multicodex use ${account.email}`;
|
|
360
|
+
throw new Error(
|
|
361
|
+
`${account.email}: re-authentication required — run ${hint}`,
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
343
365
|
if (Date.now() < account.expiresAt - 5 * 60 * 1000) {
|
|
344
366
|
return account.accessToken;
|
|
345
367
|
}
|
|
@@ -369,6 +391,9 @@ export class AccountManager {
|
|
|
369
391
|
this.save();
|
|
370
392
|
this.notifyStateChanged();
|
|
371
393
|
return account.accessToken;
|
|
394
|
+
} catch (error) {
|
|
395
|
+
this.markNeedsReauth(account);
|
|
396
|
+
throw error;
|
|
372
397
|
} finally {
|
|
373
398
|
this.refreshPromises.delete(account.email);
|
|
374
399
|
}
|
|
@@ -410,11 +435,17 @@ export class AccountManager {
|
|
|
410
435
|
|
|
411
436
|
// Both our copy and auth.json are expired — let AuthStorage refresh with
|
|
412
437
|
// its file lock so only one caller (us or pi) fires the API call.
|
|
413
|
-
|
|
414
|
-
|
|
438
|
+
let apiKey: string | undefined;
|
|
439
|
+
try {
|
|
440
|
+
const authStorage = AuthStorage.create();
|
|
441
|
+
apiKey = await authStorage.getApiKey("openai-codex");
|
|
442
|
+
} catch {
|
|
443
|
+
// AuthStorage refresh failed; mark for re-auth below.
|
|
444
|
+
}
|
|
415
445
|
if (!apiKey) {
|
|
446
|
+
this.markNeedsReauth(account);
|
|
416
447
|
throw new Error(
|
|
417
|
-
|
|
448
|
+
`${account.email}: token refresh failed — run /login openai-codex to re-authenticate`,
|
|
418
449
|
);
|
|
419
450
|
}
|
|
420
451
|
|
package/commands.ts
CHANGED
|
@@ -80,11 +80,16 @@ function parseResetTarget(value: string): ResetTarget | undefined {
|
|
|
80
80
|
return undefined;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
function getAccountLabel(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
function getAccountLabel(
|
|
84
|
+
email: string,
|
|
85
|
+
options?: { quotaExhaustedUntil?: number; needsReauth?: boolean },
|
|
86
|
+
): string {
|
|
87
|
+
const tags: string[] = [];
|
|
88
|
+
if (options?.needsReauth) tags.push("Reauth");
|
|
89
|
+
if (options?.quotaExhaustedUntil && options.quotaExhaustedUntil > Date.now())
|
|
90
|
+
tags.push("Quota");
|
|
91
|
+
if (tags.length === 0) return email;
|
|
92
|
+
return `${email} (${tags.join(", ")})`;
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
function formatAccountStatusLine(
|
|
@@ -100,9 +105,11 @@ function formatAccountStatusLine(
|
|
|
100
105
|
account.quotaExhaustedUntil && account.quotaExhaustedUntil > Date.now();
|
|
101
106
|
const untouched = isUsageUntouched(usage) ? "untouched" : null;
|
|
102
107
|
const imported = account.importSource ? "imported" : null;
|
|
108
|
+
const reauth = account.needsReauth ? "needs reauth" : null;
|
|
103
109
|
const tags = [
|
|
104
110
|
active?.email === account.email ? "active" : null,
|
|
105
111
|
manual?.email === account.email ? "manual" : null,
|
|
112
|
+
reauth,
|
|
106
113
|
quotaHit ? "quota" : null,
|
|
107
114
|
untouched,
|
|
108
115
|
imported,
|
|
@@ -234,7 +241,10 @@ async function openAccountSelectionPanel(
|
|
|
234
241
|
const accounts = accountManager.getAccounts();
|
|
235
242
|
const items = accounts.map((account) => ({
|
|
236
243
|
value: account.email,
|
|
237
|
-
label: getAccountLabel(account.email,
|
|
244
|
+
label: getAccountLabel(account.email, {
|
|
245
|
+
quotaExhaustedUntil: account.quotaExhaustedUntil,
|
|
246
|
+
needsReauth: account.needsReauth,
|
|
247
|
+
}),
|
|
238
248
|
}));
|
|
239
249
|
|
|
240
250
|
return ctx.ui.custom<AccountPanelResult>((_tui, theme, _kb, done) => {
|
package/extension.ts
CHANGED
|
@@ -28,7 +28,7 @@ export default function multicodexExtension(pi: ExtensionAPI) {
|
|
|
28
28
|
|
|
29
29
|
pi.on("session_start", (_event: unknown, ctx: ExtensionContext) => {
|
|
30
30
|
lastContext = ctx;
|
|
31
|
-
handleSessionStart(accountManager);
|
|
31
|
+
handleSessionStart(accountManager, (msg) => ctx.ui.notify(msg, "warning"));
|
|
32
32
|
statusController.startAutoRefresh();
|
|
33
33
|
void (async () => {
|
|
34
34
|
await statusController.loadPreferences(ctx);
|
|
@@ -41,7 +41,9 @@ export default function multicodexExtension(pi: ExtensionAPI) {
|
|
|
41
41
|
(event: { reason?: string }, ctx: ExtensionContext) => {
|
|
42
42
|
lastContext = ctx;
|
|
43
43
|
if (event.reason === "new") {
|
|
44
|
-
handleNewSessionSwitch(accountManager)
|
|
44
|
+
handleNewSessionSwitch(accountManager, (msg) =>
|
|
45
|
+
ctx.ui.notify(msg, "warning"),
|
|
46
|
+
);
|
|
45
47
|
}
|
|
46
48
|
void statusController.refreshFor(ctx);
|
|
47
49
|
},
|
package/hooks.ts
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import type { AccountManager } from "./account-manager";
|
|
2
2
|
|
|
3
|
+
type WarningHandler = (message: string) => void;
|
|
4
|
+
|
|
3
5
|
async function refreshAndActivateBestAccount(
|
|
4
6
|
accountManager: AccountManager,
|
|
7
|
+
warningHandler?: WarningHandler,
|
|
5
8
|
): Promise<void> {
|
|
6
9
|
await accountManager.syncImportedOpenAICodexAuth();
|
|
7
10
|
await accountManager.refreshUsageForAllAccounts({ force: true });
|
|
11
|
+
|
|
12
|
+
const needsReauth = accountManager.getAccountsNeedingReauth();
|
|
13
|
+
if (needsReauth.length > 0) {
|
|
14
|
+
const hints = needsReauth.map((a) => {
|
|
15
|
+
const cmd = a.importSource
|
|
16
|
+
? "/login openai-codex"
|
|
17
|
+
: `/multicodex use ${a.email}`;
|
|
18
|
+
return `${a.email} (${cmd})`;
|
|
19
|
+
});
|
|
20
|
+
warningHandler?.(
|
|
21
|
+
`Multicodex: ${needsReauth.length} account(s) need re-authentication: ${hints.join(", ")}`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
8
25
|
const manual = accountManager.getAvailableManualAccount();
|
|
9
26
|
if (manual) return;
|
|
10
27
|
if (accountManager.hasManualAccount()) {
|
|
@@ -13,11 +30,17 @@ async function refreshAndActivateBestAccount(
|
|
|
13
30
|
await accountManager.activateBestAccount();
|
|
14
31
|
}
|
|
15
32
|
|
|
16
|
-
export function handleSessionStart(
|
|
33
|
+
export function handleSessionStart(
|
|
34
|
+
accountManager: AccountManager,
|
|
35
|
+
warningHandler?: WarningHandler,
|
|
36
|
+
): void {
|
|
17
37
|
if (accountManager.getAccounts().length === 0) return;
|
|
18
|
-
void refreshAndActivateBestAccount(accountManager);
|
|
38
|
+
void refreshAndActivateBestAccount(accountManager, warningHandler);
|
|
19
39
|
}
|
|
20
40
|
|
|
21
|
-
export function handleNewSessionSwitch(
|
|
22
|
-
|
|
41
|
+
export function handleNewSessionSwitch(
|
|
42
|
+
accountManager: AccountManager,
|
|
43
|
+
warningHandler?: WarningHandler,
|
|
44
|
+
): void {
|
|
45
|
+
void refreshAndActivateBestAccount(accountManager, warningHandler);
|
|
23
46
|
}
|
package/package.json
CHANGED
package/selection.ts
CHANGED