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