Codex Handbook
app-server/src/request_processors/remote_control_processor.rs 186 lines
use crate::error_code::internal_error;use crate::error_code::invalid_request;use crate::transport::RemoteControlEnableError;use crate::transport::RemoteControlHandle;use crate::transport::RemoteControlUnavailable;use codex_app_server_protocol::JSONRPCErrorError;use codex_app_server_protocol::RemoteControlClientsListParams;use codex_app_server_protocol::RemoteControlClientsListResponse;use codex_app_server_protocol::RemoteControlClientsRevokeParams;use codex_app_server_protocol::RemoteControlClientsRevokeResponse;use codex_app_server_protocol::RemoteControlDisableResponse;use codex_app_server_protocol::RemoteControlEnableResponse;use codex_app_server_protocol::RemoteControlPairingStartParams;use codex_app_server_protocol::RemoteControlPairingStartResponse;use codex_app_server_protocol::RemoteControlPairingStatusParams;use codex_app_server_protocol::RemoteControlPairingStatusResponse;use codex_app_server_protocol::RemoteControlStatusReadResponse;use std::io;#[derive(Clone)]pub(crate) struct RemoteControlRequestProcessor {    remote_control_handle: Option<RemoteControlHandle>,}impl RemoteControlRequestProcessor {    pub(crate) fn new(remote_control_handle: Option<RemoteControlHandle>) -> Self {        Self {            remote_control_handle,        }    }    pub(crate) async fn enable(        &self,        ephemeral: bool,        app_server_client_name: Option<&str>,    ) -> Result<RemoteControlEnableResponse, JSONRPCErrorError> {        let handle = self.handle()?;        let status = if ephemeral {            handle.enable_ephemeral().map_err(map_enable_error)?        } else {            handle                .enable(app_server_client_name)                .await                .map_err(map_update_error)?        };        Ok(RemoteControlEnableResponse::from(status))    }    pub(crate) async fn disable(        &self,        ephemeral: bool,        app_server_client_name: Option<&str>,    ) -> Result<RemoteControlDisableResponse, JSONRPCErrorError> {        let handle = self.handle()?;        let status = if ephemeral {            handle.disable_ephemeral().await        } else {            handle                .disable(app_server_client_name)                .await                .map_err(map_update_error)?        };        Ok(RemoteControlDisableResponse::from(status))    }    pub(crate) fn status_read(&self) -> Result<RemoteControlStatusReadResponse, JSONRPCErrorError> {        let status = self.handle()?.status();        Ok(RemoteControlStatusReadResponse {            status: status.status,            server_name: status.server_name,            installation_id: status.installation_id,            environment_id: status.environment_id,        })    }    pub(crate) async fn pairing_start(        &self,        params: RemoteControlPairingStartParams,        app_server_client_name: Option<&str>,    ) -> Result<RemoteControlPairingStartResponse, JSONRPCErrorError> {        self.handle()?            .start_pairing(params, app_server_client_name)            .await            .map_err(map_pairing_start_error)    }    pub(crate) async fn pairing_status(        &self,        params: RemoteControlPairingStatusParams,    ) -> Result<RemoteControlPairingStatusResponse, JSONRPCErrorError> {        validate_pairing_status_params(&params)?;        let handle = self.handle()?;        handle            .pairing_status(params)            .await            .map_err(map_pairing_start_error)    }    pub(crate) async fn clients_list(        &self,        params: RemoteControlClientsListParams,    ) -> Result<RemoteControlClientsListResponse, JSONRPCErrorError> {        self.handle()?            .list_clients(params)            .await            .map_err(map_client_management_error)    }    pub(crate) async fn clients_revoke(        &self,        params: RemoteControlClientsRevokeParams,    ) -> Result<RemoteControlClientsRevokeResponse, JSONRPCErrorError> {        self.handle()?            .revoke_client(params)            .await            .map_err(map_client_management_error)    }    fn handle(&self) -> Result<&RemoteControlHandle, JSONRPCErrorError> {        let handle = self            .remote_control_handle            .as_ref()            .ok_or_else(|| internal_error("remote control is unavailable for this app-server"))?;        handle            .ensure_remote_control_allowed()            .map_err(|err| invalid_request(err.to_string()))?;        Ok(handle)    }}fn map_enable_error(err: RemoteControlEnableError) -> JSONRPCErrorError {    match err {        RemoteControlEnableError::Unavailable(err) => map_unavailable(err),        RemoteControlEnableError::DisabledByRequirements(err) => invalid_request(err.to_string()),    }}fn map_unavailable(err: RemoteControlUnavailable) -> JSONRPCErrorError {    invalid_request(err.to_string())}fn map_update_error(err: io::Error) -> JSONRPCErrorError {    if matches!(        err.kind(),        io::ErrorKind::NotFound | io::ErrorKind::PermissionDenied    ) {        invalid_request(err.to_string())    } else {        internal_error(err.to_string())    }}fn map_pairing_start_error(err: io::Error) -> JSONRPCErrorError {    if err.kind() == io::ErrorKind::InvalidInput {        invalid_request(err.to_string())    } else {        internal_error(err.to_string())    }}fn validate_pairing_status_params(    params: &RemoteControlPairingStatusParams,) -> Result<(), JSONRPCErrorError> {    match (&params.pairing_code, &params.manual_pairing_code) {        (Some(_), None) | (None, Some(_)) => Ok(()),        (Some(_), Some(_)) => Err(invalid_request(            "remoteControl/pairing/status accepts either pairingCode or manualPairingCode, not both",        )),        (None, None) => Err(invalid_request(            "remoteControl/pairing/status requires pairingCode or manualPairingCode",        )),    }}fn map_client_management_error(err: io::Error) -> JSONRPCErrorError {    match err.kind() {        io::ErrorKind::InvalidInput        | io::ErrorKind::NotFound        | io::ErrorKind::PermissionDenied        | io::ErrorKind::WouldBlock => invalid_request(err.to_string()),        _ => internal_error(err.to_string()),    }}#[cfg(test)]mod remote_control_processor_tests;