mirror of
https://github.com/openai/codex.git
synced 2026-04-28 08:34:54 +00:00
544 lines
20 KiB
Markdown
544 lines
20 KiB
Markdown
# PR #1713: replace login screen with a simple prompt
|
||
|
||
- URL: https://github.com/openai/codex/pull/1713
|
||
- Author: nornagon-openai
|
||
- Created: 2025-07-28 21:44:12 UTC
|
||
- Updated: 2025-07-29 00:25:21 UTC
|
||
- Changes: +47/-107, Files changed: 6, Commits: 7
|
||
|
||
## Description
|
||
|
||
Perhaps there was an intention to make the login screen prettier, but it feels quite silly right now to just have a screen that says "press q", so replace it with something that lets the user directly login without having to quit the app.
|
||
|
||
<img width="1283" height="635" alt="Screenshot 2025-07-28 at 2 54 05 PM" src="https://github.com/user-attachments/assets/f19e5595-6ef9-4a2d-b409-aa61b30d3628" />
|
||
|
||
## Full Diff
|
||
|
||
```diff
|
||
diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs
|
||
index efda03bda4..6dd596ff9f 100644
|
||
--- a/codex-rs/cli/src/main.rs
|
||
+++ b/codex-rs/cli/src/main.rs
|
||
@@ -106,7 +106,7 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
|
||
None => {
|
||
let mut tui_cli = cli.interactive;
|
||
prepend_config_flags(&mut tui_cli.config_overrides, cli.config_overrides);
|
||
- let usage = codex_tui::run_main(tui_cli, codex_linux_sandbox_exe)?;
|
||
+ let usage = codex_tui::run_main(tui_cli, codex_linux_sandbox_exe).await?;
|
||
println!("{}", codex_core::protocol::FinalOutput::from(usage));
|
||
}
|
||
Some(Subcommand::Exec(mut exec_cli)) => {
|
||
diff --git a/codex-rs/login/src/lib.rs b/codex-rs/login/src/lib.rs
|
||
index 99d2f7f983..ab92ecf616 100644
|
||
--- a/codex-rs/login/src/lib.rs
|
||
+++ b/codex-rs/login/src/lib.rs
|
||
@@ -9,6 +9,7 @@ use std::io::Write;
|
||
use std::os::unix::fs::OpenOptionsExt;
|
||
use std::path::Path;
|
||
use std::process::Stdio;
|
||
+use std::time::Duration;
|
||
use tokio::process::Command;
|
||
|
||
const SOURCE_FOR_PYTHON_SERVER: &str = include_str!("./login_with_chatgpt.py");
|
||
@@ -73,7 +74,11 @@ pub async fn try_read_auth_json(codex_home: &Path) -> std::io::Result<AuthDotJso
|
||
let auth_dot_json: AuthDotJson = serde_json::from_str(&contents)?;
|
||
|
||
if is_expired(&auth_dot_json) {
|
||
- let refresh_response = try_refresh_token(&auth_dot_json).await?;
|
||
+ let refresh_response =
|
||
+ tokio::time::timeout(Duration::from_secs(60), try_refresh_token(&auth_dot_json))
|
||
+ .await
|
||
+ .map_err(|_| std::io::Error::other("timed out while refreshing OpenAI API key"))?
|
||
+ .map_err(std::io::Error::other)?;
|
||
let mut auth_dot_json = auth_dot_json;
|
||
auth_dot_json.tokens.id_token = refresh_response.id_token;
|
||
if let Some(refresh_token) = refresh_response.refresh_token {
|
||
diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs
|
||
index e7097e6af0..b671075ba8 100644
|
||
--- a/codex-rs/tui/src/app.rs
|
||
+++ b/codex-rs/tui/src/app.rs
|
||
@@ -5,7 +5,6 @@ use crate::file_search::FileSearchManager;
|
||
use crate::get_git_diff::get_git_diff;
|
||
use crate::git_warning_screen::GitWarningOutcome;
|
||
use crate::git_warning_screen::GitWarningScreen;
|
||
-use crate::login_screen::LoginScreen;
|
||
use crate::scroll_event_helper::ScrollEventHelper;
|
||
use crate::slash_command::SlashCommand;
|
||
use crate::tui;
|
||
@@ -37,8 +36,6 @@ enum AppState<'a> {
|
||
/// `AppState`.
|
||
widget: Box<ChatWidget<'a>>,
|
||
},
|
||
- /// The login screen for the OpenAI provider.
|
||
- Login { screen: LoginScreen },
|
||
/// The start-up warning that recommends running codex inside a Git repo.
|
||
GitWarning { screen: GitWarningScreen },
|
||
}
|
||
@@ -74,7 +71,6 @@ impl App<'_> {
|
||
pub(crate) fn new(
|
||
config: Config,
|
||
initial_prompt: Option<String>,
|
||
- show_login_screen: bool,
|
||
show_git_warning: bool,
|
||
initial_images: Vec<std::path::PathBuf>,
|
||
) -> Self {
|
||
@@ -138,18 +134,7 @@ impl App<'_> {
|
||
});
|
||
}
|
||
|
||
- let (app_state, chat_args) = if show_login_screen {
|
||
- (
|
||
- AppState::Login {
|
||
- screen: LoginScreen::new(app_event_tx.clone(), config.codex_home.clone()),
|
||
- },
|
||
- Some(ChatWidgetArgs {
|
||
- config: config.clone(),
|
||
- initial_prompt,
|
||
- initial_images,
|
||
- }),
|
||
- )
|
||
- } else if show_git_warning {
|
||
+ let (app_state, chat_args) = if show_git_warning {
|
||
(
|
||
AppState::GitWarning {
|
||
screen: GitWarningScreen::new(),
|
||
@@ -243,7 +228,7 @@ impl App<'_> {
|
||
AppState::Chat { widget } => {
|
||
widget.on_ctrl_c();
|
||
}
|
||
- AppState::Login { .. } | AppState::GitWarning { .. } => {
|
||
+ AppState::GitWarning { .. } => {
|
||
// No-op.
|
||
}
|
||
}
|
||
@@ -264,7 +249,7 @@ impl App<'_> {
|
||
self.dispatch_key_event(key_event);
|
||
}
|
||
}
|
||
- AppState::Login { .. } | AppState::GitWarning { .. } => {
|
||
+ AppState::GitWarning { .. } => {
|
||
self.app_event_tx.send(AppEvent::ExitRequest);
|
||
}
|
||
}
|
||
@@ -288,11 +273,11 @@ impl App<'_> {
|
||
}
|
||
AppEvent::CodexOp(op) => match &mut self.app_state {
|
||
AppState::Chat { widget } => widget.submit_op(op),
|
||
- AppState::Login { .. } | AppState::GitWarning { .. } => {}
|
||
+ AppState::GitWarning { .. } => {}
|
||
},
|
||
AppEvent::LatestLog(line) => match &mut self.app_state {
|
||
AppState::Chat { widget } => widget.update_latest_log(line),
|
||
- AppState::Login { .. } | AppState::GitWarning { .. } => {}
|
||
+ AppState::GitWarning { .. } => {}
|
||
},
|
||
AppEvent::DispatchCommand(command) => match command {
|
||
SlashCommand::New => {
|
||
@@ -348,9 +333,7 @@ impl App<'_> {
|
||
pub(crate) fn token_usage(&self) -> codex_core::protocol::TokenUsage {
|
||
match &self.app_state {
|
||
AppState::Chat { widget } => widget.token_usage().clone(),
|
||
- AppState::Login { .. } | AppState::GitWarning { .. } => {
|
||
- codex_core::protocol::TokenUsage::default()
|
||
- }
|
||
+ AppState::GitWarning { .. } => codex_core::protocol::TokenUsage::default(),
|
||
}
|
||
}
|
||
|
||
@@ -361,9 +344,6 @@ impl App<'_> {
|
||
AppState::Chat { widget } => {
|
||
terminal.draw(|frame| frame.render_widget_ref(&**widget, frame.area()))?;
|
||
}
|
||
- AppState::Login { screen } => {
|
||
- terminal.draw(|frame| frame.render_widget_ref(&*screen, frame.area()))?;
|
||
- }
|
||
AppState::GitWarning { screen } => {
|
||
terminal.draw(|frame| frame.render_widget_ref(&*screen, frame.area()))?;
|
||
}
|
||
@@ -378,7 +358,6 @@ impl App<'_> {
|
||
AppState::Chat { widget } => {
|
||
widget.handle_key_event(key_event);
|
||
}
|
||
- AppState::Login { screen } => screen.handle_key_event(key_event),
|
||
AppState::GitWarning { screen } => match screen.handle_key_event(key_event) {
|
||
GitWarningOutcome::Continue => {
|
||
// User accepted – switch to chat view.
|
||
@@ -409,21 +388,21 @@ impl App<'_> {
|
||
fn dispatch_paste_event(&mut self, pasted: String) {
|
||
match &mut self.app_state {
|
||
AppState::Chat { widget } => widget.handle_paste(pasted),
|
||
- AppState::Login { .. } | AppState::GitWarning { .. } => {}
|
||
+ AppState::GitWarning { .. } => {}
|
||
}
|
||
}
|
||
|
||
fn dispatch_scroll_event(&mut self, scroll_delta: i32) {
|
||
match &mut self.app_state {
|
||
AppState::Chat { widget } => widget.handle_scroll_delta(scroll_delta),
|
||
- AppState::Login { .. } | AppState::GitWarning { .. } => {}
|
||
+ AppState::GitWarning { .. } => {}
|
||
}
|
||
}
|
||
|
||
fn dispatch_codex_event(&mut self, event: Event) {
|
||
match &mut self.app_state {
|
||
AppState::Chat { widget } => widget.handle_codex_event(event),
|
||
- AppState::Login { .. } | AppState::GitWarning { .. } => {}
|
||
+ AppState::GitWarning { .. } => {}
|
||
}
|
||
}
|
||
}
|
||
diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs
|
||
index 905f0aaf0b..1f660b1aaf 100644
|
||
--- a/codex-rs/tui/src/lib.rs
|
||
+++ b/codex-rs/tui/src/lib.rs
|
||
@@ -14,6 +14,7 @@ use codex_core::util::is_inside_git_repo;
|
||
use codex_login::try_read_openai_api_key;
|
||
use log_layer::TuiLogLayer;
|
||
use std::fs::OpenOptions;
|
||
+use std::io::Write;
|
||
use std::path::PathBuf;
|
||
use tracing_appender::non_blocking;
|
||
use tracing_subscriber::EnvFilter;
|
||
@@ -35,7 +36,6 @@ mod git_warning_screen;
|
||
mod history_cell;
|
||
mod insert_history;
|
||
mod log_layer;
|
||
-mod login_screen;
|
||
mod markdown;
|
||
mod scroll_event_helper;
|
||
mod slash_command;
|
||
@@ -47,7 +47,7 @@ mod user_approval_widget;
|
||
|
||
pub use cli::Cli;
|
||
|
||
-pub fn run_main(
|
||
+pub async fn run_main(
|
||
cli: Cli,
|
||
codex_linux_sandbox_exe: Option<PathBuf>,
|
||
) -> std::io::Result<codex_core::protocol::TokenUsage> {
|
||
@@ -142,7 +142,25 @@ pub fn run_main(
|
||
.with(tui_layer)
|
||
.try_init();
|
||
|
||
- let show_login_screen = should_show_login_screen(&config);
|
||
+ let show_login_screen = should_show_login_screen(&config).await;
|
||
+ if show_login_screen {
|
||
+ std::io::stdout().write_all(
|
||
+ b"Oh dear, we don't seem to have an API key.\nTerribly sorry, but may I open a browser window for you to log in? [Yn] ",
|
||
+ )?;
|
||
+ std::io::stdout().flush()?;
|
||
+ let mut input = String::new();
|
||
+ std::io::stdin().read_line(&mut input)?;
|
||
+ let trimmed = input.trim();
|
||
+ if !(trimmed.is_empty() || trimmed.eq_ignore_ascii_case("y")) {
|
||
+ std::io::stdout().write_all(b"Right-o, fair enough. See you next time!\n")?;
|
||
+ std::process::exit(1);
|
||
+ }
|
||
+ // Spawn a task to run the login command.
|
||
+ // Block until the login command is finished.
|
||
+ let new_key = codex_login::login_with_chatgpt(&config.codex_home, false).await?;
|
||
+ set_openai_api_key(new_key);
|
||
+ std::io::stdout().write_all(b"Excellent, looks like that worked. Let's get started!\n")?;
|
||
+ }
|
||
|
||
// Determine whether we need to display the "not a git repo" warning
|
||
// modal. The flag is shown when the current working directory is *not*
|
||
@@ -150,14 +168,13 @@ pub fn run_main(
|
||
// `--allow-no-git-exec` flag.
|
||
let show_git_warning = !cli.skip_git_repo_check && !is_inside_git_repo(&config);
|
||
|
||
- run_ratatui_app(cli, config, show_login_screen, show_git_warning, log_rx)
|
||
+ run_ratatui_app(cli, config, show_git_warning, log_rx)
|
||
.map_err(|err| std::io::Error::other(err.to_string()))
|
||
}
|
||
|
||
fn run_ratatui_app(
|
||
cli: Cli,
|
||
config: Config,
|
||
- show_login_screen: bool,
|
||
show_git_warning: bool,
|
||
mut log_rx: tokio::sync::mpsc::UnboundedReceiver<String>,
|
||
) -> color_eyre::Result<codex_core::protocol::TokenUsage> {
|
||
@@ -172,13 +189,7 @@ fn run_ratatui_app(
|
||
terminal.clear()?;
|
||
|
||
let Cli { prompt, images, .. } = cli;
|
||
- let mut app = App::new(
|
||
- config.clone(),
|
||
- prompt,
|
||
- show_login_screen,
|
||
- show_git_warning,
|
||
- images,
|
||
- );
|
||
+ let mut app = App::new(config.clone(), prompt, show_git_warning, images);
|
||
|
||
// Bridge log receiver into the AppEvent channel so latest log lines update the UI.
|
||
{
|
||
@@ -210,26 +221,17 @@ fn restore() {
|
||
}
|
||
}
|
||
|
||
-#[allow(clippy::unwrap_used)]
|
||
-fn should_show_login_screen(config: &Config) -> bool {
|
||
+async fn should_show_login_screen(config: &Config) -> bool {
|
||
if is_in_need_of_openai_api_key(config) {
|
||
// Reading the OpenAI API key is an async operation because it may need
|
||
// to refresh the token. Block on it.
|
||
let codex_home = config.codex_home.clone();
|
||
- let (tx, rx) = tokio::sync::oneshot::channel();
|
||
- tokio::spawn(async move {
|
||
- match try_read_openai_api_key(&codex_home).await {
|
||
- Ok(openai_api_key) => {
|
||
- set_openai_api_key(openai_api_key);
|
||
- tx.send(false).unwrap();
|
||
- }
|
||
- Err(_) => {
|
||
- tx.send(true).unwrap();
|
||
- }
|
||
- }
|
||
- });
|
||
- // TODO(mbolin): Impose some sort of timeout.
|
||
- tokio::task::block_in_place(|| rx.blocking_recv()).unwrap()
|
||
+ if let Ok(openai_api_key) = try_read_openai_api_key(&codex_home).await {
|
||
+ set_openai_api_key(openai_api_key);
|
||
+ false
|
||
+ } else {
|
||
+ true
|
||
+ }
|
||
} else {
|
||
false
|
||
}
|
||
diff --git a/codex-rs/tui/src/login_screen.rs b/codex-rs/tui/src/login_screen.rs
|
||
deleted file mode 100644
|
||
index 1bd11c19d3..0000000000
|
||
--- a/codex-rs/tui/src/login_screen.rs
|
||
+++ /dev/null
|
||
@@ -1,46 +0,0 @@
|
||
-use std::path::PathBuf;
|
||
-
|
||
-use crossterm::event::KeyCode;
|
||
-use crossterm::event::KeyEvent;
|
||
-use ratatui::buffer::Buffer;
|
||
-use ratatui::layout::Rect;
|
||
-use ratatui::widgets::Paragraph;
|
||
-use ratatui::widgets::Widget as _;
|
||
-use ratatui::widgets::WidgetRef;
|
||
-
|
||
-use crate::app_event::AppEvent;
|
||
-use crate::app_event_sender::AppEventSender;
|
||
-
|
||
-pub(crate) struct LoginScreen {
|
||
- app_event_tx: AppEventSender,
|
||
-
|
||
- /// Use this with login_with_chatgpt() in login/src/lib.rs and, if
|
||
- /// successful, update the in-memory config via
|
||
- /// codex_core::openai_api_key::set_openai_api_key().
|
||
- #[allow(dead_code)]
|
||
- codex_home: PathBuf,
|
||
-}
|
||
-
|
||
-impl LoginScreen {
|
||
- pub(crate) fn new(app_event_tx: AppEventSender, codex_home: PathBuf) -> Self {
|
||
- Self {
|
||
- app_event_tx,
|
||
- codex_home,
|
||
- }
|
||
- }
|
||
-
|
||
- pub(crate) fn handle_key_event(&mut self, key_event: KeyEvent) {
|
||
- if let KeyCode::Char('q') = key_event.code {
|
||
- self.app_event_tx.send(AppEvent::ExitRequest);
|
||
- }
|
||
- }
|
||
-}
|
||
-
|
||
-impl WidgetRef for &LoginScreen {
|
||
- fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||
- let text = Paragraph::new(
|
||
- "Login using `codex login` and then run this command again. 'q' to quit.",
|
||
- );
|
||
- text.render(area, buf);
|
||
- }
|
||
-}
|
||
diff --git a/codex-rs/tui/src/main.rs b/codex-rs/tui/src/main.rs
|
||
index 480e56e88e..209febf035 100644
|
||
--- a/codex-rs/tui/src/main.rs
|
||
+++ b/codex-rs/tui/src/main.rs
|
||
@@ -21,7 +21,7 @@ fn main() -> anyhow::Result<()> {
|
||
.config_overrides
|
||
.raw_overrides
|
||
.splice(0..0, top_cli.config_overrides.raw_overrides);
|
||
- let usage = run_main(inner, codex_linux_sandbox_exe)?;
|
||
+ let usage = run_main(inner, codex_linux_sandbox_exe).await?;
|
||
println!("{}", codex_core::protocol::FinalOutput::from(usage));
|
||
Ok(())
|
||
})
|
||
```
|
||
|
||
## Review Comments
|
||
|
||
### codex-rs/tui/src/lib.rs
|
||
|
||
- Created: 2025-07-28 22:22:11 UTC | Link: https://github.com/openai/codex/pull/1713#discussion_r2237962280
|
||
|
||
```diff
|
||
@@ -142,22 +142,39 @@ pub fn run_main(
|
||
.with(tui_layer)
|
||
.try_init();
|
||
|
||
- let show_login_screen = should_show_login_screen(&config);
|
||
+ let show_login_screen = should_show_login_screen(&config).await;
|
||
+ if show_login_screen {
|
||
+ std::io::stdout().write_all(
|
||
+ b"Oh dear, we don't seem to have an API key.\nTerribly sorry, but may I open a browser window for you to log in? [Yn] ",
|
||
+ )?;
|
||
+ std::io::stdout().flush()?;
|
||
+ let mut input = String::new();
|
||
+ std::io::stdin().read_line(&mut input)?;
|
||
+ let trimmed = input.trim();
|
||
+ if !(trimmed.is_empty() || trimmed.eq_ignore_ascii_case("y")) {
|
||
```
|
||
|
||
> My mind wants to deMorgan this to...
|
||
>
|
||
> ```suggestion
|
||
> if !trimmed.is_empty() && !trimmed.eq_ignore_ascii_case("y") {
|
||
> ```
|
||
|
||
- Created: 2025-07-28 22:29:13 UTC | Link: https://github.com/openai/codex/pull/1713#discussion_r2237969504
|
||
|
||
```diff
|
||
@@ -47,7 +47,7 @@ mod user_approval_widget;
|
||
|
||
pub use cli::Cli;
|
||
|
||
-pub fn run_main(
|
||
+pub async fn run_main(
|
||
```
|
||
|
||
> Hmm, so I wasn't clear on how async-friendly Ratatui is, which is why I avoided it here.
|
||
>
|
||
> I guess this is fine?
|
||
|
||
- Created: 2025-07-28 22:30:21 UTC | Link: https://github.com/openai/codex/pull/1713#discussion_r2237970644
|
||
|
||
```diff
|
||
@@ -211,25 +222,23 @@ fn restore() {
|
||
}
|
||
|
||
#[allow(clippy::unwrap_used)]
|
||
-fn should_show_login_screen(config: &Config) -> bool {
|
||
+async fn should_show_login_screen(config: &Config) -> bool {
|
||
if is_in_need_of_openai_api_key(config) {
|
||
// Reading the OpenAI API key is an async operation because it may need
|
||
// to refresh the token. Block on it.
|
||
let codex_home = config.codex_home.clone();
|
||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||
- tokio::spawn(async move {
|
||
- match try_read_openai_api_key(&codex_home).await {
|
||
- Ok(openai_api_key) => {
|
||
- set_openai_api_key(openai_api_key);
|
||
- tx.send(false).unwrap();
|
||
- }
|
||
- Err(_) => {
|
||
- tx.send(true).unwrap();
|
||
- }
|
||
+ match try_read_openai_api_key(&codex_home).await {
|
||
```
|
||
|
||
> If you aren't going to `tokio::spawn()`, we don't need the `oneshot`, do we?
|
||
|
||
- Created: 2025-07-28 22:31:25 UTC | Link: https://github.com/openai/codex/pull/1713#discussion_r2237971705
|
||
|
||
```diff
|
||
@@ -211,25 +222,23 @@ fn restore() {
|
||
}
|
||
|
||
#[allow(clippy::unwrap_used)]
|
||
-fn should_show_login_screen(config: &Config) -> bool {
|
||
+async fn should_show_login_screen(config: &Config) -> bool {
|
||
if is_in_need_of_openai_api_key(config) {
|
||
// Reading the OpenAI API key is an async operation because it may need
|
||
// to refresh the token. Block on it.
|
||
let codex_home = config.codex_home.clone();
|
||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||
- tokio::spawn(async move {
|
||
- match try_read_openai_api_key(&codex_home).await {
|
||
- Ok(openai_api_key) => {
|
||
- set_openai_api_key(openai_api_key);
|
||
- tx.send(false).unwrap();
|
||
- }
|
||
- Err(_) => {
|
||
- tx.send(true).unwrap();
|
||
- }
|
||
+ match try_read_openai_api_key(&codex_home).await {
|
||
+ Ok(openai_api_key) => {
|
||
+ set_openai_api_key(openai_api_key);
|
||
+ tx.send(false).unwrap();
|
||
}
|
||
- });
|
||
+ Err(_) => {
|
||
+ tx.send(true).unwrap();
|
||
+ }
|
||
+ }
|
||
// TODO(mbolin): Impose some sort of timeout.
|
||
- tokio::task::block_in_place(|| rx.blocking_recv()).unwrap()
|
||
+ rx.await.unwrap()
|
||
```
|
||
|
||
> You can leverage `tokio::time::timeout` pretty easily to address this TODO.
|
||
|
||
- Created: 2025-07-28 23:49:20 UTC | Link: https://github.com/openai/codex/pull/1713#discussion_r2238040525
|
||
|
||
```diff
|
||
@@ -210,26 +222,25 @@ fn restore() {
|
||
}
|
||
}
|
||
|
||
-#[allow(clippy::unwrap_used)]
|
||
-fn should_show_login_screen(config: &Config) -> bool {
|
||
+#[allow(clippy::expect_used)]
|
||
+#[allow(clippy::print_stderr)]
|
||
+async fn should_show_login_screen(config: &Config) -> bool {
|
||
if is_in_need_of_openai_api_key(config) {
|
||
// Reading the OpenAI API key is an async operation because it may need
|
||
// to refresh the token. Block on it.
|
||
let codex_home = config.codex_home.clone();
|
||
- let (tx, rx) = tokio::sync::oneshot::channel();
|
||
- tokio::spawn(async move {
|
||
- match try_read_openai_api_key(&codex_home).await {
|
||
- Ok(openai_api_key) => {
|
||
- set_openai_api_key(openai_api_key);
|
||
- tx.send(false).unwrap();
|
||
- }
|
||
- Err(_) => {
|
||
- tx.send(true).unwrap();
|
||
- }
|
||
- }
|
||
- });
|
||
- // TODO(mbolin): Impose some sort of timeout.
|
||
- tokio::task::block_in_place(|| rx.blocking_recv()).unwrap()
|
||
+ if let Ok(openai_api_key) = tokio::time::timeout(
|
||
+ Duration::from_secs(60),
|
||
+ try_read_openai_api_key(&codex_home),
|
||
+ )
|
||
+ .await
|
||
+ .expect("timed out while refreshing OpenAI API key")
|
||
```
|
||
|
||
> If we timeout, we don't want to panic, do we?
|
||
|
||
- Created: 2025-07-29 00:07:38 UTC | Link: https://github.com/openai/codex/pull/1713#discussion_r2238055661
|
||
|
||
```diff
|
||
@@ -210,26 +221,19 @@ fn restore() {
|
||
}
|
||
}
|
||
|
||
-#[allow(clippy::unwrap_used)]
|
||
-fn should_show_login_screen(config: &Config) -> bool {
|
||
+#[allow(clippy::expect_used)]
|
||
```
|
||
|
||
> These are no longer necessary, correct? |