diff --git a/codex-rs/backend-client/src/client.rs b/codex-rs/backend-client/src/client.rs index 2169c39724..60d42a3d9d 100644 --- a/codex-rs/backend-client/src/client.rs +++ b/codex-rs/backend-client/src/client.rs @@ -36,7 +36,7 @@ impl Client { || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { - base_url = format!("{}/backend-api", base_url); + base_url = format!("{base_url}/backend-api"); } let http = reqwest::Client::builder().build()?; let path_style = if base_url.contains("/backend-api") { @@ -84,17 +84,16 @@ impl Client { h.insert(USER_AGENT, HeaderValue::from_static("codex-cli")); } if let Some(token) = &self.bearer_token { - let value = format!("Bearer {}", token); + let value = format!("Bearer {token}"); if let Ok(hv) = HeaderValue::from_str(&value) { h.insert(AUTHORIZATION, hv); } } - if let Some(acc) = &self.chatgpt_account_id { - if let Ok(name) = HeaderName::from_bytes(b"ChatGPT-Account-Id") { - if let Ok(hv) = HeaderValue::from_str(acc) { - h.insert(name, hv); - } - } + if let Some(acc) = &self.chatgpt_account_id + && let Ok(name) = HeaderName::from_bytes(b"ChatGPT-Account-Id") + && let Ok(hv) = HeaderValue::from_str(acc) + { + h.insert(name, hv); } // Optional internal toggle: send WHAM-FORCE-INTERNAL header when requested. // if matches!( diff --git a/codex-rs/backend-client/src/types.rs b/codex-rs/backend-client/src/types.rs index 69874309bb..60619994d1 100644 --- a/codex-rs/backend-client/src/types.rs +++ b/codex-rs/backend-client/src/types.rs @@ -62,15 +62,14 @@ impl CodeTaskDetailsResponseExt for CodeTaskDetailsResponse { .and_then(|v| v.as_array()); if let Some(items) = items { for item in items { - if item.get("type").and_then(Value::as_str) == Some("message") { - if let Some(content) = item.get("content").and_then(Value::as_array) { - for part in content { - if part.get("content_type").and_then(Value::as_str) == Some("text") - { - if let Some(txt) = part.get("text").and_then(Value::as_str) { - out.push(txt.to_string()); - } - } + if item.get("type").and_then(Value::as_str) == Some("message") + && let Some(content) = item.get("content").and_then(Value::as_array) + { + for part in content { + if part.get("content_type").and_then(Value::as_str) == Some("text") + && let Some(txt) = part.get("text").and_then(Value::as_str) + { + out.push(txt.to_string()); } } } @@ -88,7 +87,7 @@ impl CodeTaskDetailsResponseExt for CodeTaskDetailsResponse { if message.is_empty() && code.is_empty() { None } else if message.is_empty() { - Some(format!("{code}")) + Some(code.to_string()) } else if code.is_empty() { Some(message.to_string()) } else { @@ -111,31 +110,30 @@ impl CodeTaskDetailsResponseExt for CodeTaskDetailsResponse { } if let Some(od) = obj.get("output_diff").and_then(Value::as_object) { if let Some(fm) = od.get("files_modified") { - if let Some(map) = fm.as_object() { - if map.len() == 1 { - if let Some((k, _)) = map.iter().next() { - let p = k.to_string(); - return Some((p.clone(), p)); - } + if let Some(map) = fm.as_object() + && map.len() == 1 + && let Some((k, _)) = map.iter().next() + { + let p = k.to_string(); + return Some((p.clone(), p)); + } else if let Some(arr) = fm.as_array() + && arr.len() == 1 + { + let el = &arr[0]; + if let Some(p) = el.as_str() { + let p = p.to_string(); + return Some((p.clone(), p)); } - } else if let Some(arr) = fm.as_array() { - if arr.len() == 1 { - let el = &arr[0]; - if let Some(p) = el.as_str() { + if let Some(o) = el.as_object() { + let path = o.get("path").and_then(Value::as_str); + let oldp = o.get("old_path").and_then(Value::as_str); + let newp = o.get("new_path").and_then(Value::as_str); + if let Some(p) = path { let p = p.to_string(); return Some((p.clone(), p)); } - if let Some(o) = el.as_object() { - let path = o.get("path").and_then(Value::as_str); - let oldp = o.get("old_path").and_then(Value::as_str); - let newp = o.get("new_path").and_then(Value::as_str); - if let Some(p) = path { - let p = p.to_string(); - return Some((p.clone(), p)); - } - if let (Some(o1), Some(n1)) = (oldp, newp) { - return Some((o1.to_string(), n1.to_string())); - } + if let (Some(o1), Some(n1)) = (oldp, newp) { + return Some((o1.to_string(), n1.to_string())); } } } @@ -152,12 +150,11 @@ impl CodeTaskDetailsResponseExt for CodeTaskDetailsResponse { let candidates: [&Option>; 2] = [&self.current_diff_task_turn, &self.current_assistant_turn]; for map in candidates { - if let Some(m) = map.as_ref() { - if let Some(items) = m.get("output_items").and_then(serde_json::Value::as_array) { - if let Some(p) = try_from_items(items) { - return Some(p); - } - } + if let Some(m) = map.as_ref() + && let Some(items) = m.get("output_items").and_then(serde_json::Value::as_array) + && let Some(p) = try_from_items(items) + { + return Some(p); } } None @@ -223,10 +220,10 @@ pub fn extract_file_paths_list(details: &CodeTaskDetailsResponse) -> Vec<(String &details.current_assistant_turn, ]; for map in candidates { - if let Some(m) = map.as_ref() { - if let Some(items) = m.get("output_items").and_then(Value::as_array) { - push_from_items(&mut out, items); - } + if let Some(m) = map.as_ref() + && let Some(items) = m.get("output_items").and_then(Value::as_array) + { + push_from_items(&mut out, items); } } out diff --git a/codex-rs/cloud-tasks/src/app.rs b/codex-rs/cloud-tasks/src/app.rs index f79f65d2c0..7eba606b24 100644 --- a/codex-rs/cloud-tasks/src/app.rs +++ b/codex-rs/cloud-tasks/src/app.rs @@ -204,7 +204,7 @@ mod tests { let mut out = Vec::new(); for (i, t) in titles.into_iter().enumerate() { out.push(TaskSummary { - id: TaskId(format!("T-{}", i)), + id: TaskId(format!("T-{i}")), title: t.to_string(), status: codex_cloud_tasks_api::TaskStatus::Ready, updated_at: Utc::now(), diff --git a/codex-rs/cloud-tasks/src/bin/conncheck.rs b/codex-rs/cloud-tasks/src/bin/conncheck.rs index a0b7ddcf72..f0948a8ebb 100644 --- a/codex-rs/cloud-tasks/src/bin/conncheck.rs +++ b/codex-rs/cloud-tasks/src/bin/conncheck.rs @@ -105,7 +105,7 @@ async fn main() -> anyhow::Result<()> { // Print the full response object for debugging/inspection. match serde_json::to_string_pretty(&list) { Ok(json) => { - println!("\nfull response object (pretty JSON):\n{}", json); + println!("\nfull response object (pretty JSON):\n{json}"); } Err(e) => { println!("failed to serialize response to JSON: {e}"); diff --git a/codex-rs/cloud-tasks/src/bin/envcheck.rs b/codex-rs/cloud-tasks/src/bin/envcheck.rs index b119743ed0..2a906b3015 100644 --- a/codex-rs/cloud-tasks/src/bin/envcheck.rs +++ b/codex-rs/cloud-tasks/src/bin/envcheck.rs @@ -36,7 +36,7 @@ async fn main() -> anyhow::Result<()> { || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { - base_url = format!("{}/backend-api", base_url); + base_url = format!("{base_url}/backend-api"); } println!("base_url: {base_url}"); println!( @@ -68,7 +68,7 @@ async fn main() -> anyhow::Result<()> { match auth.get_token().await { Ok(token) if !token.is_empty() => { println!("auth: ChatGPT token present ({} chars)", token.len()); - let value = format!("Bearer {}", token); + let value = format!("Bearer {token}"); if let Ok(hv) = HeaderValue::from_str(&value) { headers.insert(AUTHORIZATION, hv); } diff --git a/codex-rs/cloud-tasks/src/bin/newtask.rs b/codex-rs/cloud-tasks/src/bin/newtask.rs index 72a1f7c8b8..65e01c72e8 100644 --- a/codex-rs/cloud-tasks/src/bin/newtask.rs +++ b/codex-rs/cloud-tasks/src/bin/newtask.rs @@ -47,7 +47,7 @@ async fn main() -> anyhow::Result<()> { || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { - base_url = format!("{}/backend-api", base_url); + base_url = format!("{base_url}/backend-api"); } println!("base_url: {base_url}"); let is_wham = base_url.contains("/backend-api"); @@ -73,7 +73,7 @@ async fn main() -> anyhow::Result<()> { Ok(token) if !token.is_empty() => { have_auth = true; println!("auth: ChatGPT token present ({} chars)", token.len()); - let value = format!("Bearer {}", token); + let value = format!("Bearer {token}"); if let Ok(hv) = HeaderValue::from_str(&value) { headers.insert(AUTHORIZATION, hv); } @@ -123,9 +123,9 @@ async fn main() -> anyhow::Result<()> { // Build request payload patterned after VSCode: POST /wham/tasks let url = if is_wham { - format!("{}/wham/tasks", base_url) + format!("{base_url}/wham/tasks") } else { - format!("{}/api/codex/tasks", base_url) + format!("{base_url}/api/codex/tasks") }; println!( "request: POST {}", @@ -176,14 +176,14 @@ async fn main() -> anyhow::Result<()> { .unwrap_or("") .to_string(); let body = res.text().await.unwrap_or_default(); - println!("status: {}", status); - println!("content-type: {}", ct); + println!("status: {status}"); + println!("content-type: {ct}"); match serde_json::from_str::(&body) { Ok(v) => println!( "response (pretty JSON):\n{}", serde_json::to_string_pretty(&v).unwrap_or(body) ), - Err(_) => println!("response (raw):\n{}", body), + Err(_) => println!("response (raw):\n{body}"), } if !status.is_success() { diff --git a/codex-rs/cloud-tasks/src/env_detect.rs b/codex-rs/cloud-tasks/src/env_detect.rs index e3987dc881..24b10d8e64 100644 --- a/codex-rs/cloud-tasks/src/env_detect.rs +++ b/codex-rs/cloud-tasks/src/env_detect.rs @@ -28,7 +28,7 @@ pub async fn autodetect_environment_id( ) -> anyhow::Result { // 1) Try repo-specific environments based on local git origins (GitHub only, like VSCode) let origins = get_git_origins(); - crate::append_error_log(format!("env: git origins: {:?}", origins)); + crate::append_error_log(format!("env: git origins: {origins:?}")); let mut by_repo_envs: Vec = Vec::new(); for origin in &origins { if let Some((owner, repo)) = parse_owner_repo(origin) { @@ -43,20 +43,17 @@ pub async fn autodetect_environment_id( base_url, "github", owner, repo ) }; - crate::append_error_log(format!("env: GET {}", url)); + crate::append_error_log(format!("env: GET {url}")); match get_json::>(&url, headers).await { Ok(mut list) => { crate::append_error_log(format!( - "env: by-repo returned {} env(s) for {}/{}", + "env: by-repo returned {} env(s) for {owner}/{repo}", list.len(), - owner, - repo )); by_repo_envs.append(&mut list); } Err(e) => crate::append_error_log(format!( - "env: by-repo fetch failed for {}/{}: {e}", - owner, repo + "env: by-repo fetch failed for {owner}/{repo}: {e}" )), } } @@ -70,11 +67,11 @@ pub async fn autodetect_environment_id( // 2) Fallback to the full list let list_url = if base_url.contains("/backend-api") { - format!("{}/wham/environments", base_url) + format!("{base_url}/wham/environments") } else { - format!("{}/api/codex/environments", base_url) + format!("{base_url}/api/codex/environments") }; - crate::append_error_log(format!("env: GET {}", list_url)); + crate::append_error_log(format!("env: GET {list_url}")); // Fetch and log the full environments JSON for debugging let http = reqwest::Client::builder().build()?; let res = http.get(&list_url).headers(headers.clone()).send().await?; @@ -86,25 +83,21 @@ pub async fn autodetect_environment_id( .unwrap_or("") .to_string(); let body = res.text().await.unwrap_or_default(); - crate::append_error_log(format!("env: status={} content-type={}", status, ct)); + crate::append_error_log(format!("env: status={status} content-type={ct}")); match serde_json::from_str::(&body) { Ok(v) => { let pretty = serde_json::to_string_pretty(&v).unwrap_or(body.clone()); - crate::append_error_log(format!("env: /environments JSON (pretty):\n{}", pretty)); + crate::append_error_log(format!("env: /environments JSON (pretty):\n{pretty}")); } - Err(_) => crate::append_error_log(format!("env: /environments (raw):\n{}", body)), + Err(_) => crate::append_error_log(format!("env: /environments (raw):\n{body}")), } if !status.is_success() { - anyhow::bail!(format!( - "GET {} failed: {}; content-type={}; body={}", - list_url, status, ct, body - )); + anyhow::bail!("GET {list_url} failed: {status}; content-type={ct}; body={body}"); } let all_envs: Vec = serde_json::from_str(&body).map_err(|e| { - anyhow::anyhow!(format!( - "Decode error for {}: {}; content-type={}; body={}", - list_url, e, ct, body - )) + anyhow::anyhow!( + "Decode error for {list_url}: {e}; content-type={ct}; body={body}" + ) })?; if let Some(env) = pick_environment_row(&all_envs, desired_label.as_deref()) { return Ok(AutodetectSelection { @@ -128,7 +121,7 @@ fn pick_environment_row( .iter() .find(|e| e.label.as_deref().unwrap_or("").to_lowercase() == lc) { - crate::append_error_log(format!("env: matched by label: {} -> {}", label, e.id)); + crate::append_error_log(format!("env: matched by label: {label} -> {}", e.id)); return Some(e.clone()); } } @@ -166,16 +159,12 @@ async fn get_json( .unwrap_or("") .to_string(); let body = res.text().await.unwrap_or_default(); - crate::append_error_log(format!("env: status={} content-type={}", status, ct)); + crate::append_error_log(format!("env: status={status} content-type={ct}")); if !status.is_success() { - anyhow::bail!(format!( - "GET {url} failed: {status}; content-type={ct}; body={body}" - )); + anyhow::bail!("GET {url} failed: {status}; content-type={ct}; body={body}"); } let parsed = serde_json::from_str::(&body).map_err(|e| { - anyhow::anyhow!(format!( - "Decode error for {url}: {e}; content-type={ct}; body={body}" - )) + anyhow::anyhow!("Decode error for {url}: {e}; content-type={ct}; body={body}") })?; Ok(parsed) } @@ -242,8 +231,7 @@ fn parse_owner_repo(url: &str) -> Option<(String, String)> { let owner = parts.next()?.to_string(); let repo = parts.next()?.to_string(); crate::append_error_log(format!( - "env: parsed SSH GitHub origin => {}/{}", - owner, repo + "env: parsed SSH GitHub origin => {owner}/{repo}" )); return Some((owner, repo)); } @@ -260,8 +248,7 @@ fn parse_owner_repo(url: &str) -> Option<(String, String)> { let owner = parts.next()?.to_string(); let repo = parts.next()?.to_string(); crate::append_error_log(format!( - "env: parsed HTTP GitHub origin => {}/{}", - owner, repo + "env: parsed HTTP GitHub origin => {owner}/{repo}" )); return Some((owner, repo)); } @@ -302,7 +289,7 @@ pub async fn list_environments( id: e.id.clone(), label: e.label.clone(), is_pinned: e.is_pinned.unwrap_or(false), - repo_hints: Some(format!("{}/{}", owner, repo)), + repo_hints: Some(format!("{owner}/{repo}")), }); // Merge: keep label if present, or use new; accumulate pinned flag if entry.label.is_none() { @@ -310,7 +297,7 @@ pub async fn list_environments( } entry.is_pinned = entry.is_pinned || e.is_pinned.unwrap_or(false); if entry.repo_hints.is_none() { - entry.repo_hints = Some(format!("{}/{}", owner, repo)); + entry.repo_hints = Some(format!("{owner}/{repo}")); } } } @@ -326,9 +313,9 @@ pub async fn list_environments( // 2) Fallback to the full list; on error return what we have if any. let list_url = if base_url.contains("/backend-api") { - format!("{}/wham/environments", base_url) + format!("{base_url}/wham/environments") } else { - format!("{}/api/codex/environments", base_url) + format!("{base_url}/api/codex/environments") }; match get_json::>(&list_url, headers).await { Ok(list) => { diff --git a/codex-rs/cloud-tasks/src/lib.rs b/codex-rs/cloud-tasks/src/lib.rs index bec75014f7..1f3b178653 100644 --- a/codex-rs/cloud-tasks/src/lib.rs +++ b/codex-rs/cloud-tasks/src/lib.rs @@ -112,7 +112,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a append_error_log(format!("startup: base_url={base_url} path_style={style}")); // Require ChatGPT login (SWIC). Exit with a clear message if missing. - let token = match codex_core::config::find_codex_home() + let _token = match codex_core::config::find_codex_home() .ok() .map(|home| { codex_login::AuthManager::new( @@ -250,7 +250,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { - base_url = format!("{}/backend-api", base_url); + base_url = format!("{base_url}/backend-api"); } let ua = codex_core::default_client::get_codex_user_agent(Some("codex_cloud_tasks_tui")); @@ -267,30 +267,25 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a "codex_cloud_tasks_tui".to_string(), ); if let Some(auth) = am.auth() { - if let Ok(tok) = auth.get_token().await { - if !tok.is_empty() { - let v = format!("Bearer {}", tok); - if let Ok(hv) = reqwest::header::HeaderValue::from_str(&v) { - headers.insert(reqwest::header::AUTHORIZATION, hv); - } - if let Some(acc) = auth - .get_account_id() - .or_else(|| extract_chatgpt_account_id(&tok)) - { - if let Ok(name) = - reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") - { - if let Ok(hv) = reqwest::header::HeaderValue::from_str(&acc) { - headers.insert(name, hv); - } - } - } + if let Ok(tok) = auth.get_token().await && !tok.is_empty() { + let v = format!("Bearer {tok}"); + if let Ok(hv) = reqwest::header::HeaderValue::from_str(&v) { + headers.insert(reqwest::header::AUTHORIZATION, hv); + } + if let Some(acc) = auth + .get_account_id() + .or_else(|| extract_chatgpt_account_id(&tok)) + && let Ok(name) = + reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") + && let Ok(hv) = reqwest::header::HeaderValue::from_str(&acc) + { + headers.insert(name, hv); } } } } let res = crate::env_detect::list_environments(&base_url, &headers).await; - let _ = tx2.send(app::AppEvent::EnvironmentsLoaded(res.map_err(|e| e.into()))); + let _ = tx2.send(app::AppEvent::EnvironmentsLoaded(res)); }); } @@ -309,7 +304,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { - base_url = format!("{}/backend-api", base_url); + base_url = format!("{base_url}/backend-api"); } // Build headers: UA + ChatGPT auth if available @@ -328,27 +323,20 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a "codex_cloud_tasks_tui".to_string(), ); if let Some(auth) = am.auth() { - if let Ok(token) = auth.get_token().await { - if !token.is_empty() { - if let Ok(hv) = - reqwest::header::HeaderValue::from_str(&format!("Bearer {}", token)) - { - headers.insert(reqwest::header::AUTHORIZATION, hv); - } - if let Some(account_id) = auth - .get_account_id() - .or_else(|| extract_chatgpt_account_id(&token)) - { - if let Ok(name) = - reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") - { - if let Ok(hv) = - reqwest::header::HeaderValue::from_str(&account_id) - { - headers.insert(name, hv); - } - } - } + if let Ok(token) = auth.get_token().await && !token.is_empty() { + if let Ok(hv) = + reqwest::header::HeaderValue::from_str(&format!("Bearer {token}")) + { + headers.insert(reqwest::header::AUTHORIZATION, hv); + } + if let Some(account_id) = auth + .get_account_id() + .or_else(|| extract_chatgpt_account_id(&token)) + && let Ok(name) = + reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") + && let Ok(hv) = reqwest::header::HeaderValue::from_str(&account_id) + { + headers.insert(name, hv); } } } @@ -356,9 +344,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a // Run autodetect. If it fails, we keep using "All". let res = crate::env_detect::autodetect_environment_id(&base_url, &headers, None).await; - let _ = tx2.send(app::AppEvent::EnvironmentAutodetected( - res.map_err(|e| e.into()), - )); + let _ = tx2.send(app::AppEvent::EnvironmentAutodetected(res)); }); } @@ -382,7 +368,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a recv = frame_rx.recv() => { match recv { Some(at) => { - if next_deadline.map_or(true, |cur| at < cur) { + if next_deadline.is_none_or(|cur| at < cur) { next_deadline = Some(at); } continue; // recompute sleep target @@ -486,9 +472,9 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a let _ = frame_tx.send(Instant::now()); } Err(msg) => { - append_error_log(format!("new-task: submit failed: {}", msg)); + append_error_log(format!("new-task: submit failed: {msg}")); if let Some(page) = app.new_task.as_mut() { page.submitting = false; } - app.status = format!("Submit failed: {}. See error.log for details.", msg); + app.status = format!("Submit failed: {msg}. See error.log for details."); needs_redraw = true; let _ = frame_tx.send(Instant::now()); } @@ -574,7 +560,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a let mut base_url = std::env::var("CODEX_CLOUD_TASKS_BASE_URL").unwrap_or_else(|_| "https://chatgpt.com/backend-api".to_string()); while base_url.ends_with('/') { base_url.pop(); } if (base_url.starts_with("https://chatgpt.com") || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { - base_url = format!("{}/backend-api", base_url); + base_url = format!("{base_url}/backend-api"); } let ua = codex_core::default_client::get_codex_user_agent(Some("codex_cloud_tasks_tui")); let mut headers = reqwest::header::HeaderMap::new(); @@ -586,19 +572,19 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a "codex_cloud_tasks_tui".to_string(), ); if let Some(auth) = am.auth() { - if let Ok(tok) = auth.get_token().await { if !tok.is_empty() { - let v = format!("Bearer {}", tok); + if let Ok(tok) = auth.get_token().await && !tok.is_empty() { + let v = format!("Bearer {tok}"); if let Ok(hv) = reqwest::header::HeaderValue::from_str(&v) { headers.insert(reqwest::header::AUTHORIZATION, hv); } - if let Some(acc) = auth.get_account_id().or_else(|| extract_chatgpt_account_id(&tok)) { - if let Ok(name) = reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") { - if let Ok(hv) = reqwest::header::HeaderValue::from_str(&acc) { headers.insert(name, hv); } - } + if let Some(acc) = auth.get_account_id().or_else(|| extract_chatgpt_account_id(&tok)) + && let Ok(name) = reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") + && let Ok(hv) = reqwest::header::HeaderValue::from_str(&acc) { + headers.insert(name, hv); } - }} + } } } let res = crate::env_detect::list_environments(&base_url, &headers).await; - let _ = tx3.send(app::AppEvent::EnvironmentsLoaded(res.map_err(|e| e.into()))); + let _ = tx3.send(app::AppEvent::EnvironmentsLoaded(res)); }); let _ = frame_tx.send(Instant::now()); } @@ -629,7 +615,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a } app::AppEvent::DetailsFailed { id, title, error } => { if let Some(ov) = &app.diff_overlay { if ov.task_id != id { continue; } } - append_error_log(format!("details failed for {}: {}", id.0, error)); + append_error_log(format!("details failed for {}: {error}", id.0)); let pretty = pretty_lines_from_error(&error); let mut sd = crate::scrollable_diff::ScrollableDiff::new(); sd.set_content(pretty); @@ -698,7 +684,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a .unwrap_or_else(|_| "https://chatgpt.com/backend-api".to_string()); while base_url.ends_with('/') { base_url.pop(); } if (base_url.starts_with("https://chatgpt.com") || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { - base_url = format!("{}/backend-api", base_url); + base_url = format!("{base_url}/backend-api"); } let ua = codex_core::default_client::get_codex_user_agent(Some("codex_cloud_tasks_tui")); let mut headers = reqwest::header::HeaderMap::new(); @@ -710,19 +696,19 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a "codex_cloud_tasks_tui".to_string(), ); if let Some(auth) = am.auth() { - if let Ok(tok) = auth.get_token().await { if !tok.is_empty() { - let v = format!("Bearer {}", tok); + if let Ok(tok) = auth.get_token().await && !tok.is_empty() { + let v = format!("Bearer {tok}"); if let Ok(hv) = reqwest::header::HeaderValue::from_str(&v) { headers.insert(reqwest::header::AUTHORIZATION, hv); } - if let Some(acc) = auth.get_account_id().or_else(|| extract_chatgpt_account_id(&tok)) { - if let Ok(name) = reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") { - if let Ok(hv) = reqwest::header::HeaderValue::from_str(&acc) { headers.insert(name, hv); } - } + if let Some(acc) = auth.get_account_id().or_else(|| extract_chatgpt_account_id(&tok)) + && let Ok(name) = reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") + && let Ok(hv) = reqwest::header::HeaderValue::from_str(&acc) { + headers.insert(name, hv); } - }} + } } } let res = crate::env_detect::list_environments(&base_url, &headers).await; - let _ = tx2.send(app::AppEvent::EnvironmentsLoaded(res.map_err(|e| e.into()))); + let _ = tx2.send(app::AppEvent::EnvironmentsLoaded(res)); }); } // Render after opening env modal to show it instantly. @@ -745,8 +731,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a if page.submitting { // Ignore input while submitting } else { - match page.composer.input(key) { - codex_tui::ComposerAction::Submitted(text) => { + if let codex_tui::ComposerAction::Submitted(text) = page.composer.input(key) { // Submit only if we have an env id if let Some(env) = page.env_id.clone() { append_error_log(format!( @@ -762,15 +747,13 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a let result = codex_cloud_tasks_api::CloudBackend::create_task(&*backend2, &env, &text, "main", false).await; let evt = match result { Ok(ok) => app::AppEvent::NewTaskSubmitted(Ok(ok)), - Err(e) => app::AppEvent::NewTaskSubmitted(Err(format!("{}", e))), + Err(e) => app::AppEvent::NewTaskSubmitted(Err(format!("{e}"))), }; let _ = tx2.send(evt); }); } else { app.status = "No environment selected (press 'e' to choose)".to_string(); } - } - _ => {} } } needs_redraw = true; @@ -895,7 +878,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a // Build headers (UA + ChatGPT token + account id) let mut base_url = std::env::var("CODEX_CLOUD_TASKS_BASE_URL").unwrap_or_else(|_| "https://chatgpt.com/backend-api".to_string()); while base_url.ends_with('/') { base_url.pop(); } - if (base_url.starts_with("https://chatgpt.com") || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { base_url = format!("{}/backend-api", base_url); } + if (base_url.starts_with("https://chatgpt.com") || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { base_url = format!("{base_url}/backend-api"); } let ua = codex_core::default_client::get_codex_user_agent(Some("codex_cloud_tasks_tui")); let mut headers = reqwest::header::HeaderMap::new(); headers.insert(reqwest::header::USER_AGENT, reqwest::header::HeaderValue::from_str(&ua).unwrap_or(reqwest::header::HeaderValue::from_static("codex-cli"))); @@ -905,18 +888,18 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a codex_login::AuthMode::ChatGPT, "codex_cloud_tasks_tui".to_string(), ); - if let Some(auth) = am.auth() { if let Ok(tok) = auth.get_token().await { if !tok.is_empty() { - let v = format!("Bearer {}", tok); + if let Some(auth) = am.auth() { if let Ok(tok) = auth.get_token().await && !tok.is_empty() { + let v = format!("Bearer {tok}"); if let Ok(hv) = reqwest::header::HeaderValue::from_str(&v) { headers.insert(reqwest::header::AUTHORIZATION, hv); } - if let Some(acc) = auth.get_account_id().or_else(|| extract_chatgpt_account_id(&tok)) { - if let Ok(name) = reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") { - if let Ok(hv) = reqwest::header::HeaderValue::from_str(&acc) { headers.insert(name, hv); } - } + if let Some(acc) = auth.get_account_id().or_else(|| extract_chatgpt_account_id(&tok)) + && let Ok(name) = reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") + && let Ok(hv) = reqwest::header::HeaderValue::from_str(&acc) { + headers.insert(name, hv); } - }}} + }} } let res = crate::env_detect::list_environments(&base_url, &headers).await; - let _ = tx2.send(app::AppEvent::EnvironmentsLoaded(res.map_err(|e| e.into()))); + let _ = tx2.send(app::AppEvent::EnvironmentsLoaded(res)); }); } } @@ -957,7 +940,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a // Build headers (UA + ChatGPT token + account id) let mut base_url = std::env::var("CODEX_CLOUD_TASKS_BASE_URL").unwrap_or_else(|_| "https://chatgpt.com/backend-api".to_string()); while base_url.ends_with('/') { base_url.pop(); } - if (base_url.starts_with("https://chatgpt.com") || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { base_url = format!("{}/backend-api", base_url); } + if (base_url.starts_with("https://chatgpt.com") || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { base_url = format!("{base_url}/backend-api"); } let ua = codex_core::default_client::get_codex_user_agent(Some("codex_cloud_tasks_tui")); let mut headers = reqwest::header::HeaderMap::new(); headers.insert(reqwest::header::USER_AGENT, reqwest::header::HeaderValue::from_str(&ua).unwrap_or(reqwest::header::HeaderValue::from_static("codex-cli"))); @@ -969,7 +952,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a ); if let Some(auth) = am.auth() { if let Ok(tok) = auth.get_token().await { if !tok.is_empty() { - let v = format!("Bearer {}", tok); + let v = format!("Bearer {tok}"); if let Ok(hv) = reqwest::header::HeaderValue::from_str(&v) { headers.insert(reqwest::header::AUTHORIZATION, hv); } if let Some(acc) = auth.get_account_id().or_else(|| extract_chatgpt_account_id(&tok)) { if let Ok(name) = reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") { @@ -980,7 +963,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a } } let res = crate::env_detect::list_environments(&base_url, &headers).await; - let _ = tx2.send(app::AppEvent::EnvironmentsLoaded(res.map_err(|e| e.into()))); + let _ = tx2.send(app::AppEvent::EnvironmentsLoaded(res)); }); } KeyCode::Char(ch) if !key.modifiers.contains(KeyModifiers::CONTROL) && !key.modifiers.contains(KeyModifiers::ALT) => { @@ -1099,7 +1082,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a // Build headers (UA + ChatGPT token + account id) let mut base_url = std::env::var("CODEX_CLOUD_TASKS_BASE_URL").unwrap_or_else(|_| "https://chatgpt.com/backend-api".to_string()); while base_url.ends_with('/') { base_url.pop(); } - if (base_url.starts_with("https://chatgpt.com") || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { base_url = format!("{}/backend-api", base_url); } + if (base_url.starts_with("https://chatgpt.com") || base_url.starts_with("https://chat.openai.com")) && !base_url.contains("/backend-api") { base_url = format!("{base_url}/backend-api"); } let ua = codex_core::default_client::get_codex_user_agent(Some("codex_cloud_tasks_tui")); let mut headers = reqwest::header::HeaderMap::new(); headers.insert(reqwest::header::USER_AGENT, reqwest::header::HeaderValue::from_str(&ua).unwrap_or(reqwest::header::HeaderValue::from_static("codex-cli"))); @@ -1111,7 +1094,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a ); if let Some(auth) = am.auth() { if let Ok(tok) = auth.get_token().await { if !tok.is_empty() { - let v = format!("Bearer {}", tok); + let v = format!("Bearer {tok}"); if let Ok(hv) = reqwest::header::HeaderValue::from_str(&v) { headers.insert(reqwest::header::AUTHORIZATION, hv); } if let Some(acc) = auth.get_account_id().or_else(|| extract_chatgpt_account_id(&tok)) { if let Ok(name) = reqwest::header::HeaderName::from_bytes(b"ChatGPT-Account-Id") { @@ -1122,7 +1105,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a } } let res = crate::env_detect::list_environments(&base_url, &headers).await; - let _ = tx2.send(app::AppEvent::EnvironmentsLoaded(res.map_err(|e| e.into()))); + let _ = tx2.send(app::AppEvent::EnvironmentsLoaded(res)); }); } } @@ -1134,7 +1117,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a } KeyCode::Enter => { if let Some(task) = app.tasks.get(app.selected).cloned() { - app.status = format!("Loading details for {}…", task.title); + app.status = format!("Loading details for {title}…", title = task.title); app.details_inflight = true; // Open empty overlay immediately; content arrives via events let mut sd = crate::scrollable_diff::ScrollableDiff::new(); @@ -1157,7 +1140,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option) -> a let _ = tx2.send(app::AppEvent::DetailsMessagesLoaded { id: task.id, title: task.title, messages: msgs }); } Err(e2) => { - let _ = tx2.send(app::AppEvent::DetailsFailed { id: task.id, title: task.title, error: format!("{}", e2) }); + let _ = tx2.send(app::AppEvent::DetailsFailed { id: task.id, title: task.title, error: format!("{e2}") }); } } } @@ -1295,11 +1278,11 @@ fn pretty_lines_from_error(raw: &str) -> Vec { } else { format!("{code}: {msg}") }; - lines.push(format!("Assistant error: {}", summary)); + lines.push(format!("Assistant error: {summary}")); } } if let Some(status) = t.get("turn_status").and_then(|s| s.as_str()) { - lines.push(format!("Status: {}", status)); + lines.push(format!("Status: {status}")); } if let Some(text) = t .get("latest_event") diff --git a/codex-rs/cloud-tasks/src/ui.rs b/codex-rs/cloud-tasks/src/ui.rs index 44eb95efdd..5ca637c397 100644 --- a/codex-rs/cloud-tasks/src/ui.rs +++ b/codex-rs/cloud-tasks/src/ui.rs @@ -95,7 +95,6 @@ fn overlay_content(area: Rect) -> Rect { } pub fn draw_new_task_page(frame: &mut Frame, area: Rect, app: &mut App) { - use ratatui::widgets::Wrap; let title_spans = { let mut spans: Vec = vec!["New Task".magenta().bold()]; @@ -153,7 +152,7 @@ pub fn draw_new_task_page(frame: &mut Frame, area: Rect, app: &mut App) { // Place cursor where composer wants it if let Some(page) = app.new_task.as_ref() { if let Some((x, y)) = page.composer.cursor_pos(composer_area) { - frame.set_cursor(x, y); + frame.set_cursor_position((x, y)); } } } @@ -173,7 +172,7 @@ fn draw_list(frame: &mut Frame, area: Rect, app: &mut App) { .find(|r| &r.id == id) .and_then(|r| r.label.clone()) .unwrap_or_else(|| "Selected".to_string()); - format!(" • {}", label).dim() + format!(" • {label}").dim() } else { " • All".dim() }; @@ -182,7 +181,7 @@ fn draw_list(frame: &mut Frame, area: Rect, app: &mut App) { " • 0%".dim() } else { let p = ((app.selected as f32) / ((app.tasks.len() - 1) as f32) * 100.0).round() as i32; - format!(" • {}%", p.clamp(0, 100)).dim() + format!(" • {}%", p.clamp(0, 100)).dim() }; let title_line = { let base = Line::from(vec!["Cloud Tasks".into(), suffix_span, percent_span]); @@ -270,7 +269,7 @@ fn draw_footer(frame: &mut Frame, area: Rect, app: &mut App) { if status_line.len() > 2000 { // hard cap to avoid TUI noise status_line.truncate(2000); - status_line.push_str("…"); + status_line.push('…'); } // Clear the status row to avoid trailing characters when the message shrinks. frame.render_widget(Clear, rows[1]); @@ -332,7 +331,7 @@ fn draw_diff_overlay(frame: &mut Frame, area: Rect, app: &mut App) { }; if let Some(p) = pct_opt { title_spans.push(" • ".dim()); - title_spans.push(format!("{}%", p).dim()); + title_spans.push(format!("{p}%").dim()); } let block = overlay_block().title(Line::from(title_spans)); frame.render_widget(Clear, inner); @@ -428,7 +427,7 @@ pub fn draw_apply_modal(frame: &mut Frame, area: Rect, app: &mut App) { let mut body_lines: Vec = Vec::new(); let first = match m.result_level { Some(crate::app::ApplyResultLevel::Success) => msg.clone().green(), - Some(crate::app::ApplyResultLevel::Partial) => msg.clone().yellow(), + Some(crate::app::ApplyResultLevel::Partial) => msg.clone().magenta(), Some(crate::app::ApplyResultLevel::Error) => msg.clone().red(), None => msg.clone().into(), }; @@ -453,7 +452,7 @@ pub fn draw_apply_modal(frame: &mut Frame, area: Rect, app: &mut App) { body_lines.push(Line::from("")); body_lines.push( Line::from(format!("Skipped ({}):", m.skipped_paths.len())) - .yellow() + .magenta() .bold(), ); for p in &m.skipped_paths { @@ -504,7 +503,7 @@ fn style_diff_line(raw: &str) -> Line<'static> { Line::from(vec![Span::raw(raw.to_string())]) } -fn render_task_item(app: &App, t: &codex_cloud_tasks_api::TaskSummary) -> ListItem<'static> { +fn render_task_item(_app: &App, t: &codex_cloud_tasks_api::TaskSummary) -> ListItem<'static> { let status = match t.status { TaskStatus::Ready => "READY".green(), TaskStatus::Pending => "PENDING".magenta(), @@ -543,13 +542,13 @@ fn render_task_item(app: &App, t: &codex_cloud_tasks_api::TaskSummary) -> ListIt let dels = t.summary.lines_removed; let files = t.summary.files_changed; Line::from(vec![ - format!("+{}", adds).green(), + format!("+{adds}").green(), "/".into(), - format!("−{}", dels).red(), + format!("−{dels}").red(), " ".into(), "•".dim(), " ".into(), - format!("{}", files).into(), + format!("{files}").into(), " ".into(), "files".dim(), ]) @@ -569,15 +568,15 @@ fn format_relative_time(ts: chrono::DateTime) -> String { secs = 0; } if secs < 60 { - return format!("{}s ago", secs); + return format!("{secs}s ago"); } let mins = secs / 60; if mins < 60 { - return format!("{}m ago", mins); + return format!("{mins}m ago"); } let hours = mins / 60; if hours < 24 { - return format!("{}h ago", hours); + return format!("{hours}h ago"); } let local = ts.with_timezone(&Local); local.format("%b %e %H:%M").to_string() @@ -689,7 +688,7 @@ pub fn draw_env_modal(frame: &mut Frame, area: Rect, app: &mut App) { .map(|m| m.query.clone()) .unwrap_or_default(); let ql = query.to_lowercase(); - let search = Paragraph::new(format!("Search: {}", query)).wrap(Wrap { trim: true }); + let search = Paragraph::new(format!("Search: {query}")).wrap(Wrap { trim: true }); frame.render_widget(search, rows[1]); // Filter environments by query (case-insensitive substring over label/id/hints) diff --git a/codex-rs/login/src/server.rs b/codex-rs/login/src/server.rs index 5cc08d4205..bca3b16420 100644 --- a/codex-rs/login/src/server.rs +++ b/codex-rs/login/src/server.rs @@ -30,10 +30,11 @@ pub struct ServerOptions { pub port: u16, pub open_browser: bool, pub force_state: Option, + pub originator: String, } impl ServerOptions { - pub fn new(codex_home: PathBuf, client_id: String) -> Self { + pub fn new(codex_home: PathBuf, client_id: String, originator: String) -> Self { Self { codex_home, client_id: client_id.to_string(), @@ -41,6 +42,7 @@ impl ServerOptions { port: DEFAULT_PORT, open_browser: true, force_state: None, + originator, } } } @@ -96,7 +98,14 @@ pub fn run_login_server(opts: ServerOptions) -> io::Result { let server = Arc::new(server); let redirect_uri = format!("http://localhost:{actual_port}/auth/callback"); - let auth_url = build_authorize_url(&opts.issuer, &opts.client_id, &redirect_uri, &pkce, &state); + let auth_url = build_authorize_url( + &opts.issuer, + &opts.client_id, + &redirect_uri, + &pkce, + &state, + &opts.originator, + ); if opts.open_browser { let _ = webbrowser::open(&auth_url); @@ -279,6 +288,7 @@ fn build_authorize_url( redirect_uri: &str, pkce: &PkceCodes, state: &str, + originator: &str, ) -> String { let query = vec![ ("response_type", "code"), @@ -290,6 +300,7 @@ fn build_authorize_url( ("id_token_add_organizations", "true"), ("codex_cli_simplified_flow", "true"), ("state", state), + ("originator", originator), ]; let qs = query .into_iter() diff --git a/codex-rs/login/tests/suite/login_server_e2e.rs b/codex-rs/login/tests/suite/login_server_e2e.rs index ceb0a94733..ef6b80fbe1 100644 --- a/codex-rs/login/tests/suite/login_server_e2e.rs +++ b/codex-rs/login/tests/suite/login_server_e2e.rs @@ -100,6 +100,7 @@ async fn end_to_end_login_flow_persists_auth_json() { port: 0, open_browser: false, force_state: Some(state), + originator: "test_originator".to_string(), }; let server = run_login_server(opts).unwrap(); let login_port = server.actual_port; @@ -158,6 +159,7 @@ async fn creates_missing_codex_home_dir() { port: 0, open_browser: false, force_state: Some(state), + originator: "test_originator".to_string(), }; let server = run_login_server(opts).unwrap(); let login_port = server.actual_port;