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

@ -97,121 +97,105 @@ fi
# SSH Integration # SSH Integration
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
: "${GHOSTTY_SSH_CACHE_TIMEOUT:=5}"
: "${GHOSTTY_SSH_CHECK_TIMEOUT:=3}"
# SSH wrapper that preserves Ghostty features across remote connections
ssh() { ssh() {
local ssh_env=() ssh_opts=() builtin local ssh_env ssh_opts ssh_exported_vars
ssh_env=()
ssh_opts=()
ssh_exported_vars=()
# Configure environment variables for remote session # Configure environment variables for remote session
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then 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
builtin 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" "COLORTERM=truecolor"
"TERM_PROGRAM=ghostty" "TERM_PROGRAM=ghostty"
) )
if [[ -n "$TERM_PROGRAM_VERSION" ]]; then if [[ -n "$TERM_PROGRAM_VERSION" ]]; then
ssh_env_vars+=("TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION") ssh_env+=("TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION")
fi fi
# Temporarily export variables for SSH transmission
local -a ssh_exported_vars=()
for ssh_v in "${ssh_env_vars[@]}"; do
local ssh_var_name="${ssh_v%%=*}"
if [[ -n "${!ssh_var_name+x}" ]]; then
ssh_exported_vars+=("$ssh_var_name=${!ssh_var_name}")
else
ssh_exported_vars+=("$ssh_var_name")
fi
builtin 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[@]}")
fi fi
# Install terminfo on remote host if needed # Install terminfo on remote host if needed
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
builtin local ssh_config ssh_user ssh_hostname builtin local ssh_config ssh_user ssh_hostname
ssh_config=$(builtin command ssh -G "$@" 2>/dev/null) ssh_config=$(builtin command ssh -G "$@" 2>/dev/null)
ssh_user=$(echo "$ssh_config" | while IFS=' ' read -r ssh_key ssh_value; do
[[ "$ssh_key" == "ssh_user" ]] && echo "$ssh_value" && break while IFS=' ' read -r ssh_key ssh_value; do
done) case "$ssh_key" in
ssh_hostname=$(echo "$ssh_config" | while IFS=' ' read -r ssh_key ssh_value; do user) ssh_user="$ssh_value" ;;
[[ "$ssh_key" == "hostname" ]] && echo "$ssh_value" && break hostname) ssh_hostname="$ssh_value" ;;
done) esac
[[ -n "$ssh_user" && -n "$ssh_hostname" ]] && break
done <<< "$ssh_config"
ssh_target="${ssh_user}@${ssh_hostname}" ssh_target="${ssh_user}@${ssh_hostname}"
if [[ -n "$ssh_hostname" ]]; then if [[ -n "$ssh_hostname" ]]; then
# Detect timeout command (BSD compatibility)
local ssh_timeout_cmd=""
if command -v timeout >/dev/null 2>&1; then
ssh_timeout_cmd="timeout"
elif command -v gtimeout >/dev/null 2>&1; then
ssh_timeout_cmd="gtimeout"
fi
# Check if terminfo is already cached # Check if terminfo is already cached
local ssh_cache_check_success=false builtin local ssh_cache_check_success=false
if command -v ghostty >/dev/null 2>&1; then if builtin command -v ghostty >/dev/null 2>&1; 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 ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1 && ssh_cache_check_success=true
fi fi
fi
if [[ "$ssh_cache_check_success" == "true" ]]; then if [[ "$ssh_cache_check_success" == "true" ]]; then
ssh_env+=(TERM=xterm-ghostty) ssh_env+=(TERM=xterm-ghostty)
elif builtin command -v infocmp >/dev/null 2>&1; then elif builtin command -v infocmp >/dev/null 2>&1; then
builtin local ssh_terminfo if ! builtin command -v base64 >/dev/null 2>&1; then
builtin echo "Warning: base64 command not available for terminfo installation." >&2
ssh_env+=(TERM=xterm-256color)
else
builtin local ssh_terminfo ssh_base64_decode_cmd
# Generate terminfo data (BSD base64 compatibility) # BSD vs GNU base64 compatibility
if base64 --help 2>&1 | grep -q GNU; then 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) ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 -w0 2>/dev/null)
else 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') ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
fi fi
if [[ -n "$ssh_terminfo" ]]; then if [[ -n "$ssh_terminfo" ]]; then
builtin echo "Setting up Ghostty terminfo on remote host..." >&2 builtin echo "Setting up Ghostty terminfo on $ssh_hostname..." >&2
builtin local ssh_cpath_dir ssh_cpath builtin 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_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" ssh_cpath="$ssh_cpath_dir/socket"
local ssh_base64_decode_cmd
if base64 --help 2>&1 | grep -q GNU; then
ssh_base64_decode_cmd="base64 -d"
else
ssh_base64_decode_cmd="base64 -D"
fi
if builtin echo "$ssh_terminfo" | $ssh_base64_decode_cmd | builtin command ssh "${ssh_opts[@]}" -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" ' if builtin echo "$ssh_terminfo" | $ssh_base64_decode_cmd | builtin command ssh "${ssh_opts[@]}" -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" '
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0 infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1 command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0 mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1 exit 1
' 2>/dev/null; then ' 2>/dev/null; then
builtin echo "Terminfo setup complete." >&2 builtin echo "Terminfo setup complete on $ssh_hostname." >&2
ssh_env+=(TERM=xterm-ghostty) ssh_env+=(TERM=xterm-ghostty)
ssh_opts+=(-o "ControlPath=$ssh_cpath") ssh_opts+=(-o "ControlPath=$ssh_cpath")
# Cache successful installation # Cache successful installation
if [[ -n "$ssh_target" ]] && command -v ghostty >/dev/null 2>&1; then if [[ -n "$ssh_target" ]] && builtin command -v ghostty >/dev/null 2>&1; then
( (
set +m set +m
{ {
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
ghostty +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true ghostty +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true
fi
} & } &
) )
fi fi
@ -223,6 +207,7 @@ if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
builtin echo "Warning: Could not generate terminfo data." >&2 builtin echo "Warning: Could not generate terminfo data." >&2
ssh_env+=(TERM=xterm-256color) ssh_env+=(TERM=xterm-256color)
fi fi
fi
else else
builtin echo "Warning: ghostty command not available for cache management." >&2 builtin echo "Warning: ghostty command not available for cache management." >&2
ssh_env+=(TERM=xterm-256color) ssh_env+=(TERM=xterm-256color)
@ -234,22 +219,30 @@ if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
fi fi
fi fi
# Ensure TERM is set when using ssh-env feature # Execute SSH with environment handling
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then builtin local ssh_term_override=""
local ssh_term_set=false
for ssh_v in "${ssh_env[@]}"; do for ssh_v in "${ssh_env[@]}"; do
if [[ "$ssh_v" =~ ^TERM= ]]; then if [[ "$ssh_v" =~ ^TERM=(.*)$ ]]; then
ssh_term_set=true ssh_term_override="${BASH_REMATCH[1]}"
break break
fi fi
done done
if [[ "$ssh_term_set" == "false" && "$TERM" == "xterm-ghostty" ]]; then
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env && -z "$ssh_term_override" ]]; then
ssh_env+=(TERM=xterm-256color) ssh_env+=(TERM=xterm-256color)
fi ssh_term_override="xterm-256color"
fi fi
if [[ -n "$ssh_term_override" ]]; then
builtin local ssh_original_term="$TERM"
builtin export TERM="$ssh_term_override"
builtin command ssh "${ssh_opts[@]}" "$@" builtin command ssh "${ssh_opts[@]}" "$@"
local ssh_ret=$? local ssh_ret=$?
builtin export TERM="$ssh_original_term"
else
builtin command ssh "${ssh_opts[@]}" "$@"
local ssh_ret=$?
fi
# Restore original environment variables # Restore original environment variables
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then

