Skip to content

Commit 00e9504

Browse files
committed
fix(codex-rs): restore additional missing upstream source modules
1 parent f067ce4 commit 00e9504

14 files changed

Lines changed: 1344 additions & 0 deletions

File tree

codex-rs/chatgpt/src/get_task.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use codex_core::config::Config;
2+
use serde::Deserialize;
3+
4+
use crate::chatgpt_client::chatgpt_get_request;
5+
6+
#[derive(Debug, Deserialize)]
7+
pub struct GetTaskResponse {
8+
pub current_diff_task_turn: Option<AssistantTurn>,
9+
}
10+
11+
// Only relevant fields for our extraction
12+
#[derive(Debug, Deserialize)]
13+
pub struct AssistantTurn {
14+
pub output_items: Vec<OutputItem>,
15+
}
16+
17+
#[derive(Debug, Deserialize)]
18+
#[serde(tag = "type")]
19+
pub enum OutputItem {
20+
#[serde(rename = "pr")]
21+
Pr(PrOutputItem),
22+
23+
#[serde(other)]
24+
Other,
25+
}
26+
27+
#[derive(Debug, Deserialize)]
28+
pub struct PrOutputItem {
29+
pub output_diff: OutputDiff,
30+
}
31+
32+
#[derive(Debug, Deserialize)]
33+
pub struct OutputDiff {
34+
pub diff: String,
35+
}
36+
37+
pub(crate) async fn get_task(config: &Config, task_id: String) -> anyhow::Result<GetTaskResponse> {
38+
let path = format!("/wham/tasks/{task_id}");
39+
chatgpt_get_request(config, path).await
40+
}

codex-rs/cli/src/exit_status.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#[cfg(unix)]
2+
pub(crate) fn handle_exit_status(status: std::process::ExitStatus) -> ! {
3+
use std::os::unix::process::ExitStatusExt;
4+
5+
// Use ExitStatus to derive the exit code.
6+
if let Some(code) = status.code() {
7+
std::process::exit(code);
8+
} else if let Some(signal) = status.signal() {
9+
std::process::exit(128 + signal);
10+
} else {
11+
std::process::exit(1);
12+
}
13+
}
14+
15+
#[cfg(windows)]
16+
pub(crate) fn handle_exit_status(status: std::process::ExitStatus) -> ! {
17+
if let Some(code) = status.code() {
18+
std::process::exit(code);
19+
} else {
20+
// Rare on Windows, but if it happens: use fallback code.
21+
std::process::exit(1);
22+
}
23+
}

codex-rs/cloud-tasks/src/cli.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use clap::Args;
2+
use clap::Parser;
3+
use codex_utils_cli::CliConfigOverrides;
4+
5+
#[derive(Parser, Debug, Default)]
6+
#[command(version)]
7+
pub struct Cli {
8+
#[clap(skip)]
9+
pub config_overrides: CliConfigOverrides,
10+
11+
#[command(subcommand)]
12+
pub command: Option<Command>,
13+
}
14+
15+
#[derive(Debug, clap::Subcommand)]
16+
pub enum Command {
17+
/// Submit a new Codex Cloud task without launching the TUI.
18+
Exec(ExecCommand),
19+
/// Show the status of a Codex Cloud task.
20+
Status(StatusCommand),
21+
/// List Codex Cloud tasks.
22+
List(ListCommand),
23+
/// Apply the diff for a Codex Cloud task locally.
24+
Apply(ApplyCommand),
25+
/// Show the unified diff for a Codex Cloud task.
26+
Diff(DiffCommand),
27+
}
28+
29+
#[derive(Debug, Args)]
30+
pub struct ExecCommand {
31+
/// Task prompt to run in Codex Cloud.
32+
#[arg(value_name = "QUERY")]
33+
pub query: Option<String>,
34+
35+
/// Target environment identifier (see `codex cloud` to browse).
36+
#[arg(long = "env", value_name = "ENV_ID")]
37+
pub environment: String,
38+
39+
/// Number of assistant attempts (best-of-N).
40+
#[arg(
41+
long = "attempts",
42+
default_value_t = 1usize,
43+
value_parser = parse_attempts
44+
)]
45+
pub attempts: usize,
46+
47+
/// Git branch to run in Codex Cloud (defaults to current branch).
48+
#[arg(long = "branch", value_name = "BRANCH")]
49+
pub branch: Option<String>,
50+
}
51+
52+
fn parse_attempts(input: &str) -> Result<usize, String> {
53+
let value: usize = input
54+
.parse()
55+
.map_err(|_| "attempts must be an integer between 1 and 4".to_string())?;
56+
if (1..=4).contains(&value) {
57+
Ok(value)
58+
} else {
59+
Err("attempts must be between 1 and 4".to_string())
60+
}
61+
}
62+
63+
fn parse_limit(input: &str) -> Result<i64, String> {
64+
let value: i64 = input
65+
.parse()
66+
.map_err(|_| "limit must be an integer between 1 and 20".to_string())?;
67+
if (1..=20).contains(&value) {
68+
Ok(value)
69+
} else {
70+
Err("limit must be between 1 and 20".to_string())
71+
}
72+
}
73+
74+
#[derive(Debug, Args)]
75+
pub struct StatusCommand {
76+
/// Codex Cloud task identifier to inspect.
77+
#[arg(value_name = "TASK_ID")]
78+
pub task_id: String,
79+
}
80+
81+
#[derive(Debug, Args)]
82+
pub struct ListCommand {
83+
/// Filter tasks by environment identifier.
84+
#[arg(long = "env", value_name = "ENV_ID")]
85+
pub environment: Option<String>,
86+
87+
/// Maximum number of tasks to return (1-20).
88+
#[arg(long = "limit", default_value_t = 20, value_parser = parse_limit, value_name = "N")]
89+
pub limit: i64,
90+
91+
/// Pagination cursor returned by a previous call.
92+
#[arg(long = "cursor", value_name = "CURSOR")]
93+
pub cursor: Option<String>,
94+
95+
/// Emit JSON instead of plain text.
96+
#[arg(long = "json", default_value_t = false)]
97+
pub json: bool,
98+
}
99+
100+
#[derive(Debug, Args)]
101+
pub struct ApplyCommand {
102+
/// Codex Cloud task identifier to apply.
103+
#[arg(value_name = "TASK_ID")]
104+
pub task_id: String,
105+
106+
/// Attempt number to apply (1-based).
107+
#[arg(long = "attempt", value_parser = parse_attempts, value_name = "N")]
108+
pub attempt: Option<usize>,
109+
}
110+
111+
#[derive(Debug, Args)]
112+
pub struct DiffCommand {
113+
/// Codex Cloud task identifier to display.
114+
#[arg(value_name = "TASK_ID")]
115+
pub task_id: String,
116+
117+
/// Attempt number to display (1-based).
118+
#[arg(long = "attempt", value_parser = parse_attempts, value_name = "N")]
119+
pub attempt: Option<usize>,
120+
}

