@leterhong/multichat 1.6.0

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 (214) hide show
  1. package/.agents/plugins/marketplace.json +32 -0
  2. package/.agents/plugins/plugins/developer-toolkit/.codex-plugin/plugin.json +24 -0
  3. package/.agents/plugins/plugins/developer-toolkit/skills/code-review/SKILL.md +9 -0
  4. package/.agents/plugins/plugins/developer-toolkit/skills/code-review/agents/openai.yaml +3 -0
  5. package/.agents/plugins/plugins/developer-toolkit/skills/release-notes/SKILL.md +9 -0
  6. package/.agents/plugins/plugins/developer-toolkit/skills/release-notes/agents/openai.yaml +3 -0
  7. package/.agents/plugins/plugins/developer-toolkit/skills/security-review/SKILL.md +9 -0
  8. package/.agents/plugins/plugins/developer-toolkit/skills/security-review/agents/openai.yaml +3 -0
  9. package/.agents/plugins/plugins/developer-toolkit/skills/test-plan/SKILL.md +9 -0
  10. package/.agents/plugins/plugins/developer-toolkit/skills/test-plan/agents/openai.yaml +3 -0
  11. package/.agents/plugins/plugins/writing-toolkit/.codex-plugin/plugin.json +24 -0
  12. package/.agents/plugins/plugins/writing-toolkit/skills/meeting-notes/SKILL.md +9 -0
  13. package/.agents/plugins/plugins/writing-toolkit/skills/meeting-notes/agents/openai.yaml +3 -0
  14. package/.agents/plugins/plugins/writing-toolkit/skills/resume-tune/SKILL.md +9 -0
  15. package/.agents/plugins/plugins/writing-toolkit/skills/resume-tune/agents/openai.yaml +3 -0
  16. package/.agents/plugins/plugins/writing-toolkit/skills/weekly-report/SKILL.md +9 -0
  17. package/.agents/plugins/plugins/writing-toolkit/skills/weekly-report/agents/openai.yaml +3 -0
  18. package/.agents/skills/.gitkeep +1 -0
  19. package/.agents/skills/wanghong-handwritten-ppt/SKILL.md +172 -0
  20. package/.agents/skills/wanghong-handwritten-ppt/assets/HTML-PPT-LICENSE +21 -0
  21. package/.agents/skills/wanghong-handwritten-ppt/assets/NEAT-ANNOTATIONS-LICENSE +21 -0
  22. package/.agents/skills/wanghong-handwritten-ppt/assets/animations.css +138 -0
  23. package/.agents/skills/wanghong-handwritten-ppt/assets/base.css +150 -0
  24. package/.agents/skills/wanghong-handwritten-ppt/assets/deepseek-example.css +893 -0
  25. package/.agents/skills/wanghong-handwritten-ppt/assets/neat-annotations.css +152 -0
  26. package/.agents/skills/wanghong-handwritten-ppt/assets/preview-cover.png +0 -0
  27. package/.agents/skills/wanghong-handwritten-ppt/assets/preview-diagram.png +0 -0
  28. package/.agents/skills/wanghong-handwritten-ppt/assets/preview-summary.png +0 -0
  29. package/.agents/skills/wanghong-handwritten-ppt/assets/preview-table.png +0 -0
  30. package/.agents/skills/wanghong-handwritten-ppt/assets/runtime.js +960 -0
  31. package/.agents/skills/wanghong-handwritten-ppt/assets/template.css +198 -0
  32. package/.agents/skills/wanghong-handwritten-ppt/evals/evals.json +20 -0
  33. package/.agents/skills/wanghong-handwritten-ppt/examples/deepseek-v4-flash/index.html +414 -0
  34. package/.agents/skills/wanghong-handwritten-ppt/examples/deepseek-v4-flash/style.css +897 -0
  35. package/.agents/skills/wanghong-handwritten-ppt/references/prompts.md +170 -0
  36. package/.agents/skills/wanghong-handwritten-ppt/references/style-guide.md +133 -0
  37. package/.agents/skills/wanghong-handwritten-ppt/scripts/check_deck.py +41 -0
  38. package/.agents/skills/wanghong-handwritten-ppt/scripts/prepare_render_html.py +137 -0
  39. package/.agents/skills/wanghong-handwritten-ppt/scripts/render.sh +110 -0
  40. package/.agents/skills/wanghong-handwritten-ppt/templates/deck.html +94 -0
  41. package/.agents/skills/wanghong-handwritten-ppt/tests/test_font_contract.py +73 -0
  42. package/LICENSE +21 -0
  43. package/README.md +186 -0
  44. package/backend/adapters/README.md +77 -0
  45. package/backend/adapters/anthropic.ts +115 -0
  46. package/backend/adapters/base.ts +47 -0
  47. package/backend/adapters/index.ts +41 -0
  48. package/backend/adapters/lmstudio.ts +7 -0
  49. package/backend/adapters/moonshot.ts +7 -0
  50. package/backend/adapters/ollama.ts +7 -0
  51. package/backend/adapters/openai-compatible.ts +51 -0
  52. package/backend/adapters/openai.ts +7 -0
  53. package/backend/adapters/qwen.ts +112 -0
  54. package/backend/adapters/wenxin.ts +136 -0
  55. package/backend/adapters/zhipu.ts +93 -0
  56. package/backend/dist/adapters/anthropic.js +130 -0
  57. package/backend/dist/adapters/anthropic.js.map +1 -0
  58. package/backend/dist/adapters/base.js +38 -0
  59. package/backend/dist/adapters/base.js.map +1 -0
  60. package/backend/dist/adapters/index.js +39 -0
  61. package/backend/dist/adapters/index.js.map +1 -0
  62. package/backend/dist/adapters/lmstudio.js +8 -0
  63. package/backend/dist/adapters/lmstudio.js.map +1 -0
  64. package/backend/dist/adapters/moonshot.js +8 -0
  65. package/backend/dist/adapters/moonshot.js.map +1 -0
  66. package/backend/dist/adapters/ollama.js +8 -0
  67. package/backend/dist/adapters/ollama.js.map +1 -0
  68. package/backend/dist/adapters/openai-compatible.js +47 -0
  69. package/backend/dist/adapters/openai-compatible.js.map +1 -0
  70. package/backend/dist/adapters/openai.js +8 -0
  71. package/backend/dist/adapters/openai.js.map +1 -0
  72. package/backend/dist/adapters/qwen.js +103 -0
  73. package/backend/dist/adapters/qwen.js.map +1 -0
  74. package/backend/dist/adapters/wenxin.js +114 -0
  75. package/backend/dist/adapters/wenxin.js.map +1 -0
  76. package/backend/dist/adapters/zhipu.js +86 -0
  77. package/backend/dist/adapters/zhipu.js.map +1 -0
  78. package/backend/dist/extensions/demo-mcp-server.js +75 -0
  79. package/backend/dist/extensions/demo-mcp-server.js.map +1 -0
  80. package/backend/dist/extensions/importer.js +828 -0
  81. package/backend/dist/extensions/importer.js.map +1 -0
  82. package/backend/dist/extensions/manager.js +1154 -0
  83. package/backend/dist/extensions/manager.js.map +1 -0
  84. package/backend/dist/lib/catalog.js +30 -0
  85. package/backend/dist/lib/catalog.js.map +1 -0
  86. package/backend/dist/lib/context.js +59 -0
  87. package/backend/dist/lib/context.js.map +1 -0
  88. package/backend/dist/lib/errors.js +75 -0
  89. package/backend/dist/lib/errors.js.map +1 -0
  90. package/backend/dist/lib/provider-store.js +116 -0
  91. package/backend/dist/lib/provider-store.js.map +1 -0
  92. package/backend/dist/lib/redact.js +11 -0
  93. package/backend/dist/lib/redact.js.map +1 -0
  94. package/backend/dist/lib/runtime-store.js +28 -0
  95. package/backend/dist/lib/runtime-store.js.map +1 -0
  96. package/backend/dist/lib/sqlite-store.js +218 -0
  97. package/backend/dist/lib/sqlite-store.js.map +1 -0
  98. package/backend/dist/lib/sse.js +46 -0
  99. package/backend/dist/lib/sse.js.map +1 -0
  100. package/backend/dist/lib/ssrf.js +131 -0
  101. package/backend/dist/lib/ssrf.js.map +1 -0
  102. package/backend/dist/lib/store.js +84 -0
  103. package/backend/dist/lib/store.js.map +1 -0
  104. package/backend/dist/lib/usage.js +219 -0
  105. package/backend/dist/lib/usage.js.map +1 -0
  106. package/backend/dist/lib/util.js +38 -0
  107. package/backend/dist/lib/util.js.map +1 -0
  108. package/backend/dist/lib/workspace-store.js +152 -0
  109. package/backend/dist/lib/workspace-store.js.map +1 -0
  110. package/backend/dist/mcp.js +429 -0
  111. package/backend/dist/mcp.js.map +1 -0
  112. package/backend/dist/routes/agents.js +51 -0
  113. package/backend/dist/routes/agents.js.map +1 -0
  114. package/backend/dist/routes/assistants.js +32 -0
  115. package/backend/dist/routes/assistants.js.map +1 -0
  116. package/backend/dist/routes/chat.js +697 -0
  117. package/backend/dist/routes/chat.js.map +1 -0
  118. package/backend/dist/routes/control-plane.js +200 -0
  119. package/backend/dist/routes/control-plane.js.map +1 -0
  120. package/backend/dist/routes/conversations.js +66 -0
  121. package/backend/dist/routes/conversations.js.map +1 -0
  122. package/backend/dist/routes/extensions-import.js +22 -0
  123. package/backend/dist/routes/extensions-import.js.map +1 -0
  124. package/backend/dist/routes/import.js +85 -0
  125. package/backend/dist/routes/import.js.map +1 -0
  126. package/backend/dist/routes/mcp-servers.js +56 -0
  127. package/backend/dist/routes/mcp-servers.js.map +1 -0
  128. package/backend/dist/routes/meta.js +37 -0
  129. package/backend/dist/routes/meta.js.map +1 -0
  130. package/backend/dist/routes/mock.js +28 -0
  131. package/backend/dist/routes/mock.js.map +1 -0
  132. package/backend/dist/routes/plugins.js +41 -0
  133. package/backend/dist/routes/plugins.js.map +1 -0
  134. package/backend/dist/routes/providers.js +197 -0
  135. package/backend/dist/routes/providers.js.map +1 -0
  136. package/backend/dist/routes/runs.js +57 -0
  137. package/backend/dist/routes/runs.js.map +1 -0
  138. package/backend/dist/routes/skills.js +73 -0
  139. package/backend/dist/routes/skills.js.map +1 -0
  140. package/backend/dist/routes/tools.js +40 -0
  141. package/backend/dist/routes/tools.js.map +1 -0
  142. package/backend/dist/routes/usage.js +14 -0
  143. package/backend/dist/routes/usage.js.map +1 -0
  144. package/backend/dist/routes/workspaces.js +158 -0
  145. package/backend/dist/routes/workspaces.js.map +1 -0
  146. package/backend/dist/runtime/agent.js +518 -0
  147. package/backend/dist/runtime/agent.js.map +1 -0
  148. package/backend/dist/server.js +103 -0
  149. package/backend/dist/server.js.map +1 -0
  150. package/backend/dist/types.js +3 -0
  151. package/backend/dist/types.js.map +1 -0
  152. package/backend/extensions/demo-mcp-server.js +70 -0
  153. package/backend/extensions/importer.ts +709 -0
  154. package/backend/extensions/manager.ts +1055 -0
  155. package/backend/lib/catalog.ts +28 -0
  156. package/backend/lib/context.ts +61 -0
  157. package/backend/lib/errors.ts +78 -0
  158. package/backend/lib/provider-store.ts +116 -0
  159. package/backend/lib/redact.ts +9 -0
  160. package/backend/lib/runtime-store.ts +38 -0
  161. package/backend/lib/sqlite-store.ts +220 -0
  162. package/backend/lib/sse.ts +42 -0
  163. package/backend/lib/ssrf.ts +120 -0
  164. package/backend/lib/store.ts +79 -0
  165. package/backend/lib/usage.ts +212 -0
  166. package/backend/lib/util.ts +31 -0
  167. package/backend/lib/workspace-store.ts +150 -0
  168. package/backend/mcp.ts +417 -0
  169. package/backend/package.json +34 -0
  170. package/backend/prompts.json +82 -0
  171. package/backend/routes/agents.ts +55 -0
  172. package/backend/routes/assistants.ts +30 -0
  173. package/backend/routes/chat.ts +660 -0
  174. package/backend/routes/control-plane.ts +165 -0
  175. package/backend/routes/conversations.ts +62 -0
  176. package/backend/routes/extensions-import.ts +15 -0
  177. package/backend/routes/import.ts +72 -0
  178. package/backend/routes/mcp-servers.ts +36 -0
  179. package/backend/routes/meta.ts +38 -0
  180. package/backend/routes/mock.ts +25 -0
  181. package/backend/routes/plugins.ts +27 -0
  182. package/backend/routes/providers.ts +166 -0
  183. package/backend/routes/runs.ts +55 -0
  184. package/backend/routes/skills.ts +52 -0
  185. package/backend/routes/tools.ts +40 -0
  186. package/backend/routes/usage.ts +13 -0
  187. package/backend/routes/workspaces.ts +124 -0
  188. package/backend/runtime/agent.ts +474 -0
  189. package/backend/server.ts +106 -0
  190. package/backend/types.ts +126 -0
  191. package/bin/multichat.mjs +535 -0
  192. package/frontend/dist/assets/agentPicker-C39VzmYa.js +13 -0
  193. package/frontend/dist/assets/compare-BpaGD32J.js +1 -0
  194. package/frontend/dist/assets/conversations-COuLmg99.js +7 -0
  195. package/frontend/dist/assets/data-ZH7Dux1K.js +1 -0
  196. package/frontend/dist/assets/extensionImport-CnTWKtWm.js +28 -0
  197. package/frontend/dist/assets/importExport-B3mB-9zs.js +70 -0
  198. package/frontend/dist/assets/index-CASVCOUO.js +34 -0
  199. package/frontend/dist/assets/index-DSvbjNLc.css +1 -0
  200. package/frontend/dist/assets/init-ZPK483vB.js +1 -0
  201. package/frontend/dist/assets/jsx-runtime-ZSsbr0oW.js +9 -0
  202. package/frontend/dist/assets/modal-CgHocv3n.js +2 -0
  203. package/frontend/dist/assets/modelPicker-EUmmVnbj.js +14 -0
  204. package/frontend/dist/assets/pluginsUI-B6jGUQkx.js +16 -0
  205. package/frontend/dist/assets/render-DgAg1K8A.js +1 -0
  206. package/frontend/dist/assets/send-BJhZ_5YA.js +14 -0
  207. package/frontend/dist/assets/settings-DdnYwZl-.js +2 -0
  208. package/frontend/dist/assets/shell-DFV2khQl.js +1 -0
  209. package/frontend/dist/assets/workbench-BY2J4vWC.js +41 -0
  210. package/frontend/dist/icon-maskable.svg +4 -0
  211. package/frontend/dist/icon.svg +4 -0
  212. package/frontend/dist/index.html +21 -0
  213. package/frontend/dist/manifest.webmanifest +26 -0
  214. package/package.json +58 -0
