From def9c87837ac4f89cbd0d2054c6aeca3502329cd Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 10 Dec 2025 22:29:48 -0500 Subject: [PATCH] Migrate credentials without touching settings --- .../extension_host/src/anthropic_migration.rs | 20 ++++++++++++++++--- .../extension_host/src/copilot_migration.rs | 12 ++++++++--- .../extension_host/src/google_ai_migration.rs | 20 ++++++++++++++++--- .../src/open_router_migration.rs | 20 ++++++++++++++++--- crates/extension_host/src/openai_migration.rs | 20 ++++++++++++++++--- .../src/wasm_host/llm_provider.rs | 10 ++++++++-- 6 files changed, 85 insertions(+), 17 deletions(-) diff --git a/crates/extension_host/src/anthropic_migration.rs b/crates/extension_host/src/anthropic_migration.rs index 5dd404d36edf84a311022a3cb5559bcab92e3f5c..76734bd48ce1811ad98af46aff808a21a3029935 100644 --- a/crates/extension_host/src/anthropic_migration.rs +++ b/crates/extension_host/src/anthropic_migration.rs @@ -1,5 +1,6 @@ use credentials_provider::CredentialsProvider; use gpui::App; +use util::ResultExt as _; const ANTHROPIC_EXTENSION_ID: &str = "anthropic"; const ANTHROPIC_PROVIDER_ID: &str = "anthropic"; @@ -37,18 +38,31 @@ pub fn migrate_anthropic_credentials_if_needed(extension_id: &str, cx: &mut App) let api_key = match old_credential { Some((_, key_bytes)) => match String::from_utf8(key_bytes) { - Ok(key) => key, + Ok(key) if !key.is_empty() => key, + Ok(_) => { + log::debug!("Existing Anthropic API key is empty, marking as migrated"); + String::new() + } Err(_) => { log::error!("Failed to decode Anthropic API key as UTF-8"); return; } }, None => { - log::debug!("No existing Anthropic API key found to migrate"); - return; + log::debug!("No existing Anthropic API key found, marking as migrated"); + String::new() } }; + if api_key.is_empty() { + // Write empty credentials as a marker that migration was attempted + credentials_provider + .write_credentials(&extension_credential_key, "Bearer", b"", &cx) + .await + .log_err(); + return; + } + log::info!("Migrating existing Anthropic API key to Anthropic extension"); match credentials_provider diff --git a/crates/extension_host/src/copilot_migration.rs b/crates/extension_host/src/copilot_migration.rs index cd6c4167bb1d600e32c47f378e21227dcfa4769c..8d0e84a6096bbe589b985e875bf8e246829ce946 100644 --- a/crates/extension_host/src/copilot_migration.rs +++ b/crates/extension_host/src/copilot_migration.rs @@ -1,6 +1,7 @@ use credentials_provider::CredentialsProvider; use gpui::App; use std::path::PathBuf; +use util::ResultExt as _; const COPILOT_CHAT_EXTENSION_ID: &str = "copilot-chat"; const COPILOT_CHAT_PROVIDER_ID: &str = "copilot-chat"; @@ -30,9 +31,14 @@ pub fn migrate_copilot_credentials_if_needed(extension_id: &str, cx: &mut App) { } let oauth_token = match read_copilot_oauth_token().await { - Some(token) => token, - None => { - log::debug!("No existing Copilot OAuth token found to migrate"); + Some(token) if !token.is_empty() => token, + _ => { + log::debug!("No existing Copilot OAuth token found, marking as migrated"); + // Write empty credentials as a marker that migration was attempted + credentials_provider + .write_credentials(&credential_key, "api_key", b"", &cx) + .await + .log_err(); return; } }; diff --git a/crates/extension_host/src/google_ai_migration.rs b/crates/extension_host/src/google_ai_migration.rs index 7b946d7d4301c68c01268b1d00ff49f423edd781..0addf8f26bf3bc24119ec01777bc12c52b4083fe 100644 --- a/crates/extension_host/src/google_ai_migration.rs +++ b/crates/extension_host/src/google_ai_migration.rs @@ -1,5 +1,6 @@ use credentials_provider::CredentialsProvider; use gpui::App; +use util::ResultExt as _; const GOOGLE_AI_EXTENSION_ID: &str = "google-ai"; const GOOGLE_AI_PROVIDER_ID: &str = "google-ai"; @@ -37,18 +38,31 @@ pub fn migrate_google_ai_credentials_if_needed(extension_id: &str, cx: &mut App) let api_key = match old_credential { Some((_, key_bytes)) => match String::from_utf8(key_bytes) { - Ok(key) => key, + Ok(key) if !key.is_empty() => key, + Ok(_) => { + log::debug!("Existing Google AI API key is empty, marking as migrated"); + String::new() + } Err(_) => { log::error!("Failed to decode Google AI API key as UTF-8"); return; } }, None => { - log::debug!("No existing Google AI API key found to migrate"); - return; + log::debug!("No existing Google AI API key found, marking as migrated"); + String::new() } }; + if api_key.is_empty() { + // Write empty credentials as a marker that migration was attempted + credentials_provider + .write_credentials(&extension_credential_key, "Bearer", b"", &cx) + .await + .log_err(); + return; + } + log::info!("Migrating existing Google AI API key to Google AI extension"); match credentials_provider diff --git a/crates/extension_host/src/open_router_migration.rs b/crates/extension_host/src/open_router_migration.rs index 17bf0ff425c08a9fee6232c57e1a2bf1d1aac7fe..67788b1142e8a84b3b3207f8c5b7573b26de114a 100644 --- a/crates/extension_host/src/open_router_migration.rs +++ b/crates/extension_host/src/open_router_migration.rs @@ -1,5 +1,6 @@ use credentials_provider::CredentialsProvider; use gpui::App; +use util::ResultExt as _; const OPEN_ROUTER_EXTENSION_ID: &str = "openrouter"; const OPEN_ROUTER_PROVIDER_ID: &str = "openrouter"; @@ -37,18 +38,31 @@ pub fn migrate_open_router_credentials_if_needed(extension_id: &str, cx: &mut Ap let api_key = match old_credential { Some((_, key_bytes)) => match String::from_utf8(key_bytes) { - Ok(key) => key, + Ok(key) if !key.is_empty() => key, + Ok(_) => { + log::debug!("Existing OpenRouter API key is empty, marking as migrated"); + String::new() + } Err(_) => { log::error!("Failed to decode OpenRouter API key as UTF-8"); return; } }, None => { - log::debug!("No existing OpenRouter API key found to migrate"); - return; + log::debug!("No existing OpenRouter API key found, marking as migrated"); + String::new() } }; + if api_key.is_empty() { + // Write empty credentials as a marker that migration was attempted + credentials_provider + .write_credentials(&extension_credential_key, "Bearer", b"", &cx) + .await + .log_err(); + return; + } + log::info!("Migrating existing OpenRouter API key to OpenRouter extension"); match credentials_provider diff --git a/crates/extension_host/src/openai_migration.rs b/crates/extension_host/src/openai_migration.rs index b535b9f8cef6c4b2039a606e3232e4fb89d3cc35..d226e33920d654c261fa60abc4d9f6e4133091d9 100644 --- a/crates/extension_host/src/openai_migration.rs +++ b/crates/extension_host/src/openai_migration.rs @@ -1,5 +1,6 @@ use credentials_provider::CredentialsProvider; use gpui::App; +use util::ResultExt as _; const OPENAI_EXTENSION_ID: &str = "openai"; const OPENAI_PROVIDER_ID: &str = "openai"; @@ -37,18 +38,31 @@ pub fn migrate_openai_credentials_if_needed(extension_id: &str, cx: &mut App) { let api_key = match old_credential { Some((_, key_bytes)) => match String::from_utf8(key_bytes) { - Ok(key) => key, + Ok(key) if !key.is_empty() => key, + Ok(_) => { + log::debug!("Existing OpenAI API key is empty, marking as migrated"); + String::new() + } Err(_) => { log::error!("Failed to decode OpenAI API key as UTF-8"); return; } }, None => { - log::debug!("No existing OpenAI API key found to migrate"); - return; + log::debug!("No existing OpenAI API key found, marking as migrated"); + String::new() } }; + if api_key.is_empty() { + // Write empty credentials as a marker that migration was attempted + credentials_provider + .write_credentials(&extension_credential_key, "Bearer", b"", &cx) + .await + .log_err(); + return; + } + log::info!("Migrating existing OpenAI API key to OpenAI extension"); match credentials_provider diff --git a/crates/extension_host/src/wasm_host/llm_provider.rs b/crates/extension_host/src/wasm_host/llm_provider.rs index acfdc965ba522540f5789f8cb4c45d65cf51d02a..1c1334f58dc07d00dfc6be29b894324f279c4d18 100644 --- a/crates/extension_host/src/wasm_host/llm_provider.rs +++ b/crates/extension_host/src/wasm_host/llm_provider.rs @@ -428,7 +428,10 @@ impl ExtensionProviderConfigurationView { .log_err() .flatten(); - let has_credentials = credentials.is_some(); + // Treat empty credentials as not authenticated (used as migration marker) + let has_credentials = credentials + .map(|(_, password)| !password.is_empty()) + .unwrap_or(false); // Update authentication state based on stored credentials cx.update(|cx| { @@ -536,7 +539,10 @@ impl ExtensionProviderConfigurationView { .log_err() .flatten(); - let has_credentials = credentials.is_some(); + // Treat empty credentials as not authenticated (used as migration marker) + let has_credentials = credentials + .map(|(_, password)| !password.is_empty()) + .unwrap_or(false); cx.update(|cx| { state.update(cx, |state, cx| {