()
| 126 | * an extra one during the 24h window is acceptable. |
| 127 | */ |
| 128 | export async function checkMetricsEnabled(): Promise<MetricsStatus> { |
| 129 | // Service key OAuth sessions lack user:profile scope → would 403. |
| 130 | // API key users (non-subscribers) fall through and use x-api-key auth. |
| 131 | // This check runs before the disk read so we never persist auth-state-derived |
| 132 | // answers — only real API responses go to disk. Otherwise a service-key |
| 133 | // session would poison the cache for a later full-OAuth session. |
| 134 | if (isClaudeAISubscriber() && !hasProfileScope()) { |
| 135 | return { enabled: false, hasError: false } |
| 136 | } |
| 137 | |
| 138 | const cached = getGlobalConfig().metricsStatusCache |
| 139 | if (cached) { |
| 140 | if (Date.now() - cached.timestamp > DISK_CACHE_TTL_MS) { |
| 141 | // saveGlobalConfig's fallback path (config.ts:731) can throw if both |
| 142 | // locked and fallback writes fail — catch here so fire-and-forget |
| 143 | // doesn't become an unhandled rejection. |
| 144 | void refreshMetricsStatus().catch(logError) |
| 145 | } |
| 146 | return { |
| 147 | enabled: cached.enabled, |
| 148 | hasError: false, |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | // First-ever run on this machine: block on the network to populate disk. |
| 153 | return refreshMetricsStatus() |
| 154 | } |
| 155 | |
| 156 | // Export for testing purposes only |
| 157 | export const _clearMetricsEnabledCacheForTesting = (): void => { |
no test coverage detected