( updater: (currentConfig: GlobalConfig) => GlobalConfig, )
| 795 | } |
| 796 | |
| 797 | export function saveGlobalConfig( |
| 798 | updater: (currentConfig: GlobalConfig) => GlobalConfig, |
| 799 | ): void { |
| 800 | if (process.env.NODE_ENV === 'test') { |
| 801 | const config = updater(TEST_GLOBAL_CONFIG_FOR_TESTING) |
| 802 | // Skip if no changes (same reference returned) |
| 803 | if (config === TEST_GLOBAL_CONFIG_FOR_TESTING) { |
| 804 | return |
| 805 | } |
| 806 | Object.assign(TEST_GLOBAL_CONFIG_FOR_TESTING, config) |
| 807 | return |
| 808 | } |
| 809 | |
| 810 | let written: GlobalConfig | null = null |
| 811 | try { |
| 812 | const didWrite = saveConfigWithLock( |
| 813 | getGlobalClaudeFile(), |
| 814 | createDefaultGlobalConfig, |
| 815 | current => { |
| 816 | const config = updater(current) |
| 817 | // Skip if no changes (same reference returned) |
| 818 | if (config === current) { |
| 819 | return current |
| 820 | } |
| 821 | written = { |
| 822 | ...config, |
| 823 | projects: removeProjectHistory(current.projects), |
| 824 | } |
| 825 | return written |
| 826 | }, |
| 827 | ) |
| 828 | // Only write-through if we actually wrote. If the auth-loss guard |
| 829 | // tripped (or the updater made no changes), the file is untouched and |
| 830 | // the cache is still valid -- touching it would corrupt the guard. |
| 831 | if (didWrite && written) { |
| 832 | writeThroughGlobalConfigCache(written) |
| 833 | } |
| 834 | } catch (error) { |
| 835 | logForDebugging(`Failed to save config with lock: ${error}`, { |
| 836 | level: 'error', |
| 837 | }) |
| 838 | // Fall back to non-locked version on error. This fallback is a race |
| 839 | // window: if another process is mid-write (or the file got truncated), |
| 840 | // getConfig returns defaults. Refuse to write those over a good cached |
| 841 | // config to avoid wiping auth. See GH #3117. |
| 842 | const currentConfig = getConfig( |
| 843 | getGlobalClaudeFile(), |
| 844 | createDefaultGlobalConfig, |
| 845 | ) |
| 846 | if (wouldLoseAuthState(currentConfig)) { |
| 847 | logForDebugging( |
| 848 | 'saveGlobalConfig fallback: re-read config is missing auth that cache has; refusing to write. See GH #3117.', |
| 849 | { level: 'error' }, |
| 850 | ) |
| 851 | logEvent('tengu_config_auth_loss_prevented', {}) |
| 852 | return |
| 853 | } |
| 854 | const config = updater(currentConfig) |
no test coverage detected