use super::{credential, resource, state, supervisor};
use anyhow::Result;
use serde::Serialize;
use std::path::Path;
#[derive(Debug, Serialize)]
pub struct HostSection {
pub os: String,
pub arch: String,
pub cpu_logical_cores: usize,
pub cpu_physical_cores: Option<usize>,
pub memory_total_bytes: Option<u64>,
pub memory_model: super::platform::profile::MemoryModelKind,
pub accelerators: Vec<resource::topology::AcceleratorTopology>,
pub current_pressure: resource::pressure::ResourcePressure,
}
#[derive(Debug, Serialize)]
pub struct YanaSection {
pub runtime_version: String,
pub state_schema_version: u32,
pub managed_agents: usize,
pub running_agents: usize,
pub resource_policy_configured: bool,
pub providers: Vec<credential::CredentialStatus>,
pub active_reservations: Vec<resource::reservation::Reservation>,
pub autonomy_ready: usize,
pub autonomy_waiting_approval: usize,
pub cost_today_usd: Option<f64>,
}
#[derive(Debug, Serialize)]
pub struct SafetySection {
pub mode: String,
pub halt_reason: Option<String>,
pub quarantine: Option<supervisor::QuarantineRecord>,
pub heartbeat_healthy: bool,
pub heartbeat_age_secs: Option<i64>,
pub receipt_chain_valid: bool,
pub receipt_count: usize,
pub periodic_scheduler: super::monitor_service::ServiceReport,
pub resident_service: super::service::manager::ServiceStatus,
}
#[derive(Debug, Serialize)]
pub struct UnifiedStatus {
pub host: HostSection,
pub yana: YanaSection,
pub safety: SafetySection,
pub host_capabilities: Option<super::platform::capabilities::PlatformCapabilities>,
}
pub fn aggregate(root: &Path) -> Result<UnifiedStatus> {
let dashboard = supervisor::dashboard(root)?;
let topology = resource::topology::collect()?;
let current_state = state::load(root)?;
let reservations = resource::reservation::list(root)?;
let cost_today_usd = crate::cost::daily_cost_usd(root, chrono::Utc::now()).ok();
let queue = super::autonomy::load_queue(root)?;
let autonomy_ready = queue
.actions
.iter()
.filter(|action| action.status == super::autonomy::ActionStatus::Ready)
.count();
let autonomy_waiting_approval = queue
.actions
.iter()
.filter(|action| action.status == super::autonomy::ActionStatus::WaitingApproval)
.count();
Ok(UnifiedStatus {
host: HostSection {
os: topology.os,
arch: topology.arch,
cpu_logical_cores: topology.cpu_logical_cores,
cpu_physical_cores: topology.cpu_physical_cores,
memory_total_bytes: topology.memory_total_bytes,
memory_model: topology.memory_model,
accelerators: topology.accelerators,
current_pressure: dashboard.resource_pressure,
},
yana: YanaSection {
runtime_version: env!("CARGO_PKG_VERSION").to_string(),
state_schema_version: current_state.schema_version,
managed_agents: dashboard.managed_agents,
running_agents: current_state
.agents
.iter()
.filter(|agent| agent.status == state::AgentStatus::Running)
.count(),
resource_policy_configured: current_state.resource_policy.is_some(),
providers: credential::inventory(),
active_reservations: reservations,
autonomy_ready,
autonomy_waiting_approval,
cost_today_usd,
},
safety: SafetySection {
mode: dashboard.mode,
halt_reason: dashboard.halt_reason,
quarantine: dashboard.quarantine,
heartbeat_healthy: dashboard.heartbeat_healthy,
heartbeat_age_secs: dashboard.heartbeat_age_secs,
receipt_chain_valid: dashboard.receipt_chain_valid,
receipt_count: dashboard.receipt_count,
periodic_scheduler: dashboard.periodic_scheduler,
resident_service: dashboard.resident_service,
},
host_capabilities: dashboard.host_capabilities,
})
}
pub fn print(status: &UnifiedStatus, json: bool) -> Result<()> {
if json {
println!("{}", serde_json::to_string_pretty(status)?);
return Ok(());
}
println!("Yana OS — unified status");
println!("{}", "─".repeat(76));
println!("HOST");
println!(
" os/arch {}/{}",
status.host.os, status.host.arch
);
println!(
" cpu {} logical, {} physical",
status.host.cpu_logical_cores,
status
.host
.cpu_physical_cores
.map_or_else(|| "unknown".into(), |v| v.to_string())
);
println!(
" memory {}",
status.host.memory_total_bytes.map_or_else(
|| "unknown".into(),
|v| format!("{:.1} GiB", v as f64 / 1024f64.powi(3))
)
);
println!(" memory model {:?}", status.host.memory_model);
println!(" accelerators {}", status.host.accelerators.len());
println!(
" pressure {:?}",
status.host.current_pressure.overall
);
println!("YANA");
println!(" runtime version {}", status.yana.runtime_version);
println!(" state schema {}", status.yana.state_schema_version);
println!(
" agents {} managed, {} running",
status.yana.managed_agents, status.yana.running_agents
);
println!(
" resource policy {}",
if status.yana.resource_policy_configured {
"configured"
} else {
"UNSET (preflight denies)"
}
);
println!(
" autonomy queue {} ready, {} awaiting approval",
status.yana.autonomy_ready, status.yana.autonomy_waiting_approval
);
println!(
" cost today {}",
status
.yana
.cost_today_usd
.map_or_else(|| "unknown".into(), |v| format!("${v:.6}"))
);
println!(
" reservations {}",
status.yana.active_reservations.len()
);
println!("SAFETY");
println!(" mode {}", status.safety.mode);
println!(
" heartbeat {}",
if status.safety.heartbeat_healthy {
"healthy"
} else {
"unhealthy or unknown"
}
);
println!(
" receipt chain {}",
if status.safety.receipt_chain_valid {
"valid"
} else {
"INVALID"
}
);
println!("HOST CAPABILITIES");
match &status.host_capabilities {
Some(capabilities) => println!(" {capabilities:?}"),
None => println!(" unknown (platform backend call failed)"),
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn root() -> tempfile::TempDir {
let root = tempfile::tempdir().unwrap();
let marker = root.path().join(yana_rt::flock_v1::PROTOCOL_FILE);
std::fs::create_dir_all(marker.parent().unwrap()).unwrap();
std::fs::write(marker, yana_rt::flock_v1::PROTOCOL_VERSION).unwrap();
state::initialize(root.path()).unwrap();
root
}
#[test]
fn aggregate_composes_all_four_sections_without_fabricating_data() {
let root = root();
let status = aggregate(root.path()).unwrap();
assert!(status.host.cpu_logical_cores >= 1);
assert_eq!(status.yana.state_schema_version, 1);
assert_eq!(status.yana.managed_agents, 0);
assert_eq!(status.safety.mode, "normal");
assert!(status.safety.halt_reason.is_none());
}
#[test]
fn cost_today_is_a_confirmed_zero_not_an_unknown_when_no_ledger_exists_yet() {
let root = root();
let status = aggregate(root.path()).unwrap();
assert_eq!(status.yana.cost_today_usd, Some(0.0));
}
#[test]
fn print_does_not_panic_in_either_mode() {
let root = root();
let status = aggregate(root.path()).unwrap();
print(&status, true).unwrap();
print(&status, false).unwrap();
}
}