@victor-software-house/pi-multicodex 1.0.6 → 1.0.8
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/README.md +17 -9
- package/account-manager.ts +45 -1
- package/auth.ts +106 -0
- package/commands.ts +71 -41
- package/hooks.ts +1 -0
- package/index.ts +4 -0
- package/package.json +15 -4
- package/provider.ts +1 -1
- package/status.ts +5 -1
- package/storage.ts +2 -0
- package/stream-wrapper.ts +2 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
## What it does
|
|
8
8
|
|
|
9
|
+
- overrides the normal `openai-codex` path instead of requiring a separate provider to be selected
|
|
10
|
+
- auto-imports pi's stored `openai-codex` auth when it is new or changed
|
|
9
11
|
- rotates accounts on quota and rate-limit failures
|
|
10
12
|
- prefers untouched accounts when usage data is available
|
|
11
13
|
- otherwise prefers the account whose weekly window resets first
|
|
@@ -45,12 +47,11 @@ pi -e ./index.ts
|
|
|
45
47
|
|
|
46
48
|
## Commands
|
|
47
49
|
|
|
48
|
-
- `/multicodex-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
- Select an account manually for the current session.
|
|
50
|
+
- `/multicodex-use [identifier]`
|
|
51
|
+
- Use an existing managed account, or start the Codex login flow when the account is missing or the stored auth is no longer valid.
|
|
52
|
+
- With no argument, opens an account picker.
|
|
52
53
|
- `/multicodex-status`
|
|
53
|
-
- Show account state and cached usage information.
|
|
54
|
+
- Show managed account state and cached usage information.
|
|
54
55
|
- `/multicodex-footer`
|
|
55
56
|
- Open an interactive panel to configure footer fields and ordering.
|
|
56
57
|
|
|
@@ -68,12 +69,12 @@ Current direction:
|
|
|
68
69
|
|
|
69
70
|
Current next step:
|
|
70
71
|
|
|
71
|
-
-
|
|
72
|
+
- make MultiCodex own the normal `openai-codex` path directly
|
|
73
|
+
- auto-import pi's existing `openai-codex` auth when it is new or changed
|
|
72
74
|
- mirror the existing codex usage footer style, including support for displaying both reset countdowns
|
|
73
|
-
- show footer usage only when the selected model uses the `multicodex` provider override
|
|
74
75
|
- show the active account identifier beside the 5h and 7d usage metrics
|
|
75
|
-
-
|
|
76
|
-
-
|
|
76
|
+
- keep footer configuration in an interactive panel
|
|
77
|
+
- tighten footer updates so account switches and quota rotation are reflected immediately
|
|
77
78
|
|
|
78
79
|
## Release validation
|
|
79
80
|
|
|
@@ -91,6 +92,13 @@ Release flow:
|
|
|
91
92
|
3. Create and push a matching `v*` tag.
|
|
92
93
|
4. Let GitHub Actions publish through trusted publishing.
|
|
93
94
|
|
|
95
|
+
Local push protection:
|
|
96
|
+
|
|
97
|
+
- `lefthook` runs `mise run pre-push`
|
|
98
|
+
- the `pre-push` mise task runs the same core validations as the publish workflow:
|
|
99
|
+
- `pnpm check`
|
|
100
|
+
- `npm pack --dry-run`
|
|
101
|
+
|
|
94
102
|
Prepare locally:
|
|
95
103
|
|
|
96
104
|
```bash
|
package/account-manager.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
type OAuthCredentials,
|
|
3
3
|
refreshOpenAICodexToken,
|
|
4
4
|
} from "@mariozechner/pi-ai/oauth";
|
|
5
|
+
import { loadImportedOpenAICodexAuth } from "./auth";
|
|
5
6
|
import { isAccountAvailable, pickBestAccount } from "./selection";
|
|
6
7
|
import {
|
|
7
8
|
type Account,
|
|
@@ -49,7 +50,14 @@ export class AccountManager {
|
|
|
49
50
|
this.warningHandler = handler;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
addOrUpdateAccount(
|
|
53
|
+
addOrUpdateAccount(
|
|
54
|
+
email: string,
|
|
55
|
+
creds: OAuthCredentials,
|
|
56
|
+
options?: {
|
|
57
|
+
importSource?: "pi-openai-codex";
|
|
58
|
+
importFingerprint?: string;
|
|
59
|
+
},
|
|
60
|
+
): void {
|
|
53
61
|
const existing = this.getAccount(email);
|
|
54
62
|
const accountId =
|
|
55
63
|
typeof creds.accountId === "string" ? creds.accountId : undefined;
|
|
@@ -57,6 +65,8 @@ export class AccountManager {
|
|
|
57
65
|
existing.accessToken = creds.access;
|
|
58
66
|
existing.refreshToken = creds.refresh;
|
|
59
67
|
existing.expiresAt = creds.expires;
|
|
68
|
+
existing.importSource = options?.importSource;
|
|
69
|
+
existing.importFingerprint = options?.importFingerprint;
|
|
60
70
|
if (accountId) {
|
|
61
71
|
existing.accountId = accountId;
|
|
62
72
|
}
|
|
@@ -67,6 +77,8 @@ export class AccountManager {
|
|
|
67
77
|
refreshToken: creds.refresh,
|
|
68
78
|
expiresAt: creds.expires,
|
|
69
79
|
accountId,
|
|
80
|
+
importSource: options?.importSource,
|
|
81
|
+
importFingerprint: options?.importFingerprint,
|
|
70
82
|
});
|
|
71
83
|
}
|
|
72
84
|
this.setActiveAccount(email);
|
|
@@ -112,6 +124,38 @@ export class AccountManager {
|
|
|
112
124
|
this.manualEmail = undefined;
|
|
113
125
|
}
|
|
114
126
|
|
|
127
|
+
getImportedAccount(): Account | undefined {
|
|
128
|
+
return this.data.accounts.find(
|
|
129
|
+
(account) => account.importSource === "pi-openai-codex",
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async syncImportedOpenAICodexAuth(): Promise<boolean> {
|
|
134
|
+
const imported = await loadImportedOpenAICodexAuth();
|
|
135
|
+
if (!imported) return false;
|
|
136
|
+
|
|
137
|
+
const existingImported = this.getImportedAccount();
|
|
138
|
+
if (
|
|
139
|
+
existingImported?.importFingerprint === imported.fingerprint &&
|
|
140
|
+
existingImported.email === imported.identifier
|
|
141
|
+
) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (existingImported && existingImported.email !== imported.identifier) {
|
|
146
|
+
const target = this.getAccount(imported.identifier);
|
|
147
|
+
if (!target) {
|
|
148
|
+
existingImported.email = imported.identifier;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.addOrUpdateAccount(imported.identifier, imported.credentials, {
|
|
153
|
+
importSource: "pi-openai-codex",
|
|
154
|
+
importFingerprint: imported.fingerprint,
|
|
155
|
+
});
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
115
159
|
getAvailableManualAccount(options?: {
|
|
116
160
|
excludeEmails?: Set<string>;
|
|
117
161
|
now?: number;
|
package/auth.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import type { OAuthCredentials } from "@mariozechner/pi-ai/oauth";
|
|
5
|
+
|
|
6
|
+
const AUTH_FILE = path.join(os.homedir(), ".pi", "agent", "auth.json");
|
|
7
|
+
const IMPORTED_ACCOUNT_PREFIX = "OpenAI Codex";
|
|
8
|
+
|
|
9
|
+
interface AuthEntry {
|
|
10
|
+
type?: string;
|
|
11
|
+
access?: string | null;
|
|
12
|
+
refresh?: string | null;
|
|
13
|
+
expires?: number | null;
|
|
14
|
+
accountId?: string | null;
|
|
15
|
+
account_id?: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ImportedOpenAICodexAuth {
|
|
19
|
+
identifier: string;
|
|
20
|
+
fingerprint: string;
|
|
21
|
+
credentials: OAuthCredentials;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function asAuthEntry(value: unknown): AuthEntry | undefined {
|
|
25
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return value as AuthEntry;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getAccountId(entry: AuthEntry): string | undefined {
|
|
32
|
+
const accountId = entry.accountId ?? entry.account_id;
|
|
33
|
+
return typeof accountId === "string" && accountId.trim()
|
|
34
|
+
? accountId.trim()
|
|
35
|
+
: undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getRequiredString(
|
|
39
|
+
value: string | null | undefined,
|
|
40
|
+
): string | undefined {
|
|
41
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function createImportedIdentifier(accountId: string): string {
|
|
45
|
+
return `${IMPORTED_ACCOUNT_PREFIX} ${accountId.slice(0, 8)}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function createFingerprint(entry: {
|
|
49
|
+
access: string;
|
|
50
|
+
refresh: string;
|
|
51
|
+
expires: number;
|
|
52
|
+
accountId?: string;
|
|
53
|
+
}): string {
|
|
54
|
+
return JSON.stringify({
|
|
55
|
+
access: entry.access,
|
|
56
|
+
refresh: entry.refresh,
|
|
57
|
+
expires: entry.expires,
|
|
58
|
+
accountId: entry.accountId ?? null,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function parseImportedOpenAICodexAuth(
|
|
63
|
+
auth: Record<string, unknown>,
|
|
64
|
+
): ImportedOpenAICodexAuth | undefined {
|
|
65
|
+
const entry = asAuthEntry(auth["openai-codex"]);
|
|
66
|
+
if (entry?.type !== "oauth") return undefined;
|
|
67
|
+
|
|
68
|
+
const access = getRequiredString(entry.access);
|
|
69
|
+
const refresh = getRequiredString(entry.refresh);
|
|
70
|
+
const accountId = getAccountId(entry);
|
|
71
|
+
const expires = entry.expires;
|
|
72
|
+
if (!access || !refresh || typeof expires !== "number") {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const credentials: OAuthCredentials = {
|
|
77
|
+
access,
|
|
78
|
+
refresh,
|
|
79
|
+
expires,
|
|
80
|
+
accountId,
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
identifier: createImportedIdentifier(accountId ?? "default"),
|
|
84
|
+
fingerprint: createFingerprint({ access, refresh, expires, accountId }),
|
|
85
|
+
credentials,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function loadImportedOpenAICodexAuth(): Promise<
|
|
90
|
+
ImportedOpenAICodexAuth | undefined
|
|
91
|
+
> {
|
|
92
|
+
try {
|
|
93
|
+
const raw = await fs.readFile(AUTH_FILE, "utf8");
|
|
94
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
95
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
return parseImportedOpenAICodexAuth(parsed as Record<string, unknown>);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const withCode = error as Error & { code?: string };
|
|
101
|
+
if (withCode.code === "ENOENT") {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
package/commands.ts
CHANGED
|
@@ -13,59 +13,85 @@ function getErrorMessage(error: unknown): string {
|
|
|
13
13
|
return typeof error === "string" ? error : JSON.stringify(error);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
async function loginAndActivateAccount(
|
|
17
|
+
pi: ExtensionAPI,
|
|
18
|
+
ctx: ExtensionCommandContext,
|
|
19
|
+
accountManager: AccountManager,
|
|
20
|
+
identifier: string,
|
|
21
|
+
): Promise<boolean> {
|
|
22
|
+
try {
|
|
23
|
+
ctx.ui.notify(
|
|
24
|
+
`Starting login for ${identifier}... Check your browser.`,
|
|
25
|
+
"info",
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const creds = await loginOpenAICodex({
|
|
29
|
+
onAuth: ({ url }) => {
|
|
30
|
+
void openLoginInBrowser(pi, ctx, url);
|
|
31
|
+
ctx.ui.notify(`Please open this URL to login: ${url}`, "info");
|
|
32
|
+
console.log(`[multicodex] Login URL: ${url}`);
|
|
33
|
+
},
|
|
34
|
+
onPrompt: async ({ message }) => (await ctx.ui.input(message)) || "",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
accountManager.addOrUpdateAccount(identifier, creds);
|
|
38
|
+
accountManager.setManualAccount(identifier);
|
|
39
|
+
ctx.ui.notify(`Now using ${identifier}`, "info");
|
|
40
|
+
return true;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
ctx.ui.notify(`Login failed: ${getErrorMessage(error)}`, "error");
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function useOrLoginAccount(
|
|
48
|
+
pi: ExtensionAPI,
|
|
49
|
+
ctx: ExtensionCommandContext,
|
|
50
|
+
accountManager: AccountManager,
|
|
51
|
+
identifier: string,
|
|
52
|
+
): Promise<void> {
|
|
53
|
+
const existing = accountManager.getAccount(identifier);
|
|
54
|
+
if (existing) {
|
|
55
|
+
try {
|
|
56
|
+
await accountManager.ensureValidToken(existing);
|
|
57
|
+
accountManager.setManualAccount(identifier);
|
|
58
|
+
ctx.ui.notify(`Now using ${identifier}`, "info");
|
|
59
|
+
return;
|
|
60
|
+
} catch {
|
|
61
|
+
ctx.ui.notify(
|
|
62
|
+
`Stored auth for ${identifier} is no longer valid. Starting login again.`,
|
|
63
|
+
"warning",
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await loginAndActivateAccount(pi, ctx, accountManager, identifier);
|
|
69
|
+
}
|
|
70
|
+
|
|
16
71
|
export function registerCommands(
|
|
17
72
|
pi: ExtensionAPI,
|
|
18
73
|
accountManager: AccountManager,
|
|
19
74
|
statusController: ReturnType<typeof createUsageStatusController>,
|
|
20
75
|
): void {
|
|
21
|
-
pi.registerCommand("multicodex-
|
|
22
|
-
description:
|
|
76
|
+
pi.registerCommand("multicodex-use", {
|
|
77
|
+
description:
|
|
78
|
+
"Use an existing Codex account, or log in when the identifier is missing",
|
|
23
79
|
handler: async (
|
|
24
80
|
args: string,
|
|
25
81
|
ctx: ExtensionCommandContext,
|
|
26
82
|
): Promise<void> => {
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
-
ctx
|
|
30
|
-
|
|
31
|
-
"error",
|
|
32
|
-
);
|
|
83
|
+
const identifier = args.trim();
|
|
84
|
+
if (identifier) {
|
|
85
|
+
await useOrLoginAccount(pi, ctx, accountManager, identifier);
|
|
86
|
+
await statusController.refreshFor(ctx);
|
|
33
87
|
return;
|
|
34
88
|
}
|
|
35
89
|
|
|
36
|
-
|
|
37
|
-
ctx.ui.notify(
|
|
38
|
-
`Starting login for ${email}... Check your browser.`,
|
|
39
|
-
"info",
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const creds = await loginOpenAICodex({
|
|
43
|
-
onAuth: ({ url }) => {
|
|
44
|
-
void openLoginInBrowser(pi, ctx, url);
|
|
45
|
-
ctx.ui.notify(`Please open this URL to login: ${url}`, "info");
|
|
46
|
-
console.log(`[multicodex] Login URL: ${url}`);
|
|
47
|
-
},
|
|
48
|
-
onPrompt: async ({ message }) => (await ctx.ui.input(message)) || "",
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
accountManager.addOrUpdateAccount(email, creds);
|
|
52
|
-
ctx.ui.notify(`Successfully logged in as ${email}`, "info");
|
|
53
|
-
} catch (error) {
|
|
54
|
-
ctx.ui.notify(`Login failed: ${getErrorMessage(error)}`, "error");
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
pi.registerCommand("multicodex-use", {
|
|
60
|
-
description: "Switch active Codex account for this session",
|
|
61
|
-
handler: async (
|
|
62
|
-
_args: string,
|
|
63
|
-
ctx: ExtensionCommandContext,
|
|
64
|
-
): Promise<void> => {
|
|
90
|
+
await accountManager.syncImportedOpenAICodexAuth();
|
|
65
91
|
const accounts = accountManager.getAccounts();
|
|
66
92
|
if (accounts.length === 0) {
|
|
67
93
|
ctx.ui.notify(
|
|
68
|
-
"No accounts
|
|
94
|
+
"No managed accounts found. Use /login or /multicodex-use <identifier> first.",
|
|
69
95
|
"warning",
|
|
70
96
|
);
|
|
71
97
|
return;
|
|
@@ -82,9 +108,10 @@ export function registerCommands(
|
|
|
82
108
|
const selected = await ctx.ui.select("Select Account", options);
|
|
83
109
|
if (!selected) return;
|
|
84
110
|
|
|
85
|
-
const email = selected.split(" ")[0];
|
|
111
|
+
const email = selected.split(" (")[0] ?? selected;
|
|
86
112
|
accountManager.setManualAccount(email);
|
|
87
|
-
ctx.ui.notify(`
|
|
113
|
+
ctx.ui.notify(`Now using ${email}`, "info");
|
|
114
|
+
await statusController.refreshFor(ctx);
|
|
88
115
|
},
|
|
89
116
|
});
|
|
90
117
|
|
|
@@ -94,11 +121,12 @@ export function registerCommands(
|
|
|
94
121
|
_args: string,
|
|
95
122
|
ctx: ExtensionCommandContext,
|
|
96
123
|
): Promise<void> => {
|
|
124
|
+
await accountManager.syncImportedOpenAICodexAuth();
|
|
97
125
|
await accountManager.refreshUsageForAllAccounts();
|
|
98
126
|
const accounts = accountManager.getAccounts();
|
|
99
127
|
if (accounts.length === 0) {
|
|
100
128
|
ctx.ui.notify(
|
|
101
|
-
"No accounts
|
|
129
|
+
"No managed accounts found. Use /login or /multicodex-use <identifier> first.",
|
|
102
130
|
"warning",
|
|
103
131
|
);
|
|
104
132
|
return;
|
|
@@ -112,10 +140,12 @@ export function registerCommands(
|
|
|
112
140
|
account.quotaExhaustedUntil &&
|
|
113
141
|
account.quotaExhaustedUntil > Date.now();
|
|
114
142
|
const untouched = isUsageUntouched(usage) ? "untouched" : null;
|
|
143
|
+
const imported = account.importSource ? "imported" : null;
|
|
115
144
|
const tags = [
|
|
116
145
|
isActive ? "active" : null,
|
|
117
146
|
quotaHit ? "quota" : null,
|
|
118
147
|
untouched,
|
|
148
|
+
imported,
|
|
119
149
|
]
|
|
120
150
|
.filter(Boolean)
|
|
121
151
|
.join(", ");
|
package/hooks.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { AccountManager } from "./account-manager";
|
|
|
3
3
|
async function refreshAndActivateBestAccount(
|
|
4
4
|
accountManager: AccountManager,
|
|
5
5
|
): Promise<void> {
|
|
6
|
+
await accountManager.syncImportedOpenAICodexAuth();
|
|
6
7
|
await accountManager.refreshUsageForAllAccounts({ force: true });
|
|
7
8
|
const manual = accountManager.getAvailableManualAccount();
|
|
8
9
|
if (manual) return;
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@victor-software-house/pi-multicodex",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Codex account rotation extension for pi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"files": [
|
|
33
33
|
"abort-utils.ts",
|
|
34
34
|
"account-manager.ts",
|
|
35
|
+
"auth.ts",
|
|
35
36
|
"browser.ts",
|
|
36
37
|
"commands.ts",
|
|
37
38
|
"extension.ts",
|
|
@@ -60,15 +61,25 @@
|
|
|
60
61
|
},
|
|
61
62
|
"peerDependencies": {
|
|
62
63
|
"@mariozechner/pi-ai": "*",
|
|
63
|
-
"@mariozechner/pi-coding-agent": "*"
|
|
64
|
+
"@mariozechner/pi-coding-agent": "*",
|
|
65
|
+
"@mariozechner/pi-tui": "*"
|
|
64
66
|
},
|
|
65
|
-
"
|
|
66
|
-
"@mariozechner/pi-
|
|
67
|
+
"peerDependenciesMeta": {
|
|
68
|
+
"@mariozechner/pi-ai": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"@mariozechner/pi-coding-agent": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"@mariozechner/pi-tui": {
|
|
75
|
+
"optional": true
|
|
76
|
+
}
|
|
67
77
|
},
|
|
68
78
|
"devDependencies": {
|
|
69
79
|
"@biomejs/biome": "^2.4.7",
|
|
70
80
|
"@mariozechner/pi-ai": "^0.58.1",
|
|
71
81
|
"@mariozechner/pi-coding-agent": "^0.58.1",
|
|
82
|
+
"@mariozechner/pi-tui": "^0.58.1",
|
|
72
83
|
"@types/node": "^25.5.0",
|
|
73
84
|
"@typescript/native-preview": "7.0.0-dev.20260314.1",
|
|
74
85
|
"typescript": "^5.9.3",
|
package/provider.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import type { AccountManager } from "./account-manager";
|
|
11
11
|
import { createStreamWrapper } from "./stream-wrapper";
|
|
12
12
|
|
|
13
|
-
export const PROVIDER_ID = "
|
|
13
|
+
export const PROVIDER_ID = "openai-codex";
|
|
14
14
|
|
|
15
15
|
export interface ProviderModelDef {
|
|
16
16
|
id: string;
|
package/status.ts
CHANGED
|
@@ -335,7 +335,11 @@ export function createUsageStatusController(accountManager: AccountManager) {
|
|
|
335
335
|
return;
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
-
|
|
338
|
+
let activeAccount = accountManager.getActiveAccount();
|
|
339
|
+
if (!activeAccount) {
|
|
340
|
+
await accountManager.syncImportedOpenAICodexAuth();
|
|
341
|
+
activeAccount = accountManager.getActiveAccount();
|
|
342
|
+
}
|
|
339
343
|
if (!activeAccount) {
|
|
340
344
|
ctx.ui.setStatus(
|
|
341
345
|
STATUS_KEY,
|
package/storage.ts
CHANGED
package/stream-wrapper.ts
CHANGED
|
@@ -80,6 +80,7 @@ export function createStreamWrapper(
|
|
|
80
80
|
|
|
81
81
|
(async () => {
|
|
82
82
|
try {
|
|
83
|
+
await accountManager.syncImportedOpenAICodexAuth();
|
|
83
84
|
const excludedEmails = new Set<string>();
|
|
84
85
|
for (let attempt = 0; attempt <= MAX_ROTATION_RETRIES; attempt++) {
|
|
85
86
|
const now = Date.now();
|
|
@@ -100,7 +101,7 @@ export function createStreamWrapper(
|
|
|
100
101
|
}
|
|
101
102
|
if (!account) {
|
|
102
103
|
throw new Error(
|
|
103
|
-
"No available Multicodex accounts. Please use /multicodex-
|
|
104
|
+
"No available Multicodex accounts. Please use /multicodex-use <identifier>.",
|
|
104
105
|
);
|
|
105
106
|
}
|
|
106
107
|
|