clawmates-node: host terminal uses tmux (resumable, redraw-on-attach)
ci / gates (push) Failing after 5s
ci / rust (push) Has been skipped
ci / sandbox-k8s (push) Has been skipped
ci / frontend (push) Has been skipped
ci / e2e (push) Has been skipped

The node terminal opened a bare bash PTY — bash prints its prompt once, so a
freshly-attached xterm showed nothing. Switch to `tmux new-session -A -s
clawmates` on the HOST (mirrors the agent terminal, but on the node itself, not
in a container): tmux redraws the whole screen on attach (no blank), and the
session is resumable across reopens. Starts in $HOME. Falls back to a login
shell if tmux is unavailable. ensure_tmux() best-effort installs tmux via the
host package manager when the daemon runs as root (systemd); otherwise logs a
hint to `apt install tmux`. Rebuilt + re-hosted both binaries.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-24 20:27:51 -07:00
co-authored by Claude Opus 4.8
parent cf6c331b02
commit 6e81eaa52b
+73 -2
View File
@@ -43,6 +43,7 @@ async fn main() {
std::process::exit(2); std::process::exit(2);
} }
tailscale_up(&ts_authkey); tailscale_up(&ts_authkey);
ensure_tmux();
let ws_url = ws_url(&server, &token); let ws_url = ws_url(&server, &token);
println!("clawmates-node {VERSION} connecting to {server}"); println!("clawmates-node {VERSION} connecting to {server}");
loop { loop {
@@ -277,9 +278,26 @@ async fn open_pty(
let pair = native_pty_system() let pair = native_pty_system()
.openpty(PtySize { rows, cols, pixel_width: 0, pixel_height: 0 }) .openpty(PtySize { rows, cols, pixel_width: 0, pixel_height: 0 })
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/bash".to_owned()); // Prefer a resumable host tmux session (like the agent terminal): `-A` attaches
let mut cmd = CommandBuilder::new(shell); // to the existing "clawmates" session or creates it, and tmux redraws the whole
// screen on attach (so the view is never blank). Fall back to a login shell.
let mut cmd = if has_tmux() {
let mut c = CommandBuilder::new("tmux");
c.arg("new-session");
c.arg("-A");
c.arg("-s");
c.arg("clawmates");
c
} else {
let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/bash".to_owned());
let mut c = CommandBuilder::new(shell);
c.arg("-l");
c
};
cmd.env("TERM", "xterm-256color"); cmd.env("TERM", "xterm-256color");
if let Ok(home) = std::env::var("HOME") {
cmd.cwd(home);
}
let child = pair.slave.spawn_command(cmd).map_err(|e| e.to_string())?; let child = pair.slave.spawn_command(cmd).map_err(|e| e.to_string())?;
drop(pair.slave); drop(pair.slave);
let mut reader = pair.master.try_clone_reader().map_err(|e| e.to_string())?; let mut reader = pair.master.try_clone_reader().map_err(|e| e.to_string())?;
@@ -439,3 +457,56 @@ fn tailscale_up(authkey: &str) {
]) ])
.status(); .status();
} }
/// Is tmux on PATH?
fn has_tmux() -> bool {
std::process::Command::new("tmux")
.arg("-V")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
/// Best-effort: install tmux via the host package manager (works when the daemon
/// runs as root, e.g. systemd). Non-interactive; if it can't, host terminals fall
/// back to a plain login shell.
fn ensure_tmux() {
if has_tmux() {
return;
}
let mgrs: &[(&str, &[&str])] = &[
("apt-get", &["install", "-y", "tmux"]),
("dnf", &["install", "-y", "tmux"]),
("yum", &["install", "-y", "tmux"]),
("apk", &["add", "--no-cache", "tmux"]),
("pacman", &["-S", "--noconfirm", "tmux"]),
("brew", &["install", "tmux"]),
];
for (mgr, args) in mgrs {
let present = std::process::Command::new("sh")
.arg("-c")
.arg(format!("command -v {mgr}"))
.output()
.map(|o| o.status.success())
.unwrap_or(false);
if !present {
continue;
}
if *mgr == "apt-get" {
let _ = std::process::Command::new("apt-get")
.arg("update")
.env("DEBIAN_FRONTEND", "noninteractive")
.output();
}
let _ = std::process::Command::new(mgr)
.args(*args)
.env("DEBIAN_FRONTEND", "noninteractive")
.output();
break;
}
if has_tmux() {
println!("tmux ready for host terminals");
} else {
eprintln!("tmux not found (auto-install unavailable) — host terminal will use a plain shell; `apt install tmux` for resumable sessions");
}
}