Identify Mac mini hosts in remote control handshakes (#38840)

## What changed

- On macOS, inspect the hardware profile before opening a remote-control WebSocket and send `x-codex-host-device-kind: mac_mini` when the machine name is exactly `Mac mini`.
- Cache successful detection results, bound the profile lookup to two seconds, and omit the header on other platforms or when detection fails.

## Testing

- Add parser coverage for Mac mini, other machine names, empty profiles, and malformed profile data.

GitOrigin-RevId: d2a4589bec0c879957f14ab4a195fd54747cc122
This commit is contained in:
Tin Kadoic
2026-08-16 06:26:34 +00:00
committed by copyberry
parent 9bfaf7a076
commit 9ded177ce7
5 changed files with 126 additions and 2 deletions

View File

@@ -46,6 +46,7 @@ time = { workspace = true }
tokio = { workspace = true, features = [
"io-std",
"macros",
"process",
"rt-multi-thread",
] }
tokio-tungstenite = { workspace = true }

View File

@@ -0,0 +1,74 @@
#[cfg(any(target_os = "macos", test))]
use serde::Deserialize;
pub(super) const REMOTE_CONTROL_HOST_DEVICE_KIND_HEADER: &str = "x-codex-host-device-kind";
#[cfg(any(target_os = "macos", test))]
const MAC_MINI_HOST_DEVICE_KIND: &str = "mac_mini";
#[cfg(any(target_os = "macos", test))]
#[derive(Deserialize)]
struct MacHardwareProfile {
#[serde(rename = "SPHardwareDataType")]
hardware: Vec<MacHardware>,
}
#[cfg(any(target_os = "macos", test))]
#[derive(Deserialize)]
struct MacHardware {
machine_name: String,
}
#[cfg(any(target_os = "macos", test))]
fn host_device_kind_from_profile(profile: &[u8]) -> serde_json::Result<Option<&'static str>> {
let profile: MacHardwareProfile = serde_json::from_slice(profile)?;
Ok(profile
.hardware
.first()
.is_some_and(|hardware| hardware.machine_name == "Mac mini")
.then_some(MAC_MINI_HOST_DEVICE_KIND))
}
#[cfg(target_os = "macos")]
pub(super) async fn host_device_kind() -> Option<&'static str> {
use std::process::Stdio;
use std::time::Duration;
use tokio::process::Command;
use tokio::sync::OnceCell;
static HOST_DEVICE_KIND: OnceCell<Option<&'static str>> = OnceCell::const_new();
HOST_DEVICE_KIND
.get_or_try_init(|| async {
let output = tokio::time::timeout(
Duration::from_secs(2),
Command::new("/usr/sbin/system_profiler")
.args(["-detailLevel", "mini", "SPHardwareDataType", "-json"])
.stdin(Stdio::null())
.stderr(Stdio::null())
.kill_on_drop(true)
.output(),
)
.await
.map_err(|_| ())?
.map_err(|_| ())?;
if !output.status.success() {
return Err(());
}
host_device_kind_from_profile(&output.stdout).map_err(|_| ())
})
.await
.ok()
.copied()
.flatten()
}
#[cfg(not(target_os = "macos"))]
pub(super) async fn host_device_kind() -> Option<&'static str> {
None
}
#[cfg(test)]
#[path = "host_device_tests.rs"]
mod tests;

View File

@@ -0,0 +1,38 @@
use super::host_device_kind_from_profile;
use pretty_assertions::assert_eq;
#[test]
fn recognizes_only_the_exact_mac_mini_hardware_name() {
let mac_mini_profile = br#"{"SPHardwareDataType":[{"machine_name":"Mac mini"}]}"#;
let macbook_profile = br#"{"SPHardwareDataType":[{"machine_name":"MacBook Pro"}]}"#;
let misleading_profile = br#"{"SPHardwareDataType":[{"machine_name":"Not a Mac mini"}]}"#;
assert_eq!(
host_device_kind_from_profile(mac_mini_profile).expect("valid Mac mini hardware profile"),
Some("mac_mini")
);
assert_eq!(
host_device_kind_from_profile(macbook_profile).expect("valid MacBook hardware profile"),
None
);
assert_eq!(
host_device_kind_from_profile(misleading_profile)
.expect("valid non-Mac-mini hardware profile"),
None
);
}
#[test]
fn ignores_missing_hardware_profiles() {
assert_eq!(
host_device_kind_from_profile(br#"{"SPHardwareDataType":[]}"#)
.expect("valid empty hardware profile"),
None
);
}
#[test]
fn rejects_malformed_hardware_profiles_so_they_remain_retryable() {
assert!(host_device_kind_from_profile(br#"{"machine_name":"Mac mini"}"#).is_err());
assert!(host_device_kind_from_profile(b"not json").is_err());
}

View File

@@ -3,6 +3,7 @@ mod client_tracker;
mod clients;
mod desired_state;
mod enroll;
mod host_device;
mod protocol;
mod segment;
mod server_api;

View File

@@ -27,6 +27,8 @@ use crate::transport::remote_control::enroll::format_headers;
use crate::transport::remote_control::enroll::load_persisted_remote_control_enrollment;
use crate::transport::remote_control::enroll::preview_remote_control_response_body;
use crate::transport::remote_control::enroll::update_persisted_remote_control_enrollment;
use crate::transport::remote_control::host_device::REMOTE_CONTROL_HOST_DEVICE_KIND_HEADER;
use crate::transport::remote_control::host_device::host_device_kind;
use crate::transport::remote_control::server_api::enroll_remote_control_server;
use crate::transport::remote_control::server_api::refresh_remote_control_server;
use axum::http::HeaderValue;
@@ -1250,7 +1252,7 @@ fn set_remote_control_header(
Ok(())
}
fn build_remote_control_websocket_request(
async fn build_remote_control_websocket_request(
websocket_url: &str,
enrollment: &RemoteControlEnrollment,
installation_id: &str,
@@ -1290,6 +1292,13 @@ fn build_remote_control_websocket_request(
REMOTE_CONTROL_INSTALLATION_ID_HEADER,
installation_id,
)?;
if let Some(host_device_kind) = host_device_kind().await {
set_remote_control_header(
headers,
REMOTE_CONTROL_HOST_DEVICE_KIND_HEADER,
host_device_kind,
)?;
}
if let Some(subscribe_cursor) = subscribe_cursor {
set_remote_control_header(
headers,
@@ -1345,7 +1354,8 @@ pub(super) async fn connect_remote_control_websocket(
&enrollment,
connect_options.installation_id,
connect_options.subscribe_cursor,
)?;
)
.await?;
let websocket_connect_result = tokio::time::timeout(
REMOTE_CONTROL_WEBSOCKET_CONNECT_TIMEOUT,