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