diff --git a/codex-rs/app-server-client/src/lib.rs b/codex-rs/app-server-client/src/lib.rs index 3ef8a7b2af..079d1bd2ca 100644 --- a/codex-rs/app-server-client/src/lib.rs +++ b/codex-rs/app-server-client/src/lib.rs @@ -52,7 +52,7 @@ use codex_config::NoopThreadConfigLoader; use codex_core::config::Config; pub use codex_core::otel_init::build_provider as build_otel_provider; pub use codex_exec_server::EnvironmentManager; -pub use codex_exec_server::ExecServerRuntimePaths; +pub use codex_exec_server::ExecServerRuntimeOptions; use codex_feedback::CodexFeedback; use codex_protocol::protocol::SessionSource; use codex_utils_absolute_path::AbsolutePathBuf; @@ -2081,7 +2081,7 @@ mod tests { EnvironmentManager::create_for_tests( Some("ws://127.0.0.1:8765".to_string()), Some( - ExecServerRuntimePaths::new( + ExecServerRuntimeOptions::new( std::env::current_exe().expect("current exe"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/app-server/src/bin/exec_server.rs b/codex-rs/app-server/src/bin/exec_server.rs index 49fce24620..ae7afae459 100644 --- a/codex-rs/app-server/src/bin/exec_server.rs +++ b/codex-rs/app-server/src/bin/exec_server.rs @@ -5,7 +5,7 @@ //! also handles the helper argv modes because exec-server re-execs //! `codex_self_exe` for sandboxed filesystem and process requests. -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_http_client::HttpClientFactory; use codex_http_client::OutboundProxyPolicy; use std::ffi::OsStr; @@ -29,7 +29,7 @@ fn main() -> Result<(), Box> { let current_exe = std::env::current_exe()?; let codex_linux_sandbox_exe = std::env::var_os(CODEX_LINUX_SANDBOX_EXE_ENV_VAR).map(std::path::PathBuf::from); - let runtime_paths = ExecServerRuntimePaths::new(current_exe, codex_linux_sandbox_exe)?; + let runtime_paths = ExecServerRuntimeOptions::new(current_exe, codex_linux_sandbox_exe)?; tokio::runtime::Builder::new_multi_thread() .enable_all() .build()? diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index ae748d401e..c2194670ac 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -61,7 +61,7 @@ use codex_core::ExecPolicyError; use codex_core::check_execpolicy_for_warnings; use codex_core::config::find_codex_home; use codex_exec_server::EnvironmentManager; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_features::Feature; use codex_feedback::CodexFeedback; use codex_protocol::protocol::SessionSource; @@ -518,7 +518,7 @@ pub async fn run_main_with_transport_options( ) })?; let codex_home = find_codex_home()?; - let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths( + let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths( arg0_paths.codex_self_exe.clone(), arg0_paths.codex_linux_sandbox_exe.clone(), )?; diff --git a/codex-rs/cli/src/exec_server_args_tests.rs b/codex-rs/cli/src/exec_server_args_tests.rs index e9925ef012..1724d221e9 100644 --- a/codex-rs/cli/src/exec_server_args_tests.rs +++ b/codex-rs/cli/src/exec_server_args_tests.rs @@ -22,7 +22,11 @@ fn exec_server_help_documents_remote_options() { let command = MultitoolCli::command() .term_width(80) .mut_subcommand("exec-server", |command| { - command.mut_arg("exit_on_stdin_close", |arg| arg.hide_env_values(true)) + command + .mut_arg("exit_on_stdin_close", |arg| arg.hide_env_values(true)) + .mut_arg("proxy_private_ips_via_upstream", |arg| { + arg.hide_env_values(true) + }) }); let help = command .try_get_matches_from(["codex", "exec-server", "--help"]) diff --git a/codex-rs/cli/src/exec_server_command.rs b/codex-rs/cli/src/exec_server_command.rs index de4b6643a4..588a1f05b4 100644 --- a/codex-rs/cli/src/exec_server_command.rs +++ b/codex-rs/cli/src/exec_server_command.rs @@ -14,7 +14,7 @@ use codex_core::config::ConfigLoadOptions; use codex_core::config::bootstrap_auth_config; use codex_core::config::find_codex_home; use codex_core::config::load_config_toml_with_layer_stack; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_http_client::HttpClientFactory; use codex_http_client::OutboundProxyPolicy; use codex_login::AuthManager; @@ -48,6 +48,16 @@ pub(super) struct ExecServerCommand { #[arg(long, value_name = "MODE", default_value = "isolate", global = true)] linux_sandbox_pid_namespace: codex_sandboxing::LinuxSandboxPidNamespace, + /// Allow permitted private IP destinations to use the configured upstream proxy. + /// If no valid upstream proxy applies to the request protocol, connect directly. + /// Loopback stays local. This flag does not require upstream routing. + #[arg( + long, + env = "CODEX_EXEC_SERVER_PROXY_PRIVATE_IPS_VIA_UPSTREAM", + global = true + )] + proxy_private_ips_via_upstream: bool, + /// Maximum number of requests to process concurrently on each connection. #[arg( long = "concurrent-requests", @@ -164,11 +174,12 @@ impl ExecServerCommand { .codex_self_exe .clone() .ok_or_else(|| anyhow::anyhow!("Codex executable path is not configured"))?; - let runtime_paths = ExecServerRuntimePaths::new( + let runtime_paths = ExecServerRuntimeOptions::new( codex_self_exe, arg0_paths.codex_linux_sandbox_exe.clone(), )? - .with_linux_sandbox_pid_namespace(self.linux_sandbox_pid_namespace); + .with_linux_sandbox_pid_namespace(self.linux_sandbox_pid_namespace) + .with_proxy_private_ips_via_upstream(self.proxy_private_ips_via_upstream); if let Some(base_url) = self.remote.take() { let environment_id = self.environment_id.take().ok_or_else(|| { anyhow::anyhow!("--environment-id is required when --remote is set") diff --git a/codex-rs/cli/src/snapshots/codex__exec_server_args_tests__exec_server_help_documents_remote_options.snap b/codex-rs/cli/src/snapshots/codex__exec_server_args_tests__exec_server_help_documents_remote_options.snap index 44fb6b581e..2010b1e0c5 100644 --- a/codex-rs/cli/src/snapshots/codex__exec_server_args_tests__exec_server_help_documents_remote_options.snap +++ b/codex-rs/cli/src/snapshots/codex__exec_server_args_tests__exec_server_help_documents_remote_options.snap @@ -1,6 +1,5 @@ --- source: cli/src/exec_server_args_tests.rs -assertion_line: 36 expression: help_text --- [EXPERIMENTAL] Run the standalone exec-server service @@ -40,6 +39,14 @@ Options: [default: isolate] + --proxy-private-ips-via-upstream + Allow permitted private IP destinations to use the configured upstream + proxy. If no valid upstream proxy applies to the request protocol, + connect directly. Loopback stays local. This flag does not require + upstream routing + + [env: CODEX_EXEC_SERVER_PROXY_PRIVATE_IPS_VIA_UPSTREAM] + --concurrent-requests Maximum number of requests to process concurrently on each connection diff --git a/codex-rs/core-api/src/lib.rs b/codex-rs/core-api/src/lib.rs index 2a01ba78be..0c7549b0c0 100644 --- a/codex-rs/core-api/src/lib.rs +++ b/codex-rs/core-api/src/lib.rs @@ -84,7 +84,7 @@ pub use codex_exec_server::EnvironmentRegistryHarnessKeyValidationResponse; pub use codex_exec_server::EnvironmentRegistryRegistrationRequest; pub use codex_exec_server::EnvironmentRegistryRegistrationResponse; pub use codex_exec_server::ExecServerError; -pub use codex_exec_server::ExecServerRuntimePaths; +pub use codex_exec_server::ExecServerRuntimeOptions; pub use codex_exec_server::NoiseChannelIdentity; pub use codex_exec_server::NoiseChannelPublicKey; pub use codex_exec_server::NoiseRendezvousConnectBundle; diff --git a/codex-rs/core/src/connectors.rs b/codex-rs/core/src/connectors.rs index 4156fcfc39..00640bcb75 100644 --- a/codex-rs/core/src/connectors.rs +++ b/codex-rs/core/src/connectors.rs @@ -13,7 +13,7 @@ use codex_connectors::ConnectorDirectoryCacheKey; use codex_connectors::apps_config_from_layer_stack; use codex_connectors::connector_runtime_context_key; use codex_exec_server::EnvironmentManager; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_tools::DiscoverableTool; use tokio_util::sync::CancellationToken; use tracing::instrument; @@ -165,7 +165,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options_and_status( // TODO: Wire callers that already own an EnvironmentManager into // list_accessible_connectors_from_mcp_tools_with_environment_manager instead // of constructing a temporary manager here. - let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths( + let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths( config.codex_self_exe.clone(), config.codex_linux_sandbox_exe.clone(), )?; diff --git a/codex-rs/core/src/environment_selection.rs b/codex-rs/core/src/environment_selection.rs index 34fc4a721e..eaa699adda 100644 --- a/codex-rs/core/src/environment_selection.rs +++ b/codex-rs/core/src/environment_selection.rs @@ -1183,7 +1183,7 @@ mod tests { use crate::config::PermissionProfileSnapshot; use codex_exec_server::Environment; - use codex_exec_server::ExecServerRuntimePaths; + use codex_exec_server::ExecServerRuntimeOptions; use codex_exec_server::LOCAL_ENVIRONMENT_ID; use codex_exec_server::REMOTE_ENVIRONMENT_ID; use codex_exec_server_test_support::environment_manager_without_environments; @@ -1246,8 +1246,8 @@ mod tests { turn_environments } - fn test_runtime_paths() -> ExecServerRuntimePaths { - ExecServerRuntimePaths::new( + fn test_runtime_paths() -> ExecServerRuntimeOptions { + ExecServerRuntimeOptions::new( std::env::current_exe().expect("current exe"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/core/src/prompt_debug.rs b/codex-rs/core/src/prompt_debug.rs index fd2b3a230e..e9853bf20e 100644 --- a/codex-rs/core/src/prompt_debug.rs +++ b/codex-rs/core/src/prompt_debug.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use codex_exec_server::EnvironmentManager; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_extension_api::ExtensionRegistry; use codex_extension_api::UserInstructionsProvider; use codex_login::AuthManager; @@ -37,7 +37,7 @@ pub async fn build_prompt_input( .await .map_err(|err| CodexErr::Fatal(err.to_string()))?; - let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths( + let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths( config.codex_self_exe.clone(), config.codex_linux_sandbox_exe.clone(), )?; diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index c5d56cc024..a34bcd447f 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -737,7 +737,7 @@ impl TestCodexBuilder { ); #[cfg(not(target_os = "linux"))] let codex_linux_sandbox_exe = None; - let local_runtime_paths = codex_exec_server::ExecServerRuntimePaths::new( + let local_runtime_paths = codex_exec_server::ExecServerRuntimeOptions::new( std::env::current_exe()?, codex_linux_sandbox_exe, )?; diff --git a/codex-rs/core/tests/suite/hooks_executor.rs b/codex-rs/core/tests/suite/hooks_executor.rs index 95a150e6e7..567d1c8190 100644 --- a/codex-rs/core/tests/suite/hooks_executor.rs +++ b/codex-rs/core/tests/suite/hooks_executor.rs @@ -7,7 +7,7 @@ use codex_core::StartThreadOptions; use codex_core::TurnInputRequest; use codex_core::windows_sandbox::WindowsSandboxLevelExt; use codex_exec_server::CreateDirectoryOptions; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_features::Feature; use codex_protocol::capabilities::CapabilityRootLocation; use codex_protocol::capabilities::SelectedCapabilityRoot; @@ -309,7 +309,7 @@ async fn executor_stop_hook_rejects_mismatched_environment() -> Result<()> { let executor_address = listener.local_addr()?; let executor_url = format!("ws://{executor_address}"); drop(listener); - let runtime_paths = ExecServerRuntimePaths::new( + let runtime_paths = ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?; diff --git a/codex-rs/core/tests/suite/remote_env.rs b/codex-rs/core/tests/suite/remote_env.rs index 1b21ec0e1b..c80b6696f1 100644 --- a/codex-rs/core/tests/suite/remote_env.rs +++ b/codex-rs/core/tests/suite/remote_env.rs @@ -22,7 +22,7 @@ use codex_exec_server::CopyOptions; use codex_exec_server::CreateDirectoryOptions; use codex_exec_server::EnvironmentReadyInfo; use codex_exec_server::ExecServerError; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_exec_server::FileSystemSandboxContext; use codex_exec_server::LOCAL_ENVIRONMENT_ID; use codex_exec_server::NoiseChannelPublicKey; @@ -2404,7 +2404,7 @@ async fn ready_before_selection_resolves_resumed_thread_capability_root_after_wa .mount(®istry) .await; - let runtime_paths = ExecServerRuntimePaths::new( + let runtime_paths = ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?; diff --git a/codex-rs/core/tests/suite/skills_extension.rs b/codex-rs/core/tests/suite/skills_extension.rs index 780fe3d3e5..ec7e757581 100644 --- a/codex-rs/core/tests/suite/skills_extension.rs +++ b/codex-rs/core/tests/suite/skills_extension.rs @@ -2929,7 +2929,7 @@ async fn production_turn_keeps_rebalanced_catalogs_stable_after_compaction_and_r let executor_address = listener.local_addr()?; let executor_url = format!("ws://{executor_address}"); drop(listener); - let runtime_paths = codex_exec_server::ExecServerRuntimePaths::new( + let runtime_paths = codex_exec_server::ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?; diff --git a/codex-rs/exec-server/README.md b/codex-rs/exec-server/README.md index 8fdc34e2f6..f32972604e 100644 --- a/codex-rs/exec-server/README.md +++ b/codex-rs/exec-server/README.md @@ -478,19 +478,29 @@ The crate exports: - `RemoteExecServerConnectArgs` - protocol request/response structs for process and filesystem RPCs - `DEFAULT_LISTEN_URL` and `ExecServerListenUrlParseError` -- `ExecServerRuntimePaths` +- `ExecServerRuntimeOptions` - `run_main()` for embedding the websocket server - `RemoteEnvironmentConfig` and `run_remote_environment()` for embedding remote registration mode -Callers must pass `ExecServerRuntimePaths` and an explicitly configured +Callers must pass `ExecServerRuntimeOptions` and an explicitly configured `HttpClientFactory` to `run_main()`. The top-level `codex exec-server` command -builds these paths from the `codex` arg0 dispatch state and resolves its HTTP -client factory from the effective Codex configuration. +builds the runtime options from the `codex` arg0 dispatch state and startup flags, +and resolves its HTTP client factory from the effective Codex configuration. `RemoteEnvironmentConfig::new(...)` also takes the auth provider and HTTP client factory that remote registration mode should use; the CLI builds the auth provider from Codex auth state before starting remote mode. +`--proxy-private-ips-via-upstream` (or +`CODEX_EXEC_SERVER_PROXY_PRIVATE_IPS_VIA_UPSTREAM=true`) allows permitted private IP +destinations to use an inherited upstream proxy. If no valid proxy configuration +applies to the request protocol, routing falls back to a direct connection. This +includes unset, malformed, or unsupported proxy settings. Setting +`allow_upstream_proxy=false` also keeps routing direct; loopback always stays local. +Destination policy still applies to every route. Enforcing mandatory upstream routing +would require a separate fail-closed mode. A connection failure after selecting an +upstream proxy is returned as an error, without retrying directly. + ## Example session Initialize: diff --git a/codex-rs/exec-server/src/client_refresh_tests.rs b/codex-rs/exec-server/src/client_refresh_tests.rs index 86a3bdb517..22bcb30679 100644 --- a/codex-rs/exec-server/src/client_refresh_tests.rs +++ b/codex-rs/exec-server/src/client_refresh_tests.rs @@ -13,7 +13,7 @@ use tokio::task::JoinSet; use tokio_util::task::AbortOnDropHandle; use super::*; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::NoiseChannelIdentity; use crate::ProcessId; use crate::noise_relay::stream_handler::NoiseOutboundMessage; @@ -148,7 +148,7 @@ impl Executor { validator: Validator, identity: NoiseChannelIdentity, ) -> Result { - let processor = ConnectionProcessor::new(ExecServerRuntimePaths::new( + let processor = ConnectionProcessor::new(ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?); diff --git a/codex-rs/exec-server/src/environment.rs b/codex-rs/exec-server/src/environment.rs index 12981657f1..31a35e9be4 100644 --- a/codex-rs/exec-server/src/environment.rs +++ b/codex-rs/exec-server/src/environment.rs @@ -18,7 +18,7 @@ use crate::CapabilityRootsDiscoverResponse; use crate::EnvironmentConfigReadParams; use crate::EnvironmentConfigReadResponse; use crate::ExecServerError; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::ExecutorFileSystem; use crate::HttpClient; use crate::NoiseChannelIdentity; @@ -90,7 +90,7 @@ pub struct EnvironmentManager { default_environment: Option, pub(super) environments: RwLock>>, local_environment: Option>, - local_runtime_paths: Option, + local_runtime_paths: Option, http_client_factory: HttpClientFactory, } @@ -161,7 +161,7 @@ impl EnvironmentManager { /// Builds a test-only manager from a raw exec-server URL value. pub async fn create_for_tests( exec_server_url: Option, - local_runtime_paths: Option, + local_runtime_paths: Option, ) -> Self { let provider = DefaultEnvironmentProvider::new(exec_server_url); match Self::from_snapshot( @@ -195,7 +195,7 @@ impl EnvironmentManager { /// Builds a manager from `CODEX_HOME` with an explicit outbound HTTP policy. pub async fn from_codex_home( codex_home: impl AsRef, - local_runtime_paths: Option, + local_runtime_paths: Option, http_client_factory: HttpClientFactory, ) -> Result { Self::prepare_from_codex_home(codex_home) @@ -216,7 +216,7 @@ impl EnvironmentManager { /// Builds a manager from environment variables with an explicit outbound HTTP policy. pub async fn from_env( - local_runtime_paths: Option, + local_runtime_paths: Option, http_client_factory: HttpClientFactory, ) -> Result { Self::prepare_from_env() @@ -226,7 +226,7 @@ impl EnvironmentManager { pub(crate) fn from_noise_environment_config( config: NoiseRendezvousEnvironmentConfig, - local_runtime_paths: Option, + local_runtime_paths: Option, http_client_factory: HttpClientFactory, ) -> Result { let connect_provider = config.into_connect_provider(http_client_factory.clone())?; @@ -254,7 +254,7 @@ impl EnvironmentManager { /// allowing tests to select the local environment explicitly. pub async fn create_for_tests_with_local( exec_server_url: Option, - local_runtime_paths: ExecServerRuntimePaths, + local_runtime_paths: ExecServerRuntimeOptions, ) -> Self { let mut snapshot = DefaultEnvironmentProvider::new(exec_server_url).snapshot_inner(); snapshot.include_local = true; @@ -271,7 +271,7 @@ impl EnvironmentManager { pub(crate) fn from_snapshot( snapshot: EnvironmentProviderSnapshot, - local_runtime_paths: Option, + local_runtime_paths: Option, http_client_factory: HttpClientFactory, ) -> Result { let EnvironmentProviderSnapshot { @@ -675,7 +675,7 @@ pub struct Environment { exec_backend: Arc, filesystem: Arc, http_client: Arc, - local_runtime_paths: Option, + local_runtime_paths: Option, } impl Environment { @@ -707,7 +707,7 @@ impl Environment { /// Builds an environment using the caller's effective outbound HTTP policy. pub fn create( exec_server_url: Option, - local_runtime_paths: ExecServerRuntimePaths, + local_runtime_paths: ExecServerRuntimeOptions, http_client_factory: HttpClientFactory, ) -> Result { Self::create_inner( @@ -730,7 +730,7 @@ impl Environment { /// local runtime paths used when creating local filesystem helpers. fn create_inner( exec_server_url: Option, - local_runtime_paths: Option, + local_runtime_paths: Option, http_client_factory: HttpClientFactory, ) -> Result { let (exec_server_url, disabled) = normalize_exec_server_url(exec_server_url); @@ -757,7 +757,7 @@ impl Environment { } pub(crate) fn local( - local_runtime_paths: ExecServerRuntimePaths, + local_runtime_paths: ExecServerRuntimeOptions, http_client_factory: HttpClientFactory, ) -> Self { Self { @@ -778,7 +778,7 @@ impl Environment { pub(crate) fn remote_with_transport( remote_transport: ExecServerTransportParams, - local_runtime_paths: Option, + local_runtime_paths: Option, http_client_factory: HttpClientFactory, ) -> Self { let client = LazyRemoteExecServerClient::new(remote_transport, http_client_factory); @@ -787,7 +787,7 @@ impl Environment { pub(crate) fn remote_with_client( client: LazyRemoteExecServerClient, - local_runtime_paths: Option, + local_runtime_paths: Option, ) -> Self { let exec_backend: Arc = Arc::new(RemoteProcess::new(client.clone())); let filesystem: Arc = @@ -890,7 +890,7 @@ impl Environment { .map(LazyRemoteExecServerClient::subscribe_connection_state) } - pub fn local_runtime_paths(&self) -> Option<&ExecServerRuntimePaths> { + pub fn local_runtime_paths(&self) -> Option<&ExecServerRuntimeOptions> { self.local_runtime_paths.as_ref() } @@ -1171,7 +1171,7 @@ mod tests { use super::LOCAL_ENVIRONMENT_ID; use super::REMOTE_ENVIRONMENT_ID; use super::noise_environment_config_from_values; - use crate::ExecServerRuntimePaths; + use crate::ExecServerRuntimeOptions; use crate::ProcessId; use crate::client_api::DEFAULT_REMOTE_EXEC_SERVER_CONNECT_TIMEOUT; use crate::client_api::ExecServerTransportParams; @@ -1196,8 +1196,8 @@ mod tests { ) } - fn test_runtime_paths() -> ExecServerRuntimePaths { - ExecServerRuntimePaths::new( + fn test_runtime_paths() -> ExecServerRuntimeOptions { + ExecServerRuntimeOptions::new( std::env::current_exe().expect("current exe"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/exec-server/src/environment_bootstrap.rs b/codex-rs/exec-server/src/environment_bootstrap.rs index ad557c3b29..dc9bebfaf0 100644 --- a/codex-rs/exec-server/src/environment_bootstrap.rs +++ b/codex-rs/exec-server/src/environment_bootstrap.rs @@ -2,7 +2,7 @@ use codex_http_client::HttpClientFactory; use crate::EnvironmentManager; use crate::ExecServerError; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::environment_provider::EnvironmentDefault; use crate::environment_provider::EnvironmentProviderSnapshot; use crate::remote::NoiseRendezvousEnvironmentConfig; @@ -41,7 +41,7 @@ impl PreparedEnvironmentManager { /// Builds the manager and starts remote connections using the supplied policy. pub fn build( self, - local_runtime_paths: Option, + local_runtime_paths: Option, http_client_factory: HttpClientFactory, ) -> Result { match self.source { diff --git a/codex-rs/exec-server/src/fs_sandbox.rs b/codex-rs/exec-server/src/fs_sandbox.rs index 73905a668b..89058f3ff8 100644 --- a/codex-rs/exec-server/src/fs_sandbox.rs +++ b/codex-rs/exec-server/src/fs_sandbox.rs @@ -37,7 +37,7 @@ use tokio::io::AsyncBufReadExt; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::FileSystemSandboxContext; use crate::fs_helper::CODEX_FS_HELPER_ARG1; use crate::fs_helper::FsHelperPayload; @@ -69,12 +69,12 @@ struct SandboxCwd { #[derive(Clone, Debug)] pub(crate) struct FileSystemSandboxRunner { - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, helper_env: HashMap, } impl FileSystemSandboxRunner { - pub(crate) fn new(runtime_paths: ExecServerRuntimePaths) -> Self { + pub(crate) fn new(runtime_paths: ExecServerRuntimeOptions) -> Self { Self { runtime_paths, helper_env: helper_env(), @@ -229,7 +229,7 @@ fn native_workspace_root(root: &PathUri) -> Result Vec { +fn helper_read_roots(runtime_paths: &ExecServerRuntimeOptions) -> Vec { let mut roots = vec![runtime_paths.codex_self_exe.clone()]; if let Some(path) = &runtime_paths.codex_linux_sandbox_exe && !roots.contains(path) @@ -581,7 +581,7 @@ mod tests { use codex_utils_path_uri::PathUri; use pretty_assertions::assert_eq; - use crate::ExecServerRuntimePaths; + use crate::ExecServerRuntimeOptions; use super::FileSystemSandboxRunner; use super::SandboxCwd; @@ -622,7 +622,7 @@ mod tests { fn helper_permissions_preserve_existing_writes() { let codex_self_exe = std::env::current_exe().expect("current exe"); let runtime_paths = - ExecServerRuntimePaths::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None) + ExecServerRuntimeOptions::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None) .expect("runtime paths"); let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir().as_path()) .expect("absolute cwd"); @@ -743,7 +743,7 @@ mod tests { let path = path.to_string_lossy().into_owned(); let codex_self_exe = std::env::current_exe().expect("current exe"); let runtime_paths = - ExecServerRuntimePaths::new(codex_self_exe.clone(), Some(codex_self_exe)) + ExecServerRuntimeOptions::new(codex_self_exe.clone(), Some(codex_self_exe)) .expect("runtime paths"); let runner = FileSystemSandboxRunner::new(runtime_paths); let native_cwd = AbsolutePathBuf::current_dir().expect("cwd"); @@ -799,7 +799,7 @@ mod tests { fn sandbox_exec_request_uses_filesystem_root_and_preserves_policy_cwd() { let codex_self_exe = std::env::current_exe().expect("current exe"); let runtime_paths = - ExecServerRuntimePaths::new(codex_self_exe.clone(), Some(codex_self_exe)) + ExecServerRuntimeOptions::new(codex_self_exe.clone(), Some(codex_self_exe)) .expect("runtime paths"); let runner = FileSystemSandboxRunner::new(runtime_paths); let selected = tempfile::tempdir().expect("selected directory"); @@ -889,7 +889,7 @@ mod tests { fn sandbox_exec_request_binds_windows_relative_globs_to_policy_cwd() { let codex_self_exe = std::env::current_exe().expect("current exe"); let runtime_paths = - ExecServerRuntimePaths::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None) + ExecServerRuntimeOptions::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None) .expect("runtime paths"); let runner = FileSystemSandboxRunner::new(runtime_paths); let selected = tempfile::tempdir().expect("selected directory"); @@ -979,7 +979,7 @@ mod tests { fn helper_permissions_include_only_the_helper_executable() { let codex_self_exe = std::env::current_exe().expect("current exe"); let runtime_paths = - ExecServerRuntimePaths::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None) + ExecServerRuntimeOptions::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None) .expect("runtime paths"); let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir().as_path()) .expect("absolute cwd"); @@ -1012,7 +1012,7 @@ mod tests { let codex_self_exe = root.path().join("bin").join("codex"); let codex_linux_sandbox_exe = root.path().join("aliases").join("codex-linux-sandbox"); let runtime_paths = - ExecServerRuntimePaths::new(codex_self_exe, Some(codex_linux_sandbox_exe)) + ExecServerRuntimeOptions::new(codex_self_exe, Some(codex_linux_sandbox_exe)) .expect("runtime paths"); let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir().as_path()) .expect("absolute cwd"); diff --git a/codex-rs/exec-server/src/lib.rs b/codex-rs/exec-server/src/lib.rs index 0851fd918e..2d850e9539 100644 --- a/codex-rs/exec-server/src/lib.rs +++ b/codex-rs/exec-server/src/lib.rs @@ -39,7 +39,7 @@ mod resolved_capability; mod rpc; mod rpc_server_requests; mod rpc_timing; -mod runtime_paths; +mod runtime_options; mod sandbox_selection; mod sandboxed_file_open; mod sandboxed_file_system; @@ -228,7 +228,7 @@ pub use remote::run_remote_environment_forward_until_shutdown; pub use remote::run_remote_environment_until_shutdown; pub use resolved_capability::ResolvedSelectedCapabilityRoot; pub use resolved_capability::SelectedCapabilityRootsStatus; -pub use runtime_paths::ExecServerRuntimePaths; +pub use runtime_options::ExecServerRuntimeOptions; pub use server::ConcurrentRequestLimit; pub use server::DEFAULT_LISTEN_URL; pub use server::ExecServerListenUrlParseError; diff --git a/codex-rs/exec-server/src/local_file_system.rs b/codex-rs/exec-server/src/local_file_system.rs index ea0a0f0346..500d88500f 100644 --- a/codex-rs/exec-server/src/local_file_system.rs +++ b/codex-rs/exec-server/src/local_file_system.rs @@ -20,7 +20,7 @@ use tokio_util::sync::CancellationToken; use crate::CopyOptions; use crate::CreateDirectoryOptions; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::ExecutorFileSystem; use crate::ExecutorFileSystemFuture; use crate::FILE_READ_CHUNK_SIZE; @@ -76,7 +76,7 @@ impl LocalFileSystem { } } - pub fn with_runtime_paths(runtime_paths: ExecServerRuntimePaths) -> Self { + pub fn with_runtime_paths(runtime_paths: ExecServerRuntimeOptions) -> Self { Self { unsandboxed: UnsandboxedFileSystem::default(), sandboxed: Some(SandboxedFileSystem::new(runtime_paths)), diff --git a/codex-rs/exec-server/src/local_process.rs b/codex-rs/exec-server/src/local_process.rs index a84ad8e329..85cbd9bd45 100644 --- a/codex-rs/exec-server/src/local_process.rs +++ b/codex-rs/exec-server/src/local_process.rs @@ -40,7 +40,7 @@ use crate::ExecProcessEvent; use crate::ExecProcessEventReceiver; use crate::ExecProcessFuture; use crate::ExecServerError; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::ProcessId; use crate::StartedExecProcess; use crate::network_policy_decisions::network_policy_decider; @@ -174,7 +174,7 @@ struct Inner { #[derive(Clone)] pub(crate) struct LocalProcess { inner: Arc, - runtime_paths: Option, + runtime_paths: Option, } struct LocalExecProcess { @@ -191,11 +191,11 @@ impl Default for LocalProcess { } impl LocalProcess { - pub(crate) fn with_local_runtime_paths(runtime_paths: ExecServerRuntimePaths) -> Self { + pub(crate) fn with_local_runtime_paths(runtime_paths: ExecServerRuntimeOptions) -> Self { Self::with_discarded_notifications(Some(runtime_paths)) } - fn with_discarded_notifications(runtime_paths: Option) -> Self { + fn with_discarded_notifications(runtime_paths: Option) -> Self { let (outgoing_tx, mut outgoing_rx) = mpsc::channel::(NOTIFICATION_CHANNEL_CAPACITY); tokio::spawn(async move { while outgoing_rx.recv().await.is_some() {} }); @@ -209,7 +209,7 @@ impl LocalProcess { pub(crate) fn new( notifications: RpcNotificationSender, telemetry: ExecServerTelemetry, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, ) -> Self { Self::with_runtime_paths(notifications, telemetry, Some(runtime_paths)) } @@ -217,7 +217,7 @@ impl LocalProcess { fn with_runtime_paths( notifications: RpcNotificationSender, telemetry: ExecServerTelemetry, - runtime_paths: Option, + runtime_paths: Option, ) -> Self { let requests = notifications.request_sender(); Self { diff --git a/codex-rs/exec-server/src/noise_relay/executor_stream_tests.rs b/codex-rs/exec-server/src/noise_relay/executor_stream_tests.rs index 4bf8f945df..7adfd34860 100644 --- a/codex-rs/exec-server/src/noise_relay/executor_stream_tests.rs +++ b/codex-rs/exec-server/src/noise_relay/executor_stream_tests.rs @@ -11,7 +11,7 @@ use tokio::time::timeout; use super::ClosedNoiseVirtualStream; use super::spawn_noise_virtual_stream; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::connection::CHANNEL_CAPACITY; use crate::noise_channel::InitiatorHandshake; use crate::noise_channel::NoiseChannelIdentity; @@ -92,7 +92,7 @@ async fn processor_exit_reports_closed_virtual_stream() -> Result<()> { let mut stream = spawn_noise_virtual_stream( "stream-1".to_string(), /*instance_id*/ 7, - ConnectionProcessor::new(ExecServerRuntimePaths::new( + ConnectionProcessor::new(ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?), diff --git a/codex-rs/exec-server/src/process_sandbox.rs b/codex-rs/exec-server/src/process_sandbox.rs index 567fb37081..0ad07b4ad0 100644 --- a/codex-rs/exec-server/src/process_sandbox.rs +++ b/codex-rs/exec-server/src/process_sandbox.rs @@ -35,7 +35,7 @@ use codex_utils_path_uri::PathUri; #[cfg(unix)] use crate::CODEX_ARG0_EXEC_HELPER_ARG1; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::process_telemetry::trace_process_id; use crate::protocol::ExecParams; use crate::rpc::internal_error; @@ -86,7 +86,7 @@ impl PreparedExecRequest { pub(crate) async fn prepare_exec_request_with_telemetry( params: &ExecParams, env: HashMap, - runtime_paths: Option<&ExecServerRuntimePaths>, + runtime_paths: Option<&ExecServerRuntimeOptions>, network_policy_decider: Option>, network_policy_audit_observer: Option, telemetry: &ProcessTelemetry, @@ -123,15 +123,9 @@ pub(crate) async fn prepare_exec_request_with_telemetry( let (env, managed_network, network_proxy_handle, network_proxy_restricting_sid) = prepare_managed_network( - params.managed_network.as_ref(), + params, network_proxy, - if params.sandbox.as_ref().is_some_and(|sandbox| { - sandbox.windows_sandbox_selection == WindowsSandboxSelection::Mxc - }) { - ManagedProxyRouting::DedicatedListeners - } else { - ManagedProxyRouting::SharedIngress - }, + runtime_paths.is_some_and(|paths| paths.proxy_private_ips_via_upstream), env, network_policy_decider, network_policy_audit_observer, @@ -332,9 +326,9 @@ pub(crate) async fn prepare_exec_request_with_telemetry( } async fn prepare_managed_network( - managed_network: Option<&ManagedNetworkSandboxContext>, + params: &ExecParams, network_proxy: Option<&RemoteNetworkProxyLaunchConfig>, - routing: ManagedProxyRouting, + proxy_private_ips_via_upstream: bool, env: HashMap, network_policy_decider: Option>, network_policy_audit_observer: Option, @@ -349,13 +343,22 @@ async fn prepare_managed_network( JSONRPCErrorError, > { let Some(network_proxy) = network_proxy.cloned() else { - return Ok((env, managed_network.cloned(), None, None)); + return Ok((env, params.managed_network.clone(), None, None)); }; + let routing = + if params.sandbox.as_ref().is_some_and(|sandbox| { + sandbox.windows_sandbox_selection == WindowsSandboxSelection::Mxc + }) { + ManagedProxyRouting::DedicatedListeners + } else { + ManagedProxyRouting::SharedIngress + }; let mut state = NetworkProxyState::from_remote_launch_config( network_proxy, codex_utils_path_uri::Platform::native(), ) .map_err(|err| invalid_params(format!("invalid network proxy config: {err}")))?; + state.set_proxy_private_ips_via_upstream(proxy_private_ips_via_upstream); if let Some(observer) = network_policy_audit_observer { state.set_policy_audit_observer(observer); } diff --git a/codex-rs/exec-server/src/process_sandbox_tests.rs b/codex-rs/exec-server/src/process_sandbox_tests.rs index 7f415f4dde..ef3c1e0f11 100644 --- a/codex-rs/exec-server/src/process_sandbox_tests.rs +++ b/codex-rs/exec-server/src/process_sandbox_tests.rs @@ -40,7 +40,7 @@ use super::prepare_exec_request_with_telemetry; #[cfg(unix)] use crate::CODEX_ARG0_EXEC_HELPER_ARG1; use crate::ExecParams; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; #[cfg(any(unix, windows))] use crate::FileSystemSandboxContext; use crate::ProcessId; @@ -49,7 +49,7 @@ use crate::process_telemetry::ProcessTelemetry; async fn prepare_exec_request( params: &ExecParams, env: HashMap, - runtime_paths: Option<&ExecServerRuntimePaths>, + runtime_paths: Option<&ExecServerRuntimeOptions>, network_policy_decider: Option>, network_policy_audit_observer: Option, ) -> Result { @@ -72,7 +72,7 @@ async fn sandbox_request_wraps_native_argv_on_executor() { let cwd_uri = PathUri::from_abs_path(&cwd); let self_exe = std::env::current_exe().expect("current executable"); let runtime_paths = - ExecServerRuntimePaths::new(self_exe.clone(), Some(self_exe)).expect("runtime paths"); + ExecServerRuntimeOptions::new(self_exe.clone(), Some(self_exe)).expect("runtime paths"); let sandbox = FileSystemSandboxContext::from_permission_profile( PermissionProfile::workspace_write(), cwd_uri.clone(), @@ -192,7 +192,7 @@ async fn sandbox_request_routes_custom_arg0_to_inner_helper() { let cwd_uri = PathUri::from_abs_path(&cwd); let self_exe = std::env::current_exe().expect("current executable"); let runtime_paths = - ExecServerRuntimePaths::new(self_exe.clone(), Some(self_exe)).expect("runtime paths"); + ExecServerRuntimeOptions::new(self_exe.clone(), Some(self_exe)).expect("runtime paths"); let sandbox = FileSystemSandboxContext::from_permission_profile( PermissionProfile::workspace_write(), cwd_uri.clone(), @@ -246,7 +246,7 @@ async fn sandbox_request_routes_custom_arg0_to_inner_helper() { } #[cfg(target_os = "macos")] -fn managed_network_sandbox_request() -> (ExecParams, ExecServerRuntimePaths) { +fn managed_network_sandbox_request() -> (ExecParams, ExecServerRuntimeOptions) { let cwd: AbsolutePathBuf = std::env::current_dir() .expect("current directory") .try_into() @@ -254,7 +254,7 @@ fn managed_network_sandbox_request() -> (ExecParams, ExecServerRuntimePaths) { let cwd_uri = PathUri::from_abs_path(&cwd); let self_exe = std::env::current_exe().expect("current executable"); let runtime_paths = - ExecServerRuntimePaths::new(self_exe.clone(), Some(self_exe)).expect("runtime paths"); + ExecServerRuntimeOptions::new(self_exe.clone(), Some(self_exe)).expect("runtime paths"); let sandbox = FileSystemSandboxContext::from_permission_profile( PermissionProfile::workspace_write(), cwd_uri.clone(), @@ -646,7 +646,7 @@ async fn managed_network_honors_windows_sandbox_level(windows_sandbox_level: Win .expect("absolute cwd"); let cwd_uri = PathUri::from_abs_path(&cwd); let self_exe = std::env::current_exe().expect("current executable"); - let runtime_paths = ExecServerRuntimePaths::new(self_exe, None).expect("runtime paths"); + let runtime_paths = ExecServerRuntimeOptions::new(self_exe, None).expect("runtime paths"); let permissions = PermissionProfile::read_only(); let mut sandbox = FileSystemSandboxContext::from_permission_profile(permissions.clone(), cwd_uri.clone()); diff --git a/codex-rs/exec-server/src/relay.rs b/codex-rs/exec-server/src/relay.rs index bd1346953f..6af5ee9cb9 100644 --- a/codex-rs/exec-server/src/relay.rs +++ b/codex-rs/exec-server/src/relay.rs @@ -1116,7 +1116,7 @@ mod tests { #[tokio::test] async fn multiplexed_environment_sends_keepalive() -> anyhow::Result<()> { let (client_websocket, mut server_websocket) = websocket_pair().await?; - let runtime_paths = crate::ExecServerRuntimePaths::new( + let runtime_paths = crate::ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/exec-server/src/relay_noise_tests.rs b/codex-rs/exec-server/src/relay_noise_tests.rs index e066e1eb5e..48b52d9798 100644 --- a/codex-rs/exec-server/src/relay_noise_tests.rs +++ b/codex-rs/exec-server/src/relay_noise_tests.rs @@ -28,7 +28,7 @@ use super::NOISE_HANDSHAKE_FAILURE_COOLDOWN; use super::RendezvousDisconnectReason; use super::run_multiplexed_environment; use crate::ExecServerError; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::connection::JsonRpcConnectionEvent; use crate::noise_channel::InitiatorHandshake; use crate::noise_channel::NoiseChannelIdentity; @@ -140,7 +140,7 @@ async fn missing_pong_disconnects_physical_relay() -> Result<()> { let environment_task = tokio::spawn(run_multiplexed_environment( environment_websocket, - ConnectionProcessor::new(ExecServerRuntimePaths::new( + ConnectionProcessor::new(ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?), @@ -171,7 +171,7 @@ async fn pong_keeps_physical_relay_connected() -> Result<()> { let environment_task = tokio::spawn(run_multiplexed_environment( environment_websocket, - ConnectionProcessor::new(ExecServerRuntimePaths::new( + ConnectionProcessor::new(ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?), @@ -245,7 +245,7 @@ async fn processor_exit_resets_noise_harness_stream() -> Result<()> { let environment_task = AbortOnDropHandle::new(tokio::spawn(run_multiplexed_environment( environment_websocket, ObservedRegistration( - ConnectionProcessor::new(ExecServerRuntimePaths::new( + ConnectionProcessor::new(ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?), @@ -315,7 +315,7 @@ async fn pending_harness_key_validation_does_not_block_new_handshakes() -> Resul let calls = Arc::new(AtomicUsize::new(0)); let environment_task = tokio::spawn(run_multiplexed_environment( environment_websocket, - ConnectionProcessor::new(ExecServerRuntimePaths::new( + ConnectionProcessor::new(ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?), @@ -369,7 +369,7 @@ async fn duplicate_handshakes_pause_admission_without_disconnecting() -> Result< let release = Arc::new(Notify::new()); let environment_task = tokio::spawn(run_multiplexed_environment( environment_websocket, - ConnectionProcessor::new(ExecServerRuntimePaths::new( + ConnectionProcessor::new(ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?), @@ -478,7 +478,7 @@ async fn oversized_harness_authorization_is_rejected_before_validation() -> Resu let calls = Arc::new(AtomicUsize::new(0)); let environment_task = tokio::spawn(run_multiplexed_environment( environment_websocket, - ConnectionProcessor::new(ExecServerRuntimePaths::new( + ConnectionProcessor::new(ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?), @@ -697,7 +697,7 @@ async fn early_data_during_validation_pauses_admission_without_disconnecting() - let harness_identity = NoiseChannelIdentity::generate()?; let environment_task = tokio::spawn(run_multiplexed_environment( environment_websocket, - ConnectionProcessor::new(ExecServerRuntimePaths::new( + ConnectionProcessor::new(ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?), diff --git a/codex-rs/exec-server/src/remote.rs b/codex-rs/exec-server/src/remote.rs index 6fa005ba50..ce4a7fcd53 100644 --- a/codex-rs/exec-server/src/remote.rs +++ b/codex-rs/exec-server/src/remote.rs @@ -33,7 +33,7 @@ use crate::EnvironmentRegistryHarnessKeyValidationResponse; use crate::EnvironmentRegistryRegistrationRequest; use crate::EnvironmentRegistryRegistrationResponse; use crate::ExecServerError; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::ExecServerTelemetry; use crate::NoiseChannelIdentity; use crate::NoiseChannelPublicKey; @@ -603,7 +603,7 @@ impl RemoteEnvironmentConfig { /// conflict refreshes the registration; other permanent client errors stop the runner. pub async fn run_remote_environment( config: RemoteEnvironmentConfig, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, ) -> Result<(), ExecServerError> { run_remote_environment_until_shutdown(config, runtime_paths, std::future::pending()).await } @@ -613,7 +613,7 @@ pub async fn run_remote_environment( /// Active sessions and their processes are drained before this function returns. pub async fn run_remote_environment_until_shutdown( config: RemoteEnvironmentConfig, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, shutdown: F, ) -> Result<(), ExecServerError> where diff --git a/codex-rs/exec-server/src/remote/direct_tests.rs b/codex-rs/exec-server/src/remote/direct_tests.rs index b0dcb98d5d..73e9345b9b 100644 --- a/codex-rs/exec-server/src/remote/direct_tests.rs +++ b/codex-rs/exec-server/src/remote/direct_tests.rs @@ -28,7 +28,7 @@ use wiremock::matchers::method; use wiremock::matchers::path; use super::*; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::RemoteEnvironmentTransport; #[derive(Debug)] @@ -324,7 +324,7 @@ async fn direct_registration_handles_policy_outages_and_permanent_failures() -> HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault) .with_network_policy(policy.clone().for_current_account()), )?; - let runtime_paths = ExecServerRuntimePaths::new( + let runtime_paths = ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?; @@ -414,7 +414,7 @@ async fn direct_websocket_reuses_registration_and_stops_on_permanent_errors() -> Arc::new(StaticAuthProvider), HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault), )?; - let runtime_paths = ExecServerRuntimePaths::new( + let runtime_paths = ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?; diff --git a/codex-rs/exec-server/src/remote/noise_tests.rs b/codex-rs/exec-server/src/remote/noise_tests.rs index c59328227a..312bf3351c 100644 --- a/codex-rs/exec-server/src/remote/noise_tests.rs +++ b/codex-rs/exec-server/src/remote/noise_tests.rs @@ -188,7 +188,7 @@ async fn reconnect_reuses_registration_until_url_is_rejected() -> Result<()> { )?; let environment_task = tokio::spawn(run_remote_environment( config, - ExecServerRuntimePaths::new( + ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?, diff --git a/codex-rs/exec-server/src/runtime_paths.rs b/codex-rs/exec-server/src/runtime_options.rs similarity index 81% rename from codex-rs/exec-server/src/runtime_paths.rs rename to codex-rs/exec-server/src/runtime_options.rs index 46b3f524c9..812e09553f 100644 --- a/codex-rs/exec-server/src/runtime_paths.rs +++ b/codex-rs/exec-server/src/runtime_options.rs @@ -1,3 +1,5 @@ +//! Executor-local paths and trusted startup settings used when preparing execution. + use std::path::PathBuf; use codex_sandboxing::LinuxSandboxPidNamespace; @@ -5,7 +7,7 @@ use codex_utils_absolute_path::AbsolutePathBuf; /// Paths and sandbox settings initialized when creating an executor. #[derive(Clone, Debug, Eq, PartialEq)] -pub struct ExecServerRuntimePaths { +pub struct ExecServerRuntimeOptions { /// Stable path to the Codex executable used to launch hidden helper modes. pub codex_self_exe: AbsolutePathBuf, /// Path to the Linux sandbox helper alias used when the platform sandbox @@ -13,12 +15,14 @@ pub struct ExecServerRuntimePaths { pub codex_linux_sandbox_exe: Option, /// Trusted startup policy; requests and repository config cannot change PID isolation. pub linux_sandbox_pid_namespace: LinuxSandboxPidNamespace, + /// Trusted startup routing; request policy still controls destination access. + pub proxy_private_ips_via_upstream: bool, /// User-config opt-out of writable-root symlink checks beneath this host's home. #[cfg(target_os = "macos")] pub allowed_symlinked_codex_home: Option, } -impl ExecServerRuntimePaths { +impl ExecServerRuntimeOptions { pub fn from_optional_paths( codex_self_exe: Option, codex_linux_sandbox_exe: Option, @@ -38,6 +42,7 @@ impl ExecServerRuntimePaths { ) -> std::io::Result { Ok(Self { linux_sandbox_pid_namespace: LinuxSandboxPidNamespace::default(), + proxy_private_ips_via_upstream: false, codex_self_exe: absolute_path(codex_self_exe)?, codex_linux_sandbox_exe: codex_linux_sandbox_exe.map(absolute_path).transpose()?, #[cfg(target_os = "macos")] @@ -51,6 +56,12 @@ impl ExecServerRuntimePaths { self } + /// Routes permitted private IPs through the executor's configured upstream proxy. + pub fn with_proxy_private_ips_via_upstream(mut self, enabled: bool) -> Self { + self.proxy_private_ips_via_upstream = enabled; + self + } + /// Applies the symlink opt-in resolved by the execution host's config loader. #[cfg(target_os = "macos")] pub fn with_allowed_symlinked_codex_home( diff --git a/codex-rs/exec-server/src/sandboxed_file_system.rs b/codex-rs/exec-server/src/sandboxed_file_system.rs index 92b857676f..858359444c 100644 --- a/codex-rs/exec-server/src/sandboxed_file_system.rs +++ b/codex-rs/exec-server/src/sandboxed_file_system.rs @@ -10,7 +10,7 @@ use crate::CapabilityRootsDiscoverResponse; use crate::CopyOptions; use crate::CreateDirectoryOptions; use crate::DiscoverV2CapabilitiesResponse; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::ExecutorFileSystem; use crate::ExecutorFileSystemFuture; use crate::FILE_READ_CHUNK_SIZE; @@ -96,7 +96,7 @@ impl SandboxedFileSystem { .map_err(map_sandbox_error) } - pub fn new(runtime_paths: ExecServerRuntimePaths) -> Self { + pub fn new(runtime_paths: ExecServerRuntimeOptions) -> Self { Self { sandbox_runner: FileSystemSandboxRunner::new(runtime_paths), } diff --git a/codex-rs/exec-server/src/sandboxed_file_system_path_uri_tests.rs b/codex-rs/exec-server/src/sandboxed_file_system_path_uri_tests.rs index ff8875cc69..e74e0d0a1a 100644 --- a/codex-rs/exec-server/src/sandboxed_file_system_path_uri_tests.rs +++ b/codex-rs/exec-server/src/sandboxed_file_system_path_uri_tests.rs @@ -129,7 +129,7 @@ fn sandbox_routing_uses_independent_read_write_policies_and_executor_temp_rules( #[tokio::test] async fn sandboxed_file_system_rejects_non_native_uri_as_invalid_input() { - let runtime_paths = ExecServerRuntimePaths::new( + let runtime_paths = ExecServerRuntimeOptions::new( std::env::current_exe().expect("current exe"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/exec-server/src/server.rs b/codex-rs/exec-server/src/server.rs index 1b775a6b15..29f1440d29 100644 --- a/codex-rs/exec-server/src/server.rs +++ b/codex-rs/exec-server/src/server.rs @@ -20,14 +20,14 @@ pub use request_dispatcher::RequestDispatchMode; pub use transport::DEFAULT_LISTEN_URL; pub use transport::ExecServerListenUrlParseError; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::ExecServerTelemetry; use codex_http_client::HttpClientFactory; use codex_websocket_auth::WebsocketAuthSettings; pub async fn run_main( listen_url: &str, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, http_client_factory: HttpClientFactory, ) -> Result<(), Box> { run_main_with_telemetry( @@ -48,7 +48,7 @@ pub async fn run_main( )] pub async fn run_main_with_telemetry( listen_url: &str, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, telemetry: ExecServerTelemetry, http_client_factory: HttpClientFactory, request_dispatch_mode: RequestDispatchMode, @@ -77,7 +77,7 @@ mod tests { use tracing_subscriber::prelude::*; use super::run_main_with_telemetry; - use crate::ExecServerRuntimePaths; + use crate::ExecServerRuntimeOptions; use crate::ExecServerTelemetry; #[tokio::test] @@ -93,7 +93,7 @@ mod tests { tracing::callsite::rebuild_interest_cache(); run_main_with_telemetry( "invalid", - ExecServerRuntimePaths::new( + ExecServerRuntimeOptions::new( std::env::current_exe().expect("current executable"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/exec-server/src/server/file_system_handler.rs b/codex-rs/exec-server/src/server/file_system_handler.rs index 54ea3e29c1..3dfadd53bf 100644 --- a/codex-rs/exec-server/src/server/file_system_handler.rs +++ b/codex-rs/exec-server/src/server/file_system_handler.rs @@ -8,7 +8,7 @@ use crate::CapabilityRootsDiscoverParams; use crate::CapabilityRootsDiscoverResponse; use crate::CopyOptions; use crate::CreateDirectoryOptions; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::ExecutorFileSystem; use crate::GetMetadataOptions; use crate::ReadFileOptions; @@ -59,7 +59,7 @@ pub(crate) struct FileSystemHandler { } impl FileSystemHandler { - pub(crate) fn new(runtime_paths: ExecServerRuntimePaths) -> Self { + pub(crate) fn new(runtime_paths: ExecServerRuntimeOptions) -> Self { Self { file_system: LocalFileSystem::with_runtime_paths(runtime_paths), file_reads: FileReadHandleManager::default(), @@ -369,7 +369,7 @@ mod tests { #[tokio::test] async fn no_platform_sandbox_policies_do_not_require_configured_sandbox_helper() { let temp_dir = tempfile::tempdir().expect("tempdir"); - let runtime_paths = ExecServerRuntimePaths::new( + let runtime_paths = ExecServerRuntimeOptions::new( std::env::current_exe().expect("current exe"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/exec-server/src/server/handler.rs b/codex-rs/exec-server/src/server/handler.rs index 906cc681f7..8d3374173d 100644 --- a/codex-rs/exec-server/src/server/handler.rs +++ b/codex-rs/exec-server/src/server/handler.rs @@ -13,7 +13,7 @@ use tokio::sync::Mutex; use tokio_util::sync::CancellationToken; use tokio_util::task::TaskTracker; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::client::http_client::PendingRouteAwareHttpBodyStream; use crate::client::http_client::RouteAwareHttpClient; use crate::client::http_client::RouteAwareHttpRequestRunner; @@ -83,7 +83,7 @@ pub(crate) struct ExecServerHandler { background_task_shutdown: CancellationToken, background_tasks: TaskTracker, file_system: FileSystemHandler, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, http_client: RouteAwareHttpClient, initialize_requested: AtomicBool, initialized: AtomicBool, @@ -93,7 +93,7 @@ impl ExecServerHandler { pub(crate) fn new( session_registry: Arc, notifications: RpcNotificationSender, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, http_client_factory: HttpClientFactory, ) -> Self { Self { diff --git a/codex-rs/exec-server/src/server/handler/tests.rs b/codex-rs/exec-server/src/server/handler/tests.rs index 5d2f43d11a..00de894176 100644 --- a/codex-rs/exec-server/src/server/handler/tests.rs +++ b/codex-rs/exec-server/src/server/handler/tests.rs @@ -10,7 +10,7 @@ use tokio::sync::mpsc; use uuid::Uuid; use super::ExecServerHandler; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::ProcessId; use crate::protocol::ExecParams; use crate::protocol::InitializeParams; @@ -77,8 +77,8 @@ fn windows_command_processor() -> String { std::env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".to_string()) } -fn test_runtime_paths() -> ExecServerRuntimePaths { - ExecServerRuntimePaths::new( +fn test_runtime_paths() -> ExecServerRuntimeOptions { + ExecServerRuntimeOptions::new( std::env::current_exe().expect("current exe"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/exec-server/src/server/process_handler.rs b/codex-rs/exec-server/src/server/process_handler.rs index a4aca9d72c..71fbe56f6e 100644 --- a/codex-rs/exec-server/src/server/process_handler.rs +++ b/codex-rs/exec-server/src/server/process_handler.rs @@ -1,7 +1,7 @@ use crate::process_telemetry::ProcessTelemetry; use codex_exec_server_protocol::JSONRPCErrorError; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::local_process::LocalProcess; use crate::protocol::ExecParams; use crate::protocol::ExecResponse; @@ -25,7 +25,7 @@ impl ProcessHandler { pub(crate) fn new( notifications: RpcNotificationSender, telemetry: ExecServerTelemetry, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, ) -> Self { Self { process: LocalProcess::new(notifications, telemetry, runtime_paths), diff --git a/codex-rs/exec-server/src/server/process_otel_tests.rs b/codex-rs/exec-server/src/server/process_otel_tests.rs index 2e53cb1c82..93638142d8 100644 --- a/codex-rs/exec-server/src/server/process_otel_tests.rs +++ b/codex-rs/exec-server/src/server/process_otel_tests.rs @@ -37,7 +37,7 @@ use wiremock::matchers::method; use super::ExecServerHandler; use super::registry::build_router; use super::session_registry::SessionRegistry; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::ExecServerTelemetry; use crate::connection::JsonRpcConnectionEvent; use crate::protocol::EXEC_METHOD; @@ -739,7 +739,7 @@ fn new_handler( let mut handler = ExecServerHandler::new( Arc::clone(sessions), RpcNotificationSender::new(outgoing), - ExecServerRuntimePaths::new( + ExecServerRuntimeOptions::new( std::env::current_exe().expect("test executable"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/exec-server/src/server/processor.rs b/codex-rs/exec-server/src/server/processor.rs index 289551b389..81cf9c54bf 100644 --- a/codex-rs/exec-server/src/server/processor.rs +++ b/codex-rs/exec-server/src/server/processor.rs @@ -9,7 +9,7 @@ use tokio_util::task::AbortOnDropHandle; use tracing::debug; use tracing::warn; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::LocalFileSystem; use crate::connection::CHANNEL_CAPACITY; use crate::connection::JsonRpcConnection; @@ -38,7 +38,7 @@ pub(crate) struct ConnectionProcessor { capability_manager: Arc, capability_prewarm: Arc>, session_registry: Arc, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, telemetry: ExecServerTelemetry, http_client_factory: HttpClientFactory, request_dispatch_mode: RequestDispatchMode, @@ -46,7 +46,7 @@ pub(crate) struct ConnectionProcessor { impl ConnectionProcessor { #[cfg(test)] - pub(crate) fn new(runtime_paths: ExecServerRuntimePaths) -> Self { + pub(crate) fn new(runtime_paths: ExecServerRuntimeOptions) -> Self { Self::new_with_location_request( runtime_paths, ExecServerTelemetry::default(), @@ -62,7 +62,7 @@ impl ConnectionProcessor { } pub(crate) fn new_with_telemetry( - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, telemetry: ExecServerTelemetry, http_client_factory: HttpClientFactory, request_dispatch_mode: RequestDispatchMode, @@ -83,7 +83,7 @@ impl ConnectionProcessor { } fn new_with_location_request( - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, telemetry: ExecServerTelemetry, http_client_factory: HttpClientFactory, request_dispatch_mode: RequestDispatchMode, @@ -342,7 +342,7 @@ mod tests { use super::complete_queued_client_responses; use super::run_connection; - use crate::ExecServerRuntimePaths; + use crate::ExecServerRuntimeOptions; use crate::ProcessId; use crate::connection::JsonRpcConnection; use crate::connection::JsonRpcConnectionEvent; @@ -372,7 +372,7 @@ mod tests { #[tokio::test] async fn startup_prewarms_without_a_client_connection() -> anyhow::Result<()> { - let paths = ExecServerRuntimePaths::new( + let paths = ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?; @@ -637,8 +637,8 @@ mod tests { (client_writer, BufReader::new(client_reader).lines(), task) } - fn test_runtime_paths() -> ExecServerRuntimePaths { - ExecServerRuntimePaths::new( + fn test_runtime_paths() -> ExecServerRuntimeOptions { + ExecServerRuntimeOptions::new( std::env::current_exe().expect("current exe"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/exec-server/src/server/request_dispatcher_tests.rs b/codex-rs/exec-server/src/server/request_dispatcher_tests.rs index ead6dd07c8..5da5ed0d2f 100644 --- a/codex-rs/exec-server/src/server/request_dispatcher_tests.rs +++ b/codex-rs/exec-server/src/server/request_dispatcher_tests.rs @@ -31,7 +31,7 @@ use super::ConcurrentRequestLimit; use super::RequestDispatchMode; use super::RequestDispatcher; use super::RequestTaskResult; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::connection::JsonRpcConnectionEvent; use crate::rpc::RpcNotificationSender; use crate::rpc::RpcRouter; @@ -178,7 +178,7 @@ async fn request_queue_waits_for_dispatcher_admission_before_recording_telemetry let handler = Arc::new(ExecServerHandler::new( SessionRegistry::new(telemetry.clone()), notifications, - ExecServerRuntimePaths::new( + ExecServerRuntimeOptions::new( std::env::current_exe().expect("current executable"), /*codex_linux_sandbox_exe*/ None, ) @@ -387,7 +387,7 @@ fn request_telemetry_fixture(router: RpcRouter) -> RequestTel let handler = Arc::new(ExecServerHandler::new( SessionRegistry::new(telemetry.clone()), notifications, - ExecServerRuntimePaths::new( + ExecServerRuntimeOptions::new( std::env::current_exe().expect("current executable"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/exec-server/src/server/session_registry.rs b/codex-rs/exec-server/src/server/session_registry.rs index 89a861ab7b..7cfe5c30ca 100644 --- a/codex-rs/exec-server/src/server/session_registry.rs +++ b/codex-rs/exec-server/src/server/session_registry.rs @@ -7,7 +7,7 @@ use codex_exec_server_protocol::JSONRPCErrorError; use tokio::sync::Mutex; use uuid::Uuid; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::rpc::RpcNotificationSender; use crate::rpc::invalid_request; use crate::rpc::session_already_attached; @@ -64,7 +64,7 @@ impl SessionRegistry { self: &Arc, resume_session_id: Option, notifications: RpcNotificationSender, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, ) -> Result { enum AttachOutcome { Attached(Arc), diff --git a/codex-rs/exec-server/src/server/transport.rs b/codex-rs/exec-server/src/server/transport.rs index a0935de3fb..94b48774d8 100644 --- a/codex-rs/exec-server/src/server/transport.rs +++ b/codex-rs/exec-server/src/server/transport.rs @@ -30,7 +30,7 @@ use tokio::net::TcpListener; use tracing::info; use tracing::warn; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::ExecServerTelemetry; use crate::connection::JsonRpcConnection; use crate::server::RequestDispatchMode; @@ -91,7 +91,7 @@ pub(crate) fn parse_listen_url( pub(crate) async fn run_transport( listen_url: &str, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, telemetry: ExecServerTelemetry, http_client_factory: HttpClientFactory, request_dispatch_mode: RequestDispatchMode, @@ -129,7 +129,7 @@ pub(crate) async fn run_transport( } async fn run_stdio_connection( - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, telemetry: ExecServerTelemetry, http_client_factory: HttpClientFactory, request_dispatch_mode: RequestDispatchMode, @@ -148,7 +148,7 @@ async fn run_stdio_connection( async fn run_stdio_connection_with_io( reader: R, writer: W, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, telemetry: ExecServerTelemetry, http_client_factory: HttpClientFactory, request_dispatch_mode: RequestDispatchMode, @@ -177,7 +177,7 @@ where async fn run_websocket_listener( bind_address: SocketAddr, - runtime_paths: ExecServerRuntimePaths, + runtime_paths: ExecServerRuntimeOptions, telemetry: ExecServerTelemetry, http_client_factory: HttpClientFactory, request_dispatch_mode: RequestDispatchMode, diff --git a/codex-rs/exec-server/src/server/transport_tests.rs b/codex-rs/exec-server/src/server/transport_tests.rs index c26109a3b8..08d378f785 100644 --- a/codex-rs/exec-server/src/server/transport_tests.rs +++ b/codex-rs/exec-server/src/server/transport_tests.rs @@ -17,7 +17,7 @@ use super::DEFAULT_LISTEN_URL; use super::ExecServerListenTransport; use super::parse_listen_url; use super::run_stdio_connection_with_io; -use crate::ExecServerRuntimePaths; +use crate::ExecServerRuntimeOptions; use crate::protocol::INITIALIZE_METHOD; use crate::protocol::INITIALIZED_METHOD; use crate::protocol::InitializeParams; @@ -163,8 +163,8 @@ async fn write_jsonrpc_line(writer: &mut tokio::io::DuplexStream, message: &JSON .expect("JSON-RPC newline should write"); } -fn test_runtime_paths() -> ExecServerRuntimePaths { - ExecServerRuntimePaths::new( +fn test_runtime_paths() -> ExecServerRuntimeOptions { + ExecServerRuntimeOptions::new( std::env::current_exe().expect("current exe"), /*codex_linux_sandbox_exe*/ None, ) diff --git a/codex-rs/exec-server/testing/exec_server.rs b/codex-rs/exec-server/testing/exec_server.rs index cb57ab7061..0fc557d9c8 100644 --- a/codex-rs/exec-server/testing/exec_server.rs +++ b/codex-rs/exec-server/testing/exec_server.rs @@ -4,7 +4,7 @@ //! when a test only needs a WebSocket executor endpoint. It handles the arg0 //! helper mode because sandboxed process requests re-exec this binary. -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_http_client::HttpClientFactory; use codex_http_client::OutboundProxyPolicy; use std::ffi::OsStr; @@ -28,7 +28,7 @@ fn main() -> Result<(), Box> { let current_exe = std::env::current_exe()?; let codex_linux_sandbox_exe = std::env::var_os(CODEX_LINUX_SANDBOX_EXE_ENV_VAR).map(std::path::PathBuf::from); - let runtime_paths = ExecServerRuntimePaths::new(current_exe, codex_linux_sandbox_exe)?; + let runtime_paths = ExecServerRuntimeOptions::new(current_exe, codex_linux_sandbox_exe)?; tokio::runtime::Builder::new_multi_thread() .enable_all() .build()? diff --git a/codex-rs/exec-server/tests/accepted_websocket.rs b/codex-rs/exec-server/tests/accepted_websocket.rs index 5e0a51fda6..259ea8dfac 100644 --- a/codex-rs/exec-server/tests/accepted_websocket.rs +++ b/codex-rs/exec-server/tests/accepted_websocket.rs @@ -24,7 +24,7 @@ use codex_exec_server::ExecParams; use codex_exec_server::ExecProcessEvent; use codex_exec_server::ExecResponse; use codex_exec_server::ExecServerClientConnectOptions; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_exec_server::InitializeParams; use codex_exec_server::InitializeResponse; use codex_exec_server::ProcessId; @@ -174,7 +174,7 @@ async fn accepted_websocket_interoperates_and_recovers_with_real_direct_executor http_client_factory.clone(), )?; let (codex_exe, codex_linux_sandbox_exe) = common::current_test_binary_helper_paths()?; - let runtime_paths = ExecServerRuntimePaths::new(codex_exe, codex_linux_sandbox_exe)?; + let runtime_paths = ExecServerRuntimeOptions::new(codex_exe, codex_linux_sandbox_exe)?; let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel(); let executor_task = AbortOnDropHandle::new(tokio::spawn( codex_exec_server::run_remote_environment_until_shutdown( diff --git a/codex-rs/exec-server/tests/common/mod.rs b/codex-rs/exec-server/tests/common/mod.rs index 569f310337..0d7661c66f 100644 --- a/codex-rs/exec-server/tests/common/mod.rs +++ b/codex-rs/exec-server/tests/common/mod.rs @@ -8,7 +8,7 @@ use std::time::Duration; use codex_exec_server::CODEX_ARG0_EXEC_HELPER_ARG1; use codex_exec_server::CODEX_FS_HELPER_ARG1; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_exec_server::ExecServerTelemetry; use codex_exec_server::RequestDispatchMode; use codex_http_client::HttpClientFactory; @@ -214,7 +214,7 @@ fn maybe_run_exec_server_from_test_binary(guard: Option<&TestBinaryDispatchGuard std::process::exit(1); } }; - let runtime_paths = match ExecServerRuntimePaths::new( + let runtime_paths = match ExecServerRuntimeOptions::new( current_exe.clone(), linux_sandbox_exe(guard, ¤t_exe), ) { diff --git a/codex-rs/exec-server/tests/exec_process/shell_snapshot.rs b/codex-rs/exec-server/tests/exec_process/shell_snapshot.rs index 47082e0a7a..e836fef480 100644 --- a/codex-rs/exec-server/tests/exec_process/shell_snapshot.rs +++ b/codex-rs/exec-server/tests/exec_process/shell_snapshot.rs @@ -40,7 +40,7 @@ async fn shell_snapshot_concurrent_replays_keep_independent_readers( let (exe, linux_sandbox) = current_test_binary_helper_paths()?; let environment = Environment::create( /*exec_server_url*/ None, - codex_exec_server::ExecServerRuntimePaths::new(exe, linux_sandbox)?, + codex_exec_server::ExecServerRuntimeOptions::new(exe, linux_sandbox)?, codex_http_client::HttpClientFactory::new( codex_http_client::OutboundProxyPolicy::ReqwestDefault, ), diff --git a/codex-rs/exec-server/tests/file_system/shared.rs b/codex-rs/exec-server/tests/file_system/shared.rs index cc9553113b..46531b2fb9 100644 --- a/codex-rs/exec-server/tests/file_system/shared.rs +++ b/codex-rs/exec-server/tests/file_system/shared.rs @@ -5,7 +5,7 @@ use codex_exec_server::CreateDirectoryOptions; use codex_exec_server::EnvironmentAccess; use codex_exec_server::EnvironmentAccessExt; #[cfg(unix)] -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_exec_server::ExecutorFileSystem; use codex_exec_server::FILE_READ_CHUNK_SIZE; use codex_exec_server::FileMetadata; @@ -1155,7 +1155,7 @@ async fn sandboxed_file_operations_cannot_read_helper_siblings() -> Result<()> { None }; let file_system = - LocalFileSystem::with_runtime_paths(ExecServerRuntimePaths::new(helper, linux_sandbox)?); + LocalFileSystem::with_runtime_paths(ExecServerRuntimeOptions::new(helper, linux_sandbox)?); let sibling = runtime_dir.join("credentials.json"); std::fs::write(&sibling, "secret")?; diff --git a/codex-rs/exec-server/tests/file_system/support.rs b/codex-rs/exec-server/tests/file_system/support.rs index 4db701d29e..69949808a8 100644 --- a/codex-rs/exec-server/tests/file_system/support.rs +++ b/codex-rs/exec-server/tests/file_system/support.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use anyhow::Result; use codex_exec_server::Environment; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_exec_server::ExecutorFileSystem; use codex_exec_server::FileSystemSandboxContext; use codex_exec_server::LocalFileSystem; @@ -50,7 +50,7 @@ pub(crate) async fn create_file_system_context( match implementation { FileSystemImplementation::Local => { let helper_paths = test_codex_helper_paths()?; - let runtime_paths = ExecServerRuntimePaths::new( + let runtime_paths = ExecServerRuntimeOptions::new( helper_paths.codex_exe.clone(), helper_paths.codex_linux_sandbox_exe.clone(), )?; diff --git a/codex-rs/exec-server/tests/relay.rs b/codex-rs/exec-server/tests/relay.rs index d37f76331a..e7ff5360dc 100644 --- a/codex-rs/exec-server/tests/relay.rs +++ b/codex-rs/exec-server/tests/relay.rs @@ -22,7 +22,7 @@ use codex_exec_server::EnvironmentReadyInfo; use codex_exec_server::ExecParams; use codex_exec_server::ExecResponse; use codex_exec_server::ExecServerError; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_exec_server::FsReadFileParams; use codex_exec_server::NoiseChannelPublicKey; use codex_exec_server::NoiseRendezvousConnectBundle; @@ -162,7 +162,7 @@ async fn failed_noise_environment_recovers_and_reconnects_after_ready_report() - .await; let (codex_exe, codex_linux_sandbox_exe) = common::current_test_binary_helper_paths()?; - let runtime_paths = ExecServerRuntimePaths::new(codex_exe, codex_linux_sandbox_exe)?; + let runtime_paths = ExecServerRuntimeOptions::new(codex_exe, codex_linux_sandbox_exe)?; let http_client_factory = HttpClientFactory::new(OutboundProxyPolicy::RespectSystemProxy); let config = RemoteEnvironmentConfig::new( registry.uri(), @@ -361,7 +361,7 @@ async fn next_connection_state( async fn remote_environment_routes_encrypted_exec_server_rpc() -> Result<()> { let relay = RelayTest::new().await?; let (codex_exe, codex_linux_sandbox_exe) = common::current_test_binary_helper_paths()?; - let runtime_paths = ExecServerRuntimePaths::new(codex_exe, codex_linux_sandbox_exe)?; + let runtime_paths = ExecServerRuntimeOptions::new(codex_exe, codex_linux_sandbox_exe)?; let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel(); let remote_environment = AbortOnDropHandle::new(tokio::spawn( codex_exec_server::run_remote_environment_until_shutdown( diff --git a/codex-rs/exec-server/tests/relay/registration_retry_tests.rs b/codex-rs/exec-server/tests/relay/registration_retry_tests.rs index 99455a6cc7..792d909fa0 100644 --- a/codex-rs/exec-server/tests/relay/registration_retry_tests.rs +++ b/codex-rs/exec-server/tests/relay/registration_retry_tests.rs @@ -76,7 +76,7 @@ impl RegistryFixture { HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault), )?; let (codex_exe, sandbox_exe) = common::current_test_binary_helper_paths()?; - let runtime_paths = ExecServerRuntimePaths::new(codex_exe, sandbox_exe)?; + let runtime_paths = ExecServerRuntimeOptions::new(codex_exe, sandbox_exe)?; let (shutdown_tx, shutdown_rx) = oneshot::channel(); let task = AbortOnDropHandle::new(tokio::spawn( codex_exec_server::run_remote_environment_until_shutdown( diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 4cffddd43c..0bfa07bd55 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -17,7 +17,7 @@ pub use cli::Command; pub use cli::ReviewArgs; use codex_app_server_client::DEFAULT_IN_PROCESS_CHANNEL_CAPACITY; use codex_app_server_client::EnvironmentManager; -use codex_app_server_client::ExecServerRuntimePaths; +use codex_app_server_client::ExecServerRuntimeOptions; use codex_app_server_client::InProcessAppServerClient; use codex_app_server_client::InProcessClientStartArgs; use codex_app_server_client::InProcessServerEvent; @@ -686,7 +686,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result range: None, }) .collect(); - let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths( + let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths( arg0_paths.codex_self_exe.clone(), arg0_paths.codex_linux_sandbox_exe.clone(), )?; diff --git a/codex-rs/exec/src/worktree.rs b/codex-rs/exec/src/worktree.rs index 05771ccfa9..a61d173dfa 100644 --- a/codex-rs/exec/src/worktree.rs +++ b/codex-rs/exec/src/worktree.rs @@ -20,7 +20,7 @@ pub(super) async fn fork_source( let state_db = codex_core::init_state_db(config).await; let environment_manager = EnvironmentManager::from_codex_home( config.codex_home.clone(), - Some(ExecServerRuntimePaths::from_optional_paths( + Some(ExecServerRuntimeOptions::from_optional_paths( arg0_paths.codex_self_exe.clone(), arg0_paths.codex_linux_sandbox_exe.clone(), )?), diff --git a/codex-rs/network-proxy/src/http_proxy.rs b/codex-rs/network-proxy/src/http_proxy.rs index 96163abf3c..b2953d35f1 100644 --- a/codex-rs/network-proxy/src/http_proxy.rs +++ b/codex-rs/network-proxy/src/http_proxy.rs @@ -478,7 +478,7 @@ where } }; let proxy = if allow_upstream_proxy { - proxy_for_connect(&authority) + proxy_for_connect(&authority, &app_state) } else { None }; @@ -1819,6 +1819,55 @@ mod tests { ); } + #[tokio::test] + async fn private_ip_upstream_routing_preserves_destination_policy() { + for (allowlisted, denied, expected_error) in [ + (true, true, "blocked-by-denylist"), + (false, false, "blocked-by-allowlist"), + ] { + let mut policy = NetworkProxyConfig { + allow_local_binding: Some(true), + ..NetworkProxyConfig::default() + }; + if allowlisted { + policy.set_allowed_domains(vec!["100.68.58.50".to_string()]); + } + if denied { + policy.set_denied_domains(vec!["100.68.58.50".to_string()]); + } + let mut state = network_proxy_state_for_policy(policy); + state.set_proxy_private_ips_via_upstream(/*enabled*/ true); + let state = Arc::new(state); + for method in [Method::GET, Method::CONNECT] { + let mut req = Request::builder() + .method(method.clone()) + .uri("http://100.68.58.50:80/api/items") + .header("host", "100.68.58.50:80") + .body(Body::empty()) + .unwrap(); + req.extensions_mut().insert(state.clone()); + let response = if method == Method::CONNECT { + http_connect_accept( + /*policy_decider*/ None, /*environment_id*/ None, req, + ) + .await + .unwrap_err() + } else { + http_plain_proxy( + /*policy_decider*/ None, /*environment_id*/ None, req, + ) + .await + .unwrap() + }; + assert_eq!(response.status(), StatusCode::FORBIDDEN); + assert_eq!( + response.headers().get("x-proxy-error").unwrap(), + expected_error + ); + } + } + } + #[tokio::test] async fn http_plain_proxy_rejects_absolute_uri_host_header_mismatch() { let state = Arc::new(network_proxy_state_for_policy(NetworkProxyConfig::default())); diff --git a/codex-rs/network-proxy/src/policy.rs b/codex-rs/network-proxy/src/policy.rs index c8ed7c1c29..0cfd7c04c7 100644 --- a/codex-rs/network-proxy/src/policy.rs +++ b/codex-rs/network-proxy/src/policy.rs @@ -90,6 +90,18 @@ fn ipv4_in_cidr(ip: Ipv4Addr, base: [u8; 4], prefix: u8) -> bool { (ip & mask) == (base & mask) } +/// Private unicast ranges that may be reachable through an upstream VPN proxy. +/// Loopback, link-local, and other special-use addresses retain their normal routing. +pub(crate) fn is_private_network_ip(ip: IpAddr) -> bool { + match ip { + IpAddr::V4(ip) => ip.is_private() || ipv4_in_cidr(ip, [100, 64, 0, 0], /*prefix*/ 10), + IpAddr::V6(ip) => match ip.to_ipv4() { + Some(ip) => is_private_network_ip(IpAddr::V4(ip)), + None => ip.is_unique_local(), + }, + } +} + fn is_non_public_ipv6(ip: Ipv6Addr) -> bool { if let Some(v4) = ip.to_ipv4() { return is_non_public_ipv4(v4) || ip.is_loopback(); diff --git a/codex-rs/network-proxy/src/runtime.rs b/codex-rs/network-proxy/src/runtime.rs index 8cc6adac1a..10440e00cf 100644 --- a/codex-rs/network-proxy/src/runtime.rs +++ b/codex-rs/network-proxy/src/runtime.rs @@ -262,6 +262,7 @@ pub struct NetworkProxyState { state: Arc>, /// Belongs to this proxy; config retains omission for launches on other executors. pub(crate) local_binding_policy: LocalBindingPolicy, + pub(crate) proxy_private_ips_via_upstream: bool, reloader: Arc, blocked_request_observer: Arc>>>, pub(crate) policy_audit_observer: Option, @@ -300,6 +301,7 @@ impl Clone for NetworkProxyState { Self { state: self.state.clone(), local_binding_policy: self.local_binding_policy, + proxy_private_ips_via_upstream: self.proxy_private_ips_via_upstream, reloader: self.reloader.clone(), blocked_request_observer: self.blocked_request_observer.clone(), policy_audit_observer: self.policy_audit_observer.clone(), @@ -315,6 +317,12 @@ impl Clone for NetworkProxyState { } impl NetworkProxyState { + /// Selects routing for permitted private IPs without changing destination policy. + /// Set by the execution host, independently of reloadable or remote policy. + pub fn set_proxy_private_ips_via_upstream(&mut self, enabled: bool) { + self.proxy_private_ips_via_upstream = enabled; + } + /// Builds runtime state for one executor-local proxy launch. /// The launching executor supplies its own OS; the wire policy cannot override it. pub fn from_remote_launch_config( @@ -397,6 +405,7 @@ impl NetworkProxyState { credential_broker, state: Arc::new(RwLock::new(state)), local_binding_policy, + proxy_private_ips_via_upstream: false, reloader, blocked_request_observer: Arc::new(RwLock::new(blocked_request_observer)), policy_audit_observer: None, diff --git a/codex-rs/network-proxy/src/upstream.rs b/codex-rs/network-proxy/src/upstream.rs index fa36c8f58b..2ad98e8cd4 100644 --- a/codex-rs/network-proxy/src/upstream.rs +++ b/codex-rs/network-proxy/src/upstream.rs @@ -1,5 +1,6 @@ use crate::connect_policy::TargetCheckedTcpConnector; use crate::connect_policy::is_non_public_target; +use crate::policy::is_private_network_ip; use crate::state::NetworkProxyState; use codex_utils_rustls_provider::ensure_rustls_crypto_provider; use rama_core::Layer; @@ -18,6 +19,7 @@ use rama_http::layer::version_adapter::RequestVersionAdapter; use rama_http_backend::client::HttpClientService; use rama_http_backend::client::HttpConnector; use rama_http_backend::client::proxy::layer::HttpProxyConnectorLayer; +use rama_net::address::Host; use rama_net::address::HostWithPort; use rama_net::address::ProxyAddress; use rama_net::client::EstablishedClientConnection; @@ -39,14 +41,20 @@ struct ProxyConfig { http: Option, https: Option, all: Option, + proxy_private_ips_via_upstream: bool, } impl ProxyConfig { - fn from_env() -> Self { + fn from_env(proxy_private_ips_via_upstream: bool) -> Self { let http = read_proxy_env(&["HTTP_PROXY", "http_proxy"]); let https = read_proxy_env(&["HTTPS_PROXY", "https_proxy"]); let all = read_proxy_env(&["ALL_PROXY", "all_proxy"]); - Self { http, https, all } + Self { + http, + https, + all, + proxy_private_ips_via_upstream, + } } fn proxy_for_protocol(&self, is_secure: bool) -> Option { @@ -61,7 +69,9 @@ impl ProxyConfig { } fn proxy_for_target(&self, target: &HostWithPort, is_secure: bool) -> Option { - if is_non_public_target(&target.host) { + let proxy_private_ip = self.proxy_private_ips_via_upstream + && matches!(&target.host, Host::Address(ip) if is_private_network_ip(*ip)); + if is_non_public_target(&target.host) && !proxy_private_ip { return None; } self.proxy_for_protocol(is_secure) @@ -97,8 +107,12 @@ fn read_proxy_env(keys: &[&str]) -> Option { None } -pub(crate) fn proxy_for_connect(target: &HostWithPort) -> Option { - ProxyConfig::from_env().proxy_for_target(target, /*is_secure*/ true) +pub(crate) fn proxy_for_connect( + target: &HostWithPort, + state: &NetworkProxyState, +) -> Option { + ProxyConfig::from_env(state.proxy_private_ips_via_upstream) + .proxy_for_target(target, /*is_secure*/ true) } #[derive(Clone)] @@ -122,7 +136,7 @@ impl UpstreamClient { pub(crate) fn from_env_proxy(state: Arc) -> Self { Self::new( - ProxyConfig::from_env(), + ProxyConfig::from_env(state.proxy_private_ips_via_upstream), TargetCheckedTcpConnector::new(state), client_root_certs(), ) @@ -144,7 +158,7 @@ impl UpstreamClient { tls_root_store: Arc, ) -> Self { Self::new( - ProxyConfig::from_env(), + ProxyConfig::from_env(state.proxy_private_ips_via_upstream), TargetCheckedTcpConnector::new(state), tls_root_store, ) diff --git a/codex-rs/network-proxy/src/upstream_tests.rs b/codex-rs/network-proxy/src/upstream_tests.rs index a90efe9026..f2c8ea20c4 100644 --- a/codex-rs/network-proxy/src/upstream_tests.rs +++ b/codex-rs/network-proxy/src/upstream_tests.rs @@ -119,27 +119,43 @@ async fn mitm_upstream_client_trusts_startup_custom_ca() { } #[test] -fn inherited_upstream_proxy_is_bypassed_for_non_public_targets() { +fn private_ip_upstream_routing_is_opt_in() { let proxy = ProxyAddress::try_from("http://127.0.0.1:43128").unwrap(); - let config = ProxyConfig { - http: Some(proxy.clone()), - https: Some(proxy), - all: None, - }; - - for target in [ - HostWithPort::new(Host::LOCALHOST_NAME, 8080), - HostWithPort::new(Host::LOCALHOST_IPV4, 8080), - HostWithPort::new(Host::Address("10.0.0.1".parse().unwrap()), 8080), - ] { - assert_eq!(config.proxy_for_target(&target, /*is_secure*/ false), None); - assert_eq!(config.proxy_for_target(&target, /*is_secure*/ true), None); + for enabled in [false, true] { + let config = ProxyConfig { + http: Some(proxy.clone()), + https: Some(proxy.clone()), + proxy_private_ips_via_upstream: enabled, + ..ProxyConfig::default() + }; + for (host, use_proxy) in [ + ("10.0.0.1", enabled), + ("172.16.0.1", enabled), + ("192.168.0.1", enabled), + ("100.68.58.50", enabled), + ("fd00::1", enabled), + ("::ffff:100.68.58.50", enabled), + ("localhost", false), + ("127.0.0.1", false), + ("::1", false), + ("169.254.169.254", false), + ("fe80::1", false), + ("0.0.0.0", false), + ("224.0.0.1", false), + ("192.0.2.1", false), + ("240.0.0.1", false), + ("example.com", true), + ("100.63.255.255", true), + ("100.128.0.0", true), + ] { + let target = HostWithPort::new(host.parse::().unwrap(), /*port*/ 443); + for is_secure in [false, true] { + assert_eq!( + config.proxy_for_target(&target, is_secure), + use_proxy.then(|| proxy.clone()), + "host={host}, enabled={enabled}, secure={is_secure}" + ); + } + } } - - let public = HostWithPort::new(Host::EXAMPLE_NAME, 443); - assert!( - config - .proxy_for_target(&public, /*is_secure*/ true) - .is_some() - ); } diff --git a/codex-rs/thread-manager-sample/src/main.rs b/codex-rs/thread-manager-sample/src/main.rs index b8c7410cf6..ddbd85ba91 100644 --- a/codex-rs/thread-manager-sample/src/main.rs +++ b/codex-rs/thread-manager-sample/src/main.rs @@ -27,7 +27,7 @@ use codex_core_api::ConfigRequirementsToml; use codex_core_api::Constrained; use codex_core_api::EnvironmentManager; use codex_core_api::EventMsg; -use codex_core_api::ExecServerRuntimePaths; +use codex_core_api::ExecServerRuntimeOptions; use codex_core_api::ExtensionRegistryBuilder; use codex_core_api::Feature; use codex_core_api::Features; @@ -125,7 +125,7 @@ async fn run_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> { let auth_manager = AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false).await?; - let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths( + let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths( config.codex_self_exe.clone(), config.codex_linux_sandbox_exe.clone(), )?; diff --git a/codex-rs/tui/src/app/tests.rs b/codex-rs/tui/src/app/tests.rs index 1920ce4970..8fd01e8a85 100644 --- a/codex-rs/tui/src/app/tests.rs +++ b/codex-rs/tui/src/app/tests.rs @@ -7713,7 +7713,7 @@ async fn remote_exec_resume_current_cwd_is_rejected() -> Result<()> { app.environment_manager = Arc::new( EnvironmentManager::create_for_tests( Some("ws://127.0.0.1:8765".to_string()), - Some(codex_exec_server::ExecServerRuntimePaths::new( + Some(codex_exec_server::ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?), @@ -7960,7 +7960,7 @@ async fn in_app_resume_uses_configured_or_explicit_cwd() -> Result<()> { Arc::new( EnvironmentManager::create_for_tests( Some("ws://127.0.0.1:8765".to_string()), - Some(codex_exec_server::ExecServerRuntimePaths::new( + Some(codex_exec_server::ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?), diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index a6a91b0b9f..6fdada2855 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -52,7 +52,7 @@ use codex_config::LoaderOverrides; use codex_config::format_config_error_with_source; use codex_config::types::ResumeCwdMode; use codex_exec_server::EnvironmentManager; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_features::Feature; use codex_login::AuthConfig; use codex_login::default_client::originator; @@ -2625,7 +2625,7 @@ requires_openai_auth = {requires_openai_auth} .cloud_config_bundle(cloud_config_bundle) .build() .await?; - let runtime_paths = ExecServerRuntimePaths::new( + let runtime_paths = ExecServerRuntimeOptions::new( std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?; @@ -3638,7 +3638,7 @@ requires_openai_auth = {requires_openai_auth} let target = AppServerTarget::Embedded; let environment_manager = EnvironmentManager::create_for_tests( Some("ws://127.0.0.1:8765".to_string()), - Some(ExecServerRuntimePaths::new( + Some(ExecServerRuntimeOptions::new( std::env::current_exe().expect("current exe"), /*codex_linux_sandbox_exe*/ None, )?), diff --git a/codex-rs/tui/src/session_archive_commands.rs b/codex-rs/tui/src/session_archive_commands.rs index f117254468..0da6c2a495 100644 --- a/codex-rs/tui/src/session_archive_commands.rs +++ b/codex-rs/tui/src/session_archive_commands.rs @@ -25,7 +25,7 @@ use codex_config::CloudConfigBundleLoader; use codex_config::ConfigLoadOptions; use codex_config::LoaderOverrides; use codex_exec_server::EnvironmentManager; -use codex_exec_server::ExecServerRuntimePaths; +use codex_exec_server::ExecServerRuntimeOptions; use codex_protocol::ThreadId; use codex_utils_cli::CliConfigOverrides; use codex_utils_home_dir::find_codex_home; @@ -274,7 +274,7 @@ pub(super) async fn start_app_server_for_session_command( .clone() .filter(|_| app_server_target.uses_remote_workspace()); - let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths( + let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths( arg0_paths.codex_self_exe.clone(), arg0_paths.codex_linux_sandbox_exe.clone(), ) diff --git a/codex-rs/tui/src/startup_orchestration.rs b/codex-rs/tui/src/startup_orchestration.rs index 68282439dc..a0ff9cb31f 100644 --- a/codex-rs/tui/src/startup_orchestration.rs +++ b/codex-rs/tui/src/startup_orchestration.rs @@ -318,7 +318,7 @@ pub(super) async fn run_main_inner( .clone() .filter(|_| app_server_target.uses_remote_workspace()); - let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths( + let local_runtime_paths = ExecServerRuntimeOptions::from_optional_paths( arg0_paths.codex_self_exe.clone(), arg0_paths.codex_linux_sandbox_exe.clone(), )?; diff --git a/codex-rs/tui/src/worktree_startup.rs b/codex-rs/tui/src/worktree_startup.rs index a3e1f3b3a4..3d194468ac 100644 --- a/codex-rs/tui/src/worktree_startup.rs +++ b/codex-rs/tui/src/worktree_startup.rs @@ -139,7 +139,7 @@ pub(super) async fn prepare( color_eyre::eyre::bail!("`--worktree` is only supported for local sessions"); } let environment = prepared.build( - Some(ExecServerRuntimePaths::from_optional_paths( + Some(ExecServerRuntimeOptions::from_optional_paths( arg0_paths.codex_self_exe.clone(), arg0_paths.codex_linux_sandbox_exe.clone(), )?),