diff --git a/desktop-tauri/src-tauri/server/src/server/core/auto_checkin.rs b/desktop-tauri/src-tauri/server/src/server/core/auto_checkin.rs index 736a73623..7f52997c4 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/auto_checkin.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/auto_checkin.rs @@ -260,7 +260,7 @@ pub fn providers_added_after(version: i64) -> Vec { .collect() } -/// 提供商的展示名(从注册表查,查不到就原样回显 id)。 +/// 提供商的展示名(签到语境的入口,直接委托 `providers::label_of`)。 /// /// 这是**签到语境**的展示名(签到中心的分组、自动签到的提供商勾选共用这一份)。 /// WorkBuddy 两个地区在注册表里就叫「WorkBuddy 国内版 / 国际版」(拆家后的 @@ -269,12 +269,16 @@ pub fn providers_added_after(version: i64) -> Vec { /// 不在标签里堆字。Qoder 拆家(2026-10)后注册名就是「Qoder 中国版 / /// 国际版」,不再需要这里的覆盖。其余几家不需要后缀:小浣熊没有版本区分, /// AutoClaw 两地的签到链路都存在且同形 —— 展示名已带「国内版 / 国际版」。 -pub fn provider_label(id: &str) -> &str { - crate::server::core::providers::PROVIDERS - .iter() - .find(|meta| meta.id == id) - .map(|meta| meta.label) - .unwrap_or(id) +/// +/// ── 为什么不再自己扫 `PROVIDERS` ──────────────────────────── +/// 插件架构下「有签到能力的家」不再只由内置枚举决定:签到的可签清单是 +/// `checkin_providers()`(常量表 + 运行期装入的插件家),而 `PROVIDERS` +/// 里没有插件家的条目。原先这里直接扫 `PROVIDERS`,于是插件家一律回显裸 +/// provider id —— 签到中心的分组标题就成了 `quwork` 这种用户看不懂的串, +/// 而同一页的账号、模型、报表早就通过 `label_of` 拿到了面板名。 +/// 改成委托 `label_of` 后,一处口径覆盖内置家、自定义家与插件家。 +pub fn provider_label(id: &str) -> String { + crate::server::core::providers::label_of(id) } /// 归一化配置里的提供商清单:只认 CHECKIN_PROVIDERS 里的 id(去重、保持顺序), diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/mod.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/mod.rs index b189c5a50..c70174bfa 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/providers/mod.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/mod.rs @@ -436,7 +436,13 @@ pub fn executable_provider_ids() -> Vec { /// ── 回退顺序 ────────────────────────────────────────────── /// 1. `custom-` 前缀 → 先查 `custom_providers::label_of`(用户给的名字优先), /// 查不到(已被删除 / 手改数据塞进来的陌生 id)回显原 id; -/// 2. 其余走注册表:登记的返回 `label`,未登记的原样回显。 +/// 2. 内置家走注册表:登记的返回 `label`; +/// 3. 插件家(`ProviderKind` 枚举里没有的 runtime id)→ 先读**已安装包清单**的 +/// `providers[].label`,再退回**已握手加载**的执行体自报的名字 → 都没有才回显原 id。 +/// +/// 第 3 步的两级顺序不是冗余:清单在包没跑起来时也读得到(宿主崩溃 / 用户停用 / +/// 网关刚启动握手未完成),执行体只在这家真的加载后才有。只查执行体会让「停用的 +/// 插件家」在界面上掉回一串英文 id —— 用户看不出那是什么。 /// /// 回显成 id 而不是「未知」的理由不变:调用点手上的 id 来自真实数据(账号 /// 记录、`owned_by`、日志),真出现未登记的 id 时,回显原文比一句笼统的 @@ -455,9 +461,13 @@ pub fn label_of(id: &str) -> String { match kind_from_id(id) { // 内置家优先读注册表:那是既有文案契约(插件接管后也不该改字)。 Some(kind) => meta(kind).label.to_string(), - // runtime id 不靠占位 kind 取名:插件登记优先,最后回显原 ID。 - None => crate::server::core::plugin_host::lookup(id) - .map(|adapter| adapter.label().to_string()) + // runtime id 不靠占位 kind 取名:清单优先(停用/未握手也读得到), + // 再退回已加载执行体,最后回显原 ID。 + None => crate::server::core::plugin_host::provider_label(id) + .or_else(|| { + crate::server::core::plugin_host::lookup(id) + .map(|adapter| adapter.label().to_string()) + }) .unwrap_or_else(|| id.to_string()), } }