mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
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:
@@ -46,6 +46,7 @@ time = { workspace = true }
|
||||
tokio = { workspace = true, features = [
|
||||
"io-std",
|
||||
"macros",
|
||||
"process",
|
||||
"rt-multi-thread",
|
||||
] }
|
||||
tokio-tungstenite = { workspace = true }
|
||||
|
||||
@@ -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;
|
||||
@@ -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());
|
||||
}
|
||||
@@ -3,6 +3,7 @@ mod client_tracker;
|
||||
mod clients;
|
||||
mod desired_state;
|
||||
mod enroll;
|
||||
mod host_device;
|
||||
mod protocol;
|
||||
mod segment;
|
||||
mod server_api;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user