( retryCount: number, force: boolean, )
| 1645 | } |
| 1646 | |
| 1647 | async function checkAndRefreshOAuthTokenIfNeededImpl( |
| 1648 | retryCount: number, |
| 1649 | force: boolean, |
| 1650 | ): Promise<boolean> { |
| 1651 | const MAX_RETRIES = 5 |
| 1652 | |
| 1653 | await invalidateOAuthCacheIfDiskChanged() |
| 1654 | |
| 1655 | // First check if token is expired with cached value |
| 1656 | // Skip this check if force=true (server already told us token is bad) |
| 1657 | const tokens = getClaudeAIOAuthTokens() |
| 1658 | if (!force) { |
| 1659 | if (!tokens?.refreshToken || !isOAuthTokenExpired(tokens.expiresAt)) { |
| 1660 | return false |
| 1661 | } |
| 1662 | } |
| 1663 | |
| 1664 | if (!tokens?.refreshToken) { |
| 1665 | return false |
| 1666 | } |
| 1667 | |
| 1668 | if (!shouldUseClaudeAIAuth(tokens.scopes)) { |
| 1669 | return false |
| 1670 | } |
| 1671 | |
| 1672 | // Re-read tokens async to check if they're still expired |
| 1673 | // Another process might have refreshed them |
| 1674 | getClaudeAIOAuthTokens.cache?.clear?.() |
| 1675 | clearKeychainCache() |
| 1676 | const freshTokens = await getClaudeAIOAuthTokensAsync() |
| 1677 | if ( |
| 1678 | !freshTokens?.refreshToken || |
| 1679 | (!force && !isOAuthTokenExpired(freshTokens.expiresAt)) |
| 1680 | ) { |
| 1681 | return false |
| 1682 | } |
| 1683 | |
| 1684 | // Tokens are still expired, try to acquire lock and refresh |
| 1685 | const claudeDir = getClaudeConfigHomeDir() |
| 1686 | await mkdir(claudeDir, { recursive: true }) |
| 1687 | |
| 1688 | let release |
| 1689 | try { |
| 1690 | logEvent('ncode_oauth_token_refresh_lock_acquiring', {}) |
| 1691 | release = await lockfile.lock(claudeDir) |
| 1692 | logEvent('ncode_oauth_token_refresh_lock_acquired', {}) |
| 1693 | } catch (err) { |
| 1694 | if ((err as { code?: string }).code === 'ELOCKED') { |
| 1695 | // Another process has the lock, let's retry if we haven't exceeded max retries |
| 1696 | if (retryCount < MAX_RETRIES) { |
| 1697 | logEvent('ncode_oauth_token_refresh_lock_retry', { |
| 1698 | retryCount: retryCount + 1, |
| 1699 | }) |
| 1700 | // Wait a bit before retrying |
| 1701 | await sleep(1000 + Math.random() * 1000) |
| 1702 | return checkAndRefreshOAuthTokenIfNeededImpl(retryCount + 1, force) |
| 1703 | } |
| 1704 | logEvent('ncode_oauth_token_refresh_lock_retry_limit_reached', { |
no test coverage detected