Skip to content

Commit 441c6a2

Browse files
committed
release: rebuild main PR wave for v1.2.3
1 parent 4f8b441 commit 441c6a2

14 files changed

Lines changed: 275 additions & 72 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/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: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,6 +2148,11 @@ export const OpenAIOAuthPlugin: Plugin = async ({ client }: PluginInput) => {
21482148
if (!fallbackResponse.ok) {
21492149
const { response: handledFallbackResponse, rateLimit: fallbackRateLimit } =
21502150
await handleErrorResponse(fallbackResponse);
2151+
try {
2152+
await fallbackResponse.body?.cancel();
2153+
} catch {
2154+
// Best-effort only; the error body has already been read.
2155+
}
21512156
if (handledFallbackResponse.status === 429) {
21522157
const retryAfterMs =
21532158
fallbackRateLimit?.retryAfterMs ?? 60_000;

lib/preemptive-quota-scheduler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ export class PreemptiveQuotaScheduler {
224224
usedPercent: 100,
225225
resetAtMs: nextResetAtMs,
226226
},
227-
secondary: {},
227+
secondary: existing?.secondary ? { ...existing.secondary } : {},
228228
updatedAt: Math.max(existing?.updatedAt ?? 0, now),
229229
});
230230
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codex-multi-auth",
3-
"version": "1.2.2",
3+
"version": "1.2.3",
44
"description": "Multi-account OAuth manager and codex auth wrapper for the official @openai/codex CLI, with switching, health checks, and recovery tools",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",

scripts/codex.js

Lines changed: 70 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
mkdtempSync,
1010
readFileSync,
1111
rmSync,
12+
statSync,
1213
writeFileSync,
1314
} from "node:fs";
1415
import { createRequire } from "node:module";
@@ -21,6 +22,11 @@ import { normalizeAuthAlias, shouldHandleMultiAuthAuth } from "./codex-routing.j
2122

2223
const RETRYABLE_SHADOW_HOME_CLEANUP_CODES = new Set(["EBUSY", "EPERM", "ENOTEMPTY"]);
2324
const SHADOW_HOME_CLEANUP_BACKOFF_MS = [20, 60, 120];
25+
const SHADOW_HOME_STATE_FILES = ["auth.json", "accounts.json", ".codex-global-state.json"];
26+
let shadowHomeCleanupBusyFailuresRemaining = Number.parseInt(
27+
process.env.CODEX_MULTI_AUTH_TEST_SHADOW_CLEANUP_BUSY_FAILURES ?? "0",
28+
10,
29+
);
2430

