Skip to content
Draft
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 codex-rs/app-server/tests/common/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub use responses::create_final_assistant_message_sse_response;
pub use responses::create_request_permissions_sse_response;
pub use responses::create_request_user_input_sse_response;
pub use responses::create_shell_command_sse_response;
pub use responses::create_shell_command_sse_response_from_command;
pub use rollout::create_fake_rollout;
pub use rollout::create_fake_rollout_with_source;
pub use rollout::create_fake_rollout_with_text_elements;
Expand Down
32 changes: 29 additions & 3 deletions codex-rs/app-server/tests/common/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,37 @@ pub fn create_shell_command_sse_response(
) -> anyhow::Result<String> {
// The `arguments` for the `shell_command` tool is a serialized JSON object.
let command_str = shlex::try_join(command.iter().map(String::as_str))?;
let tool_call_arguments = serde_json::to_string(&json!({
"command": command_str,
create_shell_command_sse_response_from_command(
&command_str,
workdir,
timeout_ms,
call_id,
/*login*/ None,
)
}

pub fn create_shell_command_sse_response_from_command(
command: &str,
workdir: Option<&Path>,
timeout_ms: Option<u64>,
call_id: &str,
login: Option<bool>,
) -> anyhow::Result<String> {
// Use this when a test already has a shell command string. It fixes string
// quoting for those callers by avoiding a rebuild from argv with POSIX
// rules, which can change how Windows PowerShell parses the command; for
// sleep-based tests, nested parsing can add another PowerShell startup
// before the requested sleep even begins.
let mut tool_call_arguments = json!({
"command": command,
"workdir": workdir.map(|w| w.to_string_lossy()),
"timeout_ms": timeout_ms
}))?;
});
if let Some(login) = login {
tool_call_arguments["login"] = json!(login);
}

let tool_call_arguments = serde_json::to_string(&tool_call_arguments)?;
Ok(responses::sse(vec![
responses::ev_response_created("resp-1"),
responses::ev_function_call(call_id, "shell_command", &tool_call_arguments),
Expand Down
10 changes: 8 additions & 2 deletions codex-rs/core/src/plugins/marketplace_add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,18 @@ mod tests {

let config = fs::read_to_string(codex_home.path().join(codex_config::CONFIG_TOML_FILE))?;
let config: toml::Value = toml::from_str(&config)?;
let marketplace = config
.get("marketplaces")
.and_then(toml::Value::as_table)
.and_then(|marketplaces| marketplaces.get("debug"))
.and_then(toml::Value::as_table)
.expect("debug marketplace should be present in config");
assert_eq!(
config["marketplaces"]["debug"]["source_type"].as_str(),
marketplace.get("source_type").and_then(toml::Value::as_str),
Some("local")
);
assert_eq!(
config["marketplaces"]["debug"]["source"].as_str(),
marketplace.get("source").and_then(toml::Value::as_str),
Some(expected_source.as_str())
);
Ok(())
Expand Down
18 changes: 18 additions & 0 deletions codex-rs/core/src/plugins/marketplace_add/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ fn looks_like_local_path(source: &str) -> bool {
|| source.starts_with("~/")
|| source == "."
|| source == ".."
|| {
#[cfg(windows)]
{
source.starts_with(".\\") || source.starts_with("..\\") || source.starts_with('\\')
}
#[cfg(not(windows))]
{
false
}
}
}

fn resolve_local_source_path(source: &str) -> Result<PathBuf, MarketplaceAddError> {
Expand Down Expand Up @@ -310,6 +320,14 @@ mod tests {
assert!(path.is_absolute());
}

#[cfg(windows)]
#[test]
fn windows_backslash_relative_path_looks_local() {
assert!(looks_like_local_path(r".\marketplace"));
assert!(looks_like_local_path(r"..\marketplace"));
assert!(looks_like_local_path(r"\marketplace"));
}

#[test]
fn local_file_source_is_rejected() {
let tempdir = TempDir::new().unwrap();
Expand Down
Loading