@victor-software-house/pi-multicodex 2.2.0 → 2.2.1
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 +21 -67
- package/auth.ts +1 -47
- package/commands.ts +3 -10
- package/package.json +1 -1
- package/provider.ts +1 -2
package/account-manager.ts
CHANGED
|
@@ -2,12 +2,8 @@ import {
|
|
|
2
2
|
type OAuthCredentials,
|
|
3
3
|
refreshOpenAICodexToken,
|
|
4
4
|
} from "@mariozechner/pi-ai/oauth";
|
|
5
|
-
import { AuthStorage } from "@mariozechner/pi-coding-agent";
|
|
6
5
|
import { normalizeUnknownError } from "pi-provider-utils/streams";
|
|
7
|
-
import {
|
|
8
|
-
loadImportedOpenAICodexAuth,
|
|
9
|
-
writeActiveTokenToAuthJson,
|
|
10
|
-
} from "./auth";
|
|
6
|
+
import { loadImportedOpenAICodexAuth } from "./auth";
|
|
11
7
|
import { isAccountAvailable, pickBestAccount } from "./selection";
|
|
12
8
|
import {
|
|
13
9
|
type Account,
|
|
@@ -48,23 +44,6 @@ export class AccountManager {
|
|
|
48
44
|
}
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
/**
|
|
52
|
-
* Write the active account's tokens to auth.json so pi's background features
|
|
53
|
-
* (rename, compaction) can resolve a valid API key via AuthStorage.
|
|
54
|
-
*/
|
|
55
|
-
private syncActiveTokenToAuthJson(account: Account): void {
|
|
56
|
-
try {
|
|
57
|
-
writeActiveTokenToAuthJson({
|
|
58
|
-
access: account.accessToken,
|
|
59
|
-
refresh: account.refreshToken,
|
|
60
|
-
expires: account.expiresAt,
|
|
61
|
-
accountId: account.accountId,
|
|
62
|
-
});
|
|
63
|
-
} catch {
|
|
64
|
-
// Best-effort sync — do not block token resolution.
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
47
|
onStateChange(handler: StateChangeHandler): () => void {
|
|
69
48
|
this.stateChangeHandlers.add(handler);
|
|
70
49
|
return () => {
|
|
@@ -366,6 +345,17 @@ export class AccountManager {
|
|
|
366
345
|
return changed;
|
|
367
346
|
}
|
|
368
347
|
|
|
348
|
+
detachImportedAuth(email: string): boolean {
|
|
349
|
+
const account = this.getAccount(email);
|
|
350
|
+
if (!account) return false;
|
|
351
|
+
const changed = this.clearImportedLink(account);
|
|
352
|
+
if (changed) {
|
|
353
|
+
this.save();
|
|
354
|
+
this.notifyStateChanged();
|
|
355
|
+
}
|
|
356
|
+
return changed;
|
|
357
|
+
}
|
|
358
|
+
|
|
369
359
|
async syncImportedOpenAICodexAuth(): Promise<boolean> {
|
|
370
360
|
const imported = await loadImportedOpenAICodexAuth();
|
|
371
361
|
if (!imported) return false;
|
|
@@ -624,12 +614,11 @@ export class AccountManager {
|
|
|
624
614
|
}
|
|
625
615
|
|
|
626
616
|
if (Date.now() < account.expiresAt - 5 * 60 * 1000) {
|
|
627
|
-
this.syncActiveTokenToAuthJson(account);
|
|
628
617
|
return account.accessToken;
|
|
629
618
|
}
|
|
630
619
|
|
|
631
|
-
//
|
|
632
|
-
//
|
|
620
|
+
// Imported auth is read-only. MultiCodex never refreshes or writes
|
|
621
|
+
// auth.json and instead requires the user to repair pi auth explicitly.
|
|
633
622
|
if (account.importSource === "pi-openai-codex") {
|
|
634
623
|
return this.ensureValidTokenForImportedAccount(account);
|
|
635
624
|
}
|
|
@@ -652,7 +641,6 @@ export class AccountManager {
|
|
|
652
641
|
}
|
|
653
642
|
this.save();
|
|
654
643
|
this.notifyStateChanged();
|
|
655
|
-
this.syncActiveTokenToAuthJson(account);
|
|
656
644
|
return account.accessToken;
|
|
657
645
|
} catch (error) {
|
|
658
646
|
this.markNeedsReauth(account);
|
|
@@ -667,17 +655,14 @@ export class AccountManager {
|
|
|
667
655
|
}
|
|
668
656
|
|
|
669
657
|
/**
|
|
670
|
-
*
|
|
658
|
+
* Read-only path for imported pi auth.
|
|
671
659
|
*
|
|
672
|
-
*
|
|
673
|
-
*
|
|
674
|
-
* errors caused by pi and multicodex both refreshing the same token
|
|
675
|
-
* simultaneously.
|
|
660
|
+
* MultiCodex may read auth.json to mirror pi's currently active Codex auth,
|
|
661
|
+
* but it must never refresh or write auth.json itself.
|
|
676
662
|
*/
|
|
677
663
|
private async ensureValidTokenForImportedAccount(
|
|
678
664
|
account: Account,
|
|
679
665
|
): Promise<string> {
|
|
680
|
-
// Check if pi already refreshed since our last sync.
|
|
681
666
|
const latest = await loadImportedOpenAICodexAuth();
|
|
682
667
|
if (latest && Date.now() < latest.credentials.expires - 5 * 60 * 1000) {
|
|
683
668
|
account.accessToken = latest.credentials.access;
|
|
@@ -696,40 +681,9 @@ export class AccountManager {
|
|
|
696
681
|
return account.accessToken;
|
|
697
682
|
}
|
|
698
683
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
const authStorage = AuthStorage.create();
|
|
704
|
-
apiKey = await authStorage.getApiKey("openai-codex");
|
|
705
|
-
} catch {
|
|
706
|
-
// AuthStorage refresh failed; mark for re-auth below.
|
|
707
|
-
}
|
|
708
|
-
if (!apiKey) {
|
|
709
|
-
this.markNeedsReauth(account);
|
|
710
|
-
throw new Error(
|
|
711
|
-
`${account.email}: token refresh failed — run /login openai-codex to re-authenticate`,
|
|
712
|
-
);
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
// Read the refreshed tokens back from auth.json.
|
|
716
|
-
const refreshed = await loadImportedOpenAICodexAuth();
|
|
717
|
-
if (refreshed) {
|
|
718
|
-
account.accessToken = refreshed.credentials.access;
|
|
719
|
-
account.refreshToken = refreshed.credentials.refresh;
|
|
720
|
-
account.expiresAt = refreshed.credentials.expires;
|
|
721
|
-
account.importFingerprint = refreshed.fingerprint;
|
|
722
|
-
const accountId =
|
|
723
|
-
typeof refreshed.credentials.accountId === "string"
|
|
724
|
-
? refreshed.credentials.accountId
|
|
725
|
-
: undefined;
|
|
726
|
-
if (accountId) {
|
|
727
|
-
account.accountId = accountId;
|
|
728
|
-
}
|
|
729
|
-
this.save();
|
|
730
|
-
this.notifyStateChanged();
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
return apiKey;
|
|
684
|
+
this.markNeedsReauth(account);
|
|
685
|
+
throw new Error(
|
|
686
|
+
`${account.email}: imported pi auth is expired — run /login openai-codex to re-authenticate`,
|
|
687
|
+
);
|
|
734
688
|
}
|
|
735
689
|
}
|
package/auth.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
existsSync,
|
|
3
|
-
promises as fs,
|
|
4
|
-
readFileSync,
|
|
5
|
-
writeFileSync,
|
|
6
|
-
} from "node:fs";
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
7
2
|
import type { OAuthCredentials } from "@mariozechner/pi-ai/oauth";
|
|
8
3
|
import { getAgentAuthPath } from "pi-provider-utils/agent-paths";
|
|
9
4
|
|
|
@@ -127,47 +122,6 @@ export function parseImportedOpenAICodexAuth(
|
|
|
127
122
|
};
|
|
128
123
|
}
|
|
129
124
|
|
|
130
|
-
/**
|
|
131
|
-
* Write the active account's tokens to auth.json so pi's background features
|
|
132
|
-
* (rename, compaction, inline suggestions) can resolve a valid API key through
|
|
133
|
-
* the normal AuthStorage path.
|
|
134
|
-
*/
|
|
135
|
-
/**
|
|
136
|
-
* Synchronously write the active account's tokens to auth.json so pi's
|
|
137
|
-
* background features (rename, compaction) can resolve a valid API key.
|
|
138
|
-
*
|
|
139
|
-
* Uses synchronous I/O to avoid interleaved writes with pi's own code.
|
|
140
|
-
*/
|
|
141
|
-
export function writeActiveTokenToAuthJson(creds: {
|
|
142
|
-
access: string;
|
|
143
|
-
refresh: string;
|
|
144
|
-
expires: number;
|
|
145
|
-
accountId?: string;
|
|
146
|
-
}): void {
|
|
147
|
-
let auth: Record<string, unknown> = {};
|
|
148
|
-
try {
|
|
149
|
-
if (existsSync(AUTH_FILE)) {
|
|
150
|
-
const raw = readFileSync(AUTH_FILE, "utf8");
|
|
151
|
-
const parsed = JSON.parse(raw) as unknown;
|
|
152
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
153
|
-
auth = parsed as Record<string, unknown>;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
} catch {
|
|
157
|
-
// File missing or corrupt — start fresh.
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
auth["openai-codex"] = {
|
|
161
|
-
type: "oauth",
|
|
162
|
-
access: creds.access,
|
|
163
|
-
refresh: creds.refresh,
|
|
164
|
-
expires: creds.expires,
|
|
165
|
-
accountId: creds.accountId,
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
125
|
export async function loadImportedOpenAICodexAuth(): Promise<
|
|
172
126
|
ImportedOpenAICodexAuth | undefined
|
|
173
127
|
> {
|
package/commands.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
import { getAgentSettingsPath } from "pi-provider-utils/agent-paths";
|
|
19
19
|
import { normalizeUnknownError } from "pi-provider-utils/streams";
|
|
20
20
|
import type { AccountManager } from "./account-manager";
|
|
21
|
-
import { writeActiveTokenToAuthJson } from "./auth";
|
|
22
21
|
import { openLoginInBrowser } from "./browser";
|
|
23
22
|
import type { createUsageStatusController } from "./status";
|
|
24
23
|
import { type Account, STORAGE_FILE } from "./storage";
|
|
@@ -246,16 +245,10 @@ async function loginAndActivateAccount(
|
|
|
246
245
|
onPrompt: async ({ message }) => (await ctx.ui.input(message)) || "",
|
|
247
246
|
});
|
|
248
247
|
|
|
248
|
+
const existing = accountManager.getAccount(identifier);
|
|
249
249
|
const account = accountManager.addOrUpdateAccount(identifier, creds);
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
access: creds.access,
|
|
253
|
-
refresh: creds.refresh,
|
|
254
|
-
expires: creds.expires,
|
|
255
|
-
accountId:
|
|
256
|
-
typeof creds.accountId === "string" ? creds.accountId : undefined,
|
|
257
|
-
});
|
|
258
|
-
await accountManager.syncImportedOpenAICodexAuth();
|
|
250
|
+
if (existing?.importSource) {
|
|
251
|
+
accountManager.detachImportedAuth(account.email);
|
|
259
252
|
}
|
|
260
253
|
accountManager.setManualAccount(account.email);
|
|
261
254
|
ctx.ui.notify(`Now using ${account.email}`, "info");
|
package/package.json
CHANGED
package/provider.ts
CHANGED
|
@@ -53,8 +53,7 @@ function getActiveApiKey(accountManager: AccountManager): string {
|
|
|
53
53
|
return account.accessToken;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
//
|
|
57
|
-
// as long as auth.json has valid tokens.
|
|
56
|
+
// Fallback placeholder until MultiCodex resolves a usable managed account.
|
|
58
57
|
return "pending-login";
|
|
59
58
|
}
|
|
60
59
|
|