mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-19 22:10:29 +00:00
feat(ssh): rewrite SSH cache system in native Zig
- Eliminates standalone bash dependency - Consolidates `+list-ssh-cache` and `+clear-ssh-cache` actions into single `+ssh-cache` action with args - Structured cache format with timestamps and expiration support - Memory-safe entry handling with proper file locking - Comprehensive hostname validation (IPv4/IPv6/domains) - Atomic updates via temp file + rename - Updated shell integrations for improved cross-platform support and reliability - Cache operations are now unit-testable
This commit is contained in:
@@ -97,73 +97,172 @@ fi
|
||||
|
||||
# SSH Integration
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
|
||||
: "${GHOSTTY_SSH_CACHE_TIMEOUT:=5}"
|
||||
: "${GHOSTTY_SSH_CHECK_TIMEOUT:=3}"
|
||||
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||
readonly _CACHE="${GHOSTTY_RESOURCES_DIR}/shell-integration/shared/ghostty-ssh-cache"
|
||||
fi
|
||||
|
||||
# SSH wrapper
|
||||
# SSH wrapper that preserves Ghostty features across remote connections
|
||||
ssh() {
|
||||
local env=() opts=() ctrl=()
|
||||
local ssh_env=() ssh_opts=()
|
||||
|
||||
# Set up env vars first so terminfo installation inherits them
|
||||
# Configure environment variables for remote session
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||
local vars=(
|
||||
COLORTERM=truecolor
|
||||
TERM_PROGRAM=ghostty
|
||||
${TERM_PROGRAM_VERSION:+TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION}
|
||||
local -a ssh_env_vars=(
|
||||
"COLORTERM=truecolor"
|
||||
"TERM_PROGRAM=ghostty"
|
||||
)
|
||||
for v in "${vars[@]}"; do
|
||||
builtin export "${v?}"
|
||||
opts+=(-o "SendEnv ${v%=*}" -o "SetEnv $v")
|
||||
if [[ -n "$TERM_PROGRAM_VERSION" ]]; then
|
||||
ssh_env_vars+=("TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION")
|
||||
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
|
||||
|
||||
# Install terminfo if needed, reuse control connection for main session
|
||||
# Install terminfo on remote host if needed
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||
# Get target (only when needed for terminfo)
|
||||
builtin local target
|
||||
target=$(builtin command ssh -G "$@" 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
||||
|
||||
if [[ -n "$target" ]] && "$_CACHE" chk "$target"; then
|
||||
env+=(TERM=xterm-ghostty)
|
||||
elif builtin command -v infocmp >/dev/null 2>&1; then
|
||||
builtin local tinfo
|
||||
tinfo=$(infocmp -x xterm-ghostty 2>/dev/null) || builtin echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||
if [[ -n "$tinfo" ]]; then
|
||||
builtin echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||
builtin local cpath
|
||||
cpath="/tmp/ghostty-ssh-$USER-$RANDOM-$(date +%s)"
|
||||
case $(builtin echo "$tinfo" | builtin command ssh "${opts[@]}" -o ControlMaster=yes -o ControlPath="$cpath" -o ControlPersist=60s "$@" '
|
||||
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
||||
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||
') in
|
||||
OK)
|
||||
builtin echo "Terminfo setup complete." >&2
|
||||
[[ -n "$target" ]] && "$_CACHE" add "$target"
|
||||
env+=(TERM=xterm-ghostty)
|
||||
ctrl+=(-o "ControlPath=$cpath")
|
||||
;;
|
||||
*) builtin echo "Warning: Failed to install terminfo." >&2 ;;
|
||||
esac
|
||||
builtin local ssh_config ssh_user ssh_hostname
|
||||
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
|
||||
done)
|
||||
ssh_hostname=$(echo "$ssh_config" | while IFS=' ' read -r ssh_key ssh_value; do
|
||||
[[ "$ssh_key" == "hostname" ]] && echo "$ssh_value" && break
|
||||
done)
|
||||
ssh_target="${ssh_user}@${ssh_hostname}"
|
||||
|
||||
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
|
||||
local ssh_cache_check_success=false
|
||||
if 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
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$ssh_cache_check_success" == "true" ]]; then
|
||||
ssh_env+=(TERM=xterm-ghostty)
|
||||
elif builtin command -v infocmp >/dev/null 2>&1; then
|
||||
builtin 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)
|
||||
else
|
||||
ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
|
||||
fi
|
||||
|
||||
if [[ -n "$ssh_terminfo" ]]; then
|
||||
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
|
||||
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 "$@" '
|
||||
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
|
||||
builtin echo "Terminfo setup complete." >&2
|
||||
ssh_env+=(TERM=xterm-ghostty)
|
||||
ssh_opts+=(-o "ControlPath=$ssh_cpath")
|
||||
|
||||
# Cache successful installation
|
||||
if [[ -n "$ssh_target" ]] && command -v ghostty >/dev/null 2>&1; then
|
||||
(
|
||||
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
|
||||
fi
|
||||
} &
|
||||
)
|
||||
fi
|
||||
else
|
||||
builtin echo "Warning: Failed to install terminfo." >&2
|
||||
ssh_env+=(TERM=xterm-256color)
|
||||
fi
|
||||
else
|
||||
builtin echo "Warning: Could not generate terminfo data." >&2
|
||||
ssh_env+=(TERM=xterm-256color)
|
||||
fi
|
||||
else
|
||||
builtin echo "Warning: ghostty command not available for cache management." >&2
|
||||
ssh_env+=(TERM=xterm-256color)
|
||||
fi
|
||||
else
|
||||
builtin echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||
ssh_env+=(TERM=xterm-256color)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fallback TERM only if terminfo didn't set it
|
||||
# Ensure TERM is set when using ssh-env feature
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||
[[ $TERM == xterm-ghostty && ! " ${env[*]} " =~ " TERM=" ]] && env+=(TERM=xterm-256color)
|
||||
local ssh_term_set=false
|
||||
for ssh_v in "${ssh_env[@]}"; do
|
||||
if [[ "$ssh_v" =~ ^TERM= ]]; then
|
||||
ssh_term_set=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "$ssh_term_set" == "false" && "$TERM" == "xterm-ghostty" ]]; then
|
||||
ssh_env+=(TERM=xterm-256color)
|
||||
fi
|
||||
fi
|
||||
|
||||
# Execute
|
||||
if [[ ${#env[@]} -gt 0 ]]; then
|
||||
env "${env[@]}" ssh "${opts[@]}" "${ctrl[@]}" "$@"
|
||||
else
|
||||
builtin command ssh "${opts[@]}" "${ctrl[@]}" "$@"
|
||||
builtin command ssh "${ssh_opts[@]}" "$@"
|
||||
local ssh_ret=$?
|
||||
|
||||
# Restore original environment variables
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||
for ssh_v in "${ssh_exported_vars[@]}"; do
|
||||
if [[ "$ssh_v" == *=* ]]; then
|
||||
builtin export "${ssh_v?}"
|
||||
else
|
||||
builtin unset "${ssh_v}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
return $ssh_ret
|
||||
}
|
||||
fi
|
||||
|
||||
|
||||
@@ -100,91 +100,238 @@
|
||||
|
||||
# SSH Integration
|
||||
use str
|
||||
use re
|
||||
|
||||
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) or (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 })
|
||||
|
||||
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-terminfo) {
|
||||
var _CACHE = $E:GHOSTTY_RESOURCES_DIR/shell-integration/shared/ghostty-ssh-cache
|
||||
}
|
||||
# SSH wrapper that preserves Ghostty features across remote connections
|
||||
fn ssh {|@args|
|
||||
var ssh-env = []
|
||||
var ssh-opts = []
|
||||
|
||||
# SSH wrapper
|
||||
fn ssh {|@args|
|
||||
var env = []
|
||||
var opts = []
|
||||
var ctrl = []
|
||||
# Configure environment variables for remote session
|
||||
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {
|
||||
var ssh-env-vars = [
|
||||
COLORTERM=truecolor
|
||||
TERM_PROGRAM=ghostty
|
||||
]
|
||||
|
||||
# Set up env vars first so terminfo installation inherits them
|
||||
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {
|
||||
var vars = [
|
||||
COLORTERM=truecolor
|
||||
TERM_PROGRAM=ghostty
|
||||
]
|
||||
if (not-eq $E:TERM_PROGRAM_VERSION '') {
|
||||
set vars = [$@vars TERM_PROGRAM_VERSION=$E:TERM_PROGRAM_VERSION]
|
||||
}
|
||||
if (has-env TERM_PROGRAM_VERSION) {
|
||||
set ssh-env-vars = [$@ssh-env-vars TERM_PROGRAM_VERSION=$E:TERM_PROGRAM_VERSION]
|
||||
}
|
||||
|
||||
for v $vars {
|
||||
set-env (str:split = $v | take 1) (str:split = $v | drop 1 | str:join =)
|
||||
var varname = (str:split = $v | take 1)
|
||||
set opts = [$@opts -o 'SendEnv '$varname -o 'SetEnv '$v]
|
||||
}
|
||||
}
|
||||
|
||||
# Install terminfo if needed, reuse control connection for main session
|
||||
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-terminfo) {
|
||||
# Get target
|
||||
var target = ''
|
||||
try {
|
||||
set target = (e:ssh -G $@args 2>/dev/null | e:awk '/^(user|hostname) /{print $2}' | e:paste -sd'@')
|
||||
} catch { }
|
||||
|
||||
if (and (not-eq $target '') ($_CACHE chk $target)) {
|
||||
set env = [$@env TERM=xterm-ghostty]
|
||||
} elif (has-external infocmp) {
|
||||
var tinfo = ''
|
||||
try {
|
||||
set tinfo = (e:infocmp -x xterm-ghostty 2>/dev/null)
|
||||
} catch {
|
||||
echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||
}
|
||||
|
||||
if (not-eq $tinfo '') {
|
||||
echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||
var cpath = '/tmp/ghostty-ssh-'$E:USER'-'(randint 0 32767)'-'(date +%s)
|
||||
var result = (echo $tinfo | e:ssh $@opts -o ControlMaster=yes -o ControlPath=$cpath -o ControlPersist=60s $@args '
|
||||
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
||||
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||
')
|
||||
# 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 (eq $result OK) {
|
||||
echo "Terminfo setup complete." >&2
|
||||
if (not-eq $target '') { $_CACHE add $target }
|
||||
set env = [$@env TERM=xterm-ghostty]
|
||||
set ctrl = [$@ctrl -o ControlPath=$cpath]
|
||||
} else {
|
||||
echo "Warning: Failed to install terminfo." >&2
|
||||
}
|
||||
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 {
|
||||
set ssh-exported-vars = [$@ssh-exported-vars $ssh-var-name]
|
||||
}
|
||||
|
||||
# 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]
|
||||
}
|
||||
} else {
|
||||
echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
||||
}
|
||||
}
|
||||
|
||||
# Fallback TERM only if terminfo didn't set it
|
||||
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {
|
||||
if (and (eq $E:TERM xterm-ghostty) (not (str:contains (str:join ' ' $env) 'TERM='))) {
|
||||
set env = [$@env TERM=xterm-256color]
|
||||
}
|
||||
}
|
||||
# Install terminfo on remote host if needed
|
||||
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-terminfo) {
|
||||
var ssh-config = ""
|
||||
try {
|
||||
set ssh-config = (external ssh -G $@args 2>/dev/null | slurp)
|
||||
} catch {
|
||||
set ssh-config = ""
|
||||
}
|
||||
|
||||
# Execute
|
||||
if (> (count $env) 0) {
|
||||
e:env $@env e:ssh $@opts $@ctrl $@args
|
||||
} else {
|
||||
e:ssh $@opts $@ctrl $@args
|
||||
var ssh-user = ""
|
||||
var ssh-hostname = ""
|
||||
|
||||
for line (str:split "\n" $ssh-config) {
|
||||
var parts = (str:split " " $line)
|
||||
if (and (> (count $parts) 1) (eq $parts[0] user)) {
|
||||
set ssh-user = $parts[1]
|
||||
}
|
||||
if (and (> (count $parts) 1) (eq $parts[0] hostname)) {
|
||||
set ssh-hostname = $parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
var ssh-target = $ssh-user"@"$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
|
||||
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 {
|
||||
external ghostty +ssh-cache --host=$ssh-target >/dev/null 2>&1
|
||||
set ssh-cache-check-success = $true
|
||||
} catch {
|
||||
# cache check failed
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
# ghostty not available
|
||||
}
|
||||
|
||||
if $ssh-cache-check-success {
|
||||
set ssh-env = [$@ssh-env TERM=xterm-ghostty]
|
||||
} else {
|
||||
try {
|
||||
external infocmp --help >/dev/null 2>&1
|
||||
|
||||
# Generate terminfo data (BSD base64 compatibility)
|
||||
var ssh-terminfo = ""
|
||||
try {
|
||||
var base64-help = (external base64 --help 2>&1 | slurp)
|
||||
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"
|
||||
|
||||
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
|
||||
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 '
|
||||
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
|
||||
}
|
||||
|
||||
if $terminfo-install-success {
|
||||
echo "Terminfo setup complete." >&2
|
||||
set ssh-env = [$@ssh-env TERM=xterm-ghostty]
|
||||
set ssh-opts = [$@ssh-opts -o ControlPath=$ssh-cpath]
|
||||
|
||||
# Cache successful installation
|
||||
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 &
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "Warning: Failed to install terminfo." >&2
|
||||
set ssh-env = [$@ssh-env TERM=xterm-256color]
|
||||
}
|
||||
} else {
|
||||
echo "Warning: Could not generate terminfo data." >&2
|
||||
set ssh-env = [$@ssh-env TERM=xterm-256color]
|
||||
}
|
||||
} catch {
|
||||
echo "Warning: ghostty command not available for cache management." >&2
|
||||
set ssh-env = [$@ssh-env TERM=xterm-256color]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {
|
||||
set ssh-env = [$@ssh-env TERM=xterm-256color]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Ensure TERM is set when using ssh-env feature
|
||||
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {
|
||||
var ssh-term-set = $false
|
||||
for ssh-v $ssh-env {
|
||||
if (str:has-prefix $ssh-v TERM=) {
|
||||
set ssh-term-set = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (and (not $ssh-term-set) (eq $E:TERM xterm-ghostty)) {
|
||||
set ssh-env = [$@ssh-env TERM=xterm-256color]
|
||||
}
|
||||
}
|
||||
|
||||
var ssh-ret = 0
|
||||
try {
|
||||
external ssh $@ssh-opts $@args
|
||||
} catch e {
|
||||
set ssh-ret = $e[reason][exit-status]
|
||||
}
|
||||
|
||||
# Restore original environment variables
|
||||
if (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-env) {
|
||||
for ssh-v $ssh-exported-vars {
|
||||
if (str:contains $ssh-v =) {
|
||||
var ssh-var-parts = (str:split &max=2 = $ssh-v)
|
||||
set-env $ssh-var-parts[0] $ssh-var-parts[1]
|
||||
} else {
|
||||
unset-env $ssh-v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (not-eq $ssh-ret 0) {
|
||||
fail ssh-failed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defer {
|
||||
|
||||
@@ -86,88 +86,173 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
|
||||
end
|
||||
end
|
||||
|
||||
# SSH Integration
|
||||
if string match -qr 'ssh-(env|terminfo)' $GHOSTTY_SHELL_FEATURES
|
||||
# SSH Integration for Fish Shell
|
||||
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)
|
||||
|
||||
if string match -q '*ssh-terminfo*' $GHOSTTY_SHELL_FEATURES
|
||||
set -g _CACHE "$GHOSTTY_RESOURCES_DIR/shell-integration/shared/ghostty-ssh-cache"
|
||||
end
|
||||
|
||||
# SSH wrapper
|
||||
function ssh
|
||||
set -l env
|
||||
set -l opts
|
||||
set -l ctrl
|
||||
|
||||
# Set up env vars first so terminfo installation inherits them
|
||||
if string match -q '*ssh-env*' $GHOSTTY_SHELL_FEATURES
|
||||
set -l vars \
|
||||
COLORTERM=truecolor \
|
||||
TERM_PROGRAM=ghostty
|
||||
# SSH wrapper that preserves Ghostty features across remote connections
|
||||
function ssh --wraps=ssh --description "SSH wrapper with Ghostty integration"
|
||||
set -l ssh_env
|
||||
set -l ssh_opts
|
||||
|
||||
# Configure environment variables for remote session
|
||||
if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES"
|
||||
set -l ssh_env_vars \
|
||||
"COLORTERM=truecolor" \
|
||||
"TERM_PROGRAM=ghostty"
|
||||
|
||||
if test -n "$TERM_PROGRAM_VERSION"
|
||||
set -a vars "TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION"
|
||||
set -a ssh_env_vars "TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION"
|
||||
end
|
||||
|
||||
for v in $vars
|
||||
set -l parts (string split = $v)
|
||||
set -gx $parts[1] $parts[2]
|
||||
set -a opts -o "SendEnv $parts[1]" -o "SetEnv $v"
|
||||
end
|
||||
end
|
||||
|
||||
# Install terminfo if needed, reuse control connection for main session
|
||||
if string match -q '*ssh-terminfo*' $GHOSTTY_SHELL_FEATURES
|
||||
# Get target
|
||||
set -l target (command ssh -G $argv 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
||||
|
||||
if test -n "$target" -a ("$_CACHE" chk "$target")
|
||||
set -a env TERM=xterm-ghostty
|
||||
else if command -v infocmp >/dev/null 2>&1
|
||||
set -l tinfo (infocmp -x xterm-ghostty 2>/dev/null)
|
||||
set -l status_code $status
|
||||
|
||||
if test $status_code -ne 0
|
||||
echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||
# 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
|
||||
set -a ssh_exported_vars $ssh_var_name
|
||||
end
|
||||
|
||||
if test -n "$tinfo"
|
||||
echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||
set -l cpath "/tmp/ghostty-ssh-$USER-"(random)"-"(date +%s)
|
||||
set -l result (echo "$tinfo" | command ssh $opts -o ControlMaster=yes -o ControlPath="$cpath" -o ControlPersist=60s $argv '
|
||||
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
||||
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||
')
|
||||
# Export the variable
|
||||
set -gx (string split -m1 '=' -- $ssh_v)
|
||||
|
||||
switch $result
|
||||
case OK
|
||||
echo "Terminfo setup complete." >&2
|
||||
test -n "$target" && "$_CACHE" add "$target"
|
||||
set -a env TERM=xterm-ghostty
|
||||
set -a ctrl -o "ControlPath=$cpath"
|
||||
case '*'
|
||||
echo "Warning: Failed to install terminfo." >&2
|
||||
# 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
|
||||
|
||||
set -a ssh_env $ssh_env_vars
|
||||
end
|
||||
|
||||
# Install terminfo on remote host if needed
|
||||
if string match -q '*ssh-terminfo*' -- "$GHOSTTY_SHELL_FEATURES"
|
||||
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
|
||||
test "$ssh_key" = "user"; and echo $ssh_value; and break
|
||||
end)
|
||||
set -l ssh_hostname (echo $ssh_config | while read -l ssh_key ssh_value
|
||||
test "$ssh_key" = "hostname"; and echo $ssh_value; and break
|
||||
end)
|
||||
set -l ssh_target "$ssh_user@$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
|
||||
set -l ssh_cache_check_success false
|
||||
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
|
||||
set ssh_cache_check_success true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if test "$ssh_cache_check_success" = "true"
|
||||
set -a ssh_env TERM=xterm-ghostty
|
||||
else if command -v infocmp >/dev/null 2>&1
|
||||
# Generate terminfo data (BSD base64 compatibility)
|
||||
set -l ssh_terminfo
|
||||
if base64 --help 2>&1 | grep -q GNU
|
||||
set ssh_terminfo (infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 -w0 2>/dev/null)
|
||||
else
|
||||
set ssh_terminfo (infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
|
||||
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
|
||||
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 '
|
||||
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
|
||||
echo "Terminfo setup complete." >&2
|
||||
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 "
|
||||
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
|
||||
else
|
||||
echo "Warning: Failed to install terminfo." >&2
|
||||
set -a ssh_env TERM=xterm-256color
|
||||
end
|
||||
else
|
||||
echo "Warning: Could not generate terminfo data." >&2
|
||||
set -a ssh_env TERM=xterm-256color
|
||||
end
|
||||
else
|
||||
echo "Warning: ghostty command not available for cache management." >&2
|
||||
set -a ssh_env TERM=xterm-256color
|
||||
end
|
||||
else
|
||||
echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
||||
if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES"
|
||||
set -a ssh_env TERM=xterm-256color
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Fallback TERM only if terminfo didn't set it
|
||||
if string match -q '*ssh-env*' $GHOSTTY_SHELL_FEATURES
|
||||
if test "$TERM" = xterm-ghostty -a ! (string join ' ' $env | string match -q '*TERM=*')
|
||||
set -a env TERM=xterm-256color
|
||||
# Ensure TERM is set when using ssh-env feature
|
||||
if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES"
|
||||
set -l ssh_term_set false
|
||||
for ssh_v in $ssh_env
|
||||
if string match -q 'TERM=*' -- $ssh_v
|
||||
set ssh_term_set true
|
||||
break
|
||||
end
|
||||
end
|
||||
if test "$ssh_term_set" = "false"; and test "$TERM" = "xterm-ghostty"
|
||||
set -a ssh_env TERM=xterm-256color
|
||||
end
|
||||
end
|
||||
|
||||
# Execute
|
||||
if test (count $env) -gt 0
|
||||
env $env command ssh $opts $ctrl $argv
|
||||
else
|
||||
command ssh $opts $ctrl $argv
|
||||
command ssh $ssh_opts $argv
|
||||
set -l ssh_ret $status
|
||||
|
||||
# Restore original environment variables
|
||||
if string match -q '*ssh-env*' -- "$GHOSTTY_SHELL_FEATURES"
|
||||
for ssh_v in $ssh_exported_vars
|
||||
if string match -q '*=*' -- $ssh_v
|
||||
set -gx (string split -m1 '=' -- $ssh_v)
|
||||
else
|
||||
set -e $ssh_v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return $ssh_ret
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Minimal Ghostty SSH terminfo host cache
|
||||
|
||||
readonly CACHE_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/ghostty/terminfo_hosts"
|
||||
|
||||
case "${1:-}" in
|
||||
chk) [[ -f "$CACHE_FILE" ]] && grep -qFx "$2" "$CACHE_FILE" 2>/dev/null ;;
|
||||
add) mkdir -p "${CACHE_FILE%/*}"; { [[ -f "$CACHE_FILE" ]] && cat "$CACHE_FILE"; echo "$2"; } | sort -u > "$CACHE_FILE.tmp" && mv "$CACHE_FILE.tmp" "$CACHE_FILE" && chmod 600 "$CACHE_FILE" ;;
|
||||
list) [[ -s "$CACHE_FILE" ]] && echo "Hosts with Ghostty terminfo installed:" && cat "$CACHE_FILE" || echo "No cached hosts found." ;;
|
||||
clear) rm -f "$CACHE_FILE" 2>/dev/null && echo "Ghostty SSH terminfo cache cleared." || echo "No Ghostty SSH terminfo cache found." ;;
|
||||
esac
|
||||
@@ -245,78 +245,166 @@ _ghostty_deferred_init() {
|
||||
fi
|
||||
|
||||
# SSH Integration
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-(env|terminfo) ]]; then
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ (ssh-env|ssh-terminfo) ]]; then
|
||||
: "${GHOSTTY_SSH_CACHE_TIMEOUT:=5}"
|
||||
: "${GHOSTTY_SSH_CHECK_TIMEOUT:=3}"
|
||||
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||
readonly _CACHE="${GHOSTTY_RESOURCES_DIR}/shell-integration/shared/ghostty-ssh-cache"
|
||||
fi
|
||||
|
||||
# SSH wrapper
|
||||
# SSH wrapper that preserves Ghostty features across remote connections
|
||||
ssh() {
|
||||
local -a env opts ctrl
|
||||
env=()
|
||||
opts=()
|
||||
ctrl=()
|
||||
emulate -L zsh
|
||||
setopt local_options no_glob_subst
|
||||
|
||||
local -a ssh_env ssh_opts
|
||||
|
||||
# Set up env vars first so terminfo installation inherits them
|
||||
# Configure environment variables for remote session
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||
local -a vars
|
||||
vars=(
|
||||
COLORTERM=truecolor
|
||||
TERM_PROGRAM=ghostty
|
||||
${TERM_PROGRAM_VERSION:+TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION}
|
||||
local -a ssh_env_vars=(
|
||||
"COLORTERM=truecolor"
|
||||
"TERM_PROGRAM=ghostty"
|
||||
)
|
||||
for v in "${vars[@]}"; do
|
||||
export "${v?}"
|
||||
opts+=(-o "SendEnv ${v%=*}" -o "SetEnv $v")
|
||||
[[ -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[@]}")
|
||||
fi
|
||||
|
||||
# Install terminfo if needed, reuse control connection for main session
|
||||
# Install terminfo on remote host if needed
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-terminfo ]]; then
|
||||
# Get target (only when needed for terminfo)
|
||||
local target
|
||||
target=$(command ssh -G "$@" 2>/dev/null | awk '/^(user|hostname) /{print $2}' | paste -sd'@')
|
||||
local ssh_config ssh_user ssh_hostname ssh_target
|
||||
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)
|
||||
ssh_target="${ssh_user}@${ssh_hostname}"
|
||||
|
||||
if [[ -n "$target" ]] && "$_CACHE" chk "$target"; then
|
||||
env+=(TERM=xterm-ghostty)
|
||||
elif command -v infocmp >/dev/null 2>&1; then
|
||||
local tinfo
|
||||
tinfo=$(infocmp -x xterm-ghostty 2>/dev/null) || echo "Warning: xterm-ghostty terminfo not found locally." >&2
|
||||
if [[ -n "$tinfo" ]]; then
|
||||
echo "Setting up Ghostty terminfo on remote host..." >&2
|
||||
local cpath
|
||||
cpath="/tmp/ghostty-ssh-$USER-$RANDOM-$(date +%s)"
|
||||
case $(echo "$tinfo" | command ssh "${opts[@]}" -o ControlMaster=yes -o ControlPath="$cpath" -o ControlPersist=60s "$@" '
|
||||
infocmp xterm-ghostty >/dev/null 2>&1 && echo OK && exit
|
||||
command -v tic >/dev/null 2>&1 || { echo NO_TIC; exit 1; }
|
||||
mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && echo OK || echo FAIL
|
||||
') in
|
||||
OK)
|
||||
echo "Terminfo setup complete." >&2
|
||||
[[ -n "$target" ]] && "$_CACHE" add "$target"
|
||||
env+=(TERM=xterm-ghostty)
|
||||
ctrl+=(-o "ControlPath=$cpath")
|
||||
;;
|
||||
*) echo "Warning: Failed to install terminfo." >&2 ;;
|
||||
esac
|
||||
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
|
||||
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)
|
||||
else
|
||||
ssh_terminfo=$(infocmp -0 -Q2 -q xterm-ghostty 2>/dev/null | base64 2>/dev/null | tr -d '\n')
|
||||
fi
|
||||
|
||||
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
|
||||
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 "$@" '
|
||||
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")
|
||||
|
||||
# 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
|
||||
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: Could not generate terminfo data." >&2
|
||||
ssh_env+=(TERM=xterm-256color)
|
||||
fi
|
||||
else
|
||||
print "Warning: ghostty command not available for cache management." >&2
|
||||
ssh_env+=(TERM=xterm-256color)
|
||||
fi
|
||||
else
|
||||
echo "Warning: infocmp not found locally. Terminfo installation unavailable." >&2
|
||||
[[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]] && ssh_env+=(TERM=xterm-256color)
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fallback TERM only if terminfo didn't set it
|
||||
# Ensure TERM is set when using ssh-env feature
|
||||
if [[ "$GHOSTTY_SHELL_FEATURES" =~ ssh-env ]]; then
|
||||
[[ $TERM == xterm-ghostty && ! " ${env[*]} " =~ " TERM=" ]] && env+=(TERM=xterm-256color)
|
||||
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)
|
||||
fi
|
||||
|
||||
# Execute
|
||||
if [[ ${#env[@]} -gt 0 ]]; then
|
||||
env "${env[@]}" command ssh "${opts[@]}" "${ctrl[@]}" "$@"
|
||||
else
|
||||
command ssh "${opts[@]}" "${ctrl[@]}" "$@"
|
||||
command ssh "${ssh_opts[@]}" "$@"
|
||||
local ssh_ret=$?
|
||||
|
||||
# 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}"
|
||||
else
|
||||
unset "${ssh_v}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
return $ssh_ret
|
||||
}
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user