From 2ae7e7ad1bb6754ee970eda847f086ad70ad0554 Mon Sep 17 00:00:00 2001 From: ww Date: Wed, 29 Apr 2026 20:31:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20Profile=20clone=20?= =?UTF-8?q?=E6=97=B6=E6=99=BA=E8=83=BD=E6=B8=85=E7=90=86=E7=8B=AC=E5=8D=A0?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E5=87=AD=E6=8D=AE=20+=20=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E7=8B=AC=E5=8D=A0=E8=AD=A6=E5=91=8A=20(#283)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复: profile clone 时智能清理独占平台凭据,避免 gateway 健康检查超时 # 问题 `hermes profile create --clone` 完整复制 .env + config.yaml(含独占型平台凭据 如 WEIXIN_TOKEN / TELEGRAM_BOT_TOKEN 等),导致多个 profile 共享同一身份 token。 hermes-agent 在 platform adapter 初始化或 scoped lock 获取阶段失败,gateway 健康检查 持续 15s 超时,前端报 'API Error 500: Gateway health check timed out'。 # 修复 在 web-ui 后端 clone 完成后自动: 1. 从 /.env 删除匹配独占平台的环境变量(写 .env.bak.* 备份) 2. 在 /config.yaml 中把 platforms..enabled 置为 false 3. 清理节点直挂 + extra 子节点下的敏感字段(token / app_secret / account_id 等) 前端 toast 提示被剥离的凭据、被禁用的平台、被剥离的 config 字段,便于用户后续手动 重新填入新身份再启用。 # EXCLUSIVE_PLATFORMS 列表来源 精确对齐 hermes-agent gateway/platforms/*.py 中调用 _acquire_platform_lock 的 7 个 adapter: telegram, discord, slack, whatsapp, signal, weixin, feishu。 未来上游加新独占平台时用 `grep -l _acquire_platform_lock gateway/platforms/*.py` 验证。 # 测试 新增 tests/server/profile-credentials.test.ts(12 用例全过),覆盖: - isExclusivePlatformKey 命中/未命中边界 - env 文件剥离 + 备份 - config.yaml 平台禁用 + 节点凭据清理 - 已 disabled 平台仍清理残留凭据(防止后续 re-enable 复用旧身份) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(平台设置): 独占平台显示 token 隔离警告 在 PlatformSettings 中为使用 token 互斥锁的 6 个平台 (telegram, discord, slack, whatsapp, feishu, weixin) 添加视觉警告,提示用户每个 profile 必须使用不同的身份 token,避免与其他 profile 冲突。 # 背景 hermes-agent 的 acquire_scoped_lock 是 token-level(不是 platform-level),所以 设计上支持多 profile 各自配不同身份的同一平台(如 default 用个人微信、staging 用公司微信)。但用户从 UI 配置时容易误填同一 token,导致 gateway 启动失败。 # 实现 - PlatformCard 新增 exclusive 可选 prop,开启时 body 顶部用 NAlert (warning) 展示提示 - PlatformSettings 在 6 个独占平台数组项标记 exclusive: true 并传给 PlatformCard - 8 个 i18n locale 新增 platform.exclusiveTokenWarning 翻译 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/client/src/api/hermes/profiles.ts | 28 ++- .../hermes/profiles/ProfileCreateModal.vue | 30 ++- .../hermes/settings/PlatformCard.vue | 11 +- .../hermes/settings/PlatformSettings.vue | 7 + packages/client/src/i18n/locales/de.ts | 5 + packages/client/src/i18n/locales/en.ts | 5 + packages/client/src/i18n/locales/es.ts | 5 + packages/client/src/i18n/locales/fr.ts | 5 + packages/client/src/i18n/locales/ja.ts | 5 + packages/client/src/i18n/locales/ko.ts | 5 + packages/client/src/i18n/locales/pt.ts | 5 + packages/client/src/i18n/locales/zh.ts | 5 + packages/client/src/stores/hermes/profiles.ts | 6 +- .../server/src/controllers/hermes/profiles.ts | 42 +++- .../services/hermes/profile-credentials.ts | 187 ++++++++++++++++ tests/server/profile-credentials.test.ts | 205 ++++++++++++++++++ 16 files changed, 543 insertions(+), 13 deletions(-) create mode 100644 packages/server/src/services/hermes/profile-credentials.ts create mode 100644 tests/server/profile-credentials.test.ts diff --git a/packages/client/src/api/hermes/profiles.ts b/packages/client/src/api/hermes/profiles.ts index 773e674..bacc343 100644 --- a/packages/client/src/api/hermes/profiles.ts +++ b/packages/client/src/api/hermes/profiles.ts @@ -29,15 +29,35 @@ export async function fetchProfileDetail(name: string): Promise { +export interface CreateProfileResult { + success: boolean + /** clone=true 时被清理的独占平台凭据 KEY 名 */ + strippedCredentials?: string[] + /** clone=true 时被禁用的独占平台名 */ + disabledPlatforms?: string[] + /** clone=true 时在 config.yaml 中被清理的内嵌凭据字段路径 */ + strippedConfigCredentials?: string[] +} + +export async function createProfile(name: string, clone?: boolean): Promise { try { - await request('/api/hermes/profiles', { + const res = await request<{ + success: boolean + strippedCredentials?: string[] + disabledPlatforms?: string[] + strippedConfigCredentials?: string[] + }>('/api/hermes/profiles', { method: 'POST', body: JSON.stringify({ name, clone }), }) - return true + return { + success: !!res.success, + strippedCredentials: res.strippedCredentials, + disabledPlatforms: res.disabledPlatforms, + strippedConfigCredentials: res.strippedConfigCredentials, + } } catch { - return false + return { success: false } } } diff --git a/packages/client/src/components/hermes/profiles/ProfileCreateModal.vue b/packages/client/src/components/hermes/profiles/ProfileCreateModal.vue index 39bcd21..72fd041 100644 --- a/packages/client/src/components/hermes/profiles/ProfileCreateModal.vue +++ b/packages/client/src/components/hermes/profiles/ProfileCreateModal.vue @@ -1,6 +1,6 @@