mirror of
https://github.com/openai/codex.git
synced 2026-06-01 19:02:59 +00:00
cleanup
This commit is contained in:
@@ -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(),
|
||||
|
||||
@@ -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}");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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::<serde_json::Value>(&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() {
|
||||
|
||||
@@ -28,7 +28,7 @@ pub async fn autodetect_environment_id(
|
||||
) -> anyhow::Result<AutodetectSelection> {
|
||||
// 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<CodeEnvironment> = 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::<Vec<CodeEnvironment>>(&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::<serde_json::Value>(&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<CodeEnvironment> = 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<T: serde::de::DeserializeOwned>(
|
||||
.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::<T>(&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::<Vec<CodeEnvironment>>(&list_url, headers).await {
|
||||
Ok(list) => {
|
||||
|
||||
@@ -112,7 +112,7 @@ pub async fn run_main(_cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<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 { 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<PathBuf>) -> 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<String> {
|
||||
} 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")
|
||||
|
||||
@@ -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<ratatui::text::Span> = 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<Line> = 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<Utc>) -> 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)
|
||||
|
||||
Reference in New Issue
Block a user