mirror of
https://github.com/openai/codex.git
synced 2026-05-01 01:47:18 +00:00
fix(windows) shell_command on windows, minor parsing (#6811)
## Summary Enables shell_command for windows users, and starts adding some basic command parsing here, to at least remove powershell prefixes. We'll follow this up with command parsing but I wanted to land this change separately with some basic UX. **NOTE**: This implementation parses bash and powershell on both platforms. In theory this is possible, since you can use git bash on windows or powershell on linux. In practice, this may not be worth the complexity of supporting, so I don't feel strongly about the current approach vs. platform-specific branching. ## Testing - [x] Added a bunch of tests - [x] Ran on both windows and os x
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use crate::bash::extract_bash_command;
|
||||
use crate::bash::try_parse_shell;
|
||||
use crate::bash::try_parse_word_only_commands_sequence;
|
||||
use crate::powershell::extract_powershell_command;
|
||||
use codex_protocol::parse_command::ParsedCommand;
|
||||
use shlex::split as shlex_split;
|
||||
use shlex::try_join as shlex_try_join;
|
||||
@@ -11,6 +12,11 @@ pub fn shlex_join(tokens: &[String]) -> String {
|
||||
.unwrap_or_else(|_| "<command included NUL byte>".to_string())
|
||||
}
|
||||
|
||||
/// Extracts the shell and script from a command, regardless of platform
|
||||
pub fn extract_shell_command(command: &[String]) -> Option<(&str, &str)> {
|
||||
extract_bash_command(command).or_else(|| extract_powershell_command(command))
|
||||
}
|
||||
|
||||
/// DO NOT REVIEW THIS CODE BY HAND
|
||||
/// This parsing code is quite complex and not easy to hand-modify.
|
||||
/// The easiest way to iterate is to add unit tests and have Codex fix the implementation.
|
||||
@@ -877,6 +883,42 @@ mod tests {
|
||||
}],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn powershell_command_is_stripped() {
|
||||
assert_parsed(
|
||||
&vec_str(&["powershell", "-Command", "Get-ChildItem"]),
|
||||
vec![ParsedCommand::Unknown {
|
||||
cmd: "Get-ChildItem".to_string(),
|
||||
}],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pwsh_with_noprofile_and_c_alias_is_stripped() {
|
||||
assert_parsed(
|
||||
&vec_str(&["pwsh", "-NoProfile", "-c", "Write-Host hi"]),
|
||||
vec![ParsedCommand::Unknown {
|
||||
cmd: "Write-Host hi".to_string(),
|
||||
}],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn powershell_with_path_is_stripped() {
|
||||
let command = if cfg!(windows) {
|
||||
"C:\\windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
|
||||
} else {
|
||||
"/usr/local/bin/powershell.exe"
|
||||
};
|
||||
|
||||
assert_parsed(
|
||||
&vec_str(&[command, "-NoProfile", "-c", "Write-Host hi"]),
|
||||
vec![ParsedCommand::Unknown {
|
||||
cmd: "Write-Host hi".to_string(),
|
||||
}],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_command_impl(command: &[String]) -> Vec<ParsedCommand> {
|
||||
@@ -884,6 +926,12 @@ pub fn parse_command_impl(command: &[String]) -> Vec<ParsedCommand> {
|
||||
return commands;
|
||||
}
|
||||
|
||||
if let Some((_, script)) = extract_powershell_command(command) {
|
||||
return vec![ParsedCommand::Unknown {
|
||||
cmd: script.to_string(),
|
||||
}];
|
||||
}
|
||||
|
||||
let normalized = normalize_tokens(command);
|
||||
|
||||
let parts = if contains_connectors(&normalized) {
|
||||
@@ -1190,6 +1238,7 @@ fn parse_find_query_and_path(tail: &[String]) -> (Option<String>, Option<String>
|
||||
}
|
||||
|
||||
fn parse_shell_lc_commands(original: &[String]) -> Option<Vec<ParsedCommand>> {
|
||||
// Only handle bash/zsh here; PowerShell is stripped separately without bash parsing.
|
||||
let (_, script) = extract_bash_command(original)?;
|
||||
|
||||
if let Some(tree) = try_parse_shell(script)
|
||||
|
||||
Reference in New Issue
Block a user