From c180a79a68974292868e628133b00cf273bcc567 Mon Sep 17 00:00:00 2001 From: David Wiesen Date: Thu, 11 Jun 2026 09:20:06 -0700 Subject: [PATCH] Map Windows attachment paths for WSL view_image --- codex-rs/core/src/session/turn_context.rs | 7 ++-- codex-rs/utils/path-utils/src/lib.rs | 36 +++++++++++++++++++ .../utils/path-utils/src/path_utils_tests.rs | 29 +++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index a68b5396a0..87f418b541 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -8,6 +8,7 @@ use codex_protocol::protocol::TurnEnvironmentSelection; use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile; use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy; use codex_sandboxing::policy_transforms::effective_network_sandbox_policy; +use codex_utils_path::normalize_input_path_for_wsl; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; @@ -268,8 +269,10 @@ impl TurnContext { } pub(crate) fn resolve_path(&self, path: Option) -> AbsolutePathBuf { - path.as_ref() - .map_or_else(|| self.cwd.clone(), |path| self.cwd.join(path)) + path.as_ref().map_or_else( + || self.cwd.clone(), + |path| self.cwd.join(normalize_input_path_for_wsl(path)), + ) } pub(crate) fn file_system_sandbox_context( diff --git a/codex-rs/utils/path-utils/src/lib.rs b/codex-rs/utils/path-utils/src/lib.rs index 71b0a6473d..38fbf594d6 100644 --- a/codex-rs/utils/path-utils/src/lib.rs +++ b/codex-rs/utils/path-utils/src/lib.rs @@ -32,6 +32,42 @@ pub fn normalize_for_native_workdir(path: impl AsRef) -> PathBuf { normalize_for_native_workdir_with_flag(path.as_ref().to_path_buf(), cfg!(windows)) } +/// Convert a Windows absolute path (`C:\foo\bar` or `C:/foo/bar`) to a WSL mount +/// path (`/mnt/c/foo/bar`). Returns `None` if the input does not look like a Windows +/// drive path. +pub fn win_path_to_wsl(path: &str) -> Option { + let bytes = path.as_bytes(); + if bytes.len() < 3 + || bytes[1] != b':' + || !(bytes[2] == b'\\' || bytes[2] == b'/') + || !bytes[0].is_ascii_alphabetic() + { + return None; + } + + let drive = (bytes[0] as char).to_ascii_lowercase(); + let tail = path[3..].replace('\\', "/"); + if tail.is_empty() { + return Some(format!("/mnt/{drive}")); + } + + Some(format!("/mnt/{drive}/{tail}")) +} + +/// When running under WSL, translate Windows drive paths into the equivalent +/// `/mnt//...` form before path resolution. Other inputs are returned unchanged. +pub fn normalize_input_path_for_wsl(path: &str) -> PathBuf { + normalize_input_path_for_wsl_with_flag(path, env::is_wsl()) +} + +fn normalize_input_path_for_wsl_with_flag(path: &str, is_wsl: bool) -> PathBuf { + if is_wsl && let Some(mapped) = win_path_to_wsl(path) { + return PathBuf::from(mapped); + } + + PathBuf::from(path) +} + pub struct SymlinkWritePaths { pub read_path: Option, pub write_path: PathBuf, diff --git a/codex-rs/utils/path-utils/src/path_utils_tests.rs b/codex-rs/utils/path-utils/src/path_utils_tests.rs index 94c914fec2..94b7c141e5 100644 --- a/codex-rs/utils/path-utils/src/path_utils_tests.rs +++ b/codex-rs/utils/path-utils/src/path_utils_tests.rs @@ -24,6 +24,8 @@ mod symlinks { #[cfg(target_os = "linux")] mod wsl { use super::super::normalize_for_wsl_with_flag; + use super::super::normalize_input_path_for_wsl_with_flag; + use super::super::win_path_to_wsl; use pretty_assertions::assert_eq; use std::path::PathBuf; @@ -50,6 +52,33 @@ mod wsl { assert_eq!(normalized, path); } + + #[test] + fn converts_windows_drive_paths_to_wsl_mounts() { + assert_eq!( + win_path_to_wsl(r"C:\Users\SLee\AppData\Local\Temp\image.png").as_deref(), + Some("/mnt/c/Users/SLee/AppData/Local/Temp/image.png") + ); + assert_eq!( + win_path_to_wsl("D:/Work/codex.png").as_deref(), + Some("/mnt/d/Work/codex.png") + ); + } + + #[test] + fn normalize_input_path_maps_windows_paths_in_wsl() { + assert_eq!( + normalize_input_path_for_wsl_with_flag( + r"C:\Users\SLee\AppData\Local\Temp\image.png", + /*is_wsl*/ true + ), + PathBuf::from("/mnt/c/Users/SLee/AppData/Local/Temp/image.png") + ); + assert_eq!( + normalize_input_path_for_wsl_with_flag("/tmp/copied-image.png", /*is_wsl*/ true), + PathBuf::from("/tmp/copied-image.png") + ); + } } mod native_workdir {