mirror of
https://github.com/openai/codex.git
synced 2026-11-01 19:33:35 +00:00
This does not implement the full Login with ChatGPT experience, but it should unblock people. **What works** * The `codex` multitool now has a `login` subcommand, so you can run `codex login`, which should write `CODEX_HOME/auth.json` if you complete the flow successfully. The TUI will now read the `OPENAI_API_KEY` from `auth.json`. * The TUI should refresh the token if it has expired and the necessary information is in `auth.json`. * There is a `LoginScreen` in the TUI that tells you to run `codex login` if both (1) your model provider expects to use `OPENAI_API_KEY` as its env var, and (2) `OPENAI_API_KEY` is not set. **What does not work** * The `LoginScreen` does not support the login flow from within the TUI. Instead, it tells you to quit, run `codex login`, and then run `codex` again. * `codex exec` does read from `auth.json` yet, nor does it direct the user to go through the login flow if `OPENAI_API_KEY` is not be found. * The `maybeRedeemCredits()` function from `get-api-key.tsx` has not been ported from TypeScript to `login_with_chatgpt.py` yet: https://github.com/openai/codex/blob/a67a67f3258fc21e147b6786a143fe3e15e6d5ba/codex-cli/src/utils/get-api-key.tsx#L84-L89 **Implementation** Currently, the OAuth flow requires running a local webserver on `127.0.0.1:1455`. It seemed wasteful to incur the additional binary cost of a webserver dependency in the Rust CLI just to support login, so instead we implement this logic in Python, as Python has a `http.server` module as part of its standard library. Specifically, we bundle the contents of a single Python file as a string in the Rust CLI and then use it to spawn a subprocess as `python3 -c {{SOURCE_FOR_PYTHON_SERVER}}`. As such, the most significant files in this PR are: ``` codex-rs/login/src/login_with_chatgpt.py codex-rs/login/src/lib.rs ``` Now that the CLI may load `OPENAI_API_KEY` from the environment _or_ `CODEX_HOME/auth.json`, we need a new abstraction for reading/writing this variable, so we introduce: ``` codex-rs/core/src/openai_api_key.rs ``` Note that `std::env::set_var()` is [rightfully] `unsafe` in Rust 2024, so we use a LazyLock<RwLock<Option<String>>> to store `OPENAI_API_KEY` so it is read in a thread-safe manner. Ultimately, it should be possible to go through the entire login flow from the TUI. This PR introduces a placeholder `LoginScreen` UI for that right now, though the new `codex login` subcommand introduced in this PR should be a viable workaround until the UI is ready. **Testing** Because the login flow is currently implemented in a standalone Python file, you can test it without building any Rust code as follows: ``` rm -rf /tmp/codex_home && mkdir /tmp/codex_home CODEX_HOME=/tmp/codex_home python3 codex-rs/login/src/login_with_chatgpt.py ``` For reference: * the original TypeScript implementation was introduced in https://github.com/openai/codex/pull/963 * support for redeeming credits was later added in https://github.com/openai/codex/pull/974
47 lines
1.2 KiB
Rust
47 lines
1.2 KiB
Rust
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);
|
|
}
|
|
}
|