|
1 | 1 | import { homedir } from "node:os"; |
2 | | -import { join, win32 } from "node:path"; |
| 2 | +import { join, normalize, win32 } from "node:path"; |
3 | 3 | import { existsSync, readdirSync } from "node:fs"; |
4 | 4 |
|
5 | 5 | function firstNonEmpty(values: Array<string | undefined>): string | null { |
@@ -72,6 +72,22 @@ function deduplicatePaths(paths: string[]): string[] { |
72 | 72 | return result; |
73 | 73 | } |
74 | 74 |
|
| 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 | + |
75 | 91 | /** |
76 | 92 | * Detects whether a directory contains known Codex storage indicators. |
77 | 93 | * |
@@ -169,13 +185,22 @@ export function getCodexMultiAuthDir(): string { |
169 | 185 | return fromEnv; |
170 | 186 | } |
171 | 187 |
|
| 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 | + |
172 | 193 | const primary = join(getCodexHomeDir(), "multi-auth"); |
173 | 194 | const fallbackCandidates = deduplicatePaths([ |
174 | 195 | ...getFallbackCodexHomeDirs().map((dir) => join(dir, "multi-auth")), |
175 | 196 | getLegacyCodexDir(), |
176 | 197 | ]); |
177 | 198 | const orderedCandidates = deduplicatePaths([primary, ...fallbackCandidates]); |
178 | 199 |
|
| 200 | + if (isExplicitNonDefaultHome) { |
| 201 | + return primary; |
| 202 | + } |
| 203 | + |
179 | 204 | // Prefer candidates that actually contain account storage. This prevents |
180 | 205 | // accidentally switching to a fresh empty directory that only has settings files. |
181 | 206 | for (const candidate of orderedCandidates) { |
|
0 commit comments