codex/commands/
apply_diff.rs1use std::{env, ffi::OsString};
2
3use tokio::{process::Command, time};
4
5use crate::{
6 builder::{apply_cli_overrides, resolve_cli_overrides},
7 process::{spawn_with_retry, tee_stream, ConsoleTarget},
8 ApplyDiffArtifacts, CliOverridesPatch, CodexClient, CodexError,
9};
10
11impl CodexClient {
12 pub async fn apply(&self) -> Result<ApplyDiffArtifacts, CodexError> {
22 let task_id = env::var_os("CODEX_TASK_ID")
23 .and_then(|v| crate::normalize_non_empty(&v.to_string_lossy()).map(OsString::from));
24 self.apply_task_inner(task_id).await
25 }
26
27 pub async fn apply_task(
29 &self,
30 task_id: impl AsRef<str>,
31 ) -> Result<ApplyDiffArtifacts, CodexError> {
32 let task_id = task_id.as_ref().trim();
33 if task_id.is_empty() {
34 return Err(CodexError::EmptyTaskId);
35 }
36 self.apply_task_inner(Some(OsString::from(task_id))).await
37 }
38
39 pub async fn diff(&self) -> Result<ApplyDiffArtifacts, CodexError> {
49 let task_id = env::var_os("CODEX_TASK_ID")
50 .and_then(|v| crate::normalize_non_empty(&v.to_string_lossy()).map(OsString::from));
51 self.cloud_diff_task_inner(task_id).await
52 }
53
54 pub async fn cloud_diff_task(
56 &self,
57 task_id: impl AsRef<str>,
58 ) -> Result<ApplyDiffArtifacts, CodexError> {
59 let task_id = task_id.as_ref().trim();
60 if task_id.is_empty() {
61 return Err(CodexError::EmptyTaskId);
62 }
63 self.cloud_diff_task_inner(Some(OsString::from(task_id)))
64 .await
65 }
66
67 async fn apply_task_inner(
68 &self,
69 task_id: Option<OsString>,
70 ) -> Result<ApplyDiffArtifacts, CodexError> {
71 let mut args = vec![OsString::from("apply")];
72 if let Some(task_id) = task_id {
73 args.push(task_id);
74 }
75 self.capture_codex_command(args, false).await
76 }
77
78 async fn cloud_diff_task_inner(
79 &self,
80 task_id: Option<OsString>,
81 ) -> Result<ApplyDiffArtifacts, CodexError> {
82 let mut args = vec![OsString::from("cloud"), OsString::from("diff")];
83 if let Some(task_id) = task_id {
84 args.push(task_id);
85 }
86 self.capture_codex_command(args, false).await
87 }
88
89 async fn capture_codex_command(
90 &self,
91 args: Vec<OsString>,
92 include_search: bool,
93 ) -> Result<ApplyDiffArtifacts, CodexError> {
94 let dir_ctx = self.directory_context()?;
95 let resolved_overrides = resolve_cli_overrides(
96 &self.cli_overrides,
97 &CliOverridesPatch::default(),
98 self.model.as_deref(),
99 );
100
101 let mut command = Command::new(self.command_env.binary_path());
102 command
103 .stdout(std::process::Stdio::piped())
104 .stderr(std::process::Stdio::piped())
105 .kill_on_drop(true)
106 .current_dir(dir_ctx.path());
107
108 apply_cli_overrides(&mut command, &resolved_overrides, include_search);
109 command.args(&args);
110 self.command_env.apply(&mut command)?;
111
112 let mut child = spawn_with_retry(&mut command, self.command_env.binary_path())?;
113
114 let stdout = child.stdout.take().ok_or(CodexError::StdoutUnavailable)?;
115 let stderr = child.stderr.take().ok_or(CodexError::StderrUnavailable)?;
116
117 let stdout_task = tokio::spawn(tee_stream(
118 stdout,
119 ConsoleTarget::Stdout,
120 self.mirror_stdout,
121 ));
122 let stderr_task = tokio::spawn(tee_stream(stderr, ConsoleTarget::Stderr, !self.quiet));
123
124 let wait_task = async move {
125 let status = child
126 .wait()
127 .await
128 .map_err(|source| CodexError::Wait { source })?;
129 let stdout_bytes = stdout_task
130 .await
131 .map_err(CodexError::Join)?
132 .map_err(CodexError::CaptureIo)?;
133 let stderr_bytes = stderr_task
134 .await
135 .map_err(CodexError::Join)?
136 .map_err(CodexError::CaptureIo)?;
137 Ok::<_, CodexError>((status, stdout_bytes, stderr_bytes))
138 };
139
140 let (status, stdout_bytes, stderr_bytes) = if self.timeout.is_zero() {
141 wait_task.await?
142 } else {
143 match time::timeout(self.timeout, wait_task).await {
144 Ok(result) => result?,
145 Err(_) => {
146 return Err(CodexError::Timeout {
147 timeout: self.timeout,
148 });
149 }
150 }
151 };
152
153 Ok(ApplyDiffArtifacts {
154 status,
155 stdout: String::from_utf8(stdout_bytes)?,
156 stderr: String::from_utf8(stderr_bytes)?,
157 })
158 }
159}