From 310cc2a7a84e528f97e95a4fba5148d21c128054 Mon Sep 17 00:00:00 2001 From: Lingrui98 Date: Wed, 19 Aug 2026 22:50:33 +0800 Subject: [PATCH 1/2] feat(tui): Claude auth field selector and template chip horizontal scrolling Add a TUI row for Claude providers to toggle the authentication field between ANTHROPIC_AUTH_TOKEN (default) and ANTHROPIC_API_KEY, persisting meta.apiKeyField only when ApiKey is selected. Keep the template chip selector as a single horizontal row and implement horizontal scrolling so the focused chip remains visible in narrow terminals. Co-Authored-By: Claude --- src-tauri/src/cli/i18n.rs | 20 ++++- .../src/cli/tui/app/form_handlers/provider.rs | 3 +- src-tauri/src/cli/tui/app/tests.rs | 40 +++++++++ src-tauri/src/cli/tui/form.rs | 1 + src-tauri/src/cli/tui/form/provider_state.rs | 3 + src-tauri/src/cli/tui/form/tests.rs | 74 +++++++++++++++++ src-tauri/src/cli/tui/help.rs | 7 ++ src-tauri/src/cli/tui/ui/forms/provider.rs | 7 ++ src-tauri/src/cli/tui/ui/forms/shared.rs | 66 +++++++++++++-- src-tauri/src/cli/tui/ui/tests.rs | 82 +++++++++++++++++++ 10 files changed, 291 insertions(+), 12 deletions(-) diff --git a/src-tauri/src/cli/i18n.rs b/src-tauri/src/cli/i18n.rs index 2c2d1d15..610c5bd9 100644 --- a/src-tauri/src/cli/i18n.rs +++ b/src-tauri/src/cli/i18n.rs @@ -2078,7 +2078,7 @@ pub mod texts { } } - pub fn tui_label_codex_anthropic_auth_field() -> &'static str { + fn tui_label_auth_field() -> &'static str { if is_chinese() { "认证字段" } else { @@ -2086,7 +2086,11 @@ pub mod texts { } } - pub fn tui_codex_anthropic_auth_field_value(api_key_field: &str) -> &'static str { + pub fn tui_label_codex_anthropic_auth_field() -> &'static str { + tui_label_auth_field() + } + + fn tui_auth_field_value(api_key_field: &str) -> &'static str { if api_key_field == "ANTHROPIC_API_KEY" { "ANTHROPIC_API_KEY (x-api-key)" } else { @@ -2094,6 +2098,18 @@ pub mod texts { } } + pub fn tui_codex_anthropic_auth_field_value(api_key_field: &str) -> &'static str { + tui_auth_field_value(api_key_field) + } + + pub fn tui_label_claude_auth_field() -> &'static str { + tui_label_auth_field() + } + + pub fn tui_claude_auth_field_value(api_key_field: &str) -> &'static str { + tui_auth_field_value(api_key_field) + } + pub fn tui_label_codex_impersonate_claude_code() -> &'static str { if is_chinese() { "模拟 Claude Code 客户端" diff --git a/src-tauri/src/cli/tui/app/form_handlers/provider.rs b/src-tauri/src/cli/tui/app/form_handlers/provider.rs index 837f2029..5dbb4e71 100644 --- a/src-tauri/src/cli/tui/app/form_handlers/provider.rs +++ b/src-tauri/src/cli/tui/app/form_handlers/provider.rs @@ -388,7 +388,8 @@ impl App { }; Action::None } - ProviderAddField::CodexAnthropicApiKeyField => { + ProviderAddField::CodexAnthropicApiKeyField + | ProviderAddField::ClaudeAnthropicApiKeyField => { if !matches!(key.code, KeyCode::Enter) { return Action::None; } diff --git a/src-tauri/src/cli/tui/app/tests.rs b/src-tauri/src/cli/tui/app/tests.rs index 939826f5..ce72a661 100644 --- a/src-tauri/src/cli/tui/app/tests.rs +++ b/src-tauri/src/cli/tui/app/tests.rs @@ -12592,6 +12592,46 @@ mod tests { assert_eq!(focus, super::super::form::FormFocus::Fields); } + #[test] + fn provider_add_form_claude_auth_field_enter_toggles_api_key_field() { + let mut app = App::new(Some(AppType::Claude)); + app.route = Route::Providers; + app.focus = Focus::Content; + + let data = UiData::default(); + app.on_key(key(KeyCode::Char('a')), &data); + app.on_key(key(KeyCode::Enter), &data); + + // Position the cursor on the Claude auth field selector. + let auth_field = super::super::form::ProviderAddField::ClaudeAnthropicApiKeyField; + if let Some(super::super::form::FormState::ProviderAdd(provider)) = app.form.as_mut() { + provider.focus = super::super::form::FormFocus::Fields; + let fields = provider.fields(); + let idx = fields + .iter() + .position(|field| *field == auth_field) + .expect("ClaudeAnthropicApiKeyField should be present"); + provider.field_idx = idx; + assert_eq!( + provider.claude_api_key_field, + crate::provider::ClaudeApiKeyField::AuthToken + ); + } else { + panic!("expected ProviderAdd form"); + } + + let action = app.on_key(key(KeyCode::Enter), &data); + assert!(matches!(action, Action::None)); + + let field = match app.form.as_ref() { + Some(super::super::form::FormState::ProviderAdd(provider)) => { + provider.claude_api_key_field + } + other => panic!("expected ProviderAdd form, got: {other:?}"), + }; + assert_eq!(field, crate::provider::ClaudeApiKeyField::ApiKey); + } + #[test] fn provider_form_esc_dirty_opens_save_before_close_confirm() { let mut app = App::new(Some(AppType::Claude)); diff --git a/src-tauri/src/cli/tui/form.rs b/src-tauri/src/cli/tui/form.rs index 717c3484..1970aee3 100644 --- a/src-tauri/src/cli/tui/form.rs +++ b/src-tauri/src/cli/tui/form.rs @@ -253,6 +253,7 @@ pub enum ProviderAddField { ClaudeBaseUrl, ClaudeApiFormat, ClaudeApiKey, + ClaudeAnthropicApiKeyField, ClaudeModelConfig, ClaudeFallbackModel, ClaudeAdvancedDivider, diff --git a/src-tauri/src/cli/tui/form/provider_state.rs b/src-tauri/src/cli/tui/form/provider_state.rs index e28d0dcf..acd2dda9 100644 --- a/src-tauri/src/cli/tui/form/provider_state.rs +++ b/src-tauri/src/cli/tui/form/provider_state.rs @@ -490,6 +490,7 @@ impl ProviderAddFormState { } else if !self.is_claude_official_provider() { fields.push(ProviderAddField::ClaudeBaseUrl); fields.push(ProviderAddField::ClaudeApiKey); + fields.push(ProviderAddField::ClaudeAnthropicApiKeyField); fields.push(ProviderAddField::ClaudeAdvancedDivider); fields.push(ProviderAddField::ClaudeApiFormat); fields.push(ProviderAddField::ClaudeModelConfig); @@ -696,6 +697,7 @@ impl ProviderAddFormState { ProviderAddField::HermesRateLimitDelay => Some(&self.hermes_rate_limit_delay), ProviderAddField::CodexOAuthAccount | ProviderAddField::CodexFastMode + | ProviderAddField::ClaudeAnthropicApiKeyField | ProviderAddField::CodexAnthropicApiKeyField | ProviderAddField::CodexImpersonateClaudeCode | ProviderAddField::CodexPromptCacheRouting @@ -764,6 +766,7 @@ impl ProviderAddFormState { ProviderAddField::HermesRateLimitDelay => Some(&mut self.hermes_rate_limit_delay), ProviderAddField::CodexOAuthAccount | ProviderAddField::CodexFastMode + | ProviderAddField::ClaudeAnthropicApiKeyField | ProviderAddField::CodexAnthropicApiKeyField | ProviderAddField::CodexImpersonateClaudeCode | ProviderAddField::CodexPromptCacheRouting diff --git a/src-tauri/src/cli/tui/form/tests.rs b/src-tauri/src/cli/tui/form/tests.rs index 4907a83c..3fa7ec29 100644 --- a/src-tauri/src/cli/tui/form/tests.rs +++ b/src-tauri/src/cli/tui/form/tests.rs @@ -2663,6 +2663,57 @@ fn provider_add_form_claude_builds_env_settings() { ); } +#[test] +fn provider_add_form_claude_has_auth_field_selector() { + let form = ProviderAddFormState::new(AppType::Claude); + let fields = form.fields(); + assert!( + fields.contains(&ProviderAddField::ClaudeAnthropicApiKeyField), + "Claude custom provider should expose the auth field selector" + ); +} + +#[test] +fn provider_add_form_claude_api_key_field_switches_env_and_meta() { + let mut form = ProviderAddFormState::new(AppType::Claude); + form.id.set("p1"); + form.name.set("Provider One"); + form.claude_base_url.set("https://kimi.example"); + form.claude_api_key.set("sk-kimi"); + form.claude_api_key_field = ClaudeApiKeyField::ApiKey; + + let provider = form.to_provider_json_value(); + assert_eq!( + provider["settingsConfig"]["env"]["ANTHROPIC_API_KEY"], + "sk-kimi" + ); + assert!( + provider["settingsConfig"]["env"] + .get("ANTHROPIC_AUTH_TOKEN") + .is_none(), + "ANTHROPIC_AUTH_TOKEN should be removed when ApiKey is selected" + ); + assert_eq!(provider["meta"]["apiKeyField"], "ANTHROPIC_API_KEY"); + + // Switch back to the default auth token. + form.claude_api_key_field = ClaudeApiKeyField::AuthToken; + let provider = form.to_provider_json_value(); + assert_eq!( + provider["settingsConfig"]["env"]["ANTHROPIC_AUTH_TOKEN"], + "sk-kimi" + ); + assert!( + provider["settingsConfig"]["env"] + .get("ANTHROPIC_API_KEY") + .is_none(), + "ANTHROPIC_API_KEY should be removed when AuthToken is selected" + ); + assert!( + provider["meta"].get("apiKeyField").is_none(), + "default AuthToken should not write apiKeyField meta" + ); +} + #[test] fn provider_add_form_claude_api_format_writes_openai_chat_meta() { let mut form = ProviderAddFormState::new(AppType::Claude); @@ -7059,6 +7110,29 @@ fn provider_edit_form_infers_claude_api_key_field_from_env_when_meta_missing() { ); } +#[test] +fn provider_edit_form_claude_exposes_auth_field_selector_for_api_key_env() { + let provider_value = json!({ + "id": "provider-1", + "name": "Provider One", + "settingsConfig": { + "env": { + "ANTHROPIC_BASE_URL": "https://api.example.com", + "ANTHROPIC_API_KEY": "sk-api-key" + } + } + }); + let provider: Provider = serde_json::from_value(provider_value).expect("provider json valid"); + + let form = ProviderAddFormState::from_provider(AppType::Claude, &provider); + assert_eq!(form.claude_api_key_field, ClaudeApiKeyField::ApiKey); + assert!( + form.fields() + .contains(&ProviderAddField::ClaudeAnthropicApiKeyField), + "edit form should expose the Claude auth field selector" + ); +} + #[test] fn provider_add_form_does_not_write_usage_script_until_touched() { let mut form = ProviderAddFormState::new(AppType::Claude); diff --git a/src-tauri/src/cli/tui/help.rs b/src-tauri/src/cli/tui/help.rs index e221cd05..c4a7f481 100644 --- a/src-tauri/src/cli/tui/help.rs +++ b/src-tauri/src/cli/tui/help.rs @@ -658,6 +658,13 @@ fn provider_field_help(app_type: AppType, field: ProviderAddField) -> HelpConten "Choose which header carries the API key: ANTHROPIC_AUTH_TOKEN sends Authorization: Bearer; ANTHROPIC_API_KEY sends x-api-key. Only one is sent.", ), ), + ProviderAddField::ClaudeAnthropicApiKeyField => HelpContent::new( + texts::tui_label_claude_auth_field(), + help_lines( + "选择 Claude Code 写入 settings.json 的 API Key 字段名:ANTHROPIC_AUTH_TOKEN(默认)或 ANTHROPIC_API_KEY。切换时会迁移已填写的 key。", + "Choose the API key field name written to Claude Code's settings.json: ANTHROPIC_AUTH_TOKEN (default) or ANTHROPIC_API_KEY. The entered key is migrated when switching.", + ), + ), ProviderAddField::CodexImpersonateClaudeCode => HelpContent::new( texts::tui_label_codex_impersonate_claude_code(), help_lines( diff --git a/src-tauri/src/cli/tui/ui/forms/provider.rs b/src-tauri/src/cli/tui/ui/forms/provider.rs index 94fcd7b7..09a0e7af 100644 --- a/src-tauri/src/cli/tui/ui/forms/provider.rs +++ b/src-tauri/src/cli/tui/ui/forms/provider.rs @@ -1878,6 +1878,9 @@ pub(crate) fn provider_field_label_and_value( } } ProviderAddField::ClaudeApiKey => texts::tui_label_api_key().to_string(), + ProviderAddField::ClaudeAnthropicApiKeyField => { + texts::tui_label_claude_auth_field().to_string() + } ProviderAddField::ClaudeModelConfig => texts::tui_label_claude_model_config().to_string(), ProviderAddField::ClaudeFallbackModel => { texts::tui_label_claude_fallback_model().to_string() @@ -1966,6 +1969,10 @@ pub(crate) fn provider_field_label_and_value( let value = match field { ProviderAddField::ClaudeApiFormat => provider_api_format_label(provider), + ProviderAddField::ClaudeAnthropicApiKeyField => { + texts::tui_claude_auth_field_value(provider.claude_api_key_field.as_env_key()) + .to_string() + } ProviderAddField::CodexAnthropicApiKeyField => { texts::tui_codex_anthropic_auth_field_value(provider.claude_api_key_field.as_env_key()) .to_string() diff --git a/src-tauri/src/cli/tui/ui/forms/shared.rs b/src-tauri/src/cli/tui/ui/forms/shared.rs index a7ea0c18..c36e29c7 100644 --- a/src-tauri/src/cli/tui/ui/forms/shared.rs +++ b/src-tauri/src/cli/tui/ui/forms/shared.rs @@ -66,7 +66,9 @@ pub(crate) fn add_form_key_items( ProviderAddField::GeminiAuthType | ProviderAddField::CodexPromptCacheRouting | ProviderAddField::OpenClawApiProtocol - | ProviderAddField::HermesApiMode, + | ProviderAddField::HermesApiMode + | ProviderAddField::ClaudeAnthropicApiKeyField + | ProviderAddField::CodexAnthropicApiKeyField, ) => texts::tui_key_select(), _ => texts::tui_key_edit_mode(), }; @@ -251,22 +253,68 @@ pub(crate) fn render_form_template_chips( frame.render_widget(template_block.clone(), area); let template_inner = template_block.inner(area); - let mut spans: Vec> = Vec::new(); - for (idx, label) in labels.iter().enumerate() { + if template_inner.width == 0 || labels.is_empty() { + return; + } + + let selected_idx = selected_idx.min(labels.len().saturating_sub(1)); + let viewport = template_inner.width as usize; + + // Build chip metadata: each chip renders as " {label} " followed by a + // trailing space, so its total advance is label width + 3. + let chip_widths: Vec = labels + .iter() + .map(|label| UnicodeWidthStr::width(*label).saturating_add(3)) + .collect(); + let total_width = chip_widths + .iter() + .fold(0usize, |acc, width| acc.saturating_add(*width)); + + // Decide the horizontal window so that the selected chip is always fully + // visible. When space permits, also show preceding and following chips. + let (first_visible, last_visible) = if total_width <= viewport { + (0, labels.len().saturating_sub(1)) + } else { + let mut first = selected_idx; + let mut last = selected_idx; + let mut used = chip_widths[selected_idx]; + while first > 0 { + let prev_width = chip_widths[first.saturating_sub(1)]; + if used.saturating_add(prev_width) > viewport { + break; + } + used = used.saturating_add(prev_width); + first = first.saturating_sub(1); + } + while last.saturating_add(1) < labels.len() { + let next_width = chip_widths[last.saturating_add(1)]; + if used.saturating_add(next_width) > viewport { + break; + } + used = used.saturating_add(next_width); + last = last.saturating_add(1); + } + (first, last) + }; + + let mut visible_spans: Vec> = Vec::new(); + for (idx, label) in labels + .iter() + .enumerate() + .skip(first_visible) + .take(last_visible.saturating_sub(first_visible).saturating_add(1)) + { let selected = idx == selected_idx; let style = if selected { active_chip_style(theme) } else { inactive_chip_style(theme) }; - spans.push(Span::styled(format!(" {label} "), style)); - spans.push(Span::raw(" ")); + visible_spans.push(Span::styled(format!(" {label} "), style)); + visible_spans.push(Span::raw(" ")); } - frame.render_widget( - Paragraph::new(Line::from(spans)).wrap(Wrap { trim: false }), - template_inner, - ); + frame.render_widget(Paragraph::new(Line::from(visible_spans)), template_inner); } pub(crate) fn visible_text_window(text: &str, cursor: usize, width: usize) -> (String, u16) { diff --git a/src-tauri/src/cli/tui/ui/tests.rs b/src-tauri/src/cli/tui/ui/tests.rs index fd34d54b..511c2c33 100644 --- a/src-tauri/src/cli/tui/ui/tests.rs +++ b/src-tauri/src/cli/tui/ui/tests.rs @@ -4078,6 +4078,88 @@ fn provider_form_fields_show_dashed_divider_before_common_snippet() { ); } +#[test] +fn add_form_template_chips_keep_selected_visible_when_narrow() { + let _lock = lock_env(); + let _no_color = EnvGuard::remove("NO_COLOR"); + + let mut app = App::new(Some(AppType::Claude)); + app.route = Route::Providers; + app.focus = Focus::Content; + let mut form = crate::cli::tui::form::ProviderAddFormState::new(AppType::Claude); + // Select the last sponsor preset, which is well beyond a 40-column viewport. + form.template_idx = form.template_count().saturating_sub(1); + app.form = Some(crate::cli::tui::form::FormState::ProviderAdd(form)); + + let data = minimal_data(&app.app_type); + // 100 columns gives the template chip viewport enough room to show + // several chips at once while still requiring horizontal scrolling to + // reach the last sponsor preset. + let buf = render_with_size(&app, &data, 100, 20); + + let selected_label = "* DDS"; + let mut found = false; + for y in 0..buf.area.height { + let line = line_at(&buf, y); + if line.contains(selected_label) { + found = true; + break; + } + } + assert!( + found, + "selected template chip should remain visible in a narrow viewport" + ); +} + +#[test] +fn add_form_template_chips_scroll_right_reveals_hidden_chip() { + let _lock = lock_env(); + let _no_color = EnvGuard::remove("NO_COLOR"); + + let mut app = App::new(Some(AppType::Claude)); + app.route = Route::Providers; + app.focus = Focus::Content; + app.form = Some(crate::cli::tui::form::FormState::ProviderAdd( + crate::cli::tui::form::ProviderAddFormState::new(AppType::Claude), + )); + + let data = minimal_data(&app.app_type); + // 100 columns leaves a comfortable template viewport while still being + // narrow enough that the last sponsor chip is initially off-screen. + let buf = render_with_size(&app, &data, 100, 20); + + let mut chips_y = None; + for y in 0..buf.area.height { + let line = line_at(&buf, y); + if line.contains("Custom") && line.contains("Claude Official") { + chips_y = Some(y); + break; + } + } + let chips_y = chips_y.expect("template chips row missing from add form"); + let initial = line_at(&buf, chips_y); + assert!( + initial.contains("Custom") && initial.contains("Claude Official"), + "initial viewport should show the first chips, got: {initial}" + ); + assert!( + !initial.contains("* DDS"), + "last sponsor chip should be off-screen initially" + ); + + // Move selection to the last chip; rendering should scroll it into view. + if let Some(crate::cli::tui::form::FormState::ProviderAdd(form)) = app.form.as_mut() { + form.template_idx = form.template_count().saturating_sub(1); + } + let buf = render_with_size(&app, &data, 100, 20); + let scrolled = line_at(&buf, chips_y); + assert!( + scrolled.contains("* DDS"), + "scrolled viewport should reveal the selected chip, got: {scrolled}" + ); +} + #[test] fn hermes_models_overlay_separates_models_with_dashed_divider() { let _lock = lock_env(); From 837d9713f456276a6e06d34bc218e61164866b54 Mon Sep 17 00:00:00 2001 From: Lingrui98 Date: Thu, 20 Aug 2026 01:13:41 +0800 Subject: [PATCH 2/2] fix(temp-launch): clear alternate Claude auth field to avoid stale fallback When only one of ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN is configured, Claude Code may fall back to the other field from the user's global ~/.claude/settings.json. Write an explicit empty string for the unused alternate field in the temporary launch settings so the provider's choice is honored. Co-Authored-By: Claude --- src-tauri/src/cli/claude_temp_launch.rs | 80 +++++++++++++++++++ src-tauri/src/cli/commands/start.rs | 6 +- .../tui/runtime_actions/claude_temp_launch.rs | 3 +- 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/cli/claude_temp_launch.rs b/src-tauri/src/cli/claude_temp_launch.rs index 886d07e3..756246fd 100644 --- a/src-tauri/src/cli/claude_temp_launch.rs +++ b/src-tauri/src/cli/claude_temp_launch.rs @@ -106,6 +106,43 @@ fn normalize_launch_settings(provider_id: &str, settings: &Value) -> Result Result { which::which("claude").map_err(|_| { AppError::localized( @@ -527,6 +564,49 @@ mod tests { } } + #[test] + fn clear_alternate_auth_field_clears_api_key_when_only_auth_token_is_set() { + let mut settings = json!({ + "env": { + "ANTHROPIC_AUTH_TOKEN": "sk-glm", + "ANTHROPIC_BASE_URL": "https://provider.example" + } + }); + clear_alternate_claude_auth_field(&mut settings); + let env = settings.get("env").unwrap(); + assert_eq!(env.get("ANTHROPIC_AUTH_TOKEN").unwrap(), "sk-glm"); + assert_eq!(env.get("ANTHROPIC_API_KEY").unwrap(), ""); + } + + #[test] + fn clear_alternate_auth_field_clears_auth_token_when_only_api_key_is_set() { + let mut settings = json!({ + "env": { + "ANTHROPIC_API_KEY": "sk-kimi", + "ANTHROPIC_BASE_URL": "https://provider.example" + } + }); + clear_alternate_claude_auth_field(&mut settings); + let env = settings.get("env").unwrap(); + assert_eq!(env.get("ANTHROPIC_API_KEY").unwrap(), "sk-kimi"); + assert_eq!(env.get("ANTHROPIC_AUTH_TOKEN").unwrap(), ""); + } + + #[test] + fn clear_alternate_auth_field_does_nothing_when_both_fields_present() { + let mut settings = json!({ + "env": { + "ANTHROPIC_AUTH_TOKEN": "sk-glm", + "ANTHROPIC_API_KEY": "sk-kimi", + "ANTHROPIC_BASE_URL": "https://provider.example" + } + }); + clear_alternate_claude_auth_field(&mut settings); + let env = settings.get("env").unwrap(); + assert_eq!(env.get("ANTHROPIC_AUTH_TOKEN").unwrap(), "sk-glm"); + assert_eq!(env.get("ANTHROPIC_API_KEY").unwrap(), "sk-kimi"); + } + #[test] fn missing_claude_binary_reports_an_error() { let temp_dir = TempDir::new().expect("create temp dir"); diff --git a/src-tauri/src/cli/commands/start.rs b/src-tauri/src/cli/commands/start.rs index 972d2a22..f827f7fe 100644 --- a/src-tauri/src/cli/commands/start.rs +++ b/src-tauri/src/cli/commands/start.rs @@ -186,11 +186,12 @@ fn prepare_claude_launch_with( where Resolve: FnOnce() -> Result, { - let settings = ProviderService::build_effective_live_snapshot_from_state( + let mut settings = ProviderService::build_effective_live_snapshot_from_state( state, AppType::Claude, provider, )?; + crate::cli::claude_temp_launch::clear_alternate_claude_auth_field(&mut settings); prepare_launch_from_settings_with(&provider.id, &settings, temp_dir, resolve_claude_binary) } @@ -203,11 +204,12 @@ fn preview_claude_launch_with( where Resolve: FnOnce() -> Result, { - let settings = ProviderService::build_effective_live_snapshot_from_state( + let mut settings = ProviderService::build_effective_live_snapshot_from_state( state, AppType::Claude, provider, )?; + crate::cli::claude_temp_launch::clear_alternate_claude_auth_field(&mut settings); preview_launch_from_settings_with(&provider.id, &settings, temp_dir, resolve_claude_binary) } diff --git a/src-tauri/src/cli/tui/runtime_actions/claude_temp_launch.rs b/src-tauri/src/cli/tui/runtime_actions/claude_temp_launch.rs index 52a1ff85..d86a2a4b 100644 --- a/src-tauri/src/cli/tui/runtime_actions/claude_temp_launch.rs +++ b/src-tauri/src/cli/tui/runtime_actions/claude_temp_launch.rs @@ -29,11 +29,12 @@ pub(super) fn launch(ctx: &mut RuntimeActionContext<'_>, id: String) -> Result<( fn prepare_claude_launch(id: &str, temp_dir: &Path) -> Result { let state = load_state()?; let provider = ProviderService::get_provider(&state, AppType::Claude, id)?; - let settings = ProviderService::build_effective_live_snapshot_from_state( + let mut settings = ProviderService::build_effective_live_snapshot_from_state( &state, AppType::Claude, &provider, )?; + crate::cli::claude_temp_launch::clear_alternate_claude_auth_field(&mut settings); prepare_launch_from_settings(&provider.id, &settings, temp_dir) }