aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-08 22:19:34 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-08 22:20:04 +0300
commit793f127e54dd5f2cfb78922470b7efcb74a1fa5e (patch)
treedf9a925c7d4dc4be64ffd77cf5dfe3e5abb273a0 /src/main.rs
parentbb0dad86f81768c8b7d5894acf7cd53bd05d302b (diff)
feat(remote): add network-efficient thin client transport
Add SSH stdio remote attach, negotiate terminal-ANSI render encoding, move blit encoding server-side for remote clients, and split control/render writer delivery for safer backpressure. Fixes #76
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs35
1 files changed, 35 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
index 45ac3db1..ee2c30e4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -36,6 +36,7 @@ mod persist;
mod platform;
mod raw_input;
mod release_notes;
+mod remote;
mod selection;
mod server;
mod session;
@@ -173,12 +174,38 @@ fn main() -> io::Result<()> {
std::process::exit(2);
}
};
+ let (args, remote_launch) = match remote::extract_remote_args(&args) {
+ Ok(parsed) => parsed,
+ Err(err) => {
+ eprintln!("error: {err}");
+ eprintln!("run 'herdr --help' for usage");
+ std::process::exit(2);
+ }
+ };
+
+ if remote_launch.is_some()
+ && args.get(1).is_some()
+ && !args.iter().any(|a| {
+ matches!(
+ a.as_str(),
+ "--help" | "-h" | "--version" | "-V" | "--default-config"
+ )
+ })
+ {
+ eprintln!("error: --remote can only be used with the default launch command");
+ eprintln!("run 'herdr --help' for usage");
+ std::process::exit(2);
+ }
if let cli::CommandOutcome::Handled(code) = cli::maybe_run(&args)? {
std::process::exit(code);
}
// Subcommands and flags (no TUI, no logging needed)
+ if args.get(1).map(|s| s.as_str()) == Some("remote-client-bridge") {
+ return remote::run_remote_client_bridge();
+ }
+
if args.get(1).map(|s| s.as_str()) == Some("server") {
return server::headless::run_server();
}
@@ -203,6 +230,7 @@ fn main() -> io::Result<()> {
println!();
println!("Usage: herdr [options]");
println!(" herdr --session <name> [options]");
+ println!(" herdr --remote <ssh-target> [--session <name>]");
println!(" herdr session attach <name>");
println!(" herdr update");
println!(" herdr server stop");
@@ -270,6 +298,7 @@ fn main() -> io::Result<()> {
println!("Options:");
println!(" --no-session Run monolithically (no server/client, escape hatch)");
println!(" --session <name> Use or create a named persistent session");
+ println!(" --remote <target> Attach through SSH to a remote Herdr server");
println!(" --default-config Print default configuration and exit");
println!(" --version, -V Print version and exit");
println!(" --help, -h Show this help");
@@ -295,6 +324,7 @@ fn main() -> io::Result<()> {
let known_flags = [
"--no-session",
"--session",
+ "--remote",
"--version",
"-V",
"--default-config",
@@ -311,6 +341,7 @@ fn main() -> io::Result<()> {
&& ![
"server",
"client",
+ "remote-client-bridge",
"update",
"status",
"workspace",
@@ -327,6 +358,10 @@ fn main() -> io::Result<()> {
}
}
+ if let Some(remote_launch) = remote_launch {
+ return remote::run_remote(remote_launch);
+ }
+
let loaded_config = config::Config::load();
if should_block_nested(&loaded_config.config) {
eprintln!("\x1b[1merror:\x1b[0m nested herdr is disabled by default.");