diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index e68e85b40d..ad34bc060f 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1653,6 +1653,7 @@ dependencies = [ "codex-rmcp-client", "codex-secrets", "codex-shell-command", + "codex-shell-exec-bridge", "codex-skills", "codex-state", "codex-utils-absolute-path", @@ -1786,6 +1787,7 @@ dependencies = [ "codex-execpolicy", "codex-protocol", "codex-shell-command", + "codex-shell-exec-bridge", "codex-utils-cargo-bin", "core_test_support", "exec_server_test_support", @@ -2201,6 +2203,21 @@ dependencies = [ "which", ] +[[package]] +name = "codex-shell-exec-bridge" +version = "0.0.0" +dependencies = [ + "anyhow", + "libc", + "pretty_assertions", + "serde", + "serde_json", + "socket2 0.6.2", + "tempfile", + "tokio", + "tracing", +] + [[package]] name = "codex-skills" version = "0.0.0" diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index e22dba12c2..9e165238c6 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -21,6 +21,7 @@ members = [ "core", "hooks", "secrets", + "shell-exec-bridge", "exec", "exec-server", "execpolicy", @@ -113,6 +114,7 @@ codex-responses-api-proxy = { path = "responses-api-proxy" } codex-rmcp-client = { path = "rmcp-client" } codex-secrets = { path = "secrets" } codex-shell-command = { path = "shell-command" } +codex-shell-exec-bridge = { path = "shell-exec-bridge" } codex-skills = { path = "skills" } codex-state = { path = "state" } codex-stdio-to-uds = { path = "stdio-to-uds" } diff --git a/codex-rs/app-server/src/main.rs b/codex-rs/app-server/src/main.rs index c56c3c8f98..e29efd9d7d 100644 --- a/codex-rs/app-server/src/main.rs +++ b/codex-rs/app-server/src/main.rs @@ -26,7 +26,7 @@ fn main() -> anyhow::Result<()> { arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move { // Run wrapper mode only after arg0 dispatch so `codex-linux-sandbox` // invocations don't get misclassified as zsh exec-wrapper calls. - if codex_core::maybe_run_zsh_exec_wrapper_mode()? { + if codex_core::maybe_run_zsh_exec_wrapper_mode().await? { return Ok(()); } let args = AppServerArgs::parse(); diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index cdecb18dca..82e6d1b6ea 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -546,7 +546,7 @@ fn main() -> anyhow::Result<()> { arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move { // Run wrapper mode only after arg0 dispatch so `codex-linux-sandbox` // invocations don't get misclassified as zsh exec-wrapper calls. - if codex_core::maybe_run_zsh_exec_wrapper_mode()? { + if codex_core::maybe_run_zsh_exec_wrapper_mode().await? { return Ok(()); } cli_main(codex_linux_sandbox_exe).await?; diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index 15f9d7d11c..838bf05fde 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -43,6 +43,7 @@ codex-network-proxy = { workspace = true } codex-otel = { workspace = true } codex-protocol = { workspace = true } codex-rmcp-client = { workspace = true } +codex-shell-exec-bridge = { workspace = true } codex-state = { workspace = true } codex-utils-absolute-path = { workspace = true } codex-utils-home-dir = { workspace = true } diff --git a/codex-rs/core/src/sandboxing/mod.rs b/codex-rs/core/src/sandboxing/mod.rs index f9c3c171e7..87f7298fd0 100644 --- a/codex-rs/core/src/sandboxing/mod.rs +++ b/codex-rs/core/src/sandboxing/mod.rs @@ -163,19 +163,13 @@ impl SandboxManager { SandboxType::MacosSeatbelt => { let mut seatbelt_env = HashMap::new(); seatbelt_env.insert(CODEX_SANDBOX_ENV_VAR.to_string(), "seatbelt".to_string()); - let zsh_exec_bridge_wrapper_socket = env - .get(crate::zsh_exec_bridge::ZSH_EXEC_BRIDGE_WRAPPER_SOCKET_ENV_VAR) - .map(PathBuf::from); - let zsh_exec_bridge_allowed_unix_sockets = zsh_exec_bridge_wrapper_socket - .as_ref() - .map_or_else(Vec::new, |path| vec![path.clone()]); let mut args = create_seatbelt_command_args( command.clone(), policy, sandbox_policy_cwd, enforce_managed_network, network, - &zsh_exec_bridge_allowed_unix_sockets, + &[], ); let mut full_command = Vec::with_capacity(1 + args.len()); full_command.push(MACOS_PATH_TO_SEATBELT_EXECUTABLE.to_string()); diff --git a/codex-rs/core/src/tools/runtimes/shell.rs b/codex-rs/core/src/tools/runtimes/shell.rs index bcbeafadfe..e1d9dad690 100644 --- a/codex-rs/core/src/tools/runtimes/shell.rs +++ b/codex-rs/core/src/tools/runtimes/shell.rs @@ -26,7 +26,6 @@ use crate::tools::sandboxing::ToolCtx; use crate::tools::sandboxing::ToolError; use crate::tools::sandboxing::ToolRuntime; use crate::tools::sandboxing::with_cached_approval; -use crate::zsh_exec_bridge::ZSH_EXEC_BRIDGE_WRAPPER_SOCKET_ENV_VAR; use codex_network_proxy::NetworkProxy; use codex_protocol::protocol::ReviewDecision; use futures::future::BoxFuture; @@ -182,20 +181,10 @@ impl ToolRuntime for ShellRuntime { }; if ctx.session.features().enabled(Feature::ShellZshFork) { - let wrapper_socket_path = ctx - .session - .services - .zsh_exec_bridge - .next_wrapper_socket_path(); - let mut zsh_fork_env = req.env.clone(); - zsh_fork_env.insert( - ZSH_EXEC_BRIDGE_WRAPPER_SOCKET_ENV_VAR.to_string(), - wrapper_socket_path.to_string_lossy().to_string(), - ); let spec = build_command_spec( &command, &req.cwd, - &zsh_fork_env, + &req.env, req.timeout_ms.into(), req.sandbox_permissions, req.justification.clone(), diff --git a/codex-rs/core/src/zsh_exec_bridge/mod.rs b/codex-rs/core/src/zsh_exec_bridge/mod.rs index 8094a4b786..1cfeb341db 100644 --- a/codex-rs/core/src/zsh_exec_bridge/mod.rs +++ b/codex-rs/core/src/zsh_exec_bridge/mod.rs @@ -1,6 +1,7 @@ use crate::exec::ExecToolCallOutput; use crate::tools::sandboxing::ToolError; use std::path::PathBuf; +use std::time::Instant; use tokio::sync::Mutex; use uuid::Uuid; @@ -23,27 +24,16 @@ use codex_protocol::approvals::ExecPolicyAmendment; #[cfg(unix)] use codex_utils_pty::process_group::kill_child_process_group; #[cfg(unix)] -use serde::Deserialize; -#[cfg(unix)] -use serde::Serialize; -#[cfg(unix)] -use std::io::Read; -#[cfg(unix)] -use std::io::Write; -#[cfg(unix)] -use std::time::Instant; #[cfg(unix)] use tokio::io::AsyncReadExt; -#[cfg(unix)] -use tokio::net::UnixListener; -#[cfg(unix)] -use tokio::net::UnixStream; -pub(crate) const ZSH_EXEC_BRIDGE_WRAPPER_SOCKET_ENV_VAR: &str = - "CODEX_ZSH_EXEC_BRIDGE_WRAPPER_SOCKET"; -pub(crate) const ZSH_EXEC_WRAPPER_MODE_ENV_VAR: &str = "CODEX_ZSH_EXEC_WRAPPER_MODE"; -#[cfg(unix)] -pub(crate) const EXEC_WRAPPER_ENV_VAR: &str = "EXEC_WRAPPER"; +use codex_shell_exec_bridge::AsyncSocket; +use codex_shell_exec_bridge::EXEC_WRAPPER_ENV_VAR; +use codex_shell_exec_bridge::WrapperExecAction; +use codex_shell_exec_bridge::WrapperIpcRequest; +use codex_shell_exec_bridge::WrapperIpcResponse; +use codex_shell_exec_bridge::ZSH_EXEC_BRIDGE_SOCKET_ENV_VAR; +use codex_shell_exec_bridge::ZSH_EXEC_WRAPPER_MODE_ENV_VAR; #[derive(Debug, Clone, PartialEq, Eq, Default)] pub(crate) struct ZshExecBridgeSessionState { @@ -56,37 +46,6 @@ pub(crate) struct ZshExecBridge { state: Mutex, } -#[cfg(unix)] -#[derive(Debug, Deserialize, Serialize)] -#[serde(tag = "type", rename_all = "snake_case")] -enum WrapperIpcRequest { - ExecRequest { - request_id: String, - file: String, - argv: Vec, - cwd: String, - }, -} - -#[cfg(unix)] -#[derive(Debug, Deserialize, Serialize)] -#[serde(tag = "type", rename_all = "snake_case")] -enum WrapperIpcResponse { - ExecResponse { - request_id: String, - action: WrapperExecAction, - reason: Option, - }, -} - -#[cfg(unix)] -#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -enum WrapperExecAction { - Run, - Deny, -} - impl ZshExecBridge { pub(crate) fn new(zsh_path: Option, _codex_home: PathBuf) -> Self { Self { @@ -105,13 +64,6 @@ impl ZshExecBridge { state.initialized_session_id = None; } - pub(crate) fn next_wrapper_socket_path(&self) -> PathBuf { - let socket_id = Uuid::new_v4().as_simple().to_string(); - let temp_dir = std::env::temp_dir(); - let canonical_temp_dir = temp_dir.canonicalize().unwrap_or(temp_dir); - canonical_temp_dir.join(format!("czs-{}.sock", &socket_id[..12])) - } - #[cfg(not(unix))] pub(crate) async fn execute_shell_request( &self, @@ -145,21 +97,13 @@ impl ZshExecBridge { return Err(ToolError::Rejected("command args are empty".to_string())); } - let wrapper_socket_path = req - .env - .get(ZSH_EXEC_BRIDGE_WRAPPER_SOCKET_ENV_VAR) - .map(PathBuf::from) - .unwrap_or_else(|| self.next_wrapper_socket_path()); - - let listener = { - let _ = std::fs::remove_file(&wrapper_socket_path); - UnixListener::bind(&wrapper_socket_path).map_err(|err| { - ToolError::Rejected(format!( - "bind wrapper socket at {}: {err}", - wrapper_socket_path.display() - )) - })? - }; + let (server_socket, client_socket) = AsyncSocket::pair().map_err(|err| { + ToolError::Rejected(format!("failed to create zsh wrapper socket pair: {err}")) + })?; + client_socket.set_cloexec(false).map_err(|err| { + ToolError::Rejected(format!("disable cloexec on wrapper socket: {err}")) + })?; + let fd = client_socket.as_raw_fd().to_string(); let wrapper_path = std::env::current_exe().map_err(|err| { ToolError::Rejected(format!("resolve current executable path: {err}")) @@ -180,10 +124,7 @@ impl ZshExecBridge { cmd.kill_on_drop(true); cmd.env_clear(); cmd.envs(&req.env); - cmd.env( - ZSH_EXEC_BRIDGE_WRAPPER_SOCKET_ENV_VAR, - wrapper_socket_path.to_string_lossy().to_string(), - ); + cmd.env(ZSH_EXEC_BRIDGE_SOCKET_ENV_VAR, fd); cmd.env(EXEC_WRAPPER_ENV_VAR, &wrapper_path); cmd.env(ZSH_EXEC_WRAPPER_MODE_ENV_VAR, "1"); @@ -194,6 +135,7 @@ impl ZshExecBridge { zsh_path.display() )) })?; + drop(client_socket); let (stream_tx, mut stream_rx) = tokio::sync::mpsc::unbounded_channel::<(ExecOutputStream, Vec)>(); @@ -249,7 +191,13 @@ impl ZshExecBridge { while child_exit.is_none() || stream_open { tokio::select! { result = child.wait(), if child_exit.is_none() => { - child_exit = Some(result.map_err(|err| ToolError::Rejected(format!("wait for zsh fork command exit: {err}")))?); + child_exit = Some( + result.map_err(|err| { + ToolError::Rejected(format!( + "wait for zsh fork command exit: {err}" + )) + })? + ); } stream = stream_rx.recv(), if stream_open => { if let Some((output_stream, chunk)) = stream { @@ -271,12 +219,25 @@ impl ZshExecBridge { stream_open = false; } } - accept_result = listener.accept(), if child_exit.is_none() => { - let (stream, _) = accept_result.map_err(|err| { - ToolError::Rejected(format!("failed to accept wrapper request: {err}")) + result = server_socket.receive_with_fds::(), if child_exit.is_none() => { + let (request, fds) = result.map_err(|err| { + ToolError::Rejected(format!("failed to receive wrapper request: {err}")) })?; + if !fds.is_empty() { + return Err(ToolError::Rejected(format!( + "unexpected fds in wrapper request: {}", + fds.len() + ))); + } if self - .handle_wrapper_request(stream, req.justification.clone(), session, turn, call_id) + .handle_wrapper_request( + request, + req.justification.clone(), + session, + turn, + call_id, + &server_socket, + ) .await? { user_rejected = true; @@ -294,8 +255,6 @@ impl ZshExecBridge { } } - let _ = std::fs::remove_file(&wrapper_socket_path); - let status = child_exit.ok_or_else(|| { ToolError::Rejected("zsh fork command did not return exit status".to_string()) })?; @@ -323,21 +282,13 @@ impl ZshExecBridge { #[cfg(unix)] async fn handle_wrapper_request( &self, - mut stream: UnixStream, + request: WrapperIpcRequest, approval_reason: Option, session: &crate::codex::Session, turn: &crate::codex::TurnContext, call_id: &str, + socket: &AsyncSocket, ) -> Result { - let mut request_buf = Vec::new(); - stream.read_to_end(&mut request_buf).await.map_err(|err| { - ToolError::Rejected(format!("read wrapper request from socket: {err}")) - })?; - let request_line = String::from_utf8(request_buf).map_err(|err| { - ToolError::Rejected(format!("decode wrapper request as utf-8: {err}")) - })?; - let request = parse_wrapper_request_line(request_line.trim())?; - let (request_id, file, argv, cwd) = match request { WrapperIpcRequest::ExecRequest { request_id, @@ -367,35 +318,37 @@ impl ZshExecBridge { ) .await; - let (action, reason, user_rejected) = match decision { + let (action, reason) = match decision { ReviewDecision::Approved | ReviewDecision::ApprovedForSession - | ReviewDecision::ApprovedExecpolicyAmendment { .. } => { - (WrapperExecAction::Run, None, false) - } + | ReviewDecision::ApprovedExecpolicyAmendment { .. } => (WrapperExecAction::Run, None), ReviewDecision::Denied => ( WrapperExecAction::Deny, Some("command denied by host approval policy".to_string()), - true, ), ReviewDecision::Abort => ( WrapperExecAction::Deny, Some("command aborted by host approval policy".to_string()), - true, ), }; - write_json_line( - &mut stream, - &WrapperIpcResponse::ExecResponse { - request_id, - action, - reason, - }, - ) - .await?; + let response = WrapperIpcResponse::ExecResponse { + request_id, + action, + reason, + }; + socket + .send(response.clone()) + .await + .map_err(|err| ToolError::Rejected(format!("send wrapper response failed: {err}")))?; - Ok(user_rejected) + Ok(matches!( + response, + WrapperIpcResponse::ExecResponse { + action: WrapperExecAction::Deny, + .. + } + )) } #[cfg(unix)] @@ -420,138 +373,79 @@ impl ZshExecBridge { } } -pub fn maybe_run_zsh_exec_wrapper_mode() -> anyhow::Result { +pub async fn maybe_run_zsh_exec_wrapper_mode() -> anyhow::Result { if std::env::var_os(ZSH_EXEC_WRAPPER_MODE_ENV_VAR).is_none() { return Ok(false); } - run_exec_wrapper_mode()?; + run_exec_wrapper_mode().await?; Ok(true) } -fn run_exec_wrapper_mode() -> anyhow::Result<()> { - #[cfg(not(unix))] - { - anyhow::bail!("zsh exec wrapper mode is only supported on unix"); +#[cfg(unix)] +async fn run_exec_wrapper_mode() -> anyhow::Result<()> { + let raw_fd = std::env::var(ZSH_EXEC_BRIDGE_SOCKET_ENV_VAR)? + .parse::() + .context("invalid wrapper socket fd")?; + if raw_fd < 0 { + anyhow::bail!("wrapper socket fd must be non-negative"); } - #[cfg(unix)] - { - use std::os::unix::net::UnixStream as StdUnixStream; + let args: Vec = std::env::args().collect(); + if args.len() < 2 { + anyhow::bail!("exec wrapper mode requires target executable path"); + } + let file = args[1].clone(); + let argv = if args.len() > 2 { + args[2..].to_vec() + } else { + vec![file.clone()] + }; + let cwd = std::env::current_dir()?.to_string_lossy().to_string(); - let args: Vec = std::env::args().collect(); - if args.len() < 2 { - anyhow::bail!("exec wrapper mode requires target executable path"); - } - let file = args[1].clone(); - let argv = if args.len() > 2 { - args[2..].to_vec() + let socket = { + use std::os::fd::FromRawFd; + use std::os::fd::OwnedFd; + unsafe { AsyncSocket::from_fd(OwnedFd::from_raw_fd(raw_fd))? } + }; + let request_id = Uuid::new_v4().to_string(); + let request = WrapperIpcRequest::ExecRequest { + request_id: request_id.clone(), + file: file.clone(), + argv, + cwd, + }; + socket.send(request).await?; + let response = socket.receive::().await?; + let (response_request_id, action, reason) = match response { + WrapperIpcResponse::ExecResponse { + request_id, + action, + reason, + } => (request_id, action, reason), + }; + if response_request_id != request_id { + anyhow::bail!( + "wrapper response request_id mismatch: expected {request_id}, got {response_request_id}" + ); + } + + if action == WrapperExecAction::Deny { + if let Some(reason) = reason { + tracing::warn!("execution denied: {reason}"); } else { - vec![file.clone()] - }; - let cwd = std::env::current_dir() - .context("resolve wrapper cwd")? - .to_string_lossy() - .to_string(); - let socket_path = std::env::var(ZSH_EXEC_BRIDGE_WRAPPER_SOCKET_ENV_VAR) - .context("missing wrapper socket path env var")?; - - let request_id = Uuid::new_v4().to_string(); - let request = WrapperIpcRequest::ExecRequest { - request_id: request_id.clone(), - file: file.clone(), - argv: argv.clone(), - cwd, - }; - let mut stream = StdUnixStream::connect(&socket_path) - .with_context(|| format!("connect to wrapper socket at {socket_path}"))?; - let encoded = serde_json::to_string(&request).context("serialize wrapper request")?; - stream - .write_all(encoded.as_bytes()) - .context("write wrapper request")?; - stream - .write_all(b"\n") - .context("write wrapper request newline")?; - stream - .shutdown(std::net::Shutdown::Write) - .context("shutdown wrapper write")?; - - let mut response_buf = String::new(); - stream - .read_to_string(&mut response_buf) - .context("read wrapper response")?; - let response: WrapperIpcResponse = - serde_json::from_str(response_buf.trim()).context("parse wrapper response")?; - - let (response_request_id, action, reason) = match response { - WrapperIpcResponse::ExecResponse { - request_id, - action, - reason, - } => (request_id, action, reason), - }; - if response_request_id != request_id { - anyhow::bail!( - "wrapper response request_id mismatch: expected {request_id}, got {response_request_id}" - ); + tracing::warn!("execution denied"); } - - if action == WrapperExecAction::Deny { - if let Some(reason) = reason { - tracing::warn!("execution denied: {reason}"); - } else { - tracing::warn!("execution denied"); - } - std::process::exit(1); - } - - let mut command = std::process::Command::new(&file); - if argv.len() > 1 { - command.args(&argv[1..]); - } - command.env_remove(ZSH_EXEC_WRAPPER_MODE_ENV_VAR); - command.env_remove(ZSH_EXEC_BRIDGE_WRAPPER_SOCKET_ENV_VAR); - command.env_remove(EXEC_WRAPPER_ENV_VAR); - let status = command.status().context("spawn wrapped executable")?; - std::process::exit(status.code().unwrap_or(1)); + std::process::exit(1); } -} -#[cfg(unix)] -fn parse_wrapper_request_line(request_line: &str) -> Result { - serde_json::from_str(request_line) - .map_err(|err| ToolError::Rejected(format!("parse wrapper request payload: {err}"))) -} - -#[cfg(unix)] -async fn write_json_line( - writer: &mut W, - message: &T, -) -> Result<(), ToolError> { - let encoded = serde_json::to_string(message) - .map_err(|err| ToolError::Rejected(format!("serialize wrapper message: {err}")))?; - tokio::io::AsyncWriteExt::write_all(writer, encoded.as_bytes()) - .await - .map_err(|err| ToolError::Rejected(format!("write wrapper message: {err}")))?; - tokio::io::AsyncWriteExt::write_all(writer, b"\n") - .await - .map_err(|err| ToolError::Rejected(format!("write wrapper newline: {err}")))?; - tokio::io::AsyncWriteExt::flush(writer) - .await - .map_err(|err| ToolError::Rejected(format!("flush wrapper message: {err}")))?; - Ok(()) -} - -#[cfg(all(test, unix))] -mod tests { - use super::*; - - #[test] - fn parse_wrapper_request_line_rejects_malformed_json() { - let err = parse_wrapper_request_line("this-is-not-json").unwrap_err(); - let ToolError::Rejected(message) = err else { - panic!("expected ToolError::Rejected"); - }; - assert!(message.starts_with("parse wrapper request payload:")); + let mut command = std::process::Command::new(&file); + if args.len() > 2 { + command.args(&args[2..]); } + command.env_remove(ZSH_EXEC_WRAPPER_MODE_ENV_VAR); + command.env_remove(ZSH_EXEC_BRIDGE_SOCKET_ENV_VAR); + command.env_remove(EXEC_WRAPPER_ENV_VAR); + let status = command.status().context("spawn wrapped executable")?; + std::process::exit(status.code().unwrap_or(1)); } diff --git a/codex-rs/exec-server/Cargo.toml b/codex-rs/exec-server/Cargo.toml index 9b40cd35d7..ced8a30d80 100644 --- a/codex-rs/exec-server/Cargo.toml +++ b/codex-rs/exec-server/Cargo.toml @@ -24,6 +24,7 @@ anyhow = { workspace = true } async-trait = { workspace = true } clap = { workspace = true, features = ["derive"] } codex-core = { workspace = true } +codex-shell-exec-bridge = { workspace = true } codex-execpolicy = { workspace = true } codex-protocol = { workspace = true } codex-shell-command = { workspace = true } diff --git a/codex-rs/exec-server/src/posix.rs b/codex-rs/exec-server/src/posix.rs index 907ec14b68..286009329d 100644 --- a/codex-rs/exec-server/src/posix.rs +++ b/codex-rs/exec-server/src/posix.rs @@ -73,14 +73,20 @@ use tracing_subscriber::EnvFilter; use tracing_subscriber::{self}; use crate::posix::mcp_escalation_policy::ExecPolicyOutcome; +use codex_shell_exec_bridge::ESCALATE_SOCKET_ENV_VAR; +use codex_shell_exec_bridge::EXEC_WRAPPER_ENV_VAR; +use codex_shell_exec_bridge::EscalateAction; +use codex_shell_exec_bridge::EscalateRequest; +use codex_shell_exec_bridge::EscalateResponse; +use codex_shell_exec_bridge::LEGACY_BASH_EXEC_WRAPPER_ENV_VAR; +use codex_shell_exec_bridge::SuperExecMessage; +use codex_shell_exec_bridge::SuperExecResult; mod escalate_client; -mod escalate_protocol; mod escalate_server; mod escalation_policy; mod mcp; mod mcp_escalation_policy; -mod socket; mod stopwatch; pub use mcp::ExecResult; diff --git a/codex-rs/exec-server/src/posix/escalate_client.rs b/codex-rs/exec-server/src/posix/escalate_client.rs index 1cb0c5908d..813f0c550d 100644 --- a/codex-rs/exec-server/src/posix/escalate_client.rs +++ b/codex-rs/exec-server/src/posix/escalate_client.rs @@ -4,17 +4,16 @@ use std::os::fd::FromRawFd as _; use std::os::fd::OwnedFd; use anyhow::Context as _; - -use crate::posix::escalate_protocol::ESCALATE_SOCKET_ENV_VAR; -use crate::posix::escalate_protocol::EXEC_WRAPPER_ENV_VAR; -use crate::posix::escalate_protocol::EscalateAction; -use crate::posix::escalate_protocol::EscalateRequest; -use crate::posix::escalate_protocol::EscalateResponse; -use crate::posix::escalate_protocol::LEGACY_BASH_EXEC_WRAPPER_ENV_VAR; -use crate::posix::escalate_protocol::SuperExecMessage; -use crate::posix::escalate_protocol::SuperExecResult; -use crate::posix::socket::AsyncDatagramSocket; -use crate::posix::socket::AsyncSocket; +use codex_shell_exec_bridge::AsyncDatagramSocket; +use codex_shell_exec_bridge::AsyncSocket; +use codex_shell_exec_bridge::ESCALATE_SOCKET_ENV_VAR; +use codex_shell_exec_bridge::EXEC_WRAPPER_ENV_VAR; +use codex_shell_exec_bridge::EscalateAction; +use codex_shell_exec_bridge::EscalateRequest; +use codex_shell_exec_bridge::EscalateResponse; +use codex_shell_exec_bridge::LEGACY_BASH_EXEC_WRAPPER_ENV_VAR; +use codex_shell_exec_bridge::SuperExecMessage; +use codex_shell_exec_bridge::SuperExecResult; fn get_escalate_client() -> anyhow::Result { // TODO: we should defensively require only calling this once, since AsyncSocket will take ownership of the fd. diff --git a/codex-rs/exec-server/src/posix/escalate_server.rs b/codex-rs/exec-server/src/posix/escalate_server.rs index de4bdfc2cd..4dbdfa3114 100644 --- a/codex-rs/exec-server/src/posix/escalate_server.rs +++ b/codex-rs/exec-server/src/posix/escalate_server.rs @@ -12,21 +12,21 @@ use codex_core::SandboxState; use codex_core::exec::process_exec_tool_call; use codex_core::sandboxing::SandboxPermissions; use codex_protocol::config_types::WindowsSandboxLevel; +use codex_shell_exec_bridge::AsyncDatagramSocket; +use codex_shell_exec_bridge::AsyncSocket; +use codex_shell_exec_bridge::ESCALATE_SOCKET_ENV_VAR; +use codex_shell_exec_bridge::EXEC_WRAPPER_ENV_VAR; +use codex_shell_exec_bridge::EscalateAction; +use codex_shell_exec_bridge::EscalateRequest; +use codex_shell_exec_bridge::EscalateResponse; +use codex_shell_exec_bridge::LEGACY_BASH_EXEC_WRAPPER_ENV_VAR; +use codex_shell_exec_bridge::SuperExecMessage; +use codex_shell_exec_bridge::SuperExecResult; use tokio::process::Command; use tokio_util::sync::CancellationToken; -use crate::posix::escalate_protocol::ESCALATE_SOCKET_ENV_VAR; -use crate::posix::escalate_protocol::EXEC_WRAPPER_ENV_VAR; -use crate::posix::escalate_protocol::EscalateAction; -use crate::posix::escalate_protocol::EscalateRequest; -use crate::posix::escalate_protocol::EscalateResponse; -use crate::posix::escalate_protocol::LEGACY_BASH_EXEC_WRAPPER_ENV_VAR; -use crate::posix::escalate_protocol::SuperExecMessage; -use crate::posix::escalate_protocol::SuperExecResult; use crate::posix::escalation_policy::EscalationPolicy; use crate::posix::mcp::ExecParams; -use crate::posix::socket::AsyncDatagramSocket; -use crate::posix::socket::AsyncSocket; use codex_core::exec::ExecExpiration; pub(crate) struct EscalateServer {