Files
galaxy/app/assets/bundled/bootstrap/fish.sh
T

793 lines
37 KiB
Bash

# Note that WARP_SESSION_ID is expected to have been set when executing commands to
# emit the InitShell payload, which includes the session ID.
begin
# We wrap ourselves in a begin block because these are effectively injected
# keystrokes, and we want to execute this whole script as a single command.
# Of course we are fish script, not POSIX shell script.
# Set fish_private_mode to prevent saving to history.
# Note: We don't add a leading space to the `begin` command here because this entire script is
# pasted into fish with bracketed paste, and Fish strips out the leading whitespace. Instead,
# we insert the space _before_ pasting the script in, which has the same effect
set -l saved_fish_private_mode $fish_private_mode
set -g fish_private_mode 1
# Disable fish autosuggestions - because input goes through Warp's editor instead,
# they are never actionable, and the extra output can cause problems.
set -g fish_autosuggestion_enabled 0
# Byte sequence used to signal the start of a DCS. ([0x1b, 0x50, 0x24] which
# maps to <ESC>, P, $ in ASCII.)
set -g DCS_START \u1b\u50\u24
# Appended to $DCS_START to signal that the following message is JSON-encoded.
# The Rust app also receives non-JSON-encoded DCS's sent from
# _warp_run_generator_command_internal, which instead end in 'e' (0x65).
set -g DCS_JSON_MARKER 'd'
set -g DCS_END \x1b\x5c
set -g OSC_START (printf '\e]9278;')
set -g OSC_END (printf '\a')
set -g OSC_PARAM_SEPARATOR ';'
set -g RESET_GRID_OSC (printf '\e]9279\a')
if test -n "$GALAXY_INITIAL_WORKING_DIR"
cd "$GALAXY_INITIAL_WORKING_DIR" >/dev/null 2>&1
set -e GALAXY_INITIAL_WORKING_DIR
end
# Append additional PATH entries if provided via WARP_PATH_APPEND.
if test -n "$WARP_PATH_APPEND"
set -gx --path PATH "$PATH:$WARP_PATH_APPEND"
set -e WARP_PATH_APPEND
end
function warp_send_json_message
# Sends a message to the controlling terminal as a DSC control sequence.
set -l escaped_json (warp_hex_encode_string "$argv")
if [ "$WARP_USING_WINDOWS_CON_PTY" = true ]
echo -n "$OSC_START$DCS_JSON_MARKER$OSC_PARAM_SEPARATOR$escaped_json$OSC_END"
else
echo -n "$DCS_START$DCS_JSON_MARKER$escaped_json$DCS_END"
end
end
function warp_maybe_send_reset_grid_osc
# Note that $WARP_USING_WINDOWS_CON_PTY is set in the init shell script.
if [ "$WARP_USING_WINDOWS_CON_PTY" = true ]
printf $RESET_GRID_OSC
end
end
# warp_hex_encode_string hex-encodes the given string with `od`.
function warp_hex_encode_string
echo "$argv" | od -An -v -tx1 | command tr -d ' \n'
end
# A list of PIDs for running in-band command(s). This is used to kill running
# in-band commands in preexec for a user command, so they do not interfere with
# user command output.
set -g _warp_generator_pids ''
# Runs the given command in the background, records its PID in
# _WARP_GENERATOR_PIDS_STARTED_TMP_FILE, and adds its PID from the file when
# the job is completed.
#
# Usage:
# _warp_run_generator_command_internal <command_id> '<command>'
#
# The first argument is the command's ID, which is included in the DCS string sent
# to the rust app. The second argument is the command string itself.
function _warp_run_generator_command_internal
set -l command_id $argv[1]
set -l command (string join -- ' ' (string escape $argv[2]))
# Fish cannot run shell functions in the background, so in order to run
# the command in the the background we spawn a fish process, inlining the
# code necessary to write the command's output OSC to the rust app. This
# differs from the bash and zsh implementations, which rely on running
# functions in the background.
#
# The `IFS` has to be set to the empty string to prevent fish from converting
# the command output into a list by splitting output on newlines (fish splits
# strings on $IFS into lists). Essentially, setting IFS to the empty string
# allows us to preserve newlines in command output (that might be relied
# upon) for parsing output.
#
# N.B. Fish shell variables cannot contain null characters, so the command output must be
# immediately hex encoded before being stored in a variable.
fish -c "
set -l warp_using_windows_con_pty $WARP_USING_WINDOWS_CON_PTY;
set -l reset_grid_osc $RESET_GRID_OSC;
function warp_maybe_send_reset_grid_osc
if [ \"\$warp_using_windows_con_pty\" = true ]
printf \$reset_grid_osc
end
end
set -l OSC_START_GENERATOR_OUTPUT \$(printf '\e]9277;A\a')
set -l OSC_END_GENERATOR_OUTPUT \$(printf '\e]9277;B\a')
set -l command_id $command_id;
set -l command $command;
set -l IFS;
begin
echo -n \"\$command_id;\"
eval \$command 2>&1
echo -n \";\$status\"
end | od -An -v -tx1 | command tr -d ' \n' | read -lz hex_encoded_message
set -l LC_ALL \"C\"
set -l byte_count (string length \"\$hex_encoded_message\")
echo -n \"\$OSC_START_GENERATOR_OUTPUT\$byte_count;\$hex_encoded_message\$OSC_END_GENERATOR_OUTPUT\"
warp_maybe_send_reset_grid_osc" 2> /dev/null &
set -l command_pid $last_pid
set -a _warp_generator_pids $command_pid
# Remove the command's PID from _warp_generator_pids when the command exits.
function on_command_{$command_pid}_finish --on-process-exit $command_pid --inherit-variable command_pid
set -g _warp_generator_pids (string replace $command_pid '' $_warp_generator_pids)
# Erase this function after the pids list is updated above so we don't create an infinite number of
# functions that could pollute the user's context (nested functions are still existing in the global
# scope).
functions -e on_command_{$command_pid}_finish
# Note: If we're on windows, we send a reset grid to erase any cursor mutations caused by
# the in-band command.
warp_maybe_send_reset_grid_osc
end
end
# Executes a generator command in the background, where the first argument is
# the "command ID" assigned by the Rust app and the second-nth arguments
# specify the command to be run.
#
# Note that the command string should be single-quoted so any variables are
# not substituted until the command string is actually evaluated.
#
# Usage:
# warp_run_generator_command <command_id> '<command> <arg1> ... <argn>'
function warp_run_generator_command
# Setting this environment variable allows warp_precmd to detect if a generator
# command or a user command has just completed.
set -g _WARP_GENERATOR_COMMAND 1
_warp_run_generator_command_internal $argv
end
# Run before a command is executed.
function warp_preexec --on-event fish_preexec
set -l command (warp_escape_json "$argv")
warp_send_json_message "{\"hook\": \"Preexec\", \"value\": {\"command\": \"$command\", \"session_id\": $WARP_SESSION_ID}}"
warp_maybe_send_reset_grid_osc
# If this preexec is called for user command, kill ongoing generator command jobs.
if test (! string match -q "warp_run_generator_command*" $argv[1])
for pid in $_warp_generator_pids
# Suppress stderr output; kill writes to stderr if any of the given
# PIDS are not running (which might rarely be the case due to race
# conditions in checking which PIDS to cancel and this kill command.
kill -9 $pids >/dev/null 2>/dev/null
end
set -g _warp_generator_pids ''
end
end
# The git prompts git commands are read-only and should not interfere with
# other processes. This environment variable is equivalent to running with `git
# --no-optional-locks`, but falls back gracefully for older versions of git.
# See git(1) for and git-status(1) for a description of that flag.
#
# We wrap in a local function instead of exporting the variable directly in
# order to avoid interfering with manually-run git commands by the user.
function warp_git
GIT_OPTIONAL_LOCKS=0 command git $argv
end
# Wrap fish prompt function output with OSC prompt marker sequences,
# so that we can direct the prompt bytes to the appropriate grids.
function warp_update_prompt_vars
# Back up the original fish_prompt if not already done
if not functions -q warp_original_fish_prompt
functions -c fish_prompt warp_original_fish_prompt
end
# Back up the original fish_right_prompt if it exists and not already backed up
if functions -q fish_right_prompt; and not functions -q warp_original_fish_right_prompt
functions -c fish_right_prompt warp_original_fish_right_prompt
end
# If not honoring PS1, set both prompts to be empty
if test "$WARP_HONOR_PS1" = "0"
function fish_prompt; echo -n ""; end
function fish_right_prompt; echo -n ""; end
# If honoring PS1, add prefix/suffix to both prompts
else
function end_prompt
echo -n (printf '\x1b')
if test "$WARP_HONOR_PS1" != "1" && [ "$WARP_USING_WINDOWS_CON_PTY" = true ]
echo -n "]133;B$RESET_GRID_OSC"
else
echo -n ']133;B'
end
echo -n (printf '\x07')
end
# Redefine fish_prompt to include prefix and suffix
function fish_prompt
echo -n (printf '\x1b')
echo -n ']133;A'
echo -n (printf '\x07')
warp_original_fish_prompt
end_prompt
end
# Check if warp_original_fish_right_prompt was backed up before redefining fish_right_prompt
if functions -q warp_original_fish_right_prompt
function fish_right_prompt
echo -n (printf '\x1b')
echo -n ']133;P;k=r'
echo -n (printf '\x07')
warp_original_fish_right_prompt
end_prompt
end
end
end
end
# Changes the WARP_HONOR_PS1 variable to 1, to indicate we want to use the user's custom prompt. Restores
# the original fish prompt functions (which we set to empty for Warp prompt) by calling warp_update_prompt_vars
# to refresh the prompt. We force a repaint of the prompt to ensure the change is reflected immediately.
function warp_change_prompt_modes_to_ps1
set -x WARP_HONOR_PS1 "1"
# Restores fish_prompt and fish_right_prompt.
warp_update_prompt_vars
# Forces a repaint of the current prompt to ensure the change is reflected immediately.
commandline -f repaint
end
# Changes the WARP_HONOR_PS1 variable to 0, to indicate we want to use the Warp prompt. Saves and clears
# the fish prompt functions (which we set to empty for Warp prompt) by calling warp_update_prompt_vars
# to refresh the prompt. We force a repaint of the prompt to ensure the change is reflected immediately.
function warp_change_prompt_modes_to_warp_prompt
set -x WARP_HONOR_PS1 "0"
# Updates fish_prompt and fish_right_prompt to be empty.
warp_update_prompt_vars
# Forces a repaint of the current prompt to ensure the change is reflected immediately.
commandline -f repaint
end
set block_id 0
# Run before the prompt is displayed. We also need to trigger this on "fish_posterror", as
# submitting a command containing a syntax error will not trigger "fish_preexec" or "fish_prompt".
function warp_precmd --on-event fish_prompt --on-event fish_posterror
# Handle prompt behavior (we do this first to make sure the exit status is from the command,
# rather than from our own code)
set -l exit_code $status
# This function is triggered by both "fish_prompt" and "fish_posterror" events. If it was
# "fish_prompt", then $status will be properly set. If it was "fish_posterror", it won't be.
# We want to set the exit code to 1 for errors, but there is no direct way to check which event
# triggered this function call. The length of $argv actually tells us, as "fish_posterror"
# passes the erroneous command while "fish_prompt" passes nothing.
if test (count $argv) -gt 0
set exit_code 1
end
set -l next_block_id "precmd-$WARP_SESSION_ID-$block_id"
warp_send_json_message "{\"hook\": \"CommandFinished\", \"value\": {\"exit_code\": $exit_code, \"next_block_id\": \"$next_block_id\", \"session_id\": $WARP_SESSION_ID}}"
warp_maybe_send_reset_grid_osc
set block_id (math $block_id + 1)
if ! test -z $_WARP_GENERATOR_COMMAND
set -e _WARP_GENERATOR_COMMAND
set -l escaped_json "{\"hook\": \"Precmd\", \"value\": {
\"exit_code\": $exit_code,
\"next_block_id\": \"$next_block_id\",
\"pwd\": \"\",
\"ps1\": \"\",
\"git_head\": \"\",
\"git_branch\": \"\",
\"virtual_env\": \"\",
\"conda_env\": \"\",
\"node_version\": \"\",
\"session_id\": $WARP_SESSION_ID,
\"is_after_in_band_command\": true
}}"
warp_send_json_message $escaped_json
return 0
end
# Fish's vi mode causes a conflict with the input reporting binding.
# Set the bindings back for this session. Note that just calling fish_default_key_bindings
# affects other sessions so we do it this way instead.
set -g fish_key_bindings fish_default_key_bindings
# Do not use kill-line because of the kill ring interation.
bind \cP "commandline ''"
# We use the ESC-p bindkey for this ("p" for PS1/custom prompt).
bind \ep warp_change_prompt_modes_to_ps1
# We use the ESC-w bindkey for this ("w" for Warp prompt).
bind \ew warp_change_prompt_modes_to_warp_prompt
bind \ei warp_report_input
# Define local variables in appropriate outer block for fish variable scoping.
# See https://stackoverflow.com/a/53685510.
set -l escaped_prompt
set -l escaped_right_prompt
set -l escaped_pwd
if set -q WSL_DISTRO_NAME
# In WSL, avoid symlinks b/c on Windows `std::fs` is unable to resolve symlink inside WSL containers.
set escaped_pwd (warp_escape_json (pwd -P))
else
set escaped_pwd (warp_escape_json $PWD)
end
set -l escaped_virtual_env ""
set -l escaped_conda_env ""
set -l escaped_node_version ""
set -l escaped_git_head ""
set -l escaped_git_branch ""
# Only fill these fields once we've finished bootstrapping, as the
# blocks created during the bootstrap process don't have visible
# prompts, and we don't want to invoke `git` before we've sourced the
# user's rcfiles and have a fully-populated PATH.
if test -n "$WARP_BOOTSTRAPPED"
if test -n "$VIRTUAL_ENV"
set escaped_virtual_env (warp_escape_json "$VIRTUAL_ENV")
end
if test -n "$CONDA_DEFAULT_ENV"
set escaped_conda_env (warp_escape_json "$CONDA_DEFAULT_ENV")
end
# Get the Node.js version, but only when the Node.js Version chip is enabled.
# Warp sets WARP_PROMPT_NODE_VERSION_ENABLED to "0" when the chip is not in the
# prompt (defaulting to enabled when unset), so we avoid spawning `node` on
# every prompt when the chip is not shown.
if test "$WARP_PROMPT_NODE_VERSION_ENABLED" != "0"; and command -v node > /dev/null 2>&1
# Check for package.json in current directory and parent directories
set current_dir (pwd)
set found_package_json false
set package_json_dir ""
while test -n "$current_dir"
if test -f "$current_dir/package.json"
set found_package_json true
set package_json_dir "$current_dir"
break
end
if test "$current_dir" = "/"
break
end
# Strip the last path segment without spawning `dirname`.
set current_dir (string replace -r '/[^/]*$' '' -- "$current_dir")
if test -z "$current_dir"
set current_dir "/"
end
end
# Only show node version if package.json is within a git repository
if test "$found_package_json" = true
set git_dir "$package_json_dir"
set in_git_repo false
while test -n "$git_dir"
if test -d "$git_dir/.git"
set in_git_repo true
break
end
if test "$git_dir" = "/"
break
end
set git_dir (string replace -r '/[^/]*$' '' -- "$git_dir")
if test -z "$git_dir"
set git_dir "/"
end
end
if test "$in_git_repo" = true
# Cache the resolved version keyed on PWD + PATH so we only spawn
# `node --version` when the directory or PATH changes (PATH changes
# on `nvm use`). Use global cache vars so they persist across calls.
set -l node_cache_key "$PWD:$PATH"
if test "$node_cache_key" = "$_WARP_NODE_VERSION_CACHE_KEY"
set escaped_node_version "$_WARP_NODE_VERSION_CACHE_VALUE"
else
set -l node_version (node --version 2>/dev/null)
if test -n "$node_version"
set escaped_node_version (warp_escape_json "$node_version")
end
set -g _WARP_NODE_VERSION_CACHE_KEY "$node_cache_key"
set -g _WARP_NODE_VERSION_CACHE_VALUE "$escaped_node_version"
end
end
end
end
set -l git_branch ""
set -l git_head ""
if command -q git
set git_branch (warp_git symbolic-ref --short HEAD 2> /dev/null)
if test -z "$git_branch"
# Fallback to the git commit hash if we aren't on a named branch.
set git_head (warp_git rev-parse --short HEAD 2> /dev/null)
else
set git_head "$git_branch"
end
end
set escaped_git_head (warp_escape_json "$git_head")
set escaped_git_branch (warp_escape_json "$git_branch")
end
warp_update_prompt_vars
# This is used solely for prompt previews, when we're using prompt markers with combined grid.
# We need to use this since fish does not have a way to ignore printable characters for cursor
# positioning (unlike zsh/bash), so we need a separate mechanism to send the prompt to Warp
# in the case of Warp prompt (for previewing the PS1). We send an escaped version of the raw prompt
# bytes via a hex string (in a JSON payload) to Warp.
# Note that we are CALLING the `warp_original_fish_prompt` function on the next line and assigning the
# outputted string to the local variable `raw_prompt_for_preview`.
set -l raw_prompt_for_preview (warp_original_fish_prompt)
# We encode the prompt as a hex string to pass it to Warp.
set escaped_prompt (warp_escape_prompt "$raw_prompt_for_preview")
set -l escaped_json
if test "$WARP_HONOR_PS1" = "1"
# Don't send lprompt or rprompt in this case - we'll use prompt markers for both directly!
set escaped_json "{\"hook\": \"Precmd\", \"value\": {
\"exit_code\": $exit_code,
\"next_block_id\": \"$next_block_id\",
\"pwd\": \"$escaped_pwd\",
\"ps1\": \"\",
\"rprompt\": \"\",
\"git_head\": \"$escaped_git_head\",
\"git_branch\": \"$escaped_git_branch\",
\"virtual_env\": \"$escaped_virtual_env\",
\"conda_env\": \"$escaped_conda_env\",
\"node_version\": \"$escaped_node_version\",
\"session_id\": $WARP_SESSION_ID
}}"
else
# We send an lprompt to use for prompt preview purposes only (we still use prompt markers for active prompts).
set escaped_json "{\"hook\": \"Precmd\", \"value\": {
\"exit_code\": $exit_code,
\"next_block_id\": \"$next_block_id\",
\"pwd\": \"$escaped_pwd\",
\"ps1\": \"$escaped_prompt\",
\"rprompt\": \"\",
\"git_head\": \"$escaped_git_head\",
\"git_branch\": \"$escaped_git_branch\",
\"virtual_env\": \"$escaped_virtual_env\",
\"conda_env\": \"$escaped_conda_env\",
\"node_version\": \"$escaped_node_version\",
\"session_id\": $WARP_SESSION_ID
}}"
end
warp_send_json_message $escaped_json
end
function warp_escape_prompt
# To match the implementation of PS1 support in bash / zsh, we use the same method of encoding
# the prompt as a hex string so that it can be interpreted on the Warp side
# Note: before converting the prompt to a hex string, we remove any multi-line newlines and
# replace them with a single space (to avoid prompts that span multiple empty lines)
echo "$argv" | command tr '\n\n' ' ' | command od -An -v -tx1 | command tr -d ' \n'
end
# Escapes string(s) passed as input to be JSON-serializable.
#
# Specifically, special characters like backspace, tab, form feed, carriage return, and newlines
# are replaced with escaped equivalents. Double quotes and literal backslash characters are also
# backslash-escaped.
function warp_escape_json
# Explanation of the sed replacements (each command is separated by a `;`):
# s/(["\\])/\\\1/g - Replace all double-quote (") and backslash (\) characters with the escaped versions (\" and \\)
# s/\b/\\b/g - Replace all backspace characters with \b
# s/\t/\\t/g - Replace all tab characters with \t
# s/\f/\\f/g - Replace all form feed characters with \f
# s/\r/\\r/g - Replace all carriage return characters with \r
# $!s/$/\\\\n/ - Replace any newlines (except the last trailing newline) with the literal \n character
#
# Additional note: In Fish, backslashes still need to be escaped in single quoted strings. So each `\\` below
# represents a single `\` in the above regex explanation. We also close the single-quoted string in order to
# insert literal characters (Fish supports literals with escapes when not quoting)
#
# We also use 'command sed' to ensure that we aren't accidentally executing a function named 'sed'
string join \n $argv | command sed -E 's/(["\\\\])/\\\\\\1/g; s/'\b'/\\\\b/g; s/'\t'/\\\\t/g; s/'\f'/\\\\f/g; s/'\r'/\\\\r/g; $!s/$/\\\\n/' | command tr -d '\n'
end
function warp_bootstrapped
set -l histfile_directory
set histfile_directory "$XDG_DATA_HOME"
if test -z "$histfile_directory"
set histfile_directory "$HOME/.local/share"
end
set -l escaped_histfile (warp_escape_json "$histfile_directory/fish/fish_history")
set -l vi_mode_enabled ""
if [ "$fish_key_bindings" = "fish_vi_key_bindings" ]
set vi_mode_enabled "1"
end
set -l kernel_name (uname)
if test -n "$kernel_name"
if [ "$kernel_name" = "Darwin" ]
set os_category "MacOS"
else if [ "$kernel_name" = "Linux" ]
set os_category "Linux"
set -l default_os_release_filepath "/etc/os-release"
set -l fallback_os_release_filepath "/usr/lib/os-release"
# We first try /etc/os-release and then try /usr/lib/os-release as a fallback.
if test -f "$default_os_release_filepath"
set os_release_file "$default_os_release_filepath";
else if test -f "$fallback_os_release_filepath"
set os_release_file "$fallback_os_release_filepath";
end
if test -f "$os_release_file"
set linux_distribution (cat $os_release_file | sed -nE 's/^NAME="(.*)"$/\1/p')
end
end
end
set -l escaped_abbr (warp_escape_json (abbr --show))
set -l escaped_aliases (warp_escape_json (alias))
set -l env_var_names (warp_escape_json (set --names))
set -l function_names (warp_escape_json (functions -an))
set -l escaped_builtins (warp_escape_json (builtin -n))
# Note "keywords" is set to an empty string since fish includes keywords as a
# part of its builtins (e.g. "for", "while", etc.).
set -l escaped_editor (warp_escape_json "$EDITOR")
set -l escaped_shell_path (warp_escape_json (status fish-path))
set -l escaped_json "{\"hook\": \"Bootstrapped\", \"value\": {\"histfile\": \"$escaped_histfile\", \"session_id\": $WARP_SESSION_ID, \"shell\": \"fish\", \"home_dir\": \"$HOME\", \"path\": \"$PATH\", \"editor\": \"$escaped_editor\", \"abbreviations\": \"$escaped_abbr\", \"aliases\": \"$escaped_aliases\", \"function_names\": \"$function_names\", \"env_var_names\": \"$env_var_names\", \"builtins\": \"$escaped_builtins\", \"keywords\": \"\", \"shell_version\": \"$FISH_VERSION\", \"vi_mode_enabled\": \"$vi_mode_enabled\", \"os_category\": \"$os_category\", \"linux_distribution\": \"$linux_distribution\", \"wsl_name\": \"$WSL_DISTRO_NAME\", \"shell_path\": \"$escaped_shell_path\"}}"
warp_send_json_message $escaped_json
end
function warp_init_shell
set -l init_shell "{\"hook\": \"InitShell\", \"value\": {\"shell\": \"$argv\"}}"
warp_hex_encode_string "$init_shell"
end
# Add a key binding to report the current input buffer to Warp. We can override
# any user-defined binds here because user input goes through Warp's editor, not
# the fish line editor.
# This is arbitrarily bound to ESC-i in all supported shells ("i" for input).
# Binding to ESC-1 caused bootstrap failures with vi keybindings.
function warp_report_input
set -l escaped_input (warp_escape_json (commandline))
warp_send_json_message "{ \"hook\": \"InputBuffer\", \"value\": { \"buffer\": \"$escaped_input\", \"session_id\": $WARP_SESSION_ID } }"
# This prevents fish from rendering typeahead as background output once we've collected it.
commandline ''
end
function clear
warp_send_json_message "{\"hook\": \"Clear\", \"value\": {\"session_id\": $WARP_SESSION_ID}}"
end
function warp_finish_update
set -l update_id "$argv[1]"
warp_send_json_message "{\"hook\": \"FinishUpdate\", \"value\": { \"update_id\": \"$update_id\", \"session_id\": $WARP_SESSION_ID}}"
end
# Check if the warp apt source file has been renamed to `warpdotdev.list.distUpgrade` due to an ubuntu version update.
# If this occurred, we want to rename the source file back to `warpdotdev.list` to ensure updates can proceed.
# We purposefully skip this if either the `warpdotdev.list` file already exists (indicating that the user has already
# done this themselves) _or_ if a `warpdotdev.sources` file exists (which is the new Deb822 format for source files).
# The `.sources` file could only exist if a user manually created it; Ubuntu doesn't create one automatically for the
# warp source file due to a bug in its update flow where it considers our source file to be "invalid" because it
# contains a `signed-by` key.
function warp_handle_dist_upgrade
set -l source_file_name "$argv[1]"
# The `apt-config shell` command outputs an environment variable assignment in POSIX-compliant syntax. Therefore,
# we need to run this from within an sh shell to actually get the correct directory for the sources dir.
set -l APT_SOURCESDIR (command sh -c 'eval $(apt-config shell APT_SOURCESDIR "Dir::Etc::sourceparts/d"); echo $APT_SOURCESDIR')
if not test -e $APT_SOURCESDIR$source_file_name.list; and not test -e $APT_SOURCESDIR$source_file_name.sources; and test -e $APT_SOURCESDIR$source_file_name.list.distUpgrade
# DO NOT DO THIS. We should never run a command for user with `sudo`. The only reason this is safe here is because
# we insert this function into the input for the user to determine if they want to execute (we never run it on
# their behalf without their permission). To be transparent about what is being executed with sudo, we echo out the
# command we're about to run.
echo "Executing: sudo cp \"$APT_SOURCESDIR$source_file_name.list.distUpgrade\" \"$APT_SOURCESDIR$source_file_name.list\""
sudo cp "$APT_SOURCESDIR$source_file_name.list.distUpgrade" "$APT_SOURCESDIR$source_file_name.list"
end
end
# The SSH logic only applies to local sessions, because we don't yet have support for bootstrapping
# recursive SSH sessions.
if test "$WARP_IS_LOCAL_SHELL_SESSION" = "1"
function is_interactive_ssh_session
# Parse through all ssh options, as defined in the ssh man pages. Send
# stderr to /dev/null to silence argparse output when an option is invalid.
argparse '1' '2' '4' '6' 'A' 'a' 'C' 'f' 'g' 'K' 'k' 'M' 'N' 'n' 'q' 's' 'T' 't' 'V' 'v' 'X' 'x' 'Y' 'y' 'b=' 'c=' 'D=' 'e=' 'F=' 'i=' 'L=' 'l=' 'm=' 'O=' 'o=' 'p=' 'R=' 'S=' 'W=' 'w=' -- $argv 2>/dev/null
# If argparse returned a non-success value, propagate it up.
or return
if [ $_flag_T ]
# -T disables pty allocation (aka a non-interactive session)
return 1
end
if [ $_flag_W ]
# -W implies -T
return 1
end
# If there is more than one positional argument, the user is attempting to
# run a command, not start an interactive session. If there is less than
# one positional argument, the user should be shown the usage text.
if [ (count $argv) -ne 1 ]
return 1
end
end
function warp_ssh_helper
set -l init_shell_zsh (warp_init_shell "zsh")
set -l init_shell_bash (warp_init_shell "bash")
set -l remote_session_id (command od -An -N8 -tu8 /dev/urandom 2>/dev/null | command tr -d ' \n')
if test -z "$remote_session_id"; or test "$remote_session_id" = "0"
# If we cannot generate a non-zero random token, run plain SSH instead.
command ssh $argv
return
end
# Hex-encode the ZSH environment script we use to bootstrap remote zsh b/c it contains control characters
# We decode on the SSH server using xxd if its available, otherwise fall back to a for-loop over each byte
# and use printf to convert back to plaintext
set -l zsh_env_script (printf '%s' 'unsetopt ZLE; unset RCS; unset GLOBAL_RCS; WARP_SESSION_ID='$remote_session_id'; WARP_USING_WINDOWS_CON_PTY=@@USING_CON_PTY_BOOLEAN@@; WARP_HONOR_PS1='$WARP_HONOR_PS1'; _hostname=$(command -pv hostname >/dev/null 2>&1 && command -p hostname 2>/dev/null || uname -n); _user=$(command -pv whoami >/dev/null 2>&1 && command -p whoami 2>/dev/null || echo $USER); _msg=$(printf "{\"hook\": \"InitShell\", \"value\": {\"session_id\": $WARP_SESSION_ID, \"shell\": \"zsh\", \"user\": \"%s\", \"hostname\": \"%s\"}}" "$_user" "$_hostname" | command -p od -An -v -tx1 | command -p tr -d " \n"); printf '"'"'\x1b\x50\x24\x64%s\x1b\x5c'"'"' $_msg; unset _hostname _user _msg' | command od -An -v -tx1 | command tr -d ' \n')
# Optionally attach to an existing ControlMaster the user already
# runs for this destination instead of creating our own. Resolve
# the user's configured ControlPath with `ssh -G` (which expands
# tokens like %h/%p/%r/%C into a literal path), then verify the
# master is alive with `ssh -O check`. Both probes are local-only
# commands. On any failure we fall back to creating a Warp-owned
# master, preserving the existing behavior.
set -l control_path "$SSH_SOCKET_DIR/$WARP_SESSION_ID"
set -l control_master_mode "yes"
set -l external_control_master "false"
if test "$WARP_SSH_REUSE_CONTROL_MASTER" = "1"
set -l user_control_path (command ssh -G $argv 2>/dev/null | command sed -n 's/^controlpath //p')
# Skip when no ControlPath is configured, and reject resolved
# paths containing characters we cannot safely embed in the SSH
# hook JSON below (e.g. an unexpanded % token, quotes, or
# whitespace); in those cases fall back to a Warp-owned master.
if test -n "$user_control_path"
and test "$user_control_path" != "none"
and string match --quiet --regex '^[A-Za-z0-9._/~@:+,-]+$' -- "$user_control_path"
if command ssh -O check -o ControlPath="$user_control_path" $argv >/dev/null 2>&1
# A live master exists: multiplex through it and let the
# client know Warp does not own it.
set control_path "$user_control_path"
set control_master_mode "no"
set external_control_master "true"
end
end
end
# Note that in this command, we're passing a string to the remote shell. Any variable expansions need to be
# escaped with "''" to avoid the local shell from expanding them before they're passed to the remote shell.
# We check the SHELL env var and use shell string manipulation to get the contents after the last slash to
# determine what shell is the login shell on the remote machine. We perform a preliminary check to see if
# the remote shell is the Bourne shell to avoid asking it to parse later lines that use syntax it doesn't
# support.
command ssh -o ControlMaster=$control_master_mode -o ControlPath="$control_path" \
-t $argv \
"
export TERM_PROGRAM='WarpTerminal'
test -n '$WARP_CLIENT_VERSION' && export WARP_CLIENT_VERSION='$WARP_CLIENT_VERSION'
# Only forward the protocol version if it was set locally (i.e. the HOANotifications feature flag is on).
test -n '$WARP_CLI_AGENT_PROTOCOL_VERSION' && export WARP_CLI_AGENT_PROTOCOL_VERSION='$WARP_CLI_AGENT_PROTOCOL_VERSION'
hook="'$(printf "{\"hook\": \"SSH\", \"value\": {\"socket_path\": \"'$control_path'\", \"remote_shell\": \"%s\", \"session_id\": '"$WARP_SESSION_ID"', \"remote_session_id\": '"$remote_session_id"', \"external_control_master\": '"$external_control_master"'}}" "${SHELL##*/}" | command od -An -v -tx1 | command tr -d " \n")'"
printf '$DCS_START$DCS_JSON_MARKER%s$DCS_END' "'$hook'"
if test "'"${SHELL##*/}" != "bash" -a "${SHELL##*/}" != "zsh"'"; then
# Emulate the SSHD logic to print the MotD. Because the Warp SSH wrapper passes
# a command to run, SSHD does a quiet login, updating utmp and other login
# state, but not printing the MotD. For bash and zsh, this is instead handled
# by our bootstrap script.
if test ! -e "'$HOME/.hushlogin'"; then
# This uses an if-else chain instead of a for-loop to avoid expansion issues on older shells.
if test -r /etc/motd; then
cat /etc/motd
elif test -r /run/motd; then
cat /run/motd
elif test -r /run/motd.dynamic; then
cat /run/motd.dynamic
elif test -r /usr/lib/motd; then
cat /usr/lib/motd
elif test -r /usr/lib/motd.dynamic; then
cat /usr/lib/motd.dynamic
fi
fi
# Likewise, emulate a login shell by sourcing /etc/profile
if test -r /etc/profile; then
. /etc/profile
fi
exec "'$SHELL'"
fi
case "'${SHELL##*/}'" in
bash)
exec -a bash bash --rcfile <(echo '"'
stty raw
HISTCONTROL=ignorespace
HISTIGNORE=" *"
WARP_SESSION_ID='$remote_session_id'
WARP_HONOR_PS1="'$WARP_HONOR_PS1'"
_hostname=$(command -pv hostname >/dev/null 2>&1 && command -p hostname 2>/dev/null || uname -n)
_user=$(command -pv whoami >/dev/null 2>&1 && command -p whoami 2>/dev/null || echo $USER)
_msg=$(printf "{\"hook\": \"InitShell\", \"value\": {\"session_id\": $WARP_SESSION_ID, \"shell\": \"bash\", \"user\": \"%s\", \"hostname\": \"%s\"}}" "$_user" "$_hostname" | command -p od -An -v -tx1 | command -p tr -d " \n")'"
WARP_USING_WINDOWS_CON_PTY=@@USING_CON_PTY_BOOLEAN@@
if [[ "'$OS'" == Windows_NT ]]; then WARP_IN_MSYS2=true; else WARP_IN_MSYS2=false; fi
printf '\''"'\x1b\x50\x24\x64%s\x1b\x5c'"'\'' \""'$_msg'"\"'
unset _hostname _user _msg
)
;;
zsh) WARP_TMP_DIR="'$(mktemp -d warptmp.XXXXXX)'"
local ZSH_ENV_SCRIPT='$zsh_env_script'
if [[ "'$?'" == 0 ]]; then
if command -v xxd >/dev/null 2>&1; then
echo "'$ZSH_ENV_SCRIPT'" | command xxd -p -r > "'$WARP_TMP_DIR'"/.zshenv
else
for i in {0..\$((\${#ZSH_ENV_SCRIPT} - 1))..2}; do
builtin printf "'"\x${ZSH_ENV_SCRIPT:$i:2}"'"
done > "'$WARP_TMP_DIR'"/.zshenv
fi
else
echo \"Failed to bootstrap warp. Continuing with a non-bootstrapped shell.\"
fi
TMPPREFIX="'$HOME/.zshtmp-'" WARP_SSH_RCFILES="'${ZDOTDIR:-$HOME}'" ZDOTDIR="'$WARP_TMP_DIR'" exec -l zsh -g $TRACE_FLAG_IF_WARP_SHELL_DEBUG_MODE
;;
esac
"
end
function ssh
if is_interactive_ssh_session $argv
warp_send_json_message "{\"hook\": \"PreInteractiveSSHSession\", \"value\": {\"session_id\": $WARP_SESSION_ID}}"
if [ "$WARP_USE_SSH_WRAPPER" = "1" ]
if test $WARP_SHELL_DEBUG_MODE
set -g TRACE_FLAG_IF_WARP_SHELL_DEBUG_MODE "-x"
else
set -g TRACE_FLAG_IF_WARP_SHELL_DEBUG_MODE ""
end
warp_ssh_helper $argv
else
command ssh $argv
end
else
command ssh $argv
end
end
end
warp_precmd
# Print the MotD if this is a login shell. Normally, login(1) or pam_motd(8)
# would do this. However, Warp does not use login(1) for local sessions and for
# remote sessions, SSHD thinks it is starting a non-interactive session, so it
# does not print PAM messages.
if status --is-login
and test ! -e "$HOME/.hushlogin"
for motd_file in /etc/motd /run/motd /run/motd.dynamic /usr/lib/motd /usr/lib/motd.dynamic;
if test -r "$motd_file"
command cat "$motd_file"
break
end
end
end
warp_bootstrapped
set -g WARP_BOOTSTRAPPED 1
set -g fish_private_mode $saved_fish_private_mode
end