Files
codex/codex-rs/app-server/src/main.rs
stevenlee-oai 1d952f027e Add process-scoped PSP routing for ChatGPT requests (#36986)
## What changed

- Add a hidden global `--psp` runtime flag and propagate it through TUI, exec,
  app-server, remote-control, and in-process startup paths.
- Attach the `oai-chat-psp=true` cookie to first-party ChatGPT requests when
  enabled, using a cached cookie-aware client with sensitive request logging
  disabled.
- Keep the routing selection out of persistent configuration layers while
  preserving it across config refreshes and agent role changes.

## Testing

- Cover global flag parsing, app-server propagation, config-layer isolation,
  and preservation across config rebuilds and role changes.

GitOrigin-RevId: 05cdc61ffd7162d8e48fc1e166f4732113e5a816
2026-08-04 22:23:40 +00:00

154 lines
5.0 KiB
Rust

use clap::Parser;
use codex_app_server::AppServerCodeModeHostArgs;
use codex_app_server::AppServerRuntimeOptions;
use codex_app_server::AppServerTransport;
use codex_app_server::AppServerWebsocketAuthArgs;
use codex_app_server::PluginStartupTasks;
use codex_app_server::run_main_with_transport_options;
use codex_arg0::Arg0DispatchPaths;
use codex_arg0::arg0_dispatch_or_else;
use codex_config::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)]
#[command(version)]
struct AppServerArgs {
#[command(flatten)]
config_overrides: CliConfigOverrides,
#[command(flatten)]
code_mode_host: AppServerCodeModeHostArgs,
/// 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,
/// Fail if config.toml contains unknown configuration fields.
#[arg(long = "strict-config", default_value_t = false)]
strict_config: bool,
/// Hidden debug-only test hook used by integration tests that spawn the
/// production app-server binary.
#[cfg(debug_assertions)]
#[arg(long = "disable-plugin-startup-tasks-for-tests", hide = true)]
disable_plugin_startup_tasks_for_tests: bool,
/// Enable remote control for this app-server process without changing persistence.
#[arg(long = "remote-control", hide = true)]
remote_control: bool,
/// Enable process-only PSP routing for first-party ChatGPT requests.
#[arg(long, hide = true)]
psp: bool,
}
fn main() -> anyhow::Result<()> {
let remote_control_disabled = codex_app_server::take_remote_control_disabled_env();
arg0_dispatch_or_else(move |arg0_paths: Arg0DispatchPaths| async move {
let AppServerArgs {
config_overrides,
code_mode_host,
listen,
session_source,
auth,
strict_config,
#[cfg(debug_assertions)]
disable_plugin_startup_tasks_for_tests,
remote_control,
psp,
} = 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 = listen;
let auth = auth.try_into_settings()?;
let mut runtime_options = AppServerRuntimeOptions {
code_mode_host_transport: code_mode_host.into(),
psp,
..Default::default()
};
#[cfg(debug_assertions)]
if disable_plugin_startup_tasks_for_tests {
runtime_options.plugin_startup_tasks = PluginStartupTasks::Skip;
}
runtime_options.remote_control_startup_mode =
match (remote_control, remote_control_disabled) {
(true, _) => codex_app_server::RemoteControlStartupMode::EnabledEphemeral,
(false, true) => codex_app_server::RemoteControlStartupMode::DisabledEphemeral,
(false, false) => codex_app_server::RemoteControlStartupMode::ResolvePersisted,
};
run_main_with_transport_options(
arg0_paths,
config_overrides,
loader_overrides,
strict_config,
/*default_analytics_enabled*/ false,
transport,
session_source,
auth,
runtime_options,
)
.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
}
#[cfg(test)]
#[path = "main_tests.rs"]
mod tests;