@lsamien/yaovi 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/build-info.json +15 -4
  2. package/desktop/main.mjs +1 -1
  3. package/dist/assets/{AboutDialog-CnNkIRo8.js → AboutDialog-CDpS-zAu.js} +2 -2
  4. package/dist/assets/{AboutDialog-CnNkIRo8.js.map → AboutDialog-CDpS-zAu.js.map} +1 -1
  5. package/dist/assets/{BotAutomationsView-J9Y7Unoq.js → BotAutomationsView-RXBQzdfc.js} +2 -2
  6. package/dist/assets/{BotAutomationsView-J9Y7Unoq.js.map → BotAutomationsView-RXBQzdfc.js.map} +1 -1
  7. package/dist/assets/{BotControlSettingsPanel-CtXD24FZ.js → BotControlSettingsPanel-GQEVHXbw.js} +2 -2
  8. package/dist/assets/{BotControlSettingsPanel-CtXD24FZ.js.map → BotControlSettingsPanel-GQEVHXbw.js.map} +1 -1
  9. package/dist/assets/BotPluginsDialog-HIhXqyGn.js +2 -0
  10. package/dist/assets/{BotPluginsDialog-qRw8dCbX.js.map → BotPluginsDialog-HIhXqyGn.js.map} +1 -1
  11. package/dist/assets/{BotSettingsPanel-DbCMOfAW.js → BotSettingsPanel-Bkqal3lR.js} +3 -3
  12. package/dist/assets/{BotSettingsPanel-DbCMOfAW.js.map → BotSettingsPanel-Bkqal3lR.js.map} +1 -1
  13. package/dist/assets/{ComputerPanel-CI-jDmC3.js → ComputerPanel-Dt73pgTG.js} +2 -2
  14. package/dist/assets/{ComputerPanel-CI-jDmC3.js.map → ComputerPanel-Dt73pgTG.js.map} +1 -1
  15. package/dist/assets/{ComputerViewerView-B76D6Fln.js → ComputerViewerView-ChYgaS9q.js} +2 -2
  16. package/dist/assets/{ComputerViewerView-B76D6Fln.js.map → ComputerViewerView-ChYgaS9q.js.map} +1 -1
  17. package/dist/assets/{ConversationsView-DsuaLKva.js → ConversationsView-DzcDJZh3.js} +88 -88
  18. package/dist/assets/ConversationsView-DzcDJZh3.js.map +1 -0
  19. package/dist/assets/ConversationsView-EdF5AM1T.css +1 -0
  20. package/dist/assets/{LocalVmSettingsPanel-BfXFdMYF.js → LocalVmSettingsPanel-DLbDb5Tq.js} +4 -4
  21. package/dist/assets/{LocalVmSettingsPanel-BfXFdMYF.js.map → LocalVmSettingsPanel-DLbDb5Tq.js.map} +1 -1
  22. package/dist/assets/{ModelNetworkChoice-DB6cLOBw.js → ModelNetworkChoice-Cfyt4Unl.js} +2 -2
  23. package/dist/assets/{ModelNetworkChoice-DB6cLOBw.js.map → ModelNetworkChoice-Cfyt4Unl.js.map} +1 -1
  24. package/dist/assets/{StandaloneDialog-BWUp2Wxo.js → StandaloneDialog-Cte829Ai.js} +2 -2
  25. package/dist/assets/{StandaloneDialog-BWUp2Wxo.js.map → StandaloneDialog-Cte829Ai.js.map} +1 -1
  26. package/dist/assets/{UpdateCheckDialog-CpZSPlUI.js → UpdateCheckDialog-BaMo_M4n.js} +2 -2
  27. package/dist/assets/{UpdateCheckDialog-CpZSPlUI.js.map → UpdateCheckDialog-BaMo_M4n.js.map} +1 -1
  28. package/dist/assets/{UsageView-BhiRCw7p.js → UsageView-CklMPG4n.js} +3 -3
  29. package/dist/assets/{UsageView-BhiRCw7p.js.map → UsageView-CklMPG4n.js.map} +1 -1
  30. package/dist/assets/WorkspaceKnowledgePanel-BdbhD3rO.js +3 -0
  31. package/dist/assets/{WorkspaceKnowledgePanel-D34YxglF.js.map → WorkspaceKnowledgePanel-BdbhD3rO.js.map} +1 -1
  32. package/dist/assets/WorkspaceRoutinesPanel-VpRoMe1x.js +2 -0
  33. package/dist/assets/{WorkspaceRoutinesPanel-Bn-W52Im.js.map → WorkspaceRoutinesPanel-VpRoMe1x.js.map} +1 -1
  34. package/dist/assets/{WorkstreamsView-CH6qDPvj.js → WorkstreamsView-BwZpuuhs.js} +2 -2
  35. package/dist/assets/{WorkstreamsView-CH6qDPvj.js.map → WorkstreamsView-BwZpuuhs.js.map} +1 -1
  36. package/dist/assets/index-C-5Gq82f.css +1 -0
  37. package/dist/assets/{index-DjhQ-RMG.js → index-Cx1SdzRY.js} +18 -18
  38. package/dist/assets/index-Cx1SdzRY.js.map +1 -0
  39. package/dist/assets/{pdf-wqiF06Qz.js → pdf-CoNh_Rmj.js} +2 -2
  40. package/dist/assets/{pdf-wqiF06Qz.js.map → pdf-CoNh_Rmj.js.map} +1 -1
  41. package/dist/index.html +2 -2
  42. package/dist-server/server/remoteAccess/dispatcher.js +5 -1
  43. package/node_modules/@yaovi/remote-core/index.mjs +13 -10
  44. package/node_modules/@yaovi/remote-core/package.json +2 -2
  45. package/node_modules/@yaovi/remote-core/types/remote-core/src/control/recovery.d.ts +1 -1
  46. package/node_modules/@yaovi/remote-core/types/remote-core/src/control/remote.d.ts +2 -0
  47. package/node_modules/@yaovi/remote-core/types/remote-core/src/index.d.ts +1 -0
  48. package/node_modules/@yaovi/remote-core/types/remote-core/src/transport/lan.d.ts +10 -4
  49. package/node_modules/@yaovi/remote-core/types/remote-core/src/transport/packet.d.ts +2 -2
  50. package/package.json +2 -2
  51. package/release.json +3 -3
  52. package/vendor/yaovi-remote-core-0.2.0-remote.18.tgz +0 -0
  53. package/dist/assets/BotPluginsDialog-qRw8dCbX.js +0 -2
  54. package/dist/assets/ConversationsView-DoA0eNiz.css +0 -1
  55. package/dist/assets/ConversationsView-DsuaLKva.js.map +0 -1
  56. package/dist/assets/WorkspaceKnowledgePanel-D34YxglF.js +0 -3
  57. package/dist/assets/WorkspaceRoutinesPanel-Bn-W52Im.js +0 -2
  58. package/dist/assets/index-DjhQ-RMG.js.map +0 -1
  59. package/dist/assets/index-DrIgDgnN.css +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"BotPluginsDialog-qRw8dCbX.js","names":[],"sources":["../../src/client/components/workspace/NativePluginsPanel.vue","../../src/client/components/workspace/NativePluginsPanel.vue","../../../../../provider-logos/gmail.svg","../../src/client/components/workspace/GmailPanel.vue","../../src/client/components/workspace/GmailPanel.vue","../../src/client/components/workspace/LocalAppsPanel.vue","../../src/client/components/workspace/LocalAppsPanel.vue","../../src/client/components/workspace/ComposioAppsPanel.vue","../../src/client/components/workspace/ComposioAppsPanel.vue","../../src/client/components/workspace/BotPluginsPanel.vue","../../src/client/components/workspace/BotPluginsPanel.vue","../../src/client/components/workspace/BotPluginsDialog.vue","../../src/client/components/workspace/BotPluginsDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed,onMounted,ref } from 'vue'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport { apiRequest } from '@/api/client'\nconst base='/api/app/admin/hermes/local/plugins'\ntype Plugin={name:string;title:string;description:string;sha:string;version:string;category:string;compatibility:string;compatibilityNote:string;repo:string;capabilities:Record<string,unknown>;installed?:{enabled:boolean;sha:string;version:string;dependencies?:string[];lastError?:string}}\nconst items=ref<Plugin[]>([]),search=ref(''),onlyInstalled=ref(false),busy=ref(false),error=ref(''),notice=ref(''),pending=ref<Plugin>(),review=ref<any>()\nconst filtered=computed(()=>items.value.filter(i=>(!onlyInstalled.value||i.installed)&&`${i.name} ${i.title} ${i.description}`.toLowerCase().includes(search.value.toLowerCase())))\nasync function load(){items.value=(await apiRequest<{plugins:Plugin[]}>(base+'/catalog')).plugins}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'插件操作失败'}finally{busy.value=false}}\nasync function install(i:Plugin,acceptChanges=false){await action(async()=>{const r=await apiRequest<any>(base+'/install',{method:'POST',timeoutMs:610_000,body:{name:i.name,sha:i.sha,acceptChanges}});if(r.consent_required){pending.value=i;review.value=r;notice.value='更新版本可能改变能力和依赖,请审阅后确认。';return}if(!r.ok)throw new Error(r.error??'插件未安装');pending.value=undefined;notice.value='安装完成。插件保持停用,启用时检查宿主兼容性。';await load()})}\nasync function toggle(i:Plugin){await action(async()=>{const r=await apiRequest<any>(base+'/enabled',{method:'POST',body:{name:i.name,enabled:!i.installed?.enabled}});if(!r.ok)throw new Error(r.error??'插件状态未改变');await load()})}\nasync function remove(i:Plugin){if(!window.confirm('移除 '+i.name+'?原包会保留在主机备份目录。'))return;await action(async()=>{await apiRequest(base+'/remove',{method:'POST',body:{name:i.name}});await load()})}\nonMounted(()=>action(load))\n</script>\n<template>\n <section class=\"integration-panel plugins\" aria-label=\"本地原生插件\">\n <header class=\"integration-header\"><div><h3>发现更多能力</h3><p class=\"catalog-intro\">{{items.length}} 项本地插件,按固定版本安装。</p></div><button class=\"quiet\" :disabled=\"busy\" aria-label=\"刷新本地目录\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button></header>\n <details class=\"plugin-trust\"><summary><AppIcon name=\"shield\" :size=\"17\" />仅安装你信任的插件</summary><p>插件运行在主机上,可访问文件和网络。Bot 权限控制工具入口,不能隔离插件的初始化或内部行为。桌面、Hook、供应商等能力需要各自宿主适配,安装成功不代表已可用。</p></details>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\"><AppIcon name=\"alert\" :size=\"17\" />{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\"><AppIcon name=\"check\" :size=\"17\" />{{notice}}</p>\n <div class=\"integration-toolbar\"><label class=\"integration-search\"><span class=\"sr-only\">搜索插件</span><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"search\" type=\"search\" placeholder=\"搜索插件名称或用途\"></label><label class=\"integration-check\"><input v-model=\"onlyInstalled\" type=\"checkbox\">仅显示已安装</label></div>\n <article v-if=\"pending\" class=\"integration-card update-review\"><span class=\"integration-tag warning\">需要审阅</span><h4>审阅 {{pending.name}} 更新</h4><p>{{pending.description}}</p><p>版本 {{pending.version}} · {{pending.sha}}</p><div class=\"capability-compare\"><div><h4>旧能力</h4><pre>{{JSON.stringify(review?.oldCapabilities,null,2)}}</pre></div><div><h4>新能力</h4><pre>{{JSON.stringify(review?.newCapabilities,null,2)}}</pre></div></div><h4>当前依赖</h4><pre>{{review?.oldDependencies}}</pre><p>新版本按固定 SHA 扫描并校验实际能力;依赖安装受 Host 版本约束,安装后保持停用。</p><div class=\"integration-actions\"><button class=\"primary\" :disabled=\"busy\" @click=\"install(pending!,true)\">确认更新到此版本</button><button @click=\"pending=undefined\">取消更新</button></div></article>\n <div class=\"catalog-results\"><span class=\"integration-count\">{{search||onlyInstalled?'匹配 '+filtered.length+' 项':'全部 '+items.length+' 项'}}</span><span class=\"integration-count\">安装后默认停用</span></div>\n <ul class=\"plugins-grid\"><li v-for=\"i in filtered\" :key=\"i.name\" class=\"integration-card plugin-card\">\n <div class=\"plugin-heading\"><span class=\"integration-mark\"><AppIcon :name=\"i.compatibility==='tools'?'tools':i.category==='desktop'?'monitor':'artifacts'\" :size=\"20\" /></span><div><h4>{{i.title||i.name}}</h4><span class=\"plugin-version\">v{{i.version}}</span></div><span v-if=\"i.installed\" class=\"integration-tag\" :class=\"{success:i.installed.enabled}\">{{i.installed.enabled?'已启用':'已安装'}}</span></div>\n <p class=\"plugin-description\">{{i.description}}</p>\n <p class=\"compatibility\"><span class=\"integration-tag\" :class=\"{success:i.compatibility==='tools',warning:i.compatibility!=='tools'}\">{{i.compatibility==='tools'?'工具可接入':'需适配'}}</span>{{i.compatibilityNote}}</p>\n <details class=\"plugin-details\"><summary>来源、能力与依赖</summary><p>{{i.description}}</p><a :href=\"i.repo\" target=\"_blank\" rel=\"noopener noreferrer\">查看源码 ↗</a><p class=\"source-sha\">固定版本:{{i.sha}}</p><pre>{{JSON.stringify(i.capabilities,null,2)}}</pre><p v-if=\"i.installed?.dependencies?.length\">已安装依赖:{{i.installed.dependencies.join('、')}}</p></details>\n <p v-if=\"i.installed?.lastError\" class=\"plugin-failure\">{{i.installed.lastError}}</p>\n <div class=\"integration-actions plugin-footer\"><button v-if=\"!i.installed||i.installed.sha!==i.sha\" :disabled=\"busy\" @click=\"install(i)\"><AppIcon :name=\"i.installed?'refresh':'plus'\" :size=\"15\" />{{i.installed?'检查并更新':'安装固定版本'}}</button><button v-if=\"i.installed\" :class=\"{primary:!i.installed.enabled}\" :disabled=\"busy||!i.installed.enabled&&i.compatibility!=='tools'\" @click=\"toggle(i)\">{{i.installed.enabled?'停用':'检查并启用工具'}}</button><button v-if=\"i.installed\" class=\"quiet danger\" :disabled=\"busy\" @click=\"remove(i)\">移除</button></div>\n </li></ul>\n <div v-if=\"!busy&&!filtered.length\" class=\"integration-empty\"><AppIcon name=\"search\" :size=\"28\" /><strong>没有匹配的插件</strong><p>试试其他关键词,或取消已安装筛选。</p></div>\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.catalog-intro{margin-top:5px!important;font-size:12px}.plugin-trust{padding:0 12px;border-radius:10px;background:var(--surface-soft)}.plugin-trust p{padding:0 0 13px;font-size:12px}.catalog-results{display:flex;justify-content:space-between;gap:12px}.plugins-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:0;padding:0;list-style:none}.plugin-card{display:flex;flex-direction:column;gap:13px;padding:18px}.plugin-heading{display:flex;align-items:center;gap:10px}.plugin-heading>div{flex:1;min-width:0}.plugin-heading h4{overflow-wrap:anywhere;line-height:1.4}.plugin-heading .integration-mark{width:36px;height:36px;border-radius:10px}.plugin-version{font-size:11px;color:var(--text-muted)}.plugin-description{font-size:12px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden;line-height:1.7}.compatibility{font-size:11px;line-height:1.7}.compatibility .integration-tag{margin-right:6px}.plugin-details{margin-top:auto;border-top:1px solid var(--line);padding-top:3px;min-width:0}.plugin-details summary{font-size:11px}.plugin-details p,.plugin-details a{font-size:12px}.plugin-details p{margin-bottom:10px}.source-sha{margin-top:10px!important;font-family:var(--font-code);overflow-wrap:anywhere}.plugin-footer{padding-top:2px}.plugin-footer button{font-size:12px}.plugin-failure{color:var(--danger)!important;overflow-wrap:anywhere}.update-review{display:grid;gap:12px;border-color:color-mix(in srgb,var(--warning) 35%,var(--line))}.capability-compare{display:grid;grid-template-columns:1fr 1fr;gap:12px;min-width:0}.capability-compare>div{min-width:0}@media(max-width:700px){.plugins-grid{grid-template-columns:1fr}}@media(max-width:420px){.capability-compare{grid-template-columns:1fr}.plugin-heading{flex-wrap:wrap}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed,onMounted,ref } from 'vue'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport { apiRequest } from '@/api/client'\nconst base='/api/app/admin/hermes/local/plugins'\ntype Plugin={name:string;title:string;description:string;sha:string;version:string;category:string;compatibility:string;compatibilityNote:string;repo:string;capabilities:Record<string,unknown>;installed?:{enabled:boolean;sha:string;version:string;dependencies?:string[];lastError?:string}}\nconst items=ref<Plugin[]>([]),search=ref(''),onlyInstalled=ref(false),busy=ref(false),error=ref(''),notice=ref(''),pending=ref<Plugin>(),review=ref<any>()\nconst filtered=computed(()=>items.value.filter(i=>(!onlyInstalled.value||i.installed)&&`${i.name} ${i.title} ${i.description}`.toLowerCase().includes(search.value.toLowerCase())))\nasync function load(){items.value=(await apiRequest<{plugins:Plugin[]}>(base+'/catalog')).plugins}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'插件操作失败'}finally{busy.value=false}}\nasync function install(i:Plugin,acceptChanges=false){await action(async()=>{const r=await apiRequest<any>(base+'/install',{method:'POST',timeoutMs:610_000,body:{name:i.name,sha:i.sha,acceptChanges}});if(r.consent_required){pending.value=i;review.value=r;notice.value='更新版本可能改变能力和依赖,请审阅后确认。';return}if(!r.ok)throw new Error(r.error??'插件未安装');pending.value=undefined;notice.value='安装完成。插件保持停用,启用时检查宿主兼容性。';await load()})}\nasync function toggle(i:Plugin){await action(async()=>{const r=await apiRequest<any>(base+'/enabled',{method:'POST',body:{name:i.name,enabled:!i.installed?.enabled}});if(!r.ok)throw new Error(r.error??'插件状态未改变');await load()})}\nasync function remove(i:Plugin){if(!window.confirm('移除 '+i.name+'?原包会保留在主机备份目录。'))return;await action(async()=>{await apiRequest(base+'/remove',{method:'POST',body:{name:i.name}});await load()})}\nonMounted(()=>action(load))\n</script>\n<template>\n <section class=\"integration-panel plugins\" aria-label=\"本地原生插件\">\n <header class=\"integration-header\"><div><h3>发现更多能力</h3><p class=\"catalog-intro\">{{items.length}} 项本地插件,按固定版本安装。</p></div><button class=\"quiet\" :disabled=\"busy\" aria-label=\"刷新本地目录\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button></header>\n <details class=\"plugin-trust\"><summary><AppIcon name=\"shield\" :size=\"17\" />仅安装你信任的插件</summary><p>插件运行在主机上,可访问文件和网络。Bot 权限控制工具入口,不能隔离插件的初始化或内部行为。桌面、Hook、供应商等能力需要各自宿主适配,安装成功不代表已可用。</p></details>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\"><AppIcon name=\"alert\" :size=\"17\" />{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\"><AppIcon name=\"check\" :size=\"17\" />{{notice}}</p>\n <div class=\"integration-toolbar\"><label class=\"integration-search\"><span class=\"sr-only\">搜索插件</span><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"search\" type=\"search\" placeholder=\"搜索插件名称或用途\"></label><label class=\"integration-check\"><input v-model=\"onlyInstalled\" type=\"checkbox\">仅显示已安装</label></div>\n <article v-if=\"pending\" class=\"integration-card update-review\"><span class=\"integration-tag warning\">需要审阅</span><h4>审阅 {{pending.name}} 更新</h4><p>{{pending.description}}</p><p>版本 {{pending.version}} · {{pending.sha}}</p><div class=\"capability-compare\"><div><h4>旧能力</h4><pre>{{JSON.stringify(review?.oldCapabilities,null,2)}}</pre></div><div><h4>新能力</h4><pre>{{JSON.stringify(review?.newCapabilities,null,2)}}</pre></div></div><h4>当前依赖</h4><pre>{{review?.oldDependencies}}</pre><p>新版本按固定 SHA 扫描并校验实际能力;依赖安装受 Host 版本约束,安装后保持停用。</p><div class=\"integration-actions\"><button class=\"primary\" :disabled=\"busy\" @click=\"install(pending!,true)\">确认更新到此版本</button><button @click=\"pending=undefined\">取消更新</button></div></article>\n <div class=\"catalog-results\"><span class=\"integration-count\">{{search||onlyInstalled?'匹配 '+filtered.length+' 项':'全部 '+items.length+' 项'}}</span><span class=\"integration-count\">安装后默认停用</span></div>\n <ul class=\"plugins-grid\"><li v-for=\"i in filtered\" :key=\"i.name\" class=\"integration-card plugin-card\">\n <div class=\"plugin-heading\"><span class=\"integration-mark\"><AppIcon :name=\"i.compatibility==='tools'?'tools':i.category==='desktop'?'monitor':'artifacts'\" :size=\"20\" /></span><div><h4>{{i.title||i.name}}</h4><span class=\"plugin-version\">v{{i.version}}</span></div><span v-if=\"i.installed\" class=\"integration-tag\" :class=\"{success:i.installed.enabled}\">{{i.installed.enabled?'已启用':'已安装'}}</span></div>\n <p class=\"plugin-description\">{{i.description}}</p>\n <p class=\"compatibility\"><span class=\"integration-tag\" :class=\"{success:i.compatibility==='tools',warning:i.compatibility!=='tools'}\">{{i.compatibility==='tools'?'工具可接入':'需适配'}}</span>{{i.compatibilityNote}}</p>\n <details class=\"plugin-details\"><summary>来源、能力与依赖</summary><p>{{i.description}}</p><a :href=\"i.repo\" target=\"_blank\" rel=\"noopener noreferrer\">查看源码 ↗</a><p class=\"source-sha\">固定版本:{{i.sha}}</p><pre>{{JSON.stringify(i.capabilities,null,2)}}</pre><p v-if=\"i.installed?.dependencies?.length\">已安装依赖:{{i.installed.dependencies.join('、')}}</p></details>\n <p v-if=\"i.installed?.lastError\" class=\"plugin-failure\">{{i.installed.lastError}}</p>\n <div class=\"integration-actions plugin-footer\"><button v-if=\"!i.installed||i.installed.sha!==i.sha\" :disabled=\"busy\" @click=\"install(i)\"><AppIcon :name=\"i.installed?'refresh':'plus'\" :size=\"15\" />{{i.installed?'检查并更新':'安装固定版本'}}</button><button v-if=\"i.installed\" :class=\"{primary:!i.installed.enabled}\" :disabled=\"busy||!i.installed.enabled&&i.compatibility!=='tools'\" @click=\"toggle(i)\">{{i.installed.enabled?'停用':'检查并启用工具'}}</button><button v-if=\"i.installed\" class=\"quiet danger\" :disabled=\"busy\" @click=\"remove(i)\">移除</button></div>\n </li></ul>\n <div v-if=\"!busy&&!filtered.length\" class=\"integration-empty\"><AppIcon name=\"search\" :size=\"28\" /><strong>没有匹配的插件</strong><p>试试其他关键词,或取消已安装筛选。</p></div>\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.catalog-intro{margin-top:5px!important;font-size:12px}.plugin-trust{padding:0 12px;border-radius:10px;background:var(--surface-soft)}.plugin-trust p{padding:0 0 13px;font-size:12px}.catalog-results{display:flex;justify-content:space-between;gap:12px}.plugins-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:0;padding:0;list-style:none}.plugin-card{display:flex;flex-direction:column;gap:13px;padding:18px}.plugin-heading{display:flex;align-items:center;gap:10px}.plugin-heading>div{flex:1;min-width:0}.plugin-heading h4{overflow-wrap:anywhere;line-height:1.4}.plugin-heading .integration-mark{width:36px;height:36px;border-radius:10px}.plugin-version{font-size:11px;color:var(--text-muted)}.plugin-description{font-size:12px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden;line-height:1.7}.compatibility{font-size:11px;line-height:1.7}.compatibility .integration-tag{margin-right:6px}.plugin-details{margin-top:auto;border-top:1px solid var(--line);padding-top:3px;min-width:0}.plugin-details summary{font-size:11px}.plugin-details p,.plugin-details a{font-size:12px}.plugin-details p{margin-bottom:10px}.source-sha{margin-top:10px!important;font-family:var(--font-code);overflow-wrap:anywhere}.plugin-footer{padding-top:2px}.plugin-footer button{font-size:12px}.plugin-failure{color:var(--danger)!important;overflow-wrap:anywhere}.update-review{display:grid;gap:12px;border-color:color-mix(in srgb,var(--warning) 35%,var(--line))}.capability-compare{display:grid;grid-template-columns:1fr 1fr;gap:12px;min-width:0}.capability-compare>div{min-width:0}@media(max-width:700px){.plugins-grid{grid-template-columns:1fr}}@media(max-width:420px){.capability-compare{grid-template-columns:1fr}.plugin-heading{flex-wrap:wrap}}\n</style>\n","export default \"__VITE_PUBLIC_ASSET__c5ebe966__\"","<script setup lang=\"ts\">\nimport { computed,onMounted,ref,watch } from 'vue'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport DOMPurify from 'dompurify'\nimport { apiRequest } from '@/api/client'\nimport type { GoogleAccount,GoogleOperation } from '@shared/googleWorkspace'\nconst props=withDefaults(defineProps<{accounts:GoogleAccount[];source?:'local'|'composio'}>(),{source:'local'})\nconst sourceName=computed(()=>props.source==='composio'?'Composio 托管':'本地连接')\nconst base=(source=props.source)=>source==='composio'?'/api/app/admin/composio':'/api/app/admin/hermes/local/google'\nconst account=ref(props.accounts[0]?.id??''),query=ref('in:inbox'),messages=ref<any[]>([]),message=ref<any>(),busy=ref(false),error=ref(''),notice=ref(''),compose=ref(false),reply=ref(false),to=ref(''),subject=ref(''),body=ref(''),labels=ref(''),labelOptions=ref<any[]>([]),unknown=ref(false)\nlet generation=0,reloadQueued=false\nlet draftTarget:{accountId:string;messageId?:string;operationId:string;source:'local'|'composio'}|undefined\nconst uncertain=ref<{accountId:string;operationId:string;source:'local'|'composio'}>()\nconst header=(m:any,name:string)=>m?.payload?.headers?.find((h:any)=>h.name.toLowerCase()===name.toLowerCase())?.value??''\nasync function call(operation:GoogleOperation,args:Record<string,unknown>,write=false,accountId=account.value,operationId?:string,source=props.source){\n const id=write?(operationId??crypto.randomUUID()):undefined\n try{\n const result=await apiRequest<any>(base(source)+'/call',{method:'POST',timeoutMs:95_000,body:{accountId,operation,arguments:args as any,...(write?{operationId:id}:{})}})\n if(result?.isError){if(write&&result.outcomeUnknown){unknown.value=true;uncertain.value={accountId,operationId:id!,source}}const e=new Error(result.error?.message??'Google 请求失败');Object.assign(e,{known:!result.outcomeUnknown});throw e}\n return result\n }catch(e){\n if(source==='composio'&&[400,401,403,404,409,413,422].includes((e as {status?:number})?.status??0))Object.assign(e as object,{known:true})\n if(write&&!(e as any)?.known){unknown.value=true;uncertain.value={accountId,operationId:id!,source}}\n throw e\n }\n}\nasync function action(fn:()=>Promise<void>){if(busy.value){if(fn===load)reloadQueued=true;return};busy.value=true;error.value='';notice.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'邮箱操作失败'}finally{busy.value=false;if(reloadQueued){reloadQueued=false;void action(load)}}}\nasync function load(){\n if(!account.value)return\n const version=++generation,selected=account.value,origin=props.source;message.value=undefined\n const result=await call('gmail.search',{query:query.value,max:15},false,selected,undefined,origin),rows=[]\n if(version!==generation||selected!==account.value||origin!==props.source)return\n for(const row of result.messages??[]){const detail=await call('gmail.get',{id:row.id},false,selected,undefined,origin);if(version!==generation||selected!==account.value)return;rows.push(detail)}\n const available=await call('gmail.labels',{},false,selected,undefined,origin)\n if(version===generation&&selected===account.value){messages.value=rows;labelOptions.value=available.labels??[]}\n}\nasync function open(m:any){await action(async()=>{const selected=account.value,version=generation,origin=props.source;const result=await call('gmail.get',{id:m.id},false,selected,undefined,origin);if(selected===account.value&&version===generation)message.value=result})}\nasync function checkReceipt(){await action(async()=>{if(!uncertain.value)return;const r=await apiRequest<any>(base(uncertain.value.source)+'/receipts/'+uncertain.value.accountId+'/'+encodeURIComponent(uncertain.value.operationId));notice.value=r.status==='complete'?'邮箱操作已完成,已读取保存的结果。':r.status==='rejected'?'邮箱服务已拒绝此操作。':'仍无确认结果,请在 Gmail 核对。';if(['complete','rejected'].includes(r.status)){unknown.value=false;compose.value=false;draftTarget=undefined;uncertain.value=undefined;await load()}})}\nfunction decode(data:string){const bytes=Uint8Array.from(atob(data.replace(/-/g,'+').replace(/_/g,'/')),c=>c.charCodeAt(0));return new TextDecoder().decode(bytes)}\nfunction mailBody(payload:any):string{if(!payload)return '';if(payload.mimeType==='text/plain'&&payload.body?.data)return '<pre>'+decode(payload.body.data).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\n/g,'<br>')+'</pre>';const parts=payload.parts??[];const plain=parts.find((p:any)=>p.mimeType==='text/plain');if(plain)return mailBody(plain);if(payload.mimeType==='text/html'&&payload.body?.data)return DOMPurify.sanitize(decode(payload.body.data),{FORBID_TAGS:['img','style','script','iframe','form','svg','math','video','audio','link'],FORBID_ATTR:['style','src','srcset','background']});return parts.map(mailBody).join('')}\nfunction attachments(payload:any):any[]{return [...(payload?.filename&&(payload?.body?.attachmentId||payload?.body?.data)?[payload]:[]),...(payload?.parts??[]).flatMap(attachments)]}\nasync function download(part:any){await action(async()=>{const result=part.body.data?part.body:await call('gmail.attachment',{id:message.value.id,attachmentId:part.body.attachmentId});if(result.size>25*1024*1024||String(result.data??'').length>25*1024*1024*4/3+4)throw new Error('附件超过25MB限制');const bytes=Uint8Array.from(atob(result.data.replace(/-/g,'+').replace(/_/g,'/')),c=>c.charCodeAt(0));const url=URL.createObjectURL(new Blob([bytes],{type:'application/octet-stream'}));const link=document.createElement('a');link.href=url;link.download=String(part.filename).replace(/[\\\\/\\x00-\\x1f]/g,'_').slice(0,180)||'attachment';document.body.append(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(url),30000)})}\nfunction draft(isReply=false){if(unknown.value)return;draftTarget={accountId:account.value,...(isReply?{messageId:message.value.id}:{}),operationId:crypto.randomUUID(),source:props.source};reply.value=isReply;compose.value=true;to.value=isReply?(header(message.value,'Reply-To')||header(message.value,'From')):'';subject.value=isReply?'Re: '+header(message.value,'Subject'):'';body.value=''}\nasync function send(){await action(async()=>{\n const target=draftTarget;if(!target||target.accountId!==account.value||target.source!==props.source||unknown.value)throw new Error('草稿账号已改变,请重新编辑。')\n try{const result=await call(target.messageId?'gmail.reply':'gmail.send',target.messageId?{id:target.messageId,body:body.value}:{to:to.value,subject:subject.value,body:body.value},true,target.accountId,target.operationId,target.source);if(target.accountId!==account.value||target.source!==props.source)return;compose.value=false;draftTarget=undefined;notice.value='邮件已发送,消息编号:'+(result.id??'已确认');if(result.id)message.value=await call('gmail.get',{id:result.id},false,target.accountId,undefined,target.source)}catch(e){if(!unknown.value&&draftTarget)draftTarget.operationId=crypto.randomUUID();throw e}\n})}\nasync function mutate(operation:GoogleOperation,args:Record<string,unknown>){if(unknown.value)return;await action(async()=>{const selected=account.value;await call(operation,args,true,selected);await load();notice.value='邮箱操作已完成。'})}\nwatch(()=>props.accounts.map(a=>a.id).join(','),()=>{if(!props.accounts.some(a=>a.id===account.value)){account.value=props.accounts[0]?.id??'';generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined}})\nwatch(()=>props.source,()=>{generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined;error.value='';notice.value='';void action(load)})\nwatch(account,()=>{generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined;void action(load)})\nonMounted(()=>action(load))\n</script>\n<template>\n <section class=\"integration-panel mailbox\" :aria-label=\"source==='composio'?'Composio Gmail 邮箱':'本地 Gmail 邮箱'\">\n <header class=\"integration-header\"><div class=\"integration-heading\"><span class=\"integration-mark gmail-mark\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"25\" height=\"25\"></span><div><h3>Gmail</h3><p>{{sourceName}} · {{messages.length}} 封邮件</p></div></div><button class=\"primary\" type=\"button\" :disabled=\"busy||unknown||!account\" @click=\"draft()\"><AppIcon name=\"edit\" :size=\"16\" />写邮件</button></header>\n <label class=\"mail-account\">账号<select v-model=\"account\" :disabled=\"busy\"><option v-for=\"a in accounts\" :value=\"a.id\" :key=\"a.id\">{{a.label}}</option></select></label>\n <form class=\"mail-search\" @submit.prevent=\"action(load)\"><label class=\"integration-search\"><span class=\"sr-only\">搜索邮件</span><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"query\" placeholder=\"in:inbox 或 Gmail 搜索语法\"></label><button :disabled=\"busy\"><AppIcon name=\"refresh\" :size=\"16\" />搜索 / 刷新</button></form>\n <p v-if=\"busy\" class=\"mail-loading\" role=\"status\"><AppIcon name=\"clock\" :size=\"15\" />正在处理邮箱…</p><p v-if=\"error\" class=\"integration-alert\" role=\"alert\">{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\">{{notice}}</p>\n <div v-if=\"unknown\" class=\"mail-unknown\"><div class=\"integration-heading\"><AppIcon name=\"alert\" :size=\"19\" /><strong>写入结果尚未确认</strong></div><p>请先在 Gmail 核对,再继续操作。</p><div class=\"integration-actions\"><button :disabled=\"busy\" @click=\"checkReceipt\">查询操作收据</button><button class=\"quiet\" :disabled=\"busy\" @click=\"unknown=false;compose=false;notice='已按你的确认允许新的操作;原操作不会自动重发。'\">我已在 Gmail 核对,允许新操作</button></div></div>\n <div class=\"mail-layout\">\n <aside class=\"mail-list\"><div class=\"mail-list-heading\"><span>邮件列表</span><span>{{messages.length}}</span></div><ul class=\"messages\"><li v-for=\"m in messages\" :key=\"m.id\"><button :class=\"{active:message?.id===m.id}\" :aria-pressed=\"message?.id===m.id\" :disabled=\"busy\" @click=\"open(m)\"><span class=\"message-from\">{{header(m,'From')}}</span><strong>{{header(m,'Subject')||'(无主题)'}}</strong><small>{{m.snippet}}</small></button></li></ul><div v-if=\"!busy&&!messages.length\" class=\"integration-empty\"><AppIcon name=\"archive\" :size=\"26\" /><p>没有匹配的邮件。</p></div></aside>\n <div class=\"mail-reading\">\n <form v-if=\"compose\" class=\"mail-compose\" @submit.prevent=\"send\"><div class=\"compose-heading\"><h4>{{reply?'回复邮件':'新邮件'}}</h4><span class=\"integration-tag\">{{accounts.find(a=>a.id===account)?.label}}</span></div><label>收件人<input v-model=\"to\" :disabled=\"reply\" required></label><label>主题<input v-model=\"subject\" :disabled=\"reply\" required></label><label>正文<textarea v-model=\"body\" rows=\"7\" placeholder=\"写下你的邮件…\" required /></label><div class=\"integration-actions\"><button class=\"primary\" :disabled=\"busy||unknown\"><AppIcon name=\"arrow-up\" :size=\"16\" />发送邮件</button><button class=\"quiet\" type=\"button\" :disabled=\"busy\" @click=\"compose=false\">取消编辑</button></div></form>\n <article v-if=\"message\" class=\"mail-detail\"><header class=\"mail-detail-heading\"><h4>{{header(message,'Subject')||'(无主题)'}}</h4><p>{{header(message,'From')}}</p><p class=\"message-meta\">发送至 {{header(message,'To')}}<br>{{header(message,'Date')}}</p></header><div class=\"email-body\" v-html=\"mailBody(message.payload)\" />\n <div v-if=\"attachments(message.payload).length\" class=\"mail-attachments\"><button v-for=\"part in attachments(message.payload)\" :key=\"part.partId??part.body.attachmentId??part.filename\" :disabled=\"busy\" @click=\"download(part)\"><AppIcon name=\"download\" :size=\"16\" />下载 {{part.filename}}({{part.body.size}} 字节)</button></div>\n <div class=\"integration-actions mail-actions\"><button :disabled=\"busy||unknown\" @click=\"draft(true)\"><AppIcon name=\"edit\" :size=\"15\" />回复</button><button class=\"quiet\" :disabled=\"busy||unknown\" @click=\"mutate('gmail.modify',{id:message.id,removeLabels:['INBOX']})\"><AppIcon name=\"archive\" :size=\"15\" />归档</button><button class=\"quiet danger\" :disabled=\"busy||unknown\" @click=\"mutate('gmail.trash',{id:message.id})\"><AppIcon name=\"trash\" :size=\"15\" />移入垃圾箱</button></div>\n <div class=\"mail-labels\"><label>标签<select v-model=\"labels\"><option value=\"\">选择标签</option><option v-for=\"label in labelOptions\" :key=\"label.id\" :value=\"label.id\">{{label.name}}</option></select></label><div class=\"integration-actions\"><button :disabled=\"busy||unknown||!labels.trim()\" @click=\"mutate('gmail.modify',{id:message.id,addLabels:[labels]})\">添加标签</button><button class=\"quiet\" :disabled=\"busy||unknown||!labels\" @click=\"mutate('gmail.modify',{id:message.id,removeLabels:[labels]})\">移除标签</button></div></div>\n </article>\n <div v-else-if=\"!compose\" class=\"integration-empty reading-empty\"><span class=\"reading-illustration\"><AppIcon name=\"file\" :size=\"32\" /></span><strong>选择一封邮件开始阅读</strong><p>也可以写封新邮件,继续你的对话。</p></div>\n </div>\n </div>\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.mailbox{border-top:1px solid var(--line);padding-top:24px;gap:16px}.gmail-mark{background:var(--surface)}.mail-account{max-width:340px}.mail-search{display:flex;gap:8px;align-items:center}.mail-search>button{flex-shrink:0}.mail-layout{display:grid;grid-template-columns:minmax(190px,.72fr) minmax(0,1.6fr);border:1px solid var(--line);border-radius:13px;overflow:hidden;min-height:360px}.mail-list{min-width:0;background:var(--surface-soft);border-right:1px solid var(--line)}.mail-list-heading{display:flex;justify-content:space-between;padding:12px 14px;font-size:11px;color:var(--text-muted);border-bottom:1px solid var(--line)}.messages{list-style:none;margin:0;padding:6px;display:grid;gap:3px;max-height:560px;overflow:auto}.messages button{display:grid!important;gap:5px;width:100%!important;min-width:0;text-align:left;justify-content:stretch!important;justify-items:start;border:1px solid transparent!important;border-radius:8px!important;background:transparent!important;padding:12px!important;line-height:1.5}.messages button:hover:not(:disabled){background:var(--surface-hover)!important}.messages button.active{background:var(--surface)!important;border-color:var(--line)!important;box-shadow:0 1px 3px #00000006}.messages :is(strong,span,small){width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.messages strong{font-size:12px;font-weight:600}.message-from{font-size:11px;color:var(--text-secondary)}.messages small{font-size:11px;color:var(--text-muted)}.mail-reading{min-width:0;background:var(--surface)}.mail-detail{padding:22px;min-width:0}.mail-detail-heading{display:grid;gap:8px;padding-bottom:20px;border-bottom:1px solid var(--line);overflow-wrap:anywhere}.mail-detail h4{font-size:18px;line-height:1.5}.mail-detail-heading p{font-size:12px}.message-meta{font-size:11px!important;color:var(--text-muted)!important;line-height:1.8}.email-body{padding:22px 0;overflow-wrap:anywhere;overflow:auto;max-width:100%;line-height:1.8;min-height:100px}.email-body :deep(pre){white-space:pre-wrap;background:none;padding:0;font-family:inherit;font-size:13px}.email-body :deep(p){margin:0 0 12px;color:inherit}.mail-actions{padding:16px 0;border-top:1px solid var(--line)}.mail-labels{display:grid;gap:10px;padding-top:14px;border-top:1px solid var(--line)}.mail-labels label{max-width:240px}.mail-labels button{font-size:11px}.mail-attachments{display:grid;justify-items:start;gap:8px;padding-bottom:16px}.mail-attachments button{text-align:left;overflow-wrap:anywhere;font-size:12px}.mail-compose{display:grid;gap:14px;padding:20px;border-bottom:1px solid var(--line);background:var(--surface)}.compose-heading{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}.mail-compose textarea{line-height:1.7}.mail-unknown{display:grid;gap:10px;padding:16px;border-radius:11px;border:1px solid color-mix(in srgb,var(--warning) 25%,var(--line));background:color-mix(in srgb,var(--warning) 5%,var(--surface));color:var(--warning)}.mail-unknown p{font-size:12px}.mail-unknown button{font-size:12px}.mail-loading{display:flex;align-items:center;gap:7px;color:var(--text-muted)!important;font-size:12px}.reading-empty{min-height:320px;align-content:center}.reading-illustration{display:grid;place-items:center;width:64px;height:64px;margin-bottom:8px;border-radius:18px;background:var(--surface-soft);color:var(--text-muted)}.reading-empty p{font-size:12px}@media(max-width:700px){.mail-layout{grid-template-columns:1fr}.mail-list{border-right:0;border-bottom:1px solid var(--line)}.messages{max-height:220px}.mail-detail,.mail-compose{padding:18px}.reading-empty{min-height:200px}.mail-search{align-items:stretch}.mail-search>button{padding:8px;font-size:12px}.mail-search>button svg{display:none}}@media(max-width:420px){.mail-account{max-width:100%}.mailbox>.integration-header{align-items:center}.mail-detail h4{font-size:16px}.mail-unknown .integration-actions{display:grid;justify-items:start}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed,onMounted,ref,watch } from 'vue'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport DOMPurify from 'dompurify'\nimport { apiRequest } from '@/api/client'\nimport type { GoogleAccount,GoogleOperation } from '@shared/googleWorkspace'\nconst props=withDefaults(defineProps<{accounts:GoogleAccount[];source?:'local'|'composio'}>(),{source:'local'})\nconst sourceName=computed(()=>props.source==='composio'?'Composio 托管':'本地连接')\nconst base=(source=props.source)=>source==='composio'?'/api/app/admin/composio':'/api/app/admin/hermes/local/google'\nconst account=ref(props.accounts[0]?.id??''),query=ref('in:inbox'),messages=ref<any[]>([]),message=ref<any>(),busy=ref(false),error=ref(''),notice=ref(''),compose=ref(false),reply=ref(false),to=ref(''),subject=ref(''),body=ref(''),labels=ref(''),labelOptions=ref<any[]>([]),unknown=ref(false)\nlet generation=0,reloadQueued=false\nlet draftTarget:{accountId:string;messageId?:string;operationId:string;source:'local'|'composio'}|undefined\nconst uncertain=ref<{accountId:string;operationId:string;source:'local'|'composio'}>()\nconst header=(m:any,name:string)=>m?.payload?.headers?.find((h:any)=>h.name.toLowerCase()===name.toLowerCase())?.value??''\nasync function call(operation:GoogleOperation,args:Record<string,unknown>,write=false,accountId=account.value,operationId?:string,source=props.source){\n const id=write?(operationId??crypto.randomUUID()):undefined\n try{\n const result=await apiRequest<any>(base(source)+'/call',{method:'POST',timeoutMs:95_000,body:{accountId,operation,arguments:args as any,...(write?{operationId:id}:{})}})\n if(result?.isError){if(write&&result.outcomeUnknown){unknown.value=true;uncertain.value={accountId,operationId:id!,source}}const e=new Error(result.error?.message??'Google 请求失败');Object.assign(e,{known:!result.outcomeUnknown});throw e}\n return result\n }catch(e){\n if(source==='composio'&&[400,401,403,404,409,413,422].includes((e as {status?:number})?.status??0))Object.assign(e as object,{known:true})\n if(write&&!(e as any)?.known){unknown.value=true;uncertain.value={accountId,operationId:id!,source}}\n throw e\n }\n}\nasync function action(fn:()=>Promise<void>){if(busy.value){if(fn===load)reloadQueued=true;return};busy.value=true;error.value='';notice.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'邮箱操作失败'}finally{busy.value=false;if(reloadQueued){reloadQueued=false;void action(load)}}}\nasync function load(){\n if(!account.value)return\n const version=++generation,selected=account.value,origin=props.source;message.value=undefined\n const result=await call('gmail.search',{query:query.value,max:15},false,selected,undefined,origin),rows=[]\n if(version!==generation||selected!==account.value||origin!==props.source)return\n for(const row of result.messages??[]){const detail=await call('gmail.get',{id:row.id},false,selected,undefined,origin);if(version!==generation||selected!==account.value)return;rows.push(detail)}\n const available=await call('gmail.labels',{},false,selected,undefined,origin)\n if(version===generation&&selected===account.value){messages.value=rows;labelOptions.value=available.labels??[]}\n}\nasync function open(m:any){await action(async()=>{const selected=account.value,version=generation,origin=props.source;const result=await call('gmail.get',{id:m.id},false,selected,undefined,origin);if(selected===account.value&&version===generation)message.value=result})}\nasync function checkReceipt(){await action(async()=>{if(!uncertain.value)return;const r=await apiRequest<any>(base(uncertain.value.source)+'/receipts/'+uncertain.value.accountId+'/'+encodeURIComponent(uncertain.value.operationId));notice.value=r.status==='complete'?'邮箱操作已完成,已读取保存的结果。':r.status==='rejected'?'邮箱服务已拒绝此操作。':'仍无确认结果,请在 Gmail 核对。';if(['complete','rejected'].includes(r.status)){unknown.value=false;compose.value=false;draftTarget=undefined;uncertain.value=undefined;await load()}})}\nfunction decode(data:string){const bytes=Uint8Array.from(atob(data.replace(/-/g,'+').replace(/_/g,'/')),c=>c.charCodeAt(0));return new TextDecoder().decode(bytes)}\nfunction mailBody(payload:any):string{if(!payload)return '';if(payload.mimeType==='text/plain'&&payload.body?.data)return '<pre>'+decode(payload.body.data).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\n/g,'<br>')+'</pre>';const parts=payload.parts??[];const plain=parts.find((p:any)=>p.mimeType==='text/plain');if(plain)return mailBody(plain);if(payload.mimeType==='text/html'&&payload.body?.data)return DOMPurify.sanitize(decode(payload.body.data),{FORBID_TAGS:['img','style','script','iframe','form','svg','math','video','audio','link'],FORBID_ATTR:['style','src','srcset','background']});return parts.map(mailBody).join('')}\nfunction attachments(payload:any):any[]{return [...(payload?.filename&&(payload?.body?.attachmentId||payload?.body?.data)?[payload]:[]),...(payload?.parts??[]).flatMap(attachments)]}\nasync function download(part:any){await action(async()=>{const result=part.body.data?part.body:await call('gmail.attachment',{id:message.value.id,attachmentId:part.body.attachmentId});if(result.size>25*1024*1024||String(result.data??'').length>25*1024*1024*4/3+4)throw new Error('附件超过25MB限制');const bytes=Uint8Array.from(atob(result.data.replace(/-/g,'+').replace(/_/g,'/')),c=>c.charCodeAt(0));const url=URL.createObjectURL(new Blob([bytes],{type:'application/octet-stream'}));const link=document.createElement('a');link.href=url;link.download=String(part.filename).replace(/[\\\\/\\x00-\\x1f]/g,'_').slice(0,180)||'attachment';document.body.append(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(url),30000)})}\nfunction draft(isReply=false){if(unknown.value)return;draftTarget={accountId:account.value,...(isReply?{messageId:message.value.id}:{}),operationId:crypto.randomUUID(),source:props.source};reply.value=isReply;compose.value=true;to.value=isReply?(header(message.value,'Reply-To')||header(message.value,'From')):'';subject.value=isReply?'Re: '+header(message.value,'Subject'):'';body.value=''}\nasync function send(){await action(async()=>{\n const target=draftTarget;if(!target||target.accountId!==account.value||target.source!==props.source||unknown.value)throw new Error('草稿账号已改变,请重新编辑。')\n try{const result=await call(target.messageId?'gmail.reply':'gmail.send',target.messageId?{id:target.messageId,body:body.value}:{to:to.value,subject:subject.value,body:body.value},true,target.accountId,target.operationId,target.source);if(target.accountId!==account.value||target.source!==props.source)return;compose.value=false;draftTarget=undefined;notice.value='邮件已发送,消息编号:'+(result.id??'已确认');if(result.id)message.value=await call('gmail.get',{id:result.id},false,target.accountId,undefined,target.source)}catch(e){if(!unknown.value&&draftTarget)draftTarget.operationId=crypto.randomUUID();throw e}\n})}\nasync function mutate(operation:GoogleOperation,args:Record<string,unknown>){if(unknown.value)return;await action(async()=>{const selected=account.value;await call(operation,args,true,selected);await load();notice.value='邮箱操作已完成。'})}\nwatch(()=>props.accounts.map(a=>a.id).join(','),()=>{if(!props.accounts.some(a=>a.id===account.value)){account.value=props.accounts[0]?.id??'';generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined}})\nwatch(()=>props.source,()=>{generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined;error.value='';notice.value='';void action(load)})\nwatch(account,()=>{generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined;void action(load)})\nonMounted(()=>action(load))\n</script>\n<template>\n <section class=\"integration-panel mailbox\" :aria-label=\"source==='composio'?'Composio Gmail 邮箱':'本地 Gmail 邮箱'\">\n <header class=\"integration-header\"><div class=\"integration-heading\"><span class=\"integration-mark gmail-mark\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"25\" height=\"25\"></span><div><h3>Gmail</h3><p>{{sourceName}} · {{messages.length}} 封邮件</p></div></div><button class=\"primary\" type=\"button\" :disabled=\"busy||unknown||!account\" @click=\"draft()\"><AppIcon name=\"edit\" :size=\"16\" />写邮件</button></header>\n <label class=\"mail-account\">账号<select v-model=\"account\" :disabled=\"busy\"><option v-for=\"a in accounts\" :value=\"a.id\" :key=\"a.id\">{{a.label}}</option></select></label>\n <form class=\"mail-search\" @submit.prevent=\"action(load)\"><label class=\"integration-search\"><span class=\"sr-only\">搜索邮件</span><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"query\" placeholder=\"in:inbox 或 Gmail 搜索语法\"></label><button :disabled=\"busy\"><AppIcon name=\"refresh\" :size=\"16\" />搜索 / 刷新</button></form>\n <p v-if=\"busy\" class=\"mail-loading\" role=\"status\"><AppIcon name=\"clock\" :size=\"15\" />正在处理邮箱…</p><p v-if=\"error\" class=\"integration-alert\" role=\"alert\">{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\">{{notice}}</p>\n <div v-if=\"unknown\" class=\"mail-unknown\"><div class=\"integration-heading\"><AppIcon name=\"alert\" :size=\"19\" /><strong>写入结果尚未确认</strong></div><p>请先在 Gmail 核对,再继续操作。</p><div class=\"integration-actions\"><button :disabled=\"busy\" @click=\"checkReceipt\">查询操作收据</button><button class=\"quiet\" :disabled=\"busy\" @click=\"unknown=false;compose=false;notice='已按你的确认允许新的操作;原操作不会自动重发。'\">我已在 Gmail 核对,允许新操作</button></div></div>\n <div class=\"mail-layout\">\n <aside class=\"mail-list\"><div class=\"mail-list-heading\"><span>邮件列表</span><span>{{messages.length}}</span></div><ul class=\"messages\"><li v-for=\"m in messages\" :key=\"m.id\"><button :class=\"{active:message?.id===m.id}\" :aria-pressed=\"message?.id===m.id\" :disabled=\"busy\" @click=\"open(m)\"><span class=\"message-from\">{{header(m,'From')}}</span><strong>{{header(m,'Subject')||'(无主题)'}}</strong><small>{{m.snippet}}</small></button></li></ul><div v-if=\"!busy&&!messages.length\" class=\"integration-empty\"><AppIcon name=\"archive\" :size=\"26\" /><p>没有匹配的邮件。</p></div></aside>\n <div class=\"mail-reading\">\n <form v-if=\"compose\" class=\"mail-compose\" @submit.prevent=\"send\"><div class=\"compose-heading\"><h4>{{reply?'回复邮件':'新邮件'}}</h4><span class=\"integration-tag\">{{accounts.find(a=>a.id===account)?.label}}</span></div><label>收件人<input v-model=\"to\" :disabled=\"reply\" required></label><label>主题<input v-model=\"subject\" :disabled=\"reply\" required></label><label>正文<textarea v-model=\"body\" rows=\"7\" placeholder=\"写下你的邮件…\" required /></label><div class=\"integration-actions\"><button class=\"primary\" :disabled=\"busy||unknown\"><AppIcon name=\"arrow-up\" :size=\"16\" />发送邮件</button><button class=\"quiet\" type=\"button\" :disabled=\"busy\" @click=\"compose=false\">取消编辑</button></div></form>\n <article v-if=\"message\" class=\"mail-detail\"><header class=\"mail-detail-heading\"><h4>{{header(message,'Subject')||'(无主题)'}}</h4><p>{{header(message,'From')}}</p><p class=\"message-meta\">发送至 {{header(message,'To')}}<br>{{header(message,'Date')}}</p></header><div class=\"email-body\" v-html=\"mailBody(message.payload)\" />\n <div v-if=\"attachments(message.payload).length\" class=\"mail-attachments\"><button v-for=\"part in attachments(message.payload)\" :key=\"part.partId??part.body.attachmentId??part.filename\" :disabled=\"busy\" @click=\"download(part)\"><AppIcon name=\"download\" :size=\"16\" />下载 {{part.filename}}({{part.body.size}} 字节)</button></div>\n <div class=\"integration-actions mail-actions\"><button :disabled=\"busy||unknown\" @click=\"draft(true)\"><AppIcon name=\"edit\" :size=\"15\" />回复</button><button class=\"quiet\" :disabled=\"busy||unknown\" @click=\"mutate('gmail.modify',{id:message.id,removeLabels:['INBOX']})\"><AppIcon name=\"archive\" :size=\"15\" />归档</button><button class=\"quiet danger\" :disabled=\"busy||unknown\" @click=\"mutate('gmail.trash',{id:message.id})\"><AppIcon name=\"trash\" :size=\"15\" />移入垃圾箱</button></div>\n <div class=\"mail-labels\"><label>标签<select v-model=\"labels\"><option value=\"\">选择标签</option><option v-for=\"label in labelOptions\" :key=\"label.id\" :value=\"label.id\">{{label.name}}</option></select></label><div class=\"integration-actions\"><button :disabled=\"busy||unknown||!labels.trim()\" @click=\"mutate('gmail.modify',{id:message.id,addLabels:[labels]})\">添加标签</button><button class=\"quiet\" :disabled=\"busy||unknown||!labels\" @click=\"mutate('gmail.modify',{id:message.id,removeLabels:[labels]})\">移除标签</button></div></div>\n </article>\n <div v-else-if=\"!compose\" class=\"integration-empty reading-empty\"><span class=\"reading-illustration\"><AppIcon name=\"file\" :size=\"32\" /></span><strong>选择一封邮件开始阅读</strong><p>也可以写封新邮件,继续你的对话。</p></div>\n </div>\n </div>\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.mailbox{border-top:1px solid var(--line);padding-top:24px;gap:16px}.gmail-mark{background:var(--surface)}.mail-account{max-width:340px}.mail-search{display:flex;gap:8px;align-items:center}.mail-search>button{flex-shrink:0}.mail-layout{display:grid;grid-template-columns:minmax(190px,.72fr) minmax(0,1.6fr);border:1px solid var(--line);border-radius:13px;overflow:hidden;min-height:360px}.mail-list{min-width:0;background:var(--surface-soft);border-right:1px solid var(--line)}.mail-list-heading{display:flex;justify-content:space-between;padding:12px 14px;font-size:11px;color:var(--text-muted);border-bottom:1px solid var(--line)}.messages{list-style:none;margin:0;padding:6px;display:grid;gap:3px;max-height:560px;overflow:auto}.messages button{display:grid!important;gap:5px;width:100%!important;min-width:0;text-align:left;justify-content:stretch!important;justify-items:start;border:1px solid transparent!important;border-radius:8px!important;background:transparent!important;padding:12px!important;line-height:1.5}.messages button:hover:not(:disabled){background:var(--surface-hover)!important}.messages button.active{background:var(--surface)!important;border-color:var(--line)!important;box-shadow:0 1px 3px #00000006}.messages :is(strong,span,small){width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.messages strong{font-size:12px;font-weight:600}.message-from{font-size:11px;color:var(--text-secondary)}.messages small{font-size:11px;color:var(--text-muted)}.mail-reading{min-width:0;background:var(--surface)}.mail-detail{padding:22px;min-width:0}.mail-detail-heading{display:grid;gap:8px;padding-bottom:20px;border-bottom:1px solid var(--line);overflow-wrap:anywhere}.mail-detail h4{font-size:18px;line-height:1.5}.mail-detail-heading p{font-size:12px}.message-meta{font-size:11px!important;color:var(--text-muted)!important;line-height:1.8}.email-body{padding:22px 0;overflow-wrap:anywhere;overflow:auto;max-width:100%;line-height:1.8;min-height:100px}.email-body :deep(pre){white-space:pre-wrap;background:none;padding:0;font-family:inherit;font-size:13px}.email-body :deep(p){margin:0 0 12px;color:inherit}.mail-actions{padding:16px 0;border-top:1px solid var(--line)}.mail-labels{display:grid;gap:10px;padding-top:14px;border-top:1px solid var(--line)}.mail-labels label{max-width:240px}.mail-labels button{font-size:11px}.mail-attachments{display:grid;justify-items:start;gap:8px;padding-bottom:16px}.mail-attachments button{text-align:left;overflow-wrap:anywhere;font-size:12px}.mail-compose{display:grid;gap:14px;padding:20px;border-bottom:1px solid var(--line);background:var(--surface)}.compose-heading{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}.mail-compose textarea{line-height:1.7}.mail-unknown{display:grid;gap:10px;padding:16px;border-radius:11px;border:1px solid color-mix(in srgb,var(--warning) 25%,var(--line));background:color-mix(in srgb,var(--warning) 5%,var(--surface));color:var(--warning)}.mail-unknown p{font-size:12px}.mail-unknown button{font-size:12px}.mail-loading{display:flex;align-items:center;gap:7px;color:var(--text-muted)!important;font-size:12px}.reading-empty{min-height:320px;align-content:center}.reading-illustration{display:grid;place-items:center;width:64px;height:64px;margin-bottom:8px;border-radius:18px;background:var(--surface-soft);color:var(--text-muted)}.reading-empty p{font-size:12px}@media(max-width:700px){.mail-layout{grid-template-columns:1fr}.mail-list{border-right:0;border-bottom:1px solid var(--line)}.messages{max-height:220px}.mail-detail,.mail-compose{padding:18px}.reading-empty{min-height:200px}.mail-search{align-items:stretch}.mail-search>button{padding:8px;font-size:12px}.mail-search>button svg{display:none}}@media(max-width:420px){.mail-account{max-width:100%}.mailbox>.integration-header{align-items:center}.mail-detail h4{font-size:16px}.mail-unknown .integration-actions{display:grid;justify-items:start}}\n</style>\n","<script setup lang=\"ts\">\nimport { onBeforeUnmount,onMounted,ref } from 'vue'\nimport { apiRequest } from '@/api/client'\nimport type { GoogleAuth,GoogleStatus } from '@shared/googleWorkspace'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport GmailPanel from './GmailPanel.vue'\nconst base='/api/app/admin/hermes/local/google'\nconst value=ref<GoogleStatus>(),client=ref(''),selected=ref(['gmail']),flow=ref<GoogleAuth>(),busy=ref(false),error=ref(''),notice=ref(''),mail=ref(false),reconnectAccount=ref<string>()\nconst serviceLogos:Record<string,string>={gmail:'gmail',calendar:'googlecalendar',drive:'googledrive'}\nconst labels:Record<string,string>={gmail:'Gmail',calendar:'日历',drive:'云盘',contacts:'联系人',sheets:'表格',docs:'文档'}\nlet timer:ReturnType<typeof setTimeout>|undefined,closed=false\nasync function load(){value.value=await apiRequest<GoogleStatus>(base+'/status')}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'操作失败'}finally{busy.value=false}}\nasync function save(){await action(async()=>{await apiRequest(base+'/client',{method:'POST',body:{client:JSON.parse(client.value)}});client.value='';notice.value='客户端已保存在主机。请选择服务并连接账号。';await load()})}\nasync function connect(accountId?:string){if(busy.value)return;const popup=window.open('about:blank','_blank');if(popup)popup.opener=null;await action(async()=>{try{flow.value=await apiRequest<GoogleAuth>(base+'/auth',{method:'POST',body:{services:selected.value,...(accountId?{accountId}:{})}});if(popup&&flow.value.authUrl)popup.location.href=flow.value.authUrl;else popup?.close();poll()}catch(e){popup?.close();throw e}})}\nfunction prepareReconnect(id:string){const a=value.value?.accounts.find(v=>v.id===id);if(!a)return;selected.value=[...a.services];reconnectAccount.value=id;notice.value='已选择 '+a.label+'。请在上方勾选本次服务,再点击重新授权。'}\nfunction poll(){clearTimeout(timer);if(closed||!flow.value||!['pending','received','exchanging'].includes(flow.value.status))return;timer=setTimeout(async()=>{try{const r=await apiRequest<GoogleAuth>(base+'/auth/'+flow.value!.id);if(closed)return;flow.value={...flow.value,...r};if(r.status==='approved'){notice.value='Google 已连接。';await load()}}catch(e){if(!closed)error.value=e instanceof Error?e.message:'授权状态读取失败'}poll()},2000)}\nasync function cancel(){await action(async()=>{if(flow.value)flow.value=await apiRequest<GoogleAuth>(base+'/auth/'+flow.value.id,{method:'DELETE'});clearTimeout(timer)})}\nasync function disconnect(id:string,revision:string,revoke=false){if(!window.confirm(revoke?'撤销Google授权并断开?同一Google账号和客户端的其他本地连接也可能需要重新授权。':'断开这个本地 Google 账号?使用此连接的 Bot 将无法继续访问。'))return;await action(async()=>{const result=await apiRequest<{note:string}>(base+'/accounts/'+id,{method:'DELETE',body:{revision,revoke}});notice.value=result.note;await load()})}\nconst stateLabel=(s:string)=>({pending:'等待授权',received:'授权已收到',exchanging:'确认授权中',approved:'已连接',failed:'授权未完成',expired:'授权已过期',cancelled:'已取消',connected:'已连接',partial:'部分服务未获授权',reauth_required:'需要重新授权'}[s]??s)\nonMounted(()=>action(load));onBeforeUnmount(()=>{closed=true;clearTimeout(timer);if(flow.value&&['pending','received','exchanging'].includes(flow.value.status))void apiRequest(base+'/auth/'+flow.value.id,{method:'DELETE'}).catch(()=>{})})\n</script>\n<template>\n <section class=\"integration-panel local-apps\" aria-label=\"本地应用\">\n <header class=\"integration-header\">\n <div><h3>连接你的日常工具</h3><p class=\"local-intro\">由当前主机直接连接,账号与 Nous 应用独立管理。</p></div>\n <button class=\"quiet refresh-local\" type=\"button\" :disabled=\"busy\" aria-label=\"刷新本地连接\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button>\n </header>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\"><AppIcon name=\"alert\" :size=\"17\" />{{error}}</p>\n <p v-if=\"notice\" class=\"integration-alert\" role=\"status\"><AppIcon name=\"check\" :size=\"17\" />{{notice}}</p>\n <article class=\"integration-card google-card\">\n <header class=\"integration-header\">\n <div class=\"integration-heading\"><span class=\"google-mark\"><span>G</span></span><div><h3>Google Workspace</h3><p>邮件、日程和文件,一处连接。</p></div></div>\n <span class=\"integration-tag\" :class=\"{success:value?.configured}\">{{value?.configured?'客户端已配置':'待配置'}}</span>\n </header>\n <details class=\"google-setup\">\n <summary><AppIcon name=\"settings\" :size=\"16\" />{{value?.configured?'更新 Google 客户端':'配置 Google 客户端'}}</summary>\n <div class=\"setup-content\"><p>在 Google Cloud 创建“桌面应用”客户端,启用所需 API,然后粘贴下载的 JSON。</p><a href=\"https://console.cloud.google.com/auth/clients\" target=\"_blank\" rel=\"noopener noreferrer\">打开 Google Cloud ↗</a><form @submit.prevent=\"save\"><label>桌面客户端 JSON<textarea v-model=\"client\" rows=\"5\" autocomplete=\"off\" spellcheck=\"false\" placeholder='{ \"installed\": { … } }' required /></label><button class=\"primary\" :disabled=\"busy||!client.trim()\">保存到主机</button></form></div>\n </details>\n <fieldset class=\"google-services\"><legend>选择本次授权的服务</legend><div class=\"service-grid\"><label v-for=\"(label,id) in labels\" :key=\"id\" class=\"service-option\" :class=\"{selected:selected.includes(id)}\"><input v-model=\"selected\" type=\"checkbox\" :value=\"id\"><img v-if=\"['gmail','calendar','drive'].includes(id)\" :src=\"'/provider-logos/'+serviceLogos[id]+'.svg'\" alt=\"\" width=\"19\" height=\"19\"><AppIcon v-else :name=\"id==='contacts'?'users':id==='sheets'?'board':'file'\" :size=\"19\" /><span>{{label}}</span></label></div></fieldset>\n <div class=\"connect-row\"><p><AppIcon name=\"monitor\" :size=\"15\" />首次授权需在主机电脑的浏览器完成。</p><div class=\"integration-actions\"><button class=\"primary\" type=\"button\" :disabled=\"busy||!value?.configured||!selected.length||!!flow&&['pending','received','exchanging'].includes(flow.status)\" @click=\"connect(reconnectAccount)\"><AppIcon name=\"plus\" :size=\"16\" />{{reconnectAccount?'重新授权选中的账号':'连接 Google 账号'}}</button><button v-if=\"reconnectAccount\" class=\"quiet\" :disabled=\"busy\" @click=\"reconnectAccount=undefined;selected=['gmail']\">改为添加账号</button></div></div>\n <div v-if=\"flow\" class=\"auth-progress\" role=\"status\"><span class=\"integration-tag\" :class=\"{success:flow.status==='approved'}\">{{stateLabel(flow.status)}}</span><p v-if=\"flow.error\">{{flow.error}}</p><div class=\"integration-actions\"><a v-if=\"flow.authUrl&&flow.status==='pending'\" :href=\"flow.authUrl\" target=\"_blank\" rel=\"noopener noreferrer\">在 Host 电脑浏览器继续授权 ↗</a><button v-if=\"['pending','received','exchanging'].includes(flow.status)\" class=\"quiet\" :disabled=\"busy\" @click=\"cancel\">取消授权</button></div></div>\n <div class=\"google-accounts\"><div class=\"accounts-heading\"><h4>已连接账号</h4><span class=\"integration-count\">{{value?.accounts.length??0}} 个账号</span></div><ul v-if=\"value?.accounts.length\"><li v-for=\"a in value.accounts\" :key=\"a.id\"><div class=\"account-row\"><span class=\"account-avatar\"><AppIcon name=\"users\" :size=\"18\" /></span><div class=\"account-copy\"><strong>{{a.label}}</strong><p>{{a.services.map(s=>labels[s]??s).join(' · ')}}</p></div><span class=\"integration-tag\" :class=\"{success:a.status==='connected',warning:a.status!=='connected'}\">{{stateLabel(a.status)}}</span></div><details class=\"account-manage\"><summary>管理账号</summary><div class=\"integration-actions\"><button :disabled=\"busy\" @click=\"prepareReconnect(a.id)\">重新授权</button><button class=\"quiet\" :disabled=\"busy\" @click=\"disconnect(a.id,a.revision)\">断开</button><button class=\"quiet danger\" :disabled=\"busy\" @click=\"disconnect(a.id,a.revision,true)\">撤销 Google 授权</button></div></details></li></ul><p v-else class=\"accounts-empty\">连接账号后,所有设备都可以使用已授权的服务。</p></div>\n <button v-if=\"value?.accounts?.some(a=>a.services.includes('gmail'))\" class=\"mail-entry\" @click=\"mail=!mail\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"20\" height=\"20\">{{mail?'收起邮箱':'打开本地 Gmail 邮箱'}}<AppIcon :name=\"mail?'chevron-down':'chevron-right'\" :size=\"17\" /></button>\n </article>\n <GmailPanel v-if=\"mail&&value\" :accounts=\"value.accounts.filter(a=>a.services.includes('gmail'))\" />\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.local-intro{margin-top:5px!important;font-size:12px}.refresh-local{flex:0 0 auto}.google-card{display:grid;gap:22px}.google-mark{display:grid;place-items:center;width:48px;height:48px;border:1px solid var(--line);border-radius:14px;background:var(--surface);flex-shrink:0}.google-mark span{font-size:30px;font-weight:650;font-family:Arial,sans-serif;background:conic-gradient(#4285f4 0deg 90deg,#34a853 90deg 175deg,#fbbc05 175deg 225deg,#ea4335 225deg 280deg,#4285f4 280deg);background-clip:text;color:transparent}.google-card>.integration-header{align-items:center}.google-setup{padding:0 13px;border:1px solid var(--line);border-radius:10px;background:var(--surface-soft)}.setup-content{display:grid;gap:12px;padding:5px 0 16px}.setup-content form{display:grid;gap:12px}.setup-content textarea{font:12px/1.6 var(--font-code)}.google-services{margin:0;padding:0;border:0;min-width:0}.google-services legend{padding:0;margin-bottom:10px;font-size:12px;font-weight:550;color:var(--text-secondary)}.service-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.service-option{display:flex!important;align-items:center;gap:9px!important;min-height:48px;padding:10px 12px;border:1px solid var(--line);border-radius:9px;background:var(--surface);cursor:pointer;color:var(--text-primary)!important}.service-option.selected{border-color:var(--line-strong);background:var(--surface-soft)}.service-option input{margin-right:1px}.service-option svg{color:var(--text-secondary)}.connect-row{display:grid;gap:14px}.connect-row>p{display:flex;align-items:center;gap:7px;font-size:12px}.auth-progress{display:grid;gap:10px;padding:12px;border-radius:10px;background:var(--surface-soft)}.google-accounts{border-top:1px solid var(--line);padding-top:18px}.accounts-heading{display:flex;align-items:center;justify-content:space-between;gap:10px}.google-accounts ul{list-style:none;padding:0;margin:12px 0 0;display:grid;gap:12px}.google-accounts li{min-width:0;padding:12px;border:1px solid var(--line);border-radius:10px}.account-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.account-avatar{display:grid;place-items:center;width:34px;height:34px;border-radius:50%;background:var(--surface-soft);color:var(--text-secondary)}.account-copy{flex:1;min-width:100px;overflow-wrap:anywhere}.account-copy strong{font-weight:580}.account-copy p{font-size:11px;margin-top:3px}.account-manage{margin-top:6px}.account-manage summary{min-height:30px;font-size:11px}.accounts-empty{padding-top:12px;font-size:12px}.mail-entry{width:100%!important;justify-content:flex-start!important;padding:12px 14px!important;background:var(--surface-soft)!important}.mail-entry>svg{margin-left:auto}@media(max-width:420px){.service-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.google-card>.integration-header{align-items:flex-start;flex-wrap:wrap}.google-mark{width:40px;height:40px}.google-card{gap:18px}}\n</style>\n","<script setup lang=\"ts\">\nimport { onBeforeUnmount,onMounted,ref } from 'vue'\nimport { apiRequest } from '@/api/client'\nimport type { GoogleAuth,GoogleStatus } from '@shared/googleWorkspace'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport GmailPanel from './GmailPanel.vue'\nconst base='/api/app/admin/hermes/local/google'\nconst value=ref<GoogleStatus>(),client=ref(''),selected=ref(['gmail']),flow=ref<GoogleAuth>(),busy=ref(false),error=ref(''),notice=ref(''),mail=ref(false),reconnectAccount=ref<string>()\nconst serviceLogos:Record<string,string>={gmail:'gmail',calendar:'googlecalendar',drive:'googledrive'}\nconst labels:Record<string,string>={gmail:'Gmail',calendar:'日历',drive:'云盘',contacts:'联系人',sheets:'表格',docs:'文档'}\nlet timer:ReturnType<typeof setTimeout>|undefined,closed=false\nasync function load(){value.value=await apiRequest<GoogleStatus>(base+'/status')}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'操作失败'}finally{busy.value=false}}\nasync function save(){await action(async()=>{await apiRequest(base+'/client',{method:'POST',body:{client:JSON.parse(client.value)}});client.value='';notice.value='客户端已保存在主机。请选择服务并连接账号。';await load()})}\nasync function connect(accountId?:string){if(busy.value)return;const popup=window.open('about:blank','_blank');if(popup)popup.opener=null;await action(async()=>{try{flow.value=await apiRequest<GoogleAuth>(base+'/auth',{method:'POST',body:{services:selected.value,...(accountId?{accountId}:{})}});if(popup&&flow.value.authUrl)popup.location.href=flow.value.authUrl;else popup?.close();poll()}catch(e){popup?.close();throw e}})}\nfunction prepareReconnect(id:string){const a=value.value?.accounts.find(v=>v.id===id);if(!a)return;selected.value=[...a.services];reconnectAccount.value=id;notice.value='已选择 '+a.label+'。请在上方勾选本次服务,再点击重新授权。'}\nfunction poll(){clearTimeout(timer);if(closed||!flow.value||!['pending','received','exchanging'].includes(flow.value.status))return;timer=setTimeout(async()=>{try{const r=await apiRequest<GoogleAuth>(base+'/auth/'+flow.value!.id);if(closed)return;flow.value={...flow.value,...r};if(r.status==='approved'){notice.value='Google 已连接。';await load()}}catch(e){if(!closed)error.value=e instanceof Error?e.message:'授权状态读取失败'}poll()},2000)}\nasync function cancel(){await action(async()=>{if(flow.value)flow.value=await apiRequest<GoogleAuth>(base+'/auth/'+flow.value.id,{method:'DELETE'});clearTimeout(timer)})}\nasync function disconnect(id:string,revision:string,revoke=false){if(!window.confirm(revoke?'撤销Google授权并断开?同一Google账号和客户端的其他本地连接也可能需要重新授权。':'断开这个本地 Google 账号?使用此连接的 Bot 将无法继续访问。'))return;await action(async()=>{const result=await apiRequest<{note:string}>(base+'/accounts/'+id,{method:'DELETE',body:{revision,revoke}});notice.value=result.note;await load()})}\nconst stateLabel=(s:string)=>({pending:'等待授权',received:'授权已收到',exchanging:'确认授权中',approved:'已连接',failed:'授权未完成',expired:'授权已过期',cancelled:'已取消',connected:'已连接',partial:'部分服务未获授权',reauth_required:'需要重新授权'}[s]??s)\nonMounted(()=>action(load));onBeforeUnmount(()=>{closed=true;clearTimeout(timer);if(flow.value&&['pending','received','exchanging'].includes(flow.value.status))void apiRequest(base+'/auth/'+flow.value.id,{method:'DELETE'}).catch(()=>{})})\n</script>\n<template>\n <section class=\"integration-panel local-apps\" aria-label=\"本地应用\">\n <header class=\"integration-header\">\n <div><h3>连接你的日常工具</h3><p class=\"local-intro\">由当前主机直接连接,账号与 Nous 应用独立管理。</p></div>\n <button class=\"quiet refresh-local\" type=\"button\" :disabled=\"busy\" aria-label=\"刷新本地连接\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button>\n </header>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\"><AppIcon name=\"alert\" :size=\"17\" />{{error}}</p>\n <p v-if=\"notice\" class=\"integration-alert\" role=\"status\"><AppIcon name=\"check\" :size=\"17\" />{{notice}}</p>\n <article class=\"integration-card google-card\">\n <header class=\"integration-header\">\n <div class=\"integration-heading\"><span class=\"google-mark\"><span>G</span></span><div><h3>Google Workspace</h3><p>邮件、日程和文件,一处连接。</p></div></div>\n <span class=\"integration-tag\" :class=\"{success:value?.configured}\">{{value?.configured?'客户端已配置':'待配置'}}</span>\n </header>\n <details class=\"google-setup\">\n <summary><AppIcon name=\"settings\" :size=\"16\" />{{value?.configured?'更新 Google 客户端':'配置 Google 客户端'}}</summary>\n <div class=\"setup-content\"><p>在 Google Cloud 创建“桌面应用”客户端,启用所需 API,然后粘贴下载的 JSON。</p><a href=\"https://console.cloud.google.com/auth/clients\" target=\"_blank\" rel=\"noopener noreferrer\">打开 Google Cloud ↗</a><form @submit.prevent=\"save\"><label>桌面客户端 JSON<textarea v-model=\"client\" rows=\"5\" autocomplete=\"off\" spellcheck=\"false\" placeholder='{ \"installed\": { … } }' required /></label><button class=\"primary\" :disabled=\"busy||!client.trim()\">保存到主机</button></form></div>\n </details>\n <fieldset class=\"google-services\"><legend>选择本次授权的服务</legend><div class=\"service-grid\"><label v-for=\"(label,id) in labels\" :key=\"id\" class=\"service-option\" :class=\"{selected:selected.includes(id)}\"><input v-model=\"selected\" type=\"checkbox\" :value=\"id\"><img v-if=\"['gmail','calendar','drive'].includes(id)\" :src=\"'/provider-logos/'+serviceLogos[id]+'.svg'\" alt=\"\" width=\"19\" height=\"19\"><AppIcon v-else :name=\"id==='contacts'?'users':id==='sheets'?'board':'file'\" :size=\"19\" /><span>{{label}}</span></label></div></fieldset>\n <div class=\"connect-row\"><p><AppIcon name=\"monitor\" :size=\"15\" />首次授权需在主机电脑的浏览器完成。</p><div class=\"integration-actions\"><button class=\"primary\" type=\"button\" :disabled=\"busy||!value?.configured||!selected.length||!!flow&&['pending','received','exchanging'].includes(flow.status)\" @click=\"connect(reconnectAccount)\"><AppIcon name=\"plus\" :size=\"16\" />{{reconnectAccount?'重新授权选中的账号':'连接 Google 账号'}}</button><button v-if=\"reconnectAccount\" class=\"quiet\" :disabled=\"busy\" @click=\"reconnectAccount=undefined;selected=['gmail']\">改为添加账号</button></div></div>\n <div v-if=\"flow\" class=\"auth-progress\" role=\"status\"><span class=\"integration-tag\" :class=\"{success:flow.status==='approved'}\">{{stateLabel(flow.status)}}</span><p v-if=\"flow.error\">{{flow.error}}</p><div class=\"integration-actions\"><a v-if=\"flow.authUrl&&flow.status==='pending'\" :href=\"flow.authUrl\" target=\"_blank\" rel=\"noopener noreferrer\">在 Host 电脑浏览器继续授权 ↗</a><button v-if=\"['pending','received','exchanging'].includes(flow.status)\" class=\"quiet\" :disabled=\"busy\" @click=\"cancel\">取消授权</button></div></div>\n <div class=\"google-accounts\"><div class=\"accounts-heading\"><h4>已连接账号</h4><span class=\"integration-count\">{{value?.accounts.length??0}} 个账号</span></div><ul v-if=\"value?.accounts.length\"><li v-for=\"a in value.accounts\" :key=\"a.id\"><div class=\"account-row\"><span class=\"account-avatar\"><AppIcon name=\"users\" :size=\"18\" /></span><div class=\"account-copy\"><strong>{{a.label}}</strong><p>{{a.services.map(s=>labels[s]??s).join(' · ')}}</p></div><span class=\"integration-tag\" :class=\"{success:a.status==='connected',warning:a.status!=='connected'}\">{{stateLabel(a.status)}}</span></div><details class=\"account-manage\"><summary>管理账号</summary><div class=\"integration-actions\"><button :disabled=\"busy\" @click=\"prepareReconnect(a.id)\">重新授权</button><button class=\"quiet\" :disabled=\"busy\" @click=\"disconnect(a.id,a.revision)\">断开</button><button class=\"quiet danger\" :disabled=\"busy\" @click=\"disconnect(a.id,a.revision,true)\">撤销 Google 授权</button></div></details></li></ul><p v-else class=\"accounts-empty\">连接账号后,所有设备都可以使用已授权的服务。</p></div>\n <button v-if=\"value?.accounts?.some(a=>a.services.includes('gmail'))\" class=\"mail-entry\" @click=\"mail=!mail\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"20\" height=\"20\">{{mail?'收起邮箱':'打开本地 Gmail 邮箱'}}<AppIcon :name=\"mail?'chevron-down':'chevron-right'\" :size=\"17\" /></button>\n </article>\n <GmailPanel v-if=\"mail&&value\" :accounts=\"value.accounts.filter(a=>a.services.includes('gmail'))\" />\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.local-intro{margin-top:5px!important;font-size:12px}.refresh-local{flex:0 0 auto}.google-card{display:grid;gap:22px}.google-mark{display:grid;place-items:center;width:48px;height:48px;border:1px solid var(--line);border-radius:14px;background:var(--surface);flex-shrink:0}.google-mark span{font-size:30px;font-weight:650;font-family:Arial,sans-serif;background:conic-gradient(#4285f4 0deg 90deg,#34a853 90deg 175deg,#fbbc05 175deg 225deg,#ea4335 225deg 280deg,#4285f4 280deg);background-clip:text;color:transparent}.google-card>.integration-header{align-items:center}.google-setup{padding:0 13px;border:1px solid var(--line);border-radius:10px;background:var(--surface-soft)}.setup-content{display:grid;gap:12px;padding:5px 0 16px}.setup-content form{display:grid;gap:12px}.setup-content textarea{font:12px/1.6 var(--font-code)}.google-services{margin:0;padding:0;border:0;min-width:0}.google-services legend{padding:0;margin-bottom:10px;font-size:12px;font-weight:550;color:var(--text-secondary)}.service-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.service-option{display:flex!important;align-items:center;gap:9px!important;min-height:48px;padding:10px 12px;border:1px solid var(--line);border-radius:9px;background:var(--surface);cursor:pointer;color:var(--text-primary)!important}.service-option.selected{border-color:var(--line-strong);background:var(--surface-soft)}.service-option input{margin-right:1px}.service-option svg{color:var(--text-secondary)}.connect-row{display:grid;gap:14px}.connect-row>p{display:flex;align-items:center;gap:7px;font-size:12px}.auth-progress{display:grid;gap:10px;padding:12px;border-radius:10px;background:var(--surface-soft)}.google-accounts{border-top:1px solid var(--line);padding-top:18px}.accounts-heading{display:flex;align-items:center;justify-content:space-between;gap:10px}.google-accounts ul{list-style:none;padding:0;margin:12px 0 0;display:grid;gap:12px}.google-accounts li{min-width:0;padding:12px;border:1px solid var(--line);border-radius:10px}.account-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.account-avatar{display:grid;place-items:center;width:34px;height:34px;border-radius:50%;background:var(--surface-soft);color:var(--text-secondary)}.account-copy{flex:1;min-width:100px;overflow-wrap:anywhere}.account-copy strong{font-weight:580}.account-copy p{font-size:11px;margin-top:3px}.account-manage{margin-top:6px}.account-manage summary{min-height:30px;font-size:11px}.accounts-empty{padding-top:12px;font-size:12px}.mail-entry{width:100%!important;justify-content:flex-start!important;padding:12px 14px!important;background:var(--surface-soft)!important}.mail-entry>svg{margin-left:auto}@media(max-width:420px){.service-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.google-card>.integration-header{align-items:flex-start;flex-wrap:wrap}.google-mark{width:40px;height:40px}.google-card{gap:18px}}\n</style>\n","<script setup lang=\"ts\">\nimport {onBeforeUnmount,onMounted,ref} from 'vue'\nimport {apiRequest} from '@/api/client'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport GmailPanel from './GmailPanel.vue'\nimport type {ComposioAccount,ComposioStatus} from '@shared/composioApps'\nconst base='/api/app/admin/composio'\nconst status=ref<ComposioStatus>(),accounts=ref<ComposioAccount[]>([]),busy=ref(false),error=ref(''),notice=ref(''),apiKey=ref(''),userId=ref(''),alias=ref(''),candidate=ref(''),authUrl=ref(''),mail=ref(false)\nlet deadline=0,beforeAuth=new Set<string>(),closed=false,timer:ReturnType<typeof setTimeout>|undefined\nconst stateLabel=(s:string)=>({ACTIVE:'已连接',INITIATED:'等待授权',PENDING:'等待授权',INITIALIZING:'连接中',EXPIRED:'已过期',FAILED:'连接失败',INACTIVE:'未启用',REVOKED:'已撤销'}[s.toUpperCase()]??s)\nasync function load(){\n const next=await apiRequest<ComposioStatus>(base+'/status');if(closed)return\n status.value=next;if(!next.legacy.some(c=>c.id===candidate.value))candidate.value=next.legacy[0]?.id??''\n if(!next.configured){accounts.value=[];return}\n const result=await apiRequest<{accounts:ComposioAccount[]}>(base+'/accounts');if(!closed)accounts.value=result.accounts\n}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){if(!closed)error.value=e instanceof Error?e.message:'Composio 操作失败'}finally{busy.value=false}}\nasync function importOld(){await action(async()=>{await apiRequest(base+'/import',{method:'POST',body:{candidateId:candidate.value,revision:status.value?.revision??0}});notice.value='已导入旧连接。原 Gmail 授权和 yaoyao 数据均已保留。';authUrl.value='';await load()})}\nasync function save(){await action(async()=>{await apiRequest(base+'/settings',{method:'PUT',body:{apiKey:apiKey.value.trim(),...(userId.value.trim()?{userId:userId.value.trim()}:{}),revision:status.value?.revision??0}});apiKey.value='';userId.value='';notice.value='Composio 连接配置已保存到主机。';await load()})}\nfunction stopPolling(){authUrl.value='';clearTimeout(timer)}\nfunction poll(){clearTimeout(timer);if(closed||!authUrl.value)return;if(Date.now()>deadline){notice.value='授权检查已结束,完成授权后可以手动刷新连接。';return}timer=setTimeout(async()=>{try{await load();if(accounts.value.some(a=>a.status==='ACTIVE'&&!beforeAuth.has(a.id))){authUrl.value='';notice.value='Gmail 已连接。'}}catch(e){if(!closed)error.value=e instanceof Error?e.message:'授权状态读取失败'}poll()},5000)}\nfunction trusted(value:string){try{const u=new URL(value);return u.protocol==='https:'&&!u.username&&!u.password&&(u.hostname==='composio.dev'||u.hostname.endsWith('.composio.dev'))?u.href:''}catch{return ''}}\nasync function connect(){if(busy.value)return;const popup=window.open('about:blank','_blank');if(popup)popup.opener=null;await action(async()=>{try{const next=await apiRequest<{url:string}>(base+'/authorize',{method:'POST',body:alias.value.trim()?{alias:alias.value.trim()}:{}});beforeAuth=new Set(accounts.value.filter(a=>a.status==='ACTIVE').map(a=>a.id));deadline=Date.now()+120_000;authUrl.value=trusted(next.url);if(popup&&authUrl.value)popup.location.href=authUrl.value;else popup?.close();alias.value='';notice.value='请在浏览器完成 Gmail 授权,然后刷新连接。';poll()}catch(e){popup?.close();throw e}})}\nasync function disconnect(account:ComposioAccount){if(!window.confirm('断开此 Composio Gmail 账号并撤销其授权?原 yaoyao 中使用此连接的功能也会受到影响。'))return;await action(async()=>{await apiRequest(base+'/accounts/'+encodeURIComponent(account.id),{method:'DELETE',body:{revision:status.value?.revision??0}});notice.value='已断开指定 Gmail 账号。';await load()})}\nonMounted(()=>action(load));onBeforeUnmount(()=>{closed=true;clearTimeout(timer)})\n</script>\n<template>\n <section class=\"integration-panel composio-apps\" aria-label=\"Composio 应用\">\n <header class=\"integration-header\"><div><h3>继续使用你的 Gmail</h3><p>通过 Composio 托管连接,与本地 Google 和 Nous 独立管理。</p></div><button class=\"quiet\" :disabled=\"busy\" aria-label=\"刷新 Composio 连接\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button></header>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\">{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\">{{notice}}</p>\n <article v-if=\"status?.legacy.length\" class=\"integration-card import-card\"><div class=\"integration-heading\"><span class=\"integration-mark\"><AppIcon name=\"archive\" :size=\"22\" /></span><div><h4>找到了 yaoyao 的旧连接</h4><p>导入项目配置,继续使用已有 Gmail 授权。</p></div></div><label v-if=\"status.legacy.length>1\">选择旧配置<select v-model=\"candidate\"><option v-for=\"c in status.legacy\" :key=\"c.id\" :value=\"c.id\">{{c.label}}</option></select></label><p v-if=\"status.configured\" class=\"hint\">导入将替换当前 Composio 项目配置。</p><button class=\"primary\" :disabled=\"busy||!candidate\" @click=\"importOld\"><AppIcon name=\"download\" :size=\"15\" />导入 yaoyao 旧连接</button></article>\n <article class=\"integration-card gmail-connection\"><header class=\"integration-header\"><div class=\"integration-heading\"><span class=\"integration-mark\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"25\" height=\"25\"></span><div><h3>Gmail</h3><p>邮件与草稿 · Composio</p></div></div><span class=\"integration-tag\" :class=\"{success:status?.configured}\">{{status?.configured?'项目已配置':'待配置'}}</span></header>\n <details class=\"composio-setup\"><summary><AppIcon name=\"settings\" :size=\"16\" />{{status?.configured?'更新项目配置':'手动配置 Composio'}}</summary><form @submit.prevent=\"save\"><p>也可以使用自己的 Composio 项目 API Key。凭据加密保存在这台主机,不会回显。</p><a href=\"https://platform.composio.dev\" target=\"_blank\" rel=\"noopener noreferrer\">打开 Composio ↗</a><label>项目 API Key<input v-model=\"apiKey\" type=\"password\" autocomplete=\"new-password\" placeholder=\"输入 Composio API Key\" required></label><label>已有用户标识(可选)<input v-model=\"userId\" placeholder=\"留空自动创建或保留现有标识\" maxlength=\"256\"></label><button class=\"primary\" :disabled=\"busy||!apiKey.trim()\">保存项目配置</button></form></details>\n <div class=\"accounts-heading\"><h4>已连接账号</h4><span class=\"integration-count\">{{accounts.length}} 个账号</span></div>\n <ul v-if=\"accounts.length\" class=\"composio-accounts\"><li v-for=\"account in accounts\" :key=\"account.id\"><div class=\"account-copy\"><strong>{{account.label}}</strong><p>Composio 托管授权</p></div><span class=\"integration-tag\" :class=\"{success:account.status==='ACTIVE'}\">{{stateLabel(account.status)}}</span><button class=\"quiet danger\" :disabled=\"busy\" :aria-label=\"'断开 '+account.label\" @click=\"disconnect(account)\">断开</button></li></ul><p v-else class=\"hint\">{{status?.configured?'还没有 Gmail 连接,可以添加账号。':'导入旧连接或配置项目后,即可连接 Gmail。'}}</p>\n <form class=\"add-account\" @submit.prevent=\"connect\"><label>{{accounts.length?'新账号别名':'账号别名(可选)'}}<input v-model=\"alias\" placeholder=\"例如:工作邮箱\" maxlength=\"64\" :required=\"!!accounts.length\" :disabled=\"busy||!status?.configured\"></label><button :disabled=\"busy||!status?.configured\"><AppIcon name=\"plus\" :size=\"16\" />连接 Gmail 账号</button></form>\n <div v-if=\"authUrl\" class=\"authorization-link\"><a :href=\"authUrl\" target=\"_blank\" rel=\"noopener noreferrer\">继续 Gmail 授权 ↗</a><button class=\"quiet\" @click=\"stopPolling\">停止检查授权状态</button></div>\n <button v-if=\"accounts.some(a=>a.status==='ACTIVE')\" class=\"mail-entry\" @click=\"mail=!mail\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"20\" height=\"20\">{{mail?'收起邮箱':'打开 Composio Gmail 邮箱'}}<AppIcon :name=\"mail?'chevron-down':'chevron-right'\" :size=\"17\" /></button>\n </article>\n <GmailPanel v-if=\"mail\" source=\"composio\" :accounts=\"accounts.filter(a=>a.status==='ACTIVE')\" />\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.composio-apps>.integration-header{align-items:flex-start}.composio-apps>.integration-header p{margin-top:6px;font-size:12px}.integration-card{display:grid;gap:18px}.import-card{background:var(--surface-soft)}.import-card>.primary{justify-self:start}.import-card p,.hint{font-size:12px;line-height:1.7}.composio-setup{border:1px solid var(--line);border-radius:10px;padding:0 13px;background:var(--surface-soft)}.composio-setup form{display:grid;gap:12px;padding:4px 0 16px}.composio-setup form p{font-size:12px;line-height:1.7}.accounts-heading{display:flex;justify-content:space-between;align-items:center}.composio-accounts{display:grid;gap:10px;list-style:none;padding:0;margin:0}.composio-accounts li{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:12px;border:1px solid var(--line);border-radius:10px}.account-copy{flex:1;min-width:120px;overflow-wrap:anywhere}.account-copy p{font-size:10px;margin-top:4px;font-family:var(--font-code)}.account-copy strong{font-weight:580}.add-account{display:flex;align-items:flex-end;gap:10px}.add-account label{flex:1}.authorization-link{display:grid;gap:8px;justify-items:start}.mail-entry{width:100%!important;justify-content:flex-start!important;padding:12px 14px!important;background:var(--surface-soft)!important}.mail-entry svg{margin-left:auto}@media(max-width:500px){.add-account{display:grid}.add-account button{justify-self:start}.gmail-connection>.integration-header{flex-wrap:wrap}}\n</style>\n","<script setup lang=\"ts\">\nimport {onBeforeUnmount,onMounted,ref} from 'vue'\nimport {apiRequest} from '@/api/client'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport GmailPanel from './GmailPanel.vue'\nimport type {ComposioAccount,ComposioStatus} from '@shared/composioApps'\nconst base='/api/app/admin/composio'\nconst status=ref<ComposioStatus>(),accounts=ref<ComposioAccount[]>([]),busy=ref(false),error=ref(''),notice=ref(''),apiKey=ref(''),userId=ref(''),alias=ref(''),candidate=ref(''),authUrl=ref(''),mail=ref(false)\nlet deadline=0,beforeAuth=new Set<string>(),closed=false,timer:ReturnType<typeof setTimeout>|undefined\nconst stateLabel=(s:string)=>({ACTIVE:'已连接',INITIATED:'等待授权',PENDING:'等待授权',INITIALIZING:'连接中',EXPIRED:'已过期',FAILED:'连接失败',INACTIVE:'未启用',REVOKED:'已撤销'}[s.toUpperCase()]??s)\nasync function load(){\n const next=await apiRequest<ComposioStatus>(base+'/status');if(closed)return\n status.value=next;if(!next.legacy.some(c=>c.id===candidate.value))candidate.value=next.legacy[0]?.id??''\n if(!next.configured){accounts.value=[];return}\n const result=await apiRequest<{accounts:ComposioAccount[]}>(base+'/accounts');if(!closed)accounts.value=result.accounts\n}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){if(!closed)error.value=e instanceof Error?e.message:'Composio 操作失败'}finally{busy.value=false}}\nasync function importOld(){await action(async()=>{await apiRequest(base+'/import',{method:'POST',body:{candidateId:candidate.value,revision:status.value?.revision??0}});notice.value='已导入旧连接。原 Gmail 授权和 yaoyao 数据均已保留。';authUrl.value='';await load()})}\nasync function save(){await action(async()=>{await apiRequest(base+'/settings',{method:'PUT',body:{apiKey:apiKey.value.trim(),...(userId.value.trim()?{userId:userId.value.trim()}:{}),revision:status.value?.revision??0}});apiKey.value='';userId.value='';notice.value='Composio 连接配置已保存到主机。';await load()})}\nfunction stopPolling(){authUrl.value='';clearTimeout(timer)}\nfunction poll(){clearTimeout(timer);if(closed||!authUrl.value)return;if(Date.now()>deadline){notice.value='授权检查已结束,完成授权后可以手动刷新连接。';return}timer=setTimeout(async()=>{try{await load();if(accounts.value.some(a=>a.status==='ACTIVE'&&!beforeAuth.has(a.id))){authUrl.value='';notice.value='Gmail 已连接。'}}catch(e){if(!closed)error.value=e instanceof Error?e.message:'授权状态读取失败'}poll()},5000)}\nfunction trusted(value:string){try{const u=new URL(value);return u.protocol==='https:'&&!u.username&&!u.password&&(u.hostname==='composio.dev'||u.hostname.endsWith('.composio.dev'))?u.href:''}catch{return ''}}\nasync function connect(){if(busy.value)return;const popup=window.open('about:blank','_blank');if(popup)popup.opener=null;await action(async()=>{try{const next=await apiRequest<{url:string}>(base+'/authorize',{method:'POST',body:alias.value.trim()?{alias:alias.value.trim()}:{}});beforeAuth=new Set(accounts.value.filter(a=>a.status==='ACTIVE').map(a=>a.id));deadline=Date.now()+120_000;authUrl.value=trusted(next.url);if(popup&&authUrl.value)popup.location.href=authUrl.value;else popup?.close();alias.value='';notice.value='请在浏览器完成 Gmail 授权,然后刷新连接。';poll()}catch(e){popup?.close();throw e}})}\nasync function disconnect(account:ComposioAccount){if(!window.confirm('断开此 Composio Gmail 账号并撤销其授权?原 yaoyao 中使用此连接的功能也会受到影响。'))return;await action(async()=>{await apiRequest(base+'/accounts/'+encodeURIComponent(account.id),{method:'DELETE',body:{revision:status.value?.revision??0}});notice.value='已断开指定 Gmail 账号。';await load()})}\nonMounted(()=>action(load));onBeforeUnmount(()=>{closed=true;clearTimeout(timer)})\n</script>\n<template>\n <section class=\"integration-panel composio-apps\" aria-label=\"Composio 应用\">\n <header class=\"integration-header\"><div><h3>继续使用你的 Gmail</h3><p>通过 Composio 托管连接,与本地 Google 和 Nous 独立管理。</p></div><button class=\"quiet\" :disabled=\"busy\" aria-label=\"刷新 Composio 连接\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button></header>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\">{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\">{{notice}}</p>\n <article v-if=\"status?.legacy.length\" class=\"integration-card import-card\"><div class=\"integration-heading\"><span class=\"integration-mark\"><AppIcon name=\"archive\" :size=\"22\" /></span><div><h4>找到了 yaoyao 的旧连接</h4><p>导入项目配置,继续使用已有 Gmail 授权。</p></div></div><label v-if=\"status.legacy.length>1\">选择旧配置<select v-model=\"candidate\"><option v-for=\"c in status.legacy\" :key=\"c.id\" :value=\"c.id\">{{c.label}}</option></select></label><p v-if=\"status.configured\" class=\"hint\">导入将替换当前 Composio 项目配置。</p><button class=\"primary\" :disabled=\"busy||!candidate\" @click=\"importOld\"><AppIcon name=\"download\" :size=\"15\" />导入 yaoyao 旧连接</button></article>\n <article class=\"integration-card gmail-connection\"><header class=\"integration-header\"><div class=\"integration-heading\"><span class=\"integration-mark\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"25\" height=\"25\"></span><div><h3>Gmail</h3><p>邮件与草稿 · Composio</p></div></div><span class=\"integration-tag\" :class=\"{success:status?.configured}\">{{status?.configured?'项目已配置':'待配置'}}</span></header>\n <details class=\"composio-setup\"><summary><AppIcon name=\"settings\" :size=\"16\" />{{status?.configured?'更新项目配置':'手动配置 Composio'}}</summary><form @submit.prevent=\"save\"><p>也可以使用自己的 Composio 项目 API Key。凭据加密保存在这台主机,不会回显。</p><a href=\"https://platform.composio.dev\" target=\"_blank\" rel=\"noopener noreferrer\">打开 Composio ↗</a><label>项目 API Key<input v-model=\"apiKey\" type=\"password\" autocomplete=\"new-password\" placeholder=\"输入 Composio API Key\" required></label><label>已有用户标识(可选)<input v-model=\"userId\" placeholder=\"留空自动创建或保留现有标识\" maxlength=\"256\"></label><button class=\"primary\" :disabled=\"busy||!apiKey.trim()\">保存项目配置</button></form></details>\n <div class=\"accounts-heading\"><h4>已连接账号</h4><span class=\"integration-count\">{{accounts.length}} 个账号</span></div>\n <ul v-if=\"accounts.length\" class=\"composio-accounts\"><li v-for=\"account in accounts\" :key=\"account.id\"><div class=\"account-copy\"><strong>{{account.label}}</strong><p>Composio 托管授权</p></div><span class=\"integration-tag\" :class=\"{success:account.status==='ACTIVE'}\">{{stateLabel(account.status)}}</span><button class=\"quiet danger\" :disabled=\"busy\" :aria-label=\"'断开 '+account.label\" @click=\"disconnect(account)\">断开</button></li></ul><p v-else class=\"hint\">{{status?.configured?'还没有 Gmail 连接,可以添加账号。':'导入旧连接或配置项目后,即可连接 Gmail。'}}</p>\n <form class=\"add-account\" @submit.prevent=\"connect\"><label>{{accounts.length?'新账号别名':'账号别名(可选)'}}<input v-model=\"alias\" placeholder=\"例如:工作邮箱\" maxlength=\"64\" :required=\"!!accounts.length\" :disabled=\"busy||!status?.configured\"></label><button :disabled=\"busy||!status?.configured\"><AppIcon name=\"plus\" :size=\"16\" />连接 Gmail 账号</button></form>\n <div v-if=\"authUrl\" class=\"authorization-link\"><a :href=\"authUrl\" target=\"_blank\" rel=\"noopener noreferrer\">继续 Gmail 授权 ↗</a><button class=\"quiet\" @click=\"stopPolling\">停止检查授权状态</button></div>\n <button v-if=\"accounts.some(a=>a.status==='ACTIVE')\" class=\"mail-entry\" @click=\"mail=!mail\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"20\" height=\"20\">{{mail?'收起邮箱':'打开 Composio Gmail 邮箱'}}<AppIcon :name=\"mail?'chevron-down':'chevron-right'\" :size=\"17\" /></button>\n </article>\n <GmailPanel v-if=\"mail\" source=\"composio\" :accounts=\"accounts.filter(a=>a.status==='ACTIVE')\" />\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.composio-apps>.integration-header{align-items:flex-start}.composio-apps>.integration-header p{margin-top:6px;font-size:12px}.integration-card{display:grid;gap:18px}.import-card{background:var(--surface-soft)}.import-card>.primary{justify-self:start}.import-card p,.hint{font-size:12px;line-height:1.7}.composio-setup{border:1px solid var(--line);border-radius:10px;padding:0 13px;background:var(--surface-soft)}.composio-setup form{display:grid;gap:12px;padding:4px 0 16px}.composio-setup form p{font-size:12px;line-height:1.7}.accounts-heading{display:flex;justify-content:space-between;align-items:center}.composio-accounts{display:grid;gap:10px;list-style:none;padding:0;margin:0}.composio-accounts li{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:12px;border:1px solid var(--line);border-radius:10px}.account-copy{flex:1;min-width:120px;overflow-wrap:anywhere}.account-copy p{font-size:10px;margin-top:4px;font-family:var(--font-code)}.account-copy strong{font-weight:580}.add-account{display:flex;align-items:flex-end;gap:10px}.add-account label{flex:1}.authorization-link{display:grid;gap:8px;justify-items:start}.mail-entry{width:100%!important;justify-content:flex-start!important;padding:12px 14px!important;background:var(--surface-soft)!important}.mail-entry svg{margin-left:auto}@media(max-width:500px){.add-account{display:grid}.add-account button{justify-self:start}.gmail-connection>.integration-header{flex-wrap:wrap}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { apiRequest } from '@/api/client'\nimport { hermesApi, type HermesOAuthProvider } from '@/api/hermes'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport ProviderLogo from '@/components/common/ProviderLogo.vue'\nimport ProviderLoginDialog from '@/components/app/ProviderLoginDialog.vue'\nimport { bundledProviderLogo } from '@shared/botProviderLogos'\nimport NativePluginsPanel from './NativePluginsPanel.vue'\nimport LocalAppsPanel from './LocalAppsPanel.vue'\nimport ComposioAppsPanel from './ComposioAppsPanel.vue'\nimport HermesMcpPanel from '@/components/app/HermesMcpPanel.vue'\nimport type { HermesConnectorStatus, HermesConnectorCatalogItem, HermesConnectorAccount, HermesConnectorOperation, HermesConnectorConnectInput } from '@shared/hermesConnectors'\nconst emit = defineEmits<{ 'dirty-change': [value: boolean] }>()\nconst base = '/api/app/admin/hermes/connectors'\nconst surface = ref<'local' | 'apps' | 'mcp' | 'native' | 'composio'>('local'), view = ref('marketplace'), canManage = ref(false)\nconst status = ref<HermesConnectorStatus>(), cards = ref<HermesConnectorCatalogItem[]>([]), accounts = ref<HermesConnectorAccount[]>([])\nconst loading = ref(true), busy = ref(false), error = ref(''), notice = ref(''), search = ref(''), alias = ref(''), editing = ref(''), login = ref<HermesOAuthProvider>()\nconst operation = ref<HermesConnectorOperation>(), visibleCount = ref(40), adding = ref(''), newAlias = ref('')\nwatch([search, view], () => { visibleCount.value = 40 })\nwatch([editing, adding], values => emit('dirty-change', values.some(Boolean)))\nlet closed = false, generation = 0, timer: ReturnType<typeof setTimeout> | undefined\nconst call = <T,>(path: string, method: string, body?: unknown) => apiRequest<T>(base + path, { method, ...(body === undefined ? {} : { body: body as any }) })\nconst linked = (slug: string) => accounts.value.filter(account => account.connector === slug)\nconst filtered = computed(() => {\n const all = new Map(cards.value.map(card => [card.slug, card]))\n for (const account of accounts.value) if (!all.has(account.connector)) all.set(account.connector, { slug: account.connector, name: account.connector, description: '', category: '' })\n const query = search.value.trim().toLowerCase()\n return [...all.values()].filter(card => (view.value !== 'connected' || linked(card.slug).length) && `${card.name} ${card.slug} ${card.description}`.toLowerCase().includes(query))\n})\nconst stateLabel = (state: string) => ({ active: '已连接', pending: '待授权', failed: '连接失败', expired: '已过期', revoked: '已撤销', inactive: '未启用' }[state] ?? state)\nconst operationLabel = computed(() => ({ pending: '等待完成授权', complete: '连接已完成', failed: '连接失败', cancelled: '已取消授权', expired: '授权已过期' }[operation.value?.status ?? 'pending']))\nfunction external(value?: string) { try { const url = new URL(value ?? ''); return url.protocol === 'https:' && !url.username && !url.password ? url.href : '' } catch { return '' } }\nasync function load() {\n const version = ++generation\n const results = await Promise.allSettled([apiRequest<HermesConnectorStatus>(base + '/status'), apiRequest<{ connectors: HermesConnectorCatalogItem[] }>(base + '/catalog'), apiRequest<{ accounts: HermesConnectorAccount[] }>(base + '/accounts')])\n if (closed || version !== generation) return\n const [s, c, a] = results\n if (s.status === 'fulfilled') status.value = s.value\n if (c.status === 'fulfilled') cards.value = c.value.connectors\n if (a.status === 'fulfilled') accounts.value = a.value.accounts\n error.value = results.filter((r): r is PromiseRejectedResult => r.status === 'rejected').map(r => r.reason instanceof Error ? r.reason.message : '读取失败,请重试').join(';')\n loading.value = false\n}\nasync function action(fn: () => Promise<void>) {\n if (busy.value) return\n busy.value = true; error.value = ''; notice.value = ''\n try { await fn(); if (!closed) await load() } catch (e) { if (!closed) error.value = e instanceof Error ? e.message : '操作失败,请重试' }\n finally { busy.value = false }\n}\nfunction poll() {\n clearTimeout(timer)\n if (closed || operation.value?.status !== 'pending') return\n if (Date.now() > operation.value.deadlineAt) { operation.value = { ...operation.value, status: 'expired' }; return }\n timer = setTimeout(async () => {\n const id = operation.value?.id\n if (!id || closed) return\n try {\n const next = await apiRequest<HermesConnectorOperation>(`${base}/operations/${encodeURIComponent(id)}`)\n if (closed || operation.value?.id !== id) return\n operation.value = next\n if (next.status === 'complete') { notice.value = '应用已连接,所有 Bot 均可使用。'; await load() }\n } catch (e) { if (!closed) error.value = e instanceof Error ? e.message : '暂时无法刷新授权状态' }\n poll()\n }, 2500)\n}\nasync function connect(card: HermesConnectorCatalogItem, account?: HermesConnectorAccount) {\n if (busy.value || operation.value?.status === 'pending') return\n if (!account && linked(card.slug).length && adding.value !== card.slug) { adding.value = card.slug; newAlias.value = ''; return }\n const accountAlias = !account && adding.value === card.slug ? newAlias.value.trim() : undefined\n if (accountAlias !== undefined && !/^[a-z0-9][a-z0-9-]{0,31}$/.test(accountAlias)) { error.value = '新账号别名需为 1–32 位小写字母、数字或连字符,以字母或数字开头。'; return }\n if (accountAlias && linked(card.slug).some(item => item.alias === accountAlias)) { error.value = '该应用已有同名账号,请换一个别名。'; return }\n const popup = window.open('about:blank', '_blank'); if (popup) popup.opener = null\n await action(async () => {\n try {\n const body: HermesConnectorConnectInput = { connector: card.slug, ...(accountAlias ? { alias: accountAlias } : {}), ...(account ? { reconnect: true, connectionId: account.id } : {}) }\n const next = await call<HermesConnectorOperation>('/operations', 'POST', body)\n if (closed) { popup?.close(); return }\n operation.value = next; adding.value = ''; newAlias.value = ''\n const url = external(next.targets.find(target => target.connectUrl)?.connectUrl)\n if (popup && url) popup.location.href = url; else popup?.close()\n notice.value = '请在浏览器完成授权。授权状态会自动刷新。'; poll()\n } catch (e) { popup?.close(); throw e }\n })\n}\nasync function cancel() { await action(async () => { if (operation.value) operation.value = await call<HermesConnectorOperation>(`/operations/${encodeURIComponent(operation.value.id)}`, 'DELETE'); clearTimeout(timer) }) }\nasync function disconnect(account: HermesConnectorAccount) { if (window.confirm('断开这个应用账号?所有 Bot 将失去此账号的访问权限。')) await action(async () => { await call(`/accounts/${encodeURIComponent(account.id)}`, 'DELETE') }) }\nasync function saveAlias(account: HermesConnectorAccount) { await action(async () => { if (!/^[a-z0-9][a-z0-9-]{0,31}$/.test(alias.value.trim())) throw new Error('别名需为 1–32 位小写字母、数字或连字符,以字母或数字开头。'); await call(`/accounts/${encodeURIComponent(account.id)}`, 'PATCH', { alias: alias.value.trim() }); editing.value = ''; alias.value = '' }) }\nasync function loginNous() { await action(async () => { const providers = (await hermesApi.oauth()).providers; const provider = providers.find(p => p.id === 'nous'); if (!provider) throw new Error('Nous 登录暂不可用,请检查主机版本。'); login.value = provider }) }\nonMounted(async () => {\n try { const bootstrap = await apiRequest<{ user?: { role?: string } }>('/api/app/bootstrap'); canManage.value = bootstrap.user?.role === 'admin' }\n catch (e) { error.value = e instanceof Error ? e.message : '无法读取管理权限' }\n if (closed) return\n if (canManage.value) await load(); else loading.value = false\n})\nonBeforeUnmount(() => { closed = true; generation++; clearTimeout(timer) })\n</script>\n<template>\n <section class=\"bot-plugins\" aria-label=\"Bot 插件管理\">\n <p class=\"intro\"><AppIcon name=\"link\" :size=\"14\" />连接的应用由这台服务器的所有 Bot 共用。</p>\n <div class=\"plugin-tabs\" role=\"tablist\" aria-label=\"插件类型\"><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'local'\" @click=\"surface = 'local'\"><AppIcon name=\"monitor\" :size=\"16\" />本地应用</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'native'\" @click=\"surface = 'native'\"><AppIcon name=\"artifacts\" :size=\"16\" />本地插件</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'composio'\" @click=\"surface = 'composio'\"><AppIcon name=\"link\" :size=\"16\" />Composio</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'apps'\" @click=\"surface = 'apps'\"><AppIcon name=\"link\" :size=\"17\" />Nous 应用</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'mcp'\" @click=\"surface = 'mcp'\"><AppIcon name=\"tools\" :size=\"17\" />MCP 服务</button></div>\n <NativePluginsPanel v-if=\"surface === 'native' && canManage\" /><p v-else-if=\"surface === 'native'\">本地插件由管理员管理。</p>\n <ComposioAppsPanel v-else-if=\"surface === 'composio' && canManage\" /><p v-else-if=\"surface === 'composio'\">Composio 连接由管理员管理。</p>\n <LocalAppsPanel v-else-if=\"surface === 'local' && canManage\" /><p v-else-if=\"surface === 'local'\">本地连接由管理员管理。</p>\n <HermesMcpPanel v-else-if=\"surface === 'mcp'\" :can-manage=\"canManage\" @dirty-change=\"emit('dirty-change', $event)\" />\n <template v-else>\n <p v-if=\"!loading && !canManage\" class=\"plugin-notice\">应用连接由管理员管理。</p>\n <template v-else>\n <p v-if=\"error\" class=\"plugin-error\" role=\"alert\">{{ error }} <button type=\"button\" :disabled=\"busy\" @click=\"load\">重试</button></p>\n <p v-if=\"notice\" class=\"plugin-notice\" role=\"status\">{{ notice }}</p><p v-if=\"loading\" role=\"status\">正在读取应用…</p>\n <div v-else class=\"connection-settings\">\n <template v-if=\"status?.authenticated\"><p class=\"hint\">{{ status.accountLabel || 'Nous 账号已登录' }}</p><p v-if=\"!status.available\" class=\"hint\">{{ status.reason === 'disabled' || status.reason === 'not_enabled' ? '应用连接服务尚未启用,请检查主机设置后刷新。' : '应用连接服务暂不可用,请刷新或检查主机设置。' }}</p><button :disabled=\"busy\" @click=\"loginNous\">重新登录 Nous</button></template>\n <template v-else-if=\"status?.reason === 'needs_auth' || (status?.available && !status.authenticated)\"><p>登录 Nous 后即可连接应用。登录只用于应用授权,当前主模型保持不变。</p><button :disabled=\"busy\" @click=\"loginNous\">登录 Nous</button></template>\n <p v-else-if=\"!status?.available\" class=\"hint\">{{ status?.reason === 'disabled' || status?.reason === 'not_enabled' ? '应用连接服务尚未启用,请检查主机设置后刷新。' : '应用连接服务暂不可用,请刷新或检查主机设置。' }}</p>\n </div>\n <section v-if=\"operation\" class=\"connection-settings\" aria-label=\"应用授权进度\"><p role=\"status\">{{ operationLabel }}</p><p v-if=\"operation.error\" class=\"plugin-error\">{{ operation.error }}</p><div v-for=\"target in operation.targets\" :key=\"target.connector\"><p v-if=\"target.detail\" class=\"hint\">{{ target.detail }}</p><a v-if=\"operation.status === 'pending' && external(target.connectUrl)\" :href=\"external(target.connectUrl)\" target=\"_blank\" rel=\"noopener noreferrer\">继续授权 <AppIcon name=\"external\" :size=\"13\" /></a></div><button v-if=\"operation.status === 'pending'\" :disabled=\"busy\" @click=\"cancel\">取消授权</button></section>\n <div class=\"plugin-toolbar\"><div class=\"view-tabs\" role=\"tablist\" aria-label=\"应用列表\"><button role=\"tab\" :aria-selected=\"view === 'marketplace'\" @click=\"view = 'marketplace'\">应用目录</button><button role=\"tab\" :aria-selected=\"view === 'connected'\" @click=\"view = 'connected'\">已连接 {{ accounts.length || '' }}</button></div><button :disabled=\"busy || loading\" aria-label=\"刷新应用连接\" @click=\"load\"><AppIcon name=\"refresh\" :size=\"17\" /></button></div>\n <label class=\"plugin-search\"><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"search\" type=\"search\" aria-label=\"搜索应用\" placeholder=\"搜索应用\" /></label>\n <div class=\"app-grid\"><article v-for=\"card in filtered.slice(0, visibleCount)\" :key=\"card.slug\" class=\"app-card\">\n <div class=\"card-heading\"><ProviderLogo :name=\"card.name\" :logo=\"card.logoUrl\" :bundled-logo=\"bundledProviderLogo(card.slug)\" /><h4>{{ card.name }}</h4></div><p class=\"hint\">{{ card.description }}</p>\n <div v-for=\"account in linked(card.slug)\" :key=\"account.id\" class=\"account-block\">\n <div class=\"app-account\"><span>{{ account.alias || account.label }}<small>{{ stateLabel(account.status) }}</small></span><button class=\"danger\" :disabled=\"busy\" :aria-label=\"`断开 ${card.name} ${account.alias || account.label}`\" @click=\"disconnect(account)\">断开</button></div>\n <p v-if=\"account.statusReason\" class=\"hint\">{{ account.statusReason }}</p>\n <form v-if=\"editing === account.id\" @submit.prevent=\"saveAlias(account)\"><label>账号别名<input v-model=\"alias\" maxlength=\"32\" pattern=\"[a-z0-9][a-z0-9-]{0,31}\" required placeholder=\"work-mail\" /><small class=\"hint\">1–32 位小写字母、数字或连字符,以字母或数字开头。</small></label><div class=\"card-actions\"><button type=\"button\" @click=\"editing = ''\">取消</button><button :disabled=\"busy\" type=\"submit\">保存别名</button></div></form>\n <div v-else class=\"card-actions\"><button :disabled=\"busy\" @click=\"editing = account.id; alias = account.alias || ''\">修改别名</button><button v-if=\"!account.active\" :disabled=\"busy || operation?.status === 'pending' || !status?.authenticated\" @click=\"connect(card, account)\">重新连接</button></div>\n </div>\n <form v-if=\"adding === card.slug\" @submit.prevent=\"connect(card)\"><label>新账号别名<input v-model=\"newAlias\" maxlength=\"32\" pattern=\"[a-z0-9][a-z0-9-]{0,31}\" required placeholder=\"personal-mail\" /><small class=\"hint\">使用不同的别名添加第二个账号:1–32 位小写字母、数字或连字符。</small></label><button type=\"button\" @click=\"adding = ''; newAlias = ''\">取消添加</button></form>\n <div class=\"card-actions\"><button :disabled=\"busy || !status?.authenticated || !status?.available || operation?.status === 'pending'\" @click=\"connect(card)\">{{ adding === card.slug ? '继续连接' : linked(card.slug).length ? '添加账号' : '连接' }}</button></div>\n </article></div>\n <button v-if=\"filtered.length > visibleCount\" type=\"button\" @click=\"visibleCount += 40\">显示更多应用({{ filtered.length - visibleCount }})</button><p v-if=\"!loading && !filtered.length\" class=\"plugin-empty\">{{ search ? '没有匹配的应用。' : view === 'connected' ? '还没有已连接的应用,可前往应用目录添加。' : '应用目录暂为空。' }}</p>\n </template>\n </template>\n <ProviderLoginDialog v-if=\"login\" :provider=\"login\" @close=\"login = undefined\" @success=\"login = undefined; load()\" />\n </section>\n</template>\n<style scoped>\n.bot-plugins{display:grid;gap:16px;color:var(--text-primary);font-size:13px}.intro,.hint{color:var(--text-secondary);line-height:1.6;margin:0}.hint{font-size:12px}.plugin-tabs,.view-tabs,.plugin-toolbar,.card-heading,.card-actions,.editor-actions{display:flex;align-items:center;gap:8px}.plugin-tabs{flex-wrap:wrap;border-bottom:1px solid var(--line);padding-bottom:8px}.plugin-tabs button[aria-selected=true],.view-tabs button[aria-selected=true]{background:var(--surface-soft);font-weight:650}.plugin-toolbar{justify-content:space-between}.plugin-toolbar p{margin:0;font-weight:650}.card-heading h4{font-size:14px;margin:0;flex:1;min-width:0;overflow-wrap:anywhere}.card-actions{flex-wrap:wrap}.bot-plugins button{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:44px;padding:6px 10px;border:1px solid var(--line);border-radius:8px;background:var(--surface-raised);color:inherit;font:inherit;cursor:pointer}.bot-plugins button:hover{background:var(--surface-hover)}.bot-plugins button:disabled{opacity:.45;cursor:not-allowed}.bot-plugins :is(button,input,select,textarea,a,summary):focus-visible{outline:2px solid var(--accent);outline-offset:2px}.bot-plugins a{color:var(--accent);display:inline-flex;align-items:center;gap:4px}.bot-plugins .danger{color:var(--danger)}.bot-plugins .primary{background:var(--accent);color:var(--text-on-solid);border-color:var(--accent)}.bot-plugins label{display:grid;gap:6px;font-size:12px;line-height:1.6}.bot-plugins input:not([type=checkbox]),.bot-plugins select,.bot-plugins textarea{width:100%;box-sizing:border-box;min-width:0;padding:9px 10px;border:1px solid var(--line);border-radius:8px;color:var(--text-primary);background:var(--surface);font:inherit}.bot-plugins textarea{resize:vertical;font-family:var(--font-mono,monospace)}.plugin-search{position:relative}.plugin-search>svg{position:absolute;left:10px;top:11px;color:var(--text-muted)}.plugin-search input{padding-left:34px!important}.plugin-editor,.mcp-card,.app-card,.connection-settings{border:1px solid var(--line);border-radius:12px;padding:14px;display:grid;gap:12px;background:var(--surface)}.plugin-editor h4{margin:0}.plugin-editor{background:var(--surface-soft)}.editor-actions{justify-content:flex-end}.bot-plugins fieldset{border:1px solid var(--line);border-radius:8px;display:grid;gap:4px;max-height:180px;overflow:auto}.bot-plugins .check-row{display:flex;align-items:center;gap:8px;min-height:32px}.check-row input{accent-color:var(--accent)}.app-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.app-card{align-content:start}.app-account{display:flex;align-items:center;justify-content:space-between;gap:8px;border-top:1px solid var(--line);padding-top:8px}.app-account span{min-width:0;overflow-wrap:anywhere}.app-account small{display:block;color:var(--text-muted);margin-top:3px}.badge{font-size:11px;color:var(--text-muted);white-space:nowrap}.badge.enabled{color:var(--accent)}.endpoint{overflow-wrap:anywhere}.plugin-error,.plugin-notice{padding:12px;border-radius:8px;background:var(--surface-soft);margin:0;line-height:1.6}.plugin-error{color:var(--danger)}.plugin-empty{text-align:center;color:var(--text-secondary);padding:28px 12px;line-height:1.8}.connection-settings{display:block}.connection-settings summary{cursor:pointer;font-weight:600;line-height:1.8}.connection-settings summary span{float:right;font-weight:400;color:var(--text-muted)}.connection-settings p{margin:12px 0}.connection-settings form{display:grid;gap:12px}.grant-editor{display:grid;gap:8px}@media(max-width:767px){.app-grid{grid-template-columns:1fr}.bot-plugins button{min-height:44px}.bot-plugins .check-row{min-height:44px}}\n</style>\n\n<style scoped>\n.bot-plugins{gap:22px}.intro{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--text-muted)}.plugin-tabs{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:4px;padding:4px;border:1px solid var(--line);border-radius:12px;background:var(--surface-soft)}.plugin-tabs button{min-height:38px;padding:7px 6px;border:0;border-radius:8px;background:transparent;color:var(--text-secondary);font-size:12px;white-space:nowrap}.plugin-tabs button[aria-selected=true]{background:var(--surface);color:var(--text-primary);font-weight:600;box-shadow:0 1px 4px #0000000b}.connection-settings{padding:18px;border-radius:13px}.app-card{padding:18px;border-radius:13px}.app-grid{gap:12px}.app-card>.hint{line-height:1.7}.plugin-search input{border-radius:10px}.plugin-toolbar .view-tabs button{min-height:36px;font-size:12px}.plugin-toolbar>button{border-color:transparent}@media(max-width:600px){.plugin-tabs{gap:2px;padding:3px;display:flex;flex-wrap:wrap}.plugin-tabs button{flex:1 1 85px}.plugin-tabs button{gap:5px;min-height:44px;font-size:11px}.plugin-tabs button>svg{width:14px;height:14px}.bot-plugins{gap:18px}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { apiRequest } from '@/api/client'\nimport { hermesApi, type HermesOAuthProvider } from '@/api/hermes'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport ProviderLogo from '@/components/common/ProviderLogo.vue'\nimport ProviderLoginDialog from '@/components/app/ProviderLoginDialog.vue'\nimport { bundledProviderLogo } from '@shared/botProviderLogos'\nimport NativePluginsPanel from './NativePluginsPanel.vue'\nimport LocalAppsPanel from './LocalAppsPanel.vue'\nimport ComposioAppsPanel from './ComposioAppsPanel.vue'\nimport HermesMcpPanel from '@/components/app/HermesMcpPanel.vue'\nimport type { HermesConnectorStatus, HermesConnectorCatalogItem, HermesConnectorAccount, HermesConnectorOperation, HermesConnectorConnectInput } from '@shared/hermesConnectors'\nconst emit = defineEmits<{ 'dirty-change': [value: boolean] }>()\nconst base = '/api/app/admin/hermes/connectors'\nconst surface = ref<'local' | 'apps' | 'mcp' | 'native' | 'composio'>('local'), view = ref('marketplace'), canManage = ref(false)\nconst status = ref<HermesConnectorStatus>(), cards = ref<HermesConnectorCatalogItem[]>([]), accounts = ref<HermesConnectorAccount[]>([])\nconst loading = ref(true), busy = ref(false), error = ref(''), notice = ref(''), search = ref(''), alias = ref(''), editing = ref(''), login = ref<HermesOAuthProvider>()\nconst operation = ref<HermesConnectorOperation>(), visibleCount = ref(40), adding = ref(''), newAlias = ref('')\nwatch([search, view], () => { visibleCount.value = 40 })\nwatch([editing, adding], values => emit('dirty-change', values.some(Boolean)))\nlet closed = false, generation = 0, timer: ReturnType<typeof setTimeout> | undefined\nconst call = <T,>(path: string, method: string, body?: unknown) => apiRequest<T>(base + path, { method, ...(body === undefined ? {} : { body: body as any }) })\nconst linked = (slug: string) => accounts.value.filter(account => account.connector === slug)\nconst filtered = computed(() => {\n const all = new Map(cards.value.map(card => [card.slug, card]))\n for (const account of accounts.value) if (!all.has(account.connector)) all.set(account.connector, { slug: account.connector, name: account.connector, description: '', category: '' })\n const query = search.value.trim().toLowerCase()\n return [...all.values()].filter(card => (view.value !== 'connected' || linked(card.slug).length) && `${card.name} ${card.slug} ${card.description}`.toLowerCase().includes(query))\n})\nconst stateLabel = (state: string) => ({ active: '已连接', pending: '待授权', failed: '连接失败', expired: '已过期', revoked: '已撤销', inactive: '未启用' }[state] ?? state)\nconst operationLabel = computed(() => ({ pending: '等待完成授权', complete: '连接已完成', failed: '连接失败', cancelled: '已取消授权', expired: '授权已过期' }[operation.value?.status ?? 'pending']))\nfunction external(value?: string) { try { const url = new URL(value ?? ''); return url.protocol === 'https:' && !url.username && !url.password ? url.href : '' } catch { return '' } }\nasync function load() {\n const version = ++generation\n const results = await Promise.allSettled([apiRequest<HermesConnectorStatus>(base + '/status'), apiRequest<{ connectors: HermesConnectorCatalogItem[] }>(base + '/catalog'), apiRequest<{ accounts: HermesConnectorAccount[] }>(base + '/accounts')])\n if (closed || version !== generation) return\n const [s, c, a] = results\n if (s.status === 'fulfilled') status.value = s.value\n if (c.status === 'fulfilled') cards.value = c.value.connectors\n if (a.status === 'fulfilled') accounts.value = a.value.accounts\n error.value = results.filter((r): r is PromiseRejectedResult => r.status === 'rejected').map(r => r.reason instanceof Error ? r.reason.message : '读取失败,请重试').join(';')\n loading.value = false\n}\nasync function action(fn: () => Promise<void>) {\n if (busy.value) return\n busy.value = true; error.value = ''; notice.value = ''\n try { await fn(); if (!closed) await load() } catch (e) { if (!closed) error.value = e instanceof Error ? e.message : '操作失败,请重试' }\n finally { busy.value = false }\n}\nfunction poll() {\n clearTimeout(timer)\n if (closed || operation.value?.status !== 'pending') return\n if (Date.now() > operation.value.deadlineAt) { operation.value = { ...operation.value, status: 'expired' }; return }\n timer = setTimeout(async () => {\n const id = operation.value?.id\n if (!id || closed) return\n try {\n const next = await apiRequest<HermesConnectorOperation>(`${base}/operations/${encodeURIComponent(id)}`)\n if (closed || operation.value?.id !== id) return\n operation.value = next\n if (next.status === 'complete') { notice.value = '应用已连接,所有 Bot 均可使用。'; await load() }\n } catch (e) { if (!closed) error.value = e instanceof Error ? e.message : '暂时无法刷新授权状态' }\n poll()\n }, 2500)\n}\nasync function connect(card: HermesConnectorCatalogItem, account?: HermesConnectorAccount) {\n if (busy.value || operation.value?.status === 'pending') return\n if (!account && linked(card.slug).length && adding.value !== card.slug) { adding.value = card.slug; newAlias.value = ''; return }\n const accountAlias = !account && adding.value === card.slug ? newAlias.value.trim() : undefined\n if (accountAlias !== undefined && !/^[a-z0-9][a-z0-9-]{0,31}$/.test(accountAlias)) { error.value = '新账号别名需为 1–32 位小写字母、数字或连字符,以字母或数字开头。'; return }\n if (accountAlias && linked(card.slug).some(item => item.alias === accountAlias)) { error.value = '该应用已有同名账号,请换一个别名。'; return }\n const popup = window.open('about:blank', '_blank'); if (popup) popup.opener = null\n await action(async () => {\n try {\n const body: HermesConnectorConnectInput = { connector: card.slug, ...(accountAlias ? { alias: accountAlias } : {}), ...(account ? { reconnect: true, connectionId: account.id } : {}) }\n const next = await call<HermesConnectorOperation>('/operations', 'POST', body)\n if (closed) { popup?.close(); return }\n operation.value = next; adding.value = ''; newAlias.value = ''\n const url = external(next.targets.find(target => target.connectUrl)?.connectUrl)\n if (popup && url) popup.location.href = url; else popup?.close()\n notice.value = '请在浏览器完成授权。授权状态会自动刷新。'; poll()\n } catch (e) { popup?.close(); throw e }\n })\n}\nasync function cancel() { await action(async () => { if (operation.value) operation.value = await call<HermesConnectorOperation>(`/operations/${encodeURIComponent(operation.value.id)}`, 'DELETE'); clearTimeout(timer) }) }\nasync function disconnect(account: HermesConnectorAccount) { if (window.confirm('断开这个应用账号?所有 Bot 将失去此账号的访问权限。')) await action(async () => { await call(`/accounts/${encodeURIComponent(account.id)}`, 'DELETE') }) }\nasync function saveAlias(account: HermesConnectorAccount) { await action(async () => { if (!/^[a-z0-9][a-z0-9-]{0,31}$/.test(alias.value.trim())) throw new Error('别名需为 1–32 位小写字母、数字或连字符,以字母或数字开头。'); await call(`/accounts/${encodeURIComponent(account.id)}`, 'PATCH', { alias: alias.value.trim() }); editing.value = ''; alias.value = '' }) }\nasync function loginNous() { await action(async () => { const providers = (await hermesApi.oauth()).providers; const provider = providers.find(p => p.id === 'nous'); if (!provider) throw new Error('Nous 登录暂不可用,请检查主机版本。'); login.value = provider }) }\nonMounted(async () => {\n try { const bootstrap = await apiRequest<{ user?: { role?: string } }>('/api/app/bootstrap'); canManage.value = bootstrap.user?.role === 'admin' }\n catch (e) { error.value = e instanceof Error ? e.message : '无法读取管理权限' }\n if (closed) return\n if (canManage.value) await load(); else loading.value = false\n})\nonBeforeUnmount(() => { closed = true; generation++; clearTimeout(timer) })\n</script>\n<template>\n <section class=\"bot-plugins\" aria-label=\"Bot 插件管理\">\n <p class=\"intro\"><AppIcon name=\"link\" :size=\"14\" />连接的应用由这台服务器的所有 Bot 共用。</p>\n <div class=\"plugin-tabs\" role=\"tablist\" aria-label=\"插件类型\"><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'local'\" @click=\"surface = 'local'\"><AppIcon name=\"monitor\" :size=\"16\" />本地应用</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'native'\" @click=\"surface = 'native'\"><AppIcon name=\"artifacts\" :size=\"16\" />本地插件</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'composio'\" @click=\"surface = 'composio'\"><AppIcon name=\"link\" :size=\"16\" />Composio</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'apps'\" @click=\"surface = 'apps'\"><AppIcon name=\"link\" :size=\"17\" />Nous 应用</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'mcp'\" @click=\"surface = 'mcp'\"><AppIcon name=\"tools\" :size=\"17\" />MCP 服务</button></div>\n <NativePluginsPanel v-if=\"surface === 'native' && canManage\" /><p v-else-if=\"surface === 'native'\">本地插件由管理员管理。</p>\n <ComposioAppsPanel v-else-if=\"surface === 'composio' && canManage\" /><p v-else-if=\"surface === 'composio'\">Composio 连接由管理员管理。</p>\n <LocalAppsPanel v-else-if=\"surface === 'local' && canManage\" /><p v-else-if=\"surface === 'local'\">本地连接由管理员管理。</p>\n <HermesMcpPanel v-else-if=\"surface === 'mcp'\" :can-manage=\"canManage\" @dirty-change=\"emit('dirty-change', $event)\" />\n <template v-else>\n <p v-if=\"!loading && !canManage\" class=\"plugin-notice\">应用连接由管理员管理。</p>\n <template v-else>\n <p v-if=\"error\" class=\"plugin-error\" role=\"alert\">{{ error }} <button type=\"button\" :disabled=\"busy\" @click=\"load\">重试</button></p>\n <p v-if=\"notice\" class=\"plugin-notice\" role=\"status\">{{ notice }}</p><p v-if=\"loading\" role=\"status\">正在读取应用…</p>\n <div v-else class=\"connection-settings\">\n <template v-if=\"status?.authenticated\"><p class=\"hint\">{{ status.accountLabel || 'Nous 账号已登录' }}</p><p v-if=\"!status.available\" class=\"hint\">{{ status.reason === 'disabled' || status.reason === 'not_enabled' ? '应用连接服务尚未启用,请检查主机设置后刷新。' : '应用连接服务暂不可用,请刷新或检查主机设置。' }}</p><button :disabled=\"busy\" @click=\"loginNous\">重新登录 Nous</button></template>\n <template v-else-if=\"status?.reason === 'needs_auth' || (status?.available && !status.authenticated)\"><p>登录 Nous 后即可连接应用。登录只用于应用授权,当前主模型保持不变。</p><button :disabled=\"busy\" @click=\"loginNous\">登录 Nous</button></template>\n <p v-else-if=\"!status?.available\" class=\"hint\">{{ status?.reason === 'disabled' || status?.reason === 'not_enabled' ? '应用连接服务尚未启用,请检查主机设置后刷新。' : '应用连接服务暂不可用,请刷新或检查主机设置。' }}</p>\n </div>\n <section v-if=\"operation\" class=\"connection-settings\" aria-label=\"应用授权进度\"><p role=\"status\">{{ operationLabel }}</p><p v-if=\"operation.error\" class=\"plugin-error\">{{ operation.error }}</p><div v-for=\"target in operation.targets\" :key=\"target.connector\"><p v-if=\"target.detail\" class=\"hint\">{{ target.detail }}</p><a v-if=\"operation.status === 'pending' && external(target.connectUrl)\" :href=\"external(target.connectUrl)\" target=\"_blank\" rel=\"noopener noreferrer\">继续授权 <AppIcon name=\"external\" :size=\"13\" /></a></div><button v-if=\"operation.status === 'pending'\" :disabled=\"busy\" @click=\"cancel\">取消授权</button></section>\n <div class=\"plugin-toolbar\"><div class=\"view-tabs\" role=\"tablist\" aria-label=\"应用列表\"><button role=\"tab\" :aria-selected=\"view === 'marketplace'\" @click=\"view = 'marketplace'\">应用目录</button><button role=\"tab\" :aria-selected=\"view === 'connected'\" @click=\"view = 'connected'\">已连接 {{ accounts.length || '' }}</button></div><button :disabled=\"busy || loading\" aria-label=\"刷新应用连接\" @click=\"load\"><AppIcon name=\"refresh\" :size=\"17\" /></button></div>\n <label class=\"plugin-search\"><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"search\" type=\"search\" aria-label=\"搜索应用\" placeholder=\"搜索应用\" /></label>\n <div class=\"app-grid\"><article v-for=\"card in filtered.slice(0, visibleCount)\" :key=\"card.slug\" class=\"app-card\">\n <div class=\"card-heading\"><ProviderLogo :name=\"card.name\" :logo=\"card.logoUrl\" :bundled-logo=\"bundledProviderLogo(card.slug)\" /><h4>{{ card.name }}</h4></div><p class=\"hint\">{{ card.description }}</p>\n <div v-for=\"account in linked(card.slug)\" :key=\"account.id\" class=\"account-block\">\n <div class=\"app-account\"><span>{{ account.alias || account.label }}<small>{{ stateLabel(account.status) }}</small></span><button class=\"danger\" :disabled=\"busy\" :aria-label=\"`断开 ${card.name} ${account.alias || account.label}`\" @click=\"disconnect(account)\">断开</button></div>\n <p v-if=\"account.statusReason\" class=\"hint\">{{ account.statusReason }}</p>\n <form v-if=\"editing === account.id\" @submit.prevent=\"saveAlias(account)\"><label>账号别名<input v-model=\"alias\" maxlength=\"32\" pattern=\"[a-z0-9][a-z0-9-]{0,31}\" required placeholder=\"work-mail\" /><small class=\"hint\">1–32 位小写字母、数字或连字符,以字母或数字开头。</small></label><div class=\"card-actions\"><button type=\"button\" @click=\"editing = ''\">取消</button><button :disabled=\"busy\" type=\"submit\">保存别名</button></div></form>\n <div v-else class=\"card-actions\"><button :disabled=\"busy\" @click=\"editing = account.id; alias = account.alias || ''\">修改别名</button><button v-if=\"!account.active\" :disabled=\"busy || operation?.status === 'pending' || !status?.authenticated\" @click=\"connect(card, account)\">重新连接</button></div>\n </div>\n <form v-if=\"adding === card.slug\" @submit.prevent=\"connect(card)\"><label>新账号别名<input v-model=\"newAlias\" maxlength=\"32\" pattern=\"[a-z0-9][a-z0-9-]{0,31}\" required placeholder=\"personal-mail\" /><small class=\"hint\">使用不同的别名添加第二个账号:1–32 位小写字母、数字或连字符。</small></label><button type=\"button\" @click=\"adding = ''; newAlias = ''\">取消添加</button></form>\n <div class=\"card-actions\"><button :disabled=\"busy || !status?.authenticated || !status?.available || operation?.status === 'pending'\" @click=\"connect(card)\">{{ adding === card.slug ? '继续连接' : linked(card.slug).length ? '添加账号' : '连接' }}</button></div>\n </article></div>\n <button v-if=\"filtered.length > visibleCount\" type=\"button\" @click=\"visibleCount += 40\">显示更多应用({{ filtered.length - visibleCount }})</button><p v-if=\"!loading && !filtered.length\" class=\"plugin-empty\">{{ search ? '没有匹配的应用。' : view === 'connected' ? '还没有已连接的应用,可前往应用目录添加。' : '应用目录暂为空。' }}</p>\n </template>\n </template>\n <ProviderLoginDialog v-if=\"login\" :provider=\"login\" @close=\"login = undefined\" @success=\"login = undefined; load()\" />\n </section>\n</template>\n<style scoped>\n.bot-plugins{display:grid;gap:16px;color:var(--text-primary);font-size:13px}.intro,.hint{color:var(--text-secondary);line-height:1.6;margin:0}.hint{font-size:12px}.plugin-tabs,.view-tabs,.plugin-toolbar,.card-heading,.card-actions,.editor-actions{display:flex;align-items:center;gap:8px}.plugin-tabs{flex-wrap:wrap;border-bottom:1px solid var(--line);padding-bottom:8px}.plugin-tabs button[aria-selected=true],.view-tabs button[aria-selected=true]{background:var(--surface-soft);font-weight:650}.plugin-toolbar{justify-content:space-between}.plugin-toolbar p{margin:0;font-weight:650}.card-heading h4{font-size:14px;margin:0;flex:1;min-width:0;overflow-wrap:anywhere}.card-actions{flex-wrap:wrap}.bot-plugins button{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:44px;padding:6px 10px;border:1px solid var(--line);border-radius:8px;background:var(--surface-raised);color:inherit;font:inherit;cursor:pointer}.bot-plugins button:hover{background:var(--surface-hover)}.bot-plugins button:disabled{opacity:.45;cursor:not-allowed}.bot-plugins :is(button,input,select,textarea,a,summary):focus-visible{outline:2px solid var(--accent);outline-offset:2px}.bot-plugins a{color:var(--accent);display:inline-flex;align-items:center;gap:4px}.bot-plugins .danger{color:var(--danger)}.bot-plugins .primary{background:var(--accent);color:var(--text-on-solid);border-color:var(--accent)}.bot-plugins label{display:grid;gap:6px;font-size:12px;line-height:1.6}.bot-plugins input:not([type=checkbox]),.bot-plugins select,.bot-plugins textarea{width:100%;box-sizing:border-box;min-width:0;padding:9px 10px;border:1px solid var(--line);border-radius:8px;color:var(--text-primary);background:var(--surface);font:inherit}.bot-plugins textarea{resize:vertical;font-family:var(--font-mono,monospace)}.plugin-search{position:relative}.plugin-search>svg{position:absolute;left:10px;top:11px;color:var(--text-muted)}.plugin-search input{padding-left:34px!important}.plugin-editor,.mcp-card,.app-card,.connection-settings{border:1px solid var(--line);border-radius:12px;padding:14px;display:grid;gap:12px;background:var(--surface)}.plugin-editor h4{margin:0}.plugin-editor{background:var(--surface-soft)}.editor-actions{justify-content:flex-end}.bot-plugins fieldset{border:1px solid var(--line);border-radius:8px;display:grid;gap:4px;max-height:180px;overflow:auto}.bot-plugins .check-row{display:flex;align-items:center;gap:8px;min-height:32px}.check-row input{accent-color:var(--accent)}.app-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.app-card{align-content:start}.app-account{display:flex;align-items:center;justify-content:space-between;gap:8px;border-top:1px solid var(--line);padding-top:8px}.app-account span{min-width:0;overflow-wrap:anywhere}.app-account small{display:block;color:var(--text-muted);margin-top:3px}.badge{font-size:11px;color:var(--text-muted);white-space:nowrap}.badge.enabled{color:var(--accent)}.endpoint{overflow-wrap:anywhere}.plugin-error,.plugin-notice{padding:12px;border-radius:8px;background:var(--surface-soft);margin:0;line-height:1.6}.plugin-error{color:var(--danger)}.plugin-empty{text-align:center;color:var(--text-secondary);padding:28px 12px;line-height:1.8}.connection-settings{display:block}.connection-settings summary{cursor:pointer;font-weight:600;line-height:1.8}.connection-settings summary span{float:right;font-weight:400;color:var(--text-muted)}.connection-settings p{margin:12px 0}.connection-settings form{display:grid;gap:12px}.grant-editor{display:grid;gap:8px}@media(max-width:767px){.app-grid{grid-template-columns:1fr}.bot-plugins button{min-height:44px}.bot-plugins .check-row{min-height:44px}}\n</style>\n\n<style scoped>\n.bot-plugins{gap:22px}.intro{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--text-muted)}.plugin-tabs{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:4px;padding:4px;border:1px solid var(--line);border-radius:12px;background:var(--surface-soft)}.plugin-tabs button{min-height:38px;padding:7px 6px;border:0;border-radius:8px;background:transparent;color:var(--text-secondary);font-size:12px;white-space:nowrap}.plugin-tabs button[aria-selected=true]{background:var(--surface);color:var(--text-primary);font-weight:600;box-shadow:0 1px 4px #0000000b}.connection-settings{padding:18px;border-radius:13px}.app-card{padding:18px;border-radius:13px}.app-grid{gap:12px}.app-card>.hint{line-height:1.7}.plugin-search input{border-radius:10px}.plugin-toolbar .view-tabs button{min-height:36px;font-size:12px}.plugin-toolbar>button{border-color:transparent}@media(max-width:600px){.plugin-tabs{gap:2px;padding:3px;display:flex;flex-wrap:wrap}.plugin-tabs button{flex:1 1 85px}.plugin-tabs button{gap:5px;min-height:44px;font-size:11px}.plugin-tabs button>svg{width:14px;height:14px}.bot-plugins{gap:18px}}\n</style>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport StandaloneDialog from '@/components/common/StandaloneDialog.vue'\nimport BotPluginsPanel from './BotPluginsPanel.vue'\nconst emit = defineEmits<{ close: [] }>()\nconst dirty = ref(false)\nconst beforeClose = () => !dirty.value || window.confirm('放弃尚未保存的插件更改?')\n</script>\n<template><StandaloneDialog title=\"已连接应用\" :before-close=\"beforeClose\" @close=\"emit('close')\"><BotPluginsPanel @dirty-change=\"dirty = $event\" /></StandaloneDialog></template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport StandaloneDialog from '@/components/common/StandaloneDialog.vue'\nimport BotPluginsPanel from './BotPluginsPanel.vue'\nconst emit = defineEmits<{ close: [] }>()\nconst dirty = ref(false)\nconst beforeClose = () => !dirty.value || window.confirm('放弃尚未保存的插件更改?')\n</script>\n<template><StandaloneDialog title=\"已连接应用\" :before-close=\"beforeClose\" @close=\"emit('close')\"><BotPluginsPanel @dirty-change=\"dirty = $event\" /></StandaloneDialog></template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"BotPluginsDialog-HIhXqyGn.js","names":[],"sources":["../../src/client/components/workspace/NativePluginsPanel.vue","../../src/client/components/workspace/NativePluginsPanel.vue","../../../../../provider-logos/gmail.svg","../../src/client/components/workspace/GmailPanel.vue","../../src/client/components/workspace/GmailPanel.vue","../../src/client/components/workspace/LocalAppsPanel.vue","../../src/client/components/workspace/LocalAppsPanel.vue","../../src/client/components/workspace/ComposioAppsPanel.vue","../../src/client/components/workspace/ComposioAppsPanel.vue","../../src/client/components/workspace/BotPluginsPanel.vue","../../src/client/components/workspace/BotPluginsPanel.vue","../../src/client/components/workspace/BotPluginsDialog.vue","../../src/client/components/workspace/BotPluginsDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed,onMounted,ref } from 'vue'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport { apiRequest } from '@/api/client'\nconst base='/api/app/admin/hermes/local/plugins'\ntype Plugin={name:string;title:string;description:string;sha:string;version:string;category:string;compatibility:string;compatibilityNote:string;repo:string;capabilities:Record<string,unknown>;installed?:{enabled:boolean;sha:string;version:string;dependencies?:string[];lastError?:string}}\nconst items=ref<Plugin[]>([]),search=ref(''),onlyInstalled=ref(false),busy=ref(false),error=ref(''),notice=ref(''),pending=ref<Plugin>(),review=ref<any>()\nconst filtered=computed(()=>items.value.filter(i=>(!onlyInstalled.value||i.installed)&&`${i.name} ${i.title} ${i.description}`.toLowerCase().includes(search.value.toLowerCase())))\nasync function load(){items.value=(await apiRequest<{plugins:Plugin[]}>(base+'/catalog')).plugins}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'插件操作失败'}finally{busy.value=false}}\nasync function install(i:Plugin,acceptChanges=false){await action(async()=>{const r=await apiRequest<any>(base+'/install',{method:'POST',timeoutMs:610_000,body:{name:i.name,sha:i.sha,acceptChanges}});if(r.consent_required){pending.value=i;review.value=r;notice.value='更新版本可能改变能力和依赖,请审阅后确认。';return}if(!r.ok)throw new Error(r.error??'插件未安装');pending.value=undefined;notice.value='安装完成。插件保持停用,启用时检查宿主兼容性。';await load()})}\nasync function toggle(i:Plugin){await action(async()=>{const r=await apiRequest<any>(base+'/enabled',{method:'POST',body:{name:i.name,enabled:!i.installed?.enabled}});if(!r.ok)throw new Error(r.error??'插件状态未改变');await load()})}\nasync function remove(i:Plugin){if(!window.confirm('移除 '+i.name+'?原包会保留在主机备份目录。'))return;await action(async()=>{await apiRequest(base+'/remove',{method:'POST',body:{name:i.name}});await load()})}\nonMounted(()=>action(load))\n</script>\n<template>\n <section class=\"integration-panel plugins\" aria-label=\"本地原生插件\">\n <header class=\"integration-header\"><div><h3>发现更多能力</h3><p class=\"catalog-intro\">{{items.length}} 项本地插件,按固定版本安装。</p></div><button class=\"quiet\" :disabled=\"busy\" aria-label=\"刷新本地目录\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button></header>\n <details class=\"plugin-trust\"><summary><AppIcon name=\"shield\" :size=\"17\" />仅安装你信任的插件</summary><p>插件运行在主机上,可访问文件和网络。Bot 权限控制工具入口,不能隔离插件的初始化或内部行为。桌面、Hook、供应商等能力需要各自宿主适配,安装成功不代表已可用。</p></details>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\"><AppIcon name=\"alert\" :size=\"17\" />{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\"><AppIcon name=\"check\" :size=\"17\" />{{notice}}</p>\n <div class=\"integration-toolbar\"><label class=\"integration-search\"><span class=\"sr-only\">搜索插件</span><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"search\" type=\"search\" placeholder=\"搜索插件名称或用途\"></label><label class=\"integration-check\"><input v-model=\"onlyInstalled\" type=\"checkbox\">仅显示已安装</label></div>\n <article v-if=\"pending\" class=\"integration-card update-review\"><span class=\"integration-tag warning\">需要审阅</span><h4>审阅 {{pending.name}} 更新</h4><p>{{pending.description}}</p><p>版本 {{pending.version}} · {{pending.sha}}</p><div class=\"capability-compare\"><div><h4>旧能力</h4><pre>{{JSON.stringify(review?.oldCapabilities,null,2)}}</pre></div><div><h4>新能力</h4><pre>{{JSON.stringify(review?.newCapabilities,null,2)}}</pre></div></div><h4>当前依赖</h4><pre>{{review?.oldDependencies}}</pre><p>新版本按固定 SHA 扫描并校验实际能力;依赖安装受 Host 版本约束,安装后保持停用。</p><div class=\"integration-actions\"><button class=\"primary\" :disabled=\"busy\" @click=\"install(pending!,true)\">确认更新到此版本</button><button @click=\"pending=undefined\">取消更新</button></div></article>\n <div class=\"catalog-results\"><span class=\"integration-count\">{{search||onlyInstalled?'匹配 '+filtered.length+' 项':'全部 '+items.length+' 项'}}</span><span class=\"integration-count\">安装后默认停用</span></div>\n <ul class=\"plugins-grid\"><li v-for=\"i in filtered\" :key=\"i.name\" class=\"integration-card plugin-card\">\n <div class=\"plugin-heading\"><span class=\"integration-mark\"><AppIcon :name=\"i.compatibility==='tools'?'tools':i.category==='desktop'?'monitor':'artifacts'\" :size=\"20\" /></span><div><h4>{{i.title||i.name}}</h4><span class=\"plugin-version\">v{{i.version}}</span></div><span v-if=\"i.installed\" class=\"integration-tag\" :class=\"{success:i.installed.enabled}\">{{i.installed.enabled?'已启用':'已安装'}}</span></div>\n <p class=\"plugin-description\">{{i.description}}</p>\n <p class=\"compatibility\"><span class=\"integration-tag\" :class=\"{success:i.compatibility==='tools',warning:i.compatibility!=='tools'}\">{{i.compatibility==='tools'?'工具可接入':'需适配'}}</span>{{i.compatibilityNote}}</p>\n <details class=\"plugin-details\"><summary>来源、能力与依赖</summary><p>{{i.description}}</p><a :href=\"i.repo\" target=\"_blank\" rel=\"noopener noreferrer\">查看源码 ↗</a><p class=\"source-sha\">固定版本:{{i.sha}}</p><pre>{{JSON.stringify(i.capabilities,null,2)}}</pre><p v-if=\"i.installed?.dependencies?.length\">已安装依赖:{{i.installed.dependencies.join('、')}}</p></details>\n <p v-if=\"i.installed?.lastError\" class=\"plugin-failure\">{{i.installed.lastError}}</p>\n <div class=\"integration-actions plugin-footer\"><button v-if=\"!i.installed||i.installed.sha!==i.sha\" :disabled=\"busy\" @click=\"install(i)\"><AppIcon :name=\"i.installed?'refresh':'plus'\" :size=\"15\" />{{i.installed?'检查并更新':'安装固定版本'}}</button><button v-if=\"i.installed\" :class=\"{primary:!i.installed.enabled}\" :disabled=\"busy||!i.installed.enabled&&i.compatibility!=='tools'\" @click=\"toggle(i)\">{{i.installed.enabled?'停用':'检查并启用工具'}}</button><button v-if=\"i.installed\" class=\"quiet danger\" :disabled=\"busy\" @click=\"remove(i)\">移除</button></div>\n </li></ul>\n <div v-if=\"!busy&&!filtered.length\" class=\"integration-empty\"><AppIcon name=\"search\" :size=\"28\" /><strong>没有匹配的插件</strong><p>试试其他关键词,或取消已安装筛选。</p></div>\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.catalog-intro{margin-top:5px!important;font-size:12px}.plugin-trust{padding:0 12px;border-radius:10px;background:var(--surface-soft)}.plugin-trust p{padding:0 0 13px;font-size:12px}.catalog-results{display:flex;justify-content:space-between;gap:12px}.plugins-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:0;padding:0;list-style:none}.plugin-card{display:flex;flex-direction:column;gap:13px;padding:18px}.plugin-heading{display:flex;align-items:center;gap:10px}.plugin-heading>div{flex:1;min-width:0}.plugin-heading h4{overflow-wrap:anywhere;line-height:1.4}.plugin-heading .integration-mark{width:36px;height:36px;border-radius:10px}.plugin-version{font-size:11px;color:var(--text-muted)}.plugin-description{font-size:12px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden;line-height:1.7}.compatibility{font-size:11px;line-height:1.7}.compatibility .integration-tag{margin-right:6px}.plugin-details{margin-top:auto;border-top:1px solid var(--line);padding-top:3px;min-width:0}.plugin-details summary{font-size:11px}.plugin-details p,.plugin-details a{font-size:12px}.plugin-details p{margin-bottom:10px}.source-sha{margin-top:10px!important;font-family:var(--font-code);overflow-wrap:anywhere}.plugin-footer{padding-top:2px}.plugin-footer button{font-size:12px}.plugin-failure{color:var(--danger)!important;overflow-wrap:anywhere}.update-review{display:grid;gap:12px;border-color:color-mix(in srgb,var(--warning) 35%,var(--line))}.capability-compare{display:grid;grid-template-columns:1fr 1fr;gap:12px;min-width:0}.capability-compare>div{min-width:0}@media(max-width:700px){.plugins-grid{grid-template-columns:1fr}}@media(max-width:420px){.capability-compare{grid-template-columns:1fr}.plugin-heading{flex-wrap:wrap}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed,onMounted,ref } from 'vue'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport { apiRequest } from '@/api/client'\nconst base='/api/app/admin/hermes/local/plugins'\ntype Plugin={name:string;title:string;description:string;sha:string;version:string;category:string;compatibility:string;compatibilityNote:string;repo:string;capabilities:Record<string,unknown>;installed?:{enabled:boolean;sha:string;version:string;dependencies?:string[];lastError?:string}}\nconst items=ref<Plugin[]>([]),search=ref(''),onlyInstalled=ref(false),busy=ref(false),error=ref(''),notice=ref(''),pending=ref<Plugin>(),review=ref<any>()\nconst filtered=computed(()=>items.value.filter(i=>(!onlyInstalled.value||i.installed)&&`${i.name} ${i.title} ${i.description}`.toLowerCase().includes(search.value.toLowerCase())))\nasync function load(){items.value=(await apiRequest<{plugins:Plugin[]}>(base+'/catalog')).plugins}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'插件操作失败'}finally{busy.value=false}}\nasync function install(i:Plugin,acceptChanges=false){await action(async()=>{const r=await apiRequest<any>(base+'/install',{method:'POST',timeoutMs:610_000,body:{name:i.name,sha:i.sha,acceptChanges}});if(r.consent_required){pending.value=i;review.value=r;notice.value='更新版本可能改变能力和依赖,请审阅后确认。';return}if(!r.ok)throw new Error(r.error??'插件未安装');pending.value=undefined;notice.value='安装完成。插件保持停用,启用时检查宿主兼容性。';await load()})}\nasync function toggle(i:Plugin){await action(async()=>{const r=await apiRequest<any>(base+'/enabled',{method:'POST',body:{name:i.name,enabled:!i.installed?.enabled}});if(!r.ok)throw new Error(r.error??'插件状态未改变');await load()})}\nasync function remove(i:Plugin){if(!window.confirm('移除 '+i.name+'?原包会保留在主机备份目录。'))return;await action(async()=>{await apiRequest(base+'/remove',{method:'POST',body:{name:i.name}});await load()})}\nonMounted(()=>action(load))\n</script>\n<template>\n <section class=\"integration-panel plugins\" aria-label=\"本地原生插件\">\n <header class=\"integration-header\"><div><h3>发现更多能力</h3><p class=\"catalog-intro\">{{items.length}} 项本地插件,按固定版本安装。</p></div><button class=\"quiet\" :disabled=\"busy\" aria-label=\"刷新本地目录\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button></header>\n <details class=\"plugin-trust\"><summary><AppIcon name=\"shield\" :size=\"17\" />仅安装你信任的插件</summary><p>插件运行在主机上,可访问文件和网络。Bot 权限控制工具入口,不能隔离插件的初始化或内部行为。桌面、Hook、供应商等能力需要各自宿主适配,安装成功不代表已可用。</p></details>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\"><AppIcon name=\"alert\" :size=\"17\" />{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\"><AppIcon name=\"check\" :size=\"17\" />{{notice}}</p>\n <div class=\"integration-toolbar\"><label class=\"integration-search\"><span class=\"sr-only\">搜索插件</span><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"search\" type=\"search\" placeholder=\"搜索插件名称或用途\"></label><label class=\"integration-check\"><input v-model=\"onlyInstalled\" type=\"checkbox\">仅显示已安装</label></div>\n <article v-if=\"pending\" class=\"integration-card update-review\"><span class=\"integration-tag warning\">需要审阅</span><h4>审阅 {{pending.name}} 更新</h4><p>{{pending.description}}</p><p>版本 {{pending.version}} · {{pending.sha}}</p><div class=\"capability-compare\"><div><h4>旧能力</h4><pre>{{JSON.stringify(review?.oldCapabilities,null,2)}}</pre></div><div><h4>新能力</h4><pre>{{JSON.stringify(review?.newCapabilities,null,2)}}</pre></div></div><h4>当前依赖</h4><pre>{{review?.oldDependencies}}</pre><p>新版本按固定 SHA 扫描并校验实际能力;依赖安装受 Host 版本约束,安装后保持停用。</p><div class=\"integration-actions\"><button class=\"primary\" :disabled=\"busy\" @click=\"install(pending!,true)\">确认更新到此版本</button><button @click=\"pending=undefined\">取消更新</button></div></article>\n <div class=\"catalog-results\"><span class=\"integration-count\">{{search||onlyInstalled?'匹配 '+filtered.length+' 项':'全部 '+items.length+' 项'}}</span><span class=\"integration-count\">安装后默认停用</span></div>\n <ul class=\"plugins-grid\"><li v-for=\"i in filtered\" :key=\"i.name\" class=\"integration-card plugin-card\">\n <div class=\"plugin-heading\"><span class=\"integration-mark\"><AppIcon :name=\"i.compatibility==='tools'?'tools':i.category==='desktop'?'monitor':'artifacts'\" :size=\"20\" /></span><div><h4>{{i.title||i.name}}</h4><span class=\"plugin-version\">v{{i.version}}</span></div><span v-if=\"i.installed\" class=\"integration-tag\" :class=\"{success:i.installed.enabled}\">{{i.installed.enabled?'已启用':'已安装'}}</span></div>\n <p class=\"plugin-description\">{{i.description}}</p>\n <p class=\"compatibility\"><span class=\"integration-tag\" :class=\"{success:i.compatibility==='tools',warning:i.compatibility!=='tools'}\">{{i.compatibility==='tools'?'工具可接入':'需适配'}}</span>{{i.compatibilityNote}}</p>\n <details class=\"plugin-details\"><summary>来源、能力与依赖</summary><p>{{i.description}}</p><a :href=\"i.repo\" target=\"_blank\" rel=\"noopener noreferrer\">查看源码 ↗</a><p class=\"source-sha\">固定版本:{{i.sha}}</p><pre>{{JSON.stringify(i.capabilities,null,2)}}</pre><p v-if=\"i.installed?.dependencies?.length\">已安装依赖:{{i.installed.dependencies.join('、')}}</p></details>\n <p v-if=\"i.installed?.lastError\" class=\"plugin-failure\">{{i.installed.lastError}}</p>\n <div class=\"integration-actions plugin-footer\"><button v-if=\"!i.installed||i.installed.sha!==i.sha\" :disabled=\"busy\" @click=\"install(i)\"><AppIcon :name=\"i.installed?'refresh':'plus'\" :size=\"15\" />{{i.installed?'检查并更新':'安装固定版本'}}</button><button v-if=\"i.installed\" :class=\"{primary:!i.installed.enabled}\" :disabled=\"busy||!i.installed.enabled&&i.compatibility!=='tools'\" @click=\"toggle(i)\">{{i.installed.enabled?'停用':'检查并启用工具'}}</button><button v-if=\"i.installed\" class=\"quiet danger\" :disabled=\"busy\" @click=\"remove(i)\">移除</button></div>\n </li></ul>\n <div v-if=\"!busy&&!filtered.length\" class=\"integration-empty\"><AppIcon name=\"search\" :size=\"28\" /><strong>没有匹配的插件</strong><p>试试其他关键词,或取消已安装筛选。</p></div>\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.catalog-intro{margin-top:5px!important;font-size:12px}.plugin-trust{padding:0 12px;border-radius:10px;background:var(--surface-soft)}.plugin-trust p{padding:0 0 13px;font-size:12px}.catalog-results{display:flex;justify-content:space-between;gap:12px}.plugins-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:0;padding:0;list-style:none}.plugin-card{display:flex;flex-direction:column;gap:13px;padding:18px}.plugin-heading{display:flex;align-items:center;gap:10px}.plugin-heading>div{flex:1;min-width:0}.plugin-heading h4{overflow-wrap:anywhere;line-height:1.4}.plugin-heading .integration-mark{width:36px;height:36px;border-radius:10px}.plugin-version{font-size:11px;color:var(--text-muted)}.plugin-description{font-size:12px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden;line-height:1.7}.compatibility{font-size:11px;line-height:1.7}.compatibility .integration-tag{margin-right:6px}.plugin-details{margin-top:auto;border-top:1px solid var(--line);padding-top:3px;min-width:0}.plugin-details summary{font-size:11px}.plugin-details p,.plugin-details a{font-size:12px}.plugin-details p{margin-bottom:10px}.source-sha{margin-top:10px!important;font-family:var(--font-code);overflow-wrap:anywhere}.plugin-footer{padding-top:2px}.plugin-footer button{font-size:12px}.plugin-failure{color:var(--danger)!important;overflow-wrap:anywhere}.update-review{display:grid;gap:12px;border-color:color-mix(in srgb,var(--warning) 35%,var(--line))}.capability-compare{display:grid;grid-template-columns:1fr 1fr;gap:12px;min-width:0}.capability-compare>div{min-width:0}@media(max-width:700px){.plugins-grid{grid-template-columns:1fr}}@media(max-width:420px){.capability-compare{grid-template-columns:1fr}.plugin-heading{flex-wrap:wrap}}\n</style>\n","export default \"__VITE_PUBLIC_ASSET__c5ebe966__\"","<script setup lang=\"ts\">\nimport { computed,onMounted,ref,watch } from 'vue'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport DOMPurify from 'dompurify'\nimport { apiRequest } from '@/api/client'\nimport type { GoogleAccount,GoogleOperation } from '@shared/googleWorkspace'\nconst props=withDefaults(defineProps<{accounts:GoogleAccount[];source?:'local'|'composio'}>(),{source:'local'})\nconst sourceName=computed(()=>props.source==='composio'?'Composio 托管':'本地连接')\nconst base=(source=props.source)=>source==='composio'?'/api/app/admin/composio':'/api/app/admin/hermes/local/google'\nconst account=ref(props.accounts[0]?.id??''),query=ref('in:inbox'),messages=ref<any[]>([]),message=ref<any>(),busy=ref(false),error=ref(''),notice=ref(''),compose=ref(false),reply=ref(false),to=ref(''),subject=ref(''),body=ref(''),labels=ref(''),labelOptions=ref<any[]>([]),unknown=ref(false)\nlet generation=0,reloadQueued=false\nlet draftTarget:{accountId:string;messageId?:string;operationId:string;source:'local'|'composio'}|undefined\nconst uncertain=ref<{accountId:string;operationId:string;source:'local'|'composio'}>()\nconst header=(m:any,name:string)=>m?.payload?.headers?.find((h:any)=>h.name.toLowerCase()===name.toLowerCase())?.value??''\nasync function call(operation:GoogleOperation,args:Record<string,unknown>,write=false,accountId=account.value,operationId?:string,source=props.source){\n const id=write?(operationId??crypto.randomUUID()):undefined\n try{\n const result=await apiRequest<any>(base(source)+'/call',{method:'POST',timeoutMs:95_000,body:{accountId,operation,arguments:args as any,...(write?{operationId:id}:{})}})\n if(result?.isError){if(write&&result.outcomeUnknown){unknown.value=true;uncertain.value={accountId,operationId:id!,source}}const e=new Error(result.error?.message??'Google 请求失败');Object.assign(e,{known:!result.outcomeUnknown});throw e}\n return result\n }catch(e){\n if(source==='composio'&&[400,401,403,404,409,413,422].includes((e as {status?:number})?.status??0))Object.assign(e as object,{known:true})\n if(write&&!(e as any)?.known){unknown.value=true;uncertain.value={accountId,operationId:id!,source}}\n throw e\n }\n}\nasync function action(fn:()=>Promise<void>){if(busy.value){if(fn===load)reloadQueued=true;return};busy.value=true;error.value='';notice.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'邮箱操作失败'}finally{busy.value=false;if(reloadQueued){reloadQueued=false;void action(load)}}}\nasync function load(){\n if(!account.value)return\n const version=++generation,selected=account.value,origin=props.source;message.value=undefined\n const result=await call('gmail.search',{query:query.value,max:15},false,selected,undefined,origin),rows=[]\n if(version!==generation||selected!==account.value||origin!==props.source)return\n for(const row of result.messages??[]){const detail=await call('gmail.get',{id:row.id},false,selected,undefined,origin);if(version!==generation||selected!==account.value)return;rows.push(detail)}\n const available=await call('gmail.labels',{},false,selected,undefined,origin)\n if(version===generation&&selected===account.value){messages.value=rows;labelOptions.value=available.labels??[]}\n}\nasync function open(m:any){await action(async()=>{const selected=account.value,version=generation,origin=props.source;const result=await call('gmail.get',{id:m.id},false,selected,undefined,origin);if(selected===account.value&&version===generation)message.value=result})}\nasync function checkReceipt(){await action(async()=>{if(!uncertain.value)return;const r=await apiRequest<any>(base(uncertain.value.source)+'/receipts/'+uncertain.value.accountId+'/'+encodeURIComponent(uncertain.value.operationId));notice.value=r.status==='complete'?'邮箱操作已完成,已读取保存的结果。':r.status==='rejected'?'邮箱服务已拒绝此操作。':'仍无确认结果,请在 Gmail 核对。';if(['complete','rejected'].includes(r.status)){unknown.value=false;compose.value=false;draftTarget=undefined;uncertain.value=undefined;await load()}})}\nfunction decode(data:string){const bytes=Uint8Array.from(atob(data.replace(/-/g,'+').replace(/_/g,'/')),c=>c.charCodeAt(0));return new TextDecoder().decode(bytes)}\nfunction mailBody(payload:any):string{if(!payload)return '';if(payload.mimeType==='text/plain'&&payload.body?.data)return '<pre>'+decode(payload.body.data).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\n/g,'<br>')+'</pre>';const parts=payload.parts??[];const plain=parts.find((p:any)=>p.mimeType==='text/plain');if(plain)return mailBody(plain);if(payload.mimeType==='text/html'&&payload.body?.data)return DOMPurify.sanitize(decode(payload.body.data),{FORBID_TAGS:['img','style','script','iframe','form','svg','math','video','audio','link'],FORBID_ATTR:['style','src','srcset','background']});return parts.map(mailBody).join('')}\nfunction attachments(payload:any):any[]{return [...(payload?.filename&&(payload?.body?.attachmentId||payload?.body?.data)?[payload]:[]),...(payload?.parts??[]).flatMap(attachments)]}\nasync function download(part:any){await action(async()=>{const result=part.body.data?part.body:await call('gmail.attachment',{id:message.value.id,attachmentId:part.body.attachmentId});if(result.size>25*1024*1024||String(result.data??'').length>25*1024*1024*4/3+4)throw new Error('附件超过25MB限制');const bytes=Uint8Array.from(atob(result.data.replace(/-/g,'+').replace(/_/g,'/')),c=>c.charCodeAt(0));const url=URL.createObjectURL(new Blob([bytes],{type:'application/octet-stream'}));const link=document.createElement('a');link.href=url;link.download=String(part.filename).replace(/[\\\\/\\x00-\\x1f]/g,'_').slice(0,180)||'attachment';document.body.append(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(url),30000)})}\nfunction draft(isReply=false){if(unknown.value)return;draftTarget={accountId:account.value,...(isReply?{messageId:message.value.id}:{}),operationId:crypto.randomUUID(),source:props.source};reply.value=isReply;compose.value=true;to.value=isReply?(header(message.value,'Reply-To')||header(message.value,'From')):'';subject.value=isReply?'Re: '+header(message.value,'Subject'):'';body.value=''}\nasync function send(){await action(async()=>{\n const target=draftTarget;if(!target||target.accountId!==account.value||target.source!==props.source||unknown.value)throw new Error('草稿账号已改变,请重新编辑。')\n try{const result=await call(target.messageId?'gmail.reply':'gmail.send',target.messageId?{id:target.messageId,body:body.value}:{to:to.value,subject:subject.value,body:body.value},true,target.accountId,target.operationId,target.source);if(target.accountId!==account.value||target.source!==props.source)return;compose.value=false;draftTarget=undefined;notice.value='邮件已发送,消息编号:'+(result.id??'已确认');if(result.id)message.value=await call('gmail.get',{id:result.id},false,target.accountId,undefined,target.source)}catch(e){if(!unknown.value&&draftTarget)draftTarget.operationId=crypto.randomUUID();throw e}\n})}\nasync function mutate(operation:GoogleOperation,args:Record<string,unknown>){if(unknown.value)return;await action(async()=>{const selected=account.value;await call(operation,args,true,selected);await load();notice.value='邮箱操作已完成。'})}\nwatch(()=>props.accounts.map(a=>a.id).join(','),()=>{if(!props.accounts.some(a=>a.id===account.value)){account.value=props.accounts[0]?.id??'';generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined}})\nwatch(()=>props.source,()=>{generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined;error.value='';notice.value='';void action(load)})\nwatch(account,()=>{generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined;void action(load)})\nonMounted(()=>action(load))\n</script>\n<template>\n <section class=\"integration-panel mailbox\" :aria-label=\"source==='composio'?'Composio Gmail 邮箱':'本地 Gmail 邮箱'\">\n <header class=\"integration-header\"><div class=\"integration-heading\"><span class=\"integration-mark gmail-mark\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"25\" height=\"25\"></span><div><h3>Gmail</h3><p>{{sourceName}} · {{messages.length}} 封邮件</p></div></div><button class=\"primary\" type=\"button\" :disabled=\"busy||unknown||!account\" @click=\"draft()\"><AppIcon name=\"edit\" :size=\"16\" />写邮件</button></header>\n <label class=\"mail-account\">账号<select v-model=\"account\" :disabled=\"busy\"><option v-for=\"a in accounts\" :value=\"a.id\" :key=\"a.id\">{{a.label}}</option></select></label>\n <form class=\"mail-search\" @submit.prevent=\"action(load)\"><label class=\"integration-search\"><span class=\"sr-only\">搜索邮件</span><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"query\" placeholder=\"in:inbox 或 Gmail 搜索语法\"></label><button :disabled=\"busy\"><AppIcon name=\"refresh\" :size=\"16\" />搜索 / 刷新</button></form>\n <p v-if=\"busy\" class=\"mail-loading\" role=\"status\"><AppIcon name=\"clock\" :size=\"15\" />正在处理邮箱…</p><p v-if=\"error\" class=\"integration-alert\" role=\"alert\">{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\">{{notice}}</p>\n <div v-if=\"unknown\" class=\"mail-unknown\"><div class=\"integration-heading\"><AppIcon name=\"alert\" :size=\"19\" /><strong>写入结果尚未确认</strong></div><p>请先在 Gmail 核对,再继续操作。</p><div class=\"integration-actions\"><button :disabled=\"busy\" @click=\"checkReceipt\">查询操作收据</button><button class=\"quiet\" :disabled=\"busy\" @click=\"unknown=false;compose=false;notice='已按你的确认允许新的操作;原操作不会自动重发。'\">我已在 Gmail 核对,允许新操作</button></div></div>\n <div class=\"mail-layout\">\n <aside class=\"mail-list\"><div class=\"mail-list-heading\"><span>邮件列表</span><span>{{messages.length}}</span></div><ul class=\"messages\"><li v-for=\"m in messages\" :key=\"m.id\"><button :class=\"{active:message?.id===m.id}\" :aria-pressed=\"message?.id===m.id\" :disabled=\"busy\" @click=\"open(m)\"><span class=\"message-from\">{{header(m,'From')}}</span><strong>{{header(m,'Subject')||'(无主题)'}}</strong><small>{{m.snippet}}</small></button></li></ul><div v-if=\"!busy&&!messages.length\" class=\"integration-empty\"><AppIcon name=\"archive\" :size=\"26\" /><p>没有匹配的邮件。</p></div></aside>\n <div class=\"mail-reading\">\n <form v-if=\"compose\" class=\"mail-compose\" @submit.prevent=\"send\"><div class=\"compose-heading\"><h4>{{reply?'回复邮件':'新邮件'}}</h4><span class=\"integration-tag\">{{accounts.find(a=>a.id===account)?.label}}</span></div><label>收件人<input v-model=\"to\" :disabled=\"reply\" required></label><label>主题<input v-model=\"subject\" :disabled=\"reply\" required></label><label>正文<textarea v-model=\"body\" rows=\"7\" placeholder=\"写下你的邮件…\" required /></label><div class=\"integration-actions\"><button class=\"primary\" :disabled=\"busy||unknown\"><AppIcon name=\"arrow-up\" :size=\"16\" />发送邮件</button><button class=\"quiet\" type=\"button\" :disabled=\"busy\" @click=\"compose=false\">取消编辑</button></div></form>\n <article v-if=\"message\" class=\"mail-detail\"><header class=\"mail-detail-heading\"><h4>{{header(message,'Subject')||'(无主题)'}}</h4><p>{{header(message,'From')}}</p><p class=\"message-meta\">发送至 {{header(message,'To')}}<br>{{header(message,'Date')}}</p></header><div class=\"email-body\" v-html=\"mailBody(message.payload)\" />\n <div v-if=\"attachments(message.payload).length\" class=\"mail-attachments\"><button v-for=\"part in attachments(message.payload)\" :key=\"part.partId??part.body.attachmentId??part.filename\" :disabled=\"busy\" @click=\"download(part)\"><AppIcon name=\"download\" :size=\"16\" />下载 {{part.filename}}({{part.body.size}} 字节)</button></div>\n <div class=\"integration-actions mail-actions\"><button :disabled=\"busy||unknown\" @click=\"draft(true)\"><AppIcon name=\"edit\" :size=\"15\" />回复</button><button class=\"quiet\" :disabled=\"busy||unknown\" @click=\"mutate('gmail.modify',{id:message.id,removeLabels:['INBOX']})\"><AppIcon name=\"archive\" :size=\"15\" />归档</button><button class=\"quiet danger\" :disabled=\"busy||unknown\" @click=\"mutate('gmail.trash',{id:message.id})\"><AppIcon name=\"trash\" :size=\"15\" />移入垃圾箱</button></div>\n <div class=\"mail-labels\"><label>标签<select v-model=\"labels\"><option value=\"\">选择标签</option><option v-for=\"label in labelOptions\" :key=\"label.id\" :value=\"label.id\">{{label.name}}</option></select></label><div class=\"integration-actions\"><button :disabled=\"busy||unknown||!labels.trim()\" @click=\"mutate('gmail.modify',{id:message.id,addLabels:[labels]})\">添加标签</button><button class=\"quiet\" :disabled=\"busy||unknown||!labels\" @click=\"mutate('gmail.modify',{id:message.id,removeLabels:[labels]})\">移除标签</button></div></div>\n </article>\n <div v-else-if=\"!compose\" class=\"integration-empty reading-empty\"><span class=\"reading-illustration\"><AppIcon name=\"file\" :size=\"32\" /></span><strong>选择一封邮件开始阅读</strong><p>也可以写封新邮件,继续你的对话。</p></div>\n </div>\n </div>\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.mailbox{border-top:1px solid var(--line);padding-top:24px;gap:16px}.gmail-mark{background:var(--surface)}.mail-account{max-width:340px}.mail-search{display:flex;gap:8px;align-items:center}.mail-search>button{flex-shrink:0}.mail-layout{display:grid;grid-template-columns:minmax(190px,.72fr) minmax(0,1.6fr);border:1px solid var(--line);border-radius:13px;overflow:hidden;min-height:360px}.mail-list{min-width:0;background:var(--surface-soft);border-right:1px solid var(--line)}.mail-list-heading{display:flex;justify-content:space-between;padding:12px 14px;font-size:11px;color:var(--text-muted);border-bottom:1px solid var(--line)}.messages{list-style:none;margin:0;padding:6px;display:grid;gap:3px;max-height:560px;overflow:auto}.messages button{display:grid!important;gap:5px;width:100%!important;min-width:0;text-align:left;justify-content:stretch!important;justify-items:start;border:1px solid transparent!important;border-radius:8px!important;background:transparent!important;padding:12px!important;line-height:1.5}.messages button:hover:not(:disabled){background:var(--surface-hover)!important}.messages button.active{background:var(--surface)!important;border-color:var(--line)!important;box-shadow:0 1px 3px #00000006}.messages :is(strong,span,small){width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.messages strong{font-size:12px;font-weight:600}.message-from{font-size:11px;color:var(--text-secondary)}.messages small{font-size:11px;color:var(--text-muted)}.mail-reading{min-width:0;background:var(--surface)}.mail-detail{padding:22px;min-width:0}.mail-detail-heading{display:grid;gap:8px;padding-bottom:20px;border-bottom:1px solid var(--line);overflow-wrap:anywhere}.mail-detail h4{font-size:18px;line-height:1.5}.mail-detail-heading p{font-size:12px}.message-meta{font-size:11px!important;color:var(--text-muted)!important;line-height:1.8}.email-body{padding:22px 0;overflow-wrap:anywhere;overflow:auto;max-width:100%;line-height:1.8;min-height:100px}.email-body :deep(pre){white-space:pre-wrap;background:none;padding:0;font-family:inherit;font-size:13px}.email-body :deep(p){margin:0 0 12px;color:inherit}.mail-actions{padding:16px 0;border-top:1px solid var(--line)}.mail-labels{display:grid;gap:10px;padding-top:14px;border-top:1px solid var(--line)}.mail-labels label{max-width:240px}.mail-labels button{font-size:11px}.mail-attachments{display:grid;justify-items:start;gap:8px;padding-bottom:16px}.mail-attachments button{text-align:left;overflow-wrap:anywhere;font-size:12px}.mail-compose{display:grid;gap:14px;padding:20px;border-bottom:1px solid var(--line);background:var(--surface)}.compose-heading{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}.mail-compose textarea{line-height:1.7}.mail-unknown{display:grid;gap:10px;padding:16px;border-radius:11px;border:1px solid color-mix(in srgb,var(--warning) 25%,var(--line));background:color-mix(in srgb,var(--warning) 5%,var(--surface));color:var(--warning)}.mail-unknown p{font-size:12px}.mail-unknown button{font-size:12px}.mail-loading{display:flex;align-items:center;gap:7px;color:var(--text-muted)!important;font-size:12px}.reading-empty{min-height:320px;align-content:center}.reading-illustration{display:grid;place-items:center;width:64px;height:64px;margin-bottom:8px;border-radius:18px;background:var(--surface-soft);color:var(--text-muted)}.reading-empty p{font-size:12px}@media(max-width:700px){.mail-layout{grid-template-columns:1fr}.mail-list{border-right:0;border-bottom:1px solid var(--line)}.messages{max-height:220px}.mail-detail,.mail-compose{padding:18px}.reading-empty{min-height:200px}.mail-search{align-items:stretch}.mail-search>button{padding:8px;font-size:12px}.mail-search>button svg{display:none}}@media(max-width:420px){.mail-account{max-width:100%}.mailbox>.integration-header{align-items:center}.mail-detail h4{font-size:16px}.mail-unknown .integration-actions{display:grid;justify-items:start}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed,onMounted,ref,watch } from 'vue'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport DOMPurify from 'dompurify'\nimport { apiRequest } from '@/api/client'\nimport type { GoogleAccount,GoogleOperation } from '@shared/googleWorkspace'\nconst props=withDefaults(defineProps<{accounts:GoogleAccount[];source?:'local'|'composio'}>(),{source:'local'})\nconst sourceName=computed(()=>props.source==='composio'?'Composio 托管':'本地连接')\nconst base=(source=props.source)=>source==='composio'?'/api/app/admin/composio':'/api/app/admin/hermes/local/google'\nconst account=ref(props.accounts[0]?.id??''),query=ref('in:inbox'),messages=ref<any[]>([]),message=ref<any>(),busy=ref(false),error=ref(''),notice=ref(''),compose=ref(false),reply=ref(false),to=ref(''),subject=ref(''),body=ref(''),labels=ref(''),labelOptions=ref<any[]>([]),unknown=ref(false)\nlet generation=0,reloadQueued=false\nlet draftTarget:{accountId:string;messageId?:string;operationId:string;source:'local'|'composio'}|undefined\nconst uncertain=ref<{accountId:string;operationId:string;source:'local'|'composio'}>()\nconst header=(m:any,name:string)=>m?.payload?.headers?.find((h:any)=>h.name.toLowerCase()===name.toLowerCase())?.value??''\nasync function call(operation:GoogleOperation,args:Record<string,unknown>,write=false,accountId=account.value,operationId?:string,source=props.source){\n const id=write?(operationId??crypto.randomUUID()):undefined\n try{\n const result=await apiRequest<any>(base(source)+'/call',{method:'POST',timeoutMs:95_000,body:{accountId,operation,arguments:args as any,...(write?{operationId:id}:{})}})\n if(result?.isError){if(write&&result.outcomeUnknown){unknown.value=true;uncertain.value={accountId,operationId:id!,source}}const e=new Error(result.error?.message??'Google 请求失败');Object.assign(e,{known:!result.outcomeUnknown});throw e}\n return result\n }catch(e){\n if(source==='composio'&&[400,401,403,404,409,413,422].includes((e as {status?:number})?.status??0))Object.assign(e as object,{known:true})\n if(write&&!(e as any)?.known){unknown.value=true;uncertain.value={accountId,operationId:id!,source}}\n throw e\n }\n}\nasync function action(fn:()=>Promise<void>){if(busy.value){if(fn===load)reloadQueued=true;return};busy.value=true;error.value='';notice.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'邮箱操作失败'}finally{busy.value=false;if(reloadQueued){reloadQueued=false;void action(load)}}}\nasync function load(){\n if(!account.value)return\n const version=++generation,selected=account.value,origin=props.source;message.value=undefined\n const result=await call('gmail.search',{query:query.value,max:15},false,selected,undefined,origin),rows=[]\n if(version!==generation||selected!==account.value||origin!==props.source)return\n for(const row of result.messages??[]){const detail=await call('gmail.get',{id:row.id},false,selected,undefined,origin);if(version!==generation||selected!==account.value)return;rows.push(detail)}\n const available=await call('gmail.labels',{},false,selected,undefined,origin)\n if(version===generation&&selected===account.value){messages.value=rows;labelOptions.value=available.labels??[]}\n}\nasync function open(m:any){await action(async()=>{const selected=account.value,version=generation,origin=props.source;const result=await call('gmail.get',{id:m.id},false,selected,undefined,origin);if(selected===account.value&&version===generation)message.value=result})}\nasync function checkReceipt(){await action(async()=>{if(!uncertain.value)return;const r=await apiRequest<any>(base(uncertain.value.source)+'/receipts/'+uncertain.value.accountId+'/'+encodeURIComponent(uncertain.value.operationId));notice.value=r.status==='complete'?'邮箱操作已完成,已读取保存的结果。':r.status==='rejected'?'邮箱服务已拒绝此操作。':'仍无确认结果,请在 Gmail 核对。';if(['complete','rejected'].includes(r.status)){unknown.value=false;compose.value=false;draftTarget=undefined;uncertain.value=undefined;await load()}})}\nfunction decode(data:string){const bytes=Uint8Array.from(atob(data.replace(/-/g,'+').replace(/_/g,'/')),c=>c.charCodeAt(0));return new TextDecoder().decode(bytes)}\nfunction mailBody(payload:any):string{if(!payload)return '';if(payload.mimeType==='text/plain'&&payload.body?.data)return '<pre>'+decode(payload.body.data).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\n/g,'<br>')+'</pre>';const parts=payload.parts??[];const plain=parts.find((p:any)=>p.mimeType==='text/plain');if(plain)return mailBody(plain);if(payload.mimeType==='text/html'&&payload.body?.data)return DOMPurify.sanitize(decode(payload.body.data),{FORBID_TAGS:['img','style','script','iframe','form','svg','math','video','audio','link'],FORBID_ATTR:['style','src','srcset','background']});return parts.map(mailBody).join('')}\nfunction attachments(payload:any):any[]{return [...(payload?.filename&&(payload?.body?.attachmentId||payload?.body?.data)?[payload]:[]),...(payload?.parts??[]).flatMap(attachments)]}\nasync function download(part:any){await action(async()=>{const result=part.body.data?part.body:await call('gmail.attachment',{id:message.value.id,attachmentId:part.body.attachmentId});if(result.size>25*1024*1024||String(result.data??'').length>25*1024*1024*4/3+4)throw new Error('附件超过25MB限制');const bytes=Uint8Array.from(atob(result.data.replace(/-/g,'+').replace(/_/g,'/')),c=>c.charCodeAt(0));const url=URL.createObjectURL(new Blob([bytes],{type:'application/octet-stream'}));const link=document.createElement('a');link.href=url;link.download=String(part.filename).replace(/[\\\\/\\x00-\\x1f]/g,'_').slice(0,180)||'attachment';document.body.append(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(url),30000)})}\nfunction draft(isReply=false){if(unknown.value)return;draftTarget={accountId:account.value,...(isReply?{messageId:message.value.id}:{}),operationId:crypto.randomUUID(),source:props.source};reply.value=isReply;compose.value=true;to.value=isReply?(header(message.value,'Reply-To')||header(message.value,'From')):'';subject.value=isReply?'Re: '+header(message.value,'Subject'):'';body.value=''}\nasync function send(){await action(async()=>{\n const target=draftTarget;if(!target||target.accountId!==account.value||target.source!==props.source||unknown.value)throw new Error('草稿账号已改变,请重新编辑。')\n try{const result=await call(target.messageId?'gmail.reply':'gmail.send',target.messageId?{id:target.messageId,body:body.value}:{to:to.value,subject:subject.value,body:body.value},true,target.accountId,target.operationId,target.source);if(target.accountId!==account.value||target.source!==props.source)return;compose.value=false;draftTarget=undefined;notice.value='邮件已发送,消息编号:'+(result.id??'已确认');if(result.id)message.value=await call('gmail.get',{id:result.id},false,target.accountId,undefined,target.source)}catch(e){if(!unknown.value&&draftTarget)draftTarget.operationId=crypto.randomUUID();throw e}\n})}\nasync function mutate(operation:GoogleOperation,args:Record<string,unknown>){if(unknown.value)return;await action(async()=>{const selected=account.value;await call(operation,args,true,selected);await load();notice.value='邮箱操作已完成。'})}\nwatch(()=>props.accounts.map(a=>a.id).join(','),()=>{if(!props.accounts.some(a=>a.id===account.value)){account.value=props.accounts[0]?.id??'';generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined}})\nwatch(()=>props.source,()=>{generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined;error.value='';notice.value='';void action(load)})\nwatch(account,()=>{generation++;messages.value=[];message.value=undefined;compose.value=false;draftTarget=undefined;void action(load)})\nonMounted(()=>action(load))\n</script>\n<template>\n <section class=\"integration-panel mailbox\" :aria-label=\"source==='composio'?'Composio Gmail 邮箱':'本地 Gmail 邮箱'\">\n <header class=\"integration-header\"><div class=\"integration-heading\"><span class=\"integration-mark gmail-mark\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"25\" height=\"25\"></span><div><h3>Gmail</h3><p>{{sourceName}} · {{messages.length}} 封邮件</p></div></div><button class=\"primary\" type=\"button\" :disabled=\"busy||unknown||!account\" @click=\"draft()\"><AppIcon name=\"edit\" :size=\"16\" />写邮件</button></header>\n <label class=\"mail-account\">账号<select v-model=\"account\" :disabled=\"busy\"><option v-for=\"a in accounts\" :value=\"a.id\" :key=\"a.id\">{{a.label}}</option></select></label>\n <form class=\"mail-search\" @submit.prevent=\"action(load)\"><label class=\"integration-search\"><span class=\"sr-only\">搜索邮件</span><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"query\" placeholder=\"in:inbox 或 Gmail 搜索语法\"></label><button :disabled=\"busy\"><AppIcon name=\"refresh\" :size=\"16\" />搜索 / 刷新</button></form>\n <p v-if=\"busy\" class=\"mail-loading\" role=\"status\"><AppIcon name=\"clock\" :size=\"15\" />正在处理邮箱…</p><p v-if=\"error\" class=\"integration-alert\" role=\"alert\">{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\">{{notice}}</p>\n <div v-if=\"unknown\" class=\"mail-unknown\"><div class=\"integration-heading\"><AppIcon name=\"alert\" :size=\"19\" /><strong>写入结果尚未确认</strong></div><p>请先在 Gmail 核对,再继续操作。</p><div class=\"integration-actions\"><button :disabled=\"busy\" @click=\"checkReceipt\">查询操作收据</button><button class=\"quiet\" :disabled=\"busy\" @click=\"unknown=false;compose=false;notice='已按你的确认允许新的操作;原操作不会自动重发。'\">我已在 Gmail 核对,允许新操作</button></div></div>\n <div class=\"mail-layout\">\n <aside class=\"mail-list\"><div class=\"mail-list-heading\"><span>邮件列表</span><span>{{messages.length}}</span></div><ul class=\"messages\"><li v-for=\"m in messages\" :key=\"m.id\"><button :class=\"{active:message?.id===m.id}\" :aria-pressed=\"message?.id===m.id\" :disabled=\"busy\" @click=\"open(m)\"><span class=\"message-from\">{{header(m,'From')}}</span><strong>{{header(m,'Subject')||'(无主题)'}}</strong><small>{{m.snippet}}</small></button></li></ul><div v-if=\"!busy&&!messages.length\" class=\"integration-empty\"><AppIcon name=\"archive\" :size=\"26\" /><p>没有匹配的邮件。</p></div></aside>\n <div class=\"mail-reading\">\n <form v-if=\"compose\" class=\"mail-compose\" @submit.prevent=\"send\"><div class=\"compose-heading\"><h4>{{reply?'回复邮件':'新邮件'}}</h4><span class=\"integration-tag\">{{accounts.find(a=>a.id===account)?.label}}</span></div><label>收件人<input v-model=\"to\" :disabled=\"reply\" required></label><label>主题<input v-model=\"subject\" :disabled=\"reply\" required></label><label>正文<textarea v-model=\"body\" rows=\"7\" placeholder=\"写下你的邮件…\" required /></label><div class=\"integration-actions\"><button class=\"primary\" :disabled=\"busy||unknown\"><AppIcon name=\"arrow-up\" :size=\"16\" />发送邮件</button><button class=\"quiet\" type=\"button\" :disabled=\"busy\" @click=\"compose=false\">取消编辑</button></div></form>\n <article v-if=\"message\" class=\"mail-detail\"><header class=\"mail-detail-heading\"><h4>{{header(message,'Subject')||'(无主题)'}}</h4><p>{{header(message,'From')}}</p><p class=\"message-meta\">发送至 {{header(message,'To')}}<br>{{header(message,'Date')}}</p></header><div class=\"email-body\" v-html=\"mailBody(message.payload)\" />\n <div v-if=\"attachments(message.payload).length\" class=\"mail-attachments\"><button v-for=\"part in attachments(message.payload)\" :key=\"part.partId??part.body.attachmentId??part.filename\" :disabled=\"busy\" @click=\"download(part)\"><AppIcon name=\"download\" :size=\"16\" />下载 {{part.filename}}({{part.body.size}} 字节)</button></div>\n <div class=\"integration-actions mail-actions\"><button :disabled=\"busy||unknown\" @click=\"draft(true)\"><AppIcon name=\"edit\" :size=\"15\" />回复</button><button class=\"quiet\" :disabled=\"busy||unknown\" @click=\"mutate('gmail.modify',{id:message.id,removeLabels:['INBOX']})\"><AppIcon name=\"archive\" :size=\"15\" />归档</button><button class=\"quiet danger\" :disabled=\"busy||unknown\" @click=\"mutate('gmail.trash',{id:message.id})\"><AppIcon name=\"trash\" :size=\"15\" />移入垃圾箱</button></div>\n <div class=\"mail-labels\"><label>标签<select v-model=\"labels\"><option value=\"\">选择标签</option><option v-for=\"label in labelOptions\" :key=\"label.id\" :value=\"label.id\">{{label.name}}</option></select></label><div class=\"integration-actions\"><button :disabled=\"busy||unknown||!labels.trim()\" @click=\"mutate('gmail.modify',{id:message.id,addLabels:[labels]})\">添加标签</button><button class=\"quiet\" :disabled=\"busy||unknown||!labels\" @click=\"mutate('gmail.modify',{id:message.id,removeLabels:[labels]})\">移除标签</button></div></div>\n </article>\n <div v-else-if=\"!compose\" class=\"integration-empty reading-empty\"><span class=\"reading-illustration\"><AppIcon name=\"file\" :size=\"32\" /></span><strong>选择一封邮件开始阅读</strong><p>也可以写封新邮件,继续你的对话。</p></div>\n </div>\n </div>\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.mailbox{border-top:1px solid var(--line);padding-top:24px;gap:16px}.gmail-mark{background:var(--surface)}.mail-account{max-width:340px}.mail-search{display:flex;gap:8px;align-items:center}.mail-search>button{flex-shrink:0}.mail-layout{display:grid;grid-template-columns:minmax(190px,.72fr) minmax(0,1.6fr);border:1px solid var(--line);border-radius:13px;overflow:hidden;min-height:360px}.mail-list{min-width:0;background:var(--surface-soft);border-right:1px solid var(--line)}.mail-list-heading{display:flex;justify-content:space-between;padding:12px 14px;font-size:11px;color:var(--text-muted);border-bottom:1px solid var(--line)}.messages{list-style:none;margin:0;padding:6px;display:grid;gap:3px;max-height:560px;overflow:auto}.messages button{display:grid!important;gap:5px;width:100%!important;min-width:0;text-align:left;justify-content:stretch!important;justify-items:start;border:1px solid transparent!important;border-radius:8px!important;background:transparent!important;padding:12px!important;line-height:1.5}.messages button:hover:not(:disabled){background:var(--surface-hover)!important}.messages button.active{background:var(--surface)!important;border-color:var(--line)!important;box-shadow:0 1px 3px #00000006}.messages :is(strong,span,small){width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.messages strong{font-size:12px;font-weight:600}.message-from{font-size:11px;color:var(--text-secondary)}.messages small{font-size:11px;color:var(--text-muted)}.mail-reading{min-width:0;background:var(--surface)}.mail-detail{padding:22px;min-width:0}.mail-detail-heading{display:grid;gap:8px;padding-bottom:20px;border-bottom:1px solid var(--line);overflow-wrap:anywhere}.mail-detail h4{font-size:18px;line-height:1.5}.mail-detail-heading p{font-size:12px}.message-meta{font-size:11px!important;color:var(--text-muted)!important;line-height:1.8}.email-body{padding:22px 0;overflow-wrap:anywhere;overflow:auto;max-width:100%;line-height:1.8;min-height:100px}.email-body :deep(pre){white-space:pre-wrap;background:none;padding:0;font-family:inherit;font-size:13px}.email-body :deep(p){margin:0 0 12px;color:inherit}.mail-actions{padding:16px 0;border-top:1px solid var(--line)}.mail-labels{display:grid;gap:10px;padding-top:14px;border-top:1px solid var(--line)}.mail-labels label{max-width:240px}.mail-labels button{font-size:11px}.mail-attachments{display:grid;justify-items:start;gap:8px;padding-bottom:16px}.mail-attachments button{text-align:left;overflow-wrap:anywhere;font-size:12px}.mail-compose{display:grid;gap:14px;padding:20px;border-bottom:1px solid var(--line);background:var(--surface)}.compose-heading{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}.mail-compose textarea{line-height:1.7}.mail-unknown{display:grid;gap:10px;padding:16px;border-radius:11px;border:1px solid color-mix(in srgb,var(--warning) 25%,var(--line));background:color-mix(in srgb,var(--warning) 5%,var(--surface));color:var(--warning)}.mail-unknown p{font-size:12px}.mail-unknown button{font-size:12px}.mail-loading{display:flex;align-items:center;gap:7px;color:var(--text-muted)!important;font-size:12px}.reading-empty{min-height:320px;align-content:center}.reading-illustration{display:grid;place-items:center;width:64px;height:64px;margin-bottom:8px;border-radius:18px;background:var(--surface-soft);color:var(--text-muted)}.reading-empty p{font-size:12px}@media(max-width:700px){.mail-layout{grid-template-columns:1fr}.mail-list{border-right:0;border-bottom:1px solid var(--line)}.messages{max-height:220px}.mail-detail,.mail-compose{padding:18px}.reading-empty{min-height:200px}.mail-search{align-items:stretch}.mail-search>button{padding:8px;font-size:12px}.mail-search>button svg{display:none}}@media(max-width:420px){.mail-account{max-width:100%}.mailbox>.integration-header{align-items:center}.mail-detail h4{font-size:16px}.mail-unknown .integration-actions{display:grid;justify-items:start}}\n</style>\n","<script setup lang=\"ts\">\nimport { onBeforeUnmount,onMounted,ref } from 'vue'\nimport { apiRequest } from '@/api/client'\nimport type { GoogleAuth,GoogleStatus } from '@shared/googleWorkspace'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport GmailPanel from './GmailPanel.vue'\nconst base='/api/app/admin/hermes/local/google'\nconst value=ref<GoogleStatus>(),client=ref(''),selected=ref(['gmail']),flow=ref<GoogleAuth>(),busy=ref(false),error=ref(''),notice=ref(''),mail=ref(false),reconnectAccount=ref<string>()\nconst serviceLogos:Record<string,string>={gmail:'gmail',calendar:'googlecalendar',drive:'googledrive'}\nconst labels:Record<string,string>={gmail:'Gmail',calendar:'日历',drive:'云盘',contacts:'联系人',sheets:'表格',docs:'文档'}\nlet timer:ReturnType<typeof setTimeout>|undefined,closed=false\nasync function load(){value.value=await apiRequest<GoogleStatus>(base+'/status')}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'操作失败'}finally{busy.value=false}}\nasync function save(){await action(async()=>{await apiRequest(base+'/client',{method:'POST',body:{client:JSON.parse(client.value)}});client.value='';notice.value='客户端已保存在主机。请选择服务并连接账号。';await load()})}\nasync function connect(accountId?:string){if(busy.value)return;const popup=window.open('about:blank','_blank');if(popup)popup.opener=null;await action(async()=>{try{flow.value=await apiRequest<GoogleAuth>(base+'/auth',{method:'POST',body:{services:selected.value,...(accountId?{accountId}:{})}});if(popup&&flow.value.authUrl)popup.location.href=flow.value.authUrl;else popup?.close();poll()}catch(e){popup?.close();throw e}})}\nfunction prepareReconnect(id:string){const a=value.value?.accounts.find(v=>v.id===id);if(!a)return;selected.value=[...a.services];reconnectAccount.value=id;notice.value='已选择 '+a.label+'。请在上方勾选本次服务,再点击重新授权。'}\nfunction poll(){clearTimeout(timer);if(closed||!flow.value||!['pending','received','exchanging'].includes(flow.value.status))return;timer=setTimeout(async()=>{try{const r=await apiRequest<GoogleAuth>(base+'/auth/'+flow.value!.id);if(closed)return;flow.value={...flow.value,...r};if(r.status==='approved'){notice.value='Google 已连接。';await load()}}catch(e){if(!closed)error.value=e instanceof Error?e.message:'授权状态读取失败'}poll()},2000)}\nasync function cancel(){await action(async()=>{if(flow.value)flow.value=await apiRequest<GoogleAuth>(base+'/auth/'+flow.value.id,{method:'DELETE'});clearTimeout(timer)})}\nasync function disconnect(id:string,revision:string,revoke=false){if(!window.confirm(revoke?'撤销Google授权并断开?同一Google账号和客户端的其他本地连接也可能需要重新授权。':'断开这个本地 Google 账号?使用此连接的 Bot 将无法继续访问。'))return;await action(async()=>{const result=await apiRequest<{note:string}>(base+'/accounts/'+id,{method:'DELETE',body:{revision,revoke}});notice.value=result.note;await load()})}\nconst stateLabel=(s:string)=>({pending:'等待授权',received:'授权已收到',exchanging:'确认授权中',approved:'已连接',failed:'授权未完成',expired:'授权已过期',cancelled:'已取消',connected:'已连接',partial:'部分服务未获授权',reauth_required:'需要重新授权'}[s]??s)\nonMounted(()=>action(load));onBeforeUnmount(()=>{closed=true;clearTimeout(timer);if(flow.value&&['pending','received','exchanging'].includes(flow.value.status))void apiRequest(base+'/auth/'+flow.value.id,{method:'DELETE'}).catch(()=>{})})\n</script>\n<template>\n <section class=\"integration-panel local-apps\" aria-label=\"本地应用\">\n <header class=\"integration-header\">\n <div><h3>连接你的日常工具</h3><p class=\"local-intro\">由当前主机直接连接,账号与 Nous 应用独立管理。</p></div>\n <button class=\"quiet refresh-local\" type=\"button\" :disabled=\"busy\" aria-label=\"刷新本地连接\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button>\n </header>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\"><AppIcon name=\"alert\" :size=\"17\" />{{error}}</p>\n <p v-if=\"notice\" class=\"integration-alert\" role=\"status\"><AppIcon name=\"check\" :size=\"17\" />{{notice}}</p>\n <article class=\"integration-card google-card\">\n <header class=\"integration-header\">\n <div class=\"integration-heading\"><span class=\"google-mark\"><span>G</span></span><div><h3>Google Workspace</h3><p>邮件、日程和文件,一处连接。</p></div></div>\n <span class=\"integration-tag\" :class=\"{success:value?.configured}\">{{value?.configured?'客户端已配置':'待配置'}}</span>\n </header>\n <details class=\"google-setup\">\n <summary><AppIcon name=\"settings\" :size=\"16\" />{{value?.configured?'更新 Google 客户端':'配置 Google 客户端'}}</summary>\n <div class=\"setup-content\"><p>在 Google Cloud 创建“桌面应用”客户端,启用所需 API,然后粘贴下载的 JSON。</p><a href=\"https://console.cloud.google.com/auth/clients\" target=\"_blank\" rel=\"noopener noreferrer\">打开 Google Cloud ↗</a><form @submit.prevent=\"save\"><label>桌面客户端 JSON<textarea v-model=\"client\" rows=\"5\" autocomplete=\"off\" spellcheck=\"false\" placeholder='{ \"installed\": { … } }' required /></label><button class=\"primary\" :disabled=\"busy||!client.trim()\">保存到主机</button></form></div>\n </details>\n <fieldset class=\"google-services\"><legend>选择本次授权的服务</legend><div class=\"service-grid\"><label v-for=\"(label,id) in labels\" :key=\"id\" class=\"service-option\" :class=\"{selected:selected.includes(id)}\"><input v-model=\"selected\" type=\"checkbox\" :value=\"id\"><img v-if=\"['gmail','calendar','drive'].includes(id)\" :src=\"'/provider-logos/'+serviceLogos[id]+'.svg'\" alt=\"\" width=\"19\" height=\"19\"><AppIcon v-else :name=\"id==='contacts'?'users':id==='sheets'?'board':'file'\" :size=\"19\" /><span>{{label}}</span></label></div></fieldset>\n <div class=\"connect-row\"><p><AppIcon name=\"monitor\" :size=\"15\" />首次授权需在主机电脑的浏览器完成。</p><div class=\"integration-actions\"><button class=\"primary\" type=\"button\" :disabled=\"busy||!value?.configured||!selected.length||!!flow&&['pending','received','exchanging'].includes(flow.status)\" @click=\"connect(reconnectAccount)\"><AppIcon name=\"plus\" :size=\"16\" />{{reconnectAccount?'重新授权选中的账号':'连接 Google 账号'}}</button><button v-if=\"reconnectAccount\" class=\"quiet\" :disabled=\"busy\" @click=\"reconnectAccount=undefined;selected=['gmail']\">改为添加账号</button></div></div>\n <div v-if=\"flow\" class=\"auth-progress\" role=\"status\"><span class=\"integration-tag\" :class=\"{success:flow.status==='approved'}\">{{stateLabel(flow.status)}}</span><p v-if=\"flow.error\">{{flow.error}}</p><div class=\"integration-actions\"><a v-if=\"flow.authUrl&&flow.status==='pending'\" :href=\"flow.authUrl\" target=\"_blank\" rel=\"noopener noreferrer\">在 Host 电脑浏览器继续授权 ↗</a><button v-if=\"['pending','received','exchanging'].includes(flow.status)\" class=\"quiet\" :disabled=\"busy\" @click=\"cancel\">取消授权</button></div></div>\n <div class=\"google-accounts\"><div class=\"accounts-heading\"><h4>已连接账号</h4><span class=\"integration-count\">{{value?.accounts.length??0}} 个账号</span></div><ul v-if=\"value?.accounts.length\"><li v-for=\"a in value.accounts\" :key=\"a.id\"><div class=\"account-row\"><span class=\"account-avatar\"><AppIcon name=\"users\" :size=\"18\" /></span><div class=\"account-copy\"><strong>{{a.label}}</strong><p>{{a.services.map(s=>labels[s]??s).join(' · ')}}</p></div><span class=\"integration-tag\" :class=\"{success:a.status==='connected',warning:a.status!=='connected'}\">{{stateLabel(a.status)}}</span></div><details class=\"account-manage\"><summary>管理账号</summary><div class=\"integration-actions\"><button :disabled=\"busy\" @click=\"prepareReconnect(a.id)\">重新授权</button><button class=\"quiet\" :disabled=\"busy\" @click=\"disconnect(a.id,a.revision)\">断开</button><button class=\"quiet danger\" :disabled=\"busy\" @click=\"disconnect(a.id,a.revision,true)\">撤销 Google 授权</button></div></details></li></ul><p v-else class=\"accounts-empty\">连接账号后,所有设备都可以使用已授权的服务。</p></div>\n <button v-if=\"value?.accounts?.some(a=>a.services.includes('gmail'))\" class=\"mail-entry\" @click=\"mail=!mail\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"20\" height=\"20\">{{mail?'收起邮箱':'打开本地 Gmail 邮箱'}}<AppIcon :name=\"mail?'chevron-down':'chevron-right'\" :size=\"17\" /></button>\n </article>\n <GmailPanel v-if=\"mail&&value\" :accounts=\"value.accounts.filter(a=>a.services.includes('gmail'))\" />\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.local-intro{margin-top:5px!important;font-size:12px}.refresh-local{flex:0 0 auto}.google-card{display:grid;gap:22px}.google-mark{display:grid;place-items:center;width:48px;height:48px;border:1px solid var(--line);border-radius:14px;background:var(--surface);flex-shrink:0}.google-mark span{font-size:30px;font-weight:650;font-family:Arial,sans-serif;background:conic-gradient(#4285f4 0deg 90deg,#34a853 90deg 175deg,#fbbc05 175deg 225deg,#ea4335 225deg 280deg,#4285f4 280deg);background-clip:text;color:transparent}.google-card>.integration-header{align-items:center}.google-setup{padding:0 13px;border:1px solid var(--line);border-radius:10px;background:var(--surface-soft)}.setup-content{display:grid;gap:12px;padding:5px 0 16px}.setup-content form{display:grid;gap:12px}.setup-content textarea{font:12px/1.6 var(--font-code)}.google-services{margin:0;padding:0;border:0;min-width:0}.google-services legend{padding:0;margin-bottom:10px;font-size:12px;font-weight:550;color:var(--text-secondary)}.service-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.service-option{display:flex!important;align-items:center;gap:9px!important;min-height:48px;padding:10px 12px;border:1px solid var(--line);border-radius:9px;background:var(--surface);cursor:pointer;color:var(--text-primary)!important}.service-option.selected{border-color:var(--line-strong);background:var(--surface-soft)}.service-option input{margin-right:1px}.service-option svg{color:var(--text-secondary)}.connect-row{display:grid;gap:14px}.connect-row>p{display:flex;align-items:center;gap:7px;font-size:12px}.auth-progress{display:grid;gap:10px;padding:12px;border-radius:10px;background:var(--surface-soft)}.google-accounts{border-top:1px solid var(--line);padding-top:18px}.accounts-heading{display:flex;align-items:center;justify-content:space-between;gap:10px}.google-accounts ul{list-style:none;padding:0;margin:12px 0 0;display:grid;gap:12px}.google-accounts li{min-width:0;padding:12px;border:1px solid var(--line);border-radius:10px}.account-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.account-avatar{display:grid;place-items:center;width:34px;height:34px;border-radius:50%;background:var(--surface-soft);color:var(--text-secondary)}.account-copy{flex:1;min-width:100px;overflow-wrap:anywhere}.account-copy strong{font-weight:580}.account-copy p{font-size:11px;margin-top:3px}.account-manage{margin-top:6px}.account-manage summary{min-height:30px;font-size:11px}.accounts-empty{padding-top:12px;font-size:12px}.mail-entry{width:100%!important;justify-content:flex-start!important;padding:12px 14px!important;background:var(--surface-soft)!important}.mail-entry>svg{margin-left:auto}@media(max-width:420px){.service-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.google-card>.integration-header{align-items:flex-start;flex-wrap:wrap}.google-mark{width:40px;height:40px}.google-card{gap:18px}}\n</style>\n","<script setup lang=\"ts\">\nimport { onBeforeUnmount,onMounted,ref } from 'vue'\nimport { apiRequest } from '@/api/client'\nimport type { GoogleAuth,GoogleStatus } from '@shared/googleWorkspace'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport GmailPanel from './GmailPanel.vue'\nconst base='/api/app/admin/hermes/local/google'\nconst value=ref<GoogleStatus>(),client=ref(''),selected=ref(['gmail']),flow=ref<GoogleAuth>(),busy=ref(false),error=ref(''),notice=ref(''),mail=ref(false),reconnectAccount=ref<string>()\nconst serviceLogos:Record<string,string>={gmail:'gmail',calendar:'googlecalendar',drive:'googledrive'}\nconst labels:Record<string,string>={gmail:'Gmail',calendar:'日历',drive:'云盘',contacts:'联系人',sheets:'表格',docs:'文档'}\nlet timer:ReturnType<typeof setTimeout>|undefined,closed=false\nasync function load(){value.value=await apiRequest<GoogleStatus>(base+'/status')}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){error.value=e instanceof Error?e.message:'操作失败'}finally{busy.value=false}}\nasync function save(){await action(async()=>{await apiRequest(base+'/client',{method:'POST',body:{client:JSON.parse(client.value)}});client.value='';notice.value='客户端已保存在主机。请选择服务并连接账号。';await load()})}\nasync function connect(accountId?:string){if(busy.value)return;const popup=window.open('about:blank','_blank');if(popup)popup.opener=null;await action(async()=>{try{flow.value=await apiRequest<GoogleAuth>(base+'/auth',{method:'POST',body:{services:selected.value,...(accountId?{accountId}:{})}});if(popup&&flow.value.authUrl)popup.location.href=flow.value.authUrl;else popup?.close();poll()}catch(e){popup?.close();throw e}})}\nfunction prepareReconnect(id:string){const a=value.value?.accounts.find(v=>v.id===id);if(!a)return;selected.value=[...a.services];reconnectAccount.value=id;notice.value='已选择 '+a.label+'。请在上方勾选本次服务,再点击重新授权。'}\nfunction poll(){clearTimeout(timer);if(closed||!flow.value||!['pending','received','exchanging'].includes(flow.value.status))return;timer=setTimeout(async()=>{try{const r=await apiRequest<GoogleAuth>(base+'/auth/'+flow.value!.id);if(closed)return;flow.value={...flow.value,...r};if(r.status==='approved'){notice.value='Google 已连接。';await load()}}catch(e){if(!closed)error.value=e instanceof Error?e.message:'授权状态读取失败'}poll()},2000)}\nasync function cancel(){await action(async()=>{if(flow.value)flow.value=await apiRequest<GoogleAuth>(base+'/auth/'+flow.value.id,{method:'DELETE'});clearTimeout(timer)})}\nasync function disconnect(id:string,revision:string,revoke=false){if(!window.confirm(revoke?'撤销Google授权并断开?同一Google账号和客户端的其他本地连接也可能需要重新授权。':'断开这个本地 Google 账号?使用此连接的 Bot 将无法继续访问。'))return;await action(async()=>{const result=await apiRequest<{note:string}>(base+'/accounts/'+id,{method:'DELETE',body:{revision,revoke}});notice.value=result.note;await load()})}\nconst stateLabel=(s:string)=>({pending:'等待授权',received:'授权已收到',exchanging:'确认授权中',approved:'已连接',failed:'授权未完成',expired:'授权已过期',cancelled:'已取消',connected:'已连接',partial:'部分服务未获授权',reauth_required:'需要重新授权'}[s]??s)\nonMounted(()=>action(load));onBeforeUnmount(()=>{closed=true;clearTimeout(timer);if(flow.value&&['pending','received','exchanging'].includes(flow.value.status))void apiRequest(base+'/auth/'+flow.value.id,{method:'DELETE'}).catch(()=>{})})\n</script>\n<template>\n <section class=\"integration-panel local-apps\" aria-label=\"本地应用\">\n <header class=\"integration-header\">\n <div><h3>连接你的日常工具</h3><p class=\"local-intro\">由当前主机直接连接,账号与 Nous 应用独立管理。</p></div>\n <button class=\"quiet refresh-local\" type=\"button\" :disabled=\"busy\" aria-label=\"刷新本地连接\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button>\n </header>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\"><AppIcon name=\"alert\" :size=\"17\" />{{error}}</p>\n <p v-if=\"notice\" class=\"integration-alert\" role=\"status\"><AppIcon name=\"check\" :size=\"17\" />{{notice}}</p>\n <article class=\"integration-card google-card\">\n <header class=\"integration-header\">\n <div class=\"integration-heading\"><span class=\"google-mark\"><span>G</span></span><div><h3>Google Workspace</h3><p>邮件、日程和文件,一处连接。</p></div></div>\n <span class=\"integration-tag\" :class=\"{success:value?.configured}\">{{value?.configured?'客户端已配置':'待配置'}}</span>\n </header>\n <details class=\"google-setup\">\n <summary><AppIcon name=\"settings\" :size=\"16\" />{{value?.configured?'更新 Google 客户端':'配置 Google 客户端'}}</summary>\n <div class=\"setup-content\"><p>在 Google Cloud 创建“桌面应用”客户端,启用所需 API,然后粘贴下载的 JSON。</p><a href=\"https://console.cloud.google.com/auth/clients\" target=\"_blank\" rel=\"noopener noreferrer\">打开 Google Cloud ↗</a><form @submit.prevent=\"save\"><label>桌面客户端 JSON<textarea v-model=\"client\" rows=\"5\" autocomplete=\"off\" spellcheck=\"false\" placeholder='{ \"installed\": { … } }' required /></label><button class=\"primary\" :disabled=\"busy||!client.trim()\">保存到主机</button></form></div>\n </details>\n <fieldset class=\"google-services\"><legend>选择本次授权的服务</legend><div class=\"service-grid\"><label v-for=\"(label,id) in labels\" :key=\"id\" class=\"service-option\" :class=\"{selected:selected.includes(id)}\"><input v-model=\"selected\" type=\"checkbox\" :value=\"id\"><img v-if=\"['gmail','calendar','drive'].includes(id)\" :src=\"'/provider-logos/'+serviceLogos[id]+'.svg'\" alt=\"\" width=\"19\" height=\"19\"><AppIcon v-else :name=\"id==='contacts'?'users':id==='sheets'?'board':'file'\" :size=\"19\" /><span>{{label}}</span></label></div></fieldset>\n <div class=\"connect-row\"><p><AppIcon name=\"monitor\" :size=\"15\" />首次授权需在主机电脑的浏览器完成。</p><div class=\"integration-actions\"><button class=\"primary\" type=\"button\" :disabled=\"busy||!value?.configured||!selected.length||!!flow&&['pending','received','exchanging'].includes(flow.status)\" @click=\"connect(reconnectAccount)\"><AppIcon name=\"plus\" :size=\"16\" />{{reconnectAccount?'重新授权选中的账号':'连接 Google 账号'}}</button><button v-if=\"reconnectAccount\" class=\"quiet\" :disabled=\"busy\" @click=\"reconnectAccount=undefined;selected=['gmail']\">改为添加账号</button></div></div>\n <div v-if=\"flow\" class=\"auth-progress\" role=\"status\"><span class=\"integration-tag\" :class=\"{success:flow.status==='approved'}\">{{stateLabel(flow.status)}}</span><p v-if=\"flow.error\">{{flow.error}}</p><div class=\"integration-actions\"><a v-if=\"flow.authUrl&&flow.status==='pending'\" :href=\"flow.authUrl\" target=\"_blank\" rel=\"noopener noreferrer\">在 Host 电脑浏览器继续授权 ↗</a><button v-if=\"['pending','received','exchanging'].includes(flow.status)\" class=\"quiet\" :disabled=\"busy\" @click=\"cancel\">取消授权</button></div></div>\n <div class=\"google-accounts\"><div class=\"accounts-heading\"><h4>已连接账号</h4><span class=\"integration-count\">{{value?.accounts.length??0}} 个账号</span></div><ul v-if=\"value?.accounts.length\"><li v-for=\"a in value.accounts\" :key=\"a.id\"><div class=\"account-row\"><span class=\"account-avatar\"><AppIcon name=\"users\" :size=\"18\" /></span><div class=\"account-copy\"><strong>{{a.label}}</strong><p>{{a.services.map(s=>labels[s]??s).join(' · ')}}</p></div><span class=\"integration-tag\" :class=\"{success:a.status==='connected',warning:a.status!=='connected'}\">{{stateLabel(a.status)}}</span></div><details class=\"account-manage\"><summary>管理账号</summary><div class=\"integration-actions\"><button :disabled=\"busy\" @click=\"prepareReconnect(a.id)\">重新授权</button><button class=\"quiet\" :disabled=\"busy\" @click=\"disconnect(a.id,a.revision)\">断开</button><button class=\"quiet danger\" :disabled=\"busy\" @click=\"disconnect(a.id,a.revision,true)\">撤销 Google 授权</button></div></details></li></ul><p v-else class=\"accounts-empty\">连接账号后,所有设备都可以使用已授权的服务。</p></div>\n <button v-if=\"value?.accounts?.some(a=>a.services.includes('gmail'))\" class=\"mail-entry\" @click=\"mail=!mail\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"20\" height=\"20\">{{mail?'收起邮箱':'打开本地 Gmail 邮箱'}}<AppIcon :name=\"mail?'chevron-down':'chevron-right'\" :size=\"17\" /></button>\n </article>\n <GmailPanel v-if=\"mail&&value\" :accounts=\"value.accounts.filter(a=>a.services.includes('gmail'))\" />\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.local-intro{margin-top:5px!important;font-size:12px}.refresh-local{flex:0 0 auto}.google-card{display:grid;gap:22px}.google-mark{display:grid;place-items:center;width:48px;height:48px;border:1px solid var(--line);border-radius:14px;background:var(--surface);flex-shrink:0}.google-mark span{font-size:30px;font-weight:650;font-family:Arial,sans-serif;background:conic-gradient(#4285f4 0deg 90deg,#34a853 90deg 175deg,#fbbc05 175deg 225deg,#ea4335 225deg 280deg,#4285f4 280deg);background-clip:text;color:transparent}.google-card>.integration-header{align-items:center}.google-setup{padding:0 13px;border:1px solid var(--line);border-radius:10px;background:var(--surface-soft)}.setup-content{display:grid;gap:12px;padding:5px 0 16px}.setup-content form{display:grid;gap:12px}.setup-content textarea{font:12px/1.6 var(--font-code)}.google-services{margin:0;padding:0;border:0;min-width:0}.google-services legend{padding:0;margin-bottom:10px;font-size:12px;font-weight:550;color:var(--text-secondary)}.service-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.service-option{display:flex!important;align-items:center;gap:9px!important;min-height:48px;padding:10px 12px;border:1px solid var(--line);border-radius:9px;background:var(--surface);cursor:pointer;color:var(--text-primary)!important}.service-option.selected{border-color:var(--line-strong);background:var(--surface-soft)}.service-option input{margin-right:1px}.service-option svg{color:var(--text-secondary)}.connect-row{display:grid;gap:14px}.connect-row>p{display:flex;align-items:center;gap:7px;font-size:12px}.auth-progress{display:grid;gap:10px;padding:12px;border-radius:10px;background:var(--surface-soft)}.google-accounts{border-top:1px solid var(--line);padding-top:18px}.accounts-heading{display:flex;align-items:center;justify-content:space-between;gap:10px}.google-accounts ul{list-style:none;padding:0;margin:12px 0 0;display:grid;gap:12px}.google-accounts li{min-width:0;padding:12px;border:1px solid var(--line);border-radius:10px}.account-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.account-avatar{display:grid;place-items:center;width:34px;height:34px;border-radius:50%;background:var(--surface-soft);color:var(--text-secondary)}.account-copy{flex:1;min-width:100px;overflow-wrap:anywhere}.account-copy strong{font-weight:580}.account-copy p{font-size:11px;margin-top:3px}.account-manage{margin-top:6px}.account-manage summary{min-height:30px;font-size:11px}.accounts-empty{padding-top:12px;font-size:12px}.mail-entry{width:100%!important;justify-content:flex-start!important;padding:12px 14px!important;background:var(--surface-soft)!important}.mail-entry>svg{margin-left:auto}@media(max-width:420px){.service-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.google-card>.integration-header{align-items:flex-start;flex-wrap:wrap}.google-mark{width:40px;height:40px}.google-card{gap:18px}}\n</style>\n","<script setup lang=\"ts\">\nimport {onBeforeUnmount,onMounted,ref} from 'vue'\nimport {apiRequest} from '@/api/client'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport GmailPanel from './GmailPanel.vue'\nimport type {ComposioAccount,ComposioStatus} from '@shared/composioApps'\nconst base='/api/app/admin/composio'\nconst status=ref<ComposioStatus>(),accounts=ref<ComposioAccount[]>([]),busy=ref(false),error=ref(''),notice=ref(''),apiKey=ref(''),userId=ref(''),alias=ref(''),candidate=ref(''),authUrl=ref(''),mail=ref(false)\nlet deadline=0,beforeAuth=new Set<string>(),closed=false,timer:ReturnType<typeof setTimeout>|undefined\nconst stateLabel=(s:string)=>({ACTIVE:'已连接',INITIATED:'等待授权',PENDING:'等待授权',INITIALIZING:'连接中',EXPIRED:'已过期',FAILED:'连接失败',INACTIVE:'未启用',REVOKED:'已撤销'}[s.toUpperCase()]??s)\nasync function load(){\n const next=await apiRequest<ComposioStatus>(base+'/status');if(closed)return\n status.value=next;if(!next.legacy.some(c=>c.id===candidate.value))candidate.value=next.legacy[0]?.id??''\n if(!next.configured){accounts.value=[];return}\n const result=await apiRequest<{accounts:ComposioAccount[]}>(base+'/accounts');if(!closed)accounts.value=result.accounts\n}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){if(!closed)error.value=e instanceof Error?e.message:'Composio 操作失败'}finally{busy.value=false}}\nasync function importOld(){await action(async()=>{await apiRequest(base+'/import',{method:'POST',body:{candidateId:candidate.value,revision:status.value?.revision??0}});notice.value='已导入旧连接。原 Gmail 授权和 yaoyao 数据均已保留。';authUrl.value='';await load()})}\nasync function save(){await action(async()=>{await apiRequest(base+'/settings',{method:'PUT',body:{apiKey:apiKey.value.trim(),...(userId.value.trim()?{userId:userId.value.trim()}:{}),revision:status.value?.revision??0}});apiKey.value='';userId.value='';notice.value='Composio 连接配置已保存到主机。';await load()})}\nfunction stopPolling(){authUrl.value='';clearTimeout(timer)}\nfunction poll(){clearTimeout(timer);if(closed||!authUrl.value)return;if(Date.now()>deadline){notice.value='授权检查已结束,完成授权后可以手动刷新连接。';return}timer=setTimeout(async()=>{try{await load();if(accounts.value.some(a=>a.status==='ACTIVE'&&!beforeAuth.has(a.id))){authUrl.value='';notice.value='Gmail 已连接。'}}catch(e){if(!closed)error.value=e instanceof Error?e.message:'授权状态读取失败'}poll()},5000)}\nfunction trusted(value:string){try{const u=new URL(value);return u.protocol==='https:'&&!u.username&&!u.password&&(u.hostname==='composio.dev'||u.hostname.endsWith('.composio.dev'))?u.href:''}catch{return ''}}\nasync function connect(){if(busy.value)return;const popup=window.open('about:blank','_blank');if(popup)popup.opener=null;await action(async()=>{try{const next=await apiRequest<{url:string}>(base+'/authorize',{method:'POST',body:alias.value.trim()?{alias:alias.value.trim()}:{}});beforeAuth=new Set(accounts.value.filter(a=>a.status==='ACTIVE').map(a=>a.id));deadline=Date.now()+120_000;authUrl.value=trusted(next.url);if(popup&&authUrl.value)popup.location.href=authUrl.value;else popup?.close();alias.value='';notice.value='请在浏览器完成 Gmail 授权,然后刷新连接。';poll()}catch(e){popup?.close();throw e}})}\nasync function disconnect(account:ComposioAccount){if(!window.confirm('断开此 Composio Gmail 账号并撤销其授权?原 yaoyao 中使用此连接的功能也会受到影响。'))return;await action(async()=>{await apiRequest(base+'/accounts/'+encodeURIComponent(account.id),{method:'DELETE',body:{revision:status.value?.revision??0}});notice.value='已断开指定 Gmail 账号。';await load()})}\nonMounted(()=>action(load));onBeforeUnmount(()=>{closed=true;clearTimeout(timer)})\n</script>\n<template>\n <section class=\"integration-panel composio-apps\" aria-label=\"Composio 应用\">\n <header class=\"integration-header\"><div><h3>继续使用你的 Gmail</h3><p>通过 Composio 托管连接,与本地 Google 和 Nous 独立管理。</p></div><button class=\"quiet\" :disabled=\"busy\" aria-label=\"刷新 Composio 连接\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button></header>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\">{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\">{{notice}}</p>\n <article v-if=\"status?.legacy.length\" class=\"integration-card import-card\"><div class=\"integration-heading\"><span class=\"integration-mark\"><AppIcon name=\"archive\" :size=\"22\" /></span><div><h4>找到了 yaoyao 的旧连接</h4><p>导入项目配置,继续使用已有 Gmail 授权。</p></div></div><label v-if=\"status.legacy.length>1\">选择旧配置<select v-model=\"candidate\"><option v-for=\"c in status.legacy\" :key=\"c.id\" :value=\"c.id\">{{c.label}}</option></select></label><p v-if=\"status.configured\" class=\"hint\">导入将替换当前 Composio 项目配置。</p><button class=\"primary\" :disabled=\"busy||!candidate\" @click=\"importOld\"><AppIcon name=\"download\" :size=\"15\" />导入 yaoyao 旧连接</button></article>\n <article class=\"integration-card gmail-connection\"><header class=\"integration-header\"><div class=\"integration-heading\"><span class=\"integration-mark\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"25\" height=\"25\"></span><div><h3>Gmail</h3><p>邮件与草稿 · Composio</p></div></div><span class=\"integration-tag\" :class=\"{success:status?.configured}\">{{status?.configured?'项目已配置':'待配置'}}</span></header>\n <details class=\"composio-setup\"><summary><AppIcon name=\"settings\" :size=\"16\" />{{status?.configured?'更新项目配置':'手动配置 Composio'}}</summary><form @submit.prevent=\"save\"><p>也可以使用自己的 Composio 项目 API Key。凭据加密保存在这台主机,不会回显。</p><a href=\"https://platform.composio.dev\" target=\"_blank\" rel=\"noopener noreferrer\">打开 Composio ↗</a><label>项目 API Key<input v-model=\"apiKey\" type=\"password\" autocomplete=\"new-password\" placeholder=\"输入 Composio API Key\" required></label><label>已有用户标识(可选)<input v-model=\"userId\" placeholder=\"留空自动创建或保留现有标识\" maxlength=\"256\"></label><button class=\"primary\" :disabled=\"busy||!apiKey.trim()\">保存项目配置</button></form></details>\n <div class=\"accounts-heading\"><h4>已连接账号</h4><span class=\"integration-count\">{{accounts.length}} 个账号</span></div>\n <ul v-if=\"accounts.length\" class=\"composio-accounts\"><li v-for=\"account in accounts\" :key=\"account.id\"><div class=\"account-copy\"><strong>{{account.label}}</strong><p>Composio 托管授权</p></div><span class=\"integration-tag\" :class=\"{success:account.status==='ACTIVE'}\">{{stateLabel(account.status)}}</span><button class=\"quiet danger\" :disabled=\"busy\" :aria-label=\"'断开 '+account.label\" @click=\"disconnect(account)\">断开</button></li></ul><p v-else class=\"hint\">{{status?.configured?'还没有 Gmail 连接,可以添加账号。':'导入旧连接或配置项目后,即可连接 Gmail。'}}</p>\n <form class=\"add-account\" @submit.prevent=\"connect\"><label>{{accounts.length?'新账号别名':'账号别名(可选)'}}<input v-model=\"alias\" placeholder=\"例如:工作邮箱\" maxlength=\"64\" :required=\"!!accounts.length\" :disabled=\"busy||!status?.configured\"></label><button :disabled=\"busy||!status?.configured\"><AppIcon name=\"plus\" :size=\"16\" />连接 Gmail 账号</button></form>\n <div v-if=\"authUrl\" class=\"authorization-link\"><a :href=\"authUrl\" target=\"_blank\" rel=\"noopener noreferrer\">继续 Gmail 授权 ↗</a><button class=\"quiet\" @click=\"stopPolling\">停止检查授权状态</button></div>\n <button v-if=\"accounts.some(a=>a.status==='ACTIVE')\" class=\"mail-entry\" @click=\"mail=!mail\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"20\" height=\"20\">{{mail?'收起邮箱':'打开 Composio Gmail 邮箱'}}<AppIcon :name=\"mail?'chevron-down':'chevron-right'\" :size=\"17\" /></button>\n </article>\n <GmailPanel v-if=\"mail\" source=\"composio\" :accounts=\"accounts.filter(a=>a.status==='ACTIVE')\" />\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.composio-apps>.integration-header{align-items:flex-start}.composio-apps>.integration-header p{margin-top:6px;font-size:12px}.integration-card{display:grid;gap:18px}.import-card{background:var(--surface-soft)}.import-card>.primary{justify-self:start}.import-card p,.hint{font-size:12px;line-height:1.7}.composio-setup{border:1px solid var(--line);border-radius:10px;padding:0 13px;background:var(--surface-soft)}.composio-setup form{display:grid;gap:12px;padding:4px 0 16px}.composio-setup form p{font-size:12px;line-height:1.7}.accounts-heading{display:flex;justify-content:space-between;align-items:center}.composio-accounts{display:grid;gap:10px;list-style:none;padding:0;margin:0}.composio-accounts li{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:12px;border:1px solid var(--line);border-radius:10px}.account-copy{flex:1;min-width:120px;overflow-wrap:anywhere}.account-copy p{font-size:10px;margin-top:4px;font-family:var(--font-code)}.account-copy strong{font-weight:580}.add-account{display:flex;align-items:flex-end;gap:10px}.add-account label{flex:1}.authorization-link{display:grid;gap:8px;justify-items:start}.mail-entry{width:100%!important;justify-content:flex-start!important;padding:12px 14px!important;background:var(--surface-soft)!important}.mail-entry svg{margin-left:auto}@media(max-width:500px){.add-account{display:grid}.add-account button{justify-self:start}.gmail-connection>.integration-header{flex-wrap:wrap}}\n</style>\n","<script setup lang=\"ts\">\nimport {onBeforeUnmount,onMounted,ref} from 'vue'\nimport {apiRequest} from '@/api/client'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport GmailPanel from './GmailPanel.vue'\nimport type {ComposioAccount,ComposioStatus} from '@shared/composioApps'\nconst base='/api/app/admin/composio'\nconst status=ref<ComposioStatus>(),accounts=ref<ComposioAccount[]>([]),busy=ref(false),error=ref(''),notice=ref(''),apiKey=ref(''),userId=ref(''),alias=ref(''),candidate=ref(''),authUrl=ref(''),mail=ref(false)\nlet deadline=0,beforeAuth=new Set<string>(),closed=false,timer:ReturnType<typeof setTimeout>|undefined\nconst stateLabel=(s:string)=>({ACTIVE:'已连接',INITIATED:'等待授权',PENDING:'等待授权',INITIALIZING:'连接中',EXPIRED:'已过期',FAILED:'连接失败',INACTIVE:'未启用',REVOKED:'已撤销'}[s.toUpperCase()]??s)\nasync function load(){\n const next=await apiRequest<ComposioStatus>(base+'/status');if(closed)return\n status.value=next;if(!next.legacy.some(c=>c.id===candidate.value))candidate.value=next.legacy[0]?.id??''\n if(!next.configured){accounts.value=[];return}\n const result=await apiRequest<{accounts:ComposioAccount[]}>(base+'/accounts');if(!closed)accounts.value=result.accounts\n}\nasync function action(fn:()=>Promise<void>){if(busy.value)return;busy.value=true;error.value='';try{await fn()}catch(e){if(!closed)error.value=e instanceof Error?e.message:'Composio 操作失败'}finally{busy.value=false}}\nasync function importOld(){await action(async()=>{await apiRequest(base+'/import',{method:'POST',body:{candidateId:candidate.value,revision:status.value?.revision??0}});notice.value='已导入旧连接。原 Gmail 授权和 yaoyao 数据均已保留。';authUrl.value='';await load()})}\nasync function save(){await action(async()=>{await apiRequest(base+'/settings',{method:'PUT',body:{apiKey:apiKey.value.trim(),...(userId.value.trim()?{userId:userId.value.trim()}:{}),revision:status.value?.revision??0}});apiKey.value='';userId.value='';notice.value='Composio 连接配置已保存到主机。';await load()})}\nfunction stopPolling(){authUrl.value='';clearTimeout(timer)}\nfunction poll(){clearTimeout(timer);if(closed||!authUrl.value)return;if(Date.now()>deadline){notice.value='授权检查已结束,完成授权后可以手动刷新连接。';return}timer=setTimeout(async()=>{try{await load();if(accounts.value.some(a=>a.status==='ACTIVE'&&!beforeAuth.has(a.id))){authUrl.value='';notice.value='Gmail 已连接。'}}catch(e){if(!closed)error.value=e instanceof Error?e.message:'授权状态读取失败'}poll()},5000)}\nfunction trusted(value:string){try{const u=new URL(value);return u.protocol==='https:'&&!u.username&&!u.password&&(u.hostname==='composio.dev'||u.hostname.endsWith('.composio.dev'))?u.href:''}catch{return ''}}\nasync function connect(){if(busy.value)return;const popup=window.open('about:blank','_blank');if(popup)popup.opener=null;await action(async()=>{try{const next=await apiRequest<{url:string}>(base+'/authorize',{method:'POST',body:alias.value.trim()?{alias:alias.value.trim()}:{}});beforeAuth=new Set(accounts.value.filter(a=>a.status==='ACTIVE').map(a=>a.id));deadline=Date.now()+120_000;authUrl.value=trusted(next.url);if(popup&&authUrl.value)popup.location.href=authUrl.value;else popup?.close();alias.value='';notice.value='请在浏览器完成 Gmail 授权,然后刷新连接。';poll()}catch(e){popup?.close();throw e}})}\nasync function disconnect(account:ComposioAccount){if(!window.confirm('断开此 Composio Gmail 账号并撤销其授权?原 yaoyao 中使用此连接的功能也会受到影响。'))return;await action(async()=>{await apiRequest(base+'/accounts/'+encodeURIComponent(account.id),{method:'DELETE',body:{revision:status.value?.revision??0}});notice.value='已断开指定 Gmail 账号。';await load()})}\nonMounted(()=>action(load));onBeforeUnmount(()=>{closed=true;clearTimeout(timer)})\n</script>\n<template>\n <section class=\"integration-panel composio-apps\" aria-label=\"Composio 应用\">\n <header class=\"integration-header\"><div><h3>继续使用你的 Gmail</h3><p>通过 Composio 托管连接,与本地 Google 和 Nous 独立管理。</p></div><button class=\"quiet\" :disabled=\"busy\" aria-label=\"刷新 Composio 连接\" @click=\"action(load)\"><AppIcon name=\"refresh\" :size=\"17\" /></button></header>\n <p v-if=\"error\" class=\"integration-alert\" role=\"alert\">{{error}}</p><p v-if=\"notice\" class=\"integration-alert\" role=\"status\">{{notice}}</p>\n <article v-if=\"status?.legacy.length\" class=\"integration-card import-card\"><div class=\"integration-heading\"><span class=\"integration-mark\"><AppIcon name=\"archive\" :size=\"22\" /></span><div><h4>找到了 yaoyao 的旧连接</h4><p>导入项目配置,继续使用已有 Gmail 授权。</p></div></div><label v-if=\"status.legacy.length>1\">选择旧配置<select v-model=\"candidate\"><option v-for=\"c in status.legacy\" :key=\"c.id\" :value=\"c.id\">{{c.label}}</option></select></label><p v-if=\"status.configured\" class=\"hint\">导入将替换当前 Composio 项目配置。</p><button class=\"primary\" :disabled=\"busy||!candidate\" @click=\"importOld\"><AppIcon name=\"download\" :size=\"15\" />导入 yaoyao 旧连接</button></article>\n <article class=\"integration-card gmail-connection\"><header class=\"integration-header\"><div class=\"integration-heading\"><span class=\"integration-mark\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"25\" height=\"25\"></span><div><h3>Gmail</h3><p>邮件与草稿 · Composio</p></div></div><span class=\"integration-tag\" :class=\"{success:status?.configured}\">{{status?.configured?'项目已配置':'待配置'}}</span></header>\n <details class=\"composio-setup\"><summary><AppIcon name=\"settings\" :size=\"16\" />{{status?.configured?'更新项目配置':'手动配置 Composio'}}</summary><form @submit.prevent=\"save\"><p>也可以使用自己的 Composio 项目 API Key。凭据加密保存在这台主机,不会回显。</p><a href=\"https://platform.composio.dev\" target=\"_blank\" rel=\"noopener noreferrer\">打开 Composio ↗</a><label>项目 API Key<input v-model=\"apiKey\" type=\"password\" autocomplete=\"new-password\" placeholder=\"输入 Composio API Key\" required></label><label>已有用户标识(可选)<input v-model=\"userId\" placeholder=\"留空自动创建或保留现有标识\" maxlength=\"256\"></label><button class=\"primary\" :disabled=\"busy||!apiKey.trim()\">保存项目配置</button></form></details>\n <div class=\"accounts-heading\"><h4>已连接账号</h4><span class=\"integration-count\">{{accounts.length}} 个账号</span></div>\n <ul v-if=\"accounts.length\" class=\"composio-accounts\"><li v-for=\"account in accounts\" :key=\"account.id\"><div class=\"account-copy\"><strong>{{account.label}}</strong><p>Composio 托管授权</p></div><span class=\"integration-tag\" :class=\"{success:account.status==='ACTIVE'}\">{{stateLabel(account.status)}}</span><button class=\"quiet danger\" :disabled=\"busy\" :aria-label=\"'断开 '+account.label\" @click=\"disconnect(account)\">断开</button></li></ul><p v-else class=\"hint\">{{status?.configured?'还没有 Gmail 连接,可以添加账号。':'导入旧连接或配置项目后,即可连接 Gmail。'}}</p>\n <form class=\"add-account\" @submit.prevent=\"connect\"><label>{{accounts.length?'新账号别名':'账号别名(可选)'}}<input v-model=\"alias\" placeholder=\"例如:工作邮箱\" maxlength=\"64\" :required=\"!!accounts.length\" :disabled=\"busy||!status?.configured\"></label><button :disabled=\"busy||!status?.configured\"><AppIcon name=\"plus\" :size=\"16\" />连接 Gmail 账号</button></form>\n <div v-if=\"authUrl\" class=\"authorization-link\"><a :href=\"authUrl\" target=\"_blank\" rel=\"noopener noreferrer\">继续 Gmail 授权 ↗</a><button class=\"quiet\" @click=\"stopPolling\">停止检查授权状态</button></div>\n <button v-if=\"accounts.some(a=>a.status==='ACTIVE')\" class=\"mail-entry\" @click=\"mail=!mail\"><img src=\"/provider-logos/gmail.svg\" alt=\"\" width=\"20\" height=\"20\">{{mail?'收起邮箱':'打开 Composio Gmail 邮箱'}}<AppIcon :name=\"mail?'chevron-down':'chevron-right'\" :size=\"17\" /></button>\n </article>\n <GmailPanel v-if=\"mail\" source=\"composio\" :accounts=\"accounts.filter(a=>a.status==='ACTIVE')\" />\n </section>\n</template>\n<style scoped src=\"../../styles/integration-panels.css\"></style>\n<style scoped>\n.composio-apps>.integration-header{align-items:flex-start}.composio-apps>.integration-header p{margin-top:6px;font-size:12px}.integration-card{display:grid;gap:18px}.import-card{background:var(--surface-soft)}.import-card>.primary{justify-self:start}.import-card p,.hint{font-size:12px;line-height:1.7}.composio-setup{border:1px solid var(--line);border-radius:10px;padding:0 13px;background:var(--surface-soft)}.composio-setup form{display:grid;gap:12px;padding:4px 0 16px}.composio-setup form p{font-size:12px;line-height:1.7}.accounts-heading{display:flex;justify-content:space-between;align-items:center}.composio-accounts{display:grid;gap:10px;list-style:none;padding:0;margin:0}.composio-accounts li{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:12px;border:1px solid var(--line);border-radius:10px}.account-copy{flex:1;min-width:120px;overflow-wrap:anywhere}.account-copy p{font-size:10px;margin-top:4px;font-family:var(--font-code)}.account-copy strong{font-weight:580}.add-account{display:flex;align-items:flex-end;gap:10px}.add-account label{flex:1}.authorization-link{display:grid;gap:8px;justify-items:start}.mail-entry{width:100%!important;justify-content:flex-start!important;padding:12px 14px!important;background:var(--surface-soft)!important}.mail-entry svg{margin-left:auto}@media(max-width:500px){.add-account{display:grid}.add-account button{justify-self:start}.gmail-connection>.integration-header{flex-wrap:wrap}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { apiRequest } from '@/api/client'\nimport { hermesApi, type HermesOAuthProvider } from '@/api/hermes'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport ProviderLogo from '@/components/common/ProviderLogo.vue'\nimport ProviderLoginDialog from '@/components/app/ProviderLoginDialog.vue'\nimport { bundledProviderLogo } from '@shared/botProviderLogos'\nimport NativePluginsPanel from './NativePluginsPanel.vue'\nimport LocalAppsPanel from './LocalAppsPanel.vue'\nimport ComposioAppsPanel from './ComposioAppsPanel.vue'\nimport HermesMcpPanel from '@/components/app/HermesMcpPanel.vue'\nimport type { HermesConnectorStatus, HermesConnectorCatalogItem, HermesConnectorAccount, HermesConnectorOperation, HermesConnectorConnectInput } from '@shared/hermesConnectors'\nconst emit = defineEmits<{ 'dirty-change': [value: boolean] }>()\nconst base = '/api/app/admin/hermes/connectors'\nconst surface = ref<'local' | 'apps' | 'mcp' | 'native' | 'composio'>('local'), view = ref('marketplace'), canManage = ref(false)\nconst status = ref<HermesConnectorStatus>(), cards = ref<HermesConnectorCatalogItem[]>([]), accounts = ref<HermesConnectorAccount[]>([])\nconst loading = ref(true), busy = ref(false), error = ref(''), notice = ref(''), search = ref(''), alias = ref(''), editing = ref(''), login = ref<HermesOAuthProvider>()\nconst operation = ref<HermesConnectorOperation>(), visibleCount = ref(40), adding = ref(''), newAlias = ref('')\nwatch([search, view], () => { visibleCount.value = 40 })\nwatch([editing, adding], values => emit('dirty-change', values.some(Boolean)))\nlet closed = false, generation = 0, timer: ReturnType<typeof setTimeout> | undefined\nconst call = <T,>(path: string, method: string, body?: unknown) => apiRequest<T>(base + path, { method, ...(body === undefined ? {} : { body: body as any }) })\nconst linked = (slug: string) => accounts.value.filter(account => account.connector === slug)\nconst filtered = computed(() => {\n const all = new Map(cards.value.map(card => [card.slug, card]))\n for (const account of accounts.value) if (!all.has(account.connector)) all.set(account.connector, { slug: account.connector, name: account.connector, description: '', category: '' })\n const query = search.value.trim().toLowerCase()\n return [...all.values()].filter(card => (view.value !== 'connected' || linked(card.slug).length) && `${card.name} ${card.slug} ${card.description}`.toLowerCase().includes(query))\n})\nconst stateLabel = (state: string) => ({ active: '已连接', pending: '待授权', failed: '连接失败', expired: '已过期', revoked: '已撤销', inactive: '未启用' }[state] ?? state)\nconst operationLabel = computed(() => ({ pending: '等待完成授权', complete: '连接已完成', failed: '连接失败', cancelled: '已取消授权', expired: '授权已过期' }[operation.value?.status ?? 'pending']))\nfunction external(value?: string) { try { const url = new URL(value ?? ''); return url.protocol === 'https:' && !url.username && !url.password ? url.href : '' } catch { return '' } }\nasync function load() {\n const version = ++generation\n const results = await Promise.allSettled([apiRequest<HermesConnectorStatus>(base + '/status'), apiRequest<{ connectors: HermesConnectorCatalogItem[] }>(base + '/catalog'), apiRequest<{ accounts: HermesConnectorAccount[] }>(base + '/accounts')])\n if (closed || version !== generation) return\n const [s, c, a] = results\n if (s.status === 'fulfilled') status.value = s.value\n if (c.status === 'fulfilled') cards.value = c.value.connectors\n if (a.status === 'fulfilled') accounts.value = a.value.accounts\n error.value = results.filter((r): r is PromiseRejectedResult => r.status === 'rejected').map(r => r.reason instanceof Error ? r.reason.message : '读取失败,请重试').join(';')\n loading.value = false\n}\nasync function action(fn: () => Promise<void>) {\n if (busy.value) return\n busy.value = true; error.value = ''; notice.value = ''\n try { await fn(); if (!closed) await load() } catch (e) { if (!closed) error.value = e instanceof Error ? e.message : '操作失败,请重试' }\n finally { busy.value = false }\n}\nfunction poll() {\n clearTimeout(timer)\n if (closed || operation.value?.status !== 'pending') return\n if (Date.now() > operation.value.deadlineAt) { operation.value = { ...operation.value, status: 'expired' }; return }\n timer = setTimeout(async () => {\n const id = operation.value?.id\n if (!id || closed) return\n try {\n const next = await apiRequest<HermesConnectorOperation>(`${base}/operations/${encodeURIComponent(id)}`)\n if (closed || operation.value?.id !== id) return\n operation.value = next\n if (next.status === 'complete') { notice.value = '应用已连接,所有 Bot 均可使用。'; await load() }\n } catch (e) { if (!closed) error.value = e instanceof Error ? e.message : '暂时无法刷新授权状态' }\n poll()\n }, 2500)\n}\nasync function connect(card: HermesConnectorCatalogItem, account?: HermesConnectorAccount) {\n if (busy.value || operation.value?.status === 'pending') return\n if (!account && linked(card.slug).length && adding.value !== card.slug) { adding.value = card.slug; newAlias.value = ''; return }\n const accountAlias = !account && adding.value === card.slug ? newAlias.value.trim() : undefined\n if (accountAlias !== undefined && !/^[a-z0-9][a-z0-9-]{0,31}$/.test(accountAlias)) { error.value = '新账号别名需为 1–32 位小写字母、数字或连字符,以字母或数字开头。'; return }\n if (accountAlias && linked(card.slug).some(item => item.alias === accountAlias)) { error.value = '该应用已有同名账号,请换一个别名。'; return }\n const popup = window.open('about:blank', '_blank'); if (popup) popup.opener = null\n await action(async () => {\n try {\n const body: HermesConnectorConnectInput = { connector: card.slug, ...(accountAlias ? { alias: accountAlias } : {}), ...(account ? { reconnect: true, connectionId: account.id } : {}) }\n const next = await call<HermesConnectorOperation>('/operations', 'POST', body)\n if (closed) { popup?.close(); return }\n operation.value = next; adding.value = ''; newAlias.value = ''\n const url = external(next.targets.find(target => target.connectUrl)?.connectUrl)\n if (popup && url) popup.location.href = url; else popup?.close()\n notice.value = '请在浏览器完成授权。授权状态会自动刷新。'; poll()\n } catch (e) { popup?.close(); throw e }\n })\n}\nasync function cancel() { await action(async () => { if (operation.value) operation.value = await call<HermesConnectorOperation>(`/operations/${encodeURIComponent(operation.value.id)}`, 'DELETE'); clearTimeout(timer) }) }\nasync function disconnect(account: HermesConnectorAccount) { if (window.confirm('断开这个应用账号?所有 Bot 将失去此账号的访问权限。')) await action(async () => { await call(`/accounts/${encodeURIComponent(account.id)}`, 'DELETE') }) }\nasync function saveAlias(account: HermesConnectorAccount) { await action(async () => { if (!/^[a-z0-9][a-z0-9-]{0,31}$/.test(alias.value.trim())) throw new Error('别名需为 1–32 位小写字母、数字或连字符,以字母或数字开头。'); await call(`/accounts/${encodeURIComponent(account.id)}`, 'PATCH', { alias: alias.value.trim() }); editing.value = ''; alias.value = '' }) }\nasync function loginNous() { await action(async () => { const providers = (await hermesApi.oauth()).providers; const provider = providers.find(p => p.id === 'nous'); if (!provider) throw new Error('Nous 登录暂不可用,请检查主机版本。'); login.value = provider }) }\nonMounted(async () => {\n try { const bootstrap = await apiRequest<{ user?: { role?: string } }>('/api/app/bootstrap'); canManage.value = bootstrap.user?.role === 'admin' }\n catch (e) { error.value = e instanceof Error ? e.message : '无法读取管理权限' }\n if (closed) return\n if (canManage.value) await load(); else loading.value = false\n})\nonBeforeUnmount(() => { closed = true; generation++; clearTimeout(timer) })\n</script>\n<template>\n <section class=\"bot-plugins\" aria-label=\"Bot 插件管理\">\n <p class=\"intro\"><AppIcon name=\"link\" :size=\"14\" />连接的应用由这台服务器的所有 Bot 共用。</p>\n <div class=\"plugin-tabs\" role=\"tablist\" aria-label=\"插件类型\"><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'local'\" @click=\"surface = 'local'\"><AppIcon name=\"monitor\" :size=\"16\" />本地应用</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'native'\" @click=\"surface = 'native'\"><AppIcon name=\"artifacts\" :size=\"16\" />本地插件</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'composio'\" @click=\"surface = 'composio'\"><AppIcon name=\"link\" :size=\"16\" />Composio</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'apps'\" @click=\"surface = 'apps'\"><AppIcon name=\"link\" :size=\"17\" />Nous 应用</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'mcp'\" @click=\"surface = 'mcp'\"><AppIcon name=\"tools\" :size=\"17\" />MCP 服务</button></div>\n <NativePluginsPanel v-if=\"surface === 'native' && canManage\" /><p v-else-if=\"surface === 'native'\">本地插件由管理员管理。</p>\n <ComposioAppsPanel v-else-if=\"surface === 'composio' && canManage\" /><p v-else-if=\"surface === 'composio'\">Composio 连接由管理员管理。</p>\n <LocalAppsPanel v-else-if=\"surface === 'local' && canManage\" /><p v-else-if=\"surface === 'local'\">本地连接由管理员管理。</p>\n <HermesMcpPanel v-else-if=\"surface === 'mcp'\" :can-manage=\"canManage\" @dirty-change=\"emit('dirty-change', $event)\" />\n <template v-else>\n <p v-if=\"!loading && !canManage\" class=\"plugin-notice\">应用连接由管理员管理。</p>\n <template v-else>\n <p v-if=\"error\" class=\"plugin-error\" role=\"alert\">{{ error }} <button type=\"button\" :disabled=\"busy\" @click=\"load\">重试</button></p>\n <p v-if=\"notice\" class=\"plugin-notice\" role=\"status\">{{ notice }}</p><p v-if=\"loading\" role=\"status\">正在读取应用…</p>\n <div v-else class=\"connection-settings\">\n <template v-if=\"status?.authenticated\"><p class=\"hint\">{{ status.accountLabel || 'Nous 账号已登录' }}</p><p v-if=\"!status.available\" class=\"hint\">{{ status.reason === 'disabled' || status.reason === 'not_enabled' ? '应用连接服务尚未启用,请检查主机设置后刷新。' : '应用连接服务暂不可用,请刷新或检查主机设置。' }}</p><button :disabled=\"busy\" @click=\"loginNous\">重新登录 Nous</button></template>\n <template v-else-if=\"status?.reason === 'needs_auth' || (status?.available && !status.authenticated)\"><p>登录 Nous 后即可连接应用。登录只用于应用授权,当前主模型保持不变。</p><button :disabled=\"busy\" @click=\"loginNous\">登录 Nous</button></template>\n <p v-else-if=\"!status?.available\" class=\"hint\">{{ status?.reason === 'disabled' || status?.reason === 'not_enabled' ? '应用连接服务尚未启用,请检查主机设置后刷新。' : '应用连接服务暂不可用,请刷新或检查主机设置。' }}</p>\n </div>\n <section v-if=\"operation\" class=\"connection-settings\" aria-label=\"应用授权进度\"><p role=\"status\">{{ operationLabel }}</p><p v-if=\"operation.error\" class=\"plugin-error\">{{ operation.error }}</p><div v-for=\"target in operation.targets\" :key=\"target.connector\"><p v-if=\"target.detail\" class=\"hint\">{{ target.detail }}</p><a v-if=\"operation.status === 'pending' && external(target.connectUrl)\" :href=\"external(target.connectUrl)\" target=\"_blank\" rel=\"noopener noreferrer\">继续授权 <AppIcon name=\"external\" :size=\"13\" /></a></div><button v-if=\"operation.status === 'pending'\" :disabled=\"busy\" @click=\"cancel\">取消授权</button></section>\n <div class=\"plugin-toolbar\"><div class=\"view-tabs\" role=\"tablist\" aria-label=\"应用列表\"><button role=\"tab\" :aria-selected=\"view === 'marketplace'\" @click=\"view = 'marketplace'\">应用目录</button><button role=\"tab\" :aria-selected=\"view === 'connected'\" @click=\"view = 'connected'\">已连接 {{ accounts.length || '' }}</button></div><button :disabled=\"busy || loading\" aria-label=\"刷新应用连接\" @click=\"load\"><AppIcon name=\"refresh\" :size=\"17\" /></button></div>\n <label class=\"plugin-search\"><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"search\" type=\"search\" aria-label=\"搜索应用\" placeholder=\"搜索应用\" /></label>\n <div class=\"app-grid\"><article v-for=\"card in filtered.slice(0, visibleCount)\" :key=\"card.slug\" class=\"app-card\">\n <div class=\"card-heading\"><ProviderLogo :name=\"card.name\" :logo=\"card.logoUrl\" :bundled-logo=\"bundledProviderLogo(card.slug)\" /><h4>{{ card.name }}</h4></div><p class=\"hint\">{{ card.description }}</p>\n <div v-for=\"account in linked(card.slug)\" :key=\"account.id\" class=\"account-block\">\n <div class=\"app-account\"><span>{{ account.alias || account.label }}<small>{{ stateLabel(account.status) }}</small></span><button class=\"danger\" :disabled=\"busy\" :aria-label=\"`断开 ${card.name} ${account.alias || account.label}`\" @click=\"disconnect(account)\">断开</button></div>\n <p v-if=\"account.statusReason\" class=\"hint\">{{ account.statusReason }}</p>\n <form v-if=\"editing === account.id\" @submit.prevent=\"saveAlias(account)\"><label>账号别名<input v-model=\"alias\" maxlength=\"32\" pattern=\"[a-z0-9][a-z0-9-]{0,31}\" required placeholder=\"work-mail\" /><small class=\"hint\">1–32 位小写字母、数字或连字符,以字母或数字开头。</small></label><div class=\"card-actions\"><button type=\"button\" @click=\"editing = ''\">取消</button><button :disabled=\"busy\" type=\"submit\">保存别名</button></div></form>\n <div v-else class=\"card-actions\"><button :disabled=\"busy\" @click=\"editing = account.id; alias = account.alias || ''\">修改别名</button><button v-if=\"!account.active\" :disabled=\"busy || operation?.status === 'pending' || !status?.authenticated\" @click=\"connect(card, account)\">重新连接</button></div>\n </div>\n <form v-if=\"adding === card.slug\" @submit.prevent=\"connect(card)\"><label>新账号别名<input v-model=\"newAlias\" maxlength=\"32\" pattern=\"[a-z0-9][a-z0-9-]{0,31}\" required placeholder=\"personal-mail\" /><small class=\"hint\">使用不同的别名添加第二个账号:1–32 位小写字母、数字或连字符。</small></label><button type=\"button\" @click=\"adding = ''; newAlias = ''\">取消添加</button></form>\n <div class=\"card-actions\"><button :disabled=\"busy || !status?.authenticated || !status?.available || operation?.status === 'pending'\" @click=\"connect(card)\">{{ adding === card.slug ? '继续连接' : linked(card.slug).length ? '添加账号' : '连接' }}</button></div>\n </article></div>\n <button v-if=\"filtered.length > visibleCount\" type=\"button\" @click=\"visibleCount += 40\">显示更多应用({{ filtered.length - visibleCount }})</button><p v-if=\"!loading && !filtered.length\" class=\"plugin-empty\">{{ search ? '没有匹配的应用。' : view === 'connected' ? '还没有已连接的应用,可前往应用目录添加。' : '应用目录暂为空。' }}</p>\n </template>\n </template>\n <ProviderLoginDialog v-if=\"login\" :provider=\"login\" @close=\"login = undefined\" @success=\"login = undefined; load()\" />\n </section>\n</template>\n<style scoped>\n.bot-plugins{display:grid;gap:16px;color:var(--text-primary);font-size:13px}.intro,.hint{color:var(--text-secondary);line-height:1.6;margin:0}.hint{font-size:12px}.plugin-tabs,.view-tabs,.plugin-toolbar,.card-heading,.card-actions,.editor-actions{display:flex;align-items:center;gap:8px}.plugin-tabs{flex-wrap:wrap;border-bottom:1px solid var(--line);padding-bottom:8px}.plugin-tabs button[aria-selected=true],.view-tabs button[aria-selected=true]{background:var(--surface-soft);font-weight:650}.plugin-toolbar{justify-content:space-between}.plugin-toolbar p{margin:0;font-weight:650}.card-heading h4{font-size:14px;margin:0;flex:1;min-width:0;overflow-wrap:anywhere}.card-actions{flex-wrap:wrap}.bot-plugins button{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:44px;padding:6px 10px;border:1px solid var(--line);border-radius:8px;background:var(--surface-raised);color:inherit;font:inherit;cursor:pointer}.bot-plugins button:hover{background:var(--surface-hover)}.bot-plugins button:disabled{opacity:.45;cursor:not-allowed}.bot-plugins :is(button,input,select,textarea,a,summary):focus-visible{outline:2px solid var(--accent);outline-offset:2px}.bot-plugins a{color:var(--accent);display:inline-flex;align-items:center;gap:4px}.bot-plugins .danger{color:var(--danger)}.bot-plugins .primary{background:var(--accent);color:var(--text-on-solid);border-color:var(--accent)}.bot-plugins label{display:grid;gap:6px;font-size:12px;line-height:1.6}.bot-plugins input:not([type=checkbox]),.bot-plugins select,.bot-plugins textarea{width:100%;box-sizing:border-box;min-width:0;padding:9px 10px;border:1px solid var(--line);border-radius:8px;color:var(--text-primary);background:var(--surface);font:inherit}.bot-plugins textarea{resize:vertical;font-family:var(--font-mono,monospace)}.plugin-search{position:relative}.plugin-search>svg{position:absolute;left:10px;top:11px;color:var(--text-muted)}.plugin-search input{padding-left:34px!important}.plugin-editor,.mcp-card,.app-card,.connection-settings{border:1px solid var(--line);border-radius:12px;padding:14px;display:grid;gap:12px;background:var(--surface)}.plugin-editor h4{margin:0}.plugin-editor{background:var(--surface-soft)}.editor-actions{justify-content:flex-end}.bot-plugins fieldset{border:1px solid var(--line);border-radius:8px;display:grid;gap:4px;max-height:180px;overflow:auto}.bot-plugins .check-row{display:flex;align-items:center;gap:8px;min-height:32px}.check-row input{accent-color:var(--accent)}.app-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.app-card{align-content:start}.app-account{display:flex;align-items:center;justify-content:space-between;gap:8px;border-top:1px solid var(--line);padding-top:8px}.app-account span{min-width:0;overflow-wrap:anywhere}.app-account small{display:block;color:var(--text-muted);margin-top:3px}.badge{font-size:11px;color:var(--text-muted);white-space:nowrap}.badge.enabled{color:var(--accent)}.endpoint{overflow-wrap:anywhere}.plugin-error,.plugin-notice{padding:12px;border-radius:8px;background:var(--surface-soft);margin:0;line-height:1.6}.plugin-error{color:var(--danger)}.plugin-empty{text-align:center;color:var(--text-secondary);padding:28px 12px;line-height:1.8}.connection-settings{display:block}.connection-settings summary{cursor:pointer;font-weight:600;line-height:1.8}.connection-settings summary span{float:right;font-weight:400;color:var(--text-muted)}.connection-settings p{margin:12px 0}.connection-settings form{display:grid;gap:12px}.grant-editor{display:grid;gap:8px}@media(max-width:767px){.app-grid{grid-template-columns:1fr}.bot-plugins button{min-height:44px}.bot-plugins .check-row{min-height:44px}}\n</style>\n\n<style scoped>\n.bot-plugins{gap:22px}.intro{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--text-muted)}.plugin-tabs{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:4px;padding:4px;border:1px solid var(--line);border-radius:12px;background:var(--surface-soft)}.plugin-tabs button{min-height:38px;padding:7px 6px;border:0;border-radius:8px;background:transparent;color:var(--text-secondary);font-size:12px;white-space:nowrap}.plugin-tabs button[aria-selected=true]{background:var(--surface);color:var(--text-primary);font-weight:600;box-shadow:0 1px 4px #0000000b}.connection-settings{padding:18px;border-radius:13px}.app-card{padding:18px;border-radius:13px}.app-grid{gap:12px}.app-card>.hint{line-height:1.7}.plugin-search input{border-radius:10px}.plugin-toolbar .view-tabs button{min-height:36px;font-size:12px}.plugin-toolbar>button{border-color:transparent}@media(max-width:600px){.plugin-tabs{gap:2px;padding:3px;display:flex;flex-wrap:wrap}.plugin-tabs button{flex:1 1 85px}.plugin-tabs button{gap:5px;min-height:44px;font-size:11px}.plugin-tabs button>svg{width:14px;height:14px}.bot-plugins{gap:18px}}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { apiRequest } from '@/api/client'\nimport { hermesApi, type HermesOAuthProvider } from '@/api/hermes'\nimport AppIcon from '@/components/common/AppIcon.vue'\nimport ProviderLogo from '@/components/common/ProviderLogo.vue'\nimport ProviderLoginDialog from '@/components/app/ProviderLoginDialog.vue'\nimport { bundledProviderLogo } from '@shared/botProviderLogos'\nimport NativePluginsPanel from './NativePluginsPanel.vue'\nimport LocalAppsPanel from './LocalAppsPanel.vue'\nimport ComposioAppsPanel from './ComposioAppsPanel.vue'\nimport HermesMcpPanel from '@/components/app/HermesMcpPanel.vue'\nimport type { HermesConnectorStatus, HermesConnectorCatalogItem, HermesConnectorAccount, HermesConnectorOperation, HermesConnectorConnectInput } from '@shared/hermesConnectors'\nconst emit = defineEmits<{ 'dirty-change': [value: boolean] }>()\nconst base = '/api/app/admin/hermes/connectors'\nconst surface = ref<'local' | 'apps' | 'mcp' | 'native' | 'composio'>('local'), view = ref('marketplace'), canManage = ref(false)\nconst status = ref<HermesConnectorStatus>(), cards = ref<HermesConnectorCatalogItem[]>([]), accounts = ref<HermesConnectorAccount[]>([])\nconst loading = ref(true), busy = ref(false), error = ref(''), notice = ref(''), search = ref(''), alias = ref(''), editing = ref(''), login = ref<HermesOAuthProvider>()\nconst operation = ref<HermesConnectorOperation>(), visibleCount = ref(40), adding = ref(''), newAlias = ref('')\nwatch([search, view], () => { visibleCount.value = 40 })\nwatch([editing, adding], values => emit('dirty-change', values.some(Boolean)))\nlet closed = false, generation = 0, timer: ReturnType<typeof setTimeout> | undefined\nconst call = <T,>(path: string, method: string, body?: unknown) => apiRequest<T>(base + path, { method, ...(body === undefined ? {} : { body: body as any }) })\nconst linked = (slug: string) => accounts.value.filter(account => account.connector === slug)\nconst filtered = computed(() => {\n const all = new Map(cards.value.map(card => [card.slug, card]))\n for (const account of accounts.value) if (!all.has(account.connector)) all.set(account.connector, { slug: account.connector, name: account.connector, description: '', category: '' })\n const query = search.value.trim().toLowerCase()\n return [...all.values()].filter(card => (view.value !== 'connected' || linked(card.slug).length) && `${card.name} ${card.slug} ${card.description}`.toLowerCase().includes(query))\n})\nconst stateLabel = (state: string) => ({ active: '已连接', pending: '待授权', failed: '连接失败', expired: '已过期', revoked: '已撤销', inactive: '未启用' }[state] ?? state)\nconst operationLabel = computed(() => ({ pending: '等待完成授权', complete: '连接已完成', failed: '连接失败', cancelled: '已取消授权', expired: '授权已过期' }[operation.value?.status ?? 'pending']))\nfunction external(value?: string) { try { const url = new URL(value ?? ''); return url.protocol === 'https:' && !url.username && !url.password ? url.href : '' } catch { return '' } }\nasync function load() {\n const version = ++generation\n const results = await Promise.allSettled([apiRequest<HermesConnectorStatus>(base + '/status'), apiRequest<{ connectors: HermesConnectorCatalogItem[] }>(base + '/catalog'), apiRequest<{ accounts: HermesConnectorAccount[] }>(base + '/accounts')])\n if (closed || version !== generation) return\n const [s, c, a] = results\n if (s.status === 'fulfilled') status.value = s.value\n if (c.status === 'fulfilled') cards.value = c.value.connectors\n if (a.status === 'fulfilled') accounts.value = a.value.accounts\n error.value = results.filter((r): r is PromiseRejectedResult => r.status === 'rejected').map(r => r.reason instanceof Error ? r.reason.message : '读取失败,请重试').join(';')\n loading.value = false\n}\nasync function action(fn: () => Promise<void>) {\n if (busy.value) return\n busy.value = true; error.value = ''; notice.value = ''\n try { await fn(); if (!closed) await load() } catch (e) { if (!closed) error.value = e instanceof Error ? e.message : '操作失败,请重试' }\n finally { busy.value = false }\n}\nfunction poll() {\n clearTimeout(timer)\n if (closed || operation.value?.status !== 'pending') return\n if (Date.now() > operation.value.deadlineAt) { operation.value = { ...operation.value, status: 'expired' }; return }\n timer = setTimeout(async () => {\n const id = operation.value?.id\n if (!id || closed) return\n try {\n const next = await apiRequest<HermesConnectorOperation>(`${base}/operations/${encodeURIComponent(id)}`)\n if (closed || operation.value?.id !== id) return\n operation.value = next\n if (next.status === 'complete') { notice.value = '应用已连接,所有 Bot 均可使用。'; await load() }\n } catch (e) { if (!closed) error.value = e instanceof Error ? e.message : '暂时无法刷新授权状态' }\n poll()\n }, 2500)\n}\nasync function connect(card: HermesConnectorCatalogItem, account?: HermesConnectorAccount) {\n if (busy.value || operation.value?.status === 'pending') return\n if (!account && linked(card.slug).length && adding.value !== card.slug) { adding.value = card.slug; newAlias.value = ''; return }\n const accountAlias = !account && adding.value === card.slug ? newAlias.value.trim() : undefined\n if (accountAlias !== undefined && !/^[a-z0-9][a-z0-9-]{0,31}$/.test(accountAlias)) { error.value = '新账号别名需为 1–32 位小写字母、数字或连字符,以字母或数字开头。'; return }\n if (accountAlias && linked(card.slug).some(item => item.alias === accountAlias)) { error.value = '该应用已有同名账号,请换一个别名。'; return }\n const popup = window.open('about:blank', '_blank'); if (popup) popup.opener = null\n await action(async () => {\n try {\n const body: HermesConnectorConnectInput = { connector: card.slug, ...(accountAlias ? { alias: accountAlias } : {}), ...(account ? { reconnect: true, connectionId: account.id } : {}) }\n const next = await call<HermesConnectorOperation>('/operations', 'POST', body)\n if (closed) { popup?.close(); return }\n operation.value = next; adding.value = ''; newAlias.value = ''\n const url = external(next.targets.find(target => target.connectUrl)?.connectUrl)\n if (popup && url) popup.location.href = url; else popup?.close()\n notice.value = '请在浏览器完成授权。授权状态会自动刷新。'; poll()\n } catch (e) { popup?.close(); throw e }\n })\n}\nasync function cancel() { await action(async () => { if (operation.value) operation.value = await call<HermesConnectorOperation>(`/operations/${encodeURIComponent(operation.value.id)}`, 'DELETE'); clearTimeout(timer) }) }\nasync function disconnect(account: HermesConnectorAccount) { if (window.confirm('断开这个应用账号?所有 Bot 将失去此账号的访问权限。')) await action(async () => { await call(`/accounts/${encodeURIComponent(account.id)}`, 'DELETE') }) }\nasync function saveAlias(account: HermesConnectorAccount) { await action(async () => { if (!/^[a-z0-9][a-z0-9-]{0,31}$/.test(alias.value.trim())) throw new Error('别名需为 1–32 位小写字母、数字或连字符,以字母或数字开头。'); await call(`/accounts/${encodeURIComponent(account.id)}`, 'PATCH', { alias: alias.value.trim() }); editing.value = ''; alias.value = '' }) }\nasync function loginNous() { await action(async () => { const providers = (await hermesApi.oauth()).providers; const provider = providers.find(p => p.id === 'nous'); if (!provider) throw new Error('Nous 登录暂不可用,请检查主机版本。'); login.value = provider }) }\nonMounted(async () => {\n try { const bootstrap = await apiRequest<{ user?: { role?: string } }>('/api/app/bootstrap'); canManage.value = bootstrap.user?.role === 'admin' }\n catch (e) { error.value = e instanceof Error ? e.message : '无法读取管理权限' }\n if (closed) return\n if (canManage.value) await load(); else loading.value = false\n})\nonBeforeUnmount(() => { closed = true; generation++; clearTimeout(timer) })\n</script>\n<template>\n <section class=\"bot-plugins\" aria-label=\"Bot 插件管理\">\n <p class=\"intro\"><AppIcon name=\"link\" :size=\"14\" />连接的应用由这台服务器的所有 Bot 共用。</p>\n <div class=\"plugin-tabs\" role=\"tablist\" aria-label=\"插件类型\"><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'local'\" @click=\"surface = 'local'\"><AppIcon name=\"monitor\" :size=\"16\" />本地应用</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'native'\" @click=\"surface = 'native'\"><AppIcon name=\"artifacts\" :size=\"16\" />本地插件</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'composio'\" @click=\"surface = 'composio'\"><AppIcon name=\"link\" :size=\"16\" />Composio</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'apps'\" @click=\"surface = 'apps'\"><AppIcon name=\"link\" :size=\"17\" />Nous 应用</button><button type=\"button\" role=\"tab\" :aria-selected=\"surface === 'mcp'\" @click=\"surface = 'mcp'\"><AppIcon name=\"tools\" :size=\"17\" />MCP 服务</button></div>\n <NativePluginsPanel v-if=\"surface === 'native' && canManage\" /><p v-else-if=\"surface === 'native'\">本地插件由管理员管理。</p>\n <ComposioAppsPanel v-else-if=\"surface === 'composio' && canManage\" /><p v-else-if=\"surface === 'composio'\">Composio 连接由管理员管理。</p>\n <LocalAppsPanel v-else-if=\"surface === 'local' && canManage\" /><p v-else-if=\"surface === 'local'\">本地连接由管理员管理。</p>\n <HermesMcpPanel v-else-if=\"surface === 'mcp'\" :can-manage=\"canManage\" @dirty-change=\"emit('dirty-change', $event)\" />\n <template v-else>\n <p v-if=\"!loading && !canManage\" class=\"plugin-notice\">应用连接由管理员管理。</p>\n <template v-else>\n <p v-if=\"error\" class=\"plugin-error\" role=\"alert\">{{ error }} <button type=\"button\" :disabled=\"busy\" @click=\"load\">重试</button></p>\n <p v-if=\"notice\" class=\"plugin-notice\" role=\"status\">{{ notice }}</p><p v-if=\"loading\" role=\"status\">正在读取应用…</p>\n <div v-else class=\"connection-settings\">\n <template v-if=\"status?.authenticated\"><p class=\"hint\">{{ status.accountLabel || 'Nous 账号已登录' }}</p><p v-if=\"!status.available\" class=\"hint\">{{ status.reason === 'disabled' || status.reason === 'not_enabled' ? '应用连接服务尚未启用,请检查主机设置后刷新。' : '应用连接服务暂不可用,请刷新或检查主机设置。' }}</p><button :disabled=\"busy\" @click=\"loginNous\">重新登录 Nous</button></template>\n <template v-else-if=\"status?.reason === 'needs_auth' || (status?.available && !status.authenticated)\"><p>登录 Nous 后即可连接应用。登录只用于应用授权,当前主模型保持不变。</p><button :disabled=\"busy\" @click=\"loginNous\">登录 Nous</button></template>\n <p v-else-if=\"!status?.available\" class=\"hint\">{{ status?.reason === 'disabled' || status?.reason === 'not_enabled' ? '应用连接服务尚未启用,请检查主机设置后刷新。' : '应用连接服务暂不可用,请刷新或检查主机设置。' }}</p>\n </div>\n <section v-if=\"operation\" class=\"connection-settings\" aria-label=\"应用授权进度\"><p role=\"status\">{{ operationLabel }}</p><p v-if=\"operation.error\" class=\"plugin-error\">{{ operation.error }}</p><div v-for=\"target in operation.targets\" :key=\"target.connector\"><p v-if=\"target.detail\" class=\"hint\">{{ target.detail }}</p><a v-if=\"operation.status === 'pending' && external(target.connectUrl)\" :href=\"external(target.connectUrl)\" target=\"_blank\" rel=\"noopener noreferrer\">继续授权 <AppIcon name=\"external\" :size=\"13\" /></a></div><button v-if=\"operation.status === 'pending'\" :disabled=\"busy\" @click=\"cancel\">取消授权</button></section>\n <div class=\"plugin-toolbar\"><div class=\"view-tabs\" role=\"tablist\" aria-label=\"应用列表\"><button role=\"tab\" :aria-selected=\"view === 'marketplace'\" @click=\"view = 'marketplace'\">应用目录</button><button role=\"tab\" :aria-selected=\"view === 'connected'\" @click=\"view = 'connected'\">已连接 {{ accounts.length || '' }}</button></div><button :disabled=\"busy || loading\" aria-label=\"刷新应用连接\" @click=\"load\"><AppIcon name=\"refresh\" :size=\"17\" /></button></div>\n <label class=\"plugin-search\"><AppIcon name=\"search\" :size=\"17\" /><input v-model=\"search\" type=\"search\" aria-label=\"搜索应用\" placeholder=\"搜索应用\" /></label>\n <div class=\"app-grid\"><article v-for=\"card in filtered.slice(0, visibleCount)\" :key=\"card.slug\" class=\"app-card\">\n <div class=\"card-heading\"><ProviderLogo :name=\"card.name\" :logo=\"card.logoUrl\" :bundled-logo=\"bundledProviderLogo(card.slug)\" /><h4>{{ card.name }}</h4></div><p class=\"hint\">{{ card.description }}</p>\n <div v-for=\"account in linked(card.slug)\" :key=\"account.id\" class=\"account-block\">\n <div class=\"app-account\"><span>{{ account.alias || account.label }}<small>{{ stateLabel(account.status) }}</small></span><button class=\"danger\" :disabled=\"busy\" :aria-label=\"`断开 ${card.name} ${account.alias || account.label}`\" @click=\"disconnect(account)\">断开</button></div>\n <p v-if=\"account.statusReason\" class=\"hint\">{{ account.statusReason }}</p>\n <form v-if=\"editing === account.id\" @submit.prevent=\"saveAlias(account)\"><label>账号别名<input v-model=\"alias\" maxlength=\"32\" pattern=\"[a-z0-9][a-z0-9-]{0,31}\" required placeholder=\"work-mail\" /><small class=\"hint\">1–32 位小写字母、数字或连字符,以字母或数字开头。</small></label><div class=\"card-actions\"><button type=\"button\" @click=\"editing = ''\">取消</button><button :disabled=\"busy\" type=\"submit\">保存别名</button></div></form>\n <div v-else class=\"card-actions\"><button :disabled=\"busy\" @click=\"editing = account.id; alias = account.alias || ''\">修改别名</button><button v-if=\"!account.active\" :disabled=\"busy || operation?.status === 'pending' || !status?.authenticated\" @click=\"connect(card, account)\">重新连接</button></div>\n </div>\n <form v-if=\"adding === card.slug\" @submit.prevent=\"connect(card)\"><label>新账号别名<input v-model=\"newAlias\" maxlength=\"32\" pattern=\"[a-z0-9][a-z0-9-]{0,31}\" required placeholder=\"personal-mail\" /><small class=\"hint\">使用不同的别名添加第二个账号:1–32 位小写字母、数字或连字符。</small></label><button type=\"button\" @click=\"adding = ''; newAlias = ''\">取消添加</button></form>\n <div class=\"card-actions\"><button :disabled=\"busy || !status?.authenticated || !status?.available || operation?.status === 'pending'\" @click=\"connect(card)\">{{ adding === card.slug ? '继续连接' : linked(card.slug).length ? '添加账号' : '连接' }}</button></div>\n </article></div>\n <button v-if=\"filtered.length > visibleCount\" type=\"button\" @click=\"visibleCount += 40\">显示更多应用({{ filtered.length - visibleCount }})</button><p v-if=\"!loading && !filtered.length\" class=\"plugin-empty\">{{ search ? '没有匹配的应用。' : view === 'connected' ? '还没有已连接的应用,可前往应用目录添加。' : '应用目录暂为空。' }}</p>\n </template>\n </template>\n <ProviderLoginDialog v-if=\"login\" :provider=\"login\" @close=\"login = undefined\" @success=\"login = undefined; load()\" />\n </section>\n</template>\n<style scoped>\n.bot-plugins{display:grid;gap:16px;color:var(--text-primary);font-size:13px}.intro,.hint{color:var(--text-secondary);line-height:1.6;margin:0}.hint{font-size:12px}.plugin-tabs,.view-tabs,.plugin-toolbar,.card-heading,.card-actions,.editor-actions{display:flex;align-items:center;gap:8px}.plugin-tabs{flex-wrap:wrap;border-bottom:1px solid var(--line);padding-bottom:8px}.plugin-tabs button[aria-selected=true],.view-tabs button[aria-selected=true]{background:var(--surface-soft);font-weight:650}.plugin-toolbar{justify-content:space-between}.plugin-toolbar p{margin:0;font-weight:650}.card-heading h4{font-size:14px;margin:0;flex:1;min-width:0;overflow-wrap:anywhere}.card-actions{flex-wrap:wrap}.bot-plugins button{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:44px;padding:6px 10px;border:1px solid var(--line);border-radius:8px;background:var(--surface-raised);color:inherit;font:inherit;cursor:pointer}.bot-plugins button:hover{background:var(--surface-hover)}.bot-plugins button:disabled{opacity:.45;cursor:not-allowed}.bot-plugins :is(button,input,select,textarea,a,summary):focus-visible{outline:2px solid var(--accent);outline-offset:2px}.bot-plugins a{color:var(--accent);display:inline-flex;align-items:center;gap:4px}.bot-plugins .danger{color:var(--danger)}.bot-plugins .primary{background:var(--accent);color:var(--text-on-solid);border-color:var(--accent)}.bot-plugins label{display:grid;gap:6px;font-size:12px;line-height:1.6}.bot-plugins input:not([type=checkbox]),.bot-plugins select,.bot-plugins textarea{width:100%;box-sizing:border-box;min-width:0;padding:9px 10px;border:1px solid var(--line);border-radius:8px;color:var(--text-primary);background:var(--surface);font:inherit}.bot-plugins textarea{resize:vertical;font-family:var(--font-mono,monospace)}.plugin-search{position:relative}.plugin-search>svg{position:absolute;left:10px;top:11px;color:var(--text-muted)}.plugin-search input{padding-left:34px!important}.plugin-editor,.mcp-card,.app-card,.connection-settings{border:1px solid var(--line);border-radius:12px;padding:14px;display:grid;gap:12px;background:var(--surface)}.plugin-editor h4{margin:0}.plugin-editor{background:var(--surface-soft)}.editor-actions{justify-content:flex-end}.bot-plugins fieldset{border:1px solid var(--line);border-radius:8px;display:grid;gap:4px;max-height:180px;overflow:auto}.bot-plugins .check-row{display:flex;align-items:center;gap:8px;min-height:32px}.check-row input{accent-color:var(--accent)}.app-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}.app-card{align-content:start}.app-account{display:flex;align-items:center;justify-content:space-between;gap:8px;border-top:1px solid var(--line);padding-top:8px}.app-account span{min-width:0;overflow-wrap:anywhere}.app-account small{display:block;color:var(--text-muted);margin-top:3px}.badge{font-size:11px;color:var(--text-muted);white-space:nowrap}.badge.enabled{color:var(--accent)}.endpoint{overflow-wrap:anywhere}.plugin-error,.plugin-notice{padding:12px;border-radius:8px;background:var(--surface-soft);margin:0;line-height:1.6}.plugin-error{color:var(--danger)}.plugin-empty{text-align:center;color:var(--text-secondary);padding:28px 12px;line-height:1.8}.connection-settings{display:block}.connection-settings summary{cursor:pointer;font-weight:600;line-height:1.8}.connection-settings summary span{float:right;font-weight:400;color:var(--text-muted)}.connection-settings p{margin:12px 0}.connection-settings form{display:grid;gap:12px}.grant-editor{display:grid;gap:8px}@media(max-width:767px){.app-grid{grid-template-columns:1fr}.bot-plugins button{min-height:44px}.bot-plugins .check-row{min-height:44px}}\n</style>\n\n<style scoped>\n.bot-plugins{gap:22px}.intro{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--text-muted)}.plugin-tabs{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:4px;padding:4px;border:1px solid var(--line);border-radius:12px;background:var(--surface-soft)}.plugin-tabs button{min-height:38px;padding:7px 6px;border:0;border-radius:8px;background:transparent;color:var(--text-secondary);font-size:12px;white-space:nowrap}.plugin-tabs button[aria-selected=true]{background:var(--surface);color:var(--text-primary);font-weight:600;box-shadow:0 1px 4px #0000000b}.connection-settings{padding:18px;border-radius:13px}.app-card{padding:18px;border-radius:13px}.app-grid{gap:12px}.app-card>.hint{line-height:1.7}.plugin-search input{border-radius:10px}.plugin-toolbar .view-tabs button{min-height:36px;font-size:12px}.plugin-toolbar>button{border-color:transparent}@media(max-width:600px){.plugin-tabs{gap:2px;padding:3px;display:flex;flex-wrap:wrap}.plugin-tabs button{flex:1 1 85px}.plugin-tabs button{gap:5px;min-height:44px;font-size:11px}.plugin-tabs button>svg{width:14px;height:14px}.bot-plugins{gap:18px}}\n</style>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport StandaloneDialog from '@/components/common/StandaloneDialog.vue'\nimport BotPluginsPanel from './BotPluginsPanel.vue'\nconst emit = defineEmits<{ close: [] }>()\nconst dirty = ref(false)\nconst beforeClose = () => !dirty.value || window.confirm('放弃尚未保存的插件更改?')\n</script>\n<template><StandaloneDialog title=\"已连接应用\" :before-close=\"beforeClose\" @close=\"emit('close')\"><BotPluginsPanel @dirty-change=\"dirty = $event\" /></StandaloneDialog></template>\n","<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport StandaloneDialog from '@/components/common/StandaloneDialog.vue'\nimport BotPluginsPanel from './BotPluginsPanel.vue'\nconst emit = defineEmits<{ close: [] }>()\nconst dirty = ref(false)\nconst beforeClose = () => !dirty.value || window.confirm('放弃尚未保存的插件更改?')\n</script>\n<template><StandaloneDialog title=\"已连接应用\" :before-close=\"beforeClose\" @close=\"emit('close')\"><BotPluginsPanel @dirty-change=\"dirty = $event\" /></StandaloneDialog></template>\n"],"mappings":"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"}