use crate::server::app::AppState;
use crate::server::body::read_json_object;
use crate::server::errors::error;
use crate::server::routes::query::query_value;
use crate::server::sse;
use axum::body::Bytes;
use axum::extract::State;
use axum::http::{StatusCode, Uri};
use axum::response::{IntoResponse, Response};
use axum::routing::get;
use axum::{Json, Router};
use nomoreide_core::claude_settings::{get_co_author, set_co_author};
use nomoreide_core::js_number;
use nomoreide_core::mcp_auth::{mcp_auth_statuses, AgentName};
use nomoreide_core::tool_call_store::clamp_limit;
use serde_json::{json, Value};
use std::path::PathBuf;
pub(crate) fn routes() -> Router<AppState> {
Router::new()
.route(
"/api/agent/claude-settings",
get(read_settings).post(write_settings),
)
.route("/api/agent/mcp-status", get(mcp_status))
.route("/api/agent/tool-calls", get(tool_calls))
.route("/api/agent/tool-calls/stream", get(tool_calls_stream))
}
fn home() -> PathBuf {
nomoreide_core::home::home_directory()
}
fn settings_envelope(co_author: bool) -> Response {
Json(json!({ "ok": true, "settings": { "coAuthorWithClaude": co_author } })).into_response()
}
async fn read_settings() -> Response {
match get_co_author(&home()).await {
Ok(co_author) => settings_envelope(co_author),
Err(reason) => error(StatusCode::INTERNAL_SERVER_ERROR, &reason),
}
}
async fn write_settings(body: Bytes) -> Response {
let payload = read_json_object(&body);
let Some(enabled) = payload.get("coAuthorWithClaude").and_then(Value::as_bool) else {
return error(
StatusCode::BAD_REQUEST,
"coAuthorWithClaude must be boolean",
);
};
match set_co_author(&home(), enabled).await {
Ok(co_author) => settings_envelope(co_author),
Err(reason) => error(StatusCode::INTERNAL_SERVER_ERROR, &reason),
}
}
async fn mcp_status(uri: Uri) -> Response {
let agent = AgentName::parse(query_value(&uri, "agent").as_deref());
Json(json!({ "ok": true, "statuses": mcp_auth_statuses(agent).await })).into_response()
}
async fn tool_calls(State(state): State<AppState>, uri: Uri) -> Response {
let limit = clamp_limit(js_number::parse(
&query_value(&uri, "limit").unwrap_or_default(),
));
Json(json!({ "ok": true, "records": state.tool_calls.recent(limit) })).into_response()
}
async fn tool_calls_stream(State(state): State<AppState>) -> Response {
sse::stream(
sse::RETRY_AND_PING,
state
.tool_calls
.recent(STREAM_REPLAY)
.into_iter()
.map(|record| sse::named("tool-call", record))
.collect(),
state.tool_calls.events(),
|record| Some(sse::named("tool-call", record)),
)
}
const STREAM_REPLAY: usize = 50;