Compare commits

...

10 commits

6 changed files with 75 additions and 37 deletions

View file

@ -120,17 +120,14 @@ 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"
# 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
@ -142,10 +139,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
@ -157,6 +154,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:
@ -188,6 +190,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

View file

@ -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 <device> # get clipboard text
remote clipboard-set <device> <text> # set clipboard text
remote upload <device> <local> <remote> # upload file to device
remote download <device> <remote> <local> # download file from device
remote version <device> # compare relay/helios/client commits
remote version <device> # compare latest/relay/cli/client commits
remote update <device> # update all components to latest version
remote logs <device> # fetch last 20 lines of client log (default)
remote logs <device> --lines 200 # custom line count
```
### Update System
`remote update <device>` 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
@ -102,3 +113,4 @@ The relay server (`helios-remote-relay`) runs on the VPS and is not distributed.
MIT

View file

@ -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

View file

@ -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"

View file

@ -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::<Value>()
.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::<Value>()
@ -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,18 +791,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 {
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";
#[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) {

View file

@ -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.");
@ -712,13 +719,17 @@ 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<String> = 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();