fix: optimize SSH integration and improve error handling

- Replace dual-loop SSH config parsing with efficient single-pass case
statement
- Remove overly cautious timeout logic from cache checks for simplicity
- Add base64 availability check with xterm-256color fallback when
missing
- Include hostname in terminfo setup messages for better UX
- Maintain SendEnv/SetEnv dual approach for maximum OpenSSH
compatibility (relying on SetEnv alone seems to drop some vars during my
tests, despite them being explicitly included in AcceptEnv on the remote
host)
This commit is contained in:
Jason Rayne
2025-07-05 13:02:35 -07:00
parent 5ec18f426c
commit a22074a85c
4 changed files with 381 additions and 422 deletions

View File

@@ -244,132 +244,116 @@ _ghostty_deferred_init() {
}
fi
# SSH Integration
# SSH Integration
if [[ "$GHOSTTY_SHELL_FEATURES" =~ (ssh-env|ssh-terminfo) ]]; then
: "${GHOSTTY_SSH_CACHE_TIMEOUT:=5}"
: "${GHOSTTY_SSH_CHECK_TIMEOUT:=3}"
# SSH wrapper that preserves Ghostty features across remote connections
ssh() {
emulate -L zsh
setopt local_options no_glob_subst
local -a ssh_env ssh_opts
local ssh_env ssh_opts ssh_exported_vars
ssh_env=()
ssh_opts=()
ssh_exported_vars=()
# Configure environment variables for remote session
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
local -a ssh_env_vars=(
ssh_opts+=(-o "SetEnv COLORTERM=truecolor")
if [[ -n "${TERM_PROGRAM+x}" ]]; then
ssh_exported_vars+=("TERM_PROGRAM=${TERM_PROGRAM}")
else
ssh_exported_vars+=("TERM_PROGRAM")
fi
export "TERM_PROGRAM=ghostty"
ssh_opts+=(-o "SendEnv TERM_PROGRAM")
if [[ -n "$TERM_PROGRAM_VERSION" ]]; then
if [[ -n "${TERM_PROGRAM_VERSION+x}" ]]; then
ssh_exported_vars+=("TERM_PROGRAM_VERSION=${TERM_PROGRAM_VERSION}")
else
ssh_exported_vars+=("TERM_PROGRAM_VERSION")
fi
ssh_opts+=(-o "SendEnv TERM_PROGRAM_VERSION")
fi
ssh_env+=(
"COLORTERM=truecolor"
"TERM_PROGRAM=ghostty"
)
[[ -n "$TERM_PROGRAM_VERSION" ]] && ssh_env_vars+=("TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION")
# Temporarily export variables for SSH transmission
local -a ssh_exported_vars=()
local ssh_v ssh_var_name
for ssh_v in "${ssh_env_vars[@]}"; do
ssh_var_name="${ssh_v%%=*}"
if [[ -n "${(P)ssh_var_name+x}" ]]; then
ssh_exported_vars+=("$ssh_var_name=${(P)ssh_var_name}")
else
ssh_exported_vars+=("$ssh_var_name")
fi
export "${ssh_v}"
# Use both SendEnv and SetEnv for maximum compatibility
ssh_opts+=(-o "SendEnv $ssh_var_name")
ssh_opts+=(-o "SetEnv $ssh_v")
done
ssh_env+=("${ssh_env_vars[@]}")
[[ -n "$TERM_PROGRAM_VERSION" ]] && ssh_env+=("TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION")
fi
# Install terminfo on remote host if needed
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
local ssh_config ssh_user ssh_hostname ssh_target
local ssh_config ssh_user ssh_hostname
ssh_config=$(command ssh -G "$@" 2>/dev/null)
ssh_user=$(printf '%s\n' "${(@f)ssh_config}" | while IFS=' ' read -r ssh_key ssh_value; do
[[ "$ssh_key" == "user" ]] && printf '%s\n' "$ssh_value" && break
done)
ssh_hostname=$(printf '%s\n' "${(@f)ssh_config}" | while IFS=' ' read -r ssh_key ssh_value; do
[[ "$ssh_key" == "hostname" ]] && printf '%s\n' "$ssh_value" && break
done)
while IFS=' ' read -r ssh_key ssh_value; do
case "$ssh_key" in
user) ssh_user="$ssh_value" ;;
hostname) ssh_hostname="$ssh_value" ;;
esac
[[ -n "$ssh_user" && -n "$ssh_hostname" ]] && break
done <<< "$ssh_config"
ssh_target="${ssh_user}@${ssh_hostname}"
if [[ -n "$ssh_hostname" ]]; then
# Detect timeout command (BSD compatibility)
local ssh_timeout_cmd=""
if (( $+commands[timeout] )); then
ssh_timeout_cmd="timeout"
elif (( $+commands[gtimeout] )); then
ssh_timeout_cmd="gtimeout"
fi
# Check if terminfo is already cached
local ssh_cache_check_success=false
if (( $+commands[ghostty] )); then
if [[ -n "$ssh_timeout_cmd" ]]; then
$ssh_timeout_cmd "${GHOSTTY_SSH_CHECK_TIMEOUT}s" ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1 && ssh_cache_check_success=true
else
ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1 && ssh_cache_check_success=true
fi
ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1 && ssh_cache_check_success=true
fi
if [[ "$ssh_cache_check_success" == "true" ]]; then
ssh_env+=(TERM=xterm-ghostty)
elif (( $+commands[infocmp] )); then
local ssh_terminfo
# Generate terminfo data (BSD base64 compatibility)
if base64 --help 2>&1 | grep -q GNU; then
ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 -w0 2>/dev/null)
if ! (( $+commands[base64] )); then
print "Warning: base64 command not available for terminfo installation." >&2
ssh_env+=(TERM=xterm-256color)
else
ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
fi
local ssh_terminfo ssh_base64_decode_cmd
if [[ -n "$ssh_terminfo" ]]; then
print "Setting up Ghostty terminfo on remote host..." >&2
local ssh_cpath_dir ssh_cpath
ssh_cpath_dir=$(mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null) || ssh_cpath_dir="/tmp/ghostty-ssh-$ssh_user.$$"
ssh_cpath="$ssh_cpath_dir/socket"
local ssh_base64_decode_cmd
# BSD vs GNU base64 compatibility
if base64 --help 2>&1 | grep -q GNU; then
ssh_base64_decode_cmd="base64 -d"
ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 -w0 2>/dev/null)
else
ssh_base64_decode_cmd="base64 -D"
ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
fi
if print "$ssh_terminfo" | $ssh_base64_decode_cmd | command ssh "${ssh_opts[@]}" -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" '
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1
' 2>/dev/null; then
print "Terminfo setup complete." >&2
ssh_env+=(TERM=xterm-ghostty)
ssh_opts+=(-o "ControlPath=$ssh_cpath")
if [[ -n "$ssh_terminfo" ]]; then
print "Setting up Ghostty terminfo on $ssh_hostname..." >&2
local ssh_cpath_dir ssh_cpath
# Cache successful installation
if [[ -n "$ssh_target" ]] && (( $+commands[ghostty] )); then
{
if [[ -n "$ssh_timeout_cmd" ]]; then
$ssh_timeout_cmd "${GHOSTTY_SSH_CACHE_TIMEOUT}s" ghostty +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true
else
ssh_cpath_dir=$(mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null) || ssh_cpath_dir="/tmp/ghostty-ssh-$ssh_user.$$"
ssh_cpath="$ssh_cpath_dir/socket"
if print "$ssh_terminfo" | $ssh_base64_decode_cmd | command ssh "${ssh_opts[@]}" -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" '
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1
' 2>/dev/null; then
print "Terminfo setup complete on $ssh_hostname." >&2
ssh_env+=(TERM=xterm-ghostty)
ssh_opts+=(-o "ControlPath=$ssh_cpath")
# Cache successful installation
if [[ -n "$ssh_target" ]] && (( $+commands[ghostty] )); then
{
ghostty +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true
fi
} &!
} &!
fi
else
print "Warning: Failed to install terminfo." >&2
ssh_env+=(TERM=xterm-256color)
fi
else
print "Warning: Failed to install terminfo." >&2
print "Warning: Could not generate terminfo data." >&2
ssh_env+=(TERM=xterm-256color)
fi
else
print "Warning: Could not generate terminfo data." >&2
ssh_env+=(TERM=xterm-256color)
fi
else
print "Warning: ghostty command not available for cache management." >&2
@@ -380,21 +364,35 @@ _ghostty_deferred_init() {
fi
fi
# Ensure TERM is set when using ssh-env feature
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
local ssh_term_set=false ssh_v
for ssh_v in "${ssh_env[@]}"; do
[[ "$ssh_v" =~ ^TERM= ]] && ssh_term_set=true && break
done
[[ "$ssh_term_set" == "false" && "$TERM" == "xterm-ghostty" ]] && ssh_env+=(TERM=xterm-256color)
# Execute SSH with environment handling
local ssh_term_override=""
local ssh_v
for ssh_v in "${ssh_env[@]}"; do
if [[ "$ssh_v" =~ ^TERM=(.*)$ ]]; then
ssh_term_override="${match[1]}"
break
fi
done
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env && -z "$ssh_term_override" ]]; then
ssh_env+=(TERM=xterm-256color)
ssh_term_override="xterm-256color"
fi
command ssh "${ssh_opts[@]}" "$@"
local ssh_ret=$?
local ssh_ret
if [[ -n "$ssh_term_override" ]]; then
local ssh_original_term="$TERM"
export TERM="$ssh_term_override"
command ssh "${ssh_opts[@]}" "$@"
ssh_ret=$?
export TERM="$ssh_original_term"
else
command ssh "${ssh_opts[@]}" "$@"
ssh_ret=$?
fi
# Restore original environment variables
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
local ssh_v
for ssh_v in "${ssh_exported_vars[@]}"; do
if [[ "$ssh_v" == *=* ]]; then
export "${ssh_v}"