Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/assets/bundled/bootstrap/nu.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include bundled/bootstrap/nu_body.nu
228 changes: 228 additions & 0 deletions app/assets/bundled/bootstrap/nu_body.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
if ($env.WARP_BOOTSTRAPPED? | default "") == "" {
$env.WARP_USING_WINDOWS_CON_PTY = @@USING_CON_PTY_BOOLEAN@@

if ($env.WARP_INITIAL_WORKING_DIR? | default "") != "" {
try { cd $env.WARP_INITIAL_WORKING_DIR } catch { null }
hide-env WARP_INITIAL_WORKING_DIR
}

if ($env.WARP_PATH_APPEND? | default "") != "" {
let extra_paths = ($env.WARP_PATH_APPEND | split row (char esep) | where {|path| $path != "" })
if (($env.PATH | describe) | str starts-with "list") {
$env.PATH = ($env.PATH ++ $extra_paths)
} else {
$env.PATH = ([($env.PATH | into string)] ++ $extra_paths | str join (char esep))
}
hide-env WARP_PATH_APPEND
}

def warp_path_string [] {
let path = ($env.PATH? | default [])
if (($path | describe) | str starts-with "list") {
$path | str join (char esep)
} else {
$path | into string
}
}

def warp_command_names_by_type [command_type: string] {
try {
scope commands | where type == $command_type | get name | uniq | str join (char nl)
} catch { "" }
}

def warp_linux_distribution [] {
let os_release_file = if ("/etc/os-release" | path exists) {
"/etc/os-release"
} else if ("/usr/lib/os-release" | path exists) {
"/usr/lib/os-release"
} else {
""
}

if $os_release_file == "" {
""
} else {
try {
open $os_release_file
| lines
| where {|line| $line | str starts-with "NAME=" }
| first
| str replace -r '^NAME="?(.*?)"?$' '$1'
} catch { "" }
}
}

def warp_send_json_message [message: record] {
let encoded_message = ($message | to json -r | encode hex)
if ($env.WARP_USING_WINDOWS_CON_PTY? | default false) {
print -n $"\u{1b}]9278;d;($encoded_message)\a"
} else {
print -n $"\u{1b}P$d($encoded_message)\u{1b}\\"
}
}

def warp_send_reset_grid_osc [] {
if ($env.WARP_USING_WINDOWS_CON_PTY? | default false) {
print -n "\u{1b}]9279\a"
}
}

def warp_send_generator_output_osc [message: string] {
let hex_encoded_message = ($message | encode hex)
let byte_count = ($hex_encoded_message | str length)
print -n $"\u{1b}]9277;A\a($byte_count);($hex_encoded_message)\u{1b}]9277;B\a"
warp_send_reset_grid_osc
}

def --env warp_run_generator_command [command_id: string, command_text: string] {
$env._WARP_GENERATOR_COMMAND = "1"
let result = (try { ^$nu.current-exe -c $command_text | complete } catch { { stdout: "", stderr: ($in | into string), exit_code: 1 } })
let raw_output = ([$result.stdout $result.stderr] | where {|part| ($part | into string) != "" } | str join (char nl))
warp_send_generator_output_osc $"($command_id);($raw_output);($result.exit_code)"
}

def warp_preexec [] {
let command_text = (try { commandline } catch { "" })
warp_send_json_message { hook: "Preexec", value: { command: $command_text } }
warp_send_reset_grid_osc
}

def --env warp_precmd [] {
if ($env._WARP_SUPPRESS_NEXT_PRECMD? | default "") != "" {
hide-env _WARP_SUPPRESS_NEXT_PRECMD
return
}

let exit_code = ($env.LAST_EXIT_CODE? | default 0)
let next_block_id = $"precmd-($env.WARP_SESSION_ID)-(random int 0..2147483647)"
warp_send_json_message { hook: "CommandFinished", value: { exit_code: $exit_code, next_block_id: $next_block_id } }
warp_send_reset_grid_osc

if ($env._WARP_GENERATOR_COMMAND? | default "") != "" {
hide-env _WARP_GENERATOR_COMMAND
warp_send_json_message { hook: "Precmd", value: { pwd: "", ps1: "", git_head: "", git_branch: "", virtual_env: "", conda_env: "", node_version: "", session_id: ($env.WARP_SESSION_ID | into int), is_after_in_band_command: true } }
return
}

let git_branch = (try { ^git symbolic-ref --short HEAD err> /dev/null | str trim } catch { "" })
let git_head = if $git_branch != "" { $git_branch } else { try { ^git rev-parse --short HEAD err> /dev/null | str trim } catch { "" } }
let honor_ps1 = (($env.WARP_HONOR_PS1? | default "0") == "1")
warp_send_json_message { hook: "Precmd", value: { pwd: (pwd), ps1: "", honor_ps1: $honor_ps1, rprompt: "", git_head: $git_head, git_branch: $git_branch, virtual_env: ($env.VIRTUAL_ENV? | default ""), conda_env: ($env.CONDA_DEFAULT_ENV? | default ""), node_version: "", kube_config: ($env.KUBECONFIG? | default ""), session_id: ($env.WARP_SESSION_ID | into int) } }
}

def warp_report_input [] {
let input_buffer = (try { commandline } catch { "" })
warp_send_json_message { hook: "InputBuffer", value: { buffer: $input_buffer } }
try { commandline edit "" } catch { null }
}

def warp_finish_update [update_id: string] {
warp_send_json_message { hook: "FinishUpdate", value: { update_id: $update_id } }
}

def warp_handle_dist_upgrade [source_file_name: string] {
let apt_config = (try { which apt-config | get path | first } catch { "" })
if $apt_config == "" { return }
let apt_sources_dir = (try { ^sh -c $"eval $\((^($apt_config) shell APT_SOURCESDIR 'Dir::Etc::sourceparts/d')\); printf %s $APT_SOURCESDIR" } catch { "" })
if $apt_sources_dir == "" { return }
let source_file_path = $"($apt_sources_dir)($source_file_name)"
if not ($"($source_file_path).list" | path exists) and not ($"($source_file_path).sources" | path exists) and ($"($source_file_path).list.distUpgrade" | path exists) {
print $"Executing: sudo cp \"($source_file_path).list.distUpgrade\" \"($source_file_path).list\""
sudo cp $"($source_file_path).list.distUpgrade" $"($source_file_path).list"
}
}

def clear [] {
warp_send_json_message { hook: "Clear", value: {} }
}

def --env warp_change_prompt_modes_to_ps1 [] {
$env.WARP_HONOR_PS1 = "1"
warp_set_prompt_indicators
}

def --env warp_change_prompt_modes_to_warp_prompt [] {
$env.WARP_HONOR_PS1 = "0"
warp_set_prompt_indicators
}

def warp_bootstrapped [] {
let history_format = ($env.config.history.file_format? | default "plaintext")
let histfile = if $history_format == "plaintext" { $nu.history-path } else { "" }
let alias_lines = (try { scope aliases | each {|alias| $"($alias.name)\t($alias.expansion? | default "")" } | str join (char nl) } catch { "" })
let env_var_names = (try { $env | columns | str join (char nl) } catch { "" })
let os_name = ($nu.os-info.name? | default "")
let os_category = if $os_name == "macos" { "MacOS" } else if $os_name == "linux" { "Linux" } else if $os_name == "windows" { "Windows" } else { "" }
let linux_distribution = if $os_category == "Linux" { warp_linux_distribution } else { "" }
let vi_mode_enabled = if (($env.config.edit_mode? | default "") == "vi") { "1" } else { "" }
warp_send_json_message { hook: "Bootstrapped", value: { histfile: $histfile, shell: "nu", home_dir: ($nu.home-path? | default ($env.HOME? | default "")), path: (warp_path_string), editor: ($env.EDITOR? | default ""), abbreviations: "", aliases: $alias_lines, function_names: (warp_command_names_by_type "custom"), env_var_names: $env_var_names, builtins: (warp_command_names_by_type "built-in"), keywords: (warp_command_names_by_type "keyword"), shell_version: (version | get version), shell_options: "", rcfiles_start_time: "", rcfiles_end_time: "", shell_plugins: "", vi_mode_enabled: $vi_mode_enabled, os_category: $os_category, linux_distribution: $linux_distribution, wsl_name: ($env.WSL_DISTRO_NAME? | default ""), shell_path: $nu.current-exe } }
}

let warp_original_prompt_command = ($env.PROMPT_COMMAND? | default null)
let warp_original_prompt_command_right = ($env.PROMPT_COMMAND_RIGHT? | default null)
$env.WARP_ORIGINAL_PROMPT_INDICATOR = ($env.PROMPT_INDICATOR? | default "> ")
$env.WARP_ORIGINAL_PROMPT_INDICATOR_VI_INSERT = ($env.PROMPT_INDICATOR_VI_INSERT? | default ": ")
$env.WARP_ORIGINAL_PROMPT_INDICATOR_VI_NORMAL = ($env.PROMPT_INDICATOR_VI_NORMAL? | default "> ")
$env.WARP_ORIGINAL_PROMPT_MULTILINE_INDICATOR = ($env.PROMPT_MULTILINE_INDICATOR? | default "::: ")

def --env warp_set_prompt_indicators [] {
if (($env.WARP_HONOR_PS1? | default "0") == "1") {
$env.PROMPT_INDICATOR = ($env.WARP_ORIGINAL_PROMPT_INDICATOR? | default "> ")
$env.PROMPT_INDICATOR_VI_INSERT = ($env.WARP_ORIGINAL_PROMPT_INDICATOR_VI_INSERT? | default ": ")
$env.PROMPT_INDICATOR_VI_NORMAL = ($env.WARP_ORIGINAL_PROMPT_INDICATOR_VI_NORMAL? | default "> ")
$env.PROMPT_MULTILINE_INDICATOR = ($env.WARP_ORIGINAL_PROMPT_MULTILINE_INDICATOR? | default "::: ")
} else {
$env.PROMPT_INDICATOR = ""
$env.PROMPT_INDICATOR_VI_INSERT = ""
$env.PROMPT_INDICATOR_VI_NORMAL = ""
$env.PROMPT_MULTILINE_INDICATOR = ""
}
}

$env.PROMPT_COMMAND = {||
let prompt = if (($env.WARP_HONOR_PS1? | default "0") == "1") {
if (($warp_original_prompt_command | describe) == "closure") {
do $warp_original_prompt_command
} else if $warp_original_prompt_command == null {
"> "
} else {
$warp_original_prompt_command | into string
}
} else { "" }
$"\u{1b}]133;A\a($prompt)\u{1b}]133;B\a"
}

$env.PROMPT_COMMAND_RIGHT = {||
let prompt = if (($env.WARP_HONOR_PS1? | default "0") == "1") {
if (($warp_original_prompt_command_right | describe) == "closure") {
do $warp_original_prompt_command_right
} else if $warp_original_prompt_command_right == null {
""
} else {
$warp_original_prompt_command_right | into string
}
} else { "" }
if $prompt == "" { "" } else { $"\u{1b}]133;P;k=r\a($prompt)\u{1b}]133;B\a" }
}

$env.config = (
$env.config
| upsert shell_integration.osc133 false
| upsert shell_integration.osc633 false
| upsert hooks.pre_execution ([{|| warp_preexec }] ++ ($env.config.hooks.pre_execution? | default []))
| upsert hooks.pre_prompt ([{|| warp_precmd }] ++ ($env.config.hooks.pre_prompt? | default []))
| upsert keybindings (($env.config.keybindings? | default []) ++ [
{ name: warp_clear_commandline, modifier: control, keycode: char_p, mode: [emacs vi_normal vi_insert], event: { edit: Clear } }
{ name: warp_report_input, modifier: alt, keycode: char_i, mode: [emacs vi_normal vi_insert], event: { send: ExecuteHostCommand, cmd: "warp_report_input" } }
{ name: warp_prompt_ps1, modifier: alt, keycode: char_p, mode: [emacs vi_normal vi_insert], event: { send: ExecuteHostCommand, cmd: "warp_change_prompt_modes_to_ps1" } }
{ name: warp_prompt_warp, modifier: alt, keycode: char_w, mode: [emacs vi_normal vi_insert], event: { send: ExecuteHostCommand, cmd: "warp_change_prompt_modes_to_warp_prompt" } }
])
)

warp_set_prompt_indicators
warp_precmd
warp_bootstrapped
$env.WARP_BOOTSTRAPPED = "1"
$env._WARP_SUPPRESS_NEXT_PRECMD = "1"
}
6 changes: 6 additions & 0 deletions app/assets/bundled/bootstrap/nu_init_shell.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
$env.WARP_SESSION_ID = ((date now | format date "%s") + (random int 0..32767 | into string))
let username = ($env.USER? | default ($env.USERNAME? | default ""))
let hostname = (try { ^hostname | str trim } catch { $nu.hostname? | default "" })
let msg = ({ hook: "InitShell", value: { session_id: ($env.WARP_SESSION_ID | into int), shell: "nu", user: $username, hostname: $hostname } } | to json -r | encode hex)
let using_windows_con_pty = @@USING_CON_PTY_BOOLEAN@@
if $using_windows_con_pty { print -n $"\u{1b}]9278;d;($msg)\a" } else { print -n $"\u{1b}P$d($msg)\u{1b}\\" }
3 changes: 3 additions & 0 deletions app/src/ai/blocklist/action_model/execute/shell_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ impl ShellCommandExecutor {
// Fish doesn't have grouping characters. We need to use begin; and end; to ensure the command
// gets evaluated first.
Some(ShellType::Fish) => format!("begin; {command} ;end | command cat"),
// Nushell does not have an equivalent `command cat` pager bypass; group the command
// so it still runs as a single expression.
Some(ShellType::Nu) => format!("({command})"),
// For powershell, we use Out-Host to send paged output to the
// console. Add a backslash to avoid executing an alias.
Some(ShellType::PowerShell) => format!("({command}) | \\Out-Host"),
Expand Down
61 changes: 59 additions & 2 deletions app/src/autoupdate/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ pub enum PackageManager {

impl PackageManager {
pub fn update_command(&self, shell_type: ShellType, update_id: &str) -> String {
if shell_type == ShellType::Nu {
return self.nu_update_command(update_id);
}

let package_name = Self::package_name();
let repo_name = Self::repo_name();
let and = shell_type.and_combiner();
Expand All @@ -362,7 +366,7 @@ impl PackageManager {
distribution_update_disabled_repository,
} => {
let dist_upgrade_fn = match shell_type {
ShellType::Zsh | ShellType::Bash | ShellType::Fish => {
ShellType::Zsh | ShellType::Bash | ShellType::Fish | ShellType::Nu => {
"warp_handle_dist_upgrade"
}
ShellType::PowerShell => "Warp-Handle-DistUpgrade",
Expand Down Expand Up @@ -414,12 +418,65 @@ impl PackageManager {
};

let finish_update_fn = match shell_type {
ShellType::Zsh | ShellType::Bash | ShellType::Fish => "warp_finish_update",
ShellType::Zsh | ShellType::Bash | ShellType::Fish | ShellType::Nu => {
"warp_finish_update"
}
ShellType::PowerShell => "Warp-Finish-Update",
};
format!("{base_command}{and}{finish_update_fn} {update_id}")
}

fn nu_update_command(&self, update_id: &str) -> String {
let package_name = Self::package_name();
let repo_name = Self::repo_name();

let base_command = match self {
PackageManager::Apt {
distribution_update_disabled_repository,
} => {
let dist_upgrade_prefix = if *distribution_update_disabled_repository {
format!("try {{ warp_handle_dist_upgrade {repo_name} }} catch {{ null }}; ")
} else {
String::new()
};
format!("{dist_upgrade_prefix}sudo apt update; sudo apt install {package_name}")
}
PackageManager::Yum => {
format!("sudo yum --refresh --repo {repo_name} upgrade {package_name}")
}
PackageManager::Dnf => {
format!("sudo dnf --refresh --repo {repo_name} upgrade {package_name}")
}
PackageManager::Zypper => {
format!("sudo zypper update {package_name}")
}
PackageManager::Pacman {
is_repo_configured,
is_signing_key_configured,
} => {
let repo_prefix = if !is_repo_configured {
let cache_dir = warp_core::paths::cache_dir();
let cache_dir_str = cache_dir.display();
// Back up the existing pacman.conf file just in case anything goes wrong, then
// add the repository config.
format!("^mkdir -p {cache_dir_str}; ^cp /etc/pacman.conf {cache_dir_str}; sudo sh -c \"echo '\n[{repo_name}]\nServer = https://releases.warp.dev/linux/pacman/\\$repo/\\$arch' >> /etc/pacman.conf\"; ")
} else {
String::new()
};
let key_prefix = if !is_signing_key_configured {
// Retrieve our key from keys.openpgp.org and locally sign it before retrieving
// the package repository and installing the updated package.
"sudo pacman-key -r \"linux-maintainers@warp.dev\" --keyserver hkp://keys.openpgp.org:80; sudo pacman-key --lsign-key \"linux-maintainers@warp.dev\"; ".to_string()
} else {
String::new()
};
format!("{key_prefix}{repo_prefix}sudo pacman -Sy {package_name}")
}
};

format!("try {{ {base_command}; warp_finish_update {update_id} }} catch {{ print $in }}")
}

fn package_name() -> &'static str {
package_name(ChannelState::channel())
}
Expand Down
25 changes: 25 additions & 0 deletions app/src/autoupdate/linux_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,28 @@ fn test_repo_name() {
assert_eq!(repo_name(Channel::Dev), "warpdotdev-dev");
assert_eq!(repo_name(Channel::Stable), "warpdotdev");
}

#[test]
fn test_nu_update_command_gates_finish_update_on_success() {
let command = PackageManager::Apt {
distribution_update_disabled_repository: false,
}
.update_command(ShellType::Nu, "update-123");

assert!(command.starts_with("try { "));
assert!(command.contains("sudo apt update; sudo apt install "));
assert!(command.contains("; warp_finish_update update-123 }"));
assert!(!command.contains(" && "));
}

#[test]
fn test_nu_update_command_uses_nu_dist_upgrade_handler() {
let command = PackageManager::Apt {
distribution_update_disabled_repository: true,
}
.update_command(ShellType::Nu, "update-123");

assert!(command.contains("try { warp_handle_dist_upgrade "));
assert!(command.contains(" } catch { null }; sudo apt update"));
assert!(command.contains("; warp_finish_update update-123 }"));
}
Loading