Show ChatGPT login URL during onboarding (#2028)
## Summary - display authentication URL in the ChatGPT sign-in screen while onboarding <img width="684" height="151" alt="image" src="https://github.com/user-attachments/assets/a8c32cb0-77f6-4a3f-ae3b-6695247c994d" />
This commit is contained in:
@@ -9,6 +9,7 @@ use std::fs::OpenOptions;
|
||||
use std::fs::remove_file;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::io::{self};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::path::Path;
|
||||
@@ -262,6 +263,50 @@ pub struct SpawnedLogin {
|
||||
pub stderr: Arc<Mutex<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl SpawnedLogin {
|
||||
/// Returns the login URL, if one has been emitted by the login subprocess.
|
||||
///
|
||||
/// The Python helper prints the URL to stderr; we capture it and extract
|
||||
/// the last whitespace-separated token that starts with "http".
|
||||
pub fn get_login_url(&self) -> Option<String> {
|
||||
self.stderr
|
||||
.lock()
|
||||
.ok()
|
||||
.and_then(|buffer| String::from_utf8(buffer.clone()).ok())
|
||||
.and_then(|output| {
|
||||
output
|
||||
.split_whitespace()
|
||||
.filter(|part| part.starts_with("http"))
|
||||
.next_back()
|
||||
.map(|s| s.to_string())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers for streaming child output into shared buffers
|
||||
struct AppendWriter {
|
||||
buf: Arc<Mutex<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl Write for AppendWriter {
|
||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
if let Ok(mut b) = self.buf.lock() {
|
||||
b.extend_from_slice(data);
|
||||
}
|
||||
Ok(data.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_pipe_reader<R: Read + Send + 'static>(mut reader: R, buf: Arc<Mutex<Vec<u8>>>) {
|
||||
std::thread::spawn(move || {
|
||||
let _ = io::copy(&mut reader, &mut AppendWriter { buf });
|
||||
});
|
||||
}
|
||||
|
||||
/// Spawn the ChatGPT login Python server as a child process and return a handle to its process.
|
||||
pub fn spawn_login_with_chatgpt(codex_home: &Path) -> std::io::Result<SpawnedLogin> {
|
||||
let script_path = write_login_script_to_disk()?;
|
||||
@@ -278,25 +323,11 @@ pub fn spawn_login_with_chatgpt(codex_home: &Path) -> std::io::Result<SpawnedLog
|
||||
let stdout_buf = Arc::new(Mutex::new(Vec::new()));
|
||||
let stderr_buf = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
if let Some(mut out) = child.stdout.take() {
|
||||
let buf = stdout_buf.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut tmp = Vec::new();
|
||||
let _ = std::io::copy(&mut out, &mut tmp);
|
||||
if let Ok(mut b) = buf.lock() {
|
||||
b.extend_from_slice(&tmp);
|
||||
}
|
||||
});
|
||||
if let Some(out) = child.stdout.take() {
|
||||
spawn_pipe_reader(out, stdout_buf.clone());
|
||||
}
|
||||
if let Some(mut err) = child.stderr.take() {
|
||||
let buf = stderr_buf.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut tmp = Vec::new();
|
||||
let _ = std::io::copy(&mut err, &mut tmp);
|
||||
if let Ok(mut b) = buf.lock() {
|
||||
b.extend_from_slice(&tmp);
|
||||
}
|
||||
});
|
||||
if let Some(err) = child.stderr.take() {
|
||||
spawn_pipe_reader(err, stderr_buf.clone());
|
||||
}
|
||||
|
||||
Ok(SpawnedLogin {
|
||||
|
||||
Reference in New Issue
Block a user