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,131 +97,116 @@ 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 ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1 && ssh_cache_check_success=true
$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
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
# Generate terminfo data (BSD base64 compatibility) ssh_env+=(TERM=xterm-256color)
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)
else else
ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n') builtin local ssh_terminfo ssh_base64_decode_cmd
fi
if [[ -n "$ssh_terminfo" ]]; then # BSD vs GNU base64 compatibility
builtin echo "Setting up Ghostty terminfo on remote host..." >&2
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="$ssh_cpath_dir/socket"
local ssh_base64_decode_cmd
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_base64_decode_cmd="base64 -d"
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_base64_decode_cmd="base64 -D"
ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
fi 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 [[ -n "$ssh_terminfo" ]]; then
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0 builtin echo "Setting up Ghostty terminfo on $ssh_hostname..." >&2
command -v tic >/dev/null 2>&1 || exit 1 builtin local ssh_cpath_dir ssh_cpath
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
exit 1
' 2>/dev/null; then
builtin echo "Terminfo setup complete." >&2
ssh_env+=(TERM=xterm-ghostty)
ssh_opts+=(-o "ControlPath=$ssh_cpath")
# Cache successful installation ssh_cpath_dir=$(mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null) || ssh_cpath_dir="/tmp/ghostty-ssh-$ssh_user.$$"
if [[ -n "$ssh_target" ]] && command -v ghostty >/dev/null 2>&1; then ssh_cpath="$ssh_cpath_dir/socket"
(
set +m 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
if [[ -n "$ssh_timeout_cmd" ]]; then command -v tic >/dev/null 2>&1 || exit 1
$ssh_timeout_cmd "${GHOSTTY_SSH_CACHE_TIMEOUT}s" ghostty +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
else exit 1
' 2>/dev/null; then
builtin echo "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" ]] && builtin command -v ghostty >/dev/null 2>&1; then
(
set +m
{
ghostty +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true ghostty +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true
fi } &
} & )
) fi
else
builtin echo "Warning: Failed to install terminfo." >&2
ssh_env+=(TERM=xterm-256color)
fi fi
else else
builtin echo "Warning: Failed to install terminfo." >&2 builtin echo "Warning: Could not generate terminfo data." >&2
ssh_env+=(TERM=xterm-256color) ssh_env+=(TERM=xterm-256color)
fi fi
else
builtin echo "Warning: Could not generate terminfo data." >&2
ssh_env+=(TERM=xterm-256color)
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
@ -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_override="${BASH_REMATCH[1]}"
ssh_term_set=true break
break
fi
done
if [[ "$ssh_term_set" == "false" && "$TERM" == "xterm-ghostty" ]]; then
ssh_env+=(TERM=xterm-256color)
fi fi
done
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env && -z "$ssh_term_override" ]]; then
ssh_env+=(TERM=xterm-256color)
ssh_term_override="xterm-256color"
fi fi
builtin command ssh "${ssh_opts[@]}" "$@" if [[ -n "$ssh_term_override" ]]; then
local ssh_ret=$? builtin local ssh_original_term="$TERM"
builtin export TERM="$ssh_term_override"
builtin command ssh "${ssh_opts[@]}" "$@"
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
var ssh-var-parts = (str:split &max=2 = $ssh-v)
set-env $ssh-var-parts[0] $ssh-var-parts[1]
# Use both SendEnv and SetEnv for maximum compatibility
set ssh-opts = [$@ssh-opts -o "SendEnv "$ssh-var-name]
set ssh-opts = [$@ssh-opts -o "SetEnv "$ssh-v]
} }
set ssh-env = [$@ssh-env $@ssh-env-vars] set ssh-env = [
"COLORTERM=truecolor"
"TERM_PROGRAM=ghostty"
]
if (has-env TERM_PROGRAM_VERSION) {
set ssh-env = [$@ssh-env "TERM_PROGRAM_VERSION="$E:TERM_PROGRAM_VERSION]
}
} }
# Install terminfo on remote host if needed # Install terminfo on remote host if needed
@ -160,52 +151,28 @@
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) {
set ssh-user = $parts[1] if (eq $parts[0] user) {
} set ssh-user = $parts[1]
if (and (> (count $parts) 1) (eq $parts[0] hostname)) { } elif (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 { try {
external ghostty --help >/dev/null 2>&1 external ghostty +ssh-cache --host=$ssh-target >/dev/null 2>&1
if (not-eq $ssh-timeout-cmd "") { set ssh-cache-check-success = $true
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 {
external ghostty +ssh-cache --host=$ssh-target >/dev/null 2>&1
set ssh-cache-check-success = $true
} catch {
# cache check failed
}
}
} catch { } catch {
# ghostty not available # cache check failed
} }
if $ssh-cache-check-success { if $ssh-cache-check-success {
@ -213,74 +180,68 @@
} else { } else {
try { try {
external infocmp --help >/dev/null 2>&1 external infocmp --help >/dev/null 2>&1
# Generate terminfo data (BSD base64 compatibility)
var ssh-terminfo = ""
try { try {
var base64-help = (external base64 --help 2>&1 | slurp) external base64 --help >/dev/null 2>&1
if (str:contains $base64-help GNU) {
set ssh-terminfo = (external infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | external base64 -w0 2>/dev/null | slurp)
} else {
set ssh-terminfo = (external infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | external base64 2>/dev/null | external tr -d '\n' | slurp)
}
} catch {
set ssh-terminfo = ""
}
if (not-eq $ssh-terminfo "") {
echo "Setting up Ghostty terminfo on remote host..." >&2
var ssh-cpath-dir = ""
try {
set ssh-cpath-dir = (external mktemp -d "/tmp/ghostty-ssh-"$ssh-user".XXXXXX" 2>/dev/null | slurp)
} catch {
set ssh-cpath-dir = "/tmp/ghostty-ssh-"$ssh-user"."(randint 10000 99999)
}
var ssh-cpath = $ssh-cpath-dir"/socket"
# Generate terminfo data (BSD base64 compatibility)
var ssh-terminfo = ""
var ssh-base64-decode-cmd = "" 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-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)
} else { } else {
set ssh-base64-decode-cmd = "base64 -D" 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)
} }
} catch { } catch {
set ssh-base64-decode-cmd = "base64 -d" set ssh-terminfo = ""
} }
var terminfo-install-success = $false if (not-eq $ssh-terminfo "") {
try { echo "Setting up Ghostty terminfo on "$ssh-hostname"..." >&2
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 ' var ssh-cpath-dir = ""
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0 try {
command -v tic >/dev/null 2>&1 || exit 1 set ssh-cpath-dir = (external mktemp -d "/tmp/ghostty-ssh-"$ssh-user".XXXXXX" 2>/dev/null | slurp)
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0 } catch {
exit 1 set ssh-cpath-dir = "/tmp/ghostty-ssh-"$ssh-user"."(randint 10000 99999)
' >/dev/null 2>&1 }
set terminfo-install-success = $true var ssh-cpath = $ssh-cpath-dir"/socket"
} catch {
set terminfo-install-success = $false
}
if $terminfo-install-success { var terminfo-install-success = $false
echo "Terminfo setup complete." >&2 try {
set ssh-env = [$@ssh-env TERM=xterm-ghostty] 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 '
set ssh-opts = [$@ssh-opts -o ControlPath=$ssh-cpath] 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
' >/dev/null 2>&1
set terminfo-install-success = $true
} catch {
set terminfo-install-success = $false
}
# Cache successful installation if $terminfo-install-success {
if (and (not-eq $ssh-target "") (has-external ghostty)) { echo "Terminfo setup complete on "$ssh-hostname"." >&2
if (not-eq $ssh-timeout-cmd "") { set ssh-env = [$@ssh-env TERM=xterm-ghostty]
external $ssh-timeout-cmd $GHOSTTY_SSH_CACHE_TIMEOUT"s" ghostty +ssh-cache --add=$ssh-target >/dev/null 2>&1 & set ssh-opts = [$@ssh-opts -o ControlPath=$ssh-cpath]
} else {
# Cache successful installation
if (and (not-eq $ssh-target "") (has-external ghostty)) {
external ghostty +ssh-cache --add=$ssh-target >/dev/null 2>&1 & external ghostty +ssh-cache --add=$ssh-target >/dev/null 2>&1 &
} }
} else {
echo "Warning: Failed to install terminfo." >&2
set ssh-env = [$@ssh-env TERM=xterm-256color]
} }
} else { } else {
echo "Warning: Failed to install terminfo." >&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]
} }
} else { } catch {
echo "Warning: Could not generate terminfo data." >&2 echo "Warning: base64 command not available for terminfo installation." >&2
set ssh-env = [$@ssh-env TERM=xterm-256color] set ssh-env = [$@ssh-env TERM=xterm-256color]
} }
} catch { } catch {
@ -295,25 +256,36 @@
} }
} }
# 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-override = (str:trim-prefix $ssh-v TERM=)
set ssh-term-set = $true break
break
}
}
if (and (not $ssh-term-set) (eq $E:TERM xterm-ghostty)) {
set ssh-env = [$@ssh-env TERM=xterm-256color]
} }
} }
if (and (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) (eq $ssh-term-override "")) {
set ssh-env = [$@ssh-env TERM=xterm-256color]
set ssh-term-override = xterm-256color
}
var ssh-ret = 0 var ssh-ret = 0
try { if (not-eq $ssh-term-override "") {
external ssh $@ssh-opts $@args var ssh-original-term = $E:TERM
} catch e { set-env TERM $ssh-term-override
set ssh-ret = $e[reason][exit-status] try {
external ssh $@ssh-opts $@args
} catch e {
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

View File

@ -86,132 +86,119 @@ 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 end
set -a ssh_opts -o "SendEnv TERM_PROGRAM_VERSION"
# Export the variable
set -gx (string split -m1 '=' -- $ssh_v)
# Use both SendEnv and SetEnv for maximum compatibility
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 set -a ssh_env "COLORTERM=truecolor"
set -a ssh_env "TERM_PROGRAM=ghostty"
if test -n "$TERM_PROGRAM_VERSION"
set -a ssh_env "TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION"
end
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 ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1
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
set ssh_cache_check_success true
end
else
if ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1
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
set -l ssh_terminfo echo "Warning: base64 command not available for terminfo installation." >&2
if base64 --help 2>&1 | grep -q GNU set -a ssh_env TERM=xterm-256color
set ssh_terminfo (infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 -w0 2>/dev/null)
else else
set ssh_terminfo (infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n') set -l ssh_terminfo
end
if test -n "$ssh_terminfo"
echo "Setting up Ghostty terminfo on remote host..." >&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 "$ssh_cpath_dir/socket"
set -l ssh_base64_decode_cmd 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_base64_decode_cmd "base64 -d"
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_base64_decode_cmd "base64 -D"
set ssh_terminfo (infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
end 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 test -n "$ssh_terminfo"
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0 echo "Setting up Ghostty terminfo on $ssh_hostname..." >&2
command -v tic >/dev/null 2>&1 || exit 1 set -l ssh_cpath_dir (mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null; or echo "/tmp/ghostty-ssh-$ssh_user."(random))
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0 set -l ssh_cpath "$ssh_cpath_dir/socket"
exit 1
' 2>/dev/null
echo "Terminfo setup complete." >&2
set -a ssh_env TERM=xterm-ghostty
set -a ssh_opts -o "ControlPath=$ssh_cpath"
# Cache successful installation 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 test -n "$ssh_target"; and command -v ghostty >/dev/null 2>&1 infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
fish -c " command -v tic >/dev/null 2>&1 || exit 1
if test -n '$ssh_timeout_cmd' mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
$ssh_timeout_cmd '$GHOSTTY_SSH_CACHE_TIMEOUT's ghostty +ssh-cache --add='$ssh_target' >/dev/null 2>&1; or true exit 1
else ' 2>/dev/null
ghostty +ssh-cache --add='$ssh_target' >/dev/null 2>&1; or true echo "Terminfo setup complete on $ssh_hostname." >&2
end set -a ssh_env TERM=xterm-ghostty
" & set -a ssh_opts -o "ControlPath=$ssh_cpath"
# Cache successful installation
if test -n "$ssh_target"; and command -v ghostty >/dev/null 2>&1
fish -c "ghostty +ssh-cache --add='$ssh_target' >/dev/null 2>&1; or true" &
end
else
echo "Warning: Failed to install terminfo." >&2
set -a ssh_env TERM=xterm-256color
end end
else else
echo "Warning: Failed to install terminfo." >&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
else
echo "Warning: Could not generate terminfo data." >&2
set -a ssh_env TERM=xterm-256color
end end
else else
echo "Warning: ghostty command not available for cache management." >&2 echo "Warning: ghostty command not available for cache management." >&2
@ -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_override (string replace 'TERM=' '' -- $ssh_v)
set ssh_term_set true break
break
end
end
if test "$ssh_term_set" = "false"; and test "$TERM" = "xterm-ghostty"
set -a ssh_env TERM=xterm-256color
end end
end end
command ssh $ssh_opts $argv if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES"; and test -z "$ssh_term_override"
set -l ssh_ret $status set -a ssh_env TERM=xterm-256color
set ssh_term_override xterm-256color
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
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

@ -244,132 +244,116 @@ _ghostty_deferred_init() {
} }
fi fi
# 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 ghostty +ssh-cache --host="$ssh_target" >/dev/null 2>&1 && ssh_cache_check_success=true
$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
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
# Generate terminfo data (BSD base64 compatibility) ssh_env+=(TERM=xterm-256color)
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)
else else
ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n') local ssh_terminfo ssh_base64_decode_cmd
fi
if [[ -n "$ssh_terminfo" ]]; then # BSD vs GNU base64 compatibility
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
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_base64_decode_cmd="base64 -d"
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_base64_decode_cmd="base64 -D"
ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
fi fi
if print "$ssh_terminfo" | $ssh_base64_decode_cmd | command ssh "${ssh_opts[@]}" -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" ' if [[ -n "$ssh_terminfo" ]]; then
infocmp xterm-ghostty >/dev/null 2>&1 && exit 0 print "Setting up Ghostty terminfo on $ssh_hostname..." >&2
command -v tic >/dev/null 2>&1 || exit 1 local ssh_cpath_dir ssh_cpath
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")
# Cache successful installation ssh_cpath_dir=$(mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null) || ssh_cpath_dir="/tmp/ghostty-ssh-$ssh_user.$$"
if [[ -n "$ssh_target" ]] && (( $+commands[ghostty] )); then ssh_cpath="$ssh_cpath_dir/socket"
{
if [[ -n "$ssh_timeout_cmd" ]]; then if print "$ssh_terminfo" | $ssh_base64_decode_cmd | command ssh "${ssh_opts[@]}" -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" '
$ssh_timeout_cmd "${GHOSTTY_SSH_CACHE_TIMEOUT}s" ghostty +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
else 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 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 fi
else else
print "Warning: Failed to install terminfo." >&2 print "Warning: Could not generate terminfo data." >&2
ssh_env+=(TERM=xterm-256color) ssh_env+=(TERM=xterm-256color)
fi fi
else
print "Warning: Could not generate terminfo data." >&2
ssh_env+=(TERM=xterm-256color)
fi fi
else else
print "Warning: ghostty command not available for cache management." >&2 print "Warning: ghostty command not available for cache management." >&2
@ -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
done ssh_term_override="${match[1]}"
[[ "$ssh_term_set" == "false" && "$TERM" == "xterm-ghostty" ]] && ssh_env+=(TERM=xterm-256color) 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 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 # 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}"