Compare commits

...

35 Commits

Author SHA1 Message Date
Ahmed Ibrahim
ddaa072f87 revert some edits because they got too old 2025-07-24 11:56:28 -07:00
Ahmed Ibrahim
032743575d review 2025-07-24 11:43:17 -07:00
aibrahim-oai
6ddbe7ec2b Merge branch 'main' into chores-rollout 2025-07-23 15:04:43 -07:00
Ahmed Ibrahim
dbc7bc9f3a rebase 2025-07-23 14:54:49 -07:00
aibrahim-oai
3b130f3b39 Merge branch 'main' into chores-rollout 2025-07-23 14:21:31 -07:00
Ahmed Ibrahim
c29b0a09a4 Merge branch 'chores-rollout' of github.com:openai/codex into chores-rollout 2025-07-23 14:05:19 -07:00
Ahmed Ibrahim
b52156eb82 rollout 2025-07-23 14:04:57 -07:00
aibrahim-oai
ede52e89f3 Merge branch 'main' into chores-rollout 2025-07-22 11:59:03 -07:00
Ahmed Ibrahim
2c13f1e86d Merge branch 'chores-rollout' of github.com:openai/codex into chores-rollout 2025-07-21 20:10:13 -07:00
Ahmed Ibrahim
b5a12a3fd5 remove flakyness to another PR 2025-07-21 20:09:49 -07:00
aibrahim-oai
efd4edc0b6 Merge branch 'main' into chores-rollout 2025-07-21 13:58:35 -07:00
Ahmed Ibrahim
79dce68a0b adding todo 2025-07-20 18:15:13 -07:00
Ahmed Ibrahim
24e05336f4 wait for flush after task complete 2025-07-20 18:09:22 -07:00
Ahmed Ibrahim
90f9c6ee9b adding exit signal 2025-07-20 17:41:17 -07:00
Ahmed Ibrahim
7119f241d1 Merge branch 'chores-rollout' of github.com:openai/codex into chores-rollout 2025-07-20 17:33:27 -07:00
Ahmed Ibrahim
4262b77cfa adding exit signal 2025-07-20 17:32:54 -07:00
aibrahim-oai
7eccca035f Merge branch 'main' into chores-rollout 2025-07-20 17:24:50 -07:00
Ahmed Ibrahim
e382e19c65 eprintln 2025-07-20 13:53:09 -07:00
Ahmed Ibrahim
119e216c14 eprintln 2025-07-20 13:48:08 -07:00
Ahmed Ibrahim
0fe8a4b9c8 use utc instead 2025-07-20 11:34:42 -07:00
Ahmed Ibrahim
78c7b0f4df move live_cli 2025-07-20 11:19:25 -07:00
Ahmed Ibrahim
1d3b2e76dd move live_cli 2025-07-20 11:07:55 -07:00
Ahmed Ibrahim
9c1a046de1 move live_cli 2025-07-20 10:52:50 -07:00
Ahmed Ibrahim
567653a3b1 Merge branch 'chores-rollout' of github.com:openai/codex into chores-rollout 2025-07-20 10:45:37 -07:00
Ahmed Ibrahim
3616f16b8f moving to cli 2025-07-20 10:45:10 -07:00
aibrahim-oai
41c3747e45 Merge branch 'main' into chores-rollout 2025-07-20 10:39:57 -07:00
Ahmed Ibrahim
2dbe76d4a2 moving to cli 2025-07-20 10:39:03 -07:00
Ahmed Ibrahim
36a7427983 logging 2025-07-19 23:35:14 -07:00
Ahmed Ibrahim
c8c269e52c adding logging 2025-07-19 23:29:48 -07:00
Ahmed Ibrahim
60d5a80d51 use bin cargo-cli 2025-07-19 23:10:29 -07:00
Ahmed Ibrahim
b7360b3dda use bin cargo-cli 2025-07-19 23:10:24 -07:00
Ahmed Ibrahim
b376c55912 recognizing codex-rs 2025-07-19 23:02:42 -07:00
Ahmed Ibrahim
3e908f5a17 recognizing codex-rs 2025-07-19 23:00:42 -07:00
Ahmed Ibrahim
77016202a0 our binary is codex not codex-rs 2025-07-19 22:56:22 -07:00
Ahmed Ibrahim
4c66c11a62 chores 2025-07-19 22:52:43 -07:00
7 changed files with 75 additions and 46 deletions

6
codex-rs/Cargo.lock generated
View File

