Skip to main content

codex/commands/
proxy.rs

1use tokio::{io::AsyncWriteExt, process::Command};
2
3use crate::{
4    process::spawn_with_retry, CodexClient, CodexError, ResponsesApiProxyHandle,
5    ResponsesApiProxyRequest,
6};
7
8impl CodexClient {
9    /// Starts the `codex responses-api-proxy` helper with a supplied API key.
10    ///
11    /// Forwards optional `--port`, `--server-info`, `--http-shutdown`, and `--upstream-url` flags.
12    /// The API key is written to stdin immediately after spawn, stdout/stderr remain piped for callers
13    /// to drain, and the returned handle owns the child process plus any `--server-info` path used.
14    pub async fn start_responses_api_proxy(
15        &self,
16        request: ResponsesApiProxyRequest,
17    ) -> Result<ResponsesApiProxyHandle, CodexError> {
18        let ResponsesApiProxyRequest {
19            api_key,
20            port,
21            server_info_path,
22            http_shutdown,
23            upstream_url,
24        } = request;
25
26        let api_key = api_key.trim().to_string();
27        if api_key.is_empty() {
28            return Err(CodexError::EmptyApiKey);
29        }
30
31        let working_dir = self.sandbox_working_dir(None)?;
32
33        let mut command = Command::new(self.command_env.binary_path());
34        command
35            .arg("responses-api-proxy")
36            .stdin(std::process::Stdio::piped())
37            .stdout(std::process::Stdio::piped())
38            .stderr(std::process::Stdio::piped())
39            .kill_on_drop(true)
40            .current_dir(&working_dir);
41
42        if let Some(port) = port {
43            command.arg("--port").arg(port.to_string());
44        }
45
46        if let Some(path) = server_info_path.as_ref() {
47            command.arg("--server-info").arg(path);
48        }
49
50        if http_shutdown {
51            command.arg("--http-shutdown");
52        }
53
54        if let Some(url) = upstream_url.as_ref() {
55            if !url.trim().is_empty() {
56                command.arg("--upstream-url").arg(url);
57            }
58        }
59
60        self.command_env.apply(&mut command)?;
61
62        let mut child = spawn_with_retry(&mut command, self.command_env.binary_path())?;
63
64        let mut stdin = child.stdin.take().ok_or(CodexError::StdinUnavailable)?;
65        stdin
66            .write_all(api_key.as_bytes())
67            .await
68            .map_err(CodexError::StdinWrite)?;
69        stdin
70            .write_all(b"\n")
71            .await
72            .map_err(CodexError::StdinWrite)?;
73        stdin.shutdown().await.map_err(CodexError::StdinWrite)?;
74
75        Ok(ResponsesApiProxyHandle {
76            child,
77            server_info_path,
78        })
79    }
80}