mirror of
https://github.com/openai/codex.git
synced 2026-10-29 19:08:43 +00:00
cleanup
This commit is contained in:
Generated
+11
@@ -298,6 +298,17 @@ dependencies = [
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
|
||||
@@ -12,8 +12,8 @@ use codex_protocol::mcp_protocol::AuthMode;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub async fn login_with_chatgpt(codex_home: PathBuf) -> std::io::Result<()> {
|
||||
let opts = ServerOptions::new(codex_home, CLIENT_ID.to_string());
|
||||
pub async fn login_with_chatgpt(codex_home: PathBuf, originator: String) -> std::io::Result<()> {
|
||||
let opts = ServerOptions::new(codex_home, CLIENT_ID.to_string(), originator);
|
||||
let server = run_login_server(opts)?;
|
||||
|
||||
eprintln!(
|
||||
@@ -27,7 +27,12 @@ pub async fn login_with_chatgpt(codex_home: PathBuf) -> std::io::Result<()> {
|
||||
pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
let config = load_config_or_exit(cli_config_overrides);
|
||||
|
||||
match login_with_chatgpt(config.codex_home).await {
|
||||
match login_with_chatgpt(
|
||||
config.codex_home,
|
||||
config.responses_originator_header.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
eprintln!("Successfully logged in");
|
||||
std::process::exit(0);
|
||||
|
||||
@@ -34,12 +34,9 @@ pub struct ApplyModalState {
|
||||
|
||||
use crate::scrollable_diff::ScrollableDiff;
|
||||
use codex_cloud_tasks_api::CloudBackend;
|
||||
use codex_cloud_tasks_api::DiffSummary;
|
||||
use codex_cloud_tasks_api::TaskId;
|
||||
use codex_cloud_tasks_api::TaskSummary;
|
||||
use throbber_widgets_tui::ThrobberState;
|
||||
use tokio::sync::mpsc::UnboundedReceiver;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct App {
|
||||
@@ -65,8 +62,7 @@ pub struct App {
|
||||
// Background enrichment coordination
|
||||
pub list_generation: u64,
|
||||
pub in_flight: std::collections::HashSet<String>,
|
||||
pub summary_cache: std::collections::HashMap<String, (DiffSummary, std::time::Instant)>,
|
||||
pub no_diff_yet: std::collections::HashSet<String>,
|
||||
// Background enrichment caches were planned; currently unused.
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -90,8 +86,6 @@ impl App {
|
||||
apply_preflight_inflight: false,
|
||||
list_generation: 0,
|
||||
in_flight: std::collections::HashSet::new(),
|
||||
summary_cache: std::collections::HashMap::new(),
|
||||
no_diff_yet: std::collections::HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,14 +130,7 @@ pub enum AppEvent {
|
||||
env: Option<String>,
|
||||
result: anyhow::Result<Vec<TaskSummary>>,
|
||||
},
|
||||
/// Background diff summary computed for a task (or determined absent)
|
||||
TaskSummaryUpdated {
|
||||
generation: u64,
|
||||
id: TaskId,
|
||||
summary: DiffSummary,
|
||||
no_diff_yet: bool,
|
||||
environment_id: Option<String>,
|
||||
},
|
||||
// Background diff summary events were planned; removed for now to keep code minimal.
|
||||
/// Autodetection of a likely environment id finished
|
||||
EnvironmentAutodetected(anyhow::Result<crate::env_detect::AutodetectSelection>),
|
||||
/// Background completion of environment list fetch
|
||||
@@ -176,9 +163,7 @@ pub enum AppEvent {
|
||||
},
|
||||
}
|
||||
|
||||
pub type AppEventTx = UnboundedSender<AppEvent>;
|
||||
pub type AppEventRx = UnboundedReceiver<AppEvent>;
|
||||
|
||||
// Convenience aliases; currently unused.
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -19,10 +19,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
AuthMode::ChatGPT,
|
||||
"codex_cloud_tasks_detailcheck".to_string(),
|
||||
);
|
||||
if let Some(auth) = am.auth() {
|
||||
if let Ok(tok) = auth.get_token().await {
|
||||
client = client.with_bearer_token(tok);
|
||||
}
|
||||
if let Some(auth) = am.auth()
|
||||
&& let Ok(tok) = auth.get_token().await
|
||||
{
|
||||
client = client.with_bearer_token(tok);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,10 +77,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
.or_else(|| extract_chatgpt_account_id(&token))
|
||||
{
|
||||
println!("auth: ChatGPT-Account-Id: {account_id}");
|
||||
if let Ok(name) = HeaderName::from_bytes(b"ChatGPT-Account-Id") {
|
||||
if let Ok(hv) = HeaderValue::from_str(&account_id) {
|
||||
headers.insert(name, hv);
|
||||
}
|
||||
if let Ok(name) = HeaderName::from_bytes(b"ChatGPT-Account-Id")
|
||||
&& let Ok(hv) = HeaderValue::from_str(&account_id)
|
||||
{
|
||||
headers.insert(name, hv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,10 +82,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
.or_else(|| extract_chatgpt_account_id(&token))
|
||||
{
|
||||
println!("auth: ChatGPT-Account-Id: {account_id}");
|
||||
if let Ok(name) = HeaderName::from_bytes(b"ChatGPT-Account-Id") {
|
||||
if let Ok(hv) = HeaderValue::from_str(&account_id) {
|
||||
headers.insert(name, hv);
|
||||
}
|
||||
if let Ok(name) = HeaderName::from_bytes(b"ChatGPT-Account-Id")
|
||||
&& let Ok(hv) = HeaderValue::from_str(&account_id)
|
||||
{
|
||||
headers.insert(name, hv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,13 +141,13 @@ async fn main() -> anyhow::Result<()> {
|
||||
}));
|
||||
|
||||
// Optional: starting diff via env var for quick testing
|
||||
if let Ok(diff) = std::env::var("CODEX_STARTING_DIFF") {
|
||||
if !diff.is_empty() {
|
||||
input_items.push(serde_json::json!({
|
||||
"type": "pre_apply_patch",
|
||||
"output_diff": { "diff": diff }
|
||||
}));
|
||||
}
|
||||
if let Ok(diff) = std::env::var("CODEX_STARTING_DIFF")
|
||||
&& !diff.is_empty()
|
||||
{
|
||||
input_items.push(serde_json::json!({
|
||||
"type": "pre_apply_patch",
|
||||
"output_diff": { "diff": diff }
|
||||
}));
|
||||
}
|
||||
|
||||
let request_body = serde_json::json!({
|
||||
|
||||
@@ -174,38 +174,38 @@ fn get_git_origins() -> Vec<String> {
|
||||
let out = std::process::Command::new("git")
|
||||
.args(["config", "--get-regexp", "remote\\..*\\.url"])
|
||||
.output();
|
||||
if let Ok(ok) = out {
|
||||
if ok.status.success() {
|
||||
let s = String::from_utf8_lossy(&ok.stdout);
|
||||
let mut urls = Vec::new();
|
||||
for line in s.lines() {
|
||||
if let Some((_, url)) = line.split_once(' ') {
|
||||
urls.push(url.trim().to_string());
|
||||
}
|
||||
}
|
||||
if !urls.is_empty() {
|
||||
return uniq(urls);
|
||||
if let Ok(ok) = out
|
||||
&& ok.status.success()
|
||||
{
|
||||
let s = String::from_utf8_lossy(&ok.stdout);
|
||||
let mut urls = Vec::new();
|
||||
for line in s.lines() {
|
||||
if let Some((_, url)) = line.split_once(' ') {
|
||||
urls.push(url.trim().to_string());
|
||||
}
|
||||
}
|
||||
if !urls.is_empty() {
|
||||
return uniq(urls);
|
||||
}
|
||||
}
|
||||
// Fallback: git remote -v
|
||||
let out = std::process::Command::new("git")
|
||||
.args(["remote", "-v"])
|
||||
.output();
|
||||
if let Ok(ok) = out {
|
||||
if ok.status.success() {
|
||||
let s = String::from_utf8_lossy(&ok.stdout);
|
||||
let mut urls = Vec::new();
|
||||
for line in s.lines() {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() >= 2 {
|
||||
urls.push(parts[1].to_string());
|
||||
}
|
||||
}
|
||||
if !urls.is_empty() {
|
||||
return uniq(urls);
|
||||
if let Ok(ok) = out
|
||||
&& ok.status.success()
|
||||
{
|
||||
let s = String::from_utf8_lossy(&ok.stdout);
|
||||
let mut urls = Vec::new();
|
||||
for line in s.lines() {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() >= 2 {
|
||||
urls.push(parts[1].to_string());
|
||||
}
|
||||
}
|
||||
if !urls.is_empty() {
|
||||
return uniq(urls);
|
||||
}
|
||||
}
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
+62
-113
@@ -29,47 +29,7 @@ pub(crate) fn append_error_log(message: impl AsRef<str>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Summarize a unified or codex-style patch into counts for UI display.
|
||||
fn summarize_diff(patch: &str) -> codex_cloud_tasks_api::DiffSummary {
|
||||
// Count +/- lines via simple prefix scan (skip headers)
|
||||
let (mut adds, mut dels) = (0usize, 0usize);
|
||||
for line in patch.lines() {
|
||||
if line.starts_with("+++") || line.starts_with("---") || line.starts_with("@@") {
|
||||
continue;
|
||||
}
|
||||
match line.as_bytes().first().copied() {
|
||||
Some(b'+') => adds += 1,
|
||||
Some(b'-') => dels += 1,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Count files: prefer codex patch file ops, else git diff headers, else infer 1 if any changes.
|
||||
let mut files = 0usize;
|
||||
for line in patch.lines() {
|
||||
if line.starts_with("*** Add File:")
|
||||
|| line.starts_with("*** Update File:")
|
||||
|| line.starts_with("*** Delete File:")
|
||||
{
|
||||
files += 1;
|
||||
}
|
||||
}
|
||||
if files == 0 {
|
||||
files = patch
|
||||
.lines()
|
||||
.filter(|l| l.starts_with("diff --git "))
|
||||
.count();
|
||||
}
|
||||
if files == 0 && (adds > 0 || dels > 0) {
|
||||
files = 1;
|
||||
}
|
||||
|
||||
codex_cloud_tasks_api::DiffSummary {
|
||||
files_changed: files,
|
||||
lines_added: adds,
|
||||
lines_removed: dels,
|
||||
}
|
||||
}
|
||||
// (no standalone patch summarizer needed – UI displays raw diffs)
|
||||
|
||||
/// Entry point for the `codex cloud-tasks` subcommand.
|
||||
pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()> {
|
||||
@@ -211,7 +171,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
// New list generation; reset background enrichment coordination
|
||||
app.list_generation = app.list_generation.saturating_add(1);
|
||||
app.in_flight.clear();
|
||||
app.no_diff_yet.clear();
|
||||
// reset any in-flight enrichment state
|
||||
|
||||
// Event stream
|
||||
use crossterm::event::Event;
|
||||
@@ -266,21 +226,22 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> 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 && !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);
|
||||
}
|
||||
if let Some(auth) = am.auth()
|
||||
&& 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,22 +283,23 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
codex_login::AuthMode::ChatGPT,
|
||||
"codex_cloud_tasks_tui".to_string(),
|
||||
);
|
||||
if let Some(auth) = am.auth() {
|
||||
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);
|
||||
}
|
||||
if let Some(auth) = am.auth()
|
||||
&& 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -388,7 +350,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
let _ = frame_tx.send(Instant::now());
|
||||
|
||||
// Render helper to centralize immediate redraws after handling events.
|
||||
let mut render_if_needed = |terminal: &mut Terminal<CrosstermBackend<std::io::Stdout>>,
|
||||
let render_if_needed = |terminal: &mut Terminal<CrosstermBackend<std::io::Stdout>>,
|
||||
app: &mut app::App,
|
||||
needs_redraw: &mut bool|
|
||||
-> anyhow::Result<()> {
|
||||
@@ -480,22 +442,12 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
}
|
||||
}
|
||||
}
|
||||
app::AppEvent::TaskSummaryUpdated { generation, id, summary, no_diff_yet, environment_id: _ } => {
|
||||
// Ignore stale generations
|
||||
if generation != app.list_generation { continue; }
|
||||
let id_str = id.0.clone();
|
||||
if let Some(t) = app.tasks.iter_mut().find(|t| t.id.0 == id_str) {
|
||||
t.summary = summary.clone();
|
||||
}
|
||||
app.summary_cache.insert(id_str.clone(), (summary, std::time::Instant::now()));
|
||||
if no_diff_yet { app.no_diff_yet.insert(id_str); } else { app.no_diff_yet.remove(&id.0); }
|
||||
needs_redraw = true;
|
||||
let _ = frame_tx.send(Instant::now());
|
||||
}
|
||||
// (removed TaskSummaryUpdated; unused in this prototype)
|
||||
app::AppEvent::ApplyPreflightFinished { id, title, message, level, skipped, conflicts } => {
|
||||
// Only update if modal is still open and ids match
|
||||
if let Some(m) = app.apply_modal.as_mut() {
|
||||
if m.task_id == id {
|
||||
if let Some(m) = app.apply_modal.as_mut()
|
||||
&& m.task_id == id
|
||||
{
|
||||
m.title = title;
|
||||
m.result_message = Some(message);
|
||||
m.result_level = Some(level);
|
||||
@@ -504,7 +456,6 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
app.apply_preflight_inflight = false;
|
||||
needs_redraw = true;
|
||||
let _ = frame_tx.send(Instant::now());
|
||||
}
|
||||
}
|
||||
}
|
||||
app::AppEvent::EnvironmentsLoaded(result) => {
|
||||
@@ -543,7 +494,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
app.refresh_inflight = true;
|
||||
app.list_generation = app.list_generation.saturating_add(1);
|
||||
app.in_flight.clear();
|
||||
app.no_diff_yet.clear();
|
||||
// reset spinner state
|
||||
needs_redraw = true;
|
||||
let backend2 = backend.clone();
|
||||
let tx2 = tx.clone();
|
||||
@@ -571,15 +522,16 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> 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 && !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);
|
||||
}
|
||||
if let Some(auth) = am.auth()
|
||||
&& 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -593,7 +545,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
}
|
||||
app::AppEvent::DetailsDiffLoaded { id, title, diff } => {
|
||||
// Only update if the overlay still corresponds to this id.
|
||||
if let Some(ov) = &app.diff_overlay { if ov.task_id != id { continue; } }
|
||||
if let Some(ov) = &app.diff_overlay && ov.task_id != id { continue; }
|
||||
let mut sd = crate::scrollable_diff::ScrollableDiff::new();
|
||||
sd.set_content(diff.lines().map(|s| s.to_string()).collect());
|
||||
app.diff_overlay = Some(app::DiffOverlay{ title, task_id: id, sd, can_apply: true });
|
||||
@@ -602,7 +554,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
needs_redraw = true;
|
||||
}
|
||||
app::AppEvent::DetailsMessagesLoaded { id, title, messages } => {
|
||||
if let Some(ov) = &app.diff_overlay { if ov.task_id != id { continue; } }
|
||||
if let Some(ov) = &app.diff_overlay && ov.task_id != id { continue; }
|
||||
let mut lines = Vec::new();
|
||||
for m in messages { lines.extend(m.lines().map(|s| s.to_string())); lines.push(String::new()); }
|
||||
if lines.is_empty() { lines.push("<no output>".to_string()); }
|
||||
@@ -614,7 +566,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
needs_redraw = true;
|
||||
}
|
||||
app::AppEvent::DetailsFailed { id, title, error } => {
|
||||
if let Some(ov) = &app.diff_overlay { if ov.task_id != id { continue; } }
|
||||
if let Some(ov) = &app.diff_overlay && ov.task_id != id { continue; }
|
||||
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();
|
||||
@@ -777,7 +729,6 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
KeyCode::Char('y') => {
|
||||
if let Some(m) = app.apply_modal.take() {
|
||||
app.status = format!("Applying '{}'...", m.title);
|
||||
needs_redraw = true;
|
||||
match codex_cloud_tasks_api::CloudBackend::apply_task(&*backend, m.task_id.clone()).await {
|
||||
Ok(outcome) => {
|
||||
app.status = outcome.message.clone();
|
||||
@@ -791,7 +742,6 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
app.status = format!("Apply failed: {e}");
|
||||
}
|
||||
}
|
||||
needs_redraw = true;
|
||||
}
|
||||
}
|
||||
KeyCode::Char('p') => {
|
||||
@@ -990,7 +940,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
// Resolve selection over filtered set
|
||||
if let Some(state) = app.env_modal.take() {
|
||||
let q = state.query.to_lowercase();
|
||||
let mut filtered: Vec<&app::EnvironmentRow> = app.environments.iter().filter(|r| {
|
||||
let filtered: Vec<&app::EnvironmentRow> = app.environments.iter().filter(|r| {
|
||||
if q.is_empty() { return true; }
|
||||
let mut hay = String::new();
|
||||
if let Some(l) = &r.label { hay.push_str(&l.to_lowercase()); hay.push(' '); }
|
||||
@@ -1021,7 +971,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
app.refresh_inflight = true;
|
||||
app.list_generation = app.list_generation.saturating_add(1);
|
||||
app.in_flight.clear();
|
||||
app.no_diff_yet.clear();
|
||||
// reset spinner state
|
||||
needs_redraw = true;
|
||||
let backend2 = backend.clone();
|
||||
let tx2 = tx.clone();
|
||||
@@ -1059,7 +1009,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> a
|
||||
app.refresh_inflight = true;
|
||||
app.list_generation = app.list_generation.saturating_add(1);
|
||||
app.in_flight.clear();
|
||||
app.no_diff_yet.clear();
|
||||
// reset spinner state
|
||||
needs_redraw = true;
|
||||
// Spawn background refresh
|
||||
let backend2 = backend.clone();
|
||||
@@ -1251,8 +1201,9 @@ fn pretty_lines_from_error(raw: &str) -> Vec<String> {
|
||||
}
|
||||
|
||||
// Try to parse the embedded JSON body: find the first '{' after " body=" and decode.
|
||||
if let Some(body_idx) = raw.find(" body=") {
|
||||
if let Some(json_start_rel) = raw[body_idx..].find('{') {
|
||||
if let Some(body_idx) = raw.find(" body=")
|
||||
&& let Some(json_start_rel) = raw[body_idx..].find('{')
|
||||
{
|
||||
let json_start = body_idx + json_start_rel;
|
||||
let json_str = raw[json_start..].trim();
|
||||
if let Ok(v) = serde_json::from_str::<serde_json::Value>(json_str) {
|
||||
@@ -1288,15 +1239,13 @@ fn pretty_lines_from_error(raw: &str) -> Vec<String> {
|
||||
.get("latest_event")
|
||||
.and_then(|e| e.get("text"))
|
||||
.and_then(|s| s.as_str())
|
||||
&& !text.trim().is_empty()
|
||||
{
|
||||
if !text.trim().is_empty() {
|
||||
lines.push(format!("Latest event: {}", text.trim()));
|
||||
}
|
||||
lines.push(format!("Latest event: {}", text.trim()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if lines.len() == 1 {
|
||||
// Parsing yielded nothing; include a trimmed, short raw message tail for context.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use codex_tui::ComposerAction;
|
||||
use codex_tui::ComposerInput;
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -24,7 +23,5 @@ impl NewTaskPage {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_submit(&self) -> bool {
|
||||
self.env_id.is_some() && !self.submitting
|
||||
}
|
||||
// Additional helpers can be added as usage evolves.
|
||||
}
|
||||
|
||||
@@ -144,15 +144,11 @@ impl ScrollableDiff {
|
||||
out.push(prefix.trim_end().to_string());
|
||||
out_idx.push(raw_idx);
|
||||
line = rest.trim_start().to_string();
|
||||
line_cols = UnicodeWidthStr::width(line.as_str());
|
||||
last_soft_idx = None;
|
||||
// retry add current ch now that line may be shorter
|
||||
} else {
|
||||
if !line.is_empty() {
|
||||
out.push(std::mem::take(&mut line));
|
||||
out_idx.push(raw_idx);
|
||||
line_cols = 0;
|
||||
}
|
||||
} else if !line.is_empty() {
|
||||
out.push(std::mem::take(&mut line));
|
||||
out_idx.push(raw_idx);
|
||||
}
|
||||
}
|
||||
if ch.is_whitespace()
|
||||
|
||||
@@ -150,10 +150,10 @@ 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_position((x, y));
|
||||
}
|
||||
if let Some(page) = app.new_task.as_ref()
|
||||
&& let Some((x, y)) = page.composer.cursor_pos(composer_area)
|
||||
{
|
||||
frame.set_cursor_position((x, y));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,22 +627,7 @@ fn draw_centered_spinner(
|
||||
draw_inline_spinner(frame, cols[1], state, label);
|
||||
}
|
||||
|
||||
fn style_diff_fragment(src_line: &str, fragment: &str) -> Line<'static> {
|
||||
if src_line.starts_with("@@") {
|
||||
return Line::from(fragment.to_string().magenta().bold());
|
||||
}
|
||||
if src_line.starts_with("diff --git ") || src_line.starts_with("index ") {
|
||||
return Line::from(fragment.to_string().dim());
|
||||
}
|
||||
if src_line.starts_with("+++") || src_line.starts_with("---") {
|
||||
return Line::from(fragment.to_string().dim());
|
||||
}
|
||||
match src_line.as_bytes().first().copied() {
|
||||
Some(b'+') => Line::from(fragment.to_string().green()),
|
||||
Some(b'-') => Line::from(fragment.to_string().red()),
|
||||
_ => Line::from(fragment.to_string()),
|
||||
}
|
||||
}
|
||||
// Styling helpers for diff rendering live inline where used.
|
||||
|
||||
pub fn draw_env_modal(frame: &mut Frame, area: Rect, app: &mut App) {
|
||||
use ratatui::widgets::Wrap;
|
||||
|
||||
@@ -5,7 +5,7 @@ use codex_cloud_tasks_client::MockClient;
|
||||
|
||||
#[tokio::test]
|
||||
async fn mock_backend_varies_by_env() {
|
||||
let client = MockClient::default();
|
||||
let client = MockClient;
|
||||
|
||||
let root = CloudBackend::list_tasks(&client, None).await.unwrap();
|
||||
assert!(root.iter().any(|t| t.title.contains("Update README")));
|
||||
|
||||
@@ -735,10 +735,7 @@ async fn includes_user_instructions_message_in_request() {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn azure_overrides_assign_properties_used_for_responses_url() {
|
||||
let (existing_env_var_with_random_value, existing_env_var_value) = std::env::vars()
|
||||
.find(|(_, value)| !value.trim().is_empty())
|
||||
.expect("system environment variable should exist");
|
||||
let auth_header_value = format!("Bearer {existing_env_var_value}");
|
||||
let existing_env_var_with_random_value = if cfg!(windows) { "USERNAME" } else { "USER" };
|
||||
|
||||
// Mock server
|
||||
let server = MockServer::start().await;
|
||||
@@ -753,7 +750,14 @@ async fn azure_overrides_assign_properties_used_for_responses_url() {
|
||||
.and(path("/openai/responses"))
|
||||
.and(query_param("api-version", "2025-04-01-preview"))
|
||||
.and(header_regex("Custom-Header", "Value"))
|
||||
.and(header_regex("Authorization", auth_header_value.as_str()))
|
||||
.and(header_regex(
|
||||
"Authorization",
|
||||
format!(
|
||||
"Bearer {}",
|
||||
std::env::var(existing_env_var_with_random_value).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
))
|
||||
.respond_with(first)
|
||||
.expect(1)
|
||||
.mount(&server)
|
||||
@@ -763,7 +767,7 @@ async fn azure_overrides_assign_properties_used_for_responses_url() {
|
||||
name: "custom".to_string(),
|
||||
base_url: Some(format!("{}/openai", server.uri())),
|
||||
// Reuse the existing environment variable to avoid using unsafe code
|
||||
env_key: Some(existing_env_var_with_random_value.clone()),
|
||||
env_key: Some(existing_env_var_with_random_value.to_string()),
|
||||
query_params: Some(std::collections::HashMap::from([(
|
||||
"api-version".to_string(),
|
||||
"2025-04-01-preview".to_string(),
|
||||
@@ -807,10 +811,7 @@ async fn azure_overrides_assign_properties_used_for_responses_url() {
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn env_var_overrides_loaded_auth() {
|
||||
let (existing_env_var_with_random_value, existing_env_var_value) = std::env::vars()
|
||||
.find(|(_, value)| !value.trim().is_empty())
|
||||
.expect("system environment variable should exist");
|
||||
let auth_header_value = format!("Bearer {existing_env_var_value}");
|
||||
let existing_env_var_with_random_value = if cfg!(windows) { "USERNAME" } else { "USER" };
|
||||
|
||||
// Mock server
|
||||
let server = MockServer::start().await;
|
||||
@@ -825,7 +826,14 @@ async fn env_var_overrides_loaded_auth() {
|
||||
.and(path("/openai/responses"))
|
||||
.and(query_param("api-version", "2025-04-01-preview"))
|
||||
.and(header_regex("Custom-Header", "Value"))
|
||||
.and(header_regex("Authorization", auth_header_value.as_str()))
|
||||
.and(header_regex(
|
||||
"Authorization",
|
||||
format!(
|
||||
"Bearer {}",
|
||||
std::env::var(existing_env_var_with_random_value).unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
))
|
||||
.respond_with(first)
|
||||
.expect(1)
|
||||
.mount(&server)
|
||||
@@ -835,7 +843,7 @@ async fn env_var_overrides_loaded_auth() {
|
||||
name: "custom".to_string(),
|
||||
base_url: Some(format!("{}/openai", server.uri())),
|
||||
// Reuse the existing environment variable to avoid using unsafe code
|
||||
env_key: Some(existing_env_var_with_random_value.clone()),
|
||||
env_key: Some(existing_env_var_with_random_value.to_string()),
|
||||
query_params: Some(std::collections::HashMap::from([(
|
||||
"api-version".to_string(),
|
||||
"2025-04-01-preview".to_string(),
|
||||
|
||||
@@ -180,7 +180,11 @@ impl CodexMessageProcessor {
|
||||
|
||||
let opts = LoginServerOptions {
|
||||
open_browser: false,
|
||||
..LoginServerOptions::new(config.codex_home.clone(), CLIENT_ID.to_string())
|
||||
..LoginServerOptions::new(
|
||||
config.codex_home.clone(),
|
||||
CLIENT_ID.to_string(),
|
||||
config.responses_originator_header.clone(),
|
||||
)
|
||||
};
|
||||
|
||||
enum LoginChatGptReply {
|
||||
|
||||
@@ -1261,7 +1261,7 @@ impl WidgetRef for ChatComposer {
|
||||
let mut out: Vec<Span> = Vec::new();
|
||||
for (i, (key, label)) in items.iter().enumerate() {
|
||||
out.push(Span::from(" "));
|
||||
out.push(key.as_str().set_style(key_hint_style));
|
||||
out.push(key_hint::plain(key));
|
||||
out.push(Span::from(format!(" {label}")));
|
||||
if i + 1 != items.len() {
|
||||
out.push(Span::from(" "));
|
||||
|
||||
@@ -324,13 +324,11 @@ async fn run_ratatui_app(
|
||||
if should_show_onboarding {
|
||||
let directory_trust_decision = run_onboarding_app(
|
||||
OnboardingScreenArgs {
|
||||
codex_home: config.codex_home.clone(),
|
||||
cwd: config.cwd.clone(),
|
||||
show_login_screen: should_show_login_screen(login_status, &config),
|
||||
show_trust_screen: should_show_trust_screen,
|
||||
login_status,
|
||||
preferred_auth_method: config.preferred_auth_method,
|
||||
auth_manager: auth_manager.clone(),
|
||||
config: config.clone(),
|
||||
},
|
||||
&mut tui,
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use codex_core::AuthManager;
|
||||
use codex_core::auth::CLIENT_ID;
|
||||
use codex_core::config::Config;
|
||||
use codex_login::ServerOptions;
|
||||
use codex_login::ShutdownHandle;
|
||||
use codex_login::run_login_server;
|
||||
@@ -113,6 +114,7 @@ pub(crate) struct AuthModeWidget {
|
||||
pub login_status: LoginStatus,
|
||||
pub preferred_auth_method: AuthMode,
|
||||
pub auth_manager: Arc<AuthManager>,
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
impl AuthModeWidget {
|
||||
@@ -314,7 +316,11 @@ impl AuthModeWidget {
|
||||
}
|
||||
|
||||
self.error = None;
|
||||
let opts = ServerOptions::new(self.codex_home.clone(), CLIENT_ID.to_string());
|
||||
let opts = ServerOptions::new(
|
||||
self.codex_home.clone(),
|
||||
CLIENT_ID.to_string(),
|
||||
self.config.responses_originator_header.clone(),
|
||||
);
|
||||
match run_login_server(opts) {
|
||||
Ok(child) => {
|
||||
let sign_in_state = self.sign_in_state.clone();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use codex_core::AuthManager;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::git_info::get_git_repo_root;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
@@ -21,7 +22,6 @@ use crate::tui::FrameRequester;
|
||||
use crate::tui::Tui;
|
||||
use crate::tui::TuiEvent;
|
||||
use color_eyre::eyre::Result;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
@@ -53,26 +53,25 @@ pub(crate) struct OnboardingScreen {
|
||||
}
|
||||
|
||||
pub(crate) struct OnboardingScreenArgs {
|
||||
pub codex_home: PathBuf,
|
||||
pub cwd: PathBuf,
|
||||
pub show_trust_screen: bool,
|
||||
pub show_login_screen: bool,
|
||||
pub login_status: LoginStatus,
|
||||
pub preferred_auth_method: AuthMode,
|
||||
pub auth_manager: Arc<AuthManager>,
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
impl OnboardingScreen {
|
||||
pub(crate) fn new(tui: &mut Tui, args: OnboardingScreenArgs) -> Self {
|
||||
let OnboardingScreenArgs {
|
||||
codex_home,
|
||||
cwd,
|
||||
show_trust_screen,
|
||||
show_login_screen,
|
||||
login_status,
|
||||
preferred_auth_method,
|
||||
auth_manager,
|
||||
config,
|
||||
} = args;
|
||||
let preferred_auth_method = config.preferred_auth_method;
|
||||
let cwd = config.cwd.clone();
|
||||
let codex_home = config.codex_home.clone();
|
||||
let mut steps: Vec<Step> = vec![Step::Welcome(WelcomeWidget {
|
||||
is_logged_in: !matches!(login_status, LoginStatus::NotAuthenticated),
|
||||
})];
|
||||
@@ -84,8 +83,9 @@ impl OnboardingScreen {
|
||||
sign_in_state: Arc::new(RwLock::new(SignInState::PickMode)),
|
||||
codex_home: codex_home.clone(),
|
||||
login_status,
|
||||
preferred_auth_method,
|
||||
auth_manager,
|
||||
preferred_auth_method,
|
||||
config,
|
||||
}))
|
||||
}
|
||||
let is_git_repo = get_git_repo_root(&cwd).is_some();
|
||||
|
||||
Reference in New Issue
Block a user