apm-cli 0.1.17

CLI project manager for running AI coding agents in parallel, isolated by design.
Documentation
use anyhow::Result;
use std::path::Path;

#[derive(serde::Deserialize)]
struct SessionInfo {
    username: String,
    device_hint: Option<String>,
    last_seen: chrono::DateTime<chrono::Utc>,
    expires_at: chrono::DateTime<chrono::Utc>,
}

pub fn run(root: &Path) -> Result<()> {
    let config = apm_core::config::Config::load(root)?;
    let url = format!("{}/api/auth/sessions", config.server.url);
    let client = reqwest::blocking::Client::new();
    let resp = client
        .get(&url)
        .send()
        .map_err(|e| anyhow::anyhow!("error: cannot connect to apm-server: {e}"))?;
    if !resp.status().is_success() {
        let status = resp.status();
        eprintln!("error: server returned {status}");
        std::process::exit(1);
    }
    let sessions: Vec<SessionInfo> = resp
        .json()
        .map_err(|e| anyhow::anyhow!("error: invalid response from server: {e}"))?;
    if sessions.is_empty() {
        println!("No active sessions.");
        return Ok(());
    }
    let col_user = sessions.iter().map(|s| s.username.len()).max().unwrap_or(0).max(8);
    let col_device = sessions
        .iter()
        .map(|s| s.device_hint.as_deref().unwrap_or("-").len())
        .max()
        .unwrap_or(0)
        .max(6);
    println!(
        "{:<col_user$}  {:<col_device$}  {:<20}  {}",
        "USERNAME", "DEVICE", "LAST SEEN", "EXPIRES"
    );
    for s in &sessions {
        let device = s.device_hint.as_deref().unwrap_or("-");
        let last_seen = s.last_seen.format("%Y-%m-%d %H:%M UTC").to_string();
        let expires = s.expires_at.format("%Y-%m-%d %H:%M UTC").to_string();
        println!(
            "{:<col_user$}  {:<col_device$}  {:<20}  {}",
            s.username, device, last_seen, expires
        );
    }
    Ok(())
}