Files
agent-manager/src/commands/export_import.rs
T
bruno 8ee3ddf3db 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
2026-08-15 11:26:34 -04:00

136 lines
4.5 KiB
Rust

//! 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)
}