Skip to content

Commit ab096bf

Browse files
authored
Merge pull request #355 from ndycode/release/rebuild-main-pr-wave-1.2.3
release: rebuild main PR wave for v1.2.3
2 parents cd4aa12 + 467e5f5 commit ab096bf

34 files changed

Lines changed: 8879 additions & 4263 deletions

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,9 @@ codex auth doctor --json
308308

309309
## Release Notes
310310

311-
- Current stable: [docs/releases/v1.2.2.md](docs/releases/v1.2.2.md)
312-
- Previous stable: [docs/releases/v1.2.1.md](docs/releases/v1.2.1.md)
313-
- Earlier stable: [docs/releases/v1.2.0.md](docs/releases/v1.2.0.md)
311+
- Current stable: [docs/releases/v1.2.3.md](docs/releases/v1.2.3.md)
312+
- Previous stable: [docs/releases/v1.2.2.md](docs/releases/v1.2.2.md)
313+
- Earlier stable: [docs/releases/v1.2.1.md](docs/releases/v1.2.1.md)
314314
- Archived prerelease: [docs/releases/v0.1.0-beta.0.md](docs/releases/v0.1.0-beta.0.md)
315315

316316
## License

docs/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ Public documentation for `codex-multi-auth`.
2323
| [configuration.md](configuration.md) | Stable defaults, precedence, and environment overrides |
2424
| [architecture.md](architecture.md) | Public system overview of the wrapper, storage, and optional plugin runtime |
2525
| [privacy.md](privacy.md) | Data handling and local storage behavior |
26-
| [releases/v1.2.2.md](releases/v1.2.2.md) | Stable release notes |
27-
| [releases/v1.2.1.md](releases/v1.2.1.md) | Previous stable release notes |
28-
| [releases/v1.2.0.md](releases/v1.2.0.md) | Earlier stable release notes |
26+
| [releases/v1.2.3.md](releases/v1.2.3.md) | Stable release notes |
27+
| [releases/v1.2.2.md](releases/v1.2.2.md) | Previous stable release notes |
28+
| [releases/v1.2.1.md](releases/v1.2.1.md) | Earlier stable release notes |
29+
| [releases/v1.2.0.md](releases/v1.2.0.md) | Archived stable release notes |
2930
| [releases/v0.1.7.md](releases/v0.1.7.md) | Archived stable release notes |
3031
| [releases/v0.1.6.md](releases/v0.1.6.md) | Archived stable release notes |
3132
| [releases/v0.1.5.md](releases/v0.1.5.md) | Archived stable release notes |
@@ -51,7 +52,7 @@ Public documentation for `codex-multi-auth`.
5152
| [reference/storage-paths.md](reference/storage-paths.md) | Canonical and compatibility storage paths |
5253
| [reference/public-api.md](reference/public-api.md) | Public API stability and semver contract |
5354
| [reference/error-contracts.md](reference/error-contracts.md) | CLI, JSON, and helper error semantics |
54-
| [releases/v1.2.2.md](releases/v1.2.2.md) | Current stable release notes |
55+
| [releases/v1.2.3.md](releases/v1.2.3.md) | Current stable release notes |
5556
| [releases/v0.1.0-beta.0.md](releases/v0.1.0-beta.0.md) | Archived prerelease reference |
5657
| [Daily Use release notes](#daily-use) | Stable, previous, and archived release notes |
5758
| [releases/legacy-pre-0.1-history.md](releases/legacy-pre-0.1-history.md) | Archived pre-0.1 changelog history |

docs/reference/storage-paths.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Override root:
2121
| File | Default path |
2222
| --- | --- |
2323
| Unified settings | `~/.codex/multi-auth/settings.json` |
24+
| Unified settings backup | `~/.codex/multi-auth/settings.json.bak` |
2425
| Accounts | `~/.codex/multi-auth/openai-codex-accounts.json` |
2526
| Accounts backup | `~/.codex/multi-auth/openai-codex-accounts.json.bak` |
2627
| Accounts WAL | `~/.codex/multi-auth/openai-codex-accounts.json.wal` |
@@ -48,6 +49,14 @@ Compatibility note:
4849
Backup metadata:
4950

5051
- `getBackupMetadata()` reports deterministic snapshot lists for the canonical account pool (primary, WAL, `.bak`, `.bak.1`, `.bak.2`, and discovered manual backups) and flagged-account state (primary, `.bak`, `.bak.1`, `.bak.2`, and discovered manual backups). Cache-like artifacts and `.reset-intent` markers are excluded from recovery candidates.
52+
- `settings.json.bak` stores the last valid unified settings snapshot before each write and is used as a recovery fallback when `settings.json` is unreadable.
53+
- Flagged-account backup recovery is suppressed whenever the flagged reset marker is still present, so partial clears cannot revive previously cleared flagged entries.
54+
55+
Upgrade note:
56+
57+
- Restore workflows now distinguish between unreadable state and intentionally cleared state. `settings.json.bak` is only used when `settings.json` exists but cannot be read, while flagged-account backups stay suppressed whenever the reset marker survives a partial clear.
58+
- Operators validating a restore or clear flow should use `codex auth verify-flagged`, `codex auth fix --dry-run`, and `codex auth doctor --fix` to confirm what will be recovered, what stays cleared, and whether manual repair is still needed.
59+
- Maintainers validating the on-disk upgrade behavior can run `npm run build` plus `npm test -- --run test/unified-settings.test.ts test/storage-recovery-paths.test.ts test/storage-flagged.test.ts` before shipping backup or restore changes.
5160

5261
---
5362

docs/releases/v1.2.3.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Release v1.2.3
2+
3+
Release line: `stable`
4+
5+
This release supersedes the open `main`-target PR wave with one rebuilt, validated integration branch.
6+
7+
## Scope
8+
9+
- Current package version in `package.json` is `1.2.3`.
10+
- Canonical command family remains `codex auth ...`.
11+
- Canonical package name remains `codex-multi-auth`.
12+
- The release branch rebuild starts from `origin/main` commit `cbce5f5c3c5588c08a388d600306366ccb95a6a7`.
13+
14+
## What Changed
15+
16+
- remediated the `audit:ci` dependency findings and refreshed the lockfile on current `main`
17+
- fixed ready-first account ordering, including the menu auto-refresh race that could re-skip a later refresh
18+
- hardened the Codex wrapper compatibility path so unsupported reasoning-effort config is rewritten correctly, staged auth state syncs back before cleanup, and cleanup-failure tests do not rely on source rewriting
19+
- fixed usage-limit cooldown persistence across account state, fallback 429 handling, and quota scheduling without dropping secondary quota state
20+
- carried forward the config validation, unified-settings backup recovery, and flagged-backup recovery hardening from the older config/storage PR lane
21+
22+
## Included PR Lanes
23+
24+
- `#351` `chore: remediate audit-ci dependency findings`
25+
- `#352` `fix ready-first account ordering regressions`
26+
- `#353` `fix codex wrapper compatibility handling`
27+
- `#354` `fix usage-limit cooldown persistence`
28+
- `#344` `fix config validation and flagged backup recovery`
29+
30+
## Superseded PR Stack
31+
32+
- `#344` `fix config validation and flagged backup recovery`
33+
- `#351` `chore: remediate audit-ci dependency findings`
34+
- `#352` `fix ready-first account ordering regressions`
35+
- `#353` `fix codex wrapper compatibility handling`
36+
- `#354` `fix usage-limit cooldown persistence`
37+
38+
## Validation
39+
40+
- `npm run lint`
41+
- `npm run typecheck`
42+
- `npm test -- --pool=threads --maxWorkers=1`
43+
- `npm run build`
44+
- `npm run clean:repo:check`
45+
- `npm run audit:ci`
46+
- Full suite passed: `222/222` files, `3292/3292` tests
47+
48+
## Related
49+
50+
- [../getting-started.md](../getting-started.md)
51+
- [../upgrade.md](../upgrade.md)
52+
- [../reference/commands.md](../reference/commands.md)
53+
- [../reference/public-api.md](../reference/public-api.md)

index.ts

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,20 +1858,26 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
18581858
break;
18591859
}
18601860