2531
function isRetryableShadowHomeCleanupError(error) {
2632
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
@@ -37,6 +43,12 @@ function sleepSync(ms) {
3743
function removeDirectoryWithRetry(targetPath) {
3844
for (let attempt = 0; attempt <= SHADOW_HOME_CLEANUP_BACKOFF_MS.length; attempt += 1) {
3945
try {
46+
if (shadowHomeCleanupBusyFailuresRemaining > 0) {
47+
shadowHomeCleanupBusyFailuresRemaining -= 1;
48+
const error = new Error("simulated busy cleanup");
49+
error.code = "EBUSY";
50+
throw error;
51+
}
4052
rmSync(targetPath, { recursive: true, force: true });
4153
return;
4254
} catch (error) {
@@ -477,21 +489,19 @@ function resolveOriginalMultiAuthDir(env) {
477489
return undefined;
478490
}
479491

480-
function createCompatibilityCodexHome(rawArgs, baseEnv = process.env) {
481-
const { args: nextArgs, requestedModel } = rewriteReasoningConfigArgs(rawArgs);
492+
function createCompatibilityCodexHome(
493+
processedArgs,
494+
requestedModel,
495+
baseEnv = process.env,
496+
) {
482497
if (!requestedModel) {
483-
return { args: nextArgs, env: baseEnv, cleanup: undefined };
484-
}
485-
486-
const xhighCompatEffort = coerceReasoningEffortForModel(requestedModel, "xhigh");
487-
if (xhighCompatEffort === "xhigh") {
488-
return { args: nextArgs, env: baseEnv, cleanup: undefined };
498+
return { args: processedArgs, env: baseEnv, cleanup: undefined };
489499
}
490500

491501
const originalCodexHome = resolveCodexHomeDir(baseEnv);
492502
const configPath = join(originalCodexHome, "config.toml");
493503
if (!existsSync(configPath)) {
494-
return { args: nextArgs, env: baseEnv, cleanup: undefined };
504+
return { args: processedArgs, env: baseEnv, cleanup: undefined };
495505
}
496506

497507
const rawConfig = readFileSync(configPath, "utf8");
@@ -500,7 +510,7 @@ function createCompatibilityCodexHome(rawArgs, baseEnv = process.env) {
500510
requestedModel,
501511
);
502512
if (compatConfig === rawConfig) {
503-
return { args: nextArgs, env: baseEnv, cleanup: undefined };
513+
return { args: processedArgs, env: baseEnv, cleanup: undefined };
504514
}
505515

506516
const shadowCodexHome = mkdtempSync(join(tmpdir(), "codex-multi-auth-home-"));
@@ -518,11 +528,34 @@ function createCompatibilityCodexHome(rawArgs, baseEnv = process.env) {
518528
// Best-effort only; permission semantics vary by platform.
519529
}
520530
};
531+
const syncShadowHomeStateBack = () => {
532+
for (const name of SHADOW_HOME_STATE_FILES) {
533+
const shadowPath = join(shadowCodexHome, name);
534+
if (!existsSync(shadowPath)) {
535+
continue;
536+
}
537+
538+
try {
539+
const shadowStats = statSync(shadowPath);
540+
const originalPath = join(originalCodexHome, name);
541+
if (existsSync(originalPath)) {
542+
const originalStats = statSync(originalPath);
543+
if (originalStats.isFile() && originalStats.mtimeMs > shadowStats.mtimeMs) {
544+
continue;
545+
}
546+
}
547+
copyFileSync(shadowPath, originalPath);
548+
tightenShadowHomePermissions(originalPath);
549+
} catch {
550+
// Best-effort only; runtime auth refreshes should not fail cleanup.
551+
}
552+
}
553+
};
521554
try {
522555
const compatConfigPath = join(shadowCodexHome, "config.toml");
523556
writeFileSync(compatConfigPath, compatConfig, "utf8");
524557
tightenShadowHomePermissions(compatConfigPath);
525-
for (const name of ["auth.json", "accounts.json", ".codex-global-state.json"]) {
558+
for (const name of SHADOW_HOME_STATE_FILES) {
526559
const sourcePath = join(originalCodexHome, name);
527560
if (existsSync(sourcePath)) {
528561
const destinationPath = join(shadowCodexHome, name);
@@ -534,6 +567,10 @@ function createCompatibilityCodexHome(rawArgs, baseEnv = process.env) {
534567
cleanup();
535568
throw error;
536569
}
570+
const cleanupWithSync = () => {
571+
syncShadowHomeStateBack();
572+
cleanup();
573+
};
537574

538575
const forwardedEnv = {
539576
...baseEnv,
@@ -545,23 +582,30 @@ function createCompatibilityCodexHome(rawArgs, baseEnv = process.env) {
545582
}
546583

547584
return {
548-
args: nextArgs,
585+
args: processedArgs,
549586
env: forwardedEnv,
550-
cleanup,
587+
cleanup: cleanupWithSync,
551588
};
552589
}
553590

554591
function buildForwardArgs(rawArgs) {
555-
const { args: compatibilityArgs } = rewriteReasoningConfigArgs(rawArgs);
592+
const { args: compatibilityArgs, requestedModel } = rewriteReasoningConfigArgs(rawArgs);
556593
const forceFileAuthStore = (process.env.CODEX_MULTI_AUTH_FORCE_FILE_AUTH_STORE ?? "1").trim() !== "0";
557-
if (!forceFileAuthStore) return compatibilityArgs;
558-
if (hasCliAuthCredentialsStoreOverride(compatibilityArgs)) return compatibilityArgs;
594+
if (!forceFileAuthStore) {
595+
return { args: compatibilityArgs, requestedModel };
596+
}
597+
if (hasCliAuthCredentialsStoreOverride(compatibilityArgs)) {
598+
return { args: compatibilityArgs, requestedModel };
599+
}
559600

560-
return [
561-
...compatibilityArgs,
562-
"-c",
563-
'cli_auth_credentials_store="file"',
564-
];
601+
return {
602+
args: [
603+
...compatibilityArgs,
604+
"-c",
605+
'cli_auth_credentials_store="file"',
606+
],
607+
requestedModel,
608+
};
565609
}
566610

567611
function normalizeExitCode(value) {
@@ -919,8 +963,11 @@ async function main() {
919963
}
920964

921965
await autoSyncManagerActiveSelectionIfEnabled();
922-
const forwardArgs = buildForwardArgs(rawArgs);
923-
const compatibility = createCompatibilityCodexHome(forwardArgs);
966+
const { args: forwardArgs, requestedModel } = buildForwardArgs(rawArgs);
967+
const compatibility = createCompatibilityCodexHome(
968+
forwardArgs,
969+
requestedModel,
970+
);
924971
return forwardToRealCodex(
925972
realCodexBin,
926973
compatibility.args,

test/accounts.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3221,7 +3221,6 @@ describe("AccountManager", () => {
32213221
expires: now + 3600000,
32223222
});
32233223

3224-
<<<<<<< HEAD
32253224
expect(account.accountId).toBe("account-enriched");
32263225
expect(account.email).toBe("enriched@example.com");
32273226
expect(getRuntimeAccountIdentityKey(account)).toBe(

0 commit comments

Comments
 (0)