@@ -0,0 +1,2 @@
1
+ import{i as e,s as t,t as n}from"./jsx-runtime-ZSsbr0oW.js";import{_ as r,a as i,b as a,d as o,f as s,g as c,h as l,i as u,l as d,m as f,n as p,p as m,r as h,u as g,x as _}from"./index-CASVCOUO.js";import{applyProjectDefaults as v,renderFileContext as y}from"./conversations-COuLmg99.js";import{loadAgents as b,loadCapabilities as x,loadProjectControlData as S,loadProjects as C,loadProviders as w,loadRuns as T,loadSkills as E,loadTools as D,loadUsage as O,loadWorkspaces as k}from"./data-ZH7Dux1K.js";import{showExtensionImport as ee}from"./extensionImport-CnTWKtWm.js";import{doImport as te,exportEntity as ne,normalizeImport as re,showAddBuiltin as ie,showAddCustom as ae,showAgentModal as oe,showSkillModal as A}from"./importExport-B3mB-9zs.js";import{closeModal as j,showModal as M}from"./modal-CgHocv3n.js";import{renderTopbar as N}from"./modelPicker-EUmmVnbj.js";import{mountExtensionSettings as P,renderMcpServers as se,renderPlugins as ce,showDiff as le,sourceLabel as ue,t as F,unmountExtensionSettings as de}from"./pluginsUI-B6jGUQkx.js";import{renderContent as I}from"./render-DgAg1K8A.js";var L=_(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]),R=_(`file-pen-line`,[[`path`,{d:`m18 5-2.414-2.414A2 2 0 0 0 14.172 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2`,key:`142zxg`}],[`path`,{d:`M21.378 12.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z`,key:`2t3380`}],[`path`,{d:`M8 18h1`,key:`13wk12`}]]),fe=_(`pen-line`,[[`path`,{d:`M13 21h8`,key:`1jsn5i`}],[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}]]),z=_(`plus`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`M12 5v14`,key:`s699le`}]]),B=_(`refresh-cw`,[[`path`,{d:`M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8`,key:`v9h5vc`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}],[`path`,{d:`M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16`,key:`3uifl3`}],[`path`,{d:`M8 16H3v5`,key:`1cv678`}]]),pe=_(`shield-check`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]),V=_(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]),H=t(e(),1),U=n();function me({params:e,theme:t,token:n,onSaveParams:r,onTheme:i,onSaveToken:a}){let[o,s]=(0,H.useState)(Number(e.temperature)),[c,l]=(0,H.useState)(Number(e.max_tokens)),[u,d]=(0,H.useState)(Number(e.top_p)),[f,p]=(0,H.useState)(n);return(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(`h3`,{children:`偏好设置`}),(0,U.jsx)(`p`,{className:`lead`,children:`模型参数与外观`}),(0,U.jsxs)(`section`,{className:`provider-card`,children:[(0,U.jsx)(`h4`,{children:`模型参数`}),(0,U.jsx)(`div`,{className:`pmeta`,children:`应用于每次对话请求(OpenAI 兼容)`}),(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsxs)(`label`,{htmlFor:`pTempReact`,children:[`温度 Temperature:`,o]}),(0,U.jsx)(`input`,{id:`pTempReact`,type:`range`,min:`0`,max:`2`,step:`0.1`,value:o,onChange:e=>s(Number(e.target.value))})]}),(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsx)(`label`,{htmlFor:`pMaxReact`,children:`最大输出 Token`}),(0,U.jsx)(`input`,{id:`pMaxReact`,type:`number`,min:`1`,max:`128000`,value:c,onChange:e=>l(Number(e.target.value))})]}),(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsxs)(`label`,{htmlFor:`pTopReact`,children:[`Top P:`,u]}),(0,U.jsx)(`input`,{id:`pTopReact`,type:`range`,min:`0`,max:`1`,step:`0.05`,value:u,onChange:e=>d(Number(e.target.value))})]}),(0,U.jsxs)(`div`,{className:`provider-row`,children:[(0,U.jsx)(`button`,{className:`btn-ghost`,type:`button`,onClick:()=>{s(.7),l(2e3),d(1)},children:`重置`}),(0,U.jsx)(`button`,{className:`btn-primary compact-action`,type:`button`,onClick:()=>r({temperature:o,max_tokens:c||2e3,top_p:u}),children:`保存`})]})]}),(0,U.jsxs)(`section`,{className:`provider-card`,children:[(0,U.jsx)(`h4`,{children:`外观`}),(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsx)(`label`,{htmlFor:`themeReact`,children:`主题`}),(0,U.jsxs)(`select`,{id:`themeReact`,value:t,onChange:e=>i(e.target.value),children:[(0,U.jsx)(`option`,{value:`light`,children:`浅色`}),(0,U.jsx)(`option`,{value:`dark`,children:`深色`}),(0,U.jsx)(`option`,{value:`system`,children:`跟随系统`})]})]})]}),(0,U.jsxs)(`section`,{className:`provider-card`,children:[(0,U.jsx)(`h4`,{children:`本地服务访问保护`}),(0,U.jsx)(`div`,{className:`pmeta`,children:`仅在服务启用 MULTICHAT_API_TOKEN(兼容 MC_API_TOKEN)时填写;只保存在当前浏览器。`}),(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsx)(`label`,{htmlFor:`serverTokenReact`,children:`访问令牌`}),(0,U.jsx)(`input`,{id:`serverTokenReact`,type:`password`,value:f,onChange:e=>p(e.target.value),autoComplete:`off`,placeholder:`未启用则留空`})]}),(0,U.jsxs)(`div`,{className:`provider-row`,children:[(0,U.jsx)(`button`,{className:`btn-ghost`,type:`button`,onClick:()=>{p(``),a(``)},children:`清除`}),(0,U.jsx)(`button`,{className:`btn-primary compact-action`,type:`button`,onClick:()=>a(f.trim()),children:`保存令牌`})]})]}),(0,U.jsxs)(`section`,{className:`provider-card`,children:[(0,U.jsx)(`h4`,{children:`关于`}),(0,U.jsxs)(`div`,{className:`pmeta`,children:[`MultiChat · 本地优先的多模型工作台`,(0,U.jsx)(`br`,{}),`模型、Skills、MCP 与插件按运行配置组合。`]})]})]})}function he({provider:e,onSave:t,onDelete:n,onProbe:r}){let i=e.models||(e.model?[e.model]:[]),a=[`ollama`,`lmstudio`,`mock`].includes((e.apiType||``).toLowerCase())||e.id===`mock`,o=!!(e.apiKeyMasked||e.apiKey)||a,[s,c]=(0,H.useState)(``),[l,u]=(0,H.useState)(i.join(`, `)),[d,f]=(0,H.useState)(!!e.allowPrivate),[p,m]=(0,H.useState)(!1),[h,g]=(0,H.useState)(``),_=async()=>{m(!0),g(``);try{let t=await r(e),n=Array.isArray(t.models)?t.models:[];n.length?l.trim()?g(`✓ 连接成功 · 服务端返回 ${n.length} 个模型`):(u(n.join(`, `)),g(`✓ 连接成功 · 发现 ${n.length} 个模型,已填入列表,记得保存`)):g(`✓ 连接成功,但服务未返回模型清单;可手动填写`)}catch(e){g(`✗ ${e.message||`连接失败`}`)}finally{m(!1)}};return(0,U.jsxs)(`article`,{className:`provider-card provider-config-card`,children:[(0,U.jsxs)(`div`,{className:`provider-card-head`,children:[(0,U.jsx)(`span`,{className:`provider-mark`,"aria-hidden":!0,children:(e.name||e.id).trim().slice(0,1).toUpperCase()||`M`}),(0,U.jsxs)(`div`,{className:`provider-identity`,children:[(0,U.jsxs)(`h4`,{children:[e.name||e.id,` `,(0,U.jsx)(`span`,{className:`provider-id`,children:e.id})]}),(0,U.jsxs)(`div`,{className:`pmeta`,children:[e.apiType||`openai`,` · `,i.length?`${i.length} 个模型`:`手动输入模型`]})]}),(0,U.jsx)(`span`,{className:`provider-state ${o?a?`local`:`ready`:`missing`}`,children:o?a?`本地`:`已配置`:`待配置`})]}),(0,U.jsxs)(`div`,{className:`provider-card-fields`,children:[(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsx)(`label`,{htmlFor:`provider-key-r-${e.id}`,children:`API 密钥`}),(0,U.jsx)(`input`,{id:`provider-key-r-${e.id}`,type:`password`,value:s,onChange:e=>c(e.target.value),placeholder:e.apiKeyMasked?`已安全保存 ····${e.apiKeyPreview||``}`:a?`本地提供方可留空`:`输入 API 密钥`,autoComplete:`new-password`})]}),(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsx)(`label`,{htmlFor:`provider-models-r-${e.id}`,children:`模型列表`}),(0,U.jsx)(`textarea`,{id:`provider-models-r-${e.id}`,rows:1,value:l,onChange:e=>u(e.target.value)})]})]}),(0,U.jsxs)(`div`,{className:`provider-card-footer`,children:[(0,U.jsxs)(`label`,{className:`provider-private`,children:[(0,U.jsx)(`input`,{type:`checkbox`,checked:d,onChange:e=>f(e.target.checked)}),` 允许访问本机 / 内网`]}),(0,U.jsxs)(`div`,{className:`provider-row`,children:[(0,U.jsx)(`button`,{className:`btn-ghost`,type:`button`,disabled:p,onClick:()=>void _(),children:p?`测试中…`:`测试连接`}),(0,U.jsx)(`button`,{className:`btn-ghost danger-ghost`,type:`button`,onClick:()=>void n(e),children:`删除`}),(0,U.jsx)(`button`,{className:`btn-primary`,type:`button`,onClick:()=>void t(e,{...s?{apiKey:s}:{},models:l.split(/[,\n]/).map(e=>e.trim()).filter(Boolean),allowPrivate:d}),children:`保存`})]})]}),h&&(0,U.jsx)(`div`,{className:`provider-probe-result`,role:`status`,children:h})]})}function ge({providers:e,onSave:t,onDelete:n,onAddBuiltin:r,onAddCustom:i,onProbe:a}){let[o,s]=(0,H.useState)(e),c=o.reduce((e,t)=>e+(t.models?.length||+!!t.model),0),l=o.filter(e=>!!(e.apiKeyMasked||e.apiKey)||[`ollama`,`lmstudio`,`mock`].includes((e.apiType||``).toLowerCase())).length;return(0,U.jsxs)(`section`,{className:`provider-settings`,children:[(0,U.jsxs)(`div`,{className:`settings-page-heading`,children:[(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`h3`,{children:`模型连接`}),(0,U.jsx)(`p`,{className:`lead`,children:`管理提供方凭证和可选模型;所有配置仅保存在本地。`})]}),(0,U.jsxs)(`div`,{className:`provider-summary`,"aria-label":`模型配置概览`,children:[(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`strong`,{children:o.length}),` 个提供方`]}),(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`strong`,{children:c}),` 个模型`]}),(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`strong`,{children:l}),` 个可用`]})]})]}),(0,U.jsxs)(`div`,{className:`provider-grid`,children:[o.map(e=>(0,U.jsx)(he,{provider:e,onSave:t,onProbe:a,onDelete:async e=>{await n(e)&&s(t=>t.filter(t=>t.id!==e.id))}},e.id)),!o.length&&(0,U.jsx)(`div`,{className:`provider-empty`,children:`还没有添加任何模型,点击下方按钮开始配置。`})]}),(0,U.jsxs)(`div`,{className:`add-provider provider-add-grid`,children:[(0,U.jsxs)(`button`,{type:`button`,className:`add-tile`,onClick:r,children:[(0,U.jsx)(z,{className:`ico`,"aria-hidden":!0}),(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`strong`,{children:`添加提供方`}),(0,U.jsx)(`small`,{children:`从内置模板快速配置`})]})]}),(0,U.jsxs)(`button`,{type:`button`,className:`add-tile`,onClick:i,children:[(0,U.jsx)(z,{className:`ico`,"aria-hidden":!0}),(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`strong`,{children:`自定义提供方`}),(0,U.jsx)(`small`,{children:`接入 OpenAI 兼容服务`})]})]})]})]})}function _e({initialSkills:e,sourceLabel:t,onToggle:n,onEdit:i,onDiff:a,onDelete:o,onImport:s,onAdd:c}){let[l,u]=(0,H.useState)(e),[d,f]=(0,H.useState)(``),[p,m]=(0,H.useState)(``),[h,g]=(0,H.useState)(``),_=(0,H.useMemo)(()=>[...new Set(l.map(e=>e.source?.kind||`managed`))],[l]),v=l.filter(e=>(!d||[e.name,e.id,e.description,t(e.source)].filter(Boolean).join(` `).toLowerCase().includes(d.toLowerCase()))&&(!p||(e.source?.kind||`managed`)===p)&&(!h||(e.enabled?`enabled`:`disabled`)===h));return(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(`h3`,{children:`Agent Skills`}),(0,U.jsxs)(`p`,{className:`lead`,children:[`每个 Skill 都是一个以 `,(0,U.jsx)(`code`,{children:`SKILL.md`}),` 为入口的目录,可附带 scripts、references 和 assets;这里的开关控制 MultiChat 是否使用它。`]}),(0,U.jsxs)(`div`,{className:`resource-toolbar`,"aria-label":`筛选 Skills`,children:[(0,U.jsxs)(`label`,{className:`resource-search`,children:[(0,U.jsx)(r,{size:15,"aria-hidden":!0}),(0,U.jsx)(`input`,{type:`search`,"aria-label":`搜索 Skills`,placeholder:`搜索名称、说明或 ID`,value:d,onChange:e=>f(e.target.value)})]}),(0,U.jsxs)(`select`,{"aria-label":`按来源筛选`,value:p,onChange:e=>m(e.target.value),children:[(0,U.jsx)(`option`,{value:``,children:`全部来源`}),_.map(e=>(0,U.jsx)(`option`,{value:e,children:t({kind:e})},e))]}),(0,U.jsxs)(`select`,{"aria-label":`按状态筛选`,value:h,onChange:e=>g(e.target.value),children:[(0,U.jsx)(`option`,{value:``,children:`全部状态`}),(0,U.jsx)(`option`,{value:`enabled`,children:`已启用`}),(0,U.jsx)(`option`,{value:`disabled`,children:`已停用`})]}),(0,U.jsxs)(`span`,{className:`resource-count`,children:[v.length,` / `,l.length,` 个 Skills`]})]}),(0,U.jsxs)(`div`,{className:`card-grid`,children:[v.map(e=>{let r=e.key||e.id,s=[`managed`,`repo`].includes(e.source?.kind);return(0,U.jsxs)(`article`,{className:`mc-card`,onClick:()=>s&&i(e),children:[(0,U.jsxs)(`div`,{className:`mc-top`,children:[(0,U.jsx)(`div`,{className:`mc-ico`,children:(0,U.jsx)(R,{size:19,"aria-hidden":!0})}),(0,U.jsxs)(`div`,{className:`mc-title-wrap`,children:[(0,U.jsx)(`div`,{className:`mc-title`,children:e.name}),(0,U.jsx)(`div`,{className:`mc-sub`,children:e.id})]})]}),(0,U.jsx)(`div`,{className:`mc-desc`,children:e.description||`(暂无描述)`}),(0,U.jsxs)(`div`,{className:`extension-source`,children:[t(e.source),` · `,e.scope||`project`]}),(0,U.jsx)(`div`,{className:`extension-command`,title:e.path||``,children:e.path||``}),(0,U.jsxs)(`div`,{className:`mc-tags`,children:[(0,U.jsx)(`span`,{className:`mc-tag${e.enabled?` on`:``}`,children:`SKILL.md`}),(0,U.jsxs)(`span`,{className:`mc-tag`,children:[`MultiChat `,e.enabled?`已启用`:`已停用`]}),(e.resources||[]).map(e=>(0,U.jsx)(`span`,{className:`mc-tag`,children:e},e)),e.invalid&&(0,U.jsx)(`span`,{className:`mc-tag danger`,children:`格式无效`})]}),(0,U.jsxs)(`div`,{className:`skill-card-actions`,children:[(0,U.jsxs)(`button`,{type:`button`,className:`mc-toggle${e.enabled?` on`:``}`,"aria-pressed":!!e.enabled,onClick:async t=>{t.stopPropagation(),await n(e)&&u(e=>e.map(e=>(e.key||e.id)===r?{...e,enabled:!e.enabled}:e))},children:[(0,U.jsx)(`span`,{className:`mc-switch`,"aria-hidden":!0}),(0,U.jsx)(`span`,{children:e.enabled?`启用中`:`已停用`})]}),(0,U.jsxs)(`div`,{className:`mc-actions extension-card-actions`,children:[[`repo`,`plugin`].includes(e.source?.kind)&&(0,U.jsx)(`button`,{className:`mc-act`,type:`button`,title:`查看 Git 变更`,onClick:t=>{t.stopPropagation(),a(e)},children:(0,U.jsx)(pe,{size:15,"aria-hidden":!0})}),s&&(0,U.jsx)(`button`,{className:`mc-act`,type:`button`,title:`编辑`,onClick:t=>{t.stopPropagation(),i(e)},children:(0,U.jsx)(R,{size:15,"aria-hidden":!0})}),s&&(0,U.jsx)(`button`,{className:`mc-act danger`,type:`button`,title:`删除`,onClick:async t=>{t.stopPropagation(),await o(e)&&u(e=>e.filter(e=>(e.key||e.id)!==r))},children:(0,U.jsx)(V,{size:15,"aria-hidden":!0})})]})]})]},r)}),!v.length&&(0,U.jsx)(`div`,{className:`extension-empty`,children:`没有符合筛选条件的 Skill。`}),(0,U.jsxs)(`button`,{type:`button`,className:`mc-add`,onClick:s,children:[(0,U.jsx)(F,{size:20,"aria-hidden":!0}),(0,U.jsx)(`span`,{className:`plus-t`,children:`上传 / 导入 Skill`}),(0,U.jsx)(`span`,{className:`mc-sub`,children:`ZIP、SKILL.md 或完整目录`})]}),(0,U.jsxs)(`button`,{type:`button`,className:`mc-add`,onClick:c,children:[(0,U.jsx)(z,{size:20,"aria-hidden":!0}),(0,U.jsx)(`span`,{className:`plus-t`,children:`新建 Skill`})]})]})]})}function ve({initialTools:e,onToggle:t,agents:n=[],onTest:r}){let[i,a]=(0,H.useState)(e),[o,s]=(0,H.useState)(``),[l,u]=(0,H.useState)({}),d=async(e,t)=>{s(e.id);try{let n=await r(e,t),i=n.ok?`✓ ${e.type===`calculator`?t+` = `:``}${typeof n.data==`string`?n.data:JSON.stringify(n.data)}(${n.elapsedMs}ms)`:`✗ ${n.error}`;u(t=>({...t,[e.id]:i}))}catch(t){u(n=>({...n,[e.id]:`✗ ${t.message}`}))}finally{s(``)}},f=async e=>{if(e.type===`calculator`){let t=await showPrompt({title:`试跑 · ${e.name}`,label:`数学表达式`,placeholder:`例如 (2 + 3) * 14 / 5`,maxLength:200});if(t==null||!t.trim())return;await d(e,t.trim())}else await d(e)},p=e=>n.filter(t=>(t.toolIds||[]).includes(e.id)).length;return(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(`h3`,{children:`内置工具`}),(0,U.jsx)(`p`,{className:`lead`,children:`这些是 MultiChat 自身实现的函数调用能力。停用后不会暴露给模型。`}),(0,U.jsxs)(`div`,{className:`card-grid`,children:[i.map(e=>{let n=[`datetime`,`calculator`].includes(e.type),r=p(e);return(0,U.jsxs)(`article`,{className:`mc-card`,children:[(0,U.jsxs)(`div`,{className:`mc-top`,children:[(0,U.jsx)(`div`,{className:`mc-ico`,children:(0,U.jsx)(c,{size:19,"aria-hidden":!0})}),(0,U.jsxs)(`div`,{className:`mc-title-wrap`,children:[(0,U.jsx)(`div`,{className:`mc-title`,children:e.name}),(0,U.jsxs)(`div`,{className:`mc-sub`,children:[e.id,` · `,e.type||`function`]})]})]}),(0,U.jsx)(`div`,{className:`mc-desc`,children:e.description||``}),(0,U.jsxs)(`div`,{className:`mc-tags`,children:[(e.permissions||[]).map(e=>(0,U.jsx)(`span`,{className:`mc-tag`,children:e},e)),(0,U.jsx)(`span`,{className:`mc-tag${e.enabled?` on`:``}`,children:e.enabled?`已启用`:`已停用`}),(0,U.jsx)(`span`,{className:`mc-tag${r?` on`:``}`,title:`引用该工具的运行配置数量`,children:r?`${r} 个运行配置引用`:`未被运行配置引用`})]}),(0,U.jsxs)(`div`,{className:`mc-actions extension-card-actions left`,children:[(0,U.jsxs)(`button`,{type:`button`,className:`mc-toggle${e.enabled?` on`:``}`,"aria-pressed":!!e.enabled,onClick:async()=>{await t(e)&&a(t=>t.map(t=>t.id===e.id?{...t,enabled:!t.enabled}:t))},children:[(0,U.jsx)(`span`,{className:`mc-switch`,"aria-hidden":!0}),(0,U.jsx)(`span`,{children:e.enabled?`启用中`:`已停用`})]}),n&&(0,U.jsx)(`button`,{type:`button`,className:`mc-act wide`,disabled:o===e.id,onClick:()=>void f(e),children:o===e.id?`试跑中…`:`试跑`})]}),l[e.id]&&(0,U.jsx)(`div`,{className:`tool-test-result`,role:`status`,children:l[e.id]})]},e.id)}),!i.length&&(0,U.jsx)(`div`,{className:`extension-empty`,children:`没有内置工具。`})]})]})}function W(e){let t=Number(e||0);return t>=1e9?`${(t/1e9).toFixed(t>=1e10?0:1)}B`:t>=1e6?`${(t/1e6).toFixed(t>=1e7?0:1)}M`:t>=1e3?`${(t/1e3).toFixed(t>=1e4?0:1)}K`:t.toLocaleString(`zh-CN`)}function ye(e){let t=p(e=>e.workspaces),n=p(e=>e.projects),i=p(e=>e.agents),a=p(e=>e.providers),o=p(e=>e.assets),s=p(e=>e.memories),c=p(e=>e.snapshots),l=p(e=>e.selectedWorkspace),u=p(e=>e.selectedProject),d=(0,H.useRef)(null),[f,m]=(0,H.useState)(u?.defaultAgentId||``),[h,g]=(0,H.useState)(u?.defaultProviderId&&u?.defaultModel?`${u.defaultProviderId}:${u.defaultModel}`:``),[_,v]=(0,H.useState)(``),[y,b]=(0,H.useState)(!1),[x,S]=(0,H.useState)(!1),[C,w]=(0,H.useState)(``),[T,E]=(0,H.useState)([]);(0,H.useEffect)(()=>{m(u?.defaultAgentId||``),g(u?.defaultProviderId&&u?.defaultModel?`${u.defaultProviderId}:${u.defaultModel}`:``),E([]),S(!1),w(``)},[u?.id,u?.defaultAgentId,u?.defaultProviderId,u?.defaultModel]);let D=a.flatMap(e=>(e.models||[]).map(t=>({value:`${e.id}:${t}`,label:`${e.name||e.id} · ${t}`}))),O=async t=>{if(t.preventDefault(),!(!_.trim()||!u)){b(!0),w(``);try{E(await e.onSearch(_.trim()))}catch(e){E([]),w(e.message)}finally{b(!1),S(!0)}}},k=async()=>{let[t,...n]=h.split(`:`);await e.onSaveDefaults(f||null,t||null,n.join(`:`)||null)};return(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(`h3`,{children:`工作区`}),(0,U.jsx)(`p`,{className:`lead`,children:`按工作区和项目组织会话、文件与运行上下文。文件内容只保存在本地数据目录。`}),(0,U.jsxs)(`section`,{className:`provider-card`,children:[(0,U.jsx)(`h4`,{children:`当前空间`}),(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsx)(`label`,{htmlFor:`workspaceSelectReact`,children:`工作区`}),(0,U.jsx)(`select`,{id:`workspaceSelectReact`,value:l?.id||``,onChange:t=>void e.onWorkspaceChange(t.target.value),children:t.map(e=>(0,U.jsx)(`option`,{value:e.id,children:e.name},e.id))})]}),(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsx)(`label`,{htmlFor:`projectSelectReact`,children:`项目`}),(0,U.jsx)(`select`,{id:`projectSelectReact`,value:u?.id||``,onChange:t=>void e.onProjectChange(t.target.value),children:n.map(e=>(0,U.jsx)(`option`,{value:e.id,children:e.name},e.id))})]}),(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsx)(`label`,{htmlFor:`projectAgentReact`,children:`项目默认运行配置`}),(0,U.jsxs)(`select`,{id:`projectAgentReact`,value:f,onChange:e=>m(e.target.value),children:[(0,U.jsx)(`option`,{value:``,children:`(继承全局)`}),i.map(e=>(0,U.jsx)(`option`,{value:e.id,children:e.name},e.id))]})]}),(0,U.jsxs)(`div`,{className:`field`,children:[(0,U.jsx)(`label`,{htmlFor:`projectModelReact`,children:`项目默认模型`}),(0,U.jsxs)(`select`,{id:`projectModelReact`,value:h,onChange:e=>g(e.target.value),children:[(0,U.jsx)(`option`,{value:``,children:`(继承全局)`}),D.map(e=>(0,U.jsx)(`option`,{value:e.value,children:e.label},e.value))]})]}),(0,U.jsx)(`div`,{className:`provider-row`,children:(0,U.jsx)(`button`,{className:`btn-primary compact-action`,type:`button`,onClick:()=>void k(),children:`保存项目默认`})}),(0,U.jsxs)(`div`,{className:`provider-row`,children:[(0,U.jsx)(`button`,{className:`btn-ghost`,type:`button`,onClick:e.onNewWorkspace,children:`新建工作区`}),(0,U.jsx)(`button`,{className:`btn-ghost`,type:`button`,onClick:e.onNewProject,children:`新建项目`})]})]}),(0,U.jsxs)(`section`,{className:`provider-card`,children:[(0,U.jsx)(`h4`,{children:`项目文件`}),(0,U.jsx)(`div`,{className:`pmeta`,children:`支持本地文本文件和 URL 文本资源;对话时会按相关性截取并附带文件/行号来源。`}),(0,U.jsxs)(`div`,{className:`provider-row workspace-file-actions`,children:[(0,U.jsxs)(`button`,{className:`btn-ghost`,type:`button`,onClick:()=>d.current?.click(),children:[(0,U.jsx)(F,{size:15,"aria-hidden":!0}),` 上传本地文件`]}),(0,U.jsx)(`button`,{className:`btn-ghost`,type:`button`,onClick:e.onImportUrl,children:`从 URL 添加`}),(0,U.jsx)(`input`,{ref:d,type:`file`,accept:`.txt,.md,.json,.csv,.js,.ts,.py,.html,.css,.yaml,.yml`,hidden:!0,onChange:t=>{let n=t.target.files?.[0];n&&e.onUploadFile(n),t.target.value=``}})]}),(0,U.jsxs)(`div`,{className:`run-list workspace-resource-list`,children:[o.map(t=>(0,U.jsxs)(`div`,{className:`run-row`,children:[(0,U.jsx)(`span`,{className:`run-dot completed`}),(0,U.jsxs)(`div`,{className:`run-main`,children:[(0,U.jsx)(`div`,{className:`run-title`,children:t.name}),(0,U.jsxs)(`div`,{className:`run-meta`,children:[t.mimeType,` · `,t.size,` bytes · `,t.source]})]}),(0,U.jsx)(`button`,{className:`mc-act danger`,type:`button`,title:`删除`,"aria-label":`删除文件 ${t.name}`,onClick:()=>void e.onDeleteAsset(t.id),children:(0,U.jsx)(V,{size:15,"aria-hidden":!0})})]},t.id)),!o.length&&(0,U.jsx)(`div`,{className:`control-empty`,children:`当前项目还没有文件。`})]}),(0,U.jsxs)(`form`,{className:`knowledge-search`,onSubmit:O,children:[(0,U.jsxs)(`label`,{className:`resource-search`,children:[(0,U.jsx)(r,{size:15,"aria-hidden":!0}),(0,U.jsx)(`input`,{"aria-label":`搜索项目知识`,value:_,onChange:e=>v(e.target.value),placeholder:`搜索项目知识,例如:认证流程在哪里实现?`})]}),(0,U.jsx)(`button`,{className:`btn-ghost`,disabled:y,children:y?`检索中…`:`搜索知识`})]}),(0,U.jsxs)(`div`,{className:`knowledge-results`,"aria-live":`polite`,children:[C&&(0,U.jsx)(`div`,{className:`auth-error`,children:C}),T.map((e,t)=>(0,U.jsxs)(`article`,{className:`knowledge-hit`,children:[(0,U.jsxs)(`strong`,{children:[e.name,` `,(0,U.jsxs)(`span`,{children:[`L`,e.lineStart,`–L`,e.lineEnd]})]}),(0,U.jsx)(`p`,{children:String(e.snippet||``).slice(0,500)})]},`${e.id||e.name}-${t}`)),x&&!y&&!C&&T.length===0&&(0,U.jsx)(`div`,{className:`control-empty`,children:`没有找到相关片段。`})]})]}),(0,U.jsxs)(`section`,{className:`provider-card`,children:[(0,U.jsxs)(`div`,{className:`control-card-head`,children:[(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`h4`,{children:`项目记忆`}),(0,U.jsx)(`div`,{className:`pmeta`,children:`由你明确维护的事实和偏好;可逐条停用,不会从聊天中偷偷学习。`})]}),(0,U.jsx)(`button`,{className:`btn-ghost`,type:`button`,onClick:e.onAddMemory,children:`新增记忆`})]}),(0,U.jsxs)(`div`,{className:`memory-list`,children:[s.map(t=>(0,U.jsxs)(`article`,{className:`memory-item${t.enabled===!1?` disabled`:``}`,children:[(0,U.jsxs)(`button`,{className:`memory-toggle`,type:`button`,"aria-pressed":t.enabled!==!1,onClick:()=>void e.onToggleMemory(t),children:[(0,U.jsx)(`i`,{}),t.enabled===!1?`已停用`:`已启用`]}),(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`strong`,{children:t.title}),(0,U.jsx)(`p`,{children:t.content})]}),(0,U.jsxs)(`div`,{className:`memory-actions`,children:[(0,U.jsx)(`button`,{className:`mc-act`,type:`button`,onClick:()=>e.onEditMemory(t.id),children:`编辑`}),(0,U.jsx)(`button`,{className:`mc-act danger`,type:`button`,onClick:()=>void e.onDeleteMemory(t.id),children:`删除`})]})]},t.id)),!s.length&&(0,U.jsx)(`div`,{className:`control-empty`,children:`还没有项目记忆。只保存值得长期复用的事实和偏好。`})]})]}),(0,U.jsxs)(`section`,{className:`provider-card`,children:[(0,U.jsxs)(`div`,{className:`control-card-head`,children:[(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`h4`,{children:`项目时光机`}),(0,U.jsx)(`div`,{className:`pmeta`,children:`保存项目设置、知识文件、记忆、默认运行配置和当前 Git 状态;恢复前会自动再备份一次。`})]}),(0,U.jsx)(`button`,{className:`btn-primary compact-action`,type:`button`,onClick:()=>void e.onCreateSnapshot(),children:`创建快照`})]}),(0,U.jsxs)(`div`,{className:`snapshot-list`,children:[c.map(t=>(0,U.jsxs)(`article`,{className:`snapshot-item`,children:[(0,U.jsx)(`span`,{className:`snapshot-mark`,children:t.git?.commit||`LOCAL`}),(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`strong`,{children:t.title}),(0,U.jsxs)(`p`,{children:[new Date(t.createdAt).toLocaleString(`zh-CN`),` · `,t.assets,` 文件 · `,t.memories,` 记忆 · `,W(t.size),`B`,t.git?.branch?` · ${t.git.branch}${t.git.dirty?`(有改动)`:``}`:``]})]}),(0,U.jsxs)(`div`,{className:`memory-actions`,children:[(0,U.jsx)(`button`,{className:`mc-act`,type:`button`,onClick:()=>void e.onRestoreSnapshot(t.id),children:`恢复`}),(0,U.jsx)(`button`,{className:`mc-act danger`,type:`button`,onClick:()=>void e.onDeleteSnapshot(t.id),children:`删除`})]})]},t.id)),!c.length&&(0,U.jsx)(`div`,{className:`control-empty`,children:`尚未创建项目快照。`})]})]})]})}function be({onEdit:e,onExport:t,onDelete:n,onImport:r}){let i=p(e=>e.agents),o=p(e=>e.selectedAgent),s=(0,H.useRef)(null);return(0,U.jsxs)(U.Fragment,{children:[(0,U.jsxs)(`div`,{className:`settings-page-heading`,children:[(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`h3`,{children:`运行配置`}),(0,U.jsx)(`p`,{className:`lead`,children:`把系统提示词、Skills、内置工具和 MCP 服务保存为可复用配置。只有当前配置选中的能力才会进入一次运行。`})]}),(0,U.jsxs)(`div`,{className:`extension-heading-actions`,children:[(0,U.jsxs)(`button`,{className:`btn-ghost`,type:`button`,onClick:()=>s.current?.click(),children:[(0,U.jsx)(F,{size:15,"aria-hidden":!0}),` 导入配置`]}),(0,U.jsxs)(`button`,{className:`btn-primary compact-action`,type:`button`,onClick:()=>e(null),children:[(0,U.jsx)(z,{size:15,"aria-hidden":!0}),` 新建配置`]}),(0,U.jsx)(`input`,{ref:s,type:`file`,accept:`.json,application/json`,hidden:!0,onChange:e=>{let t=e.target.files?.[0];t&&r(t),e.target.value=``}})]})]}),(0,U.jsxs)(`div`,{className:`card-grid`,children:[i.map(r=>{let i=r.skillRefs||r.skillIds||[],s=r.toolIds||[],c=r.mcpServerIds||[];return(0,U.jsxs)(`article`,{className:`mc-card`,onClick:()=>e(r.id),children:[(0,U.jsxs)(`div`,{className:`mc-top`,children:[(0,U.jsx)(`div`,{className:`mc-ico`,children:(0,U.jsx)(a,{size:19,"aria-hidden":!0})}),(0,U.jsxs)(`div`,{className:`mc-title-wrap`,children:[(0,U.jsx)(`div`,{className:`mc-title`,children:r.name}),(0,U.jsx)(`div`,{className:`mc-sub`,children:r.id})]}),o?.id===r.id&&(0,U.jsx)(`span`,{className:`mc-tag on`,children:`使用中`})]}),(0,U.jsx)(`div`,{className:`mc-desc`,children:r.description||`(暂无描述)`}),(0,U.jsxs)(`div`,{className:`mc-tags`,children:[(0,U.jsxs)(`span`,{className:`mc-tag`,children:[i.length,` 个技能`]}),(0,U.jsxs)(`span`,{className:`mc-tag`,children:[s.length,` 个内置工具`]}),(0,U.jsxs)(`span`,{className:`mc-tag`,children:[c.length,` 个 MCP`]}),i.slice(0,3).map(e=>(0,U.jsx)(`span`,{className:`mc-tag`,children:e},e)),i.length>3&&(0,U.jsxs)(`span`,{className:`mc-tag`,children:[`+`,i.length-3]})]}),(0,U.jsxs)(`div`,{className:`mc-actions extension-card-actions`,children:[(0,U.jsx)(`button`,{className:`mc-act`,type:`button`,title:`导出`,"aria-label":`导出 ${r.name}`,onClick:e=>{e.stopPropagation(),t(r)},children:(0,U.jsx)(L,{size:15,"aria-hidden":!0})}),(0,U.jsx)(`button`,{className:`mc-act`,type:`button`,title:`编辑`,"aria-label":`编辑 ${r.name}`,onClick:t=>{t.stopPropagation(),e(r.id)},children:(0,U.jsx)(fe,{size:15,"aria-hidden":!0})}),(0,U.jsx)(`button`,{className:`mc-act danger`,type:`button`,title:`删除`,"aria-label":`删除 ${r.name}`,onClick:e=>{e.stopPropagation(),n(r)},children:(0,U.jsx)(V,{size:15,"aria-hidden":!0})})]})]},r.id)}),!i.length&&(0,U.jsx)(`div`,{className:`extension-empty`,children:`还没有运行配置。`})]})]})}var G={plugin:`插件`,skill:`Skill`,mcp:`MCP`,tool:`内置工具`},xe={high:`高风险`,medium:`中风险`,low:`低风险`};function Se({onRefresh:e}){let t=p(e=>e.capabilities),[n,i]=(0,H.useState)(``),[a,o]=(0,H.useState)(``),[s,c]=(0,H.useState)(``),l=(0,H.useMemo)(()=>(t?.items||[]).filter(e=>{let t=[e.name,e.id,e.source,e.scope,...e.permissions||[]].join(` `).toLowerCase();return(!n||t.includes(n.toLowerCase()))&&(!a||e.type===a)&&(!s||e.risk===s)}),[t,n,a,s]);return t?(0,U.jsxs)(`section`,{className:`passport-page`,children:[(0,U.jsxs)(`div`,{className:`settings-page-heading`,children:[(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`div`,{className:`usage-kicker`,children:`能力治理`}),(0,U.jsx)(`h3`,{children:`能力审计`}),(0,U.jsx)(`p`,{className:`lead`,children:`统一查看每项能力的来源、版本、权限、信任边界和结构完整性。`})]}),(0,U.jsxs)(`button`,{className:`btn-ghost`,type:`button`,onClick:()=>void e(),children:[(0,U.jsx)(B,{size:15,"aria-hidden":!0}),` 重新扫描`]})]}),(0,U.jsxs)(`div`,{className:`passport-summary`,children:[(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`strong`,{children:t.summary.total}),` 项能力`]}),(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`strong`,{children:t.summary.enabled}),` 项启用`]}),(0,U.jsxs)(`span`,{className:t.summary.highRisk?`warn`:``,children:[(0,U.jsx)(`strong`,{children:t.summary.highRisk}),` 项高风险`]}),(0,U.jsxs)(`span`,{className:t.summary.issues?`warn`:``,children:[(0,U.jsx)(`strong`,{children:t.summary.issues}),` 个待处理问题`]})]}),(0,U.jsxs)(`div`,{className:`resource-toolbar passport-toolbar`,children:[(0,U.jsxs)(`label`,{className:`resource-search`,children:[(0,U.jsx)(r,{size:15,"aria-hidden":!0}),(0,U.jsx)(`input`,{type:`search`,"aria-label":`搜索能力`,placeholder:`搜索能力、来源或权限`,value:n,onChange:e=>i(e.target.value)})]}),(0,U.jsxs)(`select`,{"aria-label":`按类型筛选`,value:a,onChange:e=>o(e.target.value),children:[(0,U.jsx)(`option`,{value:``,children:`全部类型`}),Object.entries(G).map(([e,t])=>(0,U.jsx)(`option`,{value:e,children:t},e))]}),(0,U.jsxs)(`select`,{"aria-label":`按风险筛选`,value:s,onChange:e=>c(e.target.value),children:[(0,U.jsx)(`option`,{value:``,children:`全部风险`}),(0,U.jsx)(`option`,{value:`high`,children:`高风险`}),(0,U.jsx)(`option`,{value:`medium`,children:`中风险`}),(0,U.jsx)(`option`,{value:`low`,children:`低风险`})]}),(0,U.jsxs)(`span`,{className:`resource-count`,children:[l.length,` / `,t.items.length,` 项`]})]}),(0,U.jsx)(`div`,{className:`passport-grid`,children:l.map(e=>(0,U.jsxs)(`article`,{className:`passport-card`,children:[(0,U.jsxs)(`div`,{className:`passport-head`,children:[(0,U.jsx)(`span`,{className:`passport-type ${e.type}`,children:G[e.type]||e.type}),(0,U.jsx)(`span`,{className:`passport-risk ${e.risk}`,children:xe[e.risk]||e.risk})]}),(0,U.jsx)(`h4`,{children:e.name}),(0,U.jsx)(`code`,{children:e.id}),(0,U.jsx)(`p`,{children:e.description||`暂无说明`}),(0,U.jsxs)(`dl`,{children:[(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`dt`,{children:`来源`}),(0,U.jsxs)(`dd`,{children:[e.source,` · `,e.scope||`project`]})]}),(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`dt`,{children:`版本 / 完整性`}),(0,U.jsxs)(`dd`,{children:[e.version||`未声明`,` · `,e.integrity||`unknown`]})]}),(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`dt`,{children:`信任`}),(0,U.jsx)(`dd`,{children:e.trust||`unknown`})]})]}),(0,U.jsxs)(`div`,{className:`passport-perms`,children:[(e.permissions||[]).map(e=>(0,U.jsx)(`span`,{children:e},e)),!e.permissions?.length&&(0,U.jsx)(`span`,{className:`none`,children:`无特殊权限`})]}),e.issues?.length?(0,U.jsx)(`div`,{className:`passport-issues`,children:e.issues.map(e=>(0,U.jsx)(`span`,{children:e},e))}):(0,U.jsx)(`div`,{className:`passport-clean`,children:`结构检查通过`})]},`${e.type}:${e.id}`))}),!l.length&&(0,U.jsx)(`div`,{className:`extension-empty`,children:`没有符合筛选条件的能力。`})]}):(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(`h3`,{children:`能力审计`}),(0,U.jsx)(`p`,{className:`lead`,children:`正在生成能力护照…`}),(0,U.jsx)(`div`,{className:`usage-loading`,role:`status`,"aria-label":`正在加载`})]})}var K=[`#6158e8`,`#16aabd`,`#9b77f2`,`#ef9f52`,`#46a575`,`#d65f7a`];function Ce({rows:e=[]}){let t=Math.max(1,...e.map(e=>Number(e.totalTokens||0))),n=710/Math.max(1,e.length),r=e.map((e,r)=>`${38+r*n+n/2},${196-178*Number(e.totalTokens||0)/t}`).join(` `);return(0,U.jsxs)(`svg`,{className:`usage-trend`,viewBox:`0 0 760 226`,role:`img`,"aria-label":`每日 Token 用量趋势`,children:[(0,U.jsx)(`g`,{className:`usage-grid`,children:[0,.5,1].map(e=>(0,U.jsxs)(`g`,{children:[(0,U.jsx)(`line`,{x1:38,x2:748,y1:18+178*(1-e),y2:18+178*(1-e)}),(0,U.jsx)(`text`,{x:`0`,y:18+178*(1-e)+4,children:W(t*e)})]},e))}),e.map((e,r)=>{let i=38+r*n+Math.max(1,n*.16),a=Math.max(2,n*.68),o=178*Number(e.inputTokens||0)/t,s=178*Number(e.outputTokens||0)/t;return(0,U.jsxs)(`g`,{children:[(0,U.jsxs)(`title`,{children:[e.date,` · `,W(e.totalTokens),` tokens`]}),(0,U.jsx)(`rect`,{className:`usage-bar-input`,x:i,y:196-o,width:a,height:o,rx:`2`}),(0,U.jsx)(`rect`,{className:`usage-bar-output`,x:i,y:196-o-s,width:a,height:s,rx:`2`})]},e.date)}),r&&(0,U.jsx)(`polyline`,{className:`usage-line`,points:r}),(0,U.jsx)(`text`,{className:`usage-date`,x:38,y:221,children:e[0]?.date?.slice(5)||``}),(0,U.jsx)(`text`,{className:`usage-date`,x:748,y:221,textAnchor:`end`,children:e[e.length-1]?.date?.slice(5)||``})]})}function we({onRange:e,onExport:t}){let n=p(e=>e.usage),r=p(e=>e.usageLoading),i=p(e=>e.usageRange);if(r&&!n)return(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(`h3`,{children:`用量中心`}),(0,U.jsx)(`p`,{className:`lead`,children:`正在汇总本地运行记录…`}),(0,U.jsx)(`div`,{className:`usage-loading`,role:`status`,"aria-label":`正在加载`})]});let a=n?.totals||{},o=n?.daily||[],s=o[o.length-1]||{},c=n?.models||[],l=n?.providers||[],u=0,d=c.slice(0,6).map((e,t)=>{let n=u;return u+=Number(e.share||0)*360,`${K[t%K.length]} ${n}deg ${u}deg`}).join(`,`);return(0,U.jsxs)(`section`,{className:`usage-page`,children:[(0,U.jsxs)(`div`,{className:`settings-page-heading usage-heading`,children:[(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`div`,{className:`usage-kicker`,children:`本地用量统计`}),(0,U.jsx)(`h3`,{children:`每日 Token 用量`}),(0,U.jsx)(`p`,{className:`lead`,children:`按本机请求汇总;上游未返回 usage 时会明确标记为估算,不读取任何提示词正文。`})]}),(0,U.jsxs)(`div`,{className:`usage-heading-actions`,children:[(0,U.jsx)(`div`,{className:`usage-ranges`,children:[[`7`,`近 7 天`],[`30`,`近 30 天`],[`all`,`全部`]].map(([t,n])=>(0,U.jsx)(`button`,{className:`usage-range${i===t?` active`:``}`,type:`button`,onClick:()=>void e(t),children:n},t))}),(0,U.jsxs)(`button`,{className:`btn-ghost`,type:`button`,onClick:()=>t(n,i),children:[(0,U.jsx)(L,{size:15,"aria-hidden":!0}),` 导出 CSV`]})]})]}),(0,U.jsxs)(`div`,{className:`usage-metrics`,children:[(0,U.jsxs)(`article`,{className:`usage-metric primary`,children:[(0,U.jsx)(`span`,{children:`Token 总量`}),(0,U.jsx)(`strong`,{children:W(a.totalTokens)}),(0,U.jsxs)(`small`,{children:[`输入 `,W(a.inputTokens),` · 输出 `,W(a.outputTokens)]})]}),(0,U.jsxs)(`article`,{className:`usage-metric`,children:[(0,U.jsx)(`span`,{children:`今日用量`}),(0,U.jsx)(`strong`,{children:W(s.totalTokens)}),(0,U.jsxs)(`small`,{children:[s.requests||0,` 次模型请求`]})]}),(0,U.jsxs)(`article`,{className:`usage-metric`,children:[(0,U.jsx)(`span`,{children:`有效交互`}),(0,U.jsx)(`strong`,{children:W(a.messages)}),(0,U.jsxs)(`small`,{children:[a.requests||0,` 次模型调用`]})]}),(0,U.jsxs)(`article`,{className:`usage-metric`,children:[(0,U.jsx)(`span`,{children:`活跃天数`}),(0,U.jsx)(`strong`,{children:a.activeDays||0}),(0,U.jsxs)(`small`,{children:[`连续 `,a.currentStreak||0,` 天 · 最长 `,a.longestStreak||0,` 天`]})]})]}),(0,U.jsxs)(`div`,{className:`usage-insights`,children:[(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`i`,{className:`success`}),`成功率 `,(0,U.jsxs)(`strong`,{children:[((a.successRate??1)*100).toFixed(1),`%`]})]}),(0,U.jsxs)(`span`,{children:[`日均 `,(0,U.jsx)(`strong`,{children:W(a.averagePerActiveDay)})]}),(0,U.jsxs)(`span`,{children:[`峰值时段 `,(0,U.jsxs)(`strong`,{children:[String(a.peakHour||0).padStart(2,`0`),`:00–`,String(((a.peakHour||0)+1)%24).padStart(2,`0`),`:00`]})]}),(0,U.jsxs)(`span`,{children:[`真实上报 `,(0,U.jsxs)(`strong`,{children:[((a.reportedShare||0)*100).toFixed(0),`%`]})]}),a.estimatedTokens?(0,U.jsxs)(`span`,{className:`estimate-note`,children:[`约 `,W(a.estimatedTokens),` Token 为本地估算`]}):null]}),(0,U.jsxs)(`div`,{className:`usage-layout`,children:[(0,U.jsxs)(`article`,{className:`usage-panel usage-wide`,children:[(0,U.jsxs)(`div`,{className:`usage-panel-head`,children:[(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`h4`,{children:`按天趋势`}),(0,U.jsx)(`p`,{children:`深色为输入,浅色为输出;悬停柱形查看当天明细。`})]}),(0,U.jsxs)(`div`,{className:`usage-legend`,children:[(0,U.jsx)(`span`,{className:`input`}),`输入 `,(0,U.jsx)(`span`,{className:`output`}),`输出`]})]}),(0,U.jsx)(Ce,{rows:o})]}),(0,U.jsxs)(`article`,{className:`usage-panel`,children:[(0,U.jsx)(`div`,{className:`usage-panel-head`,children:(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`h4`,{children:`模型用量`}),(0,U.jsx)(`p`,{children:`选定周期内的 Token 占比`})]})}),(0,U.jsxs)(`div`,{className:`usage-model-wrap`,children:[(0,U.jsx)(`div`,{className:`usage-donut`,style:{background:`conic-gradient(${d||`var(--mc-line) 0 360deg`})`},children:(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`strong`,{children:c.length}),(0,U.jsx)(`span`,{children:`模型`})]})}),(0,U.jsxs)(`div`,{className:`usage-model-list`,children:[c.slice(0,6).map((e,t)=>(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`i`,{style:{background:K[t%K.length]}}),(0,U.jsx)(`span`,{title:e.name,children:e.name}),(0,U.jsxs)(`strong`,{children:[(Number(e.share||0)*100).toFixed(1),`%`]}),(0,U.jsx)(`small`,{children:W(e.totalTokens)})]},e.name)),!c.length&&(0,U.jsx)(`p`,{className:`usage-empty`,children:`还没有用量记录`})]})]})]}),(0,U.jsxs)(`article`,{className:`usage-panel usage-wide`,children:[(0,U.jsxs)(`div`,{className:`usage-panel-head`,children:[(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`h4`,{children:`活跃热力图`}),(0,U.jsx)(`p`,{children:`最近 26 周的本地模型调用`})]}),(0,U.jsxs)(`span`,{children:[n?.heatmap?.filter(e=>e.totalTokens>0).length||0,` 个活跃日`]})]}),(0,U.jsx)(`div`,{className:`usage-heatmap`,role:`img`,"aria-label":`最近 26 周活跃情况`,children:(n?.heatmap||[]).map(e=>{let t=e.totalTokens===0?0:e.totalTokens<1e3?1:e.totalTokens<1e4?2:e.totalTokens<1e5?3:4;return(0,U.jsx)(`i`,{"data-level":t,children:(0,U.jsxs)(`span`,{children:[e.date,` · `,W(e.totalTokens),` tokens`]})},e.date)})})]}),(0,U.jsxs)(`article`,{className:`usage-panel`,children:[(0,U.jsx)(`div`,{className:`usage-panel-head`,children:(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`h4`,{children:`提供方健康`}),(0,U.jsx)(`p`,{children:`请求、用量与错误率`})]})}),(0,U.jsxs)(`div`,{className:`usage-provider-list`,children:[l.map(e=>(0,U.jsxs)(`div`,{children:[(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`i`,{}),e.name]}),(0,U.jsx)(`strong`,{children:W(e.totalTokens)}),(0,U.jsxs)(`small`,{children:[e.requests,` 次 · `,e.requests?(e.errors/e.requests*100).toFixed(0):0,`% 错误`]})]},e.id||e.name)),!l.length&&(0,U.jsx)(`p`,{className:`usage-empty`,children:`还没有提供方记录`})]})]})]})]})}function Te({onRefresh:e,onOpen:t}){let n=p(e=>e.runs||[]),r=n.filter(e=>e.status===`completed`).length,i=n.reduce((e,t)=>e+Number(t.usage?.totalTokens||0),0),a=e=>e===`completed`?`已完成`:e===`error`?`失败`:e===`cancelled`?`已取消`:`进行中`;return(0,U.jsxs)(U.Fragment,{children:[(0,U.jsxs)(`div`,{className:`settings-page-heading`,children:[(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`div`,{className:`usage-kicker`,children:`运行记录`}),(0,U.jsx)(`h3`,{children:`执行与复盘`}),(0,U.jsx)(`p`,{className:`lead`,children:`复盘模型请求、工具权限、上下文来源、耗时与 Token;点击记录查看完整上下文。`})]}),(0,U.jsxs)(`button`,{className:`btn-ghost`,type:`button`,onClick:()=>void e(),children:[(0,U.jsx)(B,{size:15,"aria-hidden":!0}),` 刷新`]})]}),(0,U.jsxs)(`div`,{className:`passport-summary`,children:[(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`strong`,{children:n.length}),` 条记录`]}),(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`strong`,{children:r}),` 次完成`]}),(0,U.jsxs)(`span`,{children:[(0,U.jsx)(`strong`,{children:W(i)}),` tokens`]}),(0,U.jsxs)(`span`,{className:n.length-r?`warn`:``,children:[(0,U.jsx)(`strong`,{children:n.length-r}),` 次未完成`]})]}),(0,U.jsxs)(`div`,{className:`run-list`,children:[n.map(e=>(0,U.jsxs)(`button`,{className:`run-row run-row-button`,type:`button`,onClick:()=>t(e.id),children:[(0,U.jsx)(`span`,{className:`run-dot ${e.status||``}`}),(0,U.jsxs)(`div`,{className:`run-main`,children:[(0,U.jsxs)(`div`,{className:`run-title`,children:[e.agentName||e.agentId||`智能体运行`,` `,(0,U.jsx)(`span`,{children:e.provider?.name||``})]}),(0,U.jsxs)(`div`,{className:`run-meta`,children:[e.model||`未指定模型`,` · `,e.steps||0,` 步 · `,e.toolCalls||0,` 次工具 · `,W(e.usage?.totalTokens||0),` tokens · `,e.finishedAt?`${((Date.parse(e.finishedAt)-Date.parse(e.startedAt))/1e3).toFixed(1)}s`:`运行中`]})]}),(0,U.jsx)(`span`,{className:`run-status`,children:a(e.status)}),(0,U.jsx)(`span`,{className:`run-open`,children:`›`})]},e.id)),!n.length&&(0,U.jsx)(`div`,{className:`control-empty`,children:`还没有 Agent 运行记录。`})]})]})}m(`#settingsBtn`).onclick=()=>J();var q=null;function J(e=`general`){let t=m(`#settings`);t.classList.contains(`open`)||(q=document.activeElement instanceof HTMLElement?document.activeElement:null),t.inert=!1,t.setAttribute(`aria-hidden`,`false`),t.classList.add(`open`),m(`#scrim`).classList.add(`open`),X(e),m(`#settingsBody`)?.focus?.({preventScroll:!0}),m(`#app`).inert=!0;let n=`#/settings/${encodeURIComponent(e)}`;location.hash!==n&&history.pushState(null,``,n)}function Y(){let e=m(`#settings`);if(!e.classList.contains(`open`))return;let t=m(`#app`);t.inert=!1;let n=m(`#settingsBtn`),r=q?.isConnected&&t.contains(q)?q:n;try{r?.focus?.({preventScroll:!0})}catch{r?.focus?.()}e.contains(document.activeElement)&&n?.focus?.({preventScroll:!0}),e.classList.remove(`open`),e.inert=!0,e.setAttribute(`aria-hidden`,`true`),m(`#scrim`).classList.remove(`open`),q=null,location.hash.startsWith(`#/settings/`)&&history.replaceState(null,``,o.currentConvId?`#/chat/${encodeURIComponent(o.currentConvId)}`:`#/new`)}m(`#scrim`).onclick=Y,m(`#closeSettings`).onclick=Y,m(`#closeSettingsTop`).onclick=Y,document.addEventListener(`keydown`,e=>{let t=m(`#settings`);if(!t.classList.contains(`open`)||m(`#modal`).classList.contains(`open`))return;if(e.key===`Escape`){Y();return}if(e.key!==`Tab`)return;let n=Array.from(t.querySelectorAll(`input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [href], [tabindex="0"]`)).filter(e=>e.offsetParent!==null);if(!n.length){e.preventDefault(),m(`#settingsBody`)?.focus();return}let r=n[0],i=n[n.length-1];e.shiftKey&&document.activeElement===r?(e.preventDefault(),i.focus()):!e.shiftKey&&document.activeElement===i&&(e.preventDefault(),r.focus())}),f(`.settings-tab[data-tab]`).forEach(e=>e.onclick=()=>X(e.dataset.tab));function X(e){if(o.currentTab=e,f(`.settings-tab[data-tab]`).forEach(t=>t.classList.toggle(`active`,t.dataset.tab===e)),Z(e),m(`#settings`).classList.contains(`open`)){let t=`#/settings/${encodeURIComponent(e)}`;location.hash!==t&&history.replaceState(null,``,t)}e===`usage`?O(o.usageRange):e===`capabilities`&&x()}function Z(e=`general`,t=!1){let n=m(`#settingsBody`);de();let r=t?n.scrollTop:0;e===`general`?P(me,{params:o.params,theme:h(),token:localStorage.getItem(`multichat_server_token`)||``,onSaveParams:e=>{o.params=e,d(),s(`已保存`)},onTheme:e=>{u(e),s(`主题已切换`)},onSaveToken:e=>{e?localStorage.setItem(`multichat_server_token`,e):localStorage.removeItem(`multichat_server_token`),s(e?`访问令牌已保存`:`访问令牌已清除`)}}):e===`providers`?P(ge,{providers:o.providers,onSave:async(e,t)=>{try{return await i(`/api/providers/${encodeURIComponent(e.id)}`,{method:`PUT`,body:JSON.stringify(t)}),await w(),N(),s(`已保存`),!0}catch(e){return s(e.message,`error`),!1}},onDelete:async e=>{if(!await showConfirm({title:`删除模型提供方`,message:`删除「${e.name||e.id}」?此操作不可撤销。`,confirmLabel:`删除`,danger:!0}))return!1;try{return await i(`/api/providers/${encodeURIComponent(e.id)}`,{method:`DELETE`}),await w(),N(),s(`已删除`),!0}catch(e){return s(e.message,`error`),!1}},onAddBuiltin:ie,onAddCustom:ae,onProbe:async e=>i(`/api/providers/${encodeURIComponent(e.id)}/probe`,{method:`POST`,body:JSON.stringify({})})}):e===`skills`?P(_e,{initialSkills:o.skills,sourceLabel:ue,onToggle:async e=>{try{return await i(`/api/skills/${encodeURIComponent(e.key||e.id)}/toggle`,{method:`POST`,body:JSON.stringify({enabled:!e.enabled})}),await E(),!0}catch(e){return s(e.message,`error`),!1}},onEdit:e=>A(e.key||e.id),onDiff:async e=>{try{le(`Skill 变更`,await i(`/api/skills/${encodeURIComponent(e.key||e.id)}/diff`))}catch(e){s(e.message,`error`)}},onDelete:async e=>{if(!await showConfirm({title:`删除 Skill`,message:`删除该 Skill?关联运行配置的引用也将被移除。`,confirmLabel:`删除`,danger:!0}))return!1;try{return await i(`/api/skills/${encodeURIComponent(e.key||e.id)}`,{method:`DELETE`}),await Promise.all([E(),b()]),!0}catch(e){return s(e.message,`error`),!1}},onImport:()=>ee(`skill`),onAdd:()=>A(null)}):e===`tools`?P(ve,{initialTools:o.tools,agents:o.agents,onTest:async(e,t)=>i(`/api/tools/${encodeURIComponent(e.id)}/test`,{method:`POST`,body:JSON.stringify(t===void 0?{}:{expression:t})}),onToggle:async e=>{try{return await i(`/api/tools/${encodeURIComponent(e.id)}`,{method:`PUT`,body:JSON.stringify({enabled:!e.enabled})}),await D(),s(e.enabled?`工具已停用`:`工具已启用`),!0}catch(e){return s(e.message,`error`),!1}}}):e===`workspace`?Ee():e===`agents`?De():e===`plugins`?ce():e===`mcp`?se():e===`capabilities`?Oe():e===`usage`?Ae():e===`runs`&&je(),t&&requestAnimationFrame(()=>{n.scrollTop=Math.min(r,Math.max(0,n.scrollHeight-n.clientHeight))})}function Ee(){P(ye,{onWorkspaceChange:async e=>{o.selectedWorkspace=o.workspaces.find(t=>t.id===e)||null,await C(),N(),I(),y()},onProjectChange:async e=>{o.selectedProject=o.projects.find(t=>t.id===e)||null,o.selectedProject&&localStorage.setItem(`multichat_project`,o.selectedProject.id),await C(),N(),I(),y()},onSaveDefaults:async(e,t,n)=>{if(!o.selectedProject){s(`请先选择项目`,`error`);return}try{await i(`/api/projects/${encodeURIComponent(o.selectedProject.id)}`,{method:`PUT`,body:JSON.stringify({defaultAgentId:e,defaultProviderId:t,defaultModel:n})});let r={...o.selectedProject,defaultAgentId:e,defaultProviderId:t,defaultModel:n};o.selectedProject=r,o.projects=o.projects.map(e=>e.id===r.id?r:e),v(),s(`已保存项目默认`)}catch(e){s(e.message,`error`)}},onNewWorkspace:Me,onNewProject:Ne,onImportUrl:Pe,onUploadFile:async e=>{if(o.selectedProject)try{await i(`/api/assets`,{method:`POST`,body:JSON.stringify({projectId:o.selectedProject.id,name:e.name,mimeType:e.type||`text/plain`,content:await e.text()})}),await C(),s(`文件已加入项目`)}catch(e){s(e.message,`error`)}},onDeleteAsset:async e=>{try{await i(`/api/assets/${encodeURIComponent(e)}`,{method:`DELETE`}),await C()}catch(e){s(e.message,`error`)}},onSearch:async e=>o.selectedProject?i(`/api/projects/${encodeURIComponent(o.selectedProject.id)}/search?q=${encodeURIComponent(e)}`):[],onAddMemory:()=>Q(),onEditMemory:e=>Q(e),onToggleMemory:async e=>{await i(`/api/memories/${encodeURIComponent(e.id)}`,{method:`PUT`,body:JSON.stringify({enabled:e.enabled===!1})}),await S()},onDeleteMemory:async e=>{await showConfirm({title:`删除项目记忆`,message:`删除这条项目记忆?`,confirmLabel:`删除`,danger:!0})&&(await i(`/api/memories/${encodeURIComponent(e)}`,{method:`DELETE`}),await S())},onCreateSnapshot:async()=>{if(!o.selectedProject)return;let e=await showPrompt({title:`新建项目快照`,label:`快照名称`,value:`项目快照 ${new Date().toLocaleString(`zh-CN`)}`,maxLength:120});if(e!=null)try{await i(`/api/snapshots`,{method:`POST`,body:JSON.stringify({projectId:o.selectedProject.id,title:e})}),await S(),s(`项目快照已创建`)}catch(e){s(e.message,`error`)}},onRestoreSnapshot:async e=>{if(await showConfirm({title:`恢复快照`,message:`恢复该快照?当前状态会先自动备份,随后替换项目文件、记忆和默认配置。`,confirmLabel:`恢复快照`,danger:!0}))try{await i(`/api/snapshots/${encodeURIComponent(e)}/restore`,{method:`POST`}),await Promise.all([C(),b()]),y(),s(`快照已恢复`)}catch(e){s(e.message,`error`)}},onDeleteSnapshot:async e=>{await showConfirm({title:`删除项目快照`,message:`删除该项目快照?`,confirmLabel:`删除`,danger:!0})&&(await i(`/api/snapshots/${encodeURIComponent(e)}`,{method:`DELETE`}),await S())}})}function De(){P(be,{onEdit:e=>oe(e),onExport:e=>ne(e,`agent`),onDelete:async e=>{if(await showConfirm({title:`删除运行配置`,message:`删除该运行配置?`,confirmLabel:`删除`,danger:!0})){try{await i(`/api/agents/${encodeURIComponent(e.id)}`,{method:`DELETE`})}catch(e){s(e.message,`error`);return}o.selectedAgent?.id===e.id&&(o.selectedAgent=null),g(),await b(),N()}},onImport:async e=>{try{await te(re(JSON.parse(await e.text())),`文件 ${e.name}`)}catch(e){s(`解析失败:${e.message}`,`error`)}}})}function Oe(){P(Se,{onRefresh:async()=>{o.capabilities=null,await x()}})}function ke(e,t){let n=[[`date`,`input_tokens`,`output_tokens`,`total_tokens`,`requests`,`errors`],...(e?.daily||[]).map(e=>[e.date,e.inputTokens,e.outputTokens,e.totalTokens,e.requests,e.errors])],r=new Blob([n.map(e=>e.join(`,`)).join(`
2
+ `)],{type:`text/csv;charset=utf-8`}),i=document.createElement(`a`);i.href=URL.createObjectURL(r),i.download=`multichat-usage-${t}d.csv`,i.click(),setTimeout(()=>URL.revokeObjectURL(i.href),1e3)}function Ae(){P(we,{onRange:async e=>{o.usage=null,await O(e)},onExport:ke})}function je(){P(Te,{onRefresh:T,onOpen:$})}function Me(){M({title:`新建工作区`,body:`<form id="workspaceForm"><div class="field"><label>名称</label><input name="name" required placeholder="例如:产品研发" /></div><div class="field"><label>描述</label><input name="description" placeholder="这个工作区用于什么" /></div><div class="row"><button type="button" class="btn-ghost" id="workspaceCancel">取消</button><button class="btn-primary" type="submit">创建</button></div></form>`,onMount:e=>{m(`#workspaceCancel`,e).onclick=j,m(`#workspaceForm`,e).onsubmit=async e=>{e.preventDefault();let t=Object.fromEntries(new FormData(e.target).entries());try{await i(`/api/workspaces`,{method:`POST`,body:JSON.stringify(t)}),await k(),N(),j(),s(`工作区已创建`)}catch(e){s(e.message,`error`)}}}})}function Q(e=null){let t=e?o.memories.find(t=>t.id===e):null;M({title:t?`编辑项目记忆`:`新增项目记忆`,body:`<form id="memoryForm"><div class="field"><label>标题</label><input name="title" required maxlength="120" value="${l(t?.title||``)}" placeholder="例如:代码风格" /></div><div class="field"><label>事实或偏好</label><textarea name="content" required maxlength="4000" rows="6" placeholder="只记录可长期复用、由你确认的信息。">${l(t?.content||``)}</textarea><div class="pmeta">记忆作为上下文参考,不会被当作可执行指令。</div></div><div id="memoryErr" class="auth-error"></div><div class="row"><button type="button" class="btn-ghost" id="memoryCancel">取消</button><button class="btn-primary" type="submit">保存</button></div></form>`,onMount:e=>{m(`#memoryCancel`,e).onclick=j,m(`#memoryForm`,e).onsubmit=async n=>{n.preventDefault();let r={...Object.fromEntries(new FormData(n.target).entries()),projectId:o.selectedProject?.id,enabled:!t||t.enabled!==!1};try{await i(t?`/api/memories/${encodeURIComponent(t.id)}`:`/api/memories`,{method:t?`PUT`:`POST`,body:JSON.stringify(r)}),await S(),j(),s(`项目记忆已保存`)}catch(t){m(`#memoryErr`,e).textContent=t.message}}}})}function Ne(){if(!o.selectedWorkspace)return s(`请先选择工作区`,`error`);M({title:`新建项目`,body:`<form id="projectForm"><div class="field"><label>名称</label><input name="name" required placeholder="例如:移动端重构" /></div><div class="field"><label>描述</label><input name="description" placeholder="项目目标或范围" /></div><div class="row"><button type="button" class="btn-ghost" id="projectCancel">取消</button><button class="btn-primary" type="submit">创建</button></div></form>`,onMount:e=>{m(`#projectCancel`,e).onclick=j,m(`#projectForm`,e).onsubmit=async e=>{e.preventDefault();let t=Object.fromEntries(new FormData(e.target).entries());t.workspaceId=o.selectedWorkspace.id;try{await i(`/api/projects`,{method:`POST`,body:JSON.stringify(t)}),await C(),j(),s(`项目已创建`)}catch(e){s(e.message,`error`)}}}})}function Pe(){if(!o.selectedProject)return s(`请先选择项目`,`error`);M({title:`从 URL 添加文件`,body:`<form id="assetUrlForm"><div class="field"><label>URL</label><input name="url" type="url" required placeholder="https://example.com/notes.md" /></div><div class="field"><label>显示名称(可选)</label><input name="name" placeholder="自动从 URL 推断" /></div><div id="assetUrlErr" class="auth-error"></div><div class="row"><button type="button" class="btn-ghost" id="assetUrlCancel">取消</button><button class="btn-primary" type="submit">添加</button></div></form>`,onMount:e=>{m(`#assetUrlCancel`,e).onclick=j,m(`#assetUrlForm`,e).onsubmit=async t=>{t.preventDefault();let n=Object.fromEntries(new FormData(t.target).entries());n.projectId=o.selectedProject.id;try{await i(`/api/assets`,{method:`POST`,body:JSON.stringify(n)}),await C(),j(),s(`远程文件已加入项目`)}catch(t){m(`#assetUrlErr`,e).textContent=t.message}}}})}async function $(e){try{let t=await i(`/api/runs/${encodeURIComponent(e)}`),n=(t.turns||[]).flatMap(e=>e.steps||[]),r=t.contextManifest||{},a=t.usage||{},o=(e,t)=>`<span><small>${l(e)}</small><strong>${l(t)}</strong></span>`,s=t.status===`completed`?`已完成`:t.status===`error`?`失败`:t.status===`cancelled`?`已取消`:`运行中`;M({title:`运行详情 · ${t.agentName||t.agentId}`,body:`<div class="run-detail-summary">${o(`状态`,s)}${o(`模型`,t.model||`—`)}${o(`Token`,W(a.totalTokens||0))}${o(`步骤`,String(n.length))}${o(`策略`,t.toolPolicy===`safe`?`安全`:`自动`)}</div><div class="run-detail-grid"><section><h4>执行时间线</h4><div class="flight-timeline">${n.map((e,t)=>`<article class="flight-step ${l(e.status)}"><i>${t+1}</i><div><strong>${e.kind===`tool_call`?l(e.tool||`工具调用`):`模型请求 · ${l(e.model||``)}`}</strong><p>${e.kind===`tool_call`?`${l((e.permissions||[]).join(`, `)||`无特殊权限`)} · ${l(e.risk||`low`)}`:`${e.messageCount||0} 条上下文 · ${W(e.usage?.total||0)} tokens`}</p>${e.error?`<code>${l(e.error)}</code>`:``}</div><span>${e.durationMs==null?l(e.status):`${(e.durationMs/1e3).toFixed(2)}s`}</span></article>`).join(``)||`<div class="control-empty">没有步骤轨迹。</div>`}</div></section><section><h4>上下文透镜</h4><div class="context-lens"><div><span>消息</span><strong>${r.messages||0}</strong></div><div><span>系统上下文</span><strong>${W(r.systemCharacters||0)} 字</strong></div><div><span>项目文件</span><strong>${(r.assets||[]).length}</strong></div><div><span>记忆</span><strong>${(r.memories||[]).length}</strong></div></div><div class="context-source"><h5>注入来源</h5>${[[`文件`,r.assets],[`记忆`,r.memories],[`Skills`,r.skills],[`工具`,r.tools],[`MCP`,r.mcpServers]].map(([e,t])=>`<div><strong>${e}</strong><span>${(t||[]).map(e=>l(e.name||e.id)).join(`、`)||`无`}</span></div>`).join(``)}</div><div class="context-source"><h5>Token 构成</h5><div><strong>输入</strong><span>${W(a.inputTokens||0)}</span></div><div><strong>输出</strong><span>${W(a.outputTokens||0)}</span></div><div><strong>缓存</strong><span>${W(a.cachedTokens||0)}</span></div><div><strong>估算</strong><span>${W(a.estimatedTokens||0)}</span></div></div></section></div><div class="row"><button class="btn-ghost" id="exportRun">导出运行快照</button><button class="btn-primary" id="closeRunDetail">关闭</button></div>`,onMount:e=>{e.classList.add(`run-detail-modal`),m(`#closeRunDetail`,e).onclick=j,m(`#exportRun`,e).onclick=()=>{let e=new Blob([JSON.stringify(t,null,2)],{type:`application/json`}),n=document.createElement(`a`);n.href=URL.createObjectURL(e),n.download=`${t.id}.json`,n.click(),setTimeout(()=>URL.revokeObjectURL(n.href),1e3)}}})}catch(e){s(e.message,`error`)}}export{Y as closeSettings,J as openSettings,Oe as renderCapabilities,je as renderRuns,Z as renderSettings,Ae as renderUsage,Pe as showAssetUrlModal,Q as showMemoryModal,Ne as showProjectForm,$ as showRunDetail,Me as showWorkspaceForm,X as switchSettingsTab};
@@ -0,0 +1 @@
1
+ import{p as e}from"./index-CASVCOUO.js";function t(){let t=e(`#sidebar`),n=e(`#sidebarToggle`),r=e(`#sidebarClose`),i=e(`#mobileScrim`),a=e(`.main`);if(!t||!n||!i)return;let o=(e=!1)=>{a&&(a.inert=!1),e&&n.focus({preventScroll:!0}),t.classList.remove(`open`),i.classList.remove(`open`),n.setAttribute(`aria-expanded`,`false`),document.body.classList.remove(`mobile-nav-open`)},s=()=>{t.classList.add(`open`),i.classList.add(`open`),n.setAttribute(`aria-expanded`,`true`),document.body.classList.add(`mobile-nav-open`);let r=e(`#convSearch`);r&&r.focus({preventScroll:!0}),a&&(a.inert=!0)};n.onclick=()=>t.classList.contains(`open`)?o(!0):s(),r&&(r.onclick=()=>o(!0)),i.onclick=()=>o(!0),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&t.classList.contains(`open`)&&o(!0)}),window.addEventListener(`resize`,()=>{window.innerWidth>760&&o()})}export{t as setupShell};
@@ -0,0 +1,41 @@
1
+ import{d as e,h as t,i as n,m as r,p as i,r as a}from"./index-CASVCOUO.js";var o=null,s=null;function c(){let t=e.selectedProvider;return!t||!e.selectedModel?!1:!!t.baseUrl&&([`ollama`,`lmstudio`,`mock`].includes(String(t.apiType||``).toLowerCase())||t.apiKeyMasked||t.apiKey)}function l(e){let t=e?.usage||{};return t.totalTokens??t.total_tokens??t.total??0}function u(e,t,n){let r=e?.[t]||e?.[n]||[];return Array.isArray(r)?r.length:0}function d(){let t=e.selectedAgent,n=!!(e.selectedProvider&&e.selectedModel),r=c();return[{label:`模型选择`,ok:n,detail:n?`${e.selectedProvider.name||e.selectedProvider.id} · ${e.selectedModel}`:`尚未选择模型`,tab:`providers`},{label:`连接配置`,ok:r,detail:r?`凭据与地址已配置`:`需要补充 API 地址或凭据`,tab:`providers`},{label:`项目上下文`,ok:!!e.selectedProject,detail:e.selectedProject?e.selectedProject.name:`尚未选择项目`,tab:`workspace`},{label:`运行配置`,ok:!0,detail:t?t.name:`直接对话(无工具注入)`,tab:`agents`}]}function f(e){return{running:`运行中`,completed:`已完成`,error:`失败`,cancelled:`已取消`,rejected:`已拒绝`}[e]||e||`暂无记录`}function p(){let n=i(`#inspectorBody`);if(!n)return;let a=e.selectedAgent,o=e.runs?.[0],s=d(),c=s.every(e=>e.ok),p=e.selectedAssetIds?.size||0,m=(e.assets||[]).filter(t=>e.selectedAssetIds?.has(t.id)).reduce((e,t)=>e+Number(t.size||t.bytes||0),0),g=Math.ceil(m/4),_=(e.memories||[]).filter(e=>e.enabled!==!1).length,v=u(a,`skillRefs`,`skillIds`),y=u(a,`toolIds`,`toolIds`),b=u(a,`mcpServerIds`,`mcpServerIds`),x=Math.round(s.filter(e=>e.ok).length/s.length*100);n.innerHTML=`
2
+ <section class="inspector-status ${c?`ready`:`needs-attention`}">
3
+ <div class="inspector-score"><strong>${x}</strong><span>%</span></div>
4
+ <div><strong>${c?`可以开始运行`:`需要完成配置`}</strong><span>${c?`必要条件已经就绪`:`补齐下面的配置项后再运行`}</span></div>
5
+ </section>
6
+ <section class="inspector-section">
7
+ <div class="inspector-section-head"><strong>会话配置</strong><button type="button" data-inspector-settings="agents">编辑</button></div>
8
+ <dl class="inspector-facts">
9
+ <div><dt>模型</dt><dd>${t(e.selectedModel||`未选择`)}</dd></div>
10
+ <div><dt>运行配置</dt><dd>${t(a?.name||`直接对话`)}</dd></div>
11
+ <div><dt>工作区</dt><dd>${t(e.selectedWorkspace?.name||`未选择`)}</dd></div>
12
+ <div><dt>项目</dt><dd>${t(e.selectedProject?.name||`未选择`)}</dd></div>
13
+ </dl>
14
+ </section>
15
+ <section class="inspector-section">
16
+ <div class="inspector-section-head"><strong>上下文装配</strong><button type="button" data-inspector-settings="workspace">管理</button></div>
17
+ <div class="inspector-metrics">
18
+ <div><strong>${p}</strong><span>文件</span></div>
19
+ <div><strong>${_}</strong><span>记忆</span></div>
20
+ <div><strong>${v}</strong><span>Skills</span></div>
21
+ <div><strong>${y+b}</strong><span>工具</span></div>
22
+ </div>
23
+ <div class="context-budget"><div><span>文件上下文估算</span><strong>${g.toLocaleString(`zh-CN`)} Token</strong></div><div class="context-budget-track"><span style="width:${Math.min(100,g/320)}%"></span></div><small>仅估算已选文件;模型系统提示和对话历史未计入。</small></div>
24
+ ${a?`<p class="inspector-note">${v} Skills · ${y} 内置工具 · ${b} MCP 服务</p>`:`<p class="inspector-note">直接对话不会注入工具或运行配置提示词。</p>`}
25
+ </section>
26
+ <section class="inspector-section">
27
+ <div class="inspector-section-head"><strong>运行前检查</strong><button type="button" data-inspector-settings="capabilities">能力清单</button></div>
28
+ <div class="inspector-checks">
29
+ ${s.map(e=>`<button type="button" data-inspector-settings="${e.tab}" class="inspector-check ${e.ok?`ok`:`warn`}"><span aria-hidden="true">${e.ok?`✓`:`!`}</span><div><strong>${t(e.label)}</strong><small>${t(e.detail)}</small></div></button>`).join(``)}
30
+ </div>
31
+ </section>
32
+ <section class="inspector-section">
33
+ <div class="inspector-section-head"><strong>最近运行</strong><button type="button" data-inspector-settings="runs">查看日志</button></div>
34
+ ${o?`<div class="inspector-run"><div><span class="run-state ${t(o.status)}">${t(f(o.status))}</span><strong>${t(o.agentName||o.agentId||`直接对话`)}</strong></div><dl><div><dt>步骤</dt><dd>${t(o.steps||0)}</dd></div><div><dt>工具调用</dt><dd>${t(o.toolCalls||0)}</dd></div><div><dt>Token</dt><dd>${t(l(o).toLocaleString(`zh-CN`))}</dd></div></dl></div>`:`<div class="inspector-empty">完成一次运行后,这里会显示步骤、工具调用和 Token。</div>`}
35
+ </section>
36
+ `,r(`[data-inspector-settings]`,n).forEach(e=>{e.onclick=()=>{h(!1),openSettings(e.dataset.inspectorSettings||`general`)}})}function m(){let e=i(`#sessionInspector`),t=i(`#mainWorkspace`),n=i(`#inspectorBtn`);!e||!t||(s=document.activeElement,p(),e.inert=!1,e.setAttribute(`aria-hidden`,`false`),t.classList.add(`inspector-open`),n?.setAttribute(`aria-expanded`,`true`),window.setTimeout(()=>i(`#inspectorClose`)?.focus(),0))}function h(e=!0){let t=i(`#sessionInspector`),n=i(`#mainWorkspace`),r=i(`#inspectorBtn`);!t||!n||(t.contains(document.activeElement)&&(r||document.body).focus(),n.classList.remove(`inspector-open`),t.setAttribute(`aria-hidden`,`true`),t.inert=!0,r?.setAttribute(`aria-expanded`,`false`),e&&s&&document.contains(s)&&s.focus())}function g(){i(`#mainWorkspace`)?.classList.contains(`inspector-open`)?h():m()}function _(){return[{id:`new`,label:`新建对话`,description:`清空当前视图并开始一段新对话`,group:`对话`,shortcut:`Ctrl N`,run:()=>newConversation()},{id:`inspect`,label:`打开会话检查器`,description:`检查模型、上下文、能力和最近运行`,group:`对话`,shortcut:`Ctrl I`,run:g},{id:`compare`,label:`并行模型对比`,description:`使用相同上下文并行比较 2–4 个模型`,group:`对话`,shortcut:`Ctrl M`,run:()=>openCompare()},{id:`providers`,label:`模型连接`,description:`添加模型提供方、地址和凭据`,group:`设置`,run:()=>openSettings(`providers`)},{id:`agents`,label:`运行配置`,description:`组合提示词、Skills、工具与 MCP`,group:`设置`,run:()=>openSettings(`agents`)},{id:`workspace`,label:`工作区与项目`,description:`管理文件、记忆、快照和项目默认值`,group:`设置`,run:()=>openSettings(`workspace`)},{id:`skills`,label:`Skills`,description:`浏览、导入和管理工作流能力`,group:`能力`,run:()=>openSettings(`skills`)},{id:`mcp`,label:`MCP 服务`,description:`管理外部工具连接与信任状态`,group:`能力`,run:()=>openSettings(`mcp`)},{id:`plugins`,label:`插件`,description:`导入、审查和启用扩展包`,group:`能力`,run:()=>openSettings(`plugins`)},{id:`runs`,label:`运行日志`,description:`查看 Turn、Step、工具调用和审批记录`,group:`观察`,run:()=>openSettings(`runs`)},{id:`usage`,label:`Token 用量`,description:`查看每日消耗、模型分布和缓存命中`,group:`观察`,run:()=>openSettings(`usage`)},{id:`theme`,label:a()===`dark`?`切换为浅色界面`:`切换为深色界面`,description:`更改当前设备上的显示主题`,group:`界面`,run:()=>n(a()===`dark`?`light`:`dark`)}]}function v(e=``){let n=i(`#commandList`);if(!n)return;let a=e.trim().toLowerCase(),o=_().filter(e=>`${e.label} ${e.description} ${e.group}`.toLowerCase().includes(a));n.innerHTML=o.length?o.map(e=>`
37
+ <button class="command-item" type="button" data-command="${e.id}">
38
+ <span class="command-item-group">${t(e.group)}</span>
39
+ <span class="command-item-copy"><strong>${t(e.label)}</strong><small>${t(e.description)}</small></span>
40
+ ${e.shortcut?`<kbd>${t(e.shortcut)}</kbd>`:`<span class="command-arrow" aria-hidden="true">›</span>`}
41
+ </button>`).join(``):`<div class="command-empty">没有匹配的操作</div>`,r(`[data-command]`,n).forEach(e=>{e.onclick=()=>{let t=_().find(t=>t.id===e.dataset.command);b(!1),t?.run()}})}function y(){let e=i(`#commandPalette`),t=i(`#commandSearch`);!e||!t||(o=document.activeElement,t.value=``,v(),e.inert=!1,e.setAttribute(`aria-hidden`,`false`),e.classList.add(`open`),i(`#app`).inert=!0,window.setTimeout(()=>t.focus(),0))}function b(e=!0){let t=i(`#commandPalette`);t&&(i(`#app`).inert=!1,t.contains(document.activeElement)&&(o||i(`#commandBtn`)||document.body).focus(),t.classList.remove(`open`),t.setAttribute(`aria-hidden`,`true`),t.inert=!0,e&&o&&document.contains(o)&&o.focus())}function x(){let e={general:`偏好设置`,providers:`模型连接`,agents:`运行配置`,capabilities:`能力清单`,usage:`用量`,runs:`运行日志`};r(`.settings-tab[data-tab]`).forEach(t=>{let n=t.querySelector(`span:last-child`);n&&e[t.dataset.tab||``]&&(n.textContent=e[t.dataset.tab||``])}),i(`#commandBtn`).onclick=y,i(`#inspectorBtn`).onclick=g,i(`#inspectorClose`).onclick=()=>h();let t=i(`#commandSearch`);t.oninput=()=>v(t.value),t.onkeydown=e=>{let t=Array.from(r(`.command-item`));e.key===`ArrowDown`&&t.length&&(e.preventDefault(),t[0].focus()),e.key===`Enter`&&t.length&&(e.preventDefault(),t[0].click())},i(`#commandList`).addEventListener(`keydown`,e=>{let n=Array.from(r(`.command-item`)),i=n.indexOf(document.activeElement);e.key===`ArrowDown`&&i>=0&&(e.preventDefault(),n[(i+1)%n.length]?.focus()),e.key===`ArrowUp`&&i>=0&&(e.preventDefault(),(i===0?t:n[i-1])?.focus())}),r(`[data-open-settings]`).forEach(e=>{e.onclick=()=>openSettings(e.dataset.openSettings||`general`)}),i(`#commandPalette`).addEventListener(`mousedown`,e=>{e.target===i(`#commandPalette`)&&b()}),document.addEventListener(`keydown`,e=>{if(!((i(`#settings`)?.classList.contains(`open`)||i(`#modal`)?.classList.contains(`open`))&&(e.ctrlKey||e.metaKey)&&[`k`,`i`,`m`,`n`].includes(e.key.toLowerCase()))){if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()===`k`){e.preventDefault(),i(`#commandPalette`)?.classList.contains(`open`)?b():y();return}if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()===`i`){e.preventDefault(),i(`#commandPalette`)?.classList.contains(`open`)&&b(!1),g();return}if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()===`m`){e.preventDefault(),i(`#commandPalette`)?.classList.contains(`open`)&&b(!1),openCompare();return}if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()===`n`){e.preventDefault(),i(`#commandPalette`)?.classList.contains(`open`)&&b(!1),newConversation();return}e.key===`Escape`&&(i(`#commandPalette`)?.classList.contains(`open`)?b():i(`#mainWorkspace`)?.classList.contains(`inspector-open`)&&h())}}),p()}export{b as closeCommandPalette,h as closeInspector,y as openCommandPalette,m as openInspector,p as renderInspector,x as setupWorkbench,g as toggleInspector};
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" fill="#171915"/>
3
+ <path d="M9.5 20.5v-9h2.6l3.9 4.7 3.9-4.7h2.6v9h-2.4v-5.4l-4.1 4.9-4.1-4.9v5.4z" fill="#f5f5ef"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" rx="7" fill="#171915"/>
3
+ <path d="M8 21V10h3l5 6 5-6h3v11h-3v-7l-5 6-5-6v7z" fill="#f5f5ef"/>
4
+ </svg>
@@ -0,0 +1,21 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
6
+ <meta name="theme-color" content="#171915" />
7
+ <meta name="color-scheme" content="light dark" />
8
+ <title>MultiChat · 本地模型工作台</title>
9
+ <link rel="manifest" href="/manifest.webmanifest" />
10
+ <link rel="icon" href="/icon.svg" type="image/svg+xml" />
11
+ <meta name="apple-mobile-web-app-capable" content="yes" />
12
+ <meta name="apple-mobile-web-app-title" content="MultiChat" />
13
+ <meta name="mobile-web-app-capable" content="yes" />
14
+ <script type="module" crossorigin src="/assets/index-CASVCOUO.js"></script>
15
+ <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-ZSsbr0oW.js">
16
+ <link rel="stylesheet" crossorigin href="/assets/index-DSvbjNLc.css">
17
+ </head>
18
+ <body>
19
+ <div id="root"></div>
20
+ </body>
21
+ </html>
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "MultiChat · 本地模型工作台",
3
+ "short_name": "MultiChat",
4
+ "description": "本地优先的多模型 Agent 工作台:模型、Skills、MCP 与插件按运行配置组合。",
5
+ "start_url": "/",
6
+ "scope": "/",
7
+ "display": "standalone",
8
+ "orientation": "any",
9
+ "background_color": "#101522",
10
+ "theme_color": "#171915",
11
+ "lang": "zh-CN",
12
+ "icons": [
13
+ {
14
+ "src": "/icon.svg",
15
+ "sizes": "any",
16
+ "type": "image/svg+xml",
17
+ "purpose": "any"
18
+ },
19
+ {
20
+ "src": "/icon-maskable.svg",
21
+ "sizes": "any",
22
+ "type": "image/svg+xml",
23
+ "purpose": "maskable"
24
+ }
25
+ ]
26
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@leterhong/multichat",
3
+ "version": "1.6.0",
4
+ "description": "Local-first multi-model agent workspace with Skills, MCP servers, and plugins",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "multichat": "bin/multichat.mjs"
9
+ },
10
+ "files": [
11
+ ".agents",
12
+ "bin",
13
+ "backend/*.ts",
14
+ "backend/dist",
15
+ "backend/adapters",
16
+ "backend/extensions",
17
+ "backend/lib",
18
+ "backend/routes",
19
+ "backend/runtime",
20
+ "backend/package.json",
21
+ "backend/prompts.json",
22
+ "frontend/dist",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "start": "node ./bin/multichat.mjs web",
27
+ "doctor": "node ./bin/multichat.mjs doctor",
28
+ "build": "npm run build --prefix backend && npm run build --prefix frontend",
29
+ "test": "node --test test/*.test.mjs",
30
+ "test:cli": "node --test test/cli.test.mjs"
31
+ },
32
+ "engines": {
33
+ "node": ">=22.13.0"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/Leterhong/MultiChat.git"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/Leterhong/MultiChat/issues"
41
+ },
42
+ "homepage": "https://github.com/Leterhong/MultiChat#readme",
43
+ "keywords": [
44
+ "agent",
45
+ "mcp",
46
+ "skills",
47
+ "multichat",
48
+ "typescript"
49
+ ],
50
+ "publishConfig": {
51
+ "access": "public"
52
+ },
53
+ "dependencies": {
54
+ "adm-zip": "^0.6.0",
55
+ "cors": "^2.8.5",
56
+ "express": "^4.19.2"
57
+ }
58
+ }