Files
codex/codex-rs/app-server/src/request_processors/environment_processor.rs
T
mattj-openai 54685110a7 Support request IDs in exec-server WebSocket handshakes (#52234)
## What changed

- Add optional `websocketRequestId` to `environment/add` and send it as `X-Request-ID` on direct WebSocket upgrades, including reconnects. It requires a secure transport or a loopback destination.
- Echo a supplied `X-Request-ID` in the exec-server WebSocket upgrade response.

## Testing

Add coverage for `websocketRequestId` serialization and for handshake header forwarding and echoing, both with and without a request ID.

GitOrigin-RevId: 235303ff7e1150405b7e1158d6024223144e0cc3
2026-10-08 19:13:52 +00:00

102 lines
3.6 KiB
Rust

use super::*;
use codex_config::ScopedSkillsConfig;
use codex_exec_server::RemoteEnvironmentOptions;
use std::time::Duration;
#[derive(Clone)]
pub(crate) struct EnvironmentRequestProcessor {
environment_manager: Arc<EnvironmentManager>,
}
impl EnvironmentRequestProcessor {
pub(crate) fn new(environment_manager: Arc<EnvironmentManager>) -> Self {
Self {
environment_manager,
}
}
pub(crate) async fn environment_add(
&self,
params: EnvironmentAddParams,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
let skills = ScopedSkillsConfig {
required: params
.skills
.and_then(|skills| skills.required)
.unwrap_or_default(),
};
let options = RemoteEnvironmentOptions {
exec_server_url: params.exec_server_url,
connect_timeout: params.connect_timeout_ms.map(Duration::from_millis),
http_headers: params
.auth_bearer_token
.into_iter()
.map(|token| {
(
"Authorization".to_string(),
format!("Bearer {}", token.into_inner()),
)
})
.chain(
params
.websocket_request_id
.map(|request_id| ("X-Request-ID".to_string(), request_id)),
)
.collect(),
};
self.environment_manager
.upsert_environment_with_options(params.environment_id, options, skills)
.map_err(|err| invalid_request(err.to_string()))?;
Ok(Some(EnvironmentAddResponse {}.into()))
}
pub(crate) async fn environment_info(
&self,
params: EnvironmentInfoParams,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
let environment_id = params.environment_id;
let environment = self
.environment_manager
.get_environment(&environment_id)
.ok_or_else(|| invalid_request(format!("unknown environment id `{environment_id}`")))?;
let info = environment.force_info().await.map_err(|err| {
internal_error(format!(
"failed to get info for environment `{environment_id}`: {err}"
))
})?;
Ok(Some(
EnvironmentInfoResponse {
shell: EnvironmentShellInfo {
name: info.shell.name,
path: info.shell.path,
},
cwd: info.cwd,
}
.into(),
))
}
pub(crate) async fn environment_status(
&self,
params: EnvironmentStatusParams,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
let environment_id = params.environment_id;
let (status, error) = match self
.environment_manager
.get_environment_status(&environment_id)
.await
{
Some(EnvironmentObservedStatus::Ready) => (EnvironmentStatusKind::Ready, None),
Some(EnvironmentObservedStatus::Pending) => (EnvironmentStatusKind::Pending, None),
Some(EnvironmentObservedStatus::Disconnected { error }) => {
(EnvironmentStatusKind::Disconnected, Some(error))
}
None => (
EnvironmentStatusKind::Unknown,
Some(format!("unknown environment id `{environment_id}`")),
),
};
Ok(Some(EnvironmentStatusResponse { status, error }.into()))
}
}