open_router_migration.rs

  1use credentials_provider::CredentialsProvider;
  2use gpui::App;
  3
  4const OPEN_ROUTER_EXTENSION_ID: &str = "openrouter";
  5const OPEN_ROUTER_PROVIDER_ID: &str = "openrouter";
  6const OPEN_ROUTER_DEFAULT_API_URL: &str = "https://openrouter.ai/api/v1";
  7
  8/// Migrates OpenRouter API credentials from the old built-in provider location
  9/// to the new extension-based location.
 10///
 11/// This should only be called during auto-install of the extension.
 12pub fn migrate_open_router_credentials_if_needed(extension_id: &str, cx: &mut App) {
 13    if extension_id != OPEN_ROUTER_EXTENSION_ID {
 14        return;
 15    }
 16
 17    let extension_credential_key = format!(
 18        "extension-llm-{}:{}",
 19        OPEN_ROUTER_EXTENSION_ID, OPEN_ROUTER_PROVIDER_ID
 20    );
 21
 22    let credentials_provider = <dyn CredentialsProvider>::global(cx);
 23
 24    cx.spawn(async move |cx| {
 25        // Read from old location
 26        let old_credential = credentials_provider
 27            .read_credentials(OPEN_ROUTER_DEFAULT_API_URL, &cx)
 28            .await
 29            .ok()
 30            .flatten();
 31
 32        let api_key = match old_credential {
 33            Some((_, key_bytes)) => match String::from_utf8(key_bytes) {
 34                Ok(key) if !key.is_empty() => key,
 35                Ok(_) => {
 36                    log::debug!("Existing OpenRouter API key is empty, nothing to migrate");
 37                    return;
 38                }
 39                Err(_) => {
 40                    log::error!("Failed to decode OpenRouter API key as UTF-8");
 41                    return;
 42                }
 43            },
 44            None => {
 45                log::debug!("No existing OpenRouter API key found to migrate");
 46                return;
 47            }
 48        };
 49
 50        log::info!("Migrating existing OpenRouter API key to OpenRouter extension");
 51
 52        match credentials_provider
 53            .write_credentials(&extension_credential_key, "Bearer", api_key.as_bytes(), &cx)
 54            .await
 55        {
 56            Ok(()) => {
 57                log::info!("Successfully migrated OpenRouter API key to extension");
 58            }
 59            Err(err) => {
 60                log::error!("Failed to migrate OpenRouter API key: {}", err);
 61            }
 62        }
 63    })
 64    .detach();
 65}
 66
 67#[cfg(test)]
 68mod tests {
 69    use super::*;
 70    use gpui::TestAppContext;
 71
 72    #[gpui::test]
 73    async fn test_migrates_credentials_from_old_location(cx: &mut TestAppContext) {
 74        let api_key = "sk-or-test-key-12345";
 75
 76        cx.write_credentials(OPEN_ROUTER_DEFAULT_API_URL, "Bearer", api_key.as_bytes());
 77
 78        cx.update(|cx| {
 79            migrate_open_router_credentials_if_needed(OPEN_ROUTER_EXTENSION_ID, cx);
 80        });
 81
 82        cx.run_until_parked();
 83
 84        let migrated = cx.read_credentials("extension-llm-openrouter:openrouter");
 85        assert!(migrated.is_some(), "Credentials should have been migrated");
 86        let (username, password) = migrated.unwrap();
 87        assert_eq!(username, "Bearer");
 88        assert_eq!(String::from_utf8(password).unwrap(), api_key);
 89    }
 90
 91    #[gpui::test]
 92    async fn test_no_migration_if_no_old_credentials(cx: &mut TestAppContext) {
 93        cx.update(|cx| {
 94            migrate_open_router_credentials_if_needed(OPEN_ROUTER_EXTENSION_ID, cx);
 95        });
 96
 97        cx.run_until_parked();
 98
 99        let credentials = cx.read_credentials("extension-llm-openrouter:openrouter");
100        assert!(
101            credentials.is_none(),
102            "Should not create credentials if none existed"
103        );
104    }
105
106    #[gpui::test]
107    async fn test_skips_migration_for_other_extensions(cx: &mut TestAppContext) {
108        let api_key = "sk-or-test-key";
109
110        cx.write_credentials(OPEN_ROUTER_DEFAULT_API_URL, "Bearer", api_key.as_bytes());
111
112        cx.update(|cx| {
113            migrate_open_router_credentials_if_needed("some-other-extension", cx);
114        });
115
116        cx.run_until_parked();
117
118        let credentials = cx.read_credentials("extension-llm-openrouter:openrouter");
119        assert!(
120            credentials.is_none(),
121            "Should not migrate for other extensions"
122        );
123    }
124}