use super::RemoteControlEnableError;use super::RemoteControlHandle;use super::RemoteControlUnavailable;use super::enroll::update_persisted_remote_control_enrollment;use super::protocol::normalize_remote_control_url;use super::publish_current_enrollment;use super::websocket::RemoteControlStatusPublisher;use codex_app_server_protocol::RemoteControlStatusChangedNotification;use codex_state::RemoteControlEnrollmentRecord;use std::io;use tokio::sync::Semaphore;use tokio::sync::SemaphorePermit;#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub(super) enum RemoteControlDesiredState { // `Unknown` exists only on plain startup before auth and enrollment scope resolve. Persisted // `1` is `Enabled { persistence_preference: Some(true) }`; `0`, `NULL`, or no row are // `Disabled`. Runtime-only enable is `Enabled { persistence_preference: None }`, so new rows // keep `NULL`; durable RPC enable uses `Some(true)`, so new rows get `1`. Durable disable writes // `0` before entering `Disabled`; runtime-only disable does not write. `Disabled` carries no // preference because disabled sessions do not create enrollments. Unknown, Disabled, Enabled { persistence_preference: Option<bool>, },}impl RemoteControlDesiredState { pub(super) fn is_enabled(self) -> bool { matches!(self, Self::Enabled { .. }) }}pub(super) async fn acquire_persistence_lock(lock: &Semaphore) -> SemaphorePermit<'_> { lock.acquire().await.unwrap_or_else(|_| unreachable!())}pub(super) fn desired_state_from_persisted_enrollment( enrollment: Option<RemoteControlEnrollmentRecord>,) -> RemoteControlDesiredState { if enrollment.and_then(|enrollment| enrollment.remote_control_enabled) == Some(true) { RemoteControlDesiredState::Enabled { persistence_preference: Some(true), } } else { RemoteControlDesiredState::Disabled }}impl RemoteControlHandle { pub async fn resolve_persisted_preference( &self, app_server_client_name: Option<&str>, ) -> io::Result<bool> { if self.ensure_remote_control_allowed().is_err() { return Ok(false); } let _transition = self .desired_state_rpc_lock .acquire() .await .unwrap_or_else(|_| unreachable!()); if !matches!( *self.desired_state_tx.borrow(), RemoteControlDesiredState::Unknown ) { return Ok(self.desired_state_tx.borrow().is_enabled()); } let state_db = self .state_db .as_deref() .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, RemoteControlUnavailable))?; let auth = super::auth::load_remote_control_auth(&self.auth_manager).await?; let remote_control_target = normalize_remote_control_url(&self.remote_control_url)?; let app_server_client_name = self.pairing_persistence_key(app_server_client_name)?; let enrollment = state_db .get_remote_control_enrollment( &remote_control_target.websocket_url, &auth.account_id, app_server_client_name.as_deref(), ) .await .map_err(io::Error::other)?; let desired_state = desired_state_from_persisted_enrollment(enrollment); self.desired_state_tx.send_if_modified(|state| { if !matches!(*state, RemoteControlDesiredState::Unknown) { return false; } *state = desired_state; true }); Ok(self.desired_state_tx.borrow().is_enabled()) } pub async fn enable( &self, app_server_client_name: Option<&str>, ) -> io::Result<RemoteControlStatusChangedNotification> { self.ensure_remote_control_allowed() .map_err(|err| io::Error::new(io::ErrorKind::PermissionDenied, err))?; let _transition = self .desired_state_rpc_lock .acquire() .await .unwrap_or_else(|_| unreachable!()); let state_db = self .state_db .as_deref() .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, RemoteControlUnavailable))?; let mut auth = super::auth::load_remote_control_auth(&self.auth_manager).await?; let remote_control_target = normalize_remote_control_url(&self.remote_control_url)?; let app_server_client_name = self.pairing_persistence_key(app_server_client_name)?; let app_server_client_name = app_server_client_name.as_deref(); let status = self.status(); let mut current_enrollment = self.current_enrollment.lock().await; let (enrollment, _) = self .load_or_enroll_server( ¤t_enrollment, &mut auth, &status.installation_id, &status.server_name, app_server_client_name, super::RemoteControlEnrollmentSelection::ReuseOrCreate, ) .await?; let current_auth = super::auth::load_remote_control_auth(&self.auth_manager).await?; if current_auth.account_id != auth.account_id { return Err(io::Error::new( io::ErrorKind::Interrupted, "remote control account changed during enrollment", )); } let _persistence = acquire_persistence_lock(&self.desired_state_persistence_lock).await; let updated = state_db .set_remote_control_enabled( &remote_control_target.websocket_url, &auth.account_id, app_server_client_name, /*remote_control_enabled*/ true, ) .await .map_err(io::Error::other)?; if updated == 0 { update_persisted_remote_control_enrollment( Some(state_db), &remote_control_target, &auth.account_id, app_server_client_name, Some(&enrollment), Some(true), ) .await?; } publish_current_enrollment(&mut current_enrollment, &enrollment); self.enable_with_preference(Some(true)).map_err(|err| { let kind = match err { RemoteControlEnableError::Unavailable(_) => io::ErrorKind::NotFound, RemoteControlEnableError::DisabledByRequirements(_) => { io::ErrorKind::PermissionDenied } }; io::Error::new(kind, err) })?; RemoteControlStatusPublisher::new(self.status_tx.as_ref().clone()) .publish_environment_id(Some(enrollment.environment_id)); Ok(self.status()) }}