- Adds --listen <URL> to codex app-server with two listen modes:
- stdio:// (default, existing behavior)
- ws://IP:PORT (new websocket transport)
- Refactors message routing to be connection-aware:
- Tracks per-connection session state (initialize/experimental
capability)
- Routes responses/errors to the originating connection
- Broadcasts server notifications/requests to initialized connections
- Updates initialization semantics to be per connection (not
process-global), and updates app-server docs accordingly.
- Adds websocket accept/read/write handling (JSON-RPC per text frame,
ping/pong handling, connection lifecycle events).
Testing
- Unit tests for transport URL parsing and targeted response/error
routing.
- New websocket integration test validating:
- per-connection initialization requirements
- no cross-connection response leakage
- same request IDs on different connections route independently.
60 lines
1.7 KiB
Rust
60 lines
1.7 KiB
Rust
use clap::Parser;
|
|
use codex_app_server::AppServerTransport;
|
|
use codex_app_server::run_main_with_transport;
|
|
use codex_arg0::arg0_dispatch_or_else;
|
|
use codex_common::CliConfigOverrides;
|
|
use codex_core::config_loader::LoaderOverrides;
|
|
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";
|
|
|
|
#[derive(Debug, Parser)]
|
|
struct AppServerArgs {
|
|
/// Transport endpoint URL. Supported values: `stdio://` (default),
|
|
/// `ws://IP:PORT`.
|
|
#[arg(
|
|
long = "listen",
|
|
value_name = "URL",
|
|
default_value = AppServerTransport::DEFAULT_LISTEN_URL
|
|
)]
|
|
listen: AppServerTransport,
|
|
}
|
|
|
|
fn main() -> anyhow::Result<()> {
|
|
arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move {
|
|
let args = AppServerArgs::parse();
|
|
let managed_config_path = managed_config_path_from_debug_env();
|
|
let loader_overrides = LoaderOverrides {
|
|
managed_config_path,
|
|
..Default::default()
|
|
};
|
|
let transport = args.listen;
|
|
|
|
run_main_with_transport(
|
|
codex_linux_sandbox_exe,
|
|
CliConfigOverrides::default(),
|
|
loader_overrides,
|
|
false,
|
|
transport,
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
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
|
|
}
|