agent-manager v0.2.0 : gestionnaire d'agents IA locaux
- CLI Rust (am) : list/install/uninstall/update/start/stop/restart/run/status/search/info/config/doctor/completion/export/import/self-update + shell interactif (Tab, historique) - 9 methodes d'installation (npm, pip, uv, cargo, go, bun, curl, binary, git) - catalogue de 72 agents, dependances, processus, securite (dry-run, checksums) - sondes de version paralleles + cache (am list ~25 ms a chaud) - installateurs one-liner : install.sh, install.ps1
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
//! status: detailed state of one agent, or of every installed agent.
|
||||
|
||||
use super::*;
|
||||
use crate::output::{print_json, status_style, Table};
|
||||
use crate::probe::ExternalInfo;
|
||||
use crate::state::InstalledEntry;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct StatusDetail {
|
||||
name: String,
|
||||
display_name: String,
|
||||
status: String,
|
||||
version: Option<String>,
|
||||
method: Option<String>,
|
||||
path: Option<String>,
|
||||
run: Option<String>,
|
||||
pid: Option<u32>,
|
||||
started_at: Option<String>,
|
||||
installed_at: Option<String>,
|
||||
log: Option<String>,
|
||||
managed: bool,
|
||||
}
|
||||
|
||||
pub fn run(app: &App, agent_name: Option<&str>) -> Result<i32> {
|
||||
let probe = crate::probe::detect_externals(app);
|
||||
match agent_name {
|
||||
Some(name) => status_one(app, name, &probe),
|
||||
None => status_all(app, &probe),
|
||||
}
|
||||
}
|
||||
|
||||
fn status_one(app: &App, name: &str, probe: &ExternalInfo) -> Result<i32> {
|
||||
let agent = require_agent(app, name)?;
|
||||
let entry: Option<InstalledEntry> = app.state.get(&agent.name).ok().flatten();
|
||||
let detail = detail_for(app, agent, entry.as_ref(), probe);
|
||||
if app.json() {
|
||||
print_json(&detail);
|
||||
return Ok(0);
|
||||
}
|
||||
let color = app.color();
|
||||
println!("{} ({})", agent.title(), agent.name);
|
||||
let status_line = status_style(&detail.status, color);
|
||||
println!(" status: {status_line}");
|
||||
println!(
|
||||
" version: {}",
|
||||
detail.version.as_deref().unwrap_or("unknown")
|
||||
);
|
||||
println!(" method: {}", detail.method.as_deref().unwrap_or("—"));
|
||||
println!(" path: {}", detail.path.as_deref().unwrap_or("—"));
|
||||
println!(" run: {}", detail.run.as_deref().unwrap_or("—"));
|
||||
if let Some(pid) = detail.pid {
|
||||
println!(" pid: {pid}");
|
||||
}
|
||||
if let Some(t) = &detail.started_at {
|
||||
println!(" started: {t}");
|
||||
}
|
||||
if let Some(t) = &detail.installed_at {
|
||||
println!(" installed: {t}");
|
||||
}
|
||||
if let Some(l) = &detail.log {
|
||||
println!(" log: {l}");
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn status_all(app: &App, probe: &ExternalInfo) -> Result<i32> {
|
||||
let entries = app.state.all().unwrap_or_default();
|
||||
let mut rows: Vec<AgentRow> = Vec::new();
|
||||
for agent in app.catalog.agents() {
|
||||
if agent.hidden {
|
||||
continue;
|
||||
}
|
||||
let entry = entries.get(&agent.name);
|
||||
let d = detail_for(app, agent, entry, probe);
|
||||
if d.status == "running" || d.status == "installed" || d.status == "external" {
|
||||
rows.push(AgentRow {
|
||||
name: agent.name.clone(),
|
||||
display_name: agent.title().to_string(),
|
||||
status: d.status,
|
||||
version: d.version,
|
||||
pid: d.pid,
|
||||
path: d.path,
|
||||
});
|
||||
}
|
||||
}
|
||||
rows.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
if app.json() {
|
||||
print_json(&rows);
|
||||
return Ok(0);
|
||||
}
|
||||
let color = app.color();
|
||||
let mut table = Table::new(vec!["NAME", "VERSION", "STATUS", "PID", "PATH"]);
|
||||
for r in &rows {
|
||||
table.row(vec![
|
||||
r.display_name.clone(),
|
||||
r.version.clone().unwrap_or_else(|| "—".to_string()),
|
||||
status_style(&r.status, color),
|
||||
r.pid.map(|p| p.to_string()).unwrap_or_else(|| "—".to_string()),
|
||||
r.path.clone().unwrap_or_else(|| "—".to_string()),
|
||||
]);
|
||||
}
|
||||
print!("{}", table.render());
|
||||
if rows.is_empty() {
|
||||
app.log.info("no installed or running agents");
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AgentRow {
|
||||
name: String,
|
||||
display_name: String,
|
||||
status: String,
|
||||
version: Option<String>,
|
||||
pid: Option<u32>,
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
fn detail_for(
|
||||
app: &App,
|
||||
agent: &AgentDef,
|
||||
entry: Option<&InstalledEntry>,
|
||||
probe: &ExternalInfo,
|
||||
) -> StatusDetail {
|
||||
match entry {
|
||||
Some(e) => {
|
||||
let running = e.pid.map(crate::process::is_running).unwrap_or(false);
|
||||
let status = if running { "running" } else { "installed" };
|
||||
let path = e
|
||||
.bins
|
||||
.first()
|
||||
.filter(|b| std::path::Path::new(b).exists())
|
||||
.cloned()
|
||||
.or_else(|| Some(e.install_dir.clone()));
|
||||
StatusDetail {
|
||||
name: agent.name.clone(),
|
||||
display_name: agent.title().to_string(),
|
||||
status: status.to_string(),
|
||||
version: e.version.clone(),
|
||||
method: Some(e.method.clone()),
|
||||
path,
|
||||
run: Some(e.run.clone()),
|
||||
pid: if running { e.pid } else { None },
|
||||
started_at: if running { e.started_at.clone() } else { None },
|
||||
installed_at: Some(e.installed_at.clone()),
|
||||
log: Some(crate::process::agent_log_path(app, &agent.name).display().to_string()),
|
||||
managed: true,
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let external = probe.path_of(&agent.name).cloned();
|
||||
let version = external
|
||||
.as_ref()
|
||||
.and_then(|_p| probe.version_of(&agent.name).cloned().flatten())
|
||||
.or_else(|| agent.version.clone());
|
||||
StatusDetail {
|
||||
name: agent.name.clone(),
|
||||
display_name: agent.title().to_string(),
|
||||
status: if external.is_some() {
|
||||
"external".to_string()
|
||||
} else if agent.installable {
|
||||
"not-installed".to_string()
|
||||
} else {
|
||||
"not-installable".to_string()
|
||||
},
|
||||
version,
|
||||
method: None,
|
||||
path: external.map(|p| p.display().to_string()),
|
||||
run: agent.run.clone(),
|
||||
pid: None,
|
||||
started_at: None,
|
||||
installed_at: None,
|
||||
log: None,
|
||||
managed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user