Skip to content

Commit 9ae2967

Browse files
committed
Merge branch 'pr-check-71' into release/rc-main-open-prs
# Conflicts: # test/helpers/remove-with-retry.ts
2 parents 35ef68d + bf74f37 commit 9ae2967

10 files changed

Lines changed: 1706 additions & 104 deletions

docs/reference/storage-paths.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Override root:
2525
| Accounts backup | `~/.codex/multi-auth/openai-codex-accounts.json.bak` |
2626
| Accounts WAL | `~/.codex/multi-auth/openai-codex-accounts.json.wal` |
2727
| Flagged accounts | `~/.codex/multi-auth/openai-codex-flagged-accounts.json` |
28+
| Flagged accounts backup | `~/.codex/multi-auth/openai-codex-flagged-accounts.json.bak` |
2829
| Quota cache | `~/.codex/multi-auth/quota-cache.json` |
2930
| Logs | `~/.codex/multi-auth/logs/codex-plugin/` |
3031
| Cache | `~/.codex/multi-auth/cache/` |
@@ -36,6 +37,10 @@ Ownership note:
3637
- `~/.codex/multi-auth/*` is managed by this project.
3738
- `~/.codex/accounts.json` and `~/.codex/auth.json` are managed by official Codex CLI.
3839

40+
Backup metadata:
41+
42+
- `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.
43+
3944
---
4045

4146
## Project-Scoped Account Paths

index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3180,7 +3180,9 @@ while (attempted.size < Math.max(1, accountCount)) {
31803180
await clearAccounts();
31813181
await clearFlaggedAccounts();
31823182
invalidateAccountManagerCache();
3183-
console.log("\nDeleted all accounts. Starting fresh.\n");
3183+
console.log(
3184+
"\nCleared saved accounts from active storage. Recovery snapshots remain available. Starting fresh.\n",
3185+
);
31843186
}
31853187
break;
31863188
}

lib/codex-manager.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {
5050
type QuotaCacheEntry,
5151
} from "./quota-cache.js";
5252
import {
53+
clearAccounts,
5354
getStoragePath,
5455
loadFlaggedAccounts,
5556
loadAccounts,
@@ -3650,12 +3651,7 @@ async function runDoctor(args: string[]): Promise<number> {
36503651
}
36513652

36523653
async function clearAccountsAndReset(): Promise<void> {
3653-
await saveAccounts({
3654-
version: 3,
3655-
accounts: [],
3656-
activeIndex: 0,
3657-
activeIndexByFamily: {},
3658-
});
3654+
await clearAccounts();
36593655
}
36603656

36613657
async function handleManageAction(
@@ -3808,7 +3804,7 @@ async function runAuthLogin(): Promise<number> {
38083804
if (menuResult.mode === "fresh" && menuResult.deleteAll) {
38093805
await runActionPanel("Reset Accounts", "Deleting all saved accounts", async () => {
38103806
await clearAccountsAndReset();
3811-
console.log("Deleted all accounts.");
3807+
console.log("Cleared saved accounts from active storage. Recovery snapshots remain available.");
38123808
}, displaySettings);
38133809
continue;
38143810
}

lib/runtime-paths.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { homedir } from "node:os";
2-
import { join, win32 } from "node:path";
2+
import { join, normalize, win32 } from "node:path";
33
import { existsSync, readdirSync } from "node:fs";
44

55
function firstNonEmpty(values: Array<string | undefined>): string | null {
@@ -72,6 +72,22 @@ function deduplicatePaths(paths: string[]): string[] {
7272
return result;
7373
}
7474

75+
function pathsEqualNormalized(a: string, b: string): boolean {
76+
const normalizePath = (value: string): string => {
77+
const trimmed = value.trim();
78+
if (process.platform === "win32") {
79+
const normalized = win32.normalize(trimmed);
80+
const root = win32.parse(normalized).root;
81+
const withoutTrailing =
82+
normalized === root ? normalized : normalized.replace(/[\\/]+$/, "");
83+
return withoutTrailing.toLowerCase();
84+
}
85+
const normalized = normalize(trimmed);
86+
return normalized === "/" ? "/" : normalized.replace(/\/+$/, "");
87+
};
88+
return normalizePath(a) === normalizePath(b);
89+
}
90+
7591
/**
7692
* Detects whether a directory contains known Codex storage indicators.
7793
*
@@ -169,13 +185,22 @@ export function getCodexMultiAuthDir(): string {
169185
return fromEnv;
170186
}
171187

188+
const codexHomeFromEnv = (process.env.CODEX_HOME ?? "").trim();
189+
const defaultCodexHome = join(getResolvedUserHomeDir(), ".codex");
190+
const isExplicitNonDefaultHome =
191+
codexHomeFromEnv.length > 0 && !pathsEqualNormalized(codexHomeFromEnv, defaultCodexHome);
192+
172193
const primary = join(getCodexHomeDir(), "multi-auth");
173194
const fallbackCandidates = deduplicatePaths([
174195
...getFallbackCodexHomeDirs().map((dir) => join(dir, "multi-auth")),
175196
getLegacyCodexDir(),
176197
]);
177198
const orderedCandidates = deduplicatePaths([primary, ...fallbackCandidates]);
178199

200+
if (isExplicitNonDefaultHome) {
201+
return primary;
202+
}
203+
179204
// Prefer candidates that actually contain account storage. This prevents
180205
// accidentally switching to a fresh empty directory that only has settings files.
181206
for (const candidate of orderedCandidates) {

0 commit comments

Comments
 (0)