use std::sync::Arc;
use serde_json::{Value, json};
use trusty_common::console_metrics::{ConsoleMetricsReport, ServiceHealth, make_report};
use crate::core::auto_resume;
use crate::core::trusty_tools_config::{self, TrustyToolsConfig};
use crate::daemon::state::DaemonState;
use crate::supervisor::metrics::FleetMetrics;
const METRICS_SCHEMA_VERSION: u32 = 1;
const SERVICE_VERSION: &str = env!("CARGO_PKG_VERSION");
async fn fleet_snapshot(state: &Arc<DaemonState>) -> Value {
let mgr = state.session_manager().await;
let records = mgr.list().await;
let fleet = FleetMetrics::from_records(&records);
let desired = auto_resume::read_desired().unwrap_or(false);
let env = auto_resume::effective_from_env();
json!({
"fleet": fleet,
"auto_resume": {
"desired": desired,
"env": env,
"pending_restart": desired != env,
},
})
}
pub async fn console_metrics(state: &Arc<DaemonState>) -> Result<Value, String> {
let snapshot = fleet_snapshot(state).await;
let errored = snapshot
.get("fleet")
.and_then(|f| f.get("errored"))
.and_then(Value::as_u64)
.unwrap_or(0);
let status = if errored > 0 {
ServiceHealth::Degraded
} else {
ServiceHealth::Ok
};
let report: ConsoleMetricsReport = make_report(
"trusty-mpm",
"Trusty MPM",
SERVICE_VERSION,
status,
snapshot,
METRICS_SCHEMA_VERSION,
);
serde_json::to_value(&report).map_err(|e| format!("serialising console_metrics report: {e}"))
}
pub async fn supervisor_status(state: &Arc<DaemonState>) -> Result<Value, String> {
Ok(fleet_snapshot(state).await)
}
pub async fn auto_resume_set(enabled: bool) -> Result<Value, String> {
auto_resume::write_desired(enabled)
.map_err(|e| format!("persisting auto_resume desired state: {e}"))?;
let desired = enabled;
let env = auto_resume::effective_from_env();
Ok(json!({
"desired": desired,
"env": env,
"pending_restart": desired != env,
}))
}
fn config_to_json(config: &TrustyToolsConfig) -> Value {
json!({
"workspace_root_template": config.workspace_root_template,
"auto_resume": config.auto_resume,
"default_model": config.default_model,
"workspace_root": trusty_tools_config::workspace_root(config).to_string_lossy(),
})
}
pub fn config_read() -> Result<Value, String> {
Ok(config_to_json(&TrustyToolsConfig::load()))
}
pub fn config_write(
workspace_root_template: Option<&str>,
auto_resume: Option<bool>,
default_model: Option<&str>,
) -> Result<Value, String> {
let mut config = TrustyToolsConfig::load();
if let Some(t) = workspace_root_template {
config.workspace_root_template = Some(t.to_string());
}
if let Some(a) = auto_resume {
config.auto_resume = Some(a);
}
if let Some(m) = default_model {
config.default_model = Some(m.to_string());
}
trusty_common::crate_config::save(trusty_tools_config::CRATE_NAME, &config)
.map_err(|e| format!("persisting trusty-mpm config: {e}"))?;
Ok(config_to_json(&config))
}
#[cfg(test)]
mod tests {
use super::*;
fn state() -> Arc<DaemonState> {
DaemonState::shared()
}
#[tokio::test]
async fn config_read_returns_resolved_root() {
let got = config_read().expect("config_read");
assert!(
got.get("workspace_root_template").is_some(),
"must carry workspace_root_template key: {got}"
);
assert!(
got["workspace_root"].is_string()
&& !got["workspace_root"].as_str().unwrap().is_empty(),
"workspace_root must be a non-empty resolved path: {got}"
);
}
#[tokio::test]
async fn console_metrics_report_has_expected_shape() {
let report = console_metrics(&state()).await.expect("report");
assert_eq!(report["service_id"], "trusty-mpm");
assert_eq!(report["display_name"], "Trusty MPM");
assert_eq!(report["metrics_schema_version"], METRICS_SCHEMA_VERSION);
assert!(
matches!(report["status"].as_str(), Some("ok") | Some("degraded")),
"status must be ok|degraded: {report}"
);
assert!(
report["metrics"]["fleet"].is_object(),
"metrics.fleet must be an object: {report}"
);
assert!(
report["metrics"]["fleet"]["total"].is_u64(),
"metrics.fleet.total must be an integer: {report}"
);
assert!(
report["metrics"]["auto_resume"].is_object(),
"metrics.auto_resume must be an object: {report}"
);
}
#[tokio::test]
async fn supervisor_status_reports_fleet_and_auto_resume() {
let status = supervisor_status(&state()).await.expect("status");
assert!(status["fleet"]["active"].is_u64());
assert!(status["fleet"]["stopped"].is_u64());
assert!(status["fleet"]["total"].is_u64());
assert!(status["auto_resume"]["desired"].is_boolean());
assert!(status["auto_resume"]["env"].is_boolean());
assert!(status["auto_resume"]["pending_restart"].is_boolean());
}
}