diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdcd514..38eee93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,14 +120,17 @@ jobs: echo "$VPS_SSH_KEY" > ~/.ssh/deploy_key chmod 600 ~/.ssh/deploy_key ssh-keyscan -H 46.225.185.232 >> ~/.ssh/known_hosts - # 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:/opt/helios-remote/target/release/helios-remote-relay-new + # Also publish relay binary to download URL for self-update 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 + 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 @@ -139,10 +142,10 @@ jobs: - name: Install Rust (stable) + targets uses: dtolnay/rust-toolchain@stable with: - targets: x86_64-unknown-linux-gnu,x86_64-pc-windows-gnu,aarch64-unknown-linux-gnu + targets: x86_64-unknown-linux-gnu,x86_64-pc-windows-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 gcc-aarch64-linux-gnu + run: sudo apt-get update && sudo apt-get install -y gcc-x86-64-linux-gnu gcc-mingw-w64-x86-64 mingw-w64-tools - name: Cache dependencies uses: Swatinem/rust-cache@v2 @@ -154,11 +157,6 @@ 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: @@ -190,9 +188,6 @@ 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/README.md b/README.md index c0f4c22..fce9145 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ irm https://raw.githubusercontent.com/agent-helios/helios-remote/master/scripts/ ``` AI Agent │ - ▼ helios-remote-cli -helios-remote-relay ──WebSocket── helios-remote-client (Windows) + ▼ remote CLI +helios-server ──WebSocket── helios-client (Windows) ``` 1. The **Windows client** connects to the relay server via WebSocket and registers with its device label. @@ -62,22 +62,11 @@ 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 latest/relay/cli/client commits -remote update # update all components to latest version +remote version # compare relay/helios/client commits 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 @@ -113,4 +102,3 @@ The relay server (`helios-remote-relay`) runs on the VPS and is not distributed. MIT - diff --git a/SKILL.md b/SKILL.md index a98d4c3..ddef201 100644 --- a/SKILL.md +++ b/SKILL.md @@ -73,16 +73,12 @@ $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 latest available vs running commits (relay / cli / client) -$SKILL_DIR/remote version moritz-pc +# Version: compare relay + remote + client commits (are they in sync?) +$SKILL_DIR/helios version moritz-pc -# 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 +# Client log (last 100 lines, --lines for more) +$SKILL_DIR/helios logs moritz-pc +$SKILL_DIR/helios logs moritz-pc --lines 200 ``` ## Typical Workflow: UI Task diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 94de807..8cf2bef 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", "rustls-tls"], default-features = false } +reqwest = { version = "0.12", features = ["blocking", "json"] } base64 = "0.22" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 17ad146..51f6361 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -722,17 +722,7 @@ fn main() { Commands::Update { device } => { validate_label(&device); - // 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 + // Fetch all three commits let relay_commit = match reqwest::blocking::get(&format!("{}/version", cfg.base_url)) { Ok(r) => r .json::() @@ -749,14 +739,13 @@ 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_current = relay_commit == latest_commit && cli_commit == latest_commit && client_commit == latest_commit; - if all_current { - println!(" ✅ Already up to date (commit: {})", latest_commit); + let all_same = relay_commit == cli_commit && cli_commit == client_commit; + if all_same { + println!(" ✅ Already up to date (commit: {})", cli_commit); return; } @@ -764,7 +753,7 @@ fn main() { let mut updated_any = false; // Update relay if needed - if relay_commit != latest_commit { + if relay_commit != cli_commit { println!(" → Updating relay..."); let data = req(&cfg, "POST", "/relay/update", None, 15); println!(" {}", data["message"].as_str().unwrap_or("triggered")); @@ -772,7 +761,7 @@ fn main() { } // Update client if needed - if client_commit != latest_commit { + if client_commit != cli_commit { println!(" → Updating client on {}...", device); let data = req( &cfg, @@ -791,13 +780,18 @@ fn main() { } // Self-update CLI if needed - if cli_commit != latest_commit { + // (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(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")))] + #[cfg(not(target_os = "windows"))] let url = "https://agent-helios.me/downloads/helios-remote/helios-remote-cli-linux"; let bytes = match reqwest::blocking::get(url) { diff --git a/crates/client/src/main.rs b/crates/client/src/main.rs index fdd6afa..1a07545 100644 --- a/crates/client/src/main.rs +++ b/crates/client/src/main.rs @@ -221,13 +221,6 @@ 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."); @@ -719,17 +712,13 @@ 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")] { - // 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(); + 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(); } #[cfg(not(target_os = "windows"))] let _ = std::process::Command::new(&exe).args(&args).spawn();