use std::sync::Arc;
use axum::{Json, extract::Query, extract::State};
use serde::Deserialize;
use crate::api::error::AppError;
use crate::api::server::AppState;
#[derive(Debug, Default, Deserialize)]
pub struct HostToolsQuery {
pub names: Option<String>,
#[serde(default)]
pub rescan: bool,
}
const DEFAULT_NAMES: &[&str] = &[
"brew", "npm", "cargo", "bun", "go", "uv", "pip", "pip3", "gh", "git", "node", "rg", "fd", "resend",
];
pub(crate) async fn handle_host_tools(
state: State<Arc<AppState>>,
Query(q): Query<HostToolsQuery>,
) -> Result<Json<Vec<oxios_kernel::DetectedTool>>, AppError> {
if q.rescan {
state.kernel.host_tools.invalidate();
}
let names: Vec<String> = match q.names {
Some(n) => n
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect(),
None => DEFAULT_NAMES.iter().map(|s| s.to_string()).collect(),
};
let tools = state.kernel.host_tools.detect_many(&names).await;
Ok(Json(tools))
}
pub(crate) async fn handle_host_tools_detect(
state: State<Arc<AppState>>,
) -> Result<Json<serde_json::Value>, AppError> {
state.kernel.host_tools.invalidate();
Ok(Json(
serde_json::json!({ "status": "ok", "message": "scan cache invalidated" }),
))
}