Skip to main content

codex_exec_server/
client_api.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3use std::sync::Arc;
4use std::time::Duration;
5
6use futures::future::BoxFuture;
7use futures::future::Shared;
8use tokio::sync::oneshot;
9
10use crate::ExecServerError;
11use crate::HttpRequestParams;
12use crate::HttpRequestResponse;
13use crate::HttpResponseBodyStream;
14use crate::NoiseChannelIdentity;
15use crate::NoiseChannelPublicKey;
16
17pub(crate) const DEFAULT_REMOTE_EXEC_SERVER_CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
18pub(crate) const DEFAULT_REMOTE_EXEC_SERVER_INITIALIZE_TIMEOUT: Duration = Duration::from_secs(10);
19
20/// Connection options for any exec-server client transport.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ExecServerClientConnectOptions {
23    pub client_name: String,
24    pub initialize_timeout: Duration,
25    pub resume_session_id: Option<String>,
26}
27
28/// WebSocket connection arguments for a remote exec-server.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct RemoteExecServerConnectArgs {
31    pub websocket_url: String,
32    pub client_name: String,
33    pub connect_timeout: Duration,
34    pub initialize_timeout: Duration,
35    pub resume_session_id: Option<String>,
36}
37
38/// Registry-authorized material for one Noise rendezvous connection attempt.
39///
40/// Treat this as an atomic, single-use bundle. The URL authorization, executor
41/// registration, pinned executor key, and harness-key authorization describe one
42/// physical connection attempt and must not be mixed with values from another
43/// registry response.
44pub struct NoiseRendezvousConnectBundle {
45    pub websocket_url: String,
46    pub environment_id: String,
47    pub executor_registration_id: String,
48    pub executor_public_key: NoiseChannelPublicKey,
49    pub harness_key_authorization: String,
50}
51
52/// Connection arguments for an authenticated Noise rendezvous exec-server.
53///
54/// `harness_identity` identifies the logical harness endpoint and may be reused
55/// across reconnects. In contrast, callers must supply a fresh
56/// [`NoiseRendezvousConnectBundle`] for each physical connection attempt.
57pub struct NoiseRendezvousConnectArgs {
58    pub bundle: NoiseRendezvousConnectBundle,
59    pub harness_identity: NoiseChannelIdentity,
60    pub client_name: String,
61    pub connect_timeout: Duration,
62    pub initialize_timeout: Duration,
63    pub resume_session_id: Option<String>,
64}
65
66/// Supplies fresh registry-authorized material for Noise rendezvous connections.
67pub trait NoiseRendezvousConnectProvider: Send + Sync {
68    /// Fetch a bundle authorizing this harness key for one physical connection.
69    fn connect_bundle(
70        &self,
71        harness_public_key: NoiseChannelPublicKey,
72    ) -> BoxFuture<'_, Result<NoiseRendezvousConnectBundle, ExecServerError>>;
73}
74
75/// Stdio connection arguments for a command-backed exec-server.
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub(crate) struct StdioExecServerConnectArgs {
78    pub command: StdioExecServerCommand,
79    pub client_name: String,
80    pub initialize_timeout: Duration,
81    pub resume_session_id: Option<String>,
82}
83
84/// Structured process command used to start an exec-server over stdio.
85#[derive(Debug, Clone, PartialEq, Eq)]
86pub(crate) struct StdioExecServerCommand {
87    pub program: String,
88    pub args: Vec<String>,
89    pub env: HashMap<String, String>,
90    pub cwd: Option<PathBuf>,
91}
92
93pub(crate) type DeferredEnvironmentReadiness = Shared<oneshot::Receiver<Result<(), String>>>;
94
95#[derive(Clone)]
96pub(crate) struct Deferred<T> {
97    pub readiness: DeferredEnvironmentReadiness,
98    pub transport: T,
99}
100
101/// Parameters used to connect to a remote exec-server environment.
102#[derive(Clone)]
103pub(crate) enum ExecServerTransportParams {
104    Deferred(Box<Deferred<ExecServerTransportParams>>),
105    WebSocketUrl {
106        websocket_url: String,
107        connect_timeout: Duration,
108        initialize_timeout: Duration,
109    },
110    NoiseRendezvous {
111        provider: Arc<dyn NoiseRendezvousConnectProvider>,
112        identity: NoiseChannelIdentity,
113    },
114    #[allow(dead_code)]
115    StdioCommand {
116        command: StdioExecServerCommand,
117        initialize_timeout: Duration,
118    },
119}
120
121impl std::fmt::Debug for ExecServerTransportParams {
122    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123        match self {
124            Self::Deferred(deferred) => f
125                .debug_struct("Deferred")
126                .field("transport", &deferred.transport)
127                .finish_non_exhaustive(),
128            Self::WebSocketUrl {
129                websocket_url,
130                connect_timeout,
131                initialize_timeout,
132            } => f
133                .debug_struct("WebSocketUrl")
134                .field("websocket_url", websocket_url)
135                .field("connect_timeout", connect_timeout)
136                .field("initialize_timeout", initialize_timeout)
137                .finish(),
138            Self::NoiseRendezvous { .. } => {
139                f.debug_struct("NoiseRendezvous").finish_non_exhaustive()
140            }
141            Self::StdioCommand {
142                command,
143                initialize_timeout,
144            } => f
145                .debug_struct("StdioCommand")
146                .field("command", command)
147                .field("initialize_timeout", initialize_timeout)
148                .finish(),
149        }
150    }
151}
152
153impl ExecServerTransportParams {
154    pub(crate) fn websocket_url(websocket_url: String, connect_timeout: Duration) -> Self {
155        Self::WebSocketUrl {
156            websocket_url,
157            connect_timeout,
158            initialize_timeout: DEFAULT_REMOTE_EXEC_SERVER_INITIALIZE_TIMEOUT,
159        }
160    }
161}
162
163/// Sends HTTP requests through a runtime-selected transport.
164///
165/// This is the HTTP capability counterpart to [`crate::ExecBackend`]. Callers
166/// use it when they need environment-owned network requests but should not
167/// depend on the concrete connection type or how that connection is established.
168pub trait HttpClient: Send + Sync {
169    /// Perform an HTTP request and buffer the response body.
170    fn http_request(
171        &self,
172        params: HttpRequestParams,
173    ) -> BoxFuture<'_, Result<HttpRequestResponse, ExecServerError>>;
174
175    /// Perform an HTTP request and return a streamed body handle.
176    fn http_request_stream(
177        &self,
178        params: HttpRequestParams,
179    ) -> BoxFuture<'_, Result<(HttpRequestResponse, HttpResponseBodyStream), ExecServerError>>;
180}