@@ -626,6 +626,7 @@ name = "codex-cli"
version = "0.0.0"
dependencies = [
"anyhow",
"assert_cmd",
"clap",
"clap_complete",
"codex-chatgpt",
@@ -636,10 +637,15 @@ dependencies = [
"codex-login",
"codex-mcp-server",
"codex-tui",
"predicates",
"serde_json",
"tempfile",
"tokio",
"tracing",
"tracing-subscriber",
"uuid",
"walkdir",
"wiremock",
]
[[package]]

View File

@@ -26,6 +26,7 @@ codex-login = { path = "../login" }
codex-linux-sandbox = { path = "../linux-sandbox" }
codex-mcp-server = { path = "../mcp-server" }
codex-tui = { path = "../tui" }
predicates = "3.1.3"
serde_json = "1"
tokio = { version = "1", features = [
"io-std",
@@ -36,3 +37,10 @@ tokio = { version = "1", features = [
] }
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
[dev-dependencies]
assert_cmd = "2"
tempfile = "3"
uuid = { version = "1", features = ["serde", "v4"] }
walkdir = "2.5.0"
wiremock = "0.6"

View File

@@ -1,7 +1,8 @@
#![expect(clippy::unwrap_used)]
use assert_cmd::Command as AssertCommand;
use assert_cmd::prelude::*;
use codex_core::exec::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
use std::process::Command;
use std::time::Duration;
use std::time::Instant;
use tempfile::TempDir;
@@ -50,13 +51,8 @@ async fn chat_mode_stream_cli() {
"model_providers.mock={{ name = \"mock\", base_url = \"{}/v1\", env_key = \"PATH\", wire_api = \"chat\" }}",
server.uri()
);
let mut cmd = AssertCommand::new("cargo");
cmd.arg("run")
.arg("-p")
.arg("codex-cli")
.arg("--quiet")
.arg("--")
.arg("exec")
let mut cmd = Command::cargo_bin("codex").unwrap();
cmd.arg("exec")
.arg("--skip-git-repo-check")
.arg("-c")
.arg(&provider_override)
@@ -100,13 +96,8 @@ async fn responses_api_stream_cli() {
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/cli_responses_fixture.sse");
let home = TempDir::new().unwrap();
let mut cmd = AssertCommand::new("cargo");
cmd.arg("run")
.arg("-p")
.arg("codex-cli")
.arg("--quiet")
.arg("--")
.arg("exec")
let mut cmd = Command::cargo_bin("codex").unwrap();
cmd.arg("exec")
.arg("--skip-git-repo-check")
.arg("-C")
.arg(env!("CARGO_MANIFEST_DIR"))
@@ -146,13 +137,8 @@ async fn integration_creates_and_checks_session_file() {
// 4. Run the codex CLI through cargo (ensures the right bin is built) and invoke `exec`,
// which is what records a session.
let mut cmd = AssertCommand::new("cargo");
cmd.arg("run")
.arg("-p")
.arg("codex-cli")
.arg("--quiet")
.arg("--")
.arg("exec")
let mut cmd = Command::cargo_bin("codex").unwrap();
cmd.arg("exec")
.arg("--skip-git-repo-check")
.arg("-C")
.arg(env!("CARGO_MANIFEST_DIR"))
@@ -185,7 +171,9 @@ async fn integration_creates_and_checks_session_file() {
for entry in WalkDir::new(&sessions_dir) {
let entry = match entry {
Ok(e) => e,
Err(_) => continue,
Err(_) => {
continue;
}
};
if !entry.file_type().is_file() {
continue;
@@ -207,7 +195,9 @@ async fn integration_creates_and_checks_session_file() {
}
let item: serde_json::Value = match serde_json::from_str(line) {
Ok(v) => v,
Err(_) => continue,
Err(_) => {
continue;
}
};
if item.get("type").and_then(|t| t.as_str()) == Some("message") {
if let Some(c) = item.get("content") {
@@ -228,7 +218,6 @@ async fn integration_creates_and_checks_session_file() {
Some(p) => p,
None => panic!("No session file containing the marker was found"),
};
// Basic sanity checks on location and metadata.
let rel = match path.strip_prefix(&sessions_dir) {
Ok(r) => r,
@@ -312,13 +301,8 @@ async fn integration_creates_and_checks_session_file() {
// to sidestep the issue.
let resume_path_str = path.to_string_lossy().replace('\\', "/");
let resume_override = format!("experimental_resume=\"{resume_path_str}\"");
let mut cmd2 = AssertCommand::new("cargo");
cmd2.arg("run")
.arg("-p")
.arg("codex-cli")
.arg("--quiet")
.arg("--")
.arg("exec")
let mut cmd2 = Command::cargo_bin("codex").unwrap();
cmd2.arg("exec")
.arg("--skip-git-repo-check")
.arg("-c")
.arg(&resume_override)

View File

@@ -30,7 +30,7 @@ fn run_live(prompt: &str) -> (assert_cmd::assert::Assert, TempDir) {
// implementation). Instead we configure the std `Command` ourselves, then later hand the
// resulting `Output` to `assert_cmd` for the familiar assertions.
let mut cmd = Command::cargo_bin("codex-rs").unwrap();
let mut cmd = Command::cargo_bin("codex-cli").unwrap();
cmd.current_dir(dir.path());
cmd.env("OPENAI_API_KEY", require_api_key());

View File

@@ -594,7 +594,7 @@ async fn submission_loop(
let mut restored_items: Option<Vec<ResponseItem>> = None;
let rollout_recorder: Option<RolloutRecorder> =
if let Some(path) = resume_path.as_ref() {
match RolloutRecorder::resume(path).await {
match RolloutRecorder::resume(path, cwd.clone()).await {
Ok((rec, saved)) => {
session_id = saved.session_id;
if !saved.items.is_empty() {

View File

@@ -20,6 +20,8 @@ use tracing::warn;
use uuid::Uuid;
use crate::config::Config;
use crate::git_info::GitInfo;
use crate::git_info::collect_git_info;
use crate::models::ResponseItem;
const SESSIONS_SUBDIR: &str = "sessions";
@@ -31,6 +33,14 @@ pub struct SessionMeta {
pub instructions: Option<String>,
}
#[derive(Serialize)]
struct SessionMetaWithGit {
#[serde(flatten)]
meta: SessionMeta,
#[serde(skip_serializing_if = "Option::is_none")]
git: Option<GitInfo>,
}
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct SessionStateSnapshot {}
@@ -86,15 +96,12 @@ impl RolloutRecorder {
.format(timestamp_format)
.map_err(|e| IoError::other(format!("failed to format timestamp: {e}")))?;
let meta = SessionMeta {
timestamp,
id: session_id,
instructions,
};
// Clone the cwd for the spawned task to collect git info asynchronously
let cwd = config.cwd.clone();
// A reasonably-sized bounded channel. If the buffer fills up the send
// future will yield, which is fine we only need to ensure we do not
// perform *blocking* I/O on the callers thread.
// perform *blocking* I/O on the caller's thread.
let (tx, rx) = mpsc::channel::<RolloutCmd>(256);
// Spawn a Tokio task that owns the file handle and performs async
@@ -103,7 +110,12 @@ impl RolloutRecorder {
tokio::task::spawn(rollout_writer(
tokio::fs::File::from_std(file),
rx,
Some(meta),
Some(SessionMeta {
timestamp,
id: session_id,
instructions,
}),
cwd,
));
Ok(Self { tx })
@@ -143,7 +155,10 @@ impl RolloutRecorder {
.map_err(|e| IoError::other(format!("failed to queue rollout state: {e}")))
}
pub async fn resume(path: &Path) -> std::io::Result<(Self, SavedSession)> {
pub async fn resume(
path: &Path,
cwd: std::path::PathBuf,
) -> std::io::Result<(Self, SavedSession)> {
info!("Resuming rollout from {path:?}");
let text = tokio::fs::read_to_string(path).await?;
let mut lines = text.lines();
@@ -201,7 +216,12 @@ impl RolloutRecorder {
.open(path)?;
let (tx, rx) = mpsc::channel::<RolloutCmd>(256);
tokio::task::spawn(rollout_writer(tokio::fs::File::from_std(file), rx, None));
tokio::task::spawn(rollout_writer(
tokio::fs::File::from_std(file),
rx,
None,
cwd,
));
info!("Resumed rollout successfully from {path:?}");
Ok((Self { tx }, saved))
}
@@ -270,15 +290,26 @@ fn create_log_file(config: &Config, session_id: Uuid) -> std::io::Result<LogFile
async fn rollout_writer(
mut file: tokio::fs::File,
mut rx: mpsc::Receiver<RolloutCmd>,
meta: Option<SessionMeta>,
mut meta: Option<SessionMeta>,
cwd: std::path::PathBuf,
) {
if let Some(meta) = meta {
if let Ok(json) = serde_json::to_string(&meta) {
// If we have a meta, collect git info asynchronously and write meta first
if let Some(session_meta) = meta.take() {
let git_info = collect_git_info(&cwd).await;
let session_meta_with_git = SessionMetaWithGit {
meta: session_meta,
git: git_info,
};
// Write the SessionMeta as the first item in the file
if let Ok(json) = serde_json::to_string(&session_meta_with_git) {
let _ = file.write_all(json.as_bytes()).await;
let _ = file.write_all(b"\n").await;
let _ = file.flush().await;
}
}
// Process rollout commands
while let Some(cmd) = rx.recv().await {
match cmd {
RolloutCmd::AddItems(items) => {