View File

@ -100,50 +100,41 @@
# SSH Integration # SSH Integration
use str use str
use re
if (or (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-terminfo)) { if (or (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-terminfo)) {
var GHOSTTY_SSH_CACHE_TIMEOUT = (if (has-env GHOSTTY_SSH_CACHE_TIMEOUT) { echo $E:GHOSTTY_SSH_CACHE_TIMEOUT } else { echo 5 })
var GHOSTTY_SSH_CHECK_TIMEOUT = (if (has-env GHOSTTY_SSH_CHECK_TIMEOUT) { echo $E:GHOSTTY_SSH_CHECK_TIMEOUT } else { echo 3 })
# SSH wrapper that preserves Ghostty features across remote connections
fn ssh {|@args| fn ssh {|@args|
var ssh-env = [] var ssh-env = []
var ssh-opts = [] var ssh-opts = []
var ssh-exported-vars = []
# Configure environment variables for remote session # Configure environment variables for remote session
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) { if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {
var ssh-env-vars = [ set ssh-opts = [$@ssh-opts -o "SetEnv COLORTERM=truecolor"]
COLORTERM=truecolor
TERM_PROGRAM=ghostty if (has-env TERM_PROGRAM) {
] set ssh-exported-vars = [$@ssh-exported-vars "TERM_PROGRAM="$E:TERM_PROGRAM]
} else {
set ssh-exported-vars = [$@ssh-exported-vars "TERM_PROGRAM"]
}
set-env TERM_PROGRAM ghostty
set ssh-opts = [$@ssh-opts -o "SendEnv TERM_PROGRAM"]
if (has-env TERM_PROGRAM_VERSION) { if (has-env TERM_PROGRAM_VERSION) {
set ssh-env-vars = [$@ssh-env-vars TERM_PROGRAM_VERSION=$E:TERM_PROGRAM_VERSION] if (has-env TERM_PROGRAM_VERSION) {
} set ssh-exported-vars = [$@ssh-exported-vars "TERM_PROGRAM_VERSION="$E:TERM_PROGRAM_VERSION]
# Store original values for restoration
var ssh-exported-vars = []
for ssh-v $ssh-env-vars {
var ssh-var-name = (str:split &max=2 = $ssh-v)[0]
if (has-env $ssh-var-name) {
var original-value = (get-env $ssh-var-name)
set ssh-exported-vars = [$@ssh-exported-vars $ssh-var-name=$original-value]
} else { } else {
set ssh-exported-vars = [$@ssh-exported-vars $ssh-var-name] set ssh-exported-vars = [$@ssh-exported-vars "TERM_PROGRAM_VERSION"]
}
set ssh-opts = [$@ssh-opts -o "SendEnv TERM_PROGRAM_VERSION"]
} }
# Export the variable set ssh-env = [
var ssh-var-parts = (str:split &max=2 = $ssh-v) "COLORTERM=truecolor"
set-env $ssh-var-parts[0] $ssh-var-parts[1] "TERM_PROGRAM=ghostty"
]
# Use both SendEnv and SetEnv for maximum compatibility if (has-env TERM_PROGRAM_VERSION) {
set ssh-opts = [$@ssh-opts -o "SendEnv "$ssh-var-name] set ssh-env = [$@ssh-env "TERM_PROGRAM_VERSION="$E:TERM_PROGRAM_VERSION]
set ssh-opts = [$@ssh-opts -o "SetEnv "$ssh-v]
} }
set ssh-env = [$@ssh-env $@ssh-env-vars]
} }
# Install terminfo on remote host if needed # Install terminfo on remote host if needed
@ -160,53 +151,29 @@
for line (str:split "\n" $ssh-config) { for line (str:split "\n" $ssh-config) {
var parts = (str:split " " $line) var parts = (str:split " " $line)
if (and (> (count $parts) 1) (eq $parts[0] user)) { if (> (count $parts) 1) {
if (eq $parts[0] user) {
set ssh-user = $parts[1] set ssh-user = $parts[1]
} } elif (eq $parts[0] hostname) {
if (and (> (count $parts) 1) (eq $parts[0] hostname)) {
set ssh-hostname = $parts[1] set ssh-hostname = $parts[1]
} }
if (and (not-eq $ssh-user "") (not-eq $ssh-hostname "")) {
break
}
}
} }
var ssh-target = $ssh-user"@"$ssh-hostname var ssh-target = $ssh-user"@"$ssh-hostname
if (not-eq $ssh-hostname "") { if (not-eq $ssh-hostname "") {
# Detect timeout command (BSD compatibility)
var ssh-timeout-cmd = ""
try {
external timeout --help >/dev/null 2>&1
set ssh-timeout-cmd = timeout
} catch {
try {
external gtimeout --help >/dev/null 2>&1
set ssh-timeout-cmd = gtimeout
} catch {
# no timeout command available
}
}
# Check if terminfo is already cached # Check if terminfo is already cached
var ssh-cache-check-success = $false var ssh-cache-check-success = $false
try {
external ghostty --help >/dev/null 2>&1
if (not-eq $ssh-timeout-cmd "") {
try {
external $ssh-timeout-cmd $GHOSTTY_SSH_CHECK_TIMEOUT"s" ghostty +ssh-cache --host=$ssh-target >/dev/null 2>&1
set ssh-cache-check-success = $true
} catch {
# cache check failed
}
} else {
try { try {
external ghostty +ssh-cache --host=$ssh-target >/dev/null 2>&1 external ghostty +ssh-cache --host=$ssh-target >/dev/null 2>&1
set ssh-cache-check-success = $true set ssh-cache-check-success = $true
} catch { } catch {
# cache check failed # cache check failed
} }
}
} catch {
# ghostty not available
}
if $ssh-cache-check-success { if $ssh-cache-check-success {
set ssh-env = [$@ssh-env TERM=xterm-ghostty] set ssh-env = [$@ssh-env TERM=xterm-ghostty]
@ -214,13 +181,19 @@
try { try {
external infocmp --help >/dev/null 2>&1 external infocmp --help >/dev/null 2>&1
try {
external base64 --help >/dev/null 2>&1
# Generate terminfo data (BSD base64 compatibility) # Generate terminfo data (BSD base64 compatibility)
var ssh-terminfo = "" var ssh-terminfo = ""
var ssh-base64-decode-cmd = ""
try { try {
var base64-help = (external base64 --help 2>&1 | slurp) var base64-help = (external base64 --help 2>&1 | slurp)
if (str:contains $base64-help GNU) { if (str:contains $base64-help GNU) {
set ssh-base64-decode-cmd = "base64 -d"
set ssh-terminfo = (external infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | external base64 -w0 2>/dev/null | slurp) set ssh-terminfo = (external infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | external base64 -w0 2>/dev/null | slurp)
} else { } else {
set ssh-base64-decode-cmd = "base64 -D"
set ssh-terminfo = (external infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | external base64 2>/dev/null | external tr -d '\n' | slurp) set ssh-terminfo = (external infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | external base64 2>/dev/null | external tr -d '\n' | slurp)
} }
} catch { } catch {
@ -228,7 +201,7 @@
} }
if (not-eq $ssh-terminfo "") { if (not-eq $ssh-terminfo "") {
echo "Setting up Ghostty terminfo on remote host..." >&2 echo "Setting up Ghostty terminfo on "$ssh-hostname"..." >&2
var ssh-cpath-dir = "" var ssh-cpath-dir = ""
try { try {
set ssh-cpath-dir = (external mktemp -d "/tmp/ghostty-ssh-"$ssh-user".XXXXXX" 2>/dev/null | slurp) set ssh-cpath-dir = (external mktemp -d "/tmp/ghostty-ssh-"$ssh-user".XXXXXX" 2>/dev/null | slurp)
@ -237,18 +210,6 @@
} }
var ssh-cpath = $ssh-cpath-dir"/socket" var ssh-cpath = $ssh-cpath-dir"/socket"
var ssh-base64-decode-cmd = ""
try {
var base64-help = (external base64 --help 2>&1 | slurp)
if (str:contains $base64-help GNU) {
set ssh-base64-decode-cmd = "base64 -d"
} else {
set ssh-base64-decode-cmd = "base64 -D"
}
} catch {
set ssh-base64-decode-cmd = "base64 -d"
}
var terminfo-install-success = $false var terminfo-install-success = $false
try { try {
echo $ssh-terminfo | external sh -c $ssh-base64-decode-cmd | external ssh $@ssh-opts -o ControlMaster=yes -o ControlPath=$ssh-cpath -o ControlPersist=60s $@args ' echo $ssh-terminfo | external sh -c $ssh-base64-decode-cmd | external ssh $@ssh-opts -o ControlMaster=yes -o ControlPath=$ssh-cpath -o ControlPersist=60s $@args '
@ -263,18 +224,14 @@
} }
if $terminfo-install-success { if $terminfo-install-success {
echo "Terminfo setup complete." >&2 echo "Terminfo setup complete on "$ssh-hostname"." >&2
set ssh-env = [$@ssh-env TERM=xterm-ghostty] set ssh-env = [$@ssh-env TERM=xterm-ghostty]
set ssh-opts = [$@ssh-opts -o ControlPath=$ssh-cpath] set ssh-opts = [$@ssh-opts -o ControlPath=$ssh-cpath]
# Cache successful installation # Cache successful installation
if (and (not-eq $ssh-target "") (has-external ghostty)) { if (and (not-eq $ssh-target "") (has-external ghostty)) {
if (not-eq $ssh-timeout-cmd "") {
external $ssh-timeout-cmd $GHOSTTY_SSH_CACHE_TIMEOUT"s" ghostty +ssh-cache --add=$ssh-target >/dev/null 2>&1 &
} else {
external ghostty +ssh-cache --add=$ssh-target >/dev/null 2>&1 & external ghostty +ssh-cache --add=$ssh-target >/dev/null 2>&1 &
} }
}
} else { } else {
echo "Warning: Failed to install terminfo." >&2 echo "Warning: Failed to install terminfo." >&2
set ssh-env = [$@ssh-env TERM=xterm-256color] set ssh-env = [$@ssh-env TERM=xterm-256color]
@ -283,6 +240,10 @@
echo "Warning: Could not generate terminfo data." >&2 echo "Warning: Could not generate terminfo data." >&2
set ssh-env = [$@ssh-env TERM=xterm-256color] set ssh-env = [$@ssh-env TERM=xterm-256color]
} }
} catch {
echo "Warning: base64 command not available for terminfo installation." >&2
set ssh-env = [$@ssh-env TERM=xterm-256color]
}
} catch { } catch {
echo "Warning: ghostty command not available for cache management." >&2 echo "Warning: ghostty command not available for cache management." >&2
set ssh-env = [$@ssh-env TERM=xterm-256color] set ssh-env = [$@ssh-env TERM=xterm-256color]
@ -295,26 +256,37 @@
} }
} }
# Ensure TERM is set when using ssh-env feature # Execute SSH with environment handling
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) { var ssh-term-override = ""
var ssh-term-set = $false
for ssh-v $ssh-env { for ssh-v $ssh-env {
if (str:has-prefix $ssh-v TERM=) { if (str:has-prefix $ssh-v TERM=) {
set ssh-term-set = $true set ssh-term-override = (str:trim-prefix $ssh-v TERM=)
break break
} }
} }
if (and (not $ssh-term-set) (eq $E:TERM xterm-ghostty)) {
if (and (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) (eq $ssh-term-override "")) {
set ssh-env = [$@ssh-env TERM=xterm-256color] set ssh-env = [$@ssh-env TERM=xterm-256color]
} set ssh-term-override = xterm-256color
} }
var ssh-ret = 0 var ssh-ret = 0
if (not-eq $ssh-term-override "") {
var ssh-original-term = $E:TERM
set-env TERM $ssh-term-override
try { try {
external ssh $@ssh-opts $@args external ssh $@ssh-opts $@args
} catch e { } catch e {
set ssh-ret = $e[reason][exit-status] set ssh-ret = $e[reason][exit-status]
} }
set-env TERM $ssh-original-term
} else {
try {
external ssh $@ssh-opts $@args
} catch e {
set ssh-ret = $e[reason][exit-status]
}
}
# Restore original environment variables # Restore original environment variables
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) { if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {

View File

@ -86,124 +86,110 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
end end
end end
# SSH Integration for Fish Shell # SSH Integration
if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES"; or string match -q '*ssh-terminfo*' -- "$GHOSTTY_SHELL_FEATURES" if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES"; or string match -q '*ssh-terminfo*' -- "$GHOSTTY_SHELL_FEATURES"
set -g GHOSTTY_SSH_CACHE_TIMEOUT (test -n "$GHOSTTY_SSH_CACHE_TIMEOUT"; and echo $GHOSTTY_SSH_CACHE_TIMEOUT; or echo 5)
set -g GHOSTTY_SSH_CHECK_TIMEOUT (test -n "$GHOSTTY_SSH_CHECK_TIMEOUT"; and echo $GHOSTTY_SSH_CHECK_TIMEOUT; or echo 3)
# SSH wrapper that preserves Ghostty features across remote connections
function ssh --wraps=ssh --description "SSH wrapper with Ghostty integration" function ssh --wraps=ssh --description "SSH wrapper with Ghostty integration"
set -l ssh_env set -l ssh_env
set -l ssh_opts set -l ssh_opts
set -l ssh_exported_vars
# Configure environment variables for remote session # Configure environment variables for remote session
if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES" if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES"
set -l ssh_env_vars \ set -a ssh_opts -o "SetEnv COLORTERM=truecolor"
"COLORTERM=truecolor" \
"TERM_PROGRAM=ghostty" if set -q TERM_PROGRAM
set -a ssh_exported_vars "TERM_PROGRAM=$TERM_PROGRAM"
else
set -a ssh_exported_vars "TERM_PROGRAM"
end
set -gx TERM_PROGRAM ghostty
set -a ssh_opts -o "SendEnv TERM_PROGRAM"
if test -n "$TERM_PROGRAM_VERSION" if test -n "$TERM_PROGRAM_VERSION"
set -a ssh_env_vars "TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION" if set -q TERM_PROGRAM_VERSION
end set -a ssh_exported_vars "TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION"
# Store original values for restoration
set -l ssh_exported_vars
for ssh_v in $ssh_env_vars
set -l ssh_var_name (string split -m1 '=' -- $ssh_v)[1]
if set -q $ssh_var_name
set -a ssh_exported_vars "$ssh_var_name="(eval echo \$$ssh_var_name)
else else
set -a ssh_exported_vars $ssh_var_name set -a ssh_exported_vars "TERM_PROGRAM_VERSION"
end
set -a ssh_opts -o "SendEnv TERM_PROGRAM_VERSION"
end end
# Export the variable set -a ssh_env "COLORTERM=truecolor"
set -gx (string split -m1 '=' -- $ssh_v) set -a ssh_env "TERM_PROGRAM=ghostty"
if test -n "$TERM_PROGRAM_VERSION"
# Use both SendEnv and SetEnv for maximum compatibility set -a ssh_env "TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION"
set -a ssh_opts -o "SendEnv $ssh_var_name"
set -a ssh_opts -o "SetEnv $ssh_v"
end end
set -a ssh_env $ssh_env_vars
end end
# Install terminfo on remote host if needed # Install terminfo on remote host if needed
if string match -q '*ssh-terminfo*' -- "$GHOSTTY_SHELL_FEATURES" if string match -q '*ssh-terminfo*' -- "$GHOSTTY_SHELL_FEATURES"
set -l ssh_config (command ssh -G $argv 2>/dev/null) set -l ssh_config (command ssh -G $argv 2>/dev/null)
set -l ssh_user (echo $ssh_config | while read -l ssh_key ssh_value set -l ssh_user
test "$ssh_key" = "user"; and echo $ssh_value; and break set -l ssh_hostname
end)
set -l ssh_hostname (echo $ssh_config | while read -l ssh_key ssh_value for line in $ssh_config
test "$ssh_key" = "hostname"; and echo $ssh_value; and break set -l parts (string split ' ' -- $line)
end) if test (count $parts) -ge 2
switch $parts[1]
case user
set ssh_user $parts[2]
case hostname
set ssh_hostname $parts[2]
end
if test -n "$ssh_user"; and test -n "$ssh_hostname"
break
end
end
end
set -l ssh_target "$ssh_user@$ssh_hostname" set -l ssh_target "$ssh_user@$ssh_hostname"
if test -n "$ssh_hostname" if test -n "$ssh_hostname"
# Detect timeout command (BSD compatibility)
set -l ssh_timeout_cmd
if command -v timeout >/dev/null 2>&1
set ssh_timeout_cmd timeout
else if command -v gtimeout >/dev/null 2>&1
set ssh_timeout_cmd gtimeout
end
# Check if terminfo is already cached # Check if terminfo is already cached
set -l ssh_cache_check_success false set -l ssh_cache_check_success false
if command -v ghostty >/dev/null 2>&1 if command -v ghostty >/dev/null 2>&1
if test -n "$ssh_timeout_cmd"
if $ssh_timeout_cmd "$GHOSTTY_SSH_CHECK_TIMEOUT"s ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1
set ssh_cache_check_success true
end
else
if ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1 if ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1
set ssh_cache_check_success true set ssh_cache_check_success true
end end
end end
end
if test "$ssh_cache_check_success" = "true" if test "$ssh_cache_check_success" = "true"
set -a ssh_env TERM=xterm-ghostty set -a ssh_env TERM=xterm-ghostty
else if command -v infocmp >/dev/null 2>&1 else if command -v infocmp >/dev/null 2>&1
# Generate terminfo data (BSD base64 compatibility) if not command -v base64 >/dev/null 2>&1
echo "Warning: base64 command not available for terminfo installation." >&2
set -a ssh_env TERM=xterm-256color
else
set -l ssh_terminfo set -l ssh_terminfo
set -l ssh_base64_decode_cmd
# BSD vs GNU base64 compatibility
if base64 --help 2>&1 | grep -q GNU if base64 --help 2>&1 | grep -q GNU
set ssh_base64_decode_cmd "base64 -d"
set ssh_terminfo (infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 -w0 2>/dev/null) set ssh_terminfo (infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 -w0 2>/dev/null)
else else
set ssh_base64_decode_cmd "base64 -D"
set ssh_terminfo (infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n') set ssh_terminfo (infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
end end
if test -n "$ssh_terminfo" if test -n "$ssh_terminfo"
echo "Setting up Ghostty terminfo on remote host..." >&2 echo "Setting up Ghostty terminfo on $ssh_hostname..." >&2
set -l ssh_cpath_dir (mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null; or echo "/tmp/ghostty-ssh-$ssh_user."(random)) set -l ssh_cpath_dir (mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null; or echo "/tmp/ghostty-ssh-$ssh_user."(random))
set -l ssh_cpath "$ssh_cpath_dir/socket" set -l ssh_cpath "$ssh_cpath_dir/socket"
set -l ssh_base64_decode_cmd
if base64 --help 2>&1 | grep -q GNU
set ssh_base64_decode_cmd "base64 -d"
else
set ssh_base64_decode_cmd "base64 -D"
end
if echo "$ssh_terminfo" | eval $ssh_base64_decode_cmd | command ssh $ssh_opts -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s $argv ' if echo "$ssh_terminfo" | eval $ssh_base64_decode_cmd | command ssh $ssh_opts -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s $argv '
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0 infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1 command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0 mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1 exit 1
' 2>/dev/null ' 2>/dev/null
echo "Terminfo setup complete." >&2 echo "Terminfo setup complete on $ssh_hostname." >&2
set -a ssh_env TERM=xterm-ghostty set -a ssh_env TERM=xterm-ghostty
set -a ssh_opts -o "ControlPath=$ssh_cpath" set -a ssh_opts -o "ControlPath=$ssh_cpath"
# Cache successful installation # Cache successful installation
if test -n "$ssh_target"; and command -v ghostty >/dev/null 2>&1 if test -n "$ssh_target"; and command -v ghostty >/dev/null 2>&1
fish -c " fish -c "ghostty +ssh-cache --add='$ssh_target' >/dev/null 2>&1; or true" &
if test -n '$ssh_timeout_cmd'
$ssh_timeout_cmd '$GHOSTTY_SSH_CACHE_TIMEOUT's ghostty +ssh-cache --add='$ssh_target' >/dev/null 2>&1; or true
else
ghostty +ssh-cache --add='$ssh_target' >/dev/null 2>&1; or true
end
" &
end end
else else
echo "Warning: Failed to install terminfo." >&2 echo "Warning: Failed to install terminfo." >&2
@ -213,6 +199,7 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
echo "Warning: Could not generate terminfo data." >&2 echo "Warning: Could not generate terminfo data." >&2
set -a ssh_env TERM=xterm-256color set -a ssh_env TERM=xterm-256color
end end
end
else else
echo "Warning: ghostty command not available for cache management." >&2 echo "Warning: ghostty command not available for cache management." >&2
set -a ssh_env TERM=xterm-256color set -a ssh_env TERM=xterm-256color
@ -224,22 +211,31 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
end end
end end
# Ensure TERM is set when using ssh-env feature # Execute SSH with environment handling
if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES" set -l ssh_term_override
set -l ssh_term_set false
for ssh_v in $ssh_env for ssh_v in $ssh_env
if string match -q 'TERM=*' -- $ssh_v if string match -q 'TERM=*' -- $ssh_v
set ssh_term_set true set ssh_term_override (string replace 'TERM=' '' -- $ssh_v)
break break
end end
end end
if test "$ssh_term_set" = "false"; and test "$TERM" = "xterm-ghostty"
if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES"; and test -z "$ssh_term_override"
set -a ssh_env TERM=xterm-256color set -a ssh_env TERM=xterm-256color
end set ssh_term_override xterm-256color
end end
set -l ssh_ret
if test -n "$ssh_term_override"
set -l ssh_original_term "$TERM"
set -gx TERM "$ssh_term_override"
command ssh $ssh_opts $argv command ssh $ssh_opts $argv
set -l ssh_ret $status set ssh_ret $status
set -gx TERM "$ssh_original_term"
else
command ssh $ssh_opts $argv
set ssh_ret $status
end
# Restore original environment variables # Restore original environment variables
if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES" if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES"

View File

@ -246,121 +246,104 @@ _ghostty_deferred_init() {
# SSH Integration # SSH Integration
if [[ "$GHOSTTY_SHELL_FEATURES" =~ (ssh-env|ssh-terminfo) ]]; then 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() { ssh() {
emulate -L zsh emulate -L zsh
setopt local_options no_glob_subst 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 # Configure environment variables for remote session
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then 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" "COLORTERM=truecolor"
"TERM_PROGRAM=ghostty" "TERM_PROGRAM=ghostty"
) )
[[ -n "$TERM_PROGRAM_VERSION" ]] && ssh_env_vars+=("TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION") [[ -n "$TERM_PROGRAM_VERSION" ]] && ssh_env+=("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[@]}")
fi fi
# Install terminfo on remote host if needed # Install terminfo on remote host if needed
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then 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_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 while IFS=' ' read -r ssh_key ssh_value; do
done) case "$ssh_key" in
ssh_hostname=$(printf '%s\n' "${(@f)ssh_config}" | while IFS=' ' read -r ssh_key ssh_value; do user) ssh_user="$ssh_value" ;;
[[ "$ssh_key" == "hostname" ]] && printf '%s\n' "$ssh_value" && break hostname) ssh_hostname="$ssh_value" ;;
done) esac
[[ -n "$ssh_user" && -n "$ssh_hostname" ]] && break
done <<< "$ssh_config"
ssh_target="${ssh_user}@${ssh_hostname}" ssh_target="${ssh_user}@${ssh_hostname}"
if [[ -n "$ssh_hostname" ]]; then 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 # Check if terminfo is already cached
local ssh_cache_check_success=false local ssh_cache_check_success=false
if (( $+commands[ghostty] )); then 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 ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1 && ssh_cache_check_success=true
fi fi
fi
if [[ "$ssh_cache_check_success" == "true" ]]; then if [[ "$ssh_cache_check_success" == "true" ]]; then
ssh_env+=(TERM=xterm-ghostty) ssh_env+=(TERM=xterm-ghostty)
elif (( $+commands[infocmp] )); then elif (( $+commands[infocmp] )); then
local ssh_terminfo if ! (( $+commands[base64] )); then
print "Warning: base64 command not available for terminfo installation." >&2
ssh_env+=(TERM=xterm-256color)
else
local ssh_terminfo ssh_base64_decode_cmd
# Generate terminfo data (BSD base64 compatibility) # BSD vs GNU base64 compatibility
if base64 --help 2>&1 | grep -q GNU; then 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) ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 -w0 2>/dev/null)
else 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') ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
fi fi
if [[ -n "$ssh_terminfo" ]]; then if [[ -n "$ssh_terminfo" ]]; then
print "Setting up Ghostty terminfo on remote host..." >&2 print "Setting up Ghostty terminfo on $ssh_hostname..." >&2
local ssh_cpath_dir ssh_cpath 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_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" ssh_cpath="$ssh_cpath_dir/socket"
local ssh_base64_decode_cmd
if base64 --help 2>&1 | grep -q GNU; then
ssh_base64_decode_cmd="base64 -d"
else
ssh_base64_decode_cmd="base64 -D"
fi
if print "$ssh_terminfo" | $ssh_base64_decode_cmd | command ssh "${ssh_opts[@]}" -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" ' 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 infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
command -v tic >/dev/null 2>&1 || exit 1 command -v tic >/dev/null 2>&1 || exit 1
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0 mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1 exit 1
' 2>/dev/null; then ' 2>/dev/null; then
print "Terminfo setup complete." >&2 print "Terminfo setup complete on $ssh_hostname." >&2
ssh_env+=(TERM=xterm-ghostty) ssh_env+=(TERM=xterm-ghostty)
ssh_opts+=(-o "ControlPath=$ssh_cpath") ssh_opts+=(-o "ControlPath=$ssh_cpath")
# Cache successful installation # Cache successful installation
if [[ -n "$ssh_target" ]] && (( $+commands[ghostty] )); then 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
ghostty +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true ghostty +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true
fi
} &! } &!
fi fi
else else
@ -371,6 +354,7 @@ _ghostty_deferred_init() {
print "Warning: Could not generate terminfo data." >&2 print "Warning: Could not generate terminfo data." >&2
ssh_env+=(TERM=xterm-256color) ssh_env+=(TERM=xterm-256color)
fi fi
fi
else else
print "Warning: ghostty command not available for cache management." >&2 print "Warning: ghostty command not available for cache management." >&2
ssh_env+=(TERM=xterm-256color) ssh_env+=(TERM=xterm-256color)
@ -380,21 +364,35 @@ _ghostty_deferred_init() {
fi fi
fi fi
# Ensure TERM is set when using ssh-env feature # Execute SSH with environment handling
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then local ssh_term_override=""
local ssh_term_set=false ssh_v local ssh_v
for ssh_v in "${ssh_env[@]}"; do for ssh_v in "${ssh_env[@]}"; do
[[ "$ssh_v" =~ ^TERM= ]] && ssh_term_set=true && break if [[ "$ssh_v" =~ ^TERM=(.*)$ ]]; then
ssh_term_override="${match[1]}"
break
fi
done done
[[ "$ssh_term_set" == "false" && "$TERM" == "xterm-ghostty" ]] && ssh_env+=(TERM=xterm-256color)
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env && -z "$ssh_term_override" ]]; then
ssh_env+=(TERM=xterm-256color)
ssh_term_override="xterm-256color"
fi fi
local ssh_ret
if [[ -n "$ssh_term_override" ]]; then
local ssh_original_term="$TERM"
export TERM="$ssh_term_override"
command ssh "${ssh_opts[@]}" "$@" command ssh "${ssh_opts[@]}" "$@"
local ssh_ret=$? ssh_ret=$?
export TERM="$ssh_original_term"
else
command ssh "${ssh_opts[@]}" "$@"
ssh_ret=$?
fi
# Restore original environment variables # Restore original environment variables
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
local ssh_v
for ssh_v in "${ssh_exported_vars[@]}"; do for ssh_v in "${ssh_exported_vars[@]}"; do
if [[ "$ssh_v" == *=* ]]; then if [[ "$ssh_v" == *=* ]]; then
export "${ssh_v}" export "${ssh_v}"