mirror of
https://github.com/openai/codex.git
synced 2026-10-27 18:48:37 +00:00
Support the app-server daemon on Windows (#42405)
## Why The managed app-server daemon and automatic TUI attachment were limited to Unix, so Windows users could not share a background server across Codex sessions. ## What changed - Enable daemon lifecycle commands, `codex agents` startup, and AF_UNIX daemon discovery on Windows. - Require Windows daemons to start non-elevated and verify the discovered socket directory and peer before connecting. - Preflight detached process support, preserve path-based environment settings, and keep daemon processes out of the launching project's working directory. - Fall back to an embedded server when an implicitly discovered daemon cannot connect, while preserving errors for explicit remote endpoints and bypassing daemon reuse when `CODEX_EXEC_SERVER_URL` is set. ## Testing Add coverage for Windows launch restrictions and socket validation, implicit connection fallback, executor selection, daemon discovery, and the agents dashboard startup result. GitOrigin-RevId: e7fd63cf8690bcca8b5155266416f31f2f792aa2
This commit is contained in:
@@ -169,8 +169,34 @@ pub struct RemoteAppServerRequestHandle {
|
||||
command_tx: mpsc::Sender<RemoteClientCommand>,
|
||||
}
|
||||
|
||||
enum SocketPeerPolicy {
|
||||
ExplicitEndpoint,
|
||||
#[cfg(windows)]
|
||||
NonElevatedCurrentUser,
|
||||
}
|
||||
|
||||
impl RemoteAppServerClient {
|
||||
pub async fn connect(args: RemoteAppServerConnectArgs) -> IoResult<Self> {
|
||||
Self::connect_with_policy(args, SocketPeerPolicy::ExplicitEndpoint).await
|
||||
}
|
||||
|
||||
/// Connects to an implicitly discovered Windows daemon, verifying its peer
|
||||
/// token before the WebSocket handshake or any session requests.
|
||||
#[cfg(windows)]
|
||||
pub async fn connect_local_daemon(args: RemoteAppServerConnectArgs) -> IoResult<Self> {
|
||||
if !matches!(args.endpoint, RemoteAppServerEndpoint::UnixSocket { .. }) {
|
||||
return Err(IoError::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"local daemon requires a Unix socket",
|
||||
));
|
||||
}
|
||||
Self::connect_with_policy(args, SocketPeerPolicy::NonElevatedCurrentUser).await
|
||||
}
|
||||
|
||||
async fn connect_with_policy(
|
||||
args: RemoteAppServerConnectArgs,
|
||||
peer_policy: SocketPeerPolicy,
|
||||
) -> IoResult<Self> {
|
||||
let channel_capacity = args.channel_capacity.max(1);
|
||||
let initialize_params = args.initialize_params();
|
||||
match args.endpoint {
|
||||
@@ -184,7 +210,8 @@ impl RemoteAppServerClient {
|
||||
.await
|
||||
}
|
||||
RemoteAppServerEndpoint::UnixSocket { socket_path } => {
|
||||
let (endpoint, stream) = connect_unix_socket_endpoint(socket_path).await?;
|
||||
let (endpoint, stream) =
|
||||
connect_unix_socket_endpoint(socket_path, peer_policy).await?;
|
||||
Self::connect_with_stream(channel_capacity, endpoint, stream, initialize_params)
|
||||
.await
|
||||
}
|
||||
@@ -757,6 +784,7 @@ async fn connect_websocket_endpoint(
|
||||
|
||||
async fn connect_unix_socket_endpoint(
|
||||
socket_path: AbsolutePathBuf,
|
||||
peer_policy: SocketPeerPolicy,
|
||||
) -> IoResult<(String, WebSocketStream<UnixStream>)> {
|
||||
let endpoint = format!("unix://{}", socket_path.display());
|
||||
let request = UDS_WEBSOCKET_HANDSHAKE_URL
|
||||
@@ -780,6 +808,11 @@ async fn connect_unix_socket_endpoint(
|
||||
"failed to connect to remote app server at `{endpoint}`: {err}"
|
||||
))
|
||||
})?;
|
||||
match peer_policy {
|
||||
SocketPeerPolicy::ExplicitEndpoint => {}
|
||||
#[cfg(windows)]
|
||||
SocketPeerPolicy::NonElevatedCurrentUser => stream.ensure_non_elevated_peer()?,
|
||||
}
|
||||
let websocket_config = remote_websocket_config();
|
||||
let stream = timeout(
|
||||
CONNECT_TIMEOUT,
|
||||
|
||||
Reference in New Issue
Block a user