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,135 @@
|
||||
//! export/import: back up the configuration and installation state.
|
||||
|
||||
use super::*;
|
||||
use crate::config::{Config, Settings};
|
||||
use crate::state::StateFile;
|
||||
use crate::output::print_json;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct ExportFile {
|
||||
tool: String,
|
||||
version: String,
|
||||
generated_at: String,
|
||||
config: Config,
|
||||
state: StateFile,
|
||||
}
|
||||
|
||||
pub fn export_cmd(app: &App, output: &Path) -> Result<i32> {
|
||||
let export = ExportFile {
|
||||
tool: "agent-manager".to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
generated_at: crate::installers::now_rfc3339(),
|
||||
config: app.config.clone(),
|
||||
state: app.state.load()?,
|
||||
};
|
||||
if app.dry_run() {
|
||||
app.log
|
||||
.dry(format!("would write {}", output.display()));
|
||||
return Ok(0);
|
||||
}
|
||||
let text = serde_json::to_string_pretty(&export)?;
|
||||
std::fs::write(output, text)
|
||||
.with_context(|| format!("cannot write {}", output.display()))?;
|
||||
app.log
|
||||
.success(&format!("exported config + state to {}", output.display()));
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn import_cmd(app: &App, file: &Path) -> Result<i32> {
|
||||
let text = std::fs::read_to_string(file)
|
||||
.with_context(|| format!("cannot read {}", file.display()))?;
|
||||
let export: ExportFile = serde_json::from_str(&text)
|
||||
.with_context(|| format!("{} is not a valid agent-manager export", file.display()))?;
|
||||
if app.dry_run() {
|
||||
app.log
|
||||
.dry(format!("would import from {}", file.display()));
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// State: merge entries (existing ones win).
|
||||
let mut sf = app.state.load()?;
|
||||
let mut imported_state = 0;
|
||||
for (name, entry) in export.state.installed {
|
||||
if !sf.installed.contains_key(&name) {
|
||||
sf.installed.insert(name, entry);
|
||||
imported_state += 1;
|
||||
}
|
||||
}
|
||||
app.state.save(&sf)?;
|
||||
|
||||
// Config: write the exported agents to a file and include it.
|
||||
let dir = app
|
||||
.paths
|
||||
.config_dir
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("cannot determine the user config directory (no HOME set)"))?;
|
||||
std::fs::create_dir_all(&dir)?;
|
||||
let ts = chrono::Local::now().format("%Y%m%d-%H%M%S");
|
||||
let fname = format!("imported-{ts}.yaml");
|
||||
let dest = dir.join(&fname);
|
||||
let cfg = Config {
|
||||
version: "1.0".to_string(),
|
||||
settings: Settings::default(),
|
||||
aliases: Default::default(),
|
||||
groups: Default::default(),
|
||||
include: vec![],
|
||||
agents: export.config.agents.clone(),
|
||||
};
|
||||
std::fs::write(&dest, serde_yaml::to_string(&cfg)?)
|
||||
.with_context(|| format!("cannot write {}", dest.display()))?;
|
||||
|
||||
let cfg_path = dir.join(crate::config::CONFIG_FILE_NAME);
|
||||
if !cfg_path.exists() {
|
||||
std::fs::write(
|
||||
&cfg_path,
|
||||
format!("version: \"1.0\"\ninclude:\n - {fname}\n"),
|
||||
)?;
|
||||
} else {
|
||||
let text = std::fs::read_to_string(&cfg_path)?;
|
||||
let mut value: serde_yaml::Value = serde_yaml::from_str(&text)
|
||||
.unwrap_or_else(|_| serde_yaml::Value::Null);
|
||||
if let serde_yaml::Value::Mapping(map) = &mut value {
|
||||
let key = serde_yaml::Value::String("include".to_string());
|
||||
match map.get_mut(&key) {
|
||||
Some(serde_yaml::Value::Sequence(seq)) => {
|
||||
let entry = serde_yaml::Value::String(fname.clone());
|
||||
if !seq.contains(&entry) {
|
||||
seq.push(entry);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
map.insert(
|
||||
key,
|
||||
serde_yaml::Value::Sequence(vec![serde_yaml::Value::String(
|
||||
fname.clone(),
|
||||
)]),
|
||||
);
|
||||
}
|
||||
}
|
||||
std::fs::write(&cfg_path, serde_yaml::to_string(&value)?)?;
|
||||
}
|
||||
}
|
||||
|
||||
if app.json() {
|
||||
print_json(&serde_json::json!({
|
||||
"agents_imported": export.config.agents.len(),
|
||||
"state_entries_imported": imported_state,
|
||||
"config_file": dest.display().to_string(),
|
||||
}));
|
||||
} else {
|
||||
app.log.success(&format!(
|
||||
"imported {} agent definition(s) and {} state entrie(s) from {}",
|
||||
export.config.agents.len(),
|
||||
imported_state,
|
||||
file.display()
|
||||
));
|
||||
app.log.info(&format!(
|
||||
"definitions written to {}",
|
||||
dest.display()
|
||||
));
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
Reference in New Issue
Block a user