@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 @@
1
+ import{i as e,n as t,s as n,t as r}from"./jsx-runtime-ZSsbr0oW.js";import{d as i,f as a,m as o,o as s,p as c,t as l}from"./index-CASVCOUO.js";var u=t(),d=n(e(),1),f=r(),p=`multichat_compare_history_v1`;function m(){try{let e=JSON.parse(localStorage.getItem(p)||`[]`);return Array.isArray(e)?e.filter(e=>e&&typeof e.id==`string`&&typeof e.prompt==`string`&&Array.isArray(e.results)).map(e=>({id:e.id,prompt:e.prompt,createdAt:typeof e.createdAt==`string`?e.createdAt:``,projectName:typeof e.projectName==`string`?e.projectName:`未选择项目`,results:e.results.filter(e=>e&&typeof e.model==`string`&&typeof e.providerId==`string`)})).slice(0,12):[]}catch{return[]}}function h(e,t,n){let r=[{id:`experiment_${Date.now().toString(36)}`,prompt:e,createdAt:new Date().toISOString(),projectName:t,results:n},...m()].slice(0,12);try{localStorage.setItem(p,JSON.stringify(r))}catch{a(`实验已完成,但本地历史无法保存`,`error`)}return r}function g({result:e,prompt:t,adopt:n}){let r=e.usage?.total_tokens??e.usage?.total??0,i=e.status===`running`?`运行中`:e.status===`error`?`失败`:e.status===`success`?`完成`:`等待`;return(0,f.jsxs)(`article`,{className:`compare-result`,"data-state":e.status,children:[(0,f.jsxs)(`header`,{children:[(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`strong`,{children:e.model}),(0,f.jsx)(`span`,{children:e.providerName})]}),(0,f.jsx)(`span`,{className:`compare-state`,children:i})]}),(0,f.jsxs)(`div`,{className:`compare-result-body`,children:[e.status===`running`&&(0,f.jsxs)(`div`,{className:`compare-loading`,"aria-label":`模型正在运行`,children:[(0,f.jsx)(`span`,{}),(0,f.jsx)(`span`,{}),(0,f.jsx)(`span`,{})]}),e.status===`error`&&(0,f.jsx)(`div`,{className:`compare-error`,children:e.error||`请求失败`}),e.status===`success`&&(0,f.jsx)(`div`,{className:`compare-answer`,children:(0,f.jsx)(l,{children:e.text||``})}),e.status===`idle`&&(0,f.jsx)(`div`,{className:`compare-empty`,children:`等待开始`})]}),e.status===`success`&&(0,f.jsxs)(`footer`,{children:[(0,f.jsxs)(`span`,{children:[((e.elapsedMs||0)/1e3).toFixed(1),` 秒`]}),(0,f.jsxs)(`span`,{children:[Number(r).toLocaleString(`zh-CN`),` Token`]}),(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`button`,{type:`button`,onClick:async()=>{try{await navigator.clipboard.writeText(e.text||``),a(`结果已复制`)}catch{a(`复制失败`,`error`)}},children:`复制`}),(0,f.jsx)(`button`,{type:`button`,className:`compare-adopt`,onClick:()=>void n(e,t),children:`采用到对话`})]})]})]})}function _({targets:e,defaultTargetIds:t,initialPrompt:n,projectName:r,fileCount:i,memoryCount:o,execute:s,adopt:c}){let[l,u]=(0,d.useState)(()=>new Set(t)),[p,_]=(0,d.useState)(n),[v,y]=(0,d.useState)([]),[b,x]=(0,d.useState)(m),[S,C]=(0,d.useState)(!1),w=(0,d.useMemo)(()=>e.filter(e=>l.has(e.id)),[e,l]),T=(e,t)=>{let n=new Set(l);if(t){if(n.size>=4){a(`一次最多实验 4 个模型`,`error`);return}n.add(e)}else n.delete(e);u(n)},E=async()=>{let e=p.trim();if(!e){a(`请输入实验任务`,`error`);return}if(w.length<2){a(`至少选择两个模型`,`error`);return}C(!0),y(w.map(e=>({...e,status:`running`})));try{let t=await Promise.all(w.map(async t=>{let n=await s(t,e);return y(e=>e.map(e=>e.id===t.id?n:e)),n}));x(h(e,r,t))}finally{C(!1)}};return(0,f.jsxs)(`div`,{className:`compare-lab`,children:[(0,f.jsxs)(`div`,{className:`compare-intro`,children:[(0,f.jsx)(`div`,{children:(0,f.jsx)(`p`,{children:`同一任务、同一上下文,并行观察不同模型的答案。`})}),(0,f.jsxs)(`span`,{className:`compare-context`,children:[i,` 个文件 · `,o,` 条记忆`]})]}),(0,f.jsxs)(`div`,{className:`compare-config`,children:[(0,f.jsxs)(`fieldset`,{className:`compare-targets`,children:[(0,f.jsx)(`legend`,{children:`参与模型`}),e.map(e=>(0,f.jsxs)(`label`,{children:[(0,f.jsx)(`input`,{type:`checkbox`,checked:l.has(e.id),onChange:t=>T(e.id,t.target.checked)}),(0,f.jsxs)(`span`,{children:[(0,f.jsx)(`strong`,{children:e.model}),(0,f.jsx)(`small`,{children:e.providerName})]})]},e.id))]}),(0,f.jsx)(`label`,{className:`compare-prompt-label`,htmlFor:`comparePrompt`,children:`实验任务`}),(0,f.jsx)(`textarea`,{id:`comparePrompt`,className:`compare-prompt`,rows:3,placeholder:`输入要交给多个模型的相同任务…`,value:p,onChange:e=>_(e.target.value)}),(0,f.jsxs)(`div`,{className:`compare-actions`,children:[(0,f.jsxs)(`span`,{children:[`已选择 `,w.length,` 个模型`]}),(0,f.jsx)(`button`,{className:`btn-primary`,type:`button`,disabled:S,onClick:()=>void E(),children:S?`实验进行中…`:v.length?`再次实验`:`开始实验`})]})]}),(0,f.jsx)(`div`,{className:`compare-results`,children:v.map(e=>(0,f.jsx)(g,{result:e,prompt:p.trim(),adopt:c},e.id))}),(0,f.jsxs)(`details`,{className:`compare-history`,open:!b.length,children:[(0,f.jsxs)(`summary`,{children:[(0,f.jsx)(`span`,{children:`历史实验`}),(0,f.jsxs)(`small`,{children:[b.length,` 条,保存在当前设备`]})]}),(0,f.jsx)(`div`,{children:b.length?b.map(e=>{let t=new Date(e.createdAt),n=Number.isNaN(t.getTime())?``:t.toLocaleString(`zh-CN`,{month:`numeric`,day:`numeric`,hour:`2-digit`,minute:`2-digit`}),r=e.results.filter(e=>e.status===`success`).length;return(0,f.jsxs)(`button`,{type:`button`,className:`compare-history-item`,onClick:()=>{_(e.prompt),y(e.results)},children:[(0,f.jsxs)(`span`,{children:[(0,f.jsx)(`strong`,{children:e.prompt}),(0,f.jsxs)(`small`,{children:[e.projectName,` · `,r,` 个结果`]})]}),(0,f.jsx)(`time`,{children:n})]},e.id)}):(0,f.jsx)(`div`,{className:`compare-history-empty`,children:`完成一次实验后,结果会保存在当前设备。`})})]})]})}function v(){return(i.providers||[]).flatMap(e=>(e.models||[]).map(t=>({id:`${e.id}:${t}`,providerId:e.id,providerName:e.name||e.id,model:t})))}async function y(e,t){let n=performance.now();try{let r=await fetch(i.apiBase+`/v1/chat/completions`,{method:`POST`,headers:{"Content-Type":`application/json`,...s()},body:JSON.stringify({providerId:e.providerId,model:e.model,messages:[{role:`user`,content:t}],stream:!1,temperature:i.params.temperature,max_tokens:i.params.max_tokens,top_p:i.params.top_p,workspaceId:i.selectedWorkspace?.id||null,projectId:i.selectedProject?.id||null,assetIds:[...i.selectedAssetIds],interactionId:`compare_${Date.now().toString(36)}_${e.providerId}`})}),a=await r.json();if(!r.ok){let e=typeof a?.error==`string`?a.error:a?.error?.message;throw Error(e||`HTTP ${r.status}`)}return{...e,status:`success`,text:a?.choices?.[0]?.message?.content||``,usage:a?.usage||{},elapsedMs:Math.round(performance.now()-n)}}catch(t){return{...e,status:`error`,error:t instanceof Error?t.message:String(t),elapsedMs:Math.round(performance.now()-n)}}}async function b(e,t){if(!e.text)return;let n=(i.providers||[]).find(t=>t.id===e.providerId);n&&(i.selectedProvider=n,i.selectedModel=e.model),i.messages=[...i.messages,{role:`user`,content:t},{role:`assistant`,content:e.text,model:e.model,providerName:e.providerName,usage:e.usage,elapsedMs:e.elapsedMs,importedFromCompare:!0}];try{await ensureConversation(),await saveCurrentMessages()}catch{}x=i.currentConvId?`#/chat/${encodeURIComponent(i.currentConvId)}`:`#/new`,renderTopbar(),renderContent(),renderConvList(),closeModal(),window.requestAnimationFrame(()=>c(`#input`)?.focus()),a(`已采用 ${e.model} 的结果`)}var x=``,S=null;function C(){let e=v();if(e.length<2){a(`模型实验至少需要配置两个模型`,`error`),openSettings(`providers`);return}if(c(`#modal`)?.classList.contains(`open`)&&c(`#modalCard`)?.classList.contains(`compare-modal-card`))return;location.hash===`#/compare`?x||=i.currentConvId?`#/chat/${encodeURIComponent(i.currentConvId)}`:`#/new`:(x=location.hash||(i.currentConvId?`#/chat/${encodeURIComponent(i.currentConvId)}`:`#/new`),history.pushState(null,``,`#/compare`));let t=[i.selectedProvider&&i.selectedModel?`${i.selectedProvider.id}:${i.selectedModel}`:``,...e.map(e=>e.id)].filter(Boolean).filter((e,t,n)=>n.indexOf(e)===t).slice(0,2),n=[...i.messages||[]].reverse().find(e=>e.role===`user`)?.content||``;showModal({title:`模型实验`,body:`<div id="compareReactRoot"></div>`,onMount:r=>{r.classList.add(`compare-modal-card`);let a=r.querySelector(`#compareReactRoot`);a&&(S=(0,u.createRoot)(a),S.render((0,d.createElement)(_,{targets:e,defaultTargetIds:t,initialPrompt:n,projectName:i.selectedProject?.name||`未选择项目`,fileCount:i.selectedAssetIds?.size||0,memoryCount:(i.memories||[]).filter(e=>e.enabled!==!1).length,execute:y,adopt:b})))},onClose:()=>{S?.unmount(),S=null,location.hash===`#/compare`&&history.replaceState(null,``,x||`#/new`),x=``}})}function w(){let e=c(`#compareBtn`);e&&(e.onclick=C),o(`[data-open-compare]`).forEach(e=>e.onclick=C)}export{C as openCompare,w as setupCompare};
@@ -0,0 +1,7 @@
1
+ import{a as e,d as t,f as n,h as r,p as i,u as a}from"./index-CASVCOUO.js";async function o(){try{t.conversations=await e(`/api/conversations`)}catch{t.conversations=[]}s()}function s(){let n=i(`#convList`),a=(t.convSearch||``).trim().toLowerCase(),o=a?t.conversations.filter(e=>(e.title||`新对话`).toLowerCase().includes(a)):t.conversations;if(!o.length){n.innerHTML=`<div style="padding:18px 12px; color:var(--label-caption); font-size:12.5px;">`+(t.conversations.length?`没有匹配的对话`:`还没有对话`)+`</div>`;return}n.innerHTML=o.map(e=>`
2
+ <div class="conv-item ${e.id===t.currentConvId?`active`:``}" data-id="${r(e.id)}">
3
+ <span class="conv-dot"></span>
4
+ <span style="overflow:hidden;text-overflow:ellipsis;">${r(e.title||`新对话`)}</span>
5
+ <button class="conv-del" data-del="${r(e.id)}" title="删除">×</button>
6
+ </div>
7
+ `).join(``),n.querySelectorAll(`.conv-item`).forEach(e=>{e.onclick=t=>{t.target.dataset.del||l(e.dataset.id)}}),n.querySelectorAll(`.conv-del`).forEach(n=>{n.onclick=async r=>{r.stopPropagation();let a=n.dataset.del;if(await showConfirm({title:`删除对话`,message:`删除该对话?`,confirmLabel:`删除`,danger:!0})){try{await e(`/api/conversations/`+a,{method:`DELETE`})}catch{}t.conversations=t.conversations.filter(e=>e.id!==a),t.currentConvId===a&&(t.currentConvId=null,t.messages=[],renderContent(),i(`#topbarTitle`).textContent=`新对话`),s()}}})}async function c(){t.currentConvId=null,t.messages=[],i(`#topbarTitle`).textContent=`新对话`,renderContent(),s(),renderInspector(),i(`#heroInput`)?.focus(),location.hash!==`#/new`&&history.pushState(null,``,`#/new`)}i(`#newChatBtn`).onclick=c;async function l(r){try{let n=await e(`/api/conversations/`+r);(n.workspaceId&&(!t.selectedWorkspace||t.selectedWorkspace.id!==n.workspaceId)||n.projectId&&(!t.selectedProject||t.selectedProject.id!==n.projectId))&&(t.selectedWorkspace=t.workspaces.find(e=>e.id===n.workspaceId)||t.selectedWorkspace,t.selectedWorkspace&&await loadProjects(),n.projectId&&(t.selectedProject=t.projects.find(e=>e.id===n.projectId)||t.selectedProject,t.selectedProject&&(localStorage.setItem(`multichat_project`,t.selectedProject.id),t.assets=await e(`/api/assets?projectId=`+encodeURIComponent(t.selectedProject.id)),t.selectedAssetIds=new Set(t.assets.map(e=>e.id))))),t.currentConvId=n.id,t.messages=(n.messages||[]).map(e=>({role:e.role,content:e.content,model:e.model})),i(`#topbarTitle`).textContent=n.title||`对话`,renderTopbar(),renderContent(),s(),d();let a=`#/chat/${encodeURIComponent(n.id)}`;location.hash!==a&&history.pushState(null,``,a)}catch(e){n(`打开对话失败:`+e.message,`error`)}}function u(e){try{return e<1024?e+` B`:e<1048576?(e/1024).toFixed(1)+` KB`:(e/1024/1024).toFixed(1)+` MB`}catch{return``}}function d(){renderContent(),renderInspector()}function f(){let e=t.selectedProject;if(e){if(e.defaultAgentId){let n=t.agents.find(t=>t.id===e.defaultAgentId);n&&(t.selectedAgent=n,a())}if(e.defaultProviderId&&e.defaultModel){let n=t.providers.find(t=>t.id===e.defaultProviderId);n&&(t.selectedProvider=n,t.selectedModel=e.defaultModel,localStorage.setItem(`multichat_lastModel`,n.id+`:`+e.defaultModel))}renderTopbar()}}export{f as applyProjectDefaults,u as fmtSize,o as loadConversations,c as newConversation,l as openConversation,s as renderConvList,d as renderFileContext};
@@ -0,0 +1 @@
1
+ import{a as e,d as t}from"./index-CASVCOUO.js";async function n(){try{t.providers=await e(`/api/providers`)}catch{t.providers=[]}}async function r(){try{t.runtime=await e(`/api/runtime`)}catch{t.runtime=null}}async function i(){try{t.runs=await e(`/api/runs?limit=40`)}catch{t.runs=[]}}async function a(n=t.usageRange){t.usageRange=String(n||`30`),t.usageLoading=!0;try{let n=new Date().getTimezoneOffset();t.usage=await e(`/api/usage?range=${encodeURIComponent(t.usageRange)}&offset=${n}`)}catch{t.usage=null}finally{t.usageLoading=!1}}async function o(){try{t.capabilities=await e(`/api/capabilities`)}catch{t.capabilities=null}}async function s(){if(!t.selectedProject){t.memories=[],t.snapshots=[];return}try{[t.memories,t.snapshots]=await Promise.all([e(`/api/memories?projectId=`+encodeURIComponent(t.selectedProject.id)),e(`/api/snapshots?projectId=`+encodeURIComponent(t.selectedProject.id))])}catch{t.memories=[],t.snapshots=[]}}async function c(){try{t.workspaces=await e(`/api/workspaces`);let n=localStorage.getItem(`multichat_workspace`);t.selectedWorkspace=t.workspaces.find(e=>e.id===n)||t.workspaces[0]||null,await l()}catch{t.workspaces=[],t.projects=[]}}async function l(){if(!t.selectedWorkspace){t.projects=[],t.selectedProject=null;return}try{t.projects=await e(`/api/projects?workspaceId=`+encodeURIComponent(t.selectedWorkspace.id));let n=localStorage.getItem(`multichat_project`);t.selectedProject=t.projects.find(e=>e.id===n)||t.projects.find(e=>e.id===t.selectedWorkspace.defaultProjectId)||t.projects[0]||null,localStorage.setItem(`multichat_workspace`,t.selectedWorkspace.id),t.selectedProject&&localStorage.setItem(`multichat_project`,t.selectedProject.id),t.selectedProject&&(t.assets=await e(`/api/assets?projectId=`+encodeURIComponent(t.selectedProject.id)),t.selectedAssetIds=new Set(t.assets.map(e=>e.id)),applyProjectDefaults(),await s())}catch{t.projects=[],t.selectedProject=null,t.assets=[],t.selectedAssetIds=new Set}}async function u(){try{t.skills=await e(`/api/skills`)}catch{t.skills=[]}}async function d(){try{t.tools=await e(`/api/tools`)}catch{t.tools=[]}}async function f(){try{t.mcpServers=await e(`/api/mcp-servers`)}catch{t.mcpServers=[]}}async function p(){let n=t.selectedAgent?.id;try{t.agents=await e(`/api/agents`),n&&(t.selectedAgent=t.agents.find(e=>e.id===n)||null)}catch{t.agents=[],n&&(t.selectedAgent=null)}}async function m(){try{t.plugins=await e(`/api/plugins`)}catch{t.plugins=[]}}export{p as loadAgents,o as loadCapabilities,f as loadMcpServers,m as loadPlugins,s as loadProjectControlData,l as loadProjects,n as loadProviders,i as loadRuns,r as loadRuntime,u as loadSkills,d as loadTools,a as loadUsage,c as loadWorkspaces};
@@ -0,0 +1,28 @@
1
+ import{a as e,f as t,h as n,p as r}from"./index-CASVCOUO.js";var i=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 15V4M7.5 8.5 12 4l4.5 4.5"/><path d="M4 15v3.5A1.5 1.5 0 0 0 5.5 20h13a1.5 1.5 0 0 0 1.5-1.5V15"/></svg>`,a=null,o={skill:{title:`导入 Agent Skill`,tab:`skills`,accept:`.zip,.md`,fileLabel:`选择 ZIP / SKILL.md`,directory:!0,help:`支持 WorkBuddy 风格的 Skill ZIP、单个 SKILL.md,或直接选择完整目录。目录中的 scripts、references、assets 和 templates 会原样保留。`},mcp:{title:`导入 MCP 配置`,tab:`mcp`,accept:`.json,.mcp.json`,fileLabel:`选择 MCP JSON`,directory:!1,help:`支持 mcpServers、mcp_servers、servers 和直接 server map。导入项默认停用且未信任,不会在导入时启动命令或连接网络。`},plugin:{title:`安装插件包`,tab:`plugins`,accept:`.zip`,fileLabel:`选择插件 ZIP`,directory:!0,help:`插件以 .codex-plugin/plugin.json 为入口,整包注册到当前项目市场,Skill、MCP 与资源目录不会被扁平化。安装与启用分离。`}};function s(e){return new Promise((t,n)=>{let r=new FileReader;r.onerror=()=>n(Error(`无法读取 ${e.name}`)),r.onload=()=>{let i=String(r.result||``),a=i.indexOf(`,`);a<0?n(Error(`无法编码 ${e.name}`)):t(i.slice(a+1))},r.readAsDataURL(e)})}async function c(e){let t=Array.from(e||[]);if(!t.length)throw Error(`请先选择文件`);if(t.length>600)throw Error(`一次最多导入 600 个文件`);if(t.reduce((e,t)=>e+t.size,0)>20971520)throw Error(`导入文件总大小不能超过 20 MB`);if(!(t.length>1||t.some(e=>!!e.webkitRelativePath))){let e=t[0];return{fileName:e.name,contentBase64:await s(e)}}return{files:await Promise.all(t.map(async e=>({path:e.webkitRelativePath||e.name,contentBase64:await s(e)})))}}function l(e){return Number.isFinite(e)?e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`:``}function u(e,t){if(e===`mcp`)return`<div class="extension-import-list">${(t.servers||[]).map(e=>`
2
+ <div class="extension-import-row"><div><strong>${n(e.name)}</strong><span>${n(e.id)}</span></div><div class="mc-tags"><span class="mc-tag">${n(String(e.transport||``).toUpperCase())}</span>${(e.targets||[]).map(e=>`<span class="mc-tag">${n(e)}</span>`).join(``)}</div></div>`).join(``)}</div>`;let r=t.components||{};return`<div class="mc-tags extension-import-tags">${[t.fileCount===void 0?``:`${t.fileCount} 个文件`,t.totalBytes===void 0?``:l(t.totalBytes),...t.resources||[],r.skills===void 0?``:`${r.skills} Skills`,r.mcpServers===void 0?``:`${r.mcpServers} MCP`].filter(Boolean).map(e=>`<span class="mc-tag">${n(e)}</span>`).join(``)}</div>
3
+ ${(t.fileTree||[]).length?`<details class="extension-file-tree"><summary>查看包内文件</summary><pre>${n(t.fileTree.join(`
4
+ `))}</pre></details>`:``}`}async function d(e){e===`skill`&&await Promise.all([loadSkills(),loadAgents(),loadRuntime()]),e===`mcp`&&await Promise.all([loadMcpServers(),loadAgents(),loadRuntime()]),e===`plugin`&&await Promise.all([loadPlugins(),loadSkills(),loadMcpServers(),loadAgents(),loadRuntime()])}function f(i,a,s){let c=o[i],l=s.conflicts||[],f=l.some(e=>e.replaceable===!1),p=s.warnings||[];showModal({title:`预检通过 · ${s.name}`,body:`<div class="extension-import-summary">
5
+ <div class="extension-import-kind">${i===`skill`?`SKILL`:i===`mcp`?`MCP`:`PLUGIN`}</div>
6
+ <div><div class="extension-import-name">${n(s.name)}</div><div class="pmeta">${n(s.description||``)}${s.version?` · v${n(s.version)}`:``}</div></div>
7
+ </div>
8
+ <div class="field"><label>安装范围</label><div class="extension-import-scope">当前项目 · ${i===`mcp`?`.codex/config.toml(启用且选择 Codex 后同步)`:i===`skill`?`.agents/skills`:`.agents/plugins/marketplace.json`}</div></div>
9
+ ${u(i,s)}
10
+ ${p.length?`<div class="extension-import-warnings"><strong>启用前请确认</strong>${p.map(e=>`<div>• ${n(e)}</div>`).join(``)}</div>`:``}
11
+ ${l.length?`<div class="extension-import-conflicts"><strong>发现同名项</strong>${l.map(e=>`<div>${n(e.id)}${e.path?` · ${n(e.path)}`:``}${e.replaceable===!1?` · 其他来源,不可覆盖`:``}</div>`).join(``)}</div>`:``}
12
+ ${i===`plugin`?`<div class="extension-import-note">插件以完整资源包安装并独立启停,包契约使用 Codex plugin。MultiChat 当前会加载其中的 Skills 与 MCP;其他资源会保留,但不会把上传的 JS、hooks 直接注入网站主进程。</div>`:``}
13
+ ${l.length&&!f?`<label class="extension-import-confirm"><input type="checkbox" id="replaceExtension" /> 我已确认,用这个包覆盖当前项目中的同名项</label>`:``}
14
+ <div id="extensionImportErr" class="auth-error">${f?`同名项来自插件或其他受保护来源,本次导入不能覆盖。`:``}</div>
15
+ <div class="row"><button type="button" class="btn-ghost" id="importBack">重新选择</button><button type="button" class="btn-primary" id="importCommit" style="width:auto;padding:9px 18px;" ${f?`disabled`:``}>确认导入</button></div>`,onMount:n=>{r(`#importBack`,n).onclick=()=>m(i),r(`#importCommit`,n).onclick=async()=>{let o=l.length>0&&!!r(`#replaceExtension`,n)?.checked;if(l.length&&!o){r(`#extensionImportErr`,n).textContent=`请先确认覆盖同名项。`;return}let u=r(`#importCommit`,n);u.disabled=!0,u.textContent=`正在导入…`;try{await e(`/api/extensions/import/${i}/install`,{method:`POST`,body:JSON.stringify({...a,expectedFingerprint:s.fingerprint,conflictPolicy:o?`replace`:`reject`})}),await d(i),renderSettings(c.tab,!0),closeModal(),t(i===`plugin`?`插件包已导入,当前保持停用`:i===`mcp`?`MCP 配置已导入,当前保持停用`:`Skill 已导入当前项目`)}catch(e){r(`#extensionImportErr`,n).textContent=e.message,u.disabled=!1,u.textContent=`确认导入`}}}})}async function p(t,n,i){let o=r(`#extensionPickStatus`,i);o.classList.remove(`error`),o.textContent=`正在读取并预检…`;let s=r(`#extensionPickRetry`,i);s&&(s.hidden=!0,s.disabled=!0);try{let r=await c(n);f(t,r,await e(`/api/extensions/import/${t}/inspect`,{method:`POST`,body:JSON.stringify(r)}))}catch(e){a={kind:t,files:n},o.textContent=e.message,o.classList.add(`error`),s&&(s.hidden=!1,s.disabled=!1,s.onclick=()=>a&&p(a.kind,a.files,i))}}function m(e){let t=o[e];showModal({title:t.title,body:`<p class="lead" style="margin-bottom:14px;">${n(t.help)}</p>
16
+ <div class="extension-dropzone" id="extensionDropzone" tabindex="0">
17
+ <div class="extension-drop-icon">${i}</div><strong>拖入文件进行预检</strong><span>不会在预检阶段执行任何脚本或 MCP 命令</span>
18
+ </div>
19
+ <div class="extension-picker-actions">
20
+ <button type="button" class="btn-primary" id="pickExtensionFile" style="width:auto;padding:9px 16px;">${n(t.fileLabel)}</button>
21
+ ${t.directory?`<button type="button" class="btn-ghost" id="pickExtensionDirectory">选择完整目录</button>`:``}
22
+ </div>
23
+ <input type="file" id="extensionFileInput" accept="${n(t.accept)}" hidden />
24
+ ${t.directory?`<input type="file" id="extensionDirectoryInput" hidden multiple />`:``}
25
+ <div id="extensionPickStatus" class="extension-pick-status" role="status"></div>
26
+ <button type="button" class="btn-ghost" id="extensionPickRetry" hidden>用刚才的文件重新预检</button>
27
+ <div class="extension-import-note">默认冲突策略是拒绝覆盖;只有预检发现冲突且你再次勾选确认后,才会替换旧版本。</div>
28
+ <div class="row"><button type="button" class="btn-ghost" id="extensionImportCancel">取消</button></div>`,onMount:t=>{let n=r(`#extensionFileInput`,t),i=r(`#extensionDirectoryInput`,t);i&&i.setAttribute(`webkitdirectory`,``),r(`#pickExtensionFile`,t).onclick=()=>n.click(),i&&(r(`#pickExtensionDirectory`,t).onclick=()=>i.click()),r(`#extensionImportCancel`,t).onclick=closeModal,n.onchange=()=>n.files?.length&&p(e,n.files,t),i&&(i.onchange=()=>i.files?.length&&p(e,i.files,t));let a=r(`#extensionDropzone`,t);a.ondragover=e=>{e.preventDefault(),a.classList.add(`dragging`)},a.ondragleave=()=>a.classList.remove(`dragging`),a.ondrop=n=>{n.preventDefault(),a.classList.remove(`dragging`),n.dataTransfer?.files?.length&&p(e,n.dataTransfer.files,t)}}})}export{m as showExtensionImport};
@@ -0,0 +1,70 @@
1
+ import{a as e,d as t,f as n,h as r,p as i}from"./index-CASVCOUO.js";function a(){return`
2
+ <div class="import-bar">
3
+ <span class="import-bar-label">运行配置备份</span>
4
+ <button class="mini-btn" id="importFile">导入配置 JSON</button>
5
+ <input type="file" id="importFileInput" accept=".json,application/json" style="display:none" />
6
+ </div>`}async function o(r,i){try{let a=await e(`/api/import`,{method:`POST`,body:JSON.stringify(r)});return await loadAgents(),renderSettings(t.currentTab||`agents`,!0),renderTopbar(),n(`已导入 ${a.agents} 个运行配置(来源:${i})`),a}catch(e){throw n(e.message,`error`),e}}function s(e){if(Array.isArray(e))return{agents:e};if(e&&e.agents)return{agents:e.agents};if(e&&typeof e==`object`&&e.id)return{agents:[e]};throw Error(`文件不是 Agent JSON 备份`)}function c(){let e=i(`#settingsBody`),t=e.querySelector(`#importFile`),r=e.querySelector(`#importFileInput`);t&&r&&(t.onclick=()=>r.click(),r.onchange=async()=>{let e=r.files&&r.files[0];if(e){try{let t=await e.text();await o(s(JSON.parse(t)),`文件 `+e.name)}catch(e){n(`解析失败:`+e.message,`error`)}r.value=``}})}function l(e,t){let r=new Blob([JSON.stringify(e,null,2)],{type:`application/json`}),i=document.createElement(`a`);i.href=URL.createObjectURL(r),i.download=(e.id||t)+`.json`,document.body.appendChild(i),i.click(),i.remove(),setTimeout(()=>URL.revokeObjectURL(i.href),1e3),n(`已导出:`+(e.name||e.id))}function u(a){let o=!!a,s=o?t.agents.find(e=>e.id===a):null;showModal({title:o?`编辑运行配置`:`新建运行配置`,body:`<form id="agentForm">
7
+ <div class="field"><label>名称</label><input name="name" value="${s?r(s.name):``}" placeholder="中英翻译" required /></div>
8
+ <div class="field"><label>描述</label><input name="description" value="${s?r(s.description||``):``}" placeholder="一句话说明这套配置适合什么任务" /></div>
9
+ <div class="field"><label>系统提示词</label><textarea name="systemPrompt" rows="4" placeholder="你是一个...">${s?r(s.systemPrompt||``):``}</textarea></div>
10
+ <div class="field"><label>单次最大执行轮数</label><input name="maxIterations" type="number" min="1" max="30" step="1" value="${r(s?.maxIterations||12)}" /><div class="pmeta">达到上限后保存检查点,可在对话中继续运行。</div></div>
11
+ <div class="field"><label>Agent Skills(先提供描述,匹配任务时再加载完整工作流)</label>
12
+ <div class="extension-checks">
13
+ ${t.skills.map(e=>{let t=e.key||e.id,n=s&&(s.skillRefs||s.skillIds)||[],i=n.includes(t)||n.includes(e.id)?`checked`:``;return`<label>
14
+ <input type="checkbox" name="skill" value="${r(t)}" ${i} /> ${r(e.name)} <span class="pmeta">${r(sourceLabel(e.source))}${e.enabled?``:` · 已停用`}</span>
15
+ </label>`}).join(``)||`<span class="pmeta">没有已启用的 Skill</span>`}
16
+ </div>
17
+ </div>
18
+ <div class="field"><label>内置工具(函数调用)</label>
19
+ <div class="extension-checks">
20
+ ${(t.tools||[]).map(e=>{let t=s&&(s.toolIds||[]).includes(e.id)?`checked`:``;return`<label><input type="checkbox" name="tool" value="${r(e.id)}" ${t} /> ${r(e.name)}${e.enabled?``:`(已停用)`}</label>`}).join(``)||`<span class="pmeta">没有已启用的内置工具</span>`}
21
+ </div>
22
+ </div>
23
+ <div class="field"><label>MCP servers(工具由 tools/list 实时发现)</label>
24
+ <div class="extension-checks">
25
+ ${(t.mcpServers||[]).filter(e=>(e.targets||[`multichat`]).includes(`multichat`)).map(e=>{let t=s&&(s.mcpServerIds||[]).includes(e.id)?`checked`:``;return`<label><input type="checkbox" name="mcpServer" value="${r(e.id)}" ${t} /> ${r(e.name)} <span class="pmeta">${(e.tools||[]).length} tools${e.enabled?``:` · 已停用`}</span></label>`}).join(``)||`<span class="pmeta">没有已启用的 MCP server</span>`}
26
+ </div>
27
+ </div>
28
+ <div id="agentErr" class="auth-error"></div>
29
+ <div class="row">
30
+ <button type="button" class="btn-ghost" id="agentCancel">取消</button>
31
+ <button type="submit" class="btn-primary" style="width:auto;padding:9px 18px;">${o?`保存`:`创建`}</button>
32
+ </div>
33
+ </form>`,onMount:r=>{i(`#agentCancel`,r).onclick=closeModal,i(`#agentForm`,r).onsubmit=async c=>{c.preventDefault();let l=new FormData(c.target),u=Object.fromEntries(l.entries());u.name&&=u.name.trim(),u.description&&=u.description.trim(),u.maxIterations=Math.max(1,Math.min(30,Number.parseInt(u.maxIterations||`12`,10)||12));let d=new Set(t.skills.flatMap(e=>[e.key||e.id,e.id])),f=new Set((t.tools||[]).map(e=>e.id)),p=new Set((t.mcpServers||[]).filter(e=>(e.targets||[`multichat`]).includes(`multichat`)).map(e=>e.id)),m=o?(s?.skillRefs||s?.skillIds||[]).filter(e=>!d.has(e)):[],h=o?(s?.toolIds||[]).filter(e=>!f.has(e)):[],g=o?(s?.mcpServerIds||[]).filter(e=>!p.has(e)):[];u.skillRefs=[...Array.from(c.target.querySelectorAll(`input[name="skill"]:checked`)).map(e=>e.value),...m],u.toolIds=[...Array.from(c.target.querySelectorAll(`input[name="tool"]:checked`)).map(e=>e.value),...h],u.mcpServerIds=[...Array.from(c.target.querySelectorAll(`input[name="mcpServer"]:checked`)).map(e=>e.value),...g];try{o?(await e(`/api/agents/`+a,{method:`PUT`,body:JSON.stringify(u)}),n(`已保存`)):(await e(`/api/agents`,{method:`POST`,body:JSON.stringify(u)}),n(`已创建`)),await loadAgents(),renderSettings(t.currentTab||`agents`,!0),renderTopbar(),closeModal()}catch(e){i(`#agentErr`,r).textContent=e.message}}}})}function d(a){let o=!!a,s=o?t.skills.find(e=>(e.key||e.id)===a):null;showModal({title:o?`编辑 Agent Skill`:`新建 Agent Skill`,body:`<form id="skillForm">
34
+ <div class="field"><label>Skill ID</label><input name="id" value="${s?r(s.id):``}" placeholder="release-notes" required pattern="[a-z0-9-]+" ${o?`readonly`:``} />
35
+ <div class="pmeta">1–64 位小写字母、数字或连字符;写入 SKILL.md frontmatter。</div></div>
36
+ <div class="field"><label>Skill name</label><input name="name" value="${s?r(s.name):``}" placeholder="release-notes" required pattern="[a-z0-9-]+" ${o?`readonly`:``} /></div>
37
+ <div class="field"><label>Description</label><textarea name="description" rows="3" placeholder="说明何时应使用此 Skill,以及它能完成什么。" required>${s?r(s.description||``):``}</textarea></div>
38
+ <div class="field"><label>Instructions(SKILL.md 正文)</label><textarea name="instructions" rows="9" placeholder="# Workflow&#10;&#10;1. ..." required>${s?r(s.instructions||``):``}</textarea>
39
+ <div class="pmeta">需要脚本、参考资料或模板时,可在对应 Skill 目录添加 scripts、references、assets。</div></div>
40
+ <div id="skillErr" class="auth-error"></div>
41
+ <div class="row">
42
+ <button type="button" class="btn-ghost" id="skillCancel">取消</button>
43
+ <button type="submit" class="btn-primary" style="width:auto;padding:9px 18px;">${o?`保存`:`创建`}</button>
44
+ </div>
45
+ </form>`,onMount:r=>{i(`#skillCancel`,r).onclick=closeModal,i(`#skillForm`,r).onsubmit=async s=>{s.preventDefault();let c=new FormData(s.target),l=Object.fromEntries(c.entries());l.id&&=l.id.trim(),l.name&&=l.name.trim(),l.description&&=l.description.trim(),l.instructions&&=l.instructions.trim();try{if(o){let t={...l};delete t.id,await e(`/api/skills/`+encodeURIComponent(a),{method:`PUT`,body:JSON.stringify(t)}),n(`已保存`)}else await e(`/api/skills`,{method:`POST`,body:JSON.stringify(l)}),n(`已创建`);await loadSkills(),renderSettings(t.currentTab||`skills`,!0),closeModal()}catch(e){i(`#skillErr`,r).textContent=e.message}}}})}var f=[{id:`openai`,name:`OpenAI`,apiType:`openai`,baseUrl:`https://api.openai.com/v1`,models:[`gpt-4o`,`gpt-4o-mini`,`gpt-4-turbo`]},{id:`deepseek`,name:`DeepSeek`,apiType:`openai`,baseUrl:`https://api.deepseek.com/v1`,models:[`deepseek-chat`,`deepseek-reasoner`]},{id:`anthropic`,name:`Anthropic`,apiType:`anthropic`,baseUrl:`https://api.anthropic.com/v1`,models:[`claude-3-5-sonnet-latest`,`claude-3-opus-latest`,`claude-3-haiku-latest`]},{id:`gemini`,name:`Google Gemini`,apiType:`openai`,baseUrl:`https://generativelanguage.googleapis.com/v1beta/openai`,models:[`gemini-1.5-pro`,`gemini-1.5-flash`,`gemini-2.0-flash`]},{id:`moonshot`,name:`月之暗面 Kimi`,apiType:`openai`,baseUrl:`https://api.moonshot.cn/v1`,models:[`moonshot-v1-8k`,`moonshot-v1-32k`,`moonshot-v1-128k`]},{id:`zhipu`,name:`智谱 GLM`,apiType:`zhipu`,baseUrl:`https://open.bigmodel.cn/api/paas/v4`,models:[`glm-4-plus`,`glm-4`,`glm-4-air`]},{id:`dashscope`,name:`阿里云 DashScope`,apiType:`openai`,baseUrl:`https://dashscope.aliyuncs.com/compatible-mode/v1`,models:[`qwen-max`,`qwen-plus`,`qwen-turbo`]},{id:`ollama`,name:`Ollama (本地)`,apiType:`ollama`,baseUrl:`http://localhost:11434/v1`,models:[]},{id:`lmstudio`,name:`LM Studio (本地)`,apiType:`lmstudio`,baseUrl:`http://localhost:1234/v1`,models:[]},{id:`siliconflow`,name:`硅基流动 SiliconFlow (注册送额度)`,apiType:`openai`,baseUrl:`https://api.siliconflow.cn/v1`,models:[`Qwen/Qwen2.5-72B-Instruct`,`deepseek-ai/DeepSeek-V3`,`Qwen/Qwen2.5-7B-Instruct`]}];function p(){showModal({title:`添加提供方`,body:`<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
46
+ ${f.map(e=>`<button class="add-tile" data-id="${e.id}" style="text-align:left;padding:14px;">
47
+ <div style="font-weight:600;color:var(--label-primary);">${e.name}</div>
48
+ <div style="font-size:11.5px;color:var(--label-caption);margin-top:2px;">${e.apiType} · ${e.baseUrl}</div>
49
+ </button>`).join(``)}
50
+ </div>`,onMount:t=>{t.querySelectorAll(`[data-id]`).forEach(t=>t.onclick=async()=>{let r=f.find(e=>e.id===t.dataset.id);try{await e(`/api/providers`,{method:`POST`,body:JSON.stringify(r)}),await loadProviders(),renderSettings(`providers`,!0),closeModal(),n(`已添加:`+r.name)}catch(e){n(e.message,`error`)}})}})}function m(){showModal({title:`自定义提供方`,body:`<form id="customForm">
51
+ <div class="field"><label>Provider ID</label><input name="id" placeholder="acme-gateway" required pattern="[-a-zA-Z0-9_]+" />
52
+ <div style="font-size:11.5px;color:var(--label-caption);margin-top:4px;">小写字母、数字、下划线或连字符,唯一标识该提供方。</div></div>
53
+ <div class="field"><label>模型列表</label><textarea name="models" rows="2" placeholder="deepseek-chat, deepseek-reasoner"></textarea>
54
+ <div style="font-size:11.5px;color:var(--label-caption);margin-top:4px;">逗号或换行分隔;如不确定可留空,稍后在模型卡片中补充。</div></div>
55
+ <div class="field"><label>显示名称</label><input name="name" placeholder="显示名称" /></div>
56
+ <div class="field"><label>API 地址</label><input name="baseUrl" placeholder="https://gateway.example/v1" required /></div>
57
+ <div class="field"><label>API 协议</label><select name="apiType">
58
+ <option value="openai">openai-completions</option>
59
+ <option value="anthropic">anthropic-messages</option>
60
+ <option value="ollama">ollama</option>
61
+ <option value="lmstudio">lmstudio</option>
62
+ </select></div>
63
+ <div class="field"><label>API 密钥</label><input name="apiKey" type="password" placeholder="输入 API 密钥" /></div>
64
+ <label class="provider-private"><input name="allowPrivate" type="checkbox" /> 允许该提供方访问本机或内网地址(仅本地网关需要)</label>
65
+ <div id="customErr" class="auth-error"></div>
66
+ <div class="row">
67
+ <button type="button" class="btn-ghost" id="customCancel">取消</button>
68
+ <button type="submit" class="btn-primary" style="width:auto;padding:9px 18px;">保存</button>
69
+ </div>
70
+ </form>`,onMount:t=>{i(`#customCancel`,t).onclick=closeModal,i(`#customForm`,t).onsubmit=async r=>{r.preventDefault();let a=new FormData(r.target),o=Object.fromEntries(a.entries());o.models=(o.models||``).toString().split(/[,\n]/).map(e=>e.trim()).filter(Boolean),o.allowPrivate=a.get(`allowPrivate`)===`on`,o.id&&=o.id.trim(),o.name&&=o.name.trim();try{await e(`/api/providers`,{method:`POST`,body:JSON.stringify(o)}),await loadProviders(),renderSettings(`providers`,!0),closeModal(),n(`已添加`)}catch(e){i(`#customErr`,t).textContent=e.message}}}})}export{f as BUILTIN_PROVIDERS,o as doImport,l as exportEntity,a as importBarHTML,s as normalizeImport,p as showAddBuiltin,m as showAddCustom,u as showAgentModal,d as showSkillModal,c as wireImportBar};