From 0b5d9679f06e821cf00a38d0892f90518dd481a9 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Sun, 10 May 2026 18:51:26 -0400 Subject: [PATCH] commands --- .../src/core/commands/providers/mod.rs | 5 +- .../providers/provider_clear_command.rs | 33 ++++++++++ .../providers/provider_list_command.rs | 60 +++++++++++++++++++ .../providers/provider_set_api_key_command.rs | 39 ++++++++++++ .../provider_credential_loading_cache.rs | 13 ++++ crates/desktop/artcraft/src/lib.rs | 9 +++ 6 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 crates/desktop/artcraft/src/core/commands/providers/provider_clear_command.rs create mode 100644 crates/desktop/artcraft/src/core/commands/providers/provider_list_command.rs create mode 100644 crates/desktop/artcraft/src/core/commands/providers/provider_set_api_key_command.rs diff --git a/crates/desktop/artcraft/src/core/commands/providers/mod.rs b/crates/desktop/artcraft/src/core/commands/providers/mod.rs index f15c614220..74b32e1655 100644 --- a/crates/desktop/artcraft/src/core/commands/providers/mod.rs +++ b/crates/desktop/artcraft/src/core/commands/providers/mod.rs @@ -1 +1,4 @@ -pub mod deprecated; \ No newline at end of file +pub mod deprecated; +pub mod provider_list_command; +pub mod provider_set_api_key_command; +pub mod provider_clear_command; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/core/commands/providers/provider_clear_command.rs b/crates/desktop/artcraft/src/core/commands/providers/provider_clear_command.rs new file mode 100644 index 0000000000..150a7a2dda --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/providers/provider_clear_command.rs @@ -0,0 +1,33 @@ +use crate::core::commands::response::shorthand::ResponseOrErrorMessage; +use crate::core::commands::response::success_response_wrapper::SerializeMarker; +use crate::core::providers::credentials::provider_credential_key::ProviderCredentialKey; +use crate::core::providers::credentials::provider_credential_loading_cache::ProviderCredentialLoadingCache; +use log::{error, info}; +use serde_derive::{Deserialize, Serialize}; +use tauri::State; + +#[derive(Deserialize)] +pub struct ProviderClearRequest { + pub provider_credential: ProviderCredentialKey, +} + +#[derive(Serialize)] +pub struct ProviderClearResponse {} + +impl SerializeMarker for ProviderClearResponse {} + +#[tauri::command] +pub async fn provider_clear_command( + request: ProviderClearRequest, + credential_cache: State<'_, ProviderCredentialLoadingCache>, +) -> ResponseOrErrorMessage { + info!("provider_clear_command called for key: {:?}", request.provider_credential); + + credential_cache.delete_credentials(request.provider_credential) + .map_err(|err| { + error!("Failed to clear provider credential: {:?}", err); + "Failed to clear provider credential" + })?; + + Ok(ProviderClearResponse {}.into()) +} diff --git a/crates/desktop/artcraft/src/core/commands/providers/provider_list_command.rs b/crates/desktop/artcraft/src/core/commands/providers/provider_list_command.rs new file mode 100644 index 0000000000..f479a26194 --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/providers/provider_list_command.rs @@ -0,0 +1,60 @@ +use crate::core::commands::response::shorthand::ResponseOrErrorMessage; +use crate::core::commands::response::success_response_wrapper::SerializeMarker; +use crate::core::providers::credentials::provider_credential_key::ProviderCredentialKey; +use crate::core::providers::credentials::provider_credential_loading_cache::ProviderCredentialLoadingCache; +use crate::core::providers::credentials::provider_credential_type::ProviderCredentialType; +use log::{info, warn}; +use serde_derive::Serialize; +use tauri::State; + +/// All known credential keys. Add new ones here as providers are added. +const ALL_KEYS: &[ProviderCredentialKey] = &[ + ProviderCredentialKey::FalApiKey, + ProviderCredentialKey::ReplicateApiKey, + ProviderCredentialKey::GrokWebLogin, + ProviderCredentialKey::HiggsfieldWebLogin, + ProviderCredentialKey::MidjourneyLogin, + ProviderCredentialKey::RunwayWebLogin, +]; + +#[derive(Serialize)] +pub struct ProviderListResponse { + pub providers: Vec, +} + +impl SerializeMarker for ProviderListResponse {} + +#[derive(Serialize)] +pub struct ProviderListEntry { + pub key: ProviderCredentialKey, + pub credential_type: ProviderCredentialType, + pub has_credentials: bool, +} + +#[tauri::command] +pub async fn provider_list_command( + credential_cache: State<'_, ProviderCredentialLoadingCache>, +) -> ResponseOrErrorMessage { + info!("provider_list_command called"); + + let mut providers = Vec::with_capacity(ALL_KEYS.len()); + + for &key in ALL_KEYS { + let has_credentials = match credential_cache.get_credentials(key) { + Ok(Some(_)) => true, + Ok(None) => false, + Err(err) => { + warn!("Error checking credential for {:?}: {:?}", key, err); + false + } + }; + + providers.push(ProviderListEntry { + key, + credential_type: key.get_type(), + has_credentials, + }); + } + + Ok(ProviderListResponse { providers }.into()) +} diff --git a/crates/desktop/artcraft/src/core/commands/providers/provider_set_api_key_command.rs b/crates/desktop/artcraft/src/core/commands/providers/provider_set_api_key_command.rs new file mode 100644 index 0000000000..fc08ce0f2d --- /dev/null +++ b/crates/desktop/artcraft/src/core/commands/providers/provider_set_api_key_command.rs @@ -0,0 +1,39 @@ +use crate::core::commands::response::shorthand::ResponseOrErrorMessage; +use crate::core::commands::response::success_response_wrapper::SerializeMarker; +use crate::core::providers::credentials::payload::api_key::ApiKeyData; +use crate::core::providers::credentials::payload::provider_credential_payload::ProviderCredentialPayload; +use crate::core::providers::credentials::provider_credential_key::ProviderCredentialKey; +use crate::core::providers::credentials::provider_credential_loading_cache::ProviderCredentialLoadingCache; +use log::{error, info}; +use serde_derive::{Deserialize, Serialize}; +use tauri::State; + +#[derive(Deserialize)] +pub struct ProviderSetApiKeyRequest { + pub provider_credential: ProviderCredentialKey, + pub api_key: String, +} + +#[derive(Serialize)] +pub struct ProviderSetApiKeyResponse {} + +impl SerializeMarker for ProviderSetApiKeyResponse {} + +#[tauri::command] +pub async fn provider_set_api_key_command( + request: ProviderSetApiKeyRequest, + credential_cache: State<'_, ProviderCredentialLoadingCache>, +) -> ResponseOrErrorMessage { + info!("provider_set_api_key_command called for provider: {:?}", request.provider_credential); + + let api_key_data = ApiKeyData::from_str(&request.api_key); + let payload = ProviderCredentialPayload::ApiKey(api_key_data); + + credential_cache.save_credentials(request.provider_credential, payload) + .map_err(|err| { + error!("Failed to save API key: {:?}", err); + "Failed to save API key" + })?; + + Ok(ProviderSetApiKeyResponse {}.into()) +} diff --git a/crates/desktop/artcraft/src/core/providers/credentials/provider_credential_loading_cache.rs b/crates/desktop/artcraft/src/core/providers/credentials/provider_credential_loading_cache.rs index dc206a2ba5..deed0e0636 100644 --- a/crates/desktop/artcraft/src/core/providers/credentials/provider_credential_loading_cache.rs +++ b/crates/desktop/artcraft/src/core/providers/credentials/provider_credential_loading_cache.rs @@ -139,6 +139,19 @@ impl ProviderCredentialLoadingCache { cache.remove(&key); } } + + pub fn delete_credentials(&self, key: ProviderCredentialKey) -> Result<(), ProviderCredentialLoadingCacheError> { + let file_path = self.app_data_root.credentials_dir().path().join(key.get_filename()); + + if file_path.exists() { + std::fs::remove_file(file_path) + .map_err(|e| ProviderCredentialLoadingCacheError::IoError(e))?; + } + + self.invalidate(key); + + Ok(()) + } } #[derive(Debug)] diff --git a/crates/desktop/artcraft/src/lib.rs b/crates/desktop/artcraft/src/lib.rs index bd0c0aca77..47e5c88445 100644 --- a/crates/desktop/artcraft/src/lib.rs +++ b/crates/desktop/artcraft/src/lib.rs @@ -24,6 +24,9 @@ use crate::core::commands::media_files::media_file_delete_command::media_file_de use crate::core::commands::platform_info_command::platform_info_command; use crate::core::commands::providers::deprecated::get_provider_order_command::get_provider_order_command; use crate::core::commands::providers::deprecated::set_provider_order_command::set_provider_order_command; +use crate::core::commands::providers::provider_clear_command::provider_clear_command; +use crate::core::commands::providers::provider_list_command::provider_list_command; +use crate::core::commands::providers::provider_set_api_key_command::provider_set_api_key_command; use crate::core::commands::task_queue::get_task_queue_command::get_task_queue_command; use crate::core::commands::task_queue::mark_task_as_dismissed_command::mark_task_as_dismissed_command; use crate::core::commands::task_queue::tasks_nuke_all_command::tasks_nuke_all_command; @@ -93,6 +96,8 @@ pub fn run() { println!("Loading app preferences..."); let app_preferences = load_app_preferences_or_default(&app_data_root); + let provider_credential_cache = crate::core::providers::credentials::provider_credential_loading_cache::ProviderCredentialLoadingCache::new(app_data_root.clone()); + // NB: tauri-plugin-http stores the credentials on disk, so we can defer to that for now. // println!("Attempting to read existing artcraft credentials..."); // let storyteller_creds_manager = StorytellerCredentialManager::initialize_from_disk_infallible(&app_data_root); @@ -196,6 +201,7 @@ pub fn run() { .manage(sora_task_queue) .manage(storyteller_creds_manager_3) .manage(worldlabs_bearer_bridge) + .manage(provider_credential_cache) .manage(worldlabs_creds_manager); // TODO: Break this out into another module, because RustRover/IntelliJ lags with these macros. @@ -218,6 +224,9 @@ pub fn run() { get_app_preferences_command, get_provider_order_command, get_task_queue_command, + provider_clear_command, + provider_list_command, + provider_set_api_key_command, grok_clear_credentials_command, grok_get_credential_info_command, grok_open_login_command,