( retryCount: number, force: boolean, )
| 1441 | } |
| 1442 | |
| 1443 | async function checkAndRefreshOAuthTokenIfNeededImpl( |
| 1444 | retryCount: number, |
| 1445 | force: boolean, |
| 1446 | ): Promise<boolean> { |
| 1447 | const MAX_RETRIES = 5 |
| 1448 | |
| 1449 | await invalidateOAuthCacheIfDiskChanged() |
| 1450 | |
| 1451 | // First check if token is expired with cached value |
| 1452 | // Skip this check if force=true (server already told us token is bad) |
| 1453 | const tokens = getClaudeAIOAuthTokens() |
| 1454 | if (!force) { |
| 1455 | if (!tokens?.refreshToken || !isOAuthTokenExpired(tokens.expiresAt)) { |
| 1456 | return false |
| 1457 | } |
| 1458 | } |
| 1459 | |
| 1460 | if (!tokens?.refreshToken) { |
| 1461 | return false |
| 1462 | } |
| 1463 | |
| 1464 | if (!shouldUseClaudeAIAuth(tokens.scopes)) { |
| 1465 | return false |
| 1466 | } |
| 1467 | |
| 1468 | // Re-read tokens async to check if they're still expired |
| 1469 | // Another process might have refreshed them |
| 1470 | getClaudeAIOAuthTokens.cache?.clear?.() |
| 1471 | clearKeychainCache() |
| 1472 | const freshTokens = await getClaudeAIOAuthTokensAsync() |
| 1473 | if ( |
| 1474 | !freshTokens?.refreshToken || |
| 1475 | !isOAuthTokenExpired(freshTokens.expiresAt) |
| 1476 | ) { |
| 1477 | return false |
| 1478 | } |
| 1479 | |
| 1480 | // Tokens are still expired, try to acquire lock and refresh |
| 1481 | const claudeDir = getClaudeConfigHomeDir() |
| 1482 | await mkdir(claudeDir, { recursive: true }) |
| 1483 | |
| 1484 | let release |
| 1485 | try { |
| 1486 | logEvent('tengu_oauth_token_refresh_lock_acquiring', {}) |
| 1487 | release = await lockfile.lock(claudeDir) |
| 1488 | logEvent('tengu_oauth_token_refresh_lock_acquired', {}) |
| 1489 | } catch (err) { |
| 1490 | if ((err as { code?: string }).code === 'ELOCKED') { |
| 1491 | // Another process has the lock, let's retry if we haven't exceeded max retries |
| 1492 | if (retryCount < MAX_RETRIES) { |
| 1493 | logEvent('tengu_oauth_token_refresh_lock_retry', { |
| 1494 | retryCount: retryCount + 1, |
| 1495 | }) |
| 1496 | // Wait a bit before retrying |
| 1497 | await sleep(1000 + Math.random() * 1000) |
| 1498 | return checkAndRefreshOAuthTokenIfNeededImpl(retryCount + 1, force) |
| 1499 | } |
| 1500 | logEvent('tengu_oauth_token_refresh_lock_retry_limit_reached', { |
no test coverage detected