fix: add client-side caching to eliminate redundant terminfo installations

- Cache known hosts with terminfo in
$GHOSTTY_RESOURCES_DIR/terminfo_hosts
- Skip installation step for cached hosts (single connection instead of
two)
- Use secure file permissions (600) and atomic writes
- Extract SSH target safely from command arguments
- Maintains full functionality while improving user experience on
repeated connections
This commit is contained in:
Jason Rayne
2025-06-17 17:43:21 -07:00
parent b6bb9abfbc
commit 4cebee5c8e
4 changed files with 366 additions and 51 deletions

View File

@ -99,6 +99,66 @@ fi
# SSH # SSH
if [[ -n "$GHOSTTY_SSH_INTEGRATION" ]]; then if [[ -n "$GHOSTTY_SSH_INTEGRATION" ]]; then
# Cache file for tracking hosts with terminfo installed
_ghostty_cache_file="${GHOSTTY_RESOURCES_DIR:-$HOME/.config/ghostty}/terminfo_hosts"
# Extract target host from SSH arguments
_ghostty_get_ssh_target() {
local target=""
local skip_next=false
for arg in "$@"; do
if [[ "$skip_next" == "true" ]]; then
skip_next=false
continue
fi
# Skip flags that take arguments
if [[ "$arg" =~ ^-[bcDEeFIiJLlmOopQRSWw]$ ]]; then
skip_next=true
continue
fi
# Skip other flags
if [[ "$arg" =~ ^- ]]; then
continue
fi
# This should be the target
target="$arg"
break
done
echo "$target"
}
# Check if host has terminfo installed
_ghostty_host_has_terminfo() {
local target="$1"
[[ -f "$_ghostty_cache_file" ]] && grep -qFx "$target" "$_ghostty_cache_file" 2>/dev/null
}
# Add host to terminfo cache
_ghostty_cache_host() {
local target="$1"
local cache_dir
cache_dir="$(dirname "$_ghostty_cache_file")"
# Create cache directory if needed
[[ ! -d "$cache_dir" ]] && mkdir -p "$cache_dir"
# Atomic write to cache file
{
if [[ -f "$_ghostty_cache_file" ]]; then
cat "$_ghostty_cache_file"
fi
echo "$target"
} | sort -u > "$_ghostty_cache_file.tmp" && mv "$_ghostty_cache_file.tmp" "$_ghostty_cache_file"
# Secure permissions
chmod 600 "$_ghostty_cache_file" 2>/dev/null
}
# Wrap `ssh` command to provide Ghostty SSH integration. # Wrap `ssh` command to provide Ghostty SSH integration.
# #
# This approach supports wrapping an `ssh` alias, but the alias definition # This approach supports wrapping an `ssh` alias, but the alias definition
@ -153,21 +213,35 @@ if [[ -n "$GHOSTTY_SSH_INTEGRATION" ]]; then
# Level: full - All features # Level: full - All features
_ghostty_ssh_full() { _ghostty_ssh_full() {
# Full integration: Two-step terminfo installation local target
if builtin command -v infocmp >/dev/null 2>&1; then target="$(_ghostty_get_ssh_target "$@")"
echo "Installing Ghostty terminfo on remote host..." >&2
# Step 1: Install terminfo # Check if we already know this host has terminfo
if infocmp -x xterm-ghostty 2>/dev/null | builtin command ssh "$@" 'tic -x - 2>/dev/null'; then if [[ -n "$target" ]] && _ghostty_host_has_terminfo "$target"; then
echo "Terminfo installed successfully. Connecting with full Ghostty support..." >&2 # Direct connection with xterm-ghostty
# Step 2: Connect with xterm-ghostty since we know terminfo is now available
local env_vars=("TERM=xterm-ghostty") local env_vars=("TERM=xterm-ghostty")
# Propagate Ghostty shell integration environment variables
[[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES") [[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES")
env "${env_vars[@]}" ssh "$@"
return 0
fi
# Normal SSH connection with Ghostty terminfo available # Full integration: Install terminfo if needed
if builtin command -v infocmp >/dev/null 2>&1; then
# Install terminfo only if needed
if infocmp -x xterm-ghostty 2>/dev/null | builtin command ssh "$@" '
if ! infocmp xterm-ghostty >/dev/null 2>&1; then
echo "Installing Ghostty terminfo..." >&2
tic -x - 2>/dev/null
fi
'; then
echo "Connecting with full Ghostty support..." >&2
# Cache this host for future connections
[[ -n "$target" ]] && _ghostty_cache_host "$target"
# Connect with xterm-ghostty since terminfo is available
local env_vars=("TERM=xterm-ghostty")
[[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES")
env "${env_vars[@]}" ssh "$@" env "${env_vars[@]}" ssh "$@"
builtin return 0 builtin return 0
fi fi

View File

@ -98,6 +98,70 @@
(external sudo) $@args (external sudo) $@args
} }
# SSH Integration
# Cache file for tracking hosts with terminfo installed
var ghostty-cache-file = (if (has-env GHOSTTY_RESOURCES_DIR) { put $E:GHOSTTY_RESOURCES_DIR"/terminfo_hosts" } else { put $E:HOME"/.config/ghostty/terminfo_hosts" })
# Extract target host from SSH arguments
fn ghostty-get-ssh-target {|@args|
var target = ""
var skip-next = $false
for arg $args {
if (eq $skip-next $true) {
set skip-next = $false
continue
}
# Skip flags that take arguments
if (re:match '^-[bcDEeFIiJLlmOopQRSWw]$' $arg) {
set skip-next = $true
continue
}
# Skip other flags
if (re:match '^-' $arg) {
continue
}
# This should be the target
set target = $arg
break
}
put $target
}
# Check if host has terminfo installed
fn ghostty-host-has-terminfo {|target|
and (path:is-regular $ghostty-cache-file) ?(grep -qFx $target $ghostty-cache-file 2>/dev/null)
}
# Add host to terminfo cache
fn ghostty-cache-host {|target|
var cache-dir = (path:dir $ghostty-cache-file)
# Create cache directory if needed
if (not (path:is-dir $cache-dir)) {
mkdir -p $cache-dir
}
# Atomic write to cache file
var temp-file = $ghostty-cache-file".tmp"
{
if (path:is-regular $ghostty-cache-file) {
cat $ghostty-cache-file
}
echo $target
} | sort -u > $temp-file
mv $temp-file $ghostty-cache-file
# Secure permissions
?chmod 600 $ghostty-cache-file 2>/dev/null
}
fn ssh-with-ghostty-integration {|@args| fn ssh-with-ghostty-integration {|@args|
if (has-env GHOSTTY_SSH_INTEGRATION) { if (has-env GHOSTTY_SSH_INTEGRATION) {
if (eq "term-only" $E:GHOSTTY_SSH_INTEGRATION) { if (eq "term-only" $E:GHOSTTY_SSH_INTEGRATION) {
@ -147,15 +211,40 @@
} }
fn ssh-full {|@args| fn ssh-full {|@args|
# Full integration: Two-step terminfo installation var target = (ghostty-get-ssh-target $@args)
# Check if we already know this host has terminfo
if (and (not-eq "" $target) (ghostty-host-has-terminfo $target)) {
# Direct connection with xterm-ghostty
var env-vars = [TERM=xterm-ghostty]
# Propagate Ghostty shell integration environment variables
if (not-eq "" $E:GHOSTTY_SHELL_FEATURES) {
set env-vars = (conj $env-vars GHOSTTY_SHELL_FEATURES=$E:GHOSTTY_SHELL_FEATURES)
}
(external env) $@env-vars ssh $@args
return
}
# Full integration: Install terminfo if needed
if (has-external infocmp) { if (has-external infocmp) {
echo "Installing Ghostty terminfo on remote host..." >&2
try { try {
infocmp -x xterm-ghostty 2>/dev/null | (external ssh) $@args 'tic -x - 2>/dev/null' # Install terminfo only if needed
echo "Terminfo installed successfully. Connecting with full Ghostty support..." >&2 infocmp -x xterm-ghostty 2>/dev/null | (external ssh) $@args '
if ! infocmp xterm-ghostty >/dev/null 2>&1; then
echo "Installing Ghostty terminfo..." >&2
tic -x - 2>/dev/null
fi
'
echo "Connecting with full Ghostty support..." >&2
# Step 2: Connect with xterm-ghostty since we know terminfo is now available # Cache this host for future connections
if (not-eq "" $target) {
ghostty-cache-host $target
}
# Connect with xterm-ghostty since terminfo is available
var env-vars = [TERM=xterm-ghostty] var env-vars = [TERM=xterm-ghostty]
# Propagate Ghostty shell integration environment variables # Propagate Ghostty shell integration environment variables
@ -179,6 +268,7 @@
if (and (has-env GHOSTTY_SSH_INTEGRATION) (has-external ssh)) { if (and (has-env GHOSTTY_SSH_INTEGRATION) (has-external ssh)) {
edit:add-var ssh~ $ssh-with-ghostty-integration~ edit:add-var ssh~ $ssh-with-ghostty-integration~
} }
defer { defer {
mark-prompt-start mark-prompt-start
report-pwd report-pwd

View File

@ -86,8 +86,68 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
end end
end end
# SSH integration wrapper # SSH integration
if test -n "$GHOSTTY_SSH_INTEGRATION" if test -n "$GHOSTTY_SSH_INTEGRATION"
# Cache file for tracking hosts with terminfo installed
set -l _ghostty_cache_file (string join / (test -n "$GHOSTTY_RESOURCES_DIR"; and echo "$GHOSTTY_RESOURCES_DIR"; or echo "$HOME/.config/ghostty") "terminfo_hosts")
# Extract target host from SSH arguments
function _ghostty_get_ssh_target
set -l target ""
set -l skip_next false
for arg in $argv
if test "$skip_next" = "true"
set skip_next false
continue
end
# Skip flags that take arguments
if string match -qr '^-[bcDEeFIiJLlmOopQRSWw]$' -- "$arg"
set skip_next true
continue
end
# Skip other flags
if string match -q -- '-*' "$arg"
continue
end
# This should be the target
set target "$arg"
break
end
echo "$target"
end
# Check if host has terminfo installed
function _ghostty_host_has_terminfo
set -l target $argv[1]
test -f "$_ghostty_cache_file"; and grep -qFx "$target" "$_ghostty_cache_file" 2>/dev/null
end
# Add host to terminfo cache
function _ghostty_cache_host
set -l target $argv[1]
set -l cache_dir (dirname "$_ghostty_cache_file")
# Create cache directory if needed
test -d "$cache_dir"; or mkdir -p "$cache_dir"
# Atomic write to cache file
begin
if test -f "$_ghostty_cache_file"
cat "$_ghostty_cache_file"
end
echo "$target"
end | sort -u > "$_ghostty_cache_file.tmp"; and mv "$_ghostty_cache_file.tmp" "$_ghostty_cache_file"
# Secure permissions
chmod 600 "$_ghostty_cache_file" 2>/dev/null
end
# Wrap `ssh` command to provide Ghostty SSH integration.
function ssh -d "Wrap ssh to provide Ghostty SSH integration" function ssh -d "Wrap ssh to provide Ghostty SSH integration"
switch "$GHOSTTY_SSH_INTEGRATION" switch "$GHOSTTY_SSH_INTEGRATION"
case term-only case term-only
@ -136,22 +196,38 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
# Level: full - All features # Level: full - All features
function _ghostty_ssh_full function _ghostty_ssh_full
# Full integration: Two-step terminfo installation set -l target (_ghostty_get_ssh_target $argv)
if type -q infocmp
echo "Installing Ghostty terminfo on remote host..." >&2
# Step 1: Install terminfo # Check if we already know this host has terminfo
if infocmp -x xterm-ghostty 2>/dev/null | ssh $argv 'tic -x - 2>/dev/null' if test -n "$target"; and _ghostty_host_has_terminfo "$target"
echo "Terminfo installed successfully. Connecting with full Ghostty support..." >&2 # Direct connection with xterm-ghostty
# Step 2: Connect with xterm-ghostty since we know terminfo is now available
set --local env_vars TERM=xterm-ghostty set --local env_vars TERM=xterm-ghostty
# Propagate Ghostty shell integration environment variables
if test -n "$GHOSTTY_SHELL_FEATURES" if test -n "$GHOSTTY_SHELL_FEATURES"
set --append env_vars GHOSTTY_SHELL_FEATURES="$GHOSTTY_SHELL_FEATURES" set --append env_vars GHOSTTY_SHELL_FEATURES="$GHOSTTY_SHELL_FEATURES"
end end
env $env_vars ssh $argv
return 0
end
# Full integration: Install terminfo if needed
if type -q infocmp
# Install terminfo only if needed
if infocmp -x xterm-ghostty 2>/dev/null | ssh $argv '
if ! infocmp xterm-ghostty >/dev/null 2>&1
echo "Installing Ghostty terminfo..." >&2
tic -x - 2>/dev/null
end
'
echo "Connecting with full Ghostty support..." >&2
# Cache this host for future connections
test -n "$target"; and _ghostty_cache_host "$target"
# Connect with xterm-ghostty since terminfo is available
set --local env_vars TERM=xterm-ghostty
if test -n "$GHOSTTY_SHELL_FEATURES"
set --append env_vars GHOSTTY_SHELL_FEATURES="$GHOSTTY_SHELL_FEATURES"
end
env $env_vars ssh $argv env $env_vars ssh $argv
builtin return 0 builtin return 0
end end
@ -162,6 +238,7 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
_ghostty_ssh_basic $argv _ghostty_ssh_basic $argv
end end
end end
# Setup prompt marking # Setup prompt marking
function __ghostty_mark_prompt_start --on-event fish_prompt --on-event fish_cancel --on-event fish_posterror function __ghostty_mark_prompt_start --on-event fish_prompt --on-event fish_cancel --on-event fish_posterror
# If we never got the output end event, then we need to send it now. # If we never got the output end event, then we need to send it now.

View File

@ -246,6 +246,66 @@ _ghostty_deferred_init() {
# SSH # SSH
if [[ -n "$GHOSTTY_SSH_INTEGRATION" ]]; then if [[ -n "$GHOSTTY_SSH_INTEGRATION" ]]; then
# Cache file for tracking hosts with terminfo installed
_ghostty_cache_file="${GHOSTTY_RESOURCES_DIR:-$HOME/.config/ghostty}/terminfo_hosts"
# Extract target host from SSH arguments
_ghostty_get_ssh_target() {
local target=""
local skip_next=false
for arg in "$@"; do
if [[ "$skip_next" == "true" ]]; then
skip_next=false
continue
fi
# Skip flags that take arguments
if [[ "$arg" =~ ^-[bcDEeFIiJLlmOopQRSWw]$ ]]; then
skip_next=true
continue
fi
# Skip other flags
if [[ "$arg" =~ ^- ]]; then
continue
fi
# This should be the target
target="$arg"
break
done
echo "$target"
}
# Check if host has terminfo installed
_ghostty_host_has_terminfo() {
local target="$1"
[[ -f "$_ghostty_cache_file" ]] && grep -qFx "$target" "$_ghostty_cache_file" 2>/dev/null
}
# Add host to terminfo cache
_ghostty_cache_host() {
local target="$1"
local cache_dir
cache_dir="$(dirname "$_ghostty_cache_file")"
# Create cache directory if needed
[[ ! -d "$cache_dir" ]] && mkdir -p "$cache_dir"
# Atomic write to cache file
{
if [[ -f "$_ghostty_cache_file" ]]; then
cat "$_ghostty_cache_file"
fi
echo "$target"
} | sort -u > "$_ghostty_cache_file.tmp" && mv "$_ghostty_cache_file.tmp" "$_ghostty_cache_file"
# Secure permissions
chmod 600 "$_ghostty_cache_file" 2>/dev/null
}
# Wrap `ssh` command to provide Ghostty SSH integration # Wrap `ssh` command to provide Ghostty SSH integration
ssh() { ssh() {
case "$GHOSTTY_SSH_INTEGRATION" in case "$GHOSTTY_SSH_INTEGRATION" in
@ -296,21 +356,35 @@ _ghostty_deferred_init() {
# Level: full - All features # Level: full - All features
_ghostty_ssh_full() { _ghostty_ssh_full() {
# Full integration: Two-step terminfo installation local target
if builtin command -v infocmp >/dev/null 2>&1; then target="$(_ghostty_get_ssh_target "$@")"
echo "Installing Ghostty terminfo on remote host..." >&2
# Step 1: Install terminfo # Check if we already know this host has terminfo
if infocmp -x xterm-ghostty 2>/dev/null | builtin command ssh "$@" 'tic -x - 2>/dev/null'; then if [[ -n "$target" ]] && _ghostty_host_has_terminfo "$target"; then
echo "Terminfo installed successfully. Connecting with full Ghostty support..." >&2 # Direct connection with xterm-ghostty
# Step 2: Connect with xterm-ghostty since we know terminfo is now available
local env_vars=("TERM=xterm-ghostty") local env_vars=("TERM=xterm-ghostty")
# Propagate Ghostty shell integration environment variables
[[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES") [[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES")
env "${env_vars[@]}" ssh "$@"
return 0
fi
# Normal SSH connection with Ghostty terminfo available # Full integration: Install terminfo if needed
if builtin command -v infocmp >/dev/null 2>&1; then
# Install terminfo only if needed
if infocmp -x xterm-ghostty 2>/dev/null | builtin command ssh "$@" '
if ! infocmp xterm-ghostty >/dev/null 2>&1; then
echo "Installing Ghostty terminfo..." >&2
tic -x - 2>/dev/null
fi
'; then
echo "Connecting with full Ghostty support..." >&2
# Cache this host for future connections
[[ -n "$target" ]] && _ghostty_cache_host "$target"
# Connect with xterm-ghostty since terminfo is available
local env_vars=("TERM=xterm-ghostty")
[[ -n "$GHOSTTY_SHELL_FEATURES" ]] && env_vars+=("GHOSTTY_SHELL_FEATURES=$GHOSTTY_SHELL_FEATURES")
env "${env_vars[@]}" ssh "$@" env "${env_vars[@]}" ssh "$@"
builtin return 0 builtin return 0
fi fi