use std::process::ExitCode;
use anyhow::Result;
use clap::Args;
use colored::Colorize;
use serde::Serialize;
use uuid::Uuid;
use crate::commands::daemon;
use crate::socket_client::SocketClient;
use crate::theme::Theme;
use crate::value_formatter::{ValueFormat, emit_structured};
#[derive(Args, Debug, Clone)]
pub(crate) struct WhoArgs {
#[arg(long, default_value = "pretty")]
pub format: String,
}
#[derive(Debug, Clone, Serialize)]
pub(crate) struct Connection {
pub agent: String,
pub platform: String,
}
pub(crate) async fn run(args: WhoArgs) -> Result<ExitCode> {
let format = ValueFormat::parse(&args.format);
let socket_path = crate::socket_client::proxy_socket_path();
if !socket_path.exists() {
if format.is_pretty() {
println!("{}", Theme::info("No Astrid daemon is running."));
} else {
emit_structured(&Vec::<Connection>::new(), format)?;
}
return Ok(ExitCode::SUCCESS);
}
let session = astrid_core::SessionId::from_uuid(Uuid::new_v4());
let Ok(mut client) = SocketClient::connect(session).await else {
eprintln!("{}", Theme::error("Failed to connect to daemon"));
return Ok(ExitCode::from(1));
};
let req = astrid_core::kernel_api::KernelRequest::GetStatus;
let val = serde_json::to_value(req)?;
let msg = astrid_types::ipc::IpcMessage::new(
"astrid.v1.request.status",
astrid_types::ipc::IpcPayload::RawJson(val),
Uuid::nil(),
);
client.send_message(msg).await?;
let raw = client
.read_until_topic(
"astrid.v1.response.status",
std::time::Duration::from_secs(10),
)
.await?;
let status = match crate::socket_client::SocketClient::extract_kernel_response(&raw) {
Some(astrid_core::kernel_api::KernelResponse::Status(s)) => Some(s),
_ => None,
};
let connections: Vec<Connection> = match status {
Some(s) if !s.connections_by_principal.is_empty() => s
.connections_by_principal
.iter()
.flat_map(|pc| {
(0..pc.count).map(move |_| Connection {
agent: pc.principal.clone(),
platform: "cli".into(),
})
})
.collect(),
Some(s) => {
let principal = astrid_core::PrincipalId::default();
(0..s.connected_clients)
.map(|_| Connection {
agent: principal.to_string(),
platform: "cli".into(),
})
.collect()
},
None => Vec::new(),
};
if !format.is_pretty() {
emit_structured(&connections, format)?;
return Ok(ExitCode::SUCCESS);
}
if connections.is_empty() {
println!("{}", Theme::info("No clients connected."));
return Ok(ExitCode::SUCCESS);
}
println!(
"{:<24} {:<12} {}",
"AGENT".bold(),
"PLATFORM".bold(),
"STATE".bold()
);
for c in &connections {
println!("{:<24} {:<12} {}", c.agent, c.platform, "active".green());
}
println!(
"\n{}",
Theme::info(
"Per-client identity attribution (idle time, platform user) needs `admin.agent.link` IPC — tracking #657."
)
);
let _ = daemon::format_uptime;
Ok(ExitCode::SUCCESS)
}