mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
merge: upstream/main into fork (prefer ours)\n\n- TUI: adopt upstream custom_terminal and auth widget; keep themes/browser/agents intact\n- Fix stable Rust let-chains across core/tui/cli/mcp-server\n- Align toolchains: build-fast.sh uses rustup (1.89.0 via rust-toolchain.toml); release.yml -> 1.89.0\n- core: file locking via fs2, remove std TryLockError usage\n- mcp-server: keep our impl; avoid upstream-only EventMsg variant\n- TUI fixes: selection list module/export, render_rows arg, history token usage, formatting utils\n- cli/login: remove preferred_auth_method, fix env var let-chain\n\nBuild: ./build-fast.sh passes with Rust 1.89.0
This commit is contained in:
@@ -60,19 +60,19 @@ pub async fn run_login_with_api_key(
|
||||
pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
let config = load_config_or_exit(cli_config_overrides);
|
||||
|
||||
match CodexAuth::from_codex_home(&config.codex_home, config.preferred_auth_method) {
|
||||
match CodexAuth::from_codex_home(&config.codex_home, AuthMode::ApiKey) {
|
||||
Ok(Some(auth)) => match auth.mode {
|
||||
AuthMode::ApiKey => match auth.get_token().await {
|
||||
Ok(api_key) => {
|
||||
eprintln!("Logged in using an API key - {}", safe_format_key(&api_key));
|
||||
|
||||
if let Ok(env_api_key) = env::var(OPENAI_API_KEY_ENV_VAR)
|
||||
&& env_api_key == api_key
|
||||
{
|
||||
if let Ok(env_api_key) = env::var(OPENAI_API_KEY_ENV_VAR) {
|
||||
if env_api_key == api_key {
|
||||
eprintln!(
|
||||
" API loaded from OPENAI_API_KEY environment variable or .env file"
|
||||
);
|
||||
}
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
Err(e) => {
|
||||
|
||||
@@ -130,7 +130,7 @@ async fn acquire_exclusive_lock_with_retry(file: &File) -> Result<()> {
|
||||
use tokio::time::sleep;
|
||||
|
||||
for _ in 0..MAX_RETRIES {
|
||||
match file.try_lock_exclusive() {
|
||||
match fs2::FileExt::try_lock_exclusive(file) {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
@@ -263,7 +263,7 @@ pub(crate) fn lookup(log_id: u64, offset: usize, config: &Config) -> Option<Hist
|
||||
#[cfg(unix)]
|
||||
fn acquire_shared_lock_with_retry(file: &File) -> Result<()> {
|
||||
for _ in 0..MAX_RETRIES {
|
||||
match file.try_lock_shared() {
|
||||
match fs2::FileExt::try_lock_shared(file) {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||
|
||||
@@ -263,7 +263,7 @@ async fn run_codex_tool_session_inner(
|
||||
| EventMsg::AgentReasoningSectionBreak(_)
|
||||
| EventMsg::McpToolCallBegin(_)
|
||||
| EventMsg::McpToolCallEnd(_)
|
||||
| EventMsg::McpListToolsResponse(_)
|
||||
// | EventMsg::McpListToolsResponse(_)
|
||||
| EventMsg::ExecCommandBegin(_)
|
||||
| EventMsg::ExecCommandOutputDelta(_)
|
||||
| EventMsg::ExecCommandEnd(_)
|
||||
|
||||
@@ -33,11 +33,11 @@ impl ApprovalModalView<'_> {
|
||||
|
||||
/// Advance to next request if the current one is finished.
|
||||
fn maybe_advance(&mut self) {
|
||||
if self.current.is_complete()
|
||||
&& let Some(req) = self.queue.pop()
|
||||
{
|
||||
if self.current.is_complete() {
|
||||
if let Some(req) = self.queue.pop() {
|
||||
self.current = UserApprovalWidget::new(req, self.app_event_tx.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -226,6 +226,7 @@ impl BottomPaneView<'_> for ListSelectionView {
|
||||
match_indices: None,
|
||||
is_current: it.is_current,
|
||||
description: it.description.clone(),
|
||||
name_color: None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -24,6 +24,7 @@ mod popup_consts;
|
||||
mod reasoning_selection_view;
|
||||
mod scroll_state;
|
||||
mod selection_popup_common;
|
||||
pub mod list_selection_view;
|
||||
mod textarea;
|
||||
mod theme_selection_view;
|
||||
mod verbosity_selection_view;
|
||||
|
||||
@@ -33,10 +33,10 @@ where
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(home_dir) = std::env::var_os("HOME").map(PathBuf::from)
|
||||
&& let Ok(rel) = path.strip_prefix(&home_dir)
|
||||
{
|
||||
return Some(rel.to_path_buf());
|
||||
if let Some(home_dir) = std::env::var_os("HOME").map(PathBuf::from) {
|
||||
if let Ok(rel) = path.strip_prefix(&home_dir) {
|
||||
return Some(rel.to_path_buf());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
|
||||
@@ -94,13 +94,13 @@ impl FileSearchManager {
|
||||
|
||||
// If there is an in-flight search that is definitely obsolete,
|
||||
// cancel it now.
|
||||
if let Some(active_search) = &st.active_search
|
||||
&& !query.starts_with(&active_search.query)
|
||||
{
|
||||
if let Some(active_search) = &st.active_search {
|
||||
if !query.starts_with(&active_search.query) {
|
||||
active_search
|
||||
.cancellation_token
|
||||
.store(true, Ordering::Relaxed);
|
||||
st.active_search = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule a search to run after debounce.
|
||||
@@ -187,11 +187,11 @@ impl FileSearchManager {
|
||||
{
|
||||
#[expect(clippy::unwrap_used)]
|
||||
let mut st = search_state.lock().unwrap();
|
||||
if let Some(active_search) = &st.active_search
|
||||
&& Arc::ptr_eq(&active_search.cancellation_token, &cancellation_token)
|
||||
{
|
||||
if let Some(active_search) = &st.active_search {
|
||||
if Arc::ptr_eq(&active_search.cancellation_token, &cancellation_token) {
|
||||
st.active_search = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2013,22 +2013,16 @@ pub(crate) fn new_status_output(config: &Config, usage: &TokenUsage) -> PlainHis
|
||||
|
||||
// 📊 Token Usage
|
||||
lines.push(Line::from(vec!["📊 ".into(), "Token Usage".bold()]));
|
||||
if let Some(session_id) = session_id {
|
||||
lines.push(Line::from(vec![
|
||||
" • Session ID: ".into(),
|
||||
session_id.to_string().into(),
|
||||
]));
|
||||
}
|
||||
// Input: <input> [+ <cached> cached]
|
||||
let mut input_line_spans: Vec<Span<'static>> = vec![
|
||||
" • Input: ".into(),
|
||||
usage.non_cached_input().to_string().into(),
|
||||
];
|
||||
if let Some(cached) = usage.cached_input_tokens
|
||||
&& cached > 0
|
||||
{
|
||||
if let Some(cached) = usage.cached_input_tokens {
|
||||
if cached > 0 {
|
||||
input_line_spans.push(format!(" (+ {cached} cached)").into());
|
||||
}
|
||||
}
|
||||
lines.push(Line::from(input_line_spans));
|
||||
// Output: <output>
|
||||
lines.push(Line::from(vec![
|
||||
|
||||
@@ -318,6 +318,7 @@ fn restore() {
|
||||
}
|
||||
|
||||
/// Minimal login status indicator for onboarding flow.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum LoginStatus {
|
||||
NotAuthenticated,
|
||||
AuthMode(AuthMode),
|
||||
|
||||
@@ -123,9 +123,8 @@ impl AuthModeWidget {
|
||||
|
||||
// If the user is already authenticated but the method differs from their
|
||||
// preferred auth method, show a brief explanation.
|
||||
if let LoginStatus::AuthMode(current) = self.login_status
|
||||
&& current != self.preferred_auth_method
|
||||
{
|
||||
if let LoginStatus::AuthMode(current) = self.login_status {
|
||||
if current != self.preferred_auth_method {
|
||||
let to_label = |mode: AuthMode| match mode {
|
||||
AuthMode::ApiKey => "API key",
|
||||
AuthMode::ChatGPT => "ChatGPT",
|
||||
@@ -138,6 +137,7 @@ impl AuthModeWidget {
|
||||
lines.push(Line::from(msg).style(Style::default()));
|
||||
lines.push(Line::from(""));
|
||||
}
|
||||
}
|
||||
|
||||
let create_mode_item = |idx: usize,
|
||||
selected_mode: AuthMode,
|
||||
@@ -221,16 +221,15 @@ impl AuthModeWidget {
|
||||
)));
|
||||
spans.extend(shimmer_spans("Finish signing in via your browser"));
|
||||
let mut lines = vec![Line::from(spans), Line::from("")];
|
||||
|
||||
if let SignInState::ChatGptContinueInBrowser(state) = &self.sign_in_state
|
||||
&& !state.auth_url.is_empty()
|
||||
{
|
||||
lines.push(Line::from(" If the link doesn't open automatically, open the following link to authenticate:"));
|
||||
lines.push(Line::from(vec![
|
||||
Span::raw(" "),
|
||||
state.auth_url.as_str().cyan().underlined(),
|
||||
]));
|
||||
lines.push(Line::from(""));
|
||||
if let SignInState::ChatGptContinueInBrowser(state) = &self.sign_in_state {
|
||||
if !state.auth_url.is_empty() {
|
||||
lines.push(Line::from(" If the link doesn't open automatically, open the following link to authenticate:"));
|
||||
lines.push(Line::from(vec![
|
||||
Span::raw(" "),
|
||||
state.auth_url.as_str().cyan().underlined(),
|
||||
]));
|
||||
lines.push(Line::from(""));
|
||||
}
|
||||
}
|
||||
|
||||
lines.push(
|
||||
@@ -412,4 +411,4 @@ impl WidgetRef for AuthModeWidget {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ impl OnboardingScreen {
|
||||
sign_in_state: SignInState::PickMode,
|
||||
codex_home: codex_home.clone(),
|
||||
login_status,
|
||||
preferred_auth_method: chat_widget_args.config.preferred_auth_method,
|
||||
preferred_auth_method: codex_login::AuthMode::ApiKey,
|
||||
}))
|
||||
}
|
||||
let is_git_repo = is_inside_git_repo(&cwd);
|
||||
|
||||
@@ -53,12 +53,12 @@ pub(crate) fn format_json_compact(text: &str) -> Option<String> {
|
||||
}
|
||||
' ' | '\t' if !in_string => {
|
||||
// Add a space after : and , but only when not in a string
|
||||
if let Some(&next_ch) = chars.peek()
|
||||
&& let Some(last_ch) = result.chars().last()
|
||||
&& (last_ch == ':' || last_ch == ',')
|
||||
&& !matches!(next_ch, '}' | ']')
|
||||
{
|
||||
result.push(' ');
|
||||
if let Some(&next_ch) = chars.peek() {
|
||||
if let Some(last_ch) = result.chars().last() {
|
||||
if (last_ch == ':' || last_ch == ',') && !matches!(next_ch, '}' | ']') {
|
||||
result.push(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
||||
Reference in New Issue
Block a user