use crate::adapters::{json_top_level_keys, HealthFuture};
use crate::health::{base_health, HealthStatus};
use crate::types::{AdapterId, AuthKind, Capabilities};
use std::path::Path;
pub(super) const CAPABILITIES: Capabilities = Capabilities {
tool_use: true,
mcp: false,
hooks: false,
sessions: true,
streaming: true,
images: true,
};
pub(super) fn parse_version(stdout: &str) -> Option<String> {
crate::adapters::first_versionish_token(stdout)
}
pub(super) fn probe_auth(home: &Path) -> AuthKind {
let dir = home.join(".gemini");
let oauth_path = dir.join("oauth_creds.json");
if oauth_path.exists() {
let Some(keys) = json_top_level_keys(&oauth_path) else {
return AuthKind::Unknown;
};
let lower: Vec<String> = keys.iter().map(|k| k.to_ascii_lowercase()).collect();
if lower
.iter()
.any(|k| k.contains("refresh_token") || k.contains("access_token"))
{
return AuthKind::Subscription;
}
return AuthKind::Unknown;
}
let settings_path = dir.join("settings.json");
if settings_path.exists() {
let Some(keys) = json_top_level_keys(&settings_path) else {
return AuthKind::Unknown;
};
let lower: Vec<String> = keys.iter().map(|k| k.to_ascii_lowercase()).collect();
if lower.iter().any(|k| k.contains("api_key") || k == "apikey") {
return AuthKind::ApiKey;
}
}
if dir.is_dir() {
AuthKind::Unknown
} else {
AuthKind::Unauthenticated
}
}
pub(super) fn health_check(binary_path: &Path) -> HealthFuture {
let _ = binary_path; Box::pin(async move {
let mut h = base_health(AdapterId::Gemini);
let home = match std::env::var_os("HOME").map(std::path::PathBuf::from) {
Some(p) => p,
None => {
h.status = HealthStatus::Unknown;
h.reason = Some("HOME unset; cannot inspect ~/.gemini".to_string());
return h;
}
};
let kind = probe_auth(&home);
h.status = match kind {
AuthKind::Subscription | AuthKind::ApiKey => HealthStatus::Ready,
AuthKind::Unauthenticated => HealthStatus::NotConfigured,
AuthKind::Unknown => HealthStatus::Unknown,
};
h.reason = Some(
"gemini CLI has no headless auth-status command (running it without args \
triggers a browser OAuth flow); falling back to credential-file shape \
inspection. Ground truth requires an actual invocation."
.to_string(),
);
h
})
}