Files
codex/codex-rs/app-server/src/main.rs
Ruslan Nigmatullin 8a0ab3fc13 app-server: add Unix socket transport (#18255)
## Summary
- add unix:// app-server transport backed by the shared codex-uds crate
- reuse the websocket connection loop for axum and tungstenite-backed
streams
- add codex app-server proxy to bridge stdio clients to the control
socket
- tolerate Windows UDS backends that report a missing rendezvous path as
connection refused before binding

## Tests
- cargo test -p codex-app-server
control_socket_acceptor_forwards_websocket_text_messages_and_pings
- cargo test -p codex-app-server
- just fmt
- just fix -p codex-app-server
- git -c core.fsmonitor=false diff --check
2026-04-23 11:09:25 -07:00

94 lines
2.8 KiB
Rust

use clap::Parser;
use codex_app_server::AppServerTransport;
use codex_app_server::AppServerWebsocketAuthArgs;
use codex_app_server::run_main_with_transport;
use codex_arg0::Arg0DispatchPaths;
use codex_arg0::arg0_dispatch_or_else;
use codex_core::config_loader::LoaderOverrides;
use codex_protocol::protocol::SessionSource;
use codex_utils_cli::CliConfigOverrides;
use std::path::PathBuf;
// Debug-only test hook: lets integration tests point the server at a temporary
// managed config file without writing to /etc.
const MANAGED_CONFIG_PATH_ENV_VAR: &str = "CODEX_APP_SERVER_MANAGED_CONFIG_PATH";
const DISABLE_MANAGED_CONFIG_ENV_VAR: &str = "CODEX_APP_SERVER_DISABLE_MANAGED_CONFIG";
#[derive(Debug, Parser)]
struct AppServerArgs {
/// Transport endpoint URL. Supported values: `stdio://` (default),
/// `unix://`, `unix://PATH`, `ws://IP:PORT`, `off`.
#[arg(
long = "listen",
value_name = "URL",
default_value = AppServerTransport::DEFAULT_LISTEN_URL
)]
listen: AppServerTransport,
/// Session source used to derive product restrictions and metadata.
#[arg(
long = "session-source",
value_name = "SOURCE",
default_value = "vscode",
value_parser = SessionSource::from_startup_arg
)]
session_source: SessionSource,
#[command(flatten)]
auth: AppServerWebsocketAuthArgs,
}
fn main() -> anyhow::Result<()> {
arg0_dispatch_or_else(|arg0_paths: Arg0DispatchPaths| async move {
let args = AppServerArgs::parse();
let loader_overrides = if disable_managed_config_from_debug_env() {
LoaderOverrides::without_managed_config_for_tests()
} else {
managed_config_path_from_debug_env()
.map(LoaderOverrides::with_managed_config_path_for_tests)
.unwrap_or_default()
};
let transport = args.listen;
let session_source = args.session_source;
let auth = args.auth.try_into_settings()?;
run_main_with_transport(
arg0_paths,
CliConfigOverrides::default(),
loader_overrides,
/*default_analytics_enabled*/ false,
transport,
session_source,
auth,
)
.await?;
Ok(())
})
}
fn disable_managed_config_from_debug_env() -> bool {
#[cfg(debug_assertions)]
{
if let Ok(value) = std::env::var(DISABLE_MANAGED_CONFIG_ENV_VAR) {
return matches!(value.as_str(), "1" | "true" | "TRUE" | "yes" | "YES");
}
}
false
}
fn managed_config_path_from_debug_env() -> Option<PathBuf> {
#[cfg(debug_assertions)]
{
if let Ok(value) = std::env::var(MANAGED_CONFIG_PATH_ENV_VAR) {
return if value.is_empty() {
None
} else {
Some(PathBuf::from(value))
};
}
}
None
}