1861-
if (rateLimit) {
1861+
if (response.status === 429) {
18621862
runtimeMetrics.rateLimitedResponses++;
1863+
const retryAfterMs =
1864+
rateLimit?.retryAfterMs ?? 60_000;
18631865
const { attempt, delayMs } = getRateLimitBackoff(
18641866
account.index,
18651867
quotaKey,
1866-
rateLimit.retryAfterMs,
1868+
retryAfterMs,
1869+
);
1870+
const cooldownMs = Math.max(
1871+
delayMs,
1872+
retryAfterMs,
18671873
);
18681874
preemptiveQuotaScheduler.markRateLimited(
18691875
quotaScheduleKey,
1870-
delayMs,
1876+
cooldownMs,
18711877
);
1872-
const waitLabel = formatWaitTime(delayMs);
1878+
const waitLabel = formatWaitTime(cooldownMs);
18731879

1874-
if (delayMs <= RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS) {
1880+
if (cooldownMs <= RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS) {
18751881
if (
18761882
accountManager.shouldShowAccountToast(
18771883
account.index,
@@ -1887,16 +1893,16 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
18871893
}
18881894

18891895
await sleep(
1890-
addJitter(Math.max(MIN_BACKOFF_MS, delayMs), 0.2),
1896+
addJitter(Math.max(MIN_BACKOFF_MS, cooldownMs), 0.2),
18911897
);
18921898
continue;
18931899
}
18941900

18951901
accountManager.markRateLimitedWithReason(
18961902
account,
1897-
delayMs,
1903+
cooldownMs,
18981904
modelFamily,
1899-
parseRateLimitReason(rateLimit.code),
1905+
parseRateLimitReason(rateLimit?.code),
19001906
model,
19011907
);
19021908
accountManager.recordRateLimit(
@@ -2142,19 +2148,34 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
21422148
);
21432149
}
21442150
if (!fallbackResponse.ok) {
2151+
const { response: handledFallbackResponse, rateLimit: fallbackRateLimit } =
2152+
await handleErrorResponse(fallbackResponse);
21452153
try {
21462154
await fallbackResponse.body?.cancel();
21472155
} catch {
2148-
// Best effort cleanup before trying next fallback account.
2156+
// Best-effort only; the error body has already been read.
21492157
}
2150-
if (fallbackResponse.status === 429) {
2158+
if (handledFallbackResponse.status === 429) {
21512159
const retryAfterMs =
2152-
parseRetryAfterHintMs(
2153-
fallbackResponse.headers,
2154-
) ?? 60_000;
2160+
fallbackRateLimit?.retryAfterMs ?? 60_000;
2161+
const fallbackQuotaKey =
2162+
model ? `${modelFamily}:${model}` : modelFamily;
2163+
const { delayMs } = getRateLimitBackoff(
2164+
fallbackAccount.index,
2165+
fallbackQuotaKey,
2166+
retryAfterMs,
2167+
);
2168+
const cooldownMs = Math.max(
2169+
delayMs,
2170+
retryAfterMs,
2171+
);
2172+
preemptiveQuotaScheduler.markRateLimited(
2173+
`${fallbackEntitlementAccountKey}:${model ?? modelFamily}`,
2174+
cooldownMs,
2175+
);
21552176
accountManager.markRateLimitedWithReason(
21562177
fallbackAccount,
2157-
retryAfterMs,
2178+
cooldownMs,
21582179
modelFamily,
21592180
"quota",
21602181
model,
@@ -2164,6 +2185,7 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
21642185
modelFamily,
21652186
model,
21662187
);
2188+
accountManager.saveToDiskDebounced();
21672189
} else {
21682190
accountManager.recordFailure(
21692191
fallbackAccount,

lib/accounts.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -783,15 +783,17 @@ export class AccountManager {
783783

784784
const baseKey = getQuotaKey(family);
785785
if (!model || reason === "quota" || reason === "unknown") {
786-
account.rateLimitResetTimes[baseKey] = resetAt;
786+
const currentResetAt = account.rateLimitResetTimes[baseKey] ?? 0;
787+
account.rateLimitResetTimes[baseKey] = Math.max(currentResetAt, resetAt);
787788
}
788789

789790
if (
790791
model &&
791792
(reason === "tokens" || reason === "concurrent" || reason === "unknown")
792793
) {
793794
const modelKey = getQuotaKey(family, model);
794-
account.rateLimitResetTimes[modelKey] = resetAt;
795+
const currentResetAt = account.rateLimitResetTimes[modelKey] ?? 0;
796+
account.rateLimitResetTimes[modelKey] = Math.max(currentResetAt, resetAt);
795797
}
796798

797799
account.lastRateLimitReason = reason;

0 commit comments

Comments
 (0)