Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
7.3 kB · 235 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236import axios from 'axios'import { getOauthConfig } from 'src/constants/oauth.js'import { getOrganizationUUID } from 'src/services/oauth/client.js'import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../../services/analytics/growthbook.js'import { checkAndRefreshOAuthTokenIfNeeded, getClaudeAIOAuthTokens, isClaudeAISubscriber,} from '../../auth.js'import { getCwd } from '../../cwd.js'import { logForDebugging } from '../../debug.js'import { detectCurrentRepository } from '../../detectRepository.js'import { errorMessage } from '../../errors.js'import { findGitRoot, getIsClean } from '../../git.js'import { getOAuthHeaders } from '../../teleport/api.js'import { fetchEnvironments } from '../../teleport/environments.js'
/** * Checks if user needs to log in with Claude.ai * Extracted from getTeleportErrors() in TeleportError.tsx * @returns true if login is required, false otherwise */export async function checkNeedsClaudeAiLogin(): Promise<boolean> { if (!isClaudeAISubscriber()) { return false } return checkAndRefreshOAuthTokenIfNeeded()}
/** * Checks if git working directory is clean (no uncommitted changes) * Ignores untracked files since they won't be lost during branch switching * Extracted from getTeleportErrors() in TeleportError.tsx * @returns true if git is clean, false otherwise */export async function checkIsGitClean(): Promise<boolean> { const isClean = await getIsClean({ ignoreUntracked: true }) return isClean}
/** * Checks if user has access to at least one remote environment * @returns true if user has remote environments, false otherwise */export async function checkHasRemoteEnvironment(): Promise<boolean> { try { const environments = await fetchEnvironments() return environments.length > 0 } catch (error) { logForDebugging(`checkHasRemoteEnvironment failed: ${errorMessage(error)}`) return false }}
/** * Checks if current directory is inside a git repository (has .git/). * Distinct from checkHasGitRemote — a local-only repo passes this but not that. */export function checkIsInGitRepo(): boolean { return findGitRoot(getCwd()) !== null}
/** * Checks if current repository has a GitHub remote configured. * Returns false for local-only repos (git init with no `origin`). */export async function checkHasGitRemote(): Promise<boolean> { const repository = await detectCurrentRepository() return repository !== null}
/** * Checks if GitHub app is installed on a specific repository * @param owner The repository owner (e.g., "anthropics") * @param repo The repository name (e.g., "claude-cli-internal") * @returns true if GitHub app is installed, false otherwise */export async function checkGithubAppInstalled( owner: string, repo: string, signal?: AbortSignal,): Promise<boolean> { try { const accessToken = getClaudeAIOAuthTokens()?.accessToken if (!accessToken) { logForDebugging( 'checkGithubAppInstalled: No access token found, assuming app not installed', ) return false }
const orgUUID = await getOrganizationUUID() if (!orgUUID) { logForDebugging( 'checkGithubAppInstalled: No org UUID found, assuming app not installed', ) return false }
const url = `${getOauthConfig().BASE_API_URL}/api/oauth/organizations/${orgUUID}/code/repos/${owner}/${repo}` const headers = { ...getOAuthHeaders(accessToken), 'x-organization-uuid': orgUUID, }
logForDebugging(`Checking GitHub app installation for ${owner}/${repo}`)
const response = await axios.get<{ repo: { name: string owner: { login: string } default_branch: string } status: { app_installed: boolean relay_enabled: boolean } | null }>(url, { headers, timeout: 15000, signal, })
if (response.status === 200) { if (response.data.status) { const installed = response.data.status.app_installed logForDebugging( `GitHub app ${installed ? 'is' : 'is not'} installed on ${owner}/${repo}`, ) return installed } // status is null - app is not installed on this repo logForDebugging( `GitHub app is not installed on ${owner}/${repo} (status is null)`, ) return false }
logForDebugging( `checkGithubAppInstalled: Unexpected response status ${response.status}`, ) return false } catch (error) { // 4XX errors typically mean app is not installed or repo not accessible if (axios.isAxiosError(error)) { const status = error.response?.status if (status && status >= 400 && status < 500) { logForDebugging( `checkGithubAppInstalled: Got ${status} error, app likely not installed on ${owner}/${repo}`, ) return false } }
logForDebugging(`checkGithubAppInstalled error: ${errorMessage(error)}`) return false }}
/** * Checks if the user has synced their GitHub credentials via /web-setup * @returns true if GitHub token is synced, false otherwise */export async function checkGithubTokenSynced(): Promise<boolean> { try { const accessToken = getClaudeAIOAuthTokens()?.accessToken if (!accessToken) { logForDebugging('checkGithubTokenSynced: No access token found') return false }
const orgUUID = await getOrganizationUUID() if (!orgUUID) { logForDebugging('checkGithubTokenSynced: No org UUID found') return false }
const url = `${getOauthConfig().BASE_API_URL}/api/oauth/organizations/${orgUUID}/sync/github/auth` const headers = { ...getOAuthHeaders(accessToken), 'x-organization-uuid': orgUUID, }
logForDebugging('Checking if GitHub token is synced via web-setup')
const response = await axios.get(url, { headers, timeout: 15000, })
const synced = response.status === 200 && response.data?.is_authenticated === true logForDebugging( `GitHub token synced: ${synced} (status=${response.status}, data=${JSON.stringify(response.data)})`, ) return synced } catch (error) { if (axios.isAxiosError(error)) { const status = error.response?.status if (status && status >= 400 && status < 500) { logForDebugging( `checkGithubTokenSynced: Got ${status}, token not synced`, ) return false } }
logForDebugging(`checkGithubTokenSynced error: ${errorMessage(error)}`) return false }}
type RepoAccessMethod = 'github-app' | 'token-sync' | 'none'
/** * Tiered check for whether a GitHub repo is accessible for remote operations. * 1. GitHub App installed on the repo * 2. GitHub token synced via /web-setup * 3. Neither — caller should prompt user to set up access */export async function checkRepoForRemoteAccess( owner: string, repo: string,): Promise<{ hasAccess: boolean; method: RepoAccessMethod }> { if (await checkGithubAppInstalled(owner, repo)) { return { hasAccess: true, method: 'github-app' } } if ( getFeatureValue_CACHED_MAY_BE_STALE('tengu_cobalt_lantern', false) && (await checkGithubTokenSynced()) ) { return { hasAccess: true, method: 'token-sync' } } return { hasAccess: false, method: 'none' }}