@@ -42,6 +42,22 @@ const RUNTIME_ROTATION_SHADOW_HOME_OMIT_STATE_FILES = new Set([
4242 "accounts.json" ,
4343] ) ;
4444const RUNTIME_ROTATION_SHADOW_HOME_OMIT_ROOT_DIRS = new Set ( [ "multi-auth" ] ) ;
45+ const RUNTIME_ROTATION_SHADOW_HOME_LINK_ONLY_ROOT_DIRS = new Set ( [
46+ ".sandbox" ,
47+ ".sandbox-bin" ,
48+ ".sandbox-secrets" ,
49+ ".tmp" ,
50+ "ambient-suggestions" ,
51+ "archived_sessions" ,
52+ "backups" ,
53+ "cache" ,
54+ "generated_images" ,
55+ "log" ,
56+ "sqlite" ,
57+ "tmp" ,
58+ "understand-anything" ,
59+ "vendor_imports" ,
60+ ] ) ;
4561const SHADOW_HOME_STATE_FILE_SET = new Set ( SHADOW_HOME_STATE_FILES ) ;
4662const SHADOW_HOME_CONFIG_FILE = "config.toml" ;
4763const SHADOW_HOME_SYNC_LOCK_DIR = ".codex-multi-auth-shadow-sync.lock" ;
@@ -91,6 +107,7 @@ const shadowHomeCleanupRetryMarkerDir =
91107let warnedInvalidRuntimeRotationProxyEnv = false ;
92108let warnedPendingAccountReadIdOverflow = false ;
93109let warnedShadowHomeSqliteLinkFailure = false ;
110+ const warnedShadowHomeLinkOnlyDirectoryFailures = new Set ( ) ;
94111const warnedShadowHomeSqliteSidecarPlaceholderFailures = new Set ( ) ;
95112
96113async function loadRuntimeConstants ( ) {
@@ -1868,6 +1885,41 @@ function mirrorDirectoryIntoShadowHome(sourcePath, destinationPath) {
18681885 return "copied" ;
18691886}
18701887
1888+ function linkDirectoryIntoShadowHome ( sourcePath , destinationPath ) {
1889+ try {
1890+ if ( ( process . env . CODEX_MULTI_AUTH_TEST_FORCE_SHADOW_DIR_COPY ?? "" ) . trim ( ) === "1" ) {
1891+ throw new Error ( "simulated directory link failure" ) ;
1892+ }
1893+ symlinkSync (
1894+ sourcePath ,
1895+ destinationPath ,
1896+ process . platform === "win32" ? "junction" : "dir" ,
1897+ ) ;
1898+ return true ;
1899+ } catch {
1900+ return false ;
1901+ }
1902+ }
1903+
1904+ function warnSkippedLinkOnlyShadowHomeDirectory ( name ) {
1905+ if ( warnedShadowHomeLinkOnlyDirectoryFailures . has ( name ) ) {
1906+ return ;
1907+ }
1908+ warnedShadowHomeLinkOnlyDirectoryFailures . add ( name ) ;
1909+ console . error (
1910+ `codex-multi-auth: skipped optional shadow-home directory ${ name } because linking failed; refusing to copy generated runtime data.` ,
1911+ ) ;
1912+ }
1913+
1914+ function shouldCopyRuntimeGeneratedShadowHomeDirectoryFallback ( ) {
1915+ const normalized = (
1916+ process . env . CODEX_MULTI_AUTH_RUNTIME_SHADOW_COPY_GENERATED_DIRS ?? ""
1917+ )
1918+ . trim ( )
1919+ . toLowerCase ( ) ;
1920+ return normalized === "1" || normalized === "true" || normalized === "yes" ;
1921+ }
1922+
18711923function linkFileIntoShadowHome ( sourcePath , destinationPath ) {
18721924 try {
18731925 symlinkSync ( sourcePath , destinationPath , "file" ) ;
@@ -1913,8 +1965,11 @@ function isCodexRuntimeLocalSqliteFile(name) {
19131965
19141966function isCodexRuntimeTransientStateFile ( name ) {
19151967 const normalizedName = normalizeRuntimeShadowHomeEntryName ( name ) ;
1916- return / ^ (?: a u t h | a c c o u n t s ) \. j s o n \. \d + \. [ a - z 0 - 9 ] + \. t m p $ / . test (
1917- normalizedName ,
1968+ return (
1969+ / ^ (?: a u t h | a c c o u n t s ) \. j s o n \. \d + \. [ a - z 0 - 9 ] + \. t m p $ / . test (
1970+ normalizedName ,
1971+ ) ||
1972+ / ^ \. c o d e x - g l o b a l - s t a t e \. j s o n \. t m p - [ a - z 0 - 9 - ] + $ / . test ( normalizedName )
19181973 ) ;
19191974}
19201975
@@ -1927,6 +1982,11 @@ function isRuntimeRotationShadowHomeOmittedEntry(name) {
19271982 ) ;
19281983}
19291984
1985+ function isRuntimeRotationShadowHomeLinkOnlyDirectory ( name ) {
1986+ const normalizedName = normalizeRuntimeShadowHomeEntryName ( name ) ;
1987+ return RUNTIME_ROTATION_SHADOW_HOME_LINK_ONLY_ROOT_DIRS . has ( normalizedName ) ;
1988+ }
1989+
19301990function shouldMaterializeFileIntoShadowHome ( name ) {
19311991 return isSqliteMainFile ( name ) || isSqliteSidecarFile ( name ) ;
19321992}
@@ -2154,6 +2214,8 @@ function createShadowHomeMirror(
21542214 const skipSyncBackNames = new Set ( options . skipSyncBackNames ?? [ ] ) ;
21552215 const skipMirrorPredicate = options . skipMirrorPredicate ?? ( ( ) => false ) ;
21562216 const skipSyncBackPredicate = options . skipSyncBackPredicate ?? ( ( ) => false ) ;
2217+ const linkOnlyDirectoryPredicate =
2218+ options . linkOnlyDirectoryPredicate ?? ( ( ) => false ) ;
21572219 const originalFileStates = new Map ( ) ;
21582220 const copiedDirectoryNames = new Set ( ) ;
21592221 const rememberSyncFile = ( name ) => {
@@ -2200,6 +2262,15 @@ function createShadowHomeMirror(
22002262 throw new Error ( `Expected ${ name } to be a file` ) ;
22012263 }
22022264 if ( directoryLike ) {
2265+ if (
2266+ linkOnlyDirectoryPredicate ( name ) &&
2267+ ! shouldCopyRuntimeGeneratedShadowHomeDirectoryFallback ( )
2268+ ) {
2269+ if ( ! linkDirectoryIntoShadowHome ( sourcePath , destinationPath ) ) {
2270+ warnSkippedLinkOnlyShadowHomeDirectory ( name ) ;
2271+ }
2272+ continue ;
2273+ }
22032274 if ( mirrorDirectoryIntoShadowHome ( sourcePath , destinationPath ) === "copied" ) {
22042275 copiedDirectoryNames . add ( name ) ;
22052276 }
@@ -2309,6 +2380,10 @@ function resolveOriginalMultiAuthDir(env) {
23092380 return undefined ;
23102381}
23112382
2383+ function resolveRuntimeRotationOriginalMultiAuthDir ( originalCodexHome , env ) {
2384+ return resolveOriginalMultiAuthDir ( env ) ?? join ( originalCodexHome , "multi-auth" ) ;
2385+ }
2386+
23122387function parseRuntimeRotationProxyEnv ( value ) {
23132388 if ( value === undefined ) return undefined ;
23142389 const normalized = value . trim ( ) . toLowerCase ( ) ;
@@ -2421,6 +2496,7 @@ function createRuntimeRotationProxyCodexHome(
24212496 skipMirrorPredicate : isRuntimeRotationShadowHomeOmittedEntry ,
24222497 skipSyncBackNames : RUNTIME_ROTATION_SHADOW_HOME_OMIT_STATE_FILES ,
24232498 skipSyncBackPredicate : isRuntimeRotationShadowHomeOmittedEntry ,
2499+ linkOnlyDirectoryPredicate : isRuntimeRotationShadowHomeLinkOnlyDirectory ,
24242500 } ,
24252501 ) ;
24262502 omitRuntimeRotationShadowHomeStateFiles ( shadowCodexHome ) ;
@@ -2445,11 +2521,11 @@ function createRuntimeRotationProxyCodexHome(
24452521 ...baseEnv ,
24462522 CODEX_HOME : shadowCodexHome ,
24472523 OPENAI_API_KEY : clientApiKey ,
2524+ CODEX_MULTI_AUTH_DIR : resolveRuntimeRotationOriginalMultiAuthDir (
2525+ originalCodexHome ,
2526+ baseEnv ,
2527+ ) ,
24482528 } ;
2449- const originalMultiAuthDir = resolveOriginalMultiAuthDir ( baseEnv ) ;
2450- if ( originalMultiAuthDir ) {
2451- forwardedEnv . CODEX_MULTI_AUTH_DIR = originalMultiAuthDir ;
2452- }
24532529
24542530 return {
24552531 env : forwardedEnv ,
@@ -2904,6 +2980,10 @@ function startRuntimeRotationAppHelper(baseContext) {
29042980 {
29052981 env : {
29062982 ...baseContext . env ,
2983+ CODEX_MULTI_AUTH_DIR : resolveRuntimeRotationOriginalMultiAuthDir (
2984+ realCodexHome ,
2985+ baseContext . env ,
2986+ ) ,
29072987 [ APP_RUNTIME_HELPER_OWNER_PID_ENV ] : String ( process . pid ) ,
29082988 [ APP_RUNTIME_HELPER_REAL_CODEX_HOME_ENV ] : realCodexHome ,
29092989 } ,
@@ -3632,6 +3712,8 @@ function repairCodexSessionIndex(codexHome) {
36323712
36333713 const additions = [ ] ;
36343714 for ( const rolloutPath of collectRolloutFiles ( sessionsDir ) ) {
3715+ const idFromName = extractRolloutIdFromFilename ( basename ( rolloutPath ) ) ;
3716+ if ( idFromName && seen . has ( idFromName ) ) continue ;
36353717 const entry = parseRolloutIndexEntry ( rolloutPath ) ;
36363718 if ( ! entry || seen . has ( entry . id ) ) continue ;
36373719 seen . add ( entry . id ) ;
0 commit comments