@henryqw/pi-multi-codex 2.0.3 → 2.0.4
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 +3 -2
- package/extensions/multi-codex.ts +14 -13
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -21,14 +21,14 @@ pi install npm:@henryqw/pi-multi-codex
|
|
|
21
21
|
|
|
22
22
|
## Use
|
|
23
23
|
|
|
24
|
-
Run `/login` and authenticate `OpenAI Codex` for slot 1 first. Run `/codex-add`, then run `/login` and select the new `OpenAI Codex #<n>` provider. Restart Pi or update model scope, then run `/codex-status`. The status lists the new slot with cached quota when available, or `unavailable` until the first successful snapshot.
|
|
24
|
+
Run `/login` and authenticate `OpenAI Codex (legacy)` for slot 1 first. Run `/codex-add`, then run `/login` and select the new `OpenAI Codex #<n>` provider. Restart Pi or update model scope, then run `/codex-status`. The status lists the new slot with cached quota when available, or `unavailable` until the first successful snapshot.
|
|
25
25
|
|
|
26
26
|
| Surface | Type | Purpose |
|
|
27
27
|
| --- | --- | --- |
|
|
28
28
|
| `/codex-add` | command | For people: create the next numbered slot, then authenticate that slot. |
|
|
29
29
|
| `/codex-status` | command | For people: show shared quota snapshots and five-hour blocks; never waits on network. |
|
|
30
30
|
| `/codex-switch` | command | For people: select an authenticated, scope-allowed slot for the current Codex model. |
|
|
31
|
-
| Pi model selector (`OpenAI Codex`, `OpenAI Codex #<n>`) | ui | Choose an authenticated account slot. |
|
|
31
|
+
| Pi model selector (`OpenAI Codex (legacy)`, `OpenAI Codex #<n>`) | ui | Choose an authenticated account slot. |
|
|
32
32
|
| Pi status `pi-multi-codex` | ui | Report the active slot's fresh quota, five-hour limit countdown, or stale/unavailable state; `@henryqw/pi-footer` displays it in the footer. |
|
|
33
33
|
|
|
34
34
|
The footer and `/codex-status` show the five-hour reset countdown for Free, Go, and Plus tiers. They show the seven-day reset countdown for Pro Lite and other tiers. A numbered slot is one Codex account position in Pi.
|
|
@@ -41,6 +41,7 @@ The footer and `/codex-status` show the five-hour reset countdown for Free, Go,
|
|
|
41
41
|
- After HTTP 429, failover can use authenticated, registered, scope-allowed, untried slots with stale or missing quota.
|
|
42
42
|
- Failover skips known active five-hour blocks. It ranks fresh known quota first, then unranked slots by slot number.
|
|
43
43
|
- Routing preserves the model ID.
|
|
44
|
+
- Only physical Codex models are slots. A virtual model listed under `openai-codex`, such as `openai-codex/auto`, is never routed to a slot and shows no quota status.
|
|
44
45
|
- During one agent run, each eligible slot is tried at most once after HTTP 429 responses.
|
|
45
46
|
- Automatic retry stops when no untried eligible slot remains.
|
|
46
47
|
- The footer shows the active slot's fresh quota or five-hour block.
|
|
@@ -80,7 +80,7 @@ type Config = { autoSwitchOn429: boolean };
|
|
|
80
80
|
|
|
81
81
|
const NATIVE_PROVIDER_ID = "openai-codex";
|
|
82
82
|
const CODEX_ALIAS_PATTERN = /^openai-codex-([2-9]|[1-9]\d+)$/;
|
|
83
|
-
const NO_ACCOUNTS_MESSAGE = "No Codex OAuth accounts found. Run /login and select OpenAI Codex.";
|
|
83
|
+
const NO_ACCOUNTS_MESSAGE = "No Codex OAuth accounts found. Run /login and select OpenAI Codex (legacy).";
|
|
84
84
|
const AGENT_STARTED_ENTRY = "pi-multi-codex:agent-started";
|
|
85
85
|
const USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
86
86
|
const REFRESH_MS = 5 * 60_000;
|
|
@@ -818,8 +818,9 @@ function providerForSlot(slot: number): string {
|
|
|
818
818
|
return slot === 1 ? NATIVE_PROVIDER_ID : `${NATIVE_PROVIDER_ID}-${slot}`;
|
|
819
819
|
}
|
|
820
820
|
|
|
821
|
-
|
|
822
|
-
|
|
821
|
+
/** A physical Codex model on a slot provider. Virtual models listed under `openai-codex` route elsewhere and are not slots. */
|
|
822
|
+
function isManagedModel(model: Model<any> | undefined): model is CodexModel {
|
|
823
|
+
return model?.api === "openai-codex-responses" && slotForProvider(model.provider) !== undefined;
|
|
823
824
|
}
|
|
824
825
|
|
|
825
826
|
function sessionHasAgentWork(ctx: ExtensionContext): boolean {
|
|
@@ -907,8 +908,8 @@ export default function multiCodex(pi: ExtensionAPI): void {
|
|
|
907
908
|
|
|
908
909
|
const footerText = (ctx: ExtensionContext): string | undefined => {
|
|
909
910
|
const model = ctx.model;
|
|
910
|
-
const slot = slotForProvider(model
|
|
911
|
-
if (!
|
|
911
|
+
const slot = isManagedModel(model) ? slotForProvider(model.provider) : undefined;
|
|
912
|
+
if (!slot) return undefined;
|
|
912
913
|
const identity = currentIdentity(slot);
|
|
913
914
|
const snapshot = quota.snapshot(slot);
|
|
914
915
|
const prefix = `Codex #${slot}`;
|
|
@@ -941,7 +942,7 @@ export default function multiCodex(pi: ExtensionAPI): void {
|
|
|
941
942
|
};
|
|
942
943
|
|
|
943
944
|
const updatePendingCandidate = (ctx: ExtensionContext): void => {
|
|
944
|
-
if (!
|
|
945
|
+
if (!isManagedModel(ctx.model)) return;
|
|
945
946
|
const slot = selectFreshSlot(ctx, ctx.model);
|
|
946
947
|
automaticCandidate = slot ? { ...ctx.model, provider: providerForSlot(slot) } : ctx.model;
|
|
947
948
|
};
|
|
@@ -970,7 +971,7 @@ export default function multiCodex(pi: ExtensionAPI): void {
|
|
|
970
971
|
ctx.ui.notify("Pi Multi Codex config is invalid. Automatic 429 switching is disabled; the file was left unchanged.", "warning");
|
|
971
972
|
}
|
|
972
973
|
}
|
|
973
|
-
automaticOpen =
|
|
974
|
+
automaticOpen = isManagedModel(ctx.model) && !sessionHasAgentWork(ctx);
|
|
974
975
|
automaticCandidate = ctx.model;
|
|
975
976
|
// Cache-only ranking happens before background refresh starts.
|
|
976
977
|
updatePendingCandidate(ctx);
|
|
@@ -983,7 +984,7 @@ export default function multiCodex(pi: ExtensionAPI): void {
|
|
|
983
984
|
triedSlots.clear();
|
|
984
985
|
providerRequest = undefined;
|
|
985
986
|
const model = ctx.model;
|
|
986
|
-
const slot = slotForProvider(model
|
|
987
|
+
const slot = isManagedModel(model) ? slotForProvider(model.provider) : undefined;
|
|
987
988
|
const identity = slot ? currentIdentity(slot) : undefined;
|
|
988
989
|
const snapshot = slot ? quota.snapshot(slot) : undefined;
|
|
989
990
|
if (!automaticOpen && (preserveModelChoice || !identity || snapshot?.accountHash !== identity.accountHash || !isFiveHourLimited(snapshot, Date.now()))) return;
|
|
@@ -991,7 +992,7 @@ export default function multiCodex(pi: ExtensionAPI): void {
|
|
|
991
992
|
// Close before await: no timer, refresh, queued turn, or provider event can route later.
|
|
992
993
|
automaticOpen = false;
|
|
993
994
|
const candidate = automaticCandidate;
|
|
994
|
-
if (!model || !candidate ||
|
|
995
|
+
if (!isManagedModel(model) || !candidate || candidate.id !== model.id || candidate.provider === model.provider) return;
|
|
995
996
|
await setModelAutomatically(candidate);
|
|
996
997
|
updateFooter(ctx);
|
|
997
998
|
});
|
|
@@ -1014,7 +1015,7 @@ export default function multiCodex(pi: ExtensionAPI): void {
|
|
|
1014
1015
|
const model = ctx.model;
|
|
1015
1016
|
const failedSlot = slotForProvider(event.message.provider);
|
|
1016
1017
|
if (
|
|
1017
|
-
!model
|
|
1018
|
+
!isManagedModel(model)
|
|
1018
1019
|
|| !failedSlot
|
|
1019
1020
|
|| request.provider !== event.message.provider
|
|
1020
1021
|
|| model.provider !== event.message.provider
|
|
@@ -1069,7 +1070,7 @@ export default function multiCodex(pi: ExtensionAPI): void {
|
|
|
1069
1070
|
handler: async (_args, ctx) => {
|
|
1070
1071
|
const slots = syncSlots();
|
|
1071
1072
|
if (!slots.has(1)) {
|
|
1072
|
-
ctx.ui.notify("Run /login and select OpenAI Codex for slot 1 first.", "warning");
|
|
1073
|
+
ctx.ui.notify("Run /login and select OpenAI Codex (legacy) for slot 1 first.", "warning");
|
|
1073
1074
|
return;
|
|
1074
1075
|
}
|
|
1075
1076
|
|
|
@@ -1093,7 +1094,7 @@ export default function multiCodex(pi: ExtensionAPI): void {
|
|
|
1093
1094
|
description: "switch current Codex model to another authenticated slot",
|
|
1094
1095
|
handler: async (_args, ctx) => {
|
|
1095
1096
|
const model = ctx.model;
|
|
1096
|
-
if (!
|
|
1097
|
+
if (!isManagedModel(model)) {
|
|
1097
1098
|
ctx.ui.notify("Select an OpenAI Codex model before switching slots.", "warning");
|
|
1098
1099
|
return;
|
|
1099
1100
|
}
|
|
@@ -1108,7 +1109,7 @@ export default function multiCodex(pi: ExtensionAPI): void {
|
|
|
1108
1109
|
ctx.ui.notify(
|
|
1109
1110
|
scopedModels.length > 0
|
|
1110
1111
|
? "No authenticated Codex slot matches this session's model scope. Restart Pi or update scoped models."
|
|
1111
|
-
: "No authenticated Codex slots found. Run /login and select OpenAI Codex.",
|
|
1112
|
+
: "No authenticated Codex slots found. Run /login and select OpenAI Codex (legacy).",
|
|
1112
1113
|
"warning",
|
|
1113
1114
|
);
|
|
1114
1115
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henryqw/pi-multi-codex",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "Use multiple ChatGPT Codex OAuth accounts in Pi.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"pack:check": "npm pack --dry-run"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@earendil-works/pi-ai": ">=0.86.0 <0.
|
|
29
|
-
"@earendil-works/pi-coding-agent": ">=0.86.0 <0.
|
|
28
|
+
"@earendil-works/pi-ai": ">=0.86.0 <0.100.0",
|
|
29
|
+
"@earendil-works/pi-coding-agent": ">=0.86.0 <0.100.0"
|
|
30
30
|
},
|
|
31
31
|
"repository": {
|
|
32
32
|
"type": "git",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"proper-lockfile": "^4.1.2"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@earendil-works/pi-ai": "0.
|
|
54
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
53
|
+
"@earendil-works/pi-ai": "0.99.1",
|
|
54
|
+
"@earendil-works/pi-coding-agent": "0.99.1",
|
|
55
55
|
"@types/proper-lockfile": "^4.1.4"
|
|
56
56
|
}
|
|
57
57
|
}
|