//! FR-S1 api_key 密钥管理 — **转发壳(方案 B 下沉,2026-06-16)** //! //! 纯密钥逻辑(get/set/delete/resolve/ensure/migrate + failcount sidecar)已下沉到 //! `df_storage::secret`(密钥解析唯一源,供 df-nodes AiNode 与本转发壳共用,DRY)。 //! //! 本文件保留两项职责: //! 1. **转发**:`pub use df_storage::secret::*;` —— 保 src-tauri 内调用路径 //! `crate::commands::ai::secret::*` / `super::secret::*` 不变(12 调用点零改动)。 //! 2. **`build_provider_for`**:消费点统一入口(resolve→ensure→`df_ai::build_provider` 三步打包)。 //! 此函数依赖 `df_ai::build_provider`,**不下沉** —— df-storage 不依赖 df-ai, //! 下沉会引 df-storage→df-ai 反向依赖(循环)。src-tauri 既依赖 df-storage 又依赖 df-ai, //! 天然适合拼装,故留 app 层。 //! //! 下沉前原文件全貌见 git 历史(commit c10adaf 前 src-tauri/src/commands/ai/secret.rs:1-228)。 // 转发所有下沉的纯密钥函数,保 src-tauri 内调用路径不变。 // 不再额外 `use df_storage::secret::{...}` 显式导入 —— 会与 glob re-export 冲突 // (编译器判 private function import)。build_provider_for 内用全限定路径调即可。 #[allow(unused_imports)] pub use df_storage::secret::*; use df_storage::models::AiProviderRecord; /// 消费点统一入口:resolve_provider_secret → ensure_resolved_key → build_provider 三步打包。 /// /// 替代散落各处的「resolve + build_provider」二件套复制(漏 ensure_resolved_key → keyring 读不到时 /// 空 key 照发请求吃 401,用户看「key 已保存」反复重试无解)。返 Result 让调用方按场景处理: /// - 主链(agentic loop):空 key 早失败 emit AiError /// - 后台 task(title/knowledge 提炼/embedding):Err 上层降级/记 log /// /// **不下沉原因**:依赖 `df_ai::build_provider`,下沉 df-storage 会引 df-storage→df-ai 循环依赖。 pub fn build_provider_for( record: &AiProviderRecord, ) -> Result, String> { let api_key = df_storage::secret::resolve_provider_secret(record); let api_key = df_storage::secret::ensure_resolved_key(&record.name, &api_key)?; Ok(df_ai::build_provider( &record.provider_type, &record.base_url, &api_key, &record.default_model, )) }