Download src/utils/background/remote/preconditions.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 7.34 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/utils/background/remote/preconditions.ts
- Command line
-
hf download hf://chenbhao/codev/src/utils/background/remote/preconditions.ts
-
curl -L -o preconditions.ts https://huggingface.co/chenbhao/codev/resolve/main/src/utils/background/remote/preconditions.ts
7.34 kB
| import 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' } | |
| } | |