Consolidate 16 separate checks into 5 (system, config, providers, tools, models). Add 3-tier formatting: default (problems-only), --status (dashboard), --verbose (deep diagnostics). Read actual loaded plugin version from opencode cache directory. Check environment variables for provider authentication.
106 lines
2.9 KiB
TypeScript
106 lines
2.9 KiB
TypeScript
export interface GhCliInfo {
|
|
installed: boolean
|
|
version: string | null
|
|
path: string | null
|
|
authenticated: boolean
|
|
username: string | null
|
|
scopes: string[]
|
|
error: string | null
|
|
}
|
|
|
|
async function checkBinaryExists(binary: string): Promise<{ exists: boolean; path: string | null }> {
|
|
try {
|
|
const binaryPath = Bun.which(binary)
|
|
return { exists: Boolean(binaryPath), path: binaryPath ?? null }
|
|
} catch {
|
|
return { exists: false, path: null }
|
|
}
|
|
}
|
|
|
|
async function getGhVersion(): Promise<string | null> {
|
|
try {
|
|
const processResult = Bun.spawn(["gh", "--version"], { stdout: "pipe", stderr: "pipe" })
|
|
const output = await new Response(processResult.stdout).text()
|
|
await processResult.exited
|
|
if (processResult.exitCode !== 0) return null
|
|
|
|
const matchedVersion = output.match(/gh version (\S+)/)
|
|
return matchedVersion?.[1] ?? output.trim().split("\n")[0] ?? null
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
async function getGhAuthStatus(): Promise<{
|
|
authenticated: boolean
|
|
username: string | null
|
|
scopes: string[]
|
|
error: string | null
|
|
}> {
|
|
try {
|
|
const processResult = Bun.spawn(["gh", "auth", "status"], {
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
env: { ...process.env, GH_NO_UPDATE_NOTIFIER: "1" },
|
|
})
|
|
|
|
const stdout = await new Response(processResult.stdout).text()
|
|
const stderr = await new Response(processResult.stderr).text()
|
|
await processResult.exited
|
|
|
|
const output = stderr || stdout
|
|
if (processResult.exitCode === 0) {
|
|
const usernameMatch = output.match(/Logged in to github\.com account (\S+)/)
|
|
const scopesMatch = output.match(/Token scopes?:\s*(.+)/i)
|
|
|
|
return {
|
|
authenticated: true,
|
|
username: usernameMatch?.[1]?.replace(/[()]/g, "") ?? null,
|
|
scopes: scopesMatch?.[1]?.split(/,\s*/).map((scope) => scope.trim()).filter(Boolean) ?? [],
|
|
error: null,
|
|
}
|
|
}
|
|
|
|
const errorMatch = output.match(/error[:\s]+(.+)/i)
|
|
return {
|
|
authenticated: false,
|
|
username: null,
|
|
scopes: [],
|
|
error: errorMatch?.[1]?.trim() ?? "Not authenticated",
|
|
}
|
|
} catch (error) {
|
|
return {
|
|
authenticated: false,
|
|
username: null,
|
|
scopes: [],
|
|
error: error instanceof Error ? error.message : "Failed to check auth status",
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function getGhCliInfo(): Promise<GhCliInfo> {
|
|
const binaryStatus = await checkBinaryExists("gh")
|
|
if (!binaryStatus.exists) {
|
|
return {
|
|
installed: false,
|
|
version: null,
|
|
path: null,
|
|
authenticated: false,
|
|
username: null,
|
|
scopes: [],
|
|
error: null,
|
|
}
|
|
}
|
|
|
|
const [version, authStatus] = await Promise.all([getGhVersion(), getGhAuthStatus()])
|
|
return {
|
|
installed: true,
|
|
version,
|
|
path: binaryStatus.path,
|
|
authenticated: authStatus.authenticated,
|
|
username: authStatus.username,
|
|
scopes: authStatus.scopes,
|
|
error: authStatus.error,
|
|
}
|
|
}
|