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(¶ms)?; 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 (¶ms.pairing_code, ¶ms.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;