use crate::AgentOpts;
use crate::commands::utils::{default_profile, show_profile};
use crate::namegen;
use daemonize::Daemonize;
use tracing::{error, info};
use volli_agent::{self, AgentConfig, Protocol as AgentProtocol, run as run_agent};
use volli_core::BootstrapSecret;
pub async fn run(mut profile: String, opts: AgentOpts) {
if opts.daemon {
if let Err(e) = Daemonize::new().start() {
error!("failed to daemonize: {}", e);
return;
}
}
if let Some(ref cfg_dir) = opts.config_dir {
unsafe {
std::env::set_var("VOLLI_CONFIG_DIR", cfg_dir);
}
}
let (host, port_opt) = if let Some((h, p)) = opts.connect.split_once(':') {
(h.to_string(), Some(p))
} else {
(opts.connect.clone(), None)
};
let protocol = opts.protocol.as_deref();
let mut cfg = AgentConfig::default();
if opts.join.is_some() && profile == default_profile() && volli_agent::profile_exists(&profile)
{
profile = namegen::random_profile();
println!("Using profile '{profile}'");
}
let state_path = volli_agent::agent_dir(Some(&profile)).join("agent_state");
if opts.join.is_some() && state_path.exists() && !opts.force {
println!("Profile {profile} already exists. Overwrite? [y/N]");
let mut c = String::new();
std::io::stdin().read_line(&mut c).ok();
if c.trim() != "y" {
println!("aborted");
return;
}
}
if opts.join.is_none() && !state_path.exists() {
eprintln!("profile '{profile}' not found");
return;
}
let join_provided = opts.join.is_some();
let mut secret_opt = if let Some(secret) = opts.join {
Some(secret)
} else {
volli_agent::load_state(&profile).ok().flatten()
};
let mut save_secret: Option<String> = None;
if let Some(secret) = secret_opt.take() {
let mut bs = BootstrapSecret::decode(&secret).expect("parse secret");
let mut changed = false;
if let Some(jh) = opts.join_host.clone() {
bs.host = jh;
changed = true;
} else if opts.connect != "localhost" {
bs.host = host.clone();
changed = true;
}
if let Some(p) = port_opt.and_then(|p| p.parse::<u16>().ok()) {
bs.quic_port = p;
bs.tcp_port = p;
changed = true;
}
let new_secret = bs.encode().unwrap();
if let Err(e) = volli_agent::apply_secret(&mut cfg, &profile, &new_secret) {
error!("invalid secret: {}", e);
return;
}
if changed {
save_secret = Some(new_secret);
} else {
save_secret = Some(secret);
}
} else {
cfg.host = host;
if let Some(p) = port_opt.and_then(|p| p.parse::<u16>().ok()) {
cfg.quic_port = p;
cfg.tcp_port = p;
}
}
cfg.protocol = protocol.map(|p| {
if p.eq_ignore_ascii_case("tcp") {
AgentProtocol::Tcp
} else {
AgentProtocol::Quic
}
});
info!(
"starting agent connecting to {} tcp:{} quic:{}",
cfg.host, cfg.tcp_port, cfg.quic_port
);
let save_profile = join_provided || opts.update_profile;
if join_provided {
let cb = if save_profile {
let prof = profile.clone();
let sec_opt = save_secret.clone();
Some(Box::new(move || {
if let Some(sec) = sec_opt {
volli_agent::save_state(&prof, &sec).ok();
}
if prof == default_profile() {
println!(
"Saved agent profile '{prof}'. Launch with:\n volli [--profile {prof}] agent",
);
} else {
println!(
"Saved agent profile '{prof}'. Launch with:\n volli --profile {prof} agent",
);
}
show_profile(&prof, false, true);
}) as Box<dyn FnOnce() + Send>)
} else {
None
};
if let Err(e) = run_agent(cfg, profile.clone(), cb).await {
error!("agent error: {}", e);
}
} else {
if opts.update_profile {
if let Some(sec) = save_secret {
volli_agent::save_state(&profile, &sec).ok();
}
}
if let Err(e) = run_agent(cfg, profile.clone(), None).await {
error!("agent error: {}", e);
}
}
}