From a3100a872b8e715e1836aeaa39561dc19c4b2fac Mon Sep 17 00:00:00 2001 From: Helios Agent Date: Fri, 6 Mar 2026 14:30:01 +0100 Subject: [PATCH 01/10] fix: delete .old.exe on startup + use cmd start for new window --- crates/client/src/main.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/client/src/main.rs b/crates/client/src/main.rs index 1a07545..58ceda2 100644 --- a/crates/client/src/main.rs +++ b/crates/client/src/main.rs @@ -221,6 +221,13 @@ async fn main() { banner(); + // Clean up leftover .old.exe from previous self-update (Windows can't delete running exe) + #[cfg(target_os = "windows")] + if let Ok(exe) = std::env::current_exe() { + let old = exe.with_extension("old.exe"); + let _ = std::fs::remove_file(&old); + } + // Single instance check if !acquire_instance_lock() { display::err("❌", "Another instance of helios-remote is already running."); @@ -716,9 +723,11 @@ async fn handle_message( let args: Vec = std::env::args().skip(1).collect(); #[cfg(target_os = "windows")] { - use std::os::windows::process::CommandExt; - const CREATE_NEW_CONSOLE: u32 = 0x00000010; - let _ = std::process::Command::new(&exe).args(&args).creation_flags(CREATE_NEW_CONSOLE).spawn(); + // Use "start" to open a new visible console window + let exe_str = exe.to_string_lossy(); + let _ = std::process::Command::new("cmd") + .args(["/c", "start", "", &exe_str]) + .spawn(); } #[cfg(not(target_os = "windows"))] let _ = std::process::Command::new(&exe).args(&args).spawn(); From bd9b92c861f2ddae6e1b96a70db3657ddfde4cb5 Mon Sep 17 00:00:00 2001 From: Helios Agent Date: Fri, 6 Mar 2026 14:30:49 +0100 Subject: [PATCH 02/10] fix: release instance lock before spawning updated client --- crates/client/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/client/src/main.rs b/crates/client/src/main.rs index 58ceda2..fdd6afa 100644 --- a/crates/client/src/main.rs +++ b/crates/client/src/main.rs @@ -719,6 +719,8 @@ async fn handle_message( display::cmd_done("🔄", "update", "", true, "updated — restarting"); // Delete old binary let _ = std::fs::remove_file(&old); + // Release single-instance lock so new process can start + release_instance_lock(); // Restart with same args (new console window on Windows) let args: Vec = std::env::args().skip(1).collect(); #[cfg(target_os = "windows")] From e15834c179a86cf62f6269d76ed6dfb41ff1b5b3 Mon Sep 17 00:00:00 2001 From: Helios Agent Date: Fri, 6 Mar 2026 14:59:58 +0100 Subject: [PATCH 03/10] fix: CI no longer auto-restarts relay; update only via CLI --- .github/workflows/ci.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38eee93..bd8f473 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,17 +120,10 @@ jobs: echo "$VPS_SSH_KEY" > ~/.ssh/deploy_key chmod 600 ~/.ssh/deploy_key ssh-keyscan -H 46.225.185.232 >> ~/.ssh/known_hosts - scp -i ~/.ssh/deploy_key \ - target/x86_64-unknown-linux-gnu/release/helios-remote-relay \ - root@46.225.185.232:/opt/helios-remote/target/release/helios-remote-relay-new - # Also publish relay binary to download URL for self-update + # Only publish to download URL — relay updates itself when triggered by CLI scp -i ~/.ssh/deploy_key \ target/x86_64-unknown-linux-gnu/release/helios-remote-relay \ root@46.225.185.232:/var/www/helios-remote/helios-remote-relay-linux - ssh -i ~/.ssh/deploy_key root@46.225.185.232 \ - "mv /opt/helios-remote/target/release/helios-remote-relay-new \ - /opt/helios-remote/target/release/helios-remote-relay && \ - systemctl restart helios-remote" build-cli: runs-on: ubuntu-latest From 6224c9a1e0219a3157c793307cc3aa2022af694f Mon Sep 17 00:00:00 2001 From: Helios Agent Date: Fri, 6 Mar 2026 15:06:20 +0100 Subject: [PATCH 04/10] feat: build aarch64 CLI + CLI self-update on all platforms --- .github/workflows/ci.yml | 12 ++++++++++-- crates/cli/src/main.rs | 9 +++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd8f473..5661813 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,10 +135,10 @@ jobs: - name: Install Rust (stable) + targets uses: dtolnay/rust-toolchain@stable with: - targets: x86_64-unknown-linux-gnu,x86_64-pc-windows-gnu + targets: x86_64-unknown-linux-gnu,x86_64-pc-windows-gnu,aarch64-unknown-linux-gnu - name: Install cross-compilers - run: sudo apt-get update && sudo apt-get install -y gcc-x86-64-linux-gnu gcc-mingw-w64-x86-64 mingw-w64-tools + run: sudo apt-get update && sudo apt-get install -y gcc-x86-64-linux-gnu gcc-mingw-w64-x86-64 mingw-w64-tools gcc-aarch64-linux-gnu - name: Cache dependencies uses: Swatinem/rust-cache@v2 @@ -150,6 +150,11 @@ jobs: env: CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER: x86_64-linux-gnu-gcc + - name: Build CLI (Linux aarch64) + run: cargo build --release --package helios-remote-cli --target aarch64-unknown-linux-gnu + env: + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc + - name: Build CLI (Windows x86_64) run: cargo build --release --package helios-remote-cli --target x86_64-pc-windows-gnu env: @@ -181,6 +186,9 @@ jobs: scp -i ~/.ssh/deploy_key \ target/x86_64-unknown-linux-gnu/release/helios-remote-cli \ root@46.225.185.232:/var/www/helios-remote/helios-remote-cli-linux + scp -i ~/.ssh/deploy_key \ + target/aarch64-unknown-linux-gnu/release/helios-remote-cli \ + root@46.225.185.232:/var/www/helios-remote/helios-remote-cli-linux-aarch64 scp -i ~/.ssh/deploy_key \ target/x86_64-pc-windows-gnu/release/helios-remote-cli.exe \ root@46.225.185.232:/var/www/helios-remote/helios-remote-cli-windows.exe diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 51f6361..5da1872 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -782,16 +782,13 @@ fn main() { // Self-update CLI if needed // (relay_commit is the "canonical" latest — if we differ from it, we're outdated) // Skip on non-x86_64 Linux (e.g. ARM/Pi) — CI only builds x86_64 Linux binaries - #[cfg(all(not(target_os = "windows"), not(target_arch = "x86_64")))] - if cli_commit != relay_commit { - println!(" → Skipping CLI update (non-x86_64, update manually)"); - } - #[cfg(any(target_os = "windows", target_arch = "x86_64"))] if cli_commit != relay_commit { println!(" → Updating CLI..."); #[cfg(target_os = "windows")] let url = "https://agent-helios.me/downloads/helios-remote/helios-remote-cli-windows.exe"; - #[cfg(not(target_os = "windows"))] + #[cfg(all(not(target_os = "windows"), target_arch = "aarch64"))] + let url = "https://agent-helios.me/downloads/helios-remote/helios-remote-cli-linux-aarch64"; + #[cfg(all(not(target_os = "windows"), not(target_arch = "aarch64")))] let url = "https://agent-helios.me/downloads/helios-remote/helios-remote-cli-linux"; let bytes = match reqwest::blocking::get(url) { From 840cecfce563d0550debb8ea211bfca00a325437 Mon Sep 17 00:00:00 2001 From: Helios Agent Date: Fri, 6 Mar 2026 15:10:17 +0100 Subject: [PATCH 05/10] fix: use rustls for CLI to fix aarch64 cross-compile --- crates/cli/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 8cf2bef..94de807 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -9,7 +9,7 @@ path = "src/main.rs" [dependencies] clap = { version = "4", features = ["derive"] } -reqwest = { version = "0.12", features = ["blocking", "json"] } +reqwest = { version = "0.12", features = ["blocking", "json", "rustls-tls"], default-features = false } base64 = "0.22" serde = { version = "1", features = ["derive"] } serde_json = "1" From e9bbbb817111a77482309672b87175d91bb1e295 Mon Sep 17 00:00:00 2001 From: Helios Agent Date: Fri, 6 Mar 2026 15:17:07 +0100 Subject: [PATCH 06/10] chore: final update test --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fce9145..05eb434 100644 --- a/README.md +++ b/README.md @@ -102,3 +102,4 @@ The relay server (`helios-remote-relay`) runs on the VPS and is not distributed. MIT + From bd1835f5a3398ef238724a5a4339d25d907496ea Mon Sep 17 00:00:00 2001 From: Helios Agent Date: Fri, 6 Mar 2026 15:20:26 +0100 Subject: [PATCH 07/10] feat: check version.json for latest available commit --- .github/workflows/ci.yml | 4 ++++ crates/cli/src/main.rs | 27 ++++++++++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5661813..bdcd514 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -124,6 +124,10 @@ jobs: scp -i ~/.ssh/deploy_key \ target/x86_64-unknown-linux-gnu/release/helios-remote-relay \ root@46.225.185.232:/var/www/helios-remote/helios-remote-relay-linux + # Write version.json so CLI knows what's available + echo "{\"commit\":\"$(git rev-parse --short HEAD)\"}" > version.json + scp -i ~/.ssh/deploy_key version.json \ + root@46.225.185.232:/var/www/helios-remote/version.json build-cli: runs-on: ubuntu-latest diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 5da1872..17ad146 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -722,7 +722,17 @@ fn main() { Commands::Update { device } => { validate_label(&device); - // Fetch all three commits + // Fetch latest available commit from version.json + let latest_commit = match reqwest::blocking::get("https://agent-helios.me/downloads/helios-remote/version.json") { + Ok(r) => r + .json::() + .ok() + .and_then(|v| v["commit"].as_str().map(String::from)) + .unwrap_or_else(|| "?".into()), + Err(e) => format!("error: {}", e), + }; + + // Fetch all three running commits let relay_commit = match reqwest::blocking::get(&format!("{}/version", cfg.base_url)) { Ok(r) => r .json::() @@ -739,13 +749,14 @@ fn main() { let cli_commit = GIT_COMMIT; + println!(" latest {}", latest_commit); println!(" relay {}", relay_commit); println!(" cli {}", cli_commit); println!(" client {}", client_commit); - let all_same = relay_commit == cli_commit && cli_commit == client_commit; - if all_same { - println!(" ✅ Already up to date (commit: {})", cli_commit); + let all_current = relay_commit == latest_commit && cli_commit == latest_commit && client_commit == latest_commit; + if all_current { + println!(" ✅ Already up to date (commit: {})", latest_commit); return; } @@ -753,7 +764,7 @@ fn main() { let mut updated_any = false; // Update relay if needed - if relay_commit != cli_commit { + if relay_commit != latest_commit { println!(" → Updating relay..."); let data = req(&cfg, "POST", "/relay/update", None, 15); println!(" {}", data["message"].as_str().unwrap_or("triggered")); @@ -761,7 +772,7 @@ fn main() { } // Update client if needed - if client_commit != cli_commit { + if client_commit != latest_commit { println!(" → Updating client on {}...", device); let data = req( &cfg, @@ -780,9 +791,7 @@ fn main() { } // Self-update CLI if needed - // (relay_commit is the "canonical" latest — if we differ from it, we're outdated) - // Skip on non-x86_64 Linux (e.g. ARM/Pi) — CI only builds x86_64 Linux binaries - if cli_commit != relay_commit { + if cli_commit != latest_commit { println!(" → Updating CLI..."); #[cfg(target_os = "windows")] let url = "https://agent-helios.me/downloads/helios-remote/helios-remote-cli-windows.exe"; From 9c429ce20e49af8e6ef642e63248539c86a2e1d0 Mon Sep 17 00:00:00 2001 From: Helios Agent Date: Fri, 6 Mar 2026 15:24:14 +0100 Subject: [PATCH 08/10] docs: update README and SKILL.md - document update system, rename binaries --- README.md | 17 ++++++++++++++--- SKILL.md | 14 +++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 05eb434..c0f4c22 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ irm https://raw.githubusercontent.com/agent-helios/helios-remote/master/scripts/ ``` AI Agent │ - ▼ remote CLI -helios-server ──WebSocket── helios-client (Windows) + ▼ helios-remote-cli +helios-remote-relay ──WebSocket── helios-remote-client (Windows) ``` 1. The **Windows client** connects to the relay server via WebSocket and registers with its device label. @@ -62,11 +62,22 @@ remote clipboard-get # get clipboard text remote clipboard-set # set clipboard text remote upload # upload file to device remote download # download file from device -remote version # compare relay/helios/client commits +remote version # compare latest/relay/cli/client commits +remote update # update all components to latest version remote logs # fetch last 20 lines of client log (default) remote logs --lines 200 # custom line count ``` +### Update System + +`remote update ` checks `version.json` on the download server for the latest available commit and updates any component that's behind: + +- **Relay** — downloads new binary, replaces itself, restarts via systemd +- **Client** — downloads new binary, replaces itself, relaunches automatically +- **CLI** — downloads new binary, replaces itself, re-executes the update command + +CI publishes new binaries after every push to `master` but does **not** auto-restart the relay. Updates only happen when explicitly triggered via `remote update`. + --- ## Server Setup diff --git a/SKILL.md b/SKILL.md index ddef201..a98d4c3 100644 --- a/SKILL.md +++ b/SKILL.md @@ -73,12 +73,16 @@ $SKILL_DIR/helios clipboard-set moritz-pc "Text for clipboard" $SKILL_DIR/helios upload moritz-pc /tmp/local.txt "C:\Users\Moritz\Desktop\remote.txt" $SKILL_DIR/helios download moritz-pc "C:\Users\Moritz\file.txt" /tmp/downloaded.txt -# Version: compare relay + remote + client commits (are they in sync?) -$SKILL_DIR/helios version moritz-pc +# Version: compare latest available vs running commits (relay / cli / client) +$SKILL_DIR/remote version moritz-pc -# Client log (last 100 lines, --lines for more) -$SKILL_DIR/helios logs moritz-pc -$SKILL_DIR/helios logs moritz-pc --lines 200 +# Update: bring all components (relay, cli, client) to latest version +# CI publishes new binaries but does NOT auto-restart — this triggers the actual update +$SKILL_DIR/remote update moritz-pc + +# Client log (last 20 lines by default, --lines for more) +$SKILL_DIR/remote logs moritz-pc +$SKILL_DIR/remote logs moritz-pc --lines 200 ``` ## Typical Workflow: UI Task From b9783cc5c81df8c9211b9de38e14c227d22d7cd3 Mon Sep 17 00:00:00 2001 From: Helios Agent Date: Fri, 6 Mar 2026 15:47:53 +0100 Subject: [PATCH 09/10] chore: final end-to-end update test From 8f3615c5cf4b3fcadfdf2dd8cbf4816b48e40cf9 Mon Sep 17 00:00:00 2001 From: Helios Agent Date: Fri, 6 Mar 2026 15:49:34 +0100 Subject: [PATCH 10/10] chore: retrigger CI