feat: add update command to CLI, relay, and client

This commit is contained in:
Helios Agent 2026-03-06 12:16:10 +01:00
parent 835d20f734
commit 6345209538
No known key found for this signature in database
GPG key ID: C8259547CD8309B5
9 changed files with 302 additions and 0 deletions

View file

@ -11,6 +11,8 @@ use tokio_tungstenite::{connect_async_tls_with_config, tungstenite::Message, Con
use base64::Engine;
use helios_common::{ClientMessage, ServerMessage};
#[allow(unused_imports)]
use reqwest;
use helios_common::protocol::{is_valid_label, sanitize_label};
mod display;
@ -661,6 +663,65 @@ async fn handle_message(
}
}
ServerMessage::UpdateRequest { request_id } => {
display::cmd_start("🔄", "update", "downloading...");
let exe = std::env::current_exe().ok();
tokio::spawn(async move {
// Give the response time to be sent before we restart
tokio::time::sleep(tokio::time::Duration::from_millis(800)).await;
let exe = match exe {
Some(p) => p,
None => {
display::err("", "update: could not determine current exe path");
return;
}
};
let url = "https://agent-helios.me/downloads/helios-remote/helios-remote-client-windows.exe";
let bytes = match reqwest::get(url).await {
Ok(r) => match r.bytes().await {
Ok(b) => b,
Err(e) => {
display::err("", &format!("update: read body failed: {e}"));
return;
}
},
Err(e) => {
display::err("", &format!("update: download failed: {e}"));
return;
}
};
// Write new binary to a temp path, then swap
let tmp = exe.with_extension("update.exe");
if let Err(e) = std::fs::write(&tmp, &bytes) {
display::err("", &format!("update: write failed: {e}"));
return;
}
// Rename current → .old, then tmp → current
let old = exe.with_extension("old.exe");
let _ = std::fs::remove_file(&old);
if let Err(e) = std::fs::rename(&exe, &old) {
display::err("", &format!("update: rename old failed: {e}"));
return;
}
if let Err(e) = std::fs::rename(&tmp, &exe) {
// Attempt rollback
let _ = std::fs::rename(&old, &exe);
display::err("", &format!("update: rename new failed: {e}"));
return;
}
display::cmd_done("🔄", "update", "", true, "updated — restarting");
// Restart
let _ = std::process::Command::new(&exe).spawn();
std::process::exit(0);
});
display::cmd_done("🔄", "update", "", true, "triggered");
ClientMessage::UpdateResponse {
request_id,
success: true,
message: "update triggered, client restarting...".into(),
}
}
ServerMessage::Ack { request_id } => {
ClientMessage::Ack { request_id }
}