@@ -2494,6 +2494,150 @@ function createRuntimeRotationShadowHome(originalCodexHome) {
24942494 return mkdtempSync ( join ( shadowRoot , "codex-multi-auth-runtime-home-" ) ) ;
24952495}
24962496
2497+ function parseHookStateTableKey ( line ) {
2498+ const basicStringMatch =
2499+ / ^ \s * \[ \s * h o o k s \. s t a t e \. ( " (?: [ ^ " \\ ] | \\ .) * " ) \s * \] \s * $ / . exec ( line ) ;
2500+ if ( basicStringMatch ) {
2501+ try {
2502+ const parsed = JSON . parse ( basicStringMatch [ 1 ] ) ;
2503+ return typeof parsed === "string" ? parsed : null ;
2504+ } catch {
2505+ return null ;
2506+ }
2507+ }
2508+
2509+ const literalStringMatch = / ^ \s * \[ \s * h o o k s \. s t a t e \. ' ( [ ^ ' ] * ) ' \s * \] \s * $ / . exec (
2510+ line ,
2511+ ) ;
2512+ return literalStringMatch ? literalStringMatch [ 1 ] : null ;
2513+ }
2514+
2515+ function isTomlTableLine ( line ) {
2516+ return / ^ \s * \[ \[ ? \s * (?: " (?: [ ^ " \\ ] | \\ .) * " | ' [ ^ ' ] * ' | [ A - Z a - z 0 - 9 _ - ] + ) (?: \s * \. | \s * \] \] ? \s * (?: # .* ) ? $ ) / . test (
2517+ line ,
2518+ ) ;
2519+ }
2520+
2521+ // Keep these TOML block scan helpers aligned with test/codex-bin-wrapper.test.ts.
2522+ function createTomlBlockScanState ( ) {
2523+ return {
2524+ arrayDepth : 0 ,
2525+ multilineStringDelimiter : null ,
2526+ } ;
2527+ }
2528+
2529+ function isTopLevelTomlBlockScanState ( state ) {
2530+ return state . arrayDepth === 0 && state . multilineStringDelimiter === null ;
2531+ }
2532+
2533+ function updateTomlBlockScanState ( line , state ) {
2534+ for ( let index = 0 ; index < line . length ; index += 1 ) {
2535+ if ( state . multilineStringDelimiter ) {
2536+ const closeIndex = line . indexOf ( state . multilineStringDelimiter , index ) ;
2537+ if ( closeIndex < 0 ) {
2538+ return ;
2539+ }
2540+ index = closeIndex + state . multilineStringDelimiter . length - 1 ;
2541+ state . multilineStringDelimiter = null ;
2542+ continue ;
2543+ }
2544+
2545+ if ( line [ index ] === "#" ) {
2546+ return ;
2547+ }
2548+ if ( line . startsWith ( '"""' , index ) || line . startsWith ( "'''" , index ) ) {
2549+ state . multilineStringDelimiter = line . slice ( index , index + 3 ) ;
2550+ index += 2 ;
2551+ continue ;
2552+ }
2553+ if ( line [ index ] === '"' ) {
2554+ index += 1 ;
2555+ for ( ; index < line . length ; index += 1 ) {
2556+ if ( line [ index ] === "\\" ) {
2557+ index += 1 ;
2558+ } else if ( line [ index ] === '"' ) {
2559+ break ;
2560+ }
2561+ }
2562+ continue ;
2563+ }
2564+ if ( line [ index ] === "'" ) {
2565+ const closeIndex = line . indexOf ( "'" , index + 1 ) ;
2566+ if ( closeIndex < 0 ) return ;
2567+ index = closeIndex ;
2568+ continue ;
2569+ }
2570+ if ( line [ index ] === "[" ) {
2571+ state . arrayDepth += 1 ;
2572+ } else if ( line [ index ] === "]" && state . arrayDepth > 0 ) {
2573+ state . arrayDepth -= 1 ;
2574+ }
2575+ }
2576+ }
2577+
2578+ function mirrorRuntimeShadowHookTrustState (
2579+ rawConfig ,
2580+ originalCodexHome ,
2581+ shadowCodexHome ,
2582+ tomlStringLiteral ,
2583+ ) {
2584+ const sourceHooksPath = join ( originalCodexHome , "hooks.json" ) ;
2585+ const shadowHooksPath = join ( shadowCodexHome , "hooks.json" ) ;
2586+ if ( sourceHooksPath === shadowHooksPath ) {
2587+ return rawConfig ;
2588+ }
2589+
2590+ const lineEnding = rawConfig . includes ( "\r\n" ) ? "\r\n" : "\n" ;
2591+ const lines = rawConfig . length > 0 ? rawConfig . split ( / \r ? \n / ) : [ ] ;
2592+ const sourcePrefix = `${ sourceHooksPath } :` ;
2593+ const existingHookStateKeys = new Set ( ) ;
2594+ for ( const line of lines ) {
2595+ const key = parseHookStateTableKey ( line ) ;
2596+ if ( key ) {
2597+ existingHookStateKeys . add ( key ) ;
2598+ }
2599+ }
2600+
2601+ const output = [ ] ;
2602+ let changed = false ;
2603+ for ( let index = 0 ; index < lines . length ; index += 1 ) {
2604+ const line = lines [ index ] ;
2605+ const key = parseHookStateTableKey ( line ) ;
2606+ output . push ( line ) ;
2607+ if ( ! key || ! key . startsWith ( sourcePrefix ) ) {
2608+ continue ;
2609+ }
2610+
2611+ const blockLines = [ ] ;
2612+ let nextIndex = index + 1 ;
2613+ const blockState = createTomlBlockScanState ( ) ;
2614+ for ( ; nextIndex < lines . length ; nextIndex += 1 ) {
2615+ const nextLine = lines [ nextIndex ] ;
2616+ if (
2617+ isTopLevelTomlBlockScanState ( blockState ) &&
2618+ isTomlTableLine ( nextLine )
2619+ ) {
2620+ break ;
2621+ }
2622+ blockLines . push ( nextLine ) ;
2623+ updateTomlBlockScanState ( nextLine , blockState ) ;
2624+ }
2625+ output . push ( ...blockLines ) ;
2626+ index = nextIndex - 1 ;
2627+ const shadowKey = `${ shadowHooksPath } :${ key . slice ( sourcePrefix . length ) } ` ;
2628+ if ( existingHookStateKeys . has ( shadowKey ) ) {
2629+ continue ;
2630+ }
2631+ output . push ( "" ) ;
2632+ output . push ( `[hooks.state.${ tomlStringLiteral ( shadowKey ) } ]` ) ;
2633+ output . push ( ...blockLines ) ;
2634+ existingHookStateKeys . add ( shadowKey ) ;
2635+ changed = true ;
2636+ }
2637+
2638+ return changed ? output . join ( lineEnding ) : rawConfig ;
2639+ }
2640+
24972641function createRuntimeRotationProxyCodexHome (
24982642 baseEnv ,
24992643 proxyBaseUrl ,
@@ -2535,10 +2679,15 @@ function createRuntimeRotationProxyCodexHome(
25352679 const rawConfig = existsSync ( originalConfigPath )
25362680 ? readFileSync ( originalConfigPath , "utf8" )
25372681 : "" ;
2538- const runtimeConfig = configTomlModule . rewriteConfigTomlForRuntimeRotationProvider (
2539- rawConfig ,
2540- proxyBaseUrl ,
2541- clientApiKey ,
2682+ const runtimeConfig = mirrorRuntimeShadowHookTrustState (
2683+ configTomlModule . rewriteConfigTomlForRuntimeRotationProvider (
2684+ rawConfig ,
2685+ proxyBaseUrl ,
2686+ clientApiKey ,
2687+ ) ,
2688+ originalCodexHome ,
2689+ shadowCodexHome ,
2690+ configTomlModule . tomlStringLiteral ,
25422691 ) ;
25432692 const runtimeConfigPath = join ( shadowCodexHome , "config.toml" ) ;
25442693 writeFileSync ( runtimeConfigPath , runtimeConfig , "utf8" ) ;
0 commit comments