codex-rs/core/src/web_search.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use codex_protocol::models::WebSearchAction;
2+
3+
fn search_action_detail(query: &Option<String>, queries: &Option<Vec<String>>) -> String {
4+
query.clone().filter(|q| !q.is_empty()).unwrap_or_else(|| {
5+
let items = queries.as_ref();
6+
let first = items
7+
.and_then(|queries| queries.first())
8+
.cloned()
9+
.unwrap_or_default();
10+
if items.is_some_and(|queries| queries.len() > 1) && !first.is_empty() {
11+
format!("{first} ...")
12+
} else {
13+
first
14+
}
15+
})
16+
}
17+
18+
pub fn web_search_action_detail(action: &WebSearchAction) -> String {
19+
match action {
20+
WebSearchAction::Search { query, queries } => search_action_detail(query, queries),
21+
WebSearchAction::OpenPage { url } => url.clone().unwrap_or_default(),
22+
WebSearchAction::FindInPage { url, pattern } => match (pattern, url) {
23+
(Some(pattern), Some(url)) => format!("'{pattern}' in {url}"),
24+
(Some(pattern), None) => format!("'{pattern}'"),
25+
(None, Some(url)) => url.clone(),
26+
(None, None) => String::new(),
27+
},
28+
WebSearchAction::Other => String::new(),
29+
}
30+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use anyhow::Context;
2+
use std::fs;
3+
use std::path::Path;
4+
use std::time::Duration;
5+
6+
pub async fn wait_for_pid_file(path: &Path) -> anyhow::Result<String> {
7+
let pid = tokio::time::timeout(Duration::from_secs(2), async {
8+
loop {
9+
if let Ok(contents) = fs::read_to_string(path) {
10+
let trimmed = contents.trim();
11+
if !trimmed.is_empty() {
12+
return trimmed.to_string();
13+
}
14+
}
15+
tokio::time::sleep(Duration::from_millis(25)).await;
16+
}
17+
})
18+
.await
19+
.context("timed out waiting for pid file")?;
20+
21+
Ok(pid)
22+
}
23+
24+
pub fn process_is_alive(pid: &str) -> anyhow::Result<bool> {
25+
let status = std::process::Command::new("kill")
26+
.args(["-0", pid])
27+
.status()
28+
.context("failed to probe process liveness with kill -0")?;
29+
Ok(status.success())
30+
}
31+
32+
async fn wait_for_process_exit_inner(pid: String) -> anyhow::Result<()> {
33+
loop {
34+
if !process_is_alive(&pid)? {
35+
return Ok(());
36+
}
37+
tokio::time::sleep(Duration::from_millis(25)).await;
38+
}
39+
}
40+
41+
pub async fn wait_for_process_exit(pid: &str) -> anyhow::Result<()> {
42+
let pid = pid.to_string();
43+
tokio::time::timeout(Duration::from_secs(2), wait_for_process_exit_inner(pid))
44+
.await
45+
.context("timed out waiting for process to exit")??;
46+
47+
Ok(())
48+
}

0 commit comments

Comments
 (0)