fix(app-server-transport): clear stale pairing client

This commit is contained in:
Anton Panasenko
2026-05-29 03:03:24 -07:00
parent 19dd22aebf
commit 413ae91e53
3 changed files with 53 additions and 6 deletions

View File

@@ -114,6 +114,7 @@ impl RemoteControlHandle {
*state = false;
changed
});
clear_pairing_client(&self.pairing_client);
let status = self.status();
info!(
@@ -211,6 +212,12 @@ fn remote_control_status_with_connection_status(
}
}
fn clear_pairing_client(pairing_client: &Arc<StdMutex<Option<RemoteControlPairingClient>>>) {
*pairing_client
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = None;
}
pub async fn start_remote_control(
config: RemoteControlStartConfig,
state_db: Option<Arc<StateRuntime>>,

View File

@@ -20,6 +20,7 @@ use codex_app_server_protocol::AuthMode;
use codex_app_server_protocol::ConfigWarningNotification;
use codex_app_server_protocol::JSONRPCMessage;
use codex_app_server_protocol::RemoteControlConnectionStatus;
use codex_app_server_protocol::RemoteControlPairingStartParams;
use codex_app_server_protocol::RemoteControlStatusChangedNotification;
use codex_app_server_protocol::ServerNotification;
use codex_config::types::AuthCredentialsStoreMode;
@@ -39,7 +40,9 @@ use pretty_assertions::assert_eq;
use serde_json::json;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex as StdMutex;
use tempfile::TempDir;
use time::OffsetDateTime;
use tokio::io::AsyncBufReadExt;
use tokio::io::AsyncReadExt;
use tokio::io::AsyncWriteExt;
@@ -895,6 +898,48 @@ async fn remote_control_handle_enable_disable_stops_and_restarts_connections() {
let _ = remote_task.await;
}
#[tokio::test]
async fn remote_control_handle_disable_clears_stale_pairing_client() {
let (enabled_tx, _enabled_rx) = watch::channel(/*init*/ true);
let (status_tx, _status_rx) = watch::channel(RemoteControlStatusChangedNotification {
status: RemoteControlConnectionStatus::Connected,
server_name: test_server_name(),
installation_id: TEST_INSTALLATION_ID.to_string(),
environment_id: Some("env_test".to_string()),
});
let pairing_client = Arc::new(StdMutex::new(Some(RemoteControlPairingClient::new(
&normalize_remote_control_url("http://127.0.0.1:1/backend-api/wham/remote/control")
.expect("remote control target should normalize"),
TEST_REMOTE_CONTROL_SERVER_TOKEN.to_string(),
OffsetDateTime::from_unix_timestamp(33_336_362_096).expect("future timestamp should parse"),
))));
let remote_handle = RemoteControlHandle {
enabled_tx: Arc::new(enabled_tx),
status_tx: Arc::new(status_tx),
state_db_available: true,
pairing_client,
};
assert_eq!(
remote_handle.disable(),
RemoteControlStatusChangedNotification {
status: RemoteControlConnectionStatus::Disabled,
server_name: test_server_name(),
installation_id: TEST_INSTALLATION_ID.to_string(),
environment_id: None,
}
);
remote_handle.enable().expect("enable should succeed");
assert_eq!(
remote_handle
.start_pairing(RemoteControlPairingStartParams { manual_code: false })
.await
.expect_err("re-enabled remote control should wait for refreshed pairing auth")
.to_string(),
"remote control pairing is unavailable until enrollment completes"
);
}
#[tokio::test]
async fn remote_control_transport_clears_outgoing_buffer_when_backend_acks() {
let listener = TcpListener::bind("127.0.0.1:0")

View File

@@ -11,6 +11,7 @@ use crate::transport::remote_control::enroll::refresh_remote_control_server;
use crate::transport::remote_control::enroll::update_persisted_remote_control_enrollment;
use crate::transport::remote_control::pairing::RemoteControlPairingClient;
use super::clear_pairing_client;
use super::protocol::ClientEnvelope;
use super::protocol::ClientEvent;
use super::protocol::ClientId;
@@ -1515,12 +1516,6 @@ fn set_pairing_client(
Ok(())
}
fn clear_pairing_client(pairing_client: &Arc<StdMutex<Option<RemoteControlPairingClient>>>) {
*pairing_client
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = None;
}
async fn enroll_remote_control_server_if_missing(
remote_control_target: &RemoteControlTarget,
state_db: &StateRuntime,