#!/bin/sh # remote-run # # Run a command on inside a detached tmux session, stream output back, # propagate exit code. If the local ssh dies mid-run, the tmux session keeps # going on the remote — recover with: # ssh tmux ls # ssh tmux attach -t # # is whatever ssh accepts: a Host alias from ~/.ssh/config, a # user@hostname, an IP, etc. set -eu if [ $# -lt 2 ]; then echo "usage: $0 " >&2 exit 2 fi host=$1 shift session="claude-$(whoami)-$$-$(date +%s)" # Base64-encode the user command so it embeds safely no matter what quotes, # pipes, semicolons, or newlines it contains. The remote side decodes it to a # script file and runs THAT — user content never lands in a shell-quoted # context where a stray " could break parsing (the old sed-escape only handled # ' and broke on any embedded ", splitting the command at the next | or ;). user_cmd=$* cmd_b64=$(printf %s "$user_cmd" | base64 | tr -d '\n') # Remote bootstrap — runs the user command in its own bash subshell so that # any `exit` or `set -e` inside it does NOT short-circuit our exit-capture. remote_cmd=$(cat < "\$cmdf" : > "\$log" tmux new-session -d -s "\$session" "bash \$cmdf > \$log 2>&1; echo \\\$? > \$exitf; tmux wait-for -S done-\$session" 2>/tmp/\${session}.tmuxerr if [ \$? -ne 0 ]; then echo "tmux failed to start session:" >&2 cat /tmp/\${session}.tmuxerr >&2 rm -f /tmp/\${session}.tmuxerr exit 127 fi rm -f /tmp/\${session}.tmuxerr ( tail -F "\$log" 2>/dev/null ) & tail_pid=\$! tmux wait-for done-\$session 2>/dev/null sleep 0.2 kill \$tail_pid 2>/dev/null || true wait \$tail_pid 2>/dev/null || true code=\$(cat "\$exitf" 2>/dev/null || echo 1) tmux kill-session -t "\$session" 2>/dev/null || true rm -f "\$log" "\$exitf" "\$cmdf" exit \$code REMOTE ) ssh "$host" "$remote